attacklab
Part I: Code Injection Attacks
开冲!!
level1
sub rsp,0x28
说明缓冲区有0x28
即40字节,我们只需在这之后加入touch1()
函数的权限地址来提权。
gets()
函数不会对输入大小进行检查,遇到\n0x0a
结束字符串读取
touch1地址为0x4017c0
payload(注意不能填充0a),同时小端序要倒序存储(低地址存放数据低位,高地址存放数据高位)
1 | 00 00 00 00 00 00 00 00 #低地址 |
通过
level2
反编译touch2
touch地址为0x4017ec
我们发现有个cookie的比较,cookie在cookie.txt文件,值为0x59b997fa
,我们需要将%edi
的值设为cookie。
因此我们需要注入汇编代码。我们让payload最后的跳转跳转到字符串开头在栈中的位置,即0x5561dc78
1 | movq $0x59b997fa,%rdi |
提示我们jmp
和call
的指令很难确定目标地址的编码,具体以后去了解一下吧,我们使用ret
将指令转化为机器码
最后得出payload:
1 | 48 c7 c7 fa 97 b9 59 68 |
通过 ~
level3
反汇编,touch3地址0x4018fa
。发现调用hexmatch函数,并且,writeup给出了函数代码
1 | /* Compare string to hex represention of unsigned value */ |
分析一下,hexmatch
传入cookie
(val)和touch3()
的参数sval
,中间部分生成随机数,覆盖栈帧中的数据,因此我们只能把数据放在父栈帧test
里。。接着是sprintf(s, “%.8x”, val),该函数类似于printf
函数,不过sprintf
函数是将将格式化字符串输出到字符串里。s
表示目的字符串,%.8x
表示读取8个字符,输出为16进制,val
表示源字符串。strncmp(sval, s, 9)
最后对cookie
和s
前九个字符进行比较,包括字符串的结束标志\x0
。总结一下就是要让touch3
的参数等于cookie的值0x59b997fa
。
由于64位系统使用%rdi第一个参数,但字符串的大小可能超过寄存器所能存储的大小,所以寄存器往往传送的是字符串的在栈中的地址。我们将字符串覆盖到%rdi
所指向的内存即可。
payload
1 | 48 c7 c7 a8 dc 61 55 68 |
Part II: Return-Oriented Programming(ROP)
即开启ASLR堆栈地址随机化
和NX栈上的数据没有执行权限
保护
Level2
同样的地址,我们回想下第一节的level2
但我们显然不能找到将立即数的操作指令,因为我们无法更改指令中立即数的值。但我们可以换种思路,将值pop
进寄存器中,由于在farm
中找不到
1 | pop %rax |
我们选择地址为0x4019cc
的代码,0x58
为pop %rax
,0x90
为nop
操作,c3
为ret指令
我们搜索movq %rax,%rdi
的机器码48 89 c7
,我们选择第一个而不是第二个。地址为0x40192
因为下一机器码为0xc3
即为ret
指令,而c7
不是,无法返回到我们的攻击代码中。
因此我们的payload为
1 | 00 00 00 00 00 00 00 00 |
PASS!
level3
终于闯到最后一关了好耶!
1 | mov %rsp,%rax |
我们注意到有一个特殊的指令。可以通过他来修改寄存器%rdi。
1 | 90 90 90 90 90 90 90 90 |
但是这题有一妙解attacklab实验报告:代码注入以及rop攻击 | xia0ji233’s blog,我们可以直接将%rax寄存器加上一个值来修改%rdi
的值,而寄存器的低位加上去也能得结果。所以,我们可以这样。
40 37
即add $0x37,%al
寄存器前要加上%号,否则寄存器会按照64位处理
1 | movq %rsp,%rax |