
CVE-2026-12960 - Improper Export of Android Application Components in the ASUS Router app (com.asus.aihome). PoC, exploit APK, video, and vendor report. Fixed in 1.0.0.9.74.
Improper Export of Android Application Components in the ASUS Router App
A component bundled in the ASUS Router Android app (com.asus.aihome) was declared
android:exported="true" with no android:permission. Any other app on the same
device, holding zero permissions, could send it a crafted Intent and make the ASUS
Router app open an attacker-controlled URI on the user's behalf.
Reported to ASUS on 2026-03-17. Fixed by ASUS and published as CVE-2026-12960 on 2026-07-03.
Discovered and reported by Sedric Louissaint of Show Up Show Out Security.
| CVE | CVE-2026-12960 |
| Product | ASUS Router App for Android (com.asus.aihome) |
| Affected | ≤ 1.0.0.9.71 |
| Fixed in | 1.0.0.9.74 |
| Weakness | CWE-926: Improper Export of Android Application Components |
| CVSS 4.0 | 6.0 Medium 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 |
| Vendor advisory | https://www.asus.com/security-advisory/ |
| Tested on | com.asus.aihome 1.0.0.9.71 (Google Play), Android 11 emulator |
The app bundles the Baidu Push SDK, which declares:
<service
android:name="com.baidu.android.pushservice.CommandService"
android:exported="true" />
No android:permission attribute, so Android's component-level access control never
runs. Any installed app can call startService() against it.
CommandService.onStartCommand() reads a PublicMsg Parcelable out of the incoming
Intent's public_msg extra and passes it to handlePrivateNotification(), which
branches on the attacker-supplied mOpenType field:
mOpenType | Behaviour |
|---|---|
1 | startActivity(ACTION_VIEW, Uri.parse(mUrl)) with the attacker's mUrl |
2 |
Neither the sender nor the payload is validated. Because the resulting activity is launched from the ASUS app's own UID and process, whatever appears on screen appears to have come from the trusted router-management app the user just opened.
Nothing about this is Baidu-specific in effect. The SDK ships the exported component, the host app inherits it, and the host app's identity is what gets borrowed.
PublicMsg Parcelable matching the target's field layout.mOpenType = 1 and mUrl to an attacker-controlled URI.CommandService. No permission required, no SecurityException.onStartCommand() → handlePrivateNotification().startActivity(ACTION_VIEW, Uri.parse(mUrl)) fires from the ASUS app's process.The PublicMsg field order, recovered from writeToParcel in the decompiled smali and
reimplemented in 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
Get the order wrong and the Parcel unmarshals into garbage. Get it right and the service accepts the message as if Baidu's own push infrastructure sent it.
Six URI schemes, one exported service, zero permissions:
media/poc_commandservice.mp4 is the full 31-second
run: build, install, fire, and the emulator reacting to each payload in turn.
| Attack 1: phishing page | Attack 2: SMS composer | Attack 3: dialer | The PoC app's permissions |
|---|---|---|---|
![]() | ![]() |
That last screenshot is the whole argument. No permissions requested. The app that just drove six actions through the ASUS Router app asked the user for nothing at all.
Requires ADB, a JDK, and the Android SDK build tools (aapt2, d8/dx, zipalign,
apksigner) plus platforms;android-30.
./poc/poc_commandservice_exploit.sh <device_serial>
The script runs three phases:
CommandService actions from com.android.shell
and reports whether any are rejected. A permission-protected service throws
SecurityException here. This one accepted all three.exploit_commandservice.apk, and
installs it. A prebuilt copy is in poc/ if you would rather skip this.Phase 1 output on a vulnerable device:
[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
And the ServiceRecord from dumpsys activity services, which records the caller:
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
Phase 3, from a real unprivileged app rather than the 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() returned the target ComponentName instead of null, and no
SecurityException was thrown. Android resolved the Intent and delivered it.
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
The PoC's outbound URL is a Burp Collaborator subdomain that was used to confirm the navigation actually happened. It is long dead. Swap in your own if you are reproducing.
For the vendor, the fix is one attribute:
<service
android:name="com.baidu.android.pushservice.CommandService"
android:exported="false" />
Or, if the component genuinely has to be reachable from outside, gate it with a
signature-protection-level permission and validate the deserialised PublicMsg
before acting on it. ASUS shipped the fix in 1.0.0.9.74.
For users: update the ASUS Router app to 1.0.0.9.74 or later.
For everyone else shipping Android apps: audit what your third-party SDKs export.
Run aapt dump xmltree base.apk AndroidManifest.xml against your own release build
and read every exported="true" in it. The manifest you ship is the sum of every
manifest you merged, and you own all of it.
| Date | Event |
|---|---|
| 2026-03-17 | Reported to ASUS with PoC APK, automated script, and video |
| 2026-06-23 | CVE-2026-12960 reserved by ASUS as CNA |
| 2026-07-03 | CVE published; fix available in 1.0.0.9.74 |
| 2026-07-25 | Public write-up and PoC released |
Published after coordinated disclosure and a vendor fix, for defensive and educational use. The PoC targets a patched version and does nothing but launch URIs. Do not run it against devices you do not own or have written authorisation to test.
Intent.parseUri(mPkgContent, 0) then startActivity() / sendBroadcast() |
| # | Scheme | Result |
|---|
| 1 | https: | Browser opens an attacker-controlled phishing page |
| 2 | sms: | SMS composer pre-filled with a social-engineering message |
| 3 | tel: | Dialer pre-filled with an attacker-controlled number |
| 4 | mailto: | Email composer pre-filled to exfiltrate credentials |
| 5 | market: | Play Store redirect for a malware install |
| 6 | geo: | Maps redirect to a fake service centre |
![]() |
![]() |