Aliexpress LTE / 4G Stick hacking - Part 2

Posted on Dec 21, 2022

In my previous post, we started to play a little bit with a cheap LTE stick from Aliexpress.

We identified the APK responsible for serving the limited Web-UI and keen eyes might have spotted something unusual:


So this APK claims to be signed by Google (well, ‘Android’) which seems odd. Well, not really:

$ adb shell getprop
msm8916_32_512-user 4.4.4 KTU84P eng.qwang.20220611 test-keys

Did you notice something? Yes: test-keys.

What are test-keys?

Android OTA packages are signed using a ‘platform key’ which should usually be kept secret.

If you try to update a package, Android will check if the update has been signed with the same key and - if not - reject the update. For convenience, AOSP comes with a set of test-keys which are (semi automatically) used to sign OTA packages during development. Developers are supposed to provide their own keys for releases, but seems like the manufacturer of this device couldn’t be bothered to do so.

Well, that’s handy for us because - if my theory is correct - we can just grab the AOSP test keys and install a patched update. All without requiring any special privileges.

Fetching the test keys

First, we need to clone one of the AOSP repos and convert the platform test keys to something that can be used by jarsigner:

$ git clone
$ cd build/target/product/security/
$ openssl pkcs8 -inform DER -nocrypt -in platform.pk8 -out platform.pem
$ openssl pkcs12 -export -in platform.x509.pem -inkey platform.pem -out platform.p12 -password pass:android -name testkey
$ keytool -importkeystore -deststorepass android -destkeystore platform.keystore -srckeystore platform.p12 -srcstoretype PKCS12 -srcstorepass android
$ mv platform.keystore /tmp/LTE

Modifying the APK

We are now the proud owner of some test keys. Lets see if we can use them to sign an update.

First, we need to install APKTool which we will use to decompile and repackaging.

Unpacking the APK

This is as simple as running:

$ java -jar apktool.jar d Jetty2m.apk -o JETTY

Then we use Gimp to modify ./assets/jetty/img/header.jpg and change the APK version number in apktool.yml:

$ git diff
diff --git a/JETTY/apktool.yml b/JETTY/apktool.yml
index 83e4507..b5d4226 100644
--- a/JETTY/apktool.yml
+++ b/JETTY/apktool.yml
@@ -95,4 +95,4 @@ usesFramework:
 version: 2.7.0
-  versionCode: '1'
-  versionName: '1.0'
+  versionCode: '2'
+  versionName: '2.0'

Repackage and sign

Repackaging the APK is done via:

$ java -jar apktool.jar b -o unsigned.apk JETTY

But before we can install our ‘update’, we should zipalign and (of course!) sign it:

$ zipalign -v 4 unsigned.apk aligned.apk
$ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ./platform.keystore  aligned.apk testkey

Installing the update

$ adb install -r aligned.apk 
Performing Push Install
aligned.apk: 1 file pushed, 0 skipped. 1962.0 MB/s (684517 bytes in 0.000s)
	pkg: /data/local/tmp/aligned.apk

Well, that already looks pretty good. But does it work?

Let’s find out by adb rebooting the device and checking:


Yay! Not only did Android accept our homegrown update, it also happily executed it.

This means that it will also be possible to modify the java bytecode of the package and hence execute our own code, something we will do in the next article of this serie.