#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