原文始发于shielder:Printing Fake Fiscal Receipts – An Italian Job p.2
TL;DR
The ItalRetail RistorAndro app installed on the SpiceT fiscal printer is affected by a pre-authentication remote arbitrary file write and an arbitrary app installation.
Moreover, the Android OS version installed is affected by two known vulnerabilities, namely CVE-2017-13156 (Janus), that allows to esclate the privileges to system, and CVE-2016-5195 (DirtyCOW) that allows to escalate the privileges to root in the vold SELinux context.
Rewind ⏮
In the first post we analyzed the fiscal unit and its local attack surface. We discovered how it’s possible for any installed Android app to abuse the fiscal features.
We also introduced the Android OS used by the SpiceT printer that makes up the remote attack surface which vulnerabilities we will now explore in detail one-by-one, and finally build a full exploit-chain.
Meet RistorAndro, the Order-Taking App ?
RistorAndro is a B4A framework based application installed by default on the SpiceT device. It has a very complex UI and it is used as a Point of Sale (PoS) in restaurants and bars.
The peculiarity of RistorAndro is that you can connect waiters’ smartphones with the WPalm app installed, on the same Wi-Fi as the SpiceT device and use the smartphones to take orders. This feature is implemented in a very very very very very … very strange fashion.
Basically, both RistorAndro and WPalm create an unauthenticated webserver on port 12312 and, through the /write
endpoint, exchange ZIP archives. These archives contain text files with the data to be trasmitted, being the clients’ orders, the receipts, information of the tables, etc. There are also other endpoints but not quite as interesting.
The /write
endpoint takes two multipart field:
matricola
that contains an integer.upfile
that contains the ZIP file content and its file name.
When receiving the files, the endpoint executes the following Java code:
|
|
Since the attacker controls both the matricola and the upfile parameters that are used in the File.Copy
method, they can exploit a path traversal resulting in an pre-authentication remote arbitrary file write scenario.
It’s worth reminding that the SpiceT uses Android 6, therefore there is no Scoped Storage and since RistorAndro has the WRITE_EXTERNAL_STORAGE
permission it’s possible to write anywhere in the whole /storage/emulated/0
mountpoint.
Auto-Updates Done Wrong
In order to be compliant with the ever changing electronic invoice XML format accepted by the Agenzia delle Entrate, RistorAndro had to implement an auto-update mechanism without the need of a technician action.
This auto-update feature steps follow:
- An APK is downloaded from the backend into the device external storage, specifically inside the
/storage/emulated/0/Updater/WebApi/
folder. - When the RistorAndro app restarts, the download folder is scanned for APK files with common names.
- The APK name is checked to contain a higher version number than the one installed.
- The APK is finally installed on the device.
As an example here are some valid filenames:
- InvioFE_31337.apk
- Spooler_31337.apk
- wPalmRistorAndro_31337.apk
- RistorAndro_31337.apk
By placing an APK file in the /storage/emulated/0/Updater/WebApi/
folder and naming it correctly it is possible to force the SpiceT device to install it automatically – an arbitrary app installation.
Faking APK signatures with Janus
CVE-2017-13156 (Janus) is a High severity Escalation of Privileges (EoP) vulnerability in the Android v1 APK Signature Scheme that allows to bypass the signature verification during the installation of an APK file.
To understand how it works we need to look into the APK file format and how the Android OS parses it.
Since APK files are just ZIP archives, they are parsed the same way. The ZIP format works by using two main structures:
- a series of local file descriptors that define the various compressed files and their metadata.
- a central directory that references every file descriptor contained inside the archive by a relative offset from the start of the archive.
The central directory is placed at the end of the archive, this means that ZIP and APK files can start with arbitrary content before the first referenced local file descriptor.
When installing an application, if the v1 APK Signature Scheme is used, the signature of every file referenced inside the ZIP central directory is verified. If a file is not referenced by the central directory it will not be verified, but it should also not be used. Right?
By analyzing the Android Runtime (ART), we can learn that it’s possible to load and execute both DEX and ZIP files, based on the file’s Magic bytes.
In this way, a valid APK file starting with DEX bytecode will sucessfully pass the APK signature verification and at the same time load and execute the unsigned DEX payload.
This is the reason this vulnerability was named after Janus, the Roman god of duality.
Notice that it’s only possible to inject DEX / Java classes, the Android manifest cannot be modified in any way.
By forging an APK using the Janus vulnerability, we could overwrite an already installed application and abuse their privileges. The most privileged applications on the SpiceT run as system
or shell
, so that’s the highest level of privileges we could achieve.
?-ing to Root
By looking at the kernel version and the patch level we can notice that the device is vulnerable to one of the most famous Linux EoP, DirtyCOW.
DirtyCOW exploits a race condition in the implementation of the copy-on-write mechanism in the kernel’s memory-management subsystem. In this way it is possible to turn a read-only mapping of a file into a writable one.
If you want a deep-dive technical explaination of CVE-2016-5195 I suggest to check out this article by Chao-tic.
While on most of the operating systems being able to write files as root is a game over itself, on Android there is SEAndroid (SELinux), which is a dream breaker. In fact we are limited by a context and its rules.
Creating the Ultimate SpiceT Exploit ⛓⛓⛓™️
It’s time to gather together all the vulnerabilities we’ve found:
- We connect to the same LAN of the SpiceT.
- We craft a backdoored and Janus-powered version of
com.fsl.ethernet
a pre-installed app which has thesystem
privileges and gives us persistence since it is executed at every reboot. - We upload the backdoored app in the APK installation folder with a proper filename using the remote file upload and the path traversal.
- We upload a broken RistorAndro config file abusing the same vulnerability, which will cause a crash and a restart of RistorAndro, triggering the malicious app installation.
- We now have a Remote Code Execution as
system
. - We use Janus once more on
com.android.shell
to downgrade our privileges toshell
as we need write and execute access into/data/local/tmp/
folder to run DirtyCOW over some binaries in/usr/bin/
. - We trigger
com.android.shell
through thecom.android.shell/.BugreportReceiver
Intent viacom.fsl.ethernet
as we needsystem
privileges to invoke it. - We run DirtyCOW over
/usr/bin/ntfsfix
which will be automatically called fromroot
in thevold
SEAndroid context if anNTFS
USB key is connected to the SpiceT. - We wait an
NTFS
USB key to be connected to the SpiceT. - We get a
root
shell in thevold
context. - We mount an
ext4
image over the/sbin
mountpoint and replace therequest-key
binary. - We call the
request-key
syscall, finally gaining theinit
SEAndroid context for theroot
user!
Finally, our chain as seen from Logcat:
03-18 11:24:16.937 517 1696 I ActivityManager: START u0 {dat=file:///storage/emulated/0/Android/data/cassandraRisto.ital/files/shared/Spooler_99999999.apk cmp=com.android.packageinstaller/.InstallAppProgress (has extras)} from uid 10016 on display 0
03-18 11:24:17.024 2895 2895 W InstallAppProgress: Replacing package:com.fsl.ethernet
03-18 11:24:17.299 517 535 I ActivityManager: Displayed com.android.packageinstaller/.InstallAppProgress: +320ms
03-18 11:24:27.716 2909 2920 D DefContainer: Copying /storage/emulated/0/Android/data/cassandraRisto.ital/files/shared/Spooler_99999999.apk to base.apk
03-18 11:24:28.173 517 530 I ActivityManager: Force stopping com.fsl.ethernet appid=1000 user=-1: replace sys pkg
03-18 11:24:28.177 517 541 W PackageManager: Trying to update system app code path from /data/app/com.fsl.ethernet-2 to /data/app/com.fsl.ethernet-1
03-18 11:24:28.179 517 541 W PackageManager: Code path for com.fsl.ethernet changing from /data/app/com.fsl.ethernet-2 to /data/app/com.fsl.ethernet-1
03-18 11:24:28.179 517 541 W PackageManager: Resource path for com.fsl.ethernet changing from /data/app/com.fsl.ethernet-2 to /data/app/com.fsl.ethernet-1
03-18 11:24:28.187 517 530 I ActivityManager: Force stopping com.fsl.ethernet appid=1000 user=-1: replace pkg
[...]
03-18 11:24:34.142 517 1634 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.fsl.ethernet cmp=com.fsl.ethernet/.MainActivity} from uid 10016 on display 0
03-18 11:24:34.355 517 1903 I ActivityManager: Start proc 3060:com.fsl.ethernet/1000 for activity com.fsl.ethernet/.MainActivity
03-18 11:24:35.125 3060 3060 D EthernetManager: We will operate iface:eth0
03-18 11:24:35.133 3060 3060 D EthernetManager: Static IP =172.16.42.110
03-18 11:24:35.139 3060 3060 E EthernetManager: Exception in setting Static IP
03-18 11:24:35.178 3060 3060 D EthernetManager: set ip manually com.fsl.ethernet.EthernetDevInfo@d86fab9
03-18 11:24:35.687 517 535 I ActivityManager: Displayed com.fsl.ethernet/.MainActivity: +1s410ms (total +6s327ms)
[...]
03-18 11:24:37.733 3078 3078 D AndroidRuntime: Calling main entry com.android.commands.am.Am
03-18 11:24:37.767 517 530 I ActivityManager: Start proc 3091:com.android.shell/2000 for broadcast com.android.shell/.BugreportReceiver
03-18 11:24:38.145 3110 3110 I cowhard : Welcome to cowhard! (/data/local/tmp/dirtycow)
03-18 11:24:38.145 3110 3110 I cowhard : ------------
03-18 11:24:38.145 3110 3110 I cowhard : uid 2000
03-18 11:24:38.146 3110 3110 I cowhard : Current selinux context: u:r:shell:s0
03-18 11:24:38.146 3110 3110 I exploit : warning: new file size (9900) and file old size (165668) differ
03-18 11:24:38.147 3110 3110 I exploit : [*] mmap 0xb6d51000
03-18 11:24:38.147 3110 3110 I exploit : [*] exploit (patch)
03-18 11:24:38.156 3110 3110 I exploit : [*] currently 0xb6d51000=464c457f
03-18 11:24:38.156 3110 3111 I exploit : [*] madvise = 0xb6d51000 165668
03-18 11:24:51.012 3110 3111 I exploit : [*] madvise = 0 1048576
[...]
03-18 11:41:38.513 517 586 D UsbHostManager: Added device UsbDevice[mName=/dev/bus/usb/002/002,mVendorId=2316,mProductId=4096,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=General,mProductName=USB Flash Disk,mVersion=2.16,mSerialNumber=0356115070002381,mConfigurations=[
03-18 11:41:40.056 197 208 V vold : /dev/block/vold/public:8:1: UUID="101E2D2D1E2D0CF2" LABEL="ShielderUSB" TYPE="ntfs"
03-18 11:41:40.089 197 208 E Vold : Filesystem check failed (not a FAT filesystem)
03-18 11:41:40.125 197 208 I Vold : Ntfs filesystem existed
03-18 11:41:40.152 3647 3647 I cowhard : Welcome to cowhard! (/system/bin/ntfsfix)
03-18 11:41:40.152 3647 3647 I cowhard : ------------
03-18 11:41:40.152 3647 3647 I cowhard : uid 0
03-18 11:41:40.153 3647 3647 I cowhard : Current selinux context: u:r:vold:s0
03-18 11:41:40.153 3647 3647 I cowhard : Executing '/data/media/0/cmd.sh'
03-18 11:41:40.298 197 208 I Vold : Ntfs filesystem check completed OK
03-18 11:41:40.299 197 208 W Vold : The SD card is world-writable because the 'persist.sampling_profiler' system property is set to '1'.
03-18 11:41:40.299 197 208 D Vold : Mounting ntfs with options:utf8,uid=1023,gid=1023,fmask=0,dmask=0,shortname=mixed,nodev,nosuid,dirsync,noexec
[...]
03-18 11:41:53.611 3780 3780 I cowhard : Welcome to cowhard! (/sbin/request-key)
03-18 11:41:53.611 3780 3780 I cowhard : ------------
03-18 11:41:53.611 3780 3780 I cowhard : uid 0
03-18 11:41:53.611 3780 3780 I cowhard : Current selinux context: u:r:init:s0
Conclusions
A good mix of spaghetti code and outdated software granted us a nice ride which led to an old fashioned remote root exploit chain.
Ah – in case you forgot along the journey – the first 4 steps of our exploit chain are enough to takeover the fiscal printer remotely and print fiscal receipts / tamper the DGFE, the other steps to get a root
shell were just a pure exploitation exercise ?️♂️.
Pitch ?️
If you are developing your own Android-powered product, make sure to do a proper threat modelling and design to prevent similar vulnerabilities to exist and make their way into production.
Get in touch with us to work arm-in-arm with your development team to make security part of the life cycle of your products: https://www.shielder.com/contacts/