diff --git a/README.md b/README.md new file mode 100644 index 0000000..3203106 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Android Java Deserialization Vulnerability Tester + +## About + +This project includes Android apps that are intended as a tool to test and create Proof of Concept (PoC) exploits for Java deserialization vulnerabilities in Android. It is based on [ysoserial](https://github.com/frohoff/ysoserial) by frohoff, but targeting the Android platform. + +This project was developed by Jan Girlich (@vollkorn) from modzero AG (@mod0). + +## Background + +Java deserialization vulnerabilities are a long known and [well researched](https://github.com/GrrrDog/Java-Deserialization-Cheat-Sheet) topic. Essentially, there is a high risk involved when deserializing untrusted, serialized Java objects. This usually leads to code execution in the context of the attacked application while deserializing malicious objects. + +On Android this becomes even more problematic because of the way how data exchange between apps is handled. Intents are messages sent between Android apps to enable communication between apps. These intents can carry extra data like strings, integers, or serialized objects. Unfortunately, when any part of intent's extra data is accessed all the extra data is unpacked. This means that any serialized Java object is deserialized as soon as any extra data of an intent is used. + +## Vulnerability + +Any app, which + +1. receives an intent and accesses any of its extra data and +1. has any Java class exploitable through a Java deserialization loaded + +is vulnerable against Java deserialization attacks on Android. + +In other words, except for using vulnerable dependencies (such as CommonsCollection6) and using regular functionality of Android (getting extras from an exported Intent), the app does not need to have any other security weakness. + +It's remarkable how such a security-critical operation is discussed [without](https://stackoverflow.com/questions/2139134/how-to-send-an-object-from-one-android-activity-to-another-using-intents) [discussing](https://stackoverflow.com/questions/4233873/how-do-i-get-extra-data-from-intent-on-android) [security](https://stackoverflow.com/questions/2736389/how-to-pass-an-object-from-one-activity-to-another-on-android), not even in the [official documentation](https://developer.android.com/reference/android/content/Intent.html#getExtras()). + +## Contents of this repository + +This repository consist of two Android apps: + +* The [attacker app](deserialization_sender/), which creates a payload and sends the malicious intent: [deserialization_sender](deserialization_sender/) +* The [vulnerable demo app](deserialization_receiver/), which is vulnerable to the CommonsCollection exploit: [deserialization_receiver](deserialization_receiver/) + +For a demonstration, open the vulnerable demo app, switch to the attacker app and use the intent target "ch.modzero.intent_receiver.deserialize.pwn". + +## Usage + +1. Checkout, open in Android Studio and compile the [attacker app](deserialization_sender/) folder. +1. Install the resulting apk on a phone or emulator where you also install the app you want to test for deserialization vulnerabilities (or the [vulnerable demo app](deserialization_receiver/)). +1. Make sure your potentially vulnerable app is running. +1. Put the name of an intent of the potentially vulnerable app listens to into the input field on the top of the main activity (or "ch.modzero.intent_receiver.deserialize.pwn" for the [vulnerable demo app](deserialization_receiver/)). +1. Hit the send button on the bottom right corner. + +The app will send all known payloads via the above entered intent. If any payload is successfully executed, the victim app will send back an intent to the attacker app to signal that it was executed. If such a payload's intent is received, the corresponding checkbox in the main activity of the attacker app gets checked. + + +## Status + +For this app it was attempted to port all known Java deserialization exploits from ysoserial to Android. But due to limitations in the Java reflection API and other APIs missing on Android, it was only possible to port one payload so far. You can compare it with the [list of payloads](https://github.com/frohoff/ysoserial#usage) in ysoserial. + +If an app accesses any intent's extra data and includes the vulnerable java library on the right hand side of the table, it is exploitable by the payload named in the left column. The app does not even need to use any functionality of the vulnerable library. Just having the classes loaded in the Java Virtual Machine is enough. + +| Name | Vulnerable Java Library | +| ---- | ----------------------- | +| CommonsCollection6 | commons-collections:3.1 | + +## Contribution + +This app is missing more working payloads to test with. Help in porting more payloads from ysoserial or adding new ones is welcome. diff --git a/apks/deserialization_receiver.apk b/apks/deserialization_receiver.apk new file mode 100644 index 0000000..b413e85 Binary files /dev/null and b/apks/deserialization_receiver.apk differ diff --git a/apks/deserialization_sender.apk b/apks/deserialization_sender.apk new file mode 100644 index 0000000..bb9f959 Binary files /dev/null and b/apks/deserialization_sender.apk differ diff --git a/deserialization_receiver/app/.gitignore b/deserialization_receiver/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/deserialization_receiver/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/deserialization_receiver/app/app.iml b/deserialization_receiver/app/app.iml new file mode 100644 index 0000000..b0585a0 --- /dev/null +++ b/deserialization_receiver/app/app.iml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deserialization_receiver/app/build.gradle b/deserialization_receiver/app/build.gradle new file mode 100644 index 0000000..5bfe3ff --- /dev/null +++ b/deserialization_receiver/app/build.gradle @@ -0,0 +1,32 @@ +apply plugin: 'com.android.application' + +android { + compileSdkVersion 26 + buildToolsVersion "26.0.2" + defaultConfig { + applicationId "ch.modzero.intent_receiver" + minSdkVersion 17 + targetSdkVersion 25 + versionCode 1 + versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } +} + +dependencies { + compile fileTree(include: ['*.jar'], dir: 'libs') + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile 'com.android.support:appcompat-v7:25.3.1' + compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha7' + compile 'com.android.support:design:25.3.1' + testCompile 'junit:junit:4.12' + compile 'commons-collections:commons-collections:3.2.1' +} diff --git a/deserialization_receiver/app/intent_receiver.apk b/deserialization_receiver/app/intent_receiver.apk new file mode 100644 index 0000000..5c3b87e Binary files /dev/null and b/deserialization_receiver/app/intent_receiver.apk differ diff --git a/deserialization_receiver/app/proguard-rules.pro b/deserialization_receiver/app/proguard-rules.pro new file mode 100644 index 0000000..36d26ac --- /dev/null +++ b/deserialization_receiver/app/proguard-rules.pro @@ -0,0 +1,25 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /home/work/Android/Sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/deserialization_receiver/app/src/main/AndroidManifest.xml b/deserialization_receiver/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..df7014a --- /dev/null +++ b/deserialization_receiver/app/src/main/AndroidManifest.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/deserialization_receiver/app/src/main/ic_launcher-web.png b/deserialization_receiver/app/src/main/ic_launcher-web.png new file mode 100644 index 0000000..316e8ac Binary files /dev/null and b/deserialization_receiver/app/src/main/ic_launcher-web.png differ diff --git a/deserialization_receiver/app/src/main/ic_launcher_round-web.png b/deserialization_receiver/app/src/main/ic_launcher_round-web.png new file mode 100644 index 0000000..316e8ac Binary files /dev/null and b/deserialization_receiver/app/src/main/ic_launcher_round-web.png differ diff --git a/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/MainActivity.java b/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/MainActivity.java new file mode 100644 index 0000000..84e7218 --- /dev/null +++ b/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/MainActivity.java @@ -0,0 +1,63 @@ +package ch.modzero.deserialization_demo; + +import android.content.Context; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.design.widget.Snackbar; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.View; +import android.view.Menu; +import android.view.MenuItem; + +import java.io.FileOutputStream; + +public class MainActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + + FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) + .setAction("Action", null).show(); + } + }); + + try { + FileOutputStream outputStream = openFileOutput("secret.txt", Context.MODE_PRIVATE); + outputStream.write("5ecret!\n".getBytes()); + outputStream.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + + //noinspection SimplifiableIfStatement + if (id == R.id.action_settings) { + return true; + } + + return super.onOptionsItemSelected(item); + } +} diff --git a/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/SomeObject.java b/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/SomeObject.java new file mode 100644 index 0000000..92bbb92 --- /dev/null +++ b/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/SomeObject.java @@ -0,0 +1,17 @@ +package ch.modzero.deserialization_demo; + +import java.io.Serializable; + +/** + * Created by work on 15.06.17. + */ + +class SomeObject implements Serializable { + private String name; + public SomeObject (final String name) { + this.name = name; + } + public String getName() { + return this.name; + } +} diff --git a/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/VulnerableReceiver.java b/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/VulnerableReceiver.java new file mode 100644 index 0000000..f999b2f --- /dev/null +++ b/deserialization_receiver/app/src/main/java/ch/modzero/deserialization_demo/VulnerableReceiver.java @@ -0,0 +1,34 @@ +package ch.modzero.deserialization_demo; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; + +/** + * Created by work on 15.06.17. + */ + +public class VulnerableReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + String app_name = "IntentReceiver"; + Log.d(app_name, "received an intent"); + Bundle extras = intent.getExtras(); + if (extras != null) { + Log.d(app_name, "intent has extra data"); + for (String extra : extras.keySet()) { + Log.d(app_name, "processing extra " + extra); + if (extra.equals("decoy")) { + Log.d(app_name, "sucessfully deserialized " + intent.getStringExtra(extra)); + } +// if (extra.equals("Encryption")) { +// HashSet deserialized = (HashSet)intent.getSerializableExtra(extra); +// Log.d(app_name, deserialized.getClass().getName()); +// Log.d(app_name, "sucessfully deserialized exploit"); +// } + } + } + } +} diff --git a/deserialization_receiver/app/src/main/res/layout/activity_main.xml b/deserialization_receiver/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..e340106 --- /dev/null +++ b/deserialization_receiver/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/deserialization_receiver/app/src/main/res/layout/content_main.xml b/deserialization_receiver/app/src/main/res/layout/content_main.xml new file mode 100644 index 0000000..e67c015 --- /dev/null +++ b/deserialization_receiver/app/src/main/res/layout/content_main.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/deserialization_receiver/app/src/main/res/menu/menu_main.xml b/deserialization_receiver/app/src/main/res/menu/menu_main.xml new file mode 100644 index 0000000..b87c709 --- /dev/null +++ b/deserialization_receiver/app/src/main/res/menu/menu_main.xml @@ -0,0 +1,10 @@ + + + diff --git a/deserialization_receiver/app/src/main/res/mipmap-hdpi/ic_launcher.png b/deserialization_receiver/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..a070c87 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/deserialization_receiver/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..a070c87 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-mdpi/ic_launcher.png b/deserialization_receiver/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..0b46218 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/deserialization_receiver/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..0b46218 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/deserialization_receiver/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..a74969d Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/deserialization_receiver/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..a74969d Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/deserialization_receiver/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..2ffb0c6 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/deserialization_receiver/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..2ffb0c6 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/deserialization_receiver/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..8cfdc26 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/deserialization_receiver/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/deserialization_receiver/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8cfdc26 Binary files /dev/null and b/deserialization_receiver/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/deserialization_receiver/app/src/main/res/values/colors.xml b/deserialization_receiver/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..3ab3e9c --- /dev/null +++ b/deserialization_receiver/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #3F51B5 + #303F9F + #FF4081 + diff --git a/deserialization_receiver/app/src/main/res/values/dimens.xml b/deserialization_receiver/app/src/main/res/values/dimens.xml new file mode 100644 index 0000000..59a0b0c --- /dev/null +++ b/deserialization_receiver/app/src/main/res/values/dimens.xml @@ -0,0 +1,3 @@ + + 16dp + diff --git a/deserialization_receiver/app/src/main/res/values/strings.xml b/deserialization_receiver/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..f0e020e --- /dev/null +++ b/deserialization_receiver/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + mod0 Receiver + Settings + diff --git a/deserialization_receiver/app/src/main/res/values/styles.xml b/deserialization_receiver/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..545b9c6 --- /dev/null +++ b/deserialization_receiver/app/src/main/res/values/styles.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + +