Aliexpress LTE / 4G Stick hacking

Posted on Dec 18, 2022

I recently decided that i “need” an LTE USB stick to tinker around with, so i visited Aliexpress and bought one of the many available cheap devices.

Weeks later, i was the proud owner of this thing: Image alt Stick from the back

First impressions

After plugging the device in, it appears as an ‘Android’ device, which is - interesting.

$ lsusb |grep Android
Bus 001 Device 017: ID 05c6:9024 Qualcomm, Inc. Android

$ dmesg | grep usb
[20388.606115] usb 1-2.4: new high-speed USB device number 16 using xhci_hcd
[20388.684301] usb 1-2.4: New USB device found, idVendor=05c6, idProduct=9091, bcdDevice=ff.ff
[20388.684309] usb 1-2.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[20388.684311] usb 1-2.4: Product: Android
[20388.684313] usb 1-2.4: Manufacturer: Android
[20388.684314] usb 1-2.4: SerialNumber: 1234567890ABCDEF
[20425.758881] usb 1-2.4: USB disconnect, device number 16
[20425.935886] usb 1-2.4: new high-speed USB device number 17 using xhci_hcd
[20426.013250] usb 1-2.4: New USB device found, idVendor=05c6, idProduct=9024, bcdDevice=ff.ff
[20426.013264] usb 1-2.4: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[20426.013270] usb 1-2.4: Product: Android
[20426.013274] usb 1-2.4: Manufacturer: Android
[20426.013277] usb 1-2.4: SerialNumber: 1234567890ABCDEF
[20426.017802] rndis_host 1-2.4:1.0 usb0: register 'rndis_host' at usb-0000:00:14.0-2.4, RNDIS device, 02:0c:08:57:35:38
[20426.029337] rndis_host 1-2.4:1.0 enp0s20f0u2u4: renamed from usb0

As we can see in the dmesg output, the device also provides a ‘virtual’ ethernet interface, so let’s see if it likes us:

$ psa-dhcpc -ifname enp0s20f0u2u4 -default_route=false
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:04 unconfiguring interface
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:04 Sending DHCPDISCOVER broadcast
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:04   ==> waiting for valid reply until 2022-12-18T17:57:04+01:00
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:06 Accepting offer for IP 192.168.42.53 from server 192.168.42.129
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:06   ==> waiting for valid reply until 2022-12-18T17:48:06+01:00
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:07 Running ARPING for 192.168.42.53
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:07 Configuring interface to use IP 192.168.42.53/ffffff00 via <nil>
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:07 -> Lease is valid for 1h0m0s
psa-dhcpc[enp0s20f0u2u4] 2022/12/18 17:47:07 -> Renew will happen after 30m0s, must rebind after 52m30s
## Add a route to the IP printed on the back of the device (because we skipped the default route)
$ ip route add 192.168.43.1 dev enp0s20f0u2u4

Cool, so we got an IP and can… access the webinterface?

Webinterface

The webinterface continues to be underwhelming:

We can change the SSID and WIFI password (and the IMEI?!), but there is not option to turn the WIFI off (i don’t want to operate device as a hotspot) and nor can we change the announced DHCP range:

DHCP fail

Thats a bummer.

ADB

Since the device identifies itself as an ‘Android’ device, let’s just try adb:

$ adb shell
shell@msm8916_32_512:/ $ id
uid=2000(shell) gid=2000(shell) groups=1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=kernel
shell@msm8916_32_512:/ $ cat /proc/version
Linux version 3.10.28 (qwang@qwang) (gcc version 4.7 (GCC) ) #1 SMP PREEMPT Sun Jun 12 15:39:30 CST 2022
shell@msm8916_32_512:/ $ ls /data
opendir failed, Permission denied
1|shell@msm8916_32_512:/ $ getprop |grep fingerprint                           
[ro.build.fingerprint]: [qcom/msm8916_32_512/msm8916_32_512:4.4.4/KTU84P/eng.qwang.20220611:user/test-keys]

Ok, so this thing does indeed run Android (4.4.4 (aka KitKat) with Linux 3.10) which makes me want to disable the WIFI hotspot even more.

Unfortunately, we are not root, but maybe we can just ask nicely?

$ adb root
restarting adbd as root
$ adb shell
shell@msm8916_32_512:/ $ id
uid=2000(shell) gid=2000(shell) groups=1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats) context=kernel

Unfortunately not :-(

However: We know that the device implements a custom webserver, so maybe it has some hidden functions we can explore?

Checking out the webserver

Who listens on port 80 ?

We know that we can access the device via ‘192.168.43.1’ on Port 80 - what else is there?

$ nmap -v -sS 192.168.43.1
...
Nmap scan report for 192.168.43.1
Host is up (0.0060s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE
53/tcp   open  domain
80/tcp   open  http
8080/tcp open  http-proxy

Not too many surprises here: The device anwers / forwards DNS queries on Port 53 - and Port 8080 shows us the same webinterface as we see on Port 80 - so the real webserver is likely not running on port 80 - and digging around in /system/etc quickly confirms this:

cat /system/etc/httpproxy.conf
# lighttpd.conf
server.modules              = (
                                "mod_access",
                                "mod_accesslog",
#                               "mod_indexfile",
                                "mod_proxy",
#                                "mod_magnet",


                               )
server.document-root = "/data/www/"

$HTTP["host"] =="192.168.43.1"{

    $HTTP["url"] !~ "^(xion.ico|.*/static/.*)$" {
        proxy.server = ( "" => (( "host" => "192.168.43.1", "port" => 8080 )))
    }
}

Ok, i’ve seen worse. Unfortunately, we don’t have root access on the stick, so figuring out who listens on port 8080 needs some guessing.

…and what’s up with Port 8080?

Its always good to be polite, so let’s just ask the webserver on Port 8080:

$ telnet 192.168.43.1 8080
Trying 192.168.43.1...
Connected to 192.168.43.1.
Escape character is '^]'.
G
HTTP/1.1 400 Bad Request
Connection: close
Server: Jetty(8.y.z-SNAPSHOT)

Error: 400

Ok, the thing is built using Jetty - and likely an APK, so lets check whats installed:

$ adb shell pm -l
package:com.qualcomm.fastdormancy
package:com.qualcomm.timeservice
package:com.android.launcher
package:com.android.defcontainer
package:android
package:com.android.settings
package:com.android.externalstorage
package:com.qualcomm.wfd.service
package:com.android.phone
package:com.jetty2m.hello
package:com.android.proxyhandler
package:com.android.systemui
package:com.qualcomm.interfacepermissions
package:com.android.keychain
package:com.qualcomm.gsmtuneaway
package:com.android.inputdevices
package:com.qualcomm.qcom_qmi
package:com.android.packageinstaller
package:com.android.providers.telephony
package:com.dsi.ant.server
package:org.codeaurora.ims
package:com.svox.pico
package:com.qualcomm.qti.networksetting
package:com.qualcomm.shutdownlistner
package:com.android.providers.userdictionary
package:com.android.documentsui
package:com.android.wallpapercropper
package:com.android.sharedstoragebackup
package:com.android.location.fused
package:com.android.backupconfirm
package:com.android.providers.settings
package:com.android.vpndialogs
package:com.android.keyguard
package:com.android.provision
package:com.android.pacprocessor
package:com.disaplay.test.test
package:com.qualcomm.qcrilmsgtunnel
package:com.android.shell
package:com.android.certinstaller

Bingo: com.jetty2m.hello sounds like what we are looking for. Now, let’s fetch the APK:

$ adb shell ls /system/app/*etty*
/system/app/Jetty2m.apk
/system/app/Jetty2m.odex
$ adb pull /system/app/Jetty2m.apk

And here it is: Jetty2m.apk

What’s next?

So far we learned that the stick runs an (old) version of Android and that the Web-UI is (very likely) served by Jetty2m.apk. While we probably could just root the stick using one of the various kernel root exploits, it might be more fun to just look into what we can do with the mysterious Web-UI.

And that’s exaclty what we will do in the next article of this series!