华硕路由器应用中的 Android 应用组件导出不当
华硕路由器 Android 应用(com.asus.aihome)中捆绑的一个组件被声明为 android:exported="true",且未设置 android:permission。同一设备上的任何其他应用,即使不持有任何权限,也可以向其发送精心构造的 Intent,并让华硕路由器应用以用户名义打开攻击者控制的 URI。
于 2026-03-17 向华硕报告。华硕已修复,并作为 CVE-2026-12960 于 2026-07-03 发布。
由 Show Up Show Out Security 的 Sedric Louissaint 发现并报告。
| CVE | CVE-2026-12960 |
| 产品 | 适用于 Android 的华硕路由器应用(com.asus.aihome) |
| 受影响版本 | ≤ 1.0.0.9.71 |
| 修复版本 | 1.0.0.9.74 |
| 弱点类型 | CWE-926:Android 应用组件导出不当 |
| CVSS 4.0 | 6.0 中危 CVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:P/VC:N/VI:N/VA:N/SC:H/SI:N/SA:N |
| CNA | ASUS |
| 厂商公告 | https://www.asus.com/security-advisory/ |
| 测试环境 | com.asus.aihome 1.0.0.9.71(Google Play),Android 11 模拟器 |
该应用捆绑了百度推送 SDK,其中声明了:
<service
android:name="com.baidu.android.pushservice.CommandService"
android:exported="true" />
由于没有 android:permission 属性,Android 的组件级访问控制根本不会生效。任何已安装的应用都可以对其调用 startService()。
CommandService.onStartCommand() 会从传入 Intent 的 public_msg extra 中读取一个 PublicMsg Parcelable,并将其传递给 handlePrivateNotification(),后者会根据攻击者提供的 mOpenType 字段进行分支处理:
mOpenType | 行为 |
|---|---|
1 | 使用攻击者的 mUrl 调用 startActivity(ACTION_VIEW, Uri.parse(mUrl)) |
发送方和载荷均未经过验证。由于最终启动的 activity 来自华硕应用自身的 UID 和进程,因此屏幕上出现的任何内容看起来都像是来自用户刚刚打开的可信路由器管理应用。
实际上,这个问题并非百度所独有。SDK 提供了导出的组件,宿主应用继承了它,而被借用的是宿主应用的身份。
PublicMsg Parcelable。mOpenType = 1,并将 mUrl 设置为攻击者控制的 URI。CommandService。无需任何权限,不会抛出 SecurityException。onStartCommand() → handlePrivateNotification()。startActivity(ACTION_VIEW, Uri.parse(mUrl)) 从华硕应用的进程中触发。PublicMsg 的字段顺序是从反编译的 smali 中的 writeToParcel 恢复出来的,并在 poc/PublicMsg.java 中重新实现:
String mMsgId, mAppId, mTitle, mDescription, mUrl, mPkgName
int mPkgVercode, mNotificationBuilder, mNotificationBasicStyle
int mOpenType, mUserConfirm
String mCustomContent, mPkgContent
int mAdvertiseStyle
String mAdvertiseSmallIconUrl, mAdvertiseLargeIconUrl, mAdvertiseClickUrl,
mAdvertiseBigPictureUrl, mAdvertiseBigPictureClickUrl, mAdvertiseDownloadClickUrl
字段顺序弄错了,Parcel 反序列化出来的就是垃圾数据;顺序正确,服务就会像对待百度自家推送基础设施发送的消息一样接受它。
六种 URI scheme、一个导出的服务、零权限:
media/poc_commandservice.mp4 是完整的 31 秒运行过程:构建、安装、触发,以及模拟器依次响应每个载荷。
| 攻击 1:钓鱼页面 | 攻击 2:短信编辑器 | 攻击 3:拨号器 | PoC 应用的权限 |
|---|---|---|---|
![]() | ![]() | ![]() |
最后一张截图就是全部论据。未请求任何权限。 刚刚通过华硕路由器应用驱动了六种操作的这个应用,对用户什么要求都没有。
需要 ADB、JDK、Android SDK 构建工具(aapt2、d8/dx、zipalign、apksigner)以及 platforms;android-30。
./poc/poc_commandservice_exploit.sh <device_serial>
该脚本分三个阶段运行:
com.android.shell 发送三个 CommandService 动作,并报告是否有任何动作被拒绝。受权限保护的服务会在这里抛出 SecurityException。而此服务接受了全部三个。exploit_commandservice.apk,然后安装。如果你不想走这一步,poc/ 目录下有一份预构建副本。在存在漏洞的设备上,阶段 1 的输出:
[ACCESS] passthrough.notification.CLICK → service accepted intent
[ACCESS] privatenotification.CLICK → service accepted intent
[ACCESS] privatenotification.DELETE → service accepted intent
Phase 1 result: 3/3 actions accepted without permission check
还有来自 dumpsys activity services 的 ServiceRecord,其中记录了调用者:
intent={act=com.baidu.android.pushservice.action.privatenotification.CLICK
cmp=com.asus.aihome/com.baidu.android.pushservice.CommandService}
recentCallingPackage=com.android.shell
startRequested=true callStart=true
阶段 3,来自真实的无特权应用(而非 shell):
W PoCExploit: [OK] attack1_url_open → com.asus.aihome/com.baidu.android.pushservice.CommandService
W PoCExploit: [OK] attack2_sms_compose → ...
(all 6 succeed)
startService() 返回了目标 ComponentName 而不是 null,并且没有抛出 SecurityException。Android 解析了 Intent 并将其投递。
poc/
poc_commandservice_exploit.sh Automated three-phase reproduction script
ExploitCommandService.java PoC activity + broadcast receiver source
PublicMsg.java Parcelable matching the target's field layout
AndroidManifest_PoC.xml PoC manifest (note: no <uses-permission>)
exploit_commandservice.apk Prebuilt PoC, debug-signed, zero permissions
media/
poc_commandservice.mp4 Full PoC recording
attack1-browser.jpg Stills pulled from the recording
attack2-sms.jpg
attack3-dialer.jpg
poc-app-no-permissions.jpg
vendor/
asus-security-advisory-submission.pdf The report as filed with ASUS
PoC 的出站 URL 是一个 Burp Collaborator 子域,用于确认导航确实发生。它早已失效。如果你要复现,请换成你自己的。
对厂商而言,修复只需一个属性:
<service
android:name="com.baidu.android.pushservice.CommandService"
android:exported="false" />
或者,如果该组件确实需要从外部可达,请用 signature 保护级别的权限加以限制,并在对反序列化后的 PublicMsg 采取行动之前对其进行验证。华硕在 1.0.0.9.74 中发布了修复。
对用户而言:请将华硕路由器应用更新到 1.0.0.9.74 或更高版本。
对于所有发布 Android 应用的其他开发者:请审计你的第三方 SDK 导出了什么。对你的正式发布构建运行 aapt dump xmltree base.apk AndroidManifest.xml,并阅读其中每一个 exported="true"。你发布的 manifest 是你合并的所有 manifest 的总和,你要为它的全部内容负责。
| 日期 | 事件 |
|---|---|
| 2026-03-17 | 向华硕报告,附上 PoC APK、自动化脚本和视频 |
| 2026-06-23 | 华硕作为 CNA 为 CVE-2026-12960 保留编号 |
| 2026-07-03 | CVE 发布;1.0.0.9.74 中提供修复 |
| 2026-07-25 | 公开技术文章和 PoC 发布 |
本文在协调披露和厂商修复之后发布,仅供防御和教育用途。该 PoC 针对已修复的版本,除了启动 URI 之外不会做任何事。请不要在非你所有或未经书面授权测试的设备上运行它。
2 | 先执行 Intent.parseUri(mPkgContent, 0),再 startActivity() / sendBroadcast() |
| # | Scheme | 结果 |
|---|
| 1 | https: | 浏览器打开攻击者控制的钓鱼页面 |
| 2 | sms: | 短信编辑器预填社会工程学消息 |
| 3 | tel: | 拨号器预填攻击者控制的号码 |
| 4 | mailto: | 邮件编辑器预填内容以窃取凭据 |
| 5 | market: | 跳转 Play Store 以安装恶意软件 |
| 6 | geo: | 地图跳转到假冒的服务中心 |
![]() |