如何绕过长度检查执行特定的缓冲区溢出

How to perform a specific buffer overflow bypassing a length check

我正在尝试执行缓冲区溢出,以便变量(类型)在其中具有特定值。我与 strlen 斗争并检查我的输入。

我尝试使用类似的东西:'AAAAA\x00AAA...A\x00\xbc\xd4\xb9' 来欺骗 strlen 检查我的输入是否只有 5 个 A 的长度。但是有些东西总是用消息剥离我的 \x00:

-bash:警告:命令替换:忽略输入中的空字节

所以我做了一个更大的输入并有一个整数溢出,这样保存长度的短部分太大并且低于 32700...但这似乎也没有用。

我的输入构造如下:

./challenge `python -c "print 'A'*30000 + '\x00' + 'A'*10000" + '\xXX\xXX\xXX\xXX'`

(对于 \xXX 我想要的输入)

./challenge `python programm.py`

我也在用gdb分析内存。但是我找不到正确的使用方法...

我的任务代码是:

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

int main(int argc, char *argv[])
{
    int type = 0xdeadbeef;
    char buf[32700];
    short len;

    if (argc < 2)
        return -1;

    len = strlen(argv[1]);
    if (len > 32700) {
        printf("Too long\n");
        return -1;
    }

    strcpy(buf, argv[1]);

    if (type == 0x00b1c2d3) {
        printf("Success\n");
    } else {
        printf("Try again\n");
    }

    exit(0);
}

对于这个问题还有其他想法(或 'solution')吗?几天以来我一直在努力。此外,多年来我与 C 没有任何关系(尤其是内存分析、gdb 和所有这些),所以我很难理解一些东西。

代码中的问题是使用 strlen() 计算的长度 lenshort。这意味着它是一个 16 位值。由于 strlen() return 是一个 size_t,在我的 x86_64 系统上是一个 64 位值,因此只有低 16 位将存储在 len 变量中.

因此,乍一看,您可以提交超过 65,535 字节的任何内容,溢出 short int,并仅存储较低的 16 位。但是还有一个问题需要注意。 len 变量是一个 有符号 短整数。这意味着它存储来自 [-32768,32767] 的值,而 size_t 是无符号值。因此,当 strlen() return 是其值时,设置了最高位的长度(超过 32,767 字节的任何长度)将变为负数。

这里有一个示例 gdb 会话说明了这一点:

(gdb) break 22
Breakpoint 1 at 0x829: file challenge.c, line 22.
(gdb) run `ruby -e 'print("A"*32768)'`
Starting program: /home/josh/foo/challenge `ruby -e 'print("A"*32768)'`

Breakpoint 1, main (argc=2, argv=0x7fffffff6338) at challenge.c:22
22          if (type == 0x00b1c2d3) {
(gdb) p len
 = -32768
(gdb) x/64bx $rsp
0x7ffffffee260: 0x38    0x63    0xff    0xff    0xff    0x7f    0x00    0x00
0x7ffffffee268: 0x00    0x00    0x00    0x00    0x02    0x00    0x00    0x00
0x7ffffffee270: 0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x7ffffffee278: 0x00    0x00    0x00    0x80    0xef    0xbe    0xad    0xde
0x7ffffffee280: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x7ffffffee288: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x7ffffffee290: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
0x7ffffffee298: 0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41
(gdb) 

看到 0x7ffffffee27a 的字节是 0x00 0x80 了吗?那是 short int 的 -32768,因此长度被认为是负数(远低于 32,700!)。

我还在内存转储中包含了一些额外的信息,因此您可以看到 type 字段紧接在内存中的 len 字段之后,最后是 buf数组(其中包含我的 32768 As!)。请注意,只有特定长度的输入有效——那些显示为负数,或在截断为 16 位时溢出到小于 32,700 的值。

这并不能解决全部挑战,但至少应该让您有个良好的开端,了解您可以控制什么。另请注意,buf 数组上方有数据,例如 rbp 点的位置,您保存的 return 指针等。取决于您如何编译它(例如,有或没有堆栈金丝雀等),一路上可能还有更多的事情需要你解决。