This is our second of the now not quite biweekly updates we want to give you on the progress of our work on the tweasel project, where we want to build a web app that detects privacy violations in mobile apps. The last weeks, we did a lot of work to iron out bugs and improve the stability of the tools and had some people test the toolchain as a whole. We also met up with a local data protection authority shortly after the last update and took home some todos, which we have been working on.
Appstraction
#Appstraction is an abstraction layer for common instrumentation functions on Android and iOS. It allows you to install, uninstall, start, stop apps and configure their permissions, as well as manage device settings like emulator snapshots, clipboard, proxy, and certificates. Appstraction can also be used for purposes other than mobile privacy.
- We finished most of the work we were talking about last time:
- I investigated pymobiledevice3 and liked it a lot. It packs quite a lot more features than
libimobiledevice
, has a really simple python API as well as a CLI that, usefully, outputs JSON and best of all, is distributed viapip
, meaning we can depend on it via autopy, a library Benjamin developed so that we can manage our python dependencies from JavaScript. It is also cross-platform, so we don’t have to worry about that anymore. Naturally, we switched, removing another host dependency the user has to manage. - We also wanted to streamline the setup of the on-device dependencies like frida on iOS, just as we already do on Android. This is really easy on a jailbroken device, since the popular package managers for jailbreaks, like Cydia or Sileo, are based on
apt
. Because of that, all I needed to do was to write a simple script to run via SSH which adds the necessary sources toapt
and installs the software we need present on the device. - I also looked into a frida bug, which prevented it to enable the
frida-server
service during install on iOS devices. It turns out that the reason is a faulty configuration I was able to fix in the setup step I just implemented.
- I investigated pymobiledevice3 and liked it a lot. It packs quite a lot more features than
- Then, we also found some problems when we presented our tools to the DPO’s experts, so we started fixing them:
- We learned that on vanilla Android devices rooted debugging is not available and so
adb root
(on which we had been relying) didn’t work. Until then we only tested on emulators or devices we rooted ourselves which were running LineageOS. To work around that problem, I rewrote ouradb shell
commands to execute them withsu 0 -c
, which is provided by Magisk. Extremely annoyingly, Android decided to use a non-POSIX-compliantsu
binary in their emulators, so this took quite a lot more work than I initially expected. - To run an already installed app on a device, we needed an app or bundle ID. Finding such an ID of an app, though, is actually not a trivial task, especially for non-expert users. On Android, you can sometimes get that information in the “App Info” or in the Play Store URL:
https://play.google.com/store/apps/details?id=<app id>
. On iOS however, the easiest way is to interface with the App Store API via ipatool. So, Benjamin wrote a simpleplatform.listApps()
method, which we can use to present users all currently available runnable apps to choose from, without the need to find an app ID. - Another problem, that was really bad UX and even tripped us up all the time, was
frida
failing if more than one device is connected at a time. While, eventually, we want to support multiple devices, we now fail inensureDevice()
if more than one device is connected. For that I needed to implement a platform-agnosticlistDevices()
utility function.
- We learned that on vanilla Android devices rooted debugging is not available and so
- There is this annoying thing on iOS, which requires you to interact with the CA trust settings manually at least once before the certificate is automatically accepted from our script. I wanted to automate that away and started investigating and stumbled upon device supervision, which allows for installing CAs without any user interaction, there is only one catch: You are supposed to reset your device to set up supervision. I wanted to find a better way:
- It turns out, that supervision is actually only managed in a single configuration file (
/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles/Library/ConfigurationProfiles/CloudConfigurationDetails.plist
) that conveniently just contains anIsSupervised
flag you need to set totrue
. Bam, device supervised. But how does one authenticate to the device now? - After I supervised one of my devices using the “Apple Configurator” software, I saw there is also a
SupervisorHostCertificates
array in the configuration file, if you do the supervision properly. That just contains an unsigned DER-encoded X.509 SSL certificate that matches up to a private key on the host machine I supervised the device with. So I implemented a way to generate the certificate and place it in the config file. - To enable the supervision, the device needs to restart. To preserve the jailbreak, we restart using
launchctl userspace reboot
, but this also cuts off the SSH connection and locks the device again. Since the app automation only works with an unlocked device, we need to recover from that. To remedy, I implemented another method to unlock the device by hijacking the “Assistive Touch” feature on iOS. On Android,adb
can just send a lock button signal viaadb shell keyevent 26
. - The implementation lives in
ios._internal.ensureSupervision()
right now and has not been merged, yet, since we don’t consider it a priority. - Finally, the configuration profiles can be installed through a
lockdownd
service via one of the many interfaces iOS exposes via USB. To avoid user interaction, though, one needs to authenticate the session using theEscalate
request, which requires you to signChallenge
bytes using the private key we have set up for supervision. In go-ios this was already implemented, but we just switched to pymobiledevice3, so I implemented it there as well.
- It turns out, that supervision is actually only managed in a single configuration file (
- Right now, to use SSH-based features on iOS devices, you need to provide the IP address of the device — contrary to how it works on Android. To get rid of this asymmetry, I started work on SSH connections via USB port forwarding, which can be done using
pymobiledevice3 usbmux forward <host port> <device port>
.- Adjacent to that, I wanted to simplify the setup on iOS to a minimum. Our current standard jailbreak palera1n enables a
dropbear
SSH server right on the first startup, but after bootstrapping in the palera1n loader app the login stops working. So, we require our users to install OpenSSH manually via Sileo. This enables an SSH server on the local port 22, which we can log into as the usermobile
. Sinceroot
can not log in without a password being set up, I needed to adapt our current code to run commands viasudo
. This reduces the manual setup on iOS to the currently possible minimum.
- Adjacent to that, I wanted to simplify the setup on iOS to a minimum. Our current standard jailbreak palera1n enables a
Certificate pinning bypass
#In order to record the traffic of apps, we inject ourselves in the connections the app makes to servers. If the connection is encrypted, we have a certificate authority we control that we trusted in the operating system. Some apps, however, use additional security measures to check for such injections. They check if the certificate used for transaction is the specific certificate they expect. To circumvent that, certificate pinning bypasses try to disable or change the app behavior so that the app accepts all certificates, including ours.
- We were a bit worried our certificate pinning bypass might not be working well, crash too many apps and also were unhappy with the UX of objection, because it left us with a running process we didn’t quite know when to close. Because of that Benjamin started an investigation.
- To check whether the certificate pinning works, we look for TLS errors in the
mitmproxy
log. We were already logging those events in cyanoacrylate, but we weren’t exposing those in the results, so Benjamin changed that. - With this change in place, Benjamin ran an experiment in which he captured the traffic and cert pinning errors of 1000 popular apps from the Play Store. He found that our favorite alternative to
objection
, httptoolkit’s certificate pinning bypass script, generated significantly fewer crashes, while both are very effective at disabling certificate pinning (about 80% of cert pinning related error were solved by both scripts). - With the data from the analysis, Benjamin then went through the apps with errors one by one to find one for which it made sense to write a new bypass. He did that for the O2 app. The new bypass has already been merged into the httptoolkit script.
- From the investigation, we also concluded that it makes sense to replace
objection
, which Benjamin started to do.
- To check whether the certificate pinning works, we look for TLS errors in the
Everything else
#- We want to publish the data we gathered in analyses like the certificate pinning experiment publicly, so other people can start research on it without having to run complicated experimentation setups. For that, Benjamin started work on a public database for request data. We want to keep it simple and just host it using Datasette. This database is also supposed to interface with trackers.tweasel.org to provide example data dynamically.
- We just started work on
docs.tweasel.org
, where we want to document the host and device setup in detail, write usage tutorials for the tools and provide background information.
on
licensed under: Creative Commons Attribution 4.0 International License Tweasel update #2: Stability, simplicity and some data