缓冲区溢出向后流动?

6 c debugging gdb

我在读《黑客:剥削的艺术》这本书时,发现本书的一部分显示了通过重新排列内存中的变量来防止缓冲区溢出漏洞的一种假定的保护措施。我试过了,该程序仍然容易受到缓冲区溢出的影响。这是代码:

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

int check_authentication(char *password) {
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*
    char password_buffer[16];
    int auth_flag = 0;
    //*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*

    strcpy(password_buffer, password);

    if (strcmp(password_buffer, "password1") == 0)
        auth_flag = 1;
    if (strcmp(password_buffer, "password2") == 0)
        auth_flag = 0;

    return auth_flag;
}


int main(int argc, char *argv[]) {
    if (check_authentication(argv[1])) {
        printf("ACCESS GRANTED");
    } else {
        printf("ACCESS DENIED");
    }
}
Run Code Online (Sandbox Code Playgroud)

简而言之,该程序检查第二个命令行参数提供的身份验证密钥,并告诉用户是否已授予访问权限。重点是带有星号的注释所概述的变量。从理论上讲,这种变量安排应防止由于FILO数据结构而引起的缓冲区溢出攻击,这意味着密码缓冲区应位于auth_flag变量之后,因此应防止密码缓冲区成为缓冲区溢出的执行点。这是对GDB中执行流程的检查:

(gdb) break 9
 Breakpoint 1 at 0x1188: file auth_overflow2.c, line 9.
(gdb) break 16
 Breakpoint 2 at 0x11d7: file auth_overflow2.c, line 16.
Run Code Online (Sandbox Code Playgroud)

检查程序的第一步是在第9行(在密码验证之前执行strcpy函数)和第16行(返回auth_flag并随后进行密码验证)设置断点。然后,我们可以运行该程序并检查断点1处的内存。

(gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 Starting program: /home/user/Hacking/a.out aaaaaaaaaaaaaaaaaaaaaaaaaaaa

 Breakpoint 1, check_authentication (password=0x7fffffffe484 'a' <repeats 29 times>) at auth_overflow2.c:9
9       strcpy(password_buffer, password);
(gdb) x/s password_buffer
 0x7fffffffe060:    ""
(gdb) x/x &auth_flag
 0x7fffffffe07c:    0x00
(gdb)x/16xw &auth_flag
 0x7fffffffe07c:    0x00000000  0xffffe0a0  0x00007fff  0x55555229
 0x7fffffffe08c:    0x00005555  0xffffe188  0x00007fff  0x00000000
 0x7fffffffe09c:    0x00000002  0x55555270  0x00005555  0xf7e0609b
 0x7fffffffe0ac:    0x00007fff  0x00000000  0x00000000  0xffffe188
Run Code Online (Sandbox Code Playgroud)

从上面的输出中可以看到,auth_flag处于适当的0,密码缓冲区为空。上面输出中的最后一条命令还表明,auth_flag变量在密码缓冲区之前已正确定位,因此,从理论上讲,缓冲区溢出不应以任何方式影响auth_flag变量。现在让我们继续执行程序,并在验证检查后检查内存。

(gdb) cont
 Continuing.

 Breakpoint 2, check_authentication (password=0x7fffffffe484 'a' <repeats 29 times>) at auth_overflow2.c:16
 16     return auth_flag;
(gdb) x/s password_buffer
 0x7fffffffe060:    'a' <repeats 29 times>
(gdb) x/x &auth_flag
 0x7fffffffe07c:    0x61
(gdb) x/16xw &auth_flag
 0x7fffffffe07c:    0x00000061  0xffffe0a0  0x00007fff  0x55555229
 0x7fffffffe08c:    0x00005555  0xffffe188  0x00007fff  0x00000000
 0x7fffffffe09c:    0x00000002  0x55555270  0x00005555  0xf7e0609b
 0x7fffffffe0ac:    0x00007fff  0x00000000  0x00000000  0xffffe188
Run Code Online (Sandbox Code Playgroud)

从上面的输出中可以看到,即使密码缓冲区位于auth_flag变量之后,auth_flag变量还是以某种方式更改为非零值。

因此,考虑到所有这些信息之后,缓冲区溢出怎么可能在内存中向后而不是向前流动?

Flo*_*mer 1

一些编译器会重新排列栈上数组,以便在栈帧中有其他变量或溢出槽时它们不与返回地址相邻,希望少数字节的有限溢出不再能够到达返回地址从而重定向执行。如果这些变量对于安全性与返回地址同样重要,那么这种启发式就会出错,在您假设的示例中,情况似乎就是这样。