On most unrooted, stock, Android phones, enabling tethering will run a "Provisioning Check" with your wireless provider to ensure that your data plan allows tethering. This post documents Tethr, a way to bypass the provisioning check on Android devices prior to version 7.1.2. After discovering this method I reported it to the Android bug bounty fixing the issue and receiving CVE-2017-0554.
The ability to tether is controlled by your device's
build.prop file, usually located at
/system/build.prop. The default is to require the provisioning check before enabling tethering, but it can by bypassed by adding the following line:
Unrooted devices can't edit
/system/build.prop, so it is up to the ROM manufacturer to set this property. Some devices like the Google Nexus 6P default to
net.tethering.noprovisioning=true. But this is an exception and not the norm. The Google Nexus 5X, Pixel, and Pixel 2 all perform provisioning checks.
When enabling tethering on Android, the OS will first do a provisioning check with the carrier to determine if the user’s plan allows tethering. If allowed, tethering is enabled, otherwise a message is displayed to the user.
If there is no sim card inserted then no provisioning check is performed, and tethering is allowed. Additionally, if tethering is enabled on a phone with no sim (not that this scenario would be of much use) and a SIM is then inserted, tethering is disabled as it should be.
However, if tethering is enabled while the radio is connecting, no provisioning check will be performed, and tethering will remain enabled after the radio connection is established.
The first issue I discovered is the ability for a user-installed application on a stock OS to reset the cellular modem. The second issue is the lack of a provisioning check once the cellular modem has finished reconnecting.
Together these bugs allow the Android OS to operate as if
net.tethering.noprovisioning=true were specified in
build.prop, even if it is not.
Before I dive into the details, observe the following video which demonstrates when enabling tethering in the Android system UI, a provisioning check is performed and tethering is not allowed. Then when using the Tethr demo app, the signal meter loses signal when the modem is resetting, and then tethering is successfully enabled.
Source code: github.com/lanrat/tethr
Compiled APK: Tethr.apk
Issue 1: Resetting radio via reflection
Java Reflection in Android can be used to allow application code to make calls to undocumented, or hidden APIs. This is not officially supported, and often strongly discouraged. It can however, allow app developers to do unsupported things, or in this case, bypass a permission check.
Resetting the cellular radio is performed in CellRefresh.java by calling
CellRefresh does a number of things to reset the cellular connection on most Android versions, but on Android 6+ the following reflections are used:
Older Android versions use the following reflections:
getSystemService(Context.CONNECTIVITY_SERVICE).mService.setRadio(); getSystemService(Context.TELEPHONY_SERVICE).getITelephony().disableDataConnectivity() getSystemService(Context.TELEPHONY_SERVICE).getITelephony().enableDataConnectivity()
The methods used should require the application to have the system or privileged permissions.
Issue 2: Tethering provisioning check race condition
In order to exploit the race condition and bypass the tethering provisioning check an Android
AccessibilityService are used to programmatically enable the desired tethering mode at exactly the right time.
First the network is reset as described above. While the reset is being performed the
PhoneStateListener (TetherPhoneStateListener.java) listens for when the cellular network is down and then starts the system’s settings tethering dialog where the
AccessibilityService (TetherAccessibilityService.java) finds the correct UI switch and toggles it.
PhoneStateListener are not strictly required for this exploit. The user can manually toggle tethering at the correct time to have the same effect. Automating the process with an
AccessibilityService makes the process easier to reproduce.
The provisioning check should happen each time the radio is reset when tethering is enabled in addition to checking when enabling tethering.
Tested Wireless Carriers:
Tested phones (all stock, locked bootloaders, OEM OS):
- Nexus 5X running Android 6.0.1
- Nexus 5X running Android 7.0.0
- Nexus 5X running Android 7.1.1
- Samsung Galaxy S7 running Android 6.0.1
Untested but should also work on:
- Pixel (XL)
- Other non-nexus devices that perform a provisioning check
As the Nexus 6P already has
net.tethering.noprovisioning=true set in its stock
build.prop there is no need for this exploit.
After submitting to the Android Bug Bounty Google created two patches for Android 7.1.2 which fixed this issue.
Patch 1: Added permission check for setCellInfoListRate
Patch 2: Fixed the logic for tethering provisioning re-evaluation
After fixing the issue Google was kind enough to give me a Pixel XL for reporting it to their bug bounty.