几天前,我从opensecuritytraining.info中获取了这段代码来测试缓冲区溢出利用:

#include <stdio.h>

char *secret = "pepito";

void go_shell(){
    char *shell = "/bin/sh";
    char *cmd[] = { "/bin/sh", 0 };
    printf("¿Quieres jugar a un juego?...\n");
    setreuid(0);
    execve(shell,cmd,0);
}

int authorize(){
    char password[64];
    printf("Escriba la contraseña: ");
    gets(password);
    if (!strcmp(password,secret))
        return 1;
    else
        return 0;
}

int main(){
    if (authorize()){
        printf("Acceso permitido\n");
        go_shell();
    } else{
        printf("Acceso denegado\n");
    }
    return 0;
}


注入shellcode之前的第一个测试是试图执行go_shell函数不知道密码,但溢出了主函数的返回地址,并将其指向go_shell的位置。据我了解,堆栈的划分如下:

[STACK] {Return_address}{EBP}{password_buffer(64)}...


因此,如果我将68个字节加上go_shell的地址存储在password_buffer中,它将覆盖返回地址并执行所需的功能。

>这里的问题是,我需要用76个字节的垃圾加上4个字节的地址填充缓冲区,以实际覆盖返回地址并将%eip指向go_shell。我不明白的是,这8个额外的字节是从哪里来的?

这是在注入74 A(0x41)+中断点地址if(!strcmp(password)之前的GDB输出,secret)):

[STACK] {4bytes (Location of go_shell)}{EBP(4 Bytes of junk)}{password_buffer(64)(64 bytes of junk)}...


并继续执行go_shell(在void go_shell(){处的断点)::

EIP现在指向最后的寄信人地址已被覆盖:

#1 楼

如果您看一下authorize()的反汇编,我相信您会发现编译器正在推动和恢复更多的寄存器,而不仅仅是EBP或对齐堆栈。我建议您在处理各种溢出时始终保持冷静。如果使用编译器和反编译器,则会隐藏许多细节。反汇编永远不会说谎,而无需借助动态分析就可以进行预测。当您刚开始学习时,我强烈建议您学习静态方法。

无论如何,无论是否还有更多的寄存器,堆栈金丝雀,堆栈对齐或其他功能,对authorize()的反汇编将给出您问题的答案。

作为参考,这是使用带有-O2的GCC 4.7.3的authorize()函数的反汇编。

push    ebx
sub     esp, 58h
lea     ebx, [esp+5Ch+password]
mov     [esp+5Ch+arg0], "Escriba la contrase"
call    _printf
mov     [esp+5Ch+arg0], ebx
call    _gets
mov     eax, secret
mov     [esp+5Ch+arg0], ebx
mov     [esp+5Ch+arg1], eax
call    _strcmp
test    eax, eax
setz    al
add     esp, 58h
movzx   eax, al
pop     ebx
retn


您会注意到它不使用push来移动参数,ebp未被用作堆栈帧,并且由于堆栈变化之和为0x60,因此编译器将堆栈对齐。返回值未对齐4,将ebx再按4,然后是sub esp,0x58导致0x60。

评论


很棒的解释,非常感谢。

– Nucklear
13-10-13在10:49

自从您回答以来,我一直试图弄清楚内存中发生了什么,但我不明白。这是在注入有效载荷pastebin.com/raw.php?i=ajCqxvGJ之前和之后的内存预览。您能提出最后一个解释吗?问候。

– Nucklear
13-10-14在14:22