摘要

這份技術報告深入分析了在 Zendesk Android 軟體開發套件 (SDK) 實作中發現的一個重大安全漏洞。該漏洞允許 0-click 大規模帳號接管,讓未經授權的人能夠存取所有受影響組織的 Zendesk 支援工單。這個缺陷源於一個可預測的 JSON Web Token (JWT) 生成機制,該機制結合了 hardcoded 的機密與連續的帳號識別碼。本報告探討了此漏洞的技術細節、其攻擊方法、根本原因和潛在的緩解策略。

驚!知名企業也中招?Zendesk Android SDK 0-Click 帳號接管漏洞深度解析 | 資訊安全新聞

1 簡介

行動應用程式的資安持續面臨重大挑戰,尤其是在第三方 SDK 處理認證和授權的方式上。本報告檢視了在 Zendesk Android SDK 中發現的一個重大漏洞,該漏洞允許攻擊者在不需任何使用者互動的情況下進行大規模帳號接管(通常稱為「0-click」攻擊)。該漏洞特別嚴重,因為它可能暴露使用 Zendesk 工單系統的眾多組織中敏感的客戶支援資料。

此安全缺陷存在於 SDK 內部,在 Android 應用程式中生成用於存取 Zendesk 支援功能的認證 tokens 的方式。與可能需要釣魚、社交工程或複雜攻擊鏈的傳統漏洞不同,這個弱點讓攻擊者能夠透過利用 Token 生成過程中可預測的元素,系統性地為任何使用者生成有效的認證 tokens。

2 漏洞分析

2.1 Token 生成機制

此漏洞的核心在於 Zendesk Android SDK 內部的 ZendeskHelper.g() 方法。這個方法實作了一個有缺陷的演算法來生成認證 tokens。

Token 生成程序遵循以下具體步驟:

graph LR A[Account ID] --> B[Base String Construction] C[Hardcoded Secret] --> B B --> D[SHA-1 Hashing] D --> E[Final Token Format]
  1. 基底字串建構 :該方法使用以下格式建立基底字串: REDACTED-{AccountID}-{HardcodedSecret}
  2. Hash 生成 :這個基底字串接著透過 SHA-1 hashing 進行處理
  3. 最終 Token 格式 :最終 Token 的格式為 {AccountID}_{SHA1Hash}

此實作中的關鍵缺陷是:

  • 使用一個在所有安裝和應用程式中都保持不變的 靜態 hardcoded secret ( 987sdasdlkjlakdjf )
  • 連續的帳號 IDs ( getRemoteId() ),可輕易被枚舉
  • Token 生成中沒有 特定裝置 特定時間 的元件

2.2 程式碼分析

這個有漏洞的方法實作如下:

  1. public final void g() {
  2. Person value = M.INSTANCE.a().o().getValue();
  3. if (value != null) {
  4. kotlin.jvm.internal.w wVar = kotlin.jvm.internal.w.f43427a;
  5. String str = String.format("REDACTED-%s-%s",
  6. Arrays.copyOf(new Object[]{
  7. Long.valueOf(value.getRemoteId()),
  8. Secrets.f39202a.k()}, 2));
  9. kotlin.jvm.internal.p.g(str, "format(...)");
  10. String str2 = String.format("%s_%s",
  11. Arrays.copyOf(new Object[]{
  12. Long.valueOf(value.getRemoteId()),
  13. r.b(str)}, 2));
  14. kotlin.jvm.internal.p.g(str2, "format(...)");
  15. JwtIdentity jwtIdentity = new JwtIdentity(str2);
  16. Zendesk zendesk2 = Zendesk.INSTANCE;
  17. zendesk2.setIdentity(jwtIdentity);
  18. Support.INSTANCE.init(zendesk2);
  19. }
  20. }

程式碼中提到的 r.b() 方法對輸入字串執行了 SHA-1 hashing:

  1. public static String b(String str) {
  2. try {
  3. MessageDigest instance = MessageDigest.getInstance("SHA-1");
  4. instance.reset();
  5. instance.update(str.getBytes("UTF-8"));
  6. byte[] digest = instance.digest();
  7. return a(digest);
  8. } catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
  9. e.printStackTrace();
  10. return "";
  11. }
  12. }

這個方法為 SHA-1 建立一個 MessageDigest 實例,重置它,使用輸入字串的 UTF-8 編碼位元組更新它,呼叫 digest() 產生 20-位元組的 SHA-1 hash,將其傳遞給一個輔助函式轉換為十六進制,並返回該十六進制字串。

2.3 認證流程

認證流程透過以下程序利用生成的 Token:

sequenceDiagram participant Client participant Server Client->>Server: POST /access/sdk/jwt with token Server->>Client: 200 OK with access_token Client->>Server: Requests with access_token Server->>Client: Access to all endpoints

客戶端應用程式會向 /access/sdk/jwt 發送一個 POST request,並在 request 本體中帶有生成的 Token:

  1. POST /access/sdk/jwt HTTP/2
  2. Host: REDACTED.zendesk.com
  3. Content-Type: application/json; charset=UTF-8
  4. {"user":{"token":"131070497_b28e1722087f8fbbde077fa1f372d7e51bbbc415"}}

伺服器會回應一個有效的 access_token ,該 token 授予對受害者 Zendesk 支援環境的完整存取權限:

  1. HTTP/2 200 OK
  2. Content-Type: application/json; charset=utf-8
  3. {"access_token":"Access_Token"}

3 攻擊方法

3.1 攻擊途徑

該漏洞可透過以下幾種攻擊途徑進行利用:

  1. 大規模帳號接管 :攻擊者可以透過重覆以帳號 ID 範圍來為任何使用者生成有效的認證 tokens。
  2. 零點擊攻擊 :無需使用者互動即可危害帳號。
  3. 完整支援存取 :成功攻擊後可存取所有 Zendesk 支援功能。

3.2 攻擊程序

系統化的攻擊程序包括:

flowchart TD A[Start with known account ID range] --> B[Generate potential tokens] B --> C[Test against Zendesk endpoint] C --> D{Valid token?} D -->|Yes| E[Gain access to account] D -->|No| F[Try next token] E --> G[Access support tickets and data] F --> B
  1. 偵察 :識別目標組織的 Zendesk 實例和帳號 ID 範圍。
  2. Token 生成 :使用可預測的演算法自動為所有可能的帳號 IDs 生成 tokens。
  3. 驗證 :針對 Zendesk 認證 endpoint 測試生成的 tokens。
  4. 存取 :使用有效的 tokens 存取支援資料和功能。

此程序可以透過簡單的腳本自動化,使攻擊者能夠有效率地危害數千個帳號。

3.3 影響評估

成功利用此漏洞,攻擊者可存取:

  • 包含敏感客戶通訊的完整工單歷史紀錄
  • 支援對話中的個人可識別資訊 (PII)
  • 內部公司通訊和支援程序
  • 客戶投訴模式和商業智慧資料
  • 在支援互動中冒充合法使用者的能力

4 根本原因分析

4.1 設計缺陷

此漏洞源於認證機制的數個設計缺陷:

  1. 可預測的 Token 生成 :Token 生成演算法依賴可預測的元件,而非加密安全的隨機值。
  2. Hardcoded 機密 :在所有安裝中使用相同的 secret 意味著危害一個安裝實際上危害了所有安裝。
  3. 缺乏速率限制 :認證 endpoint 沒有實施速率限制,允許攻擊者不受限制地測試大量的 tokens。
  4. 沒有 Token 過期 :生成的 tokens 無限期有效,增加了攻擊視窗。

4.2 實作失敗

具體的實作失敗包括:

  • 不安全的 secret 儲存 :hardcoded secret 以純文字形式儲存在應用程式程式碼中。
  • 使用已棄用的 hash 演算法 :SHA-1 被認為在加密上已失效,不適用於安全應用程式。
  • 缺乏環境綁定 :tokens 沒有綁定到特定的裝置、sessions 或時間段。

5 緩解策略

5.1 立即修復

為了修復此特定漏洞,建議採取以下措施:

  1. 替換 hardcoded secrets :為每個安裝使用獨特、高亂度的 secrets。
  2. 實施適當的 Token 設計 :使用隨機生成的 tokens,不帶有可預測的元件。
  3. 強制執行速率限制 :對認證 endpoints 實施嚴格的速率限制。
  4. 增加 Token 過期時間 :確保生成的 tokens 有較短的過期時間。

5.2 長期安全改進

為了實現全面的資安,應考慮以下改進:

  • 使用標準認證協定 :實施 OAuth 2.0、OpenID Connect 或其他標準認證協定,而非客製化解決方案。
  • 定期資安審計 :對 SDK 程式碼和認證機制進行定期資安審計。
  • 動態 secret 管理 :使用安全的 secret 管理系統,而不是 hardcoded 的值。
  • 更強的 Hash 演算法 :對於加密操作,使用 SHA-256 或更強的 hashing 演算法。

6 結論

Zendesk Android SDK 中的 0-click 大規模帳號接管漏洞證明了行動應用程式中安全認證機制的至關重要性。該缺陷源於使用 hardcoded secrets 和連續帳號 IDs 的可預測 Token 生成,使攻擊者能夠在無需受害者進行任何互動的情況下危害任何使用者的支援帳號。

這個案例研究突顯了行動應用程式資安的幾個更廣泛的教訓:

  1. 避免客製化認證解決方案 :客製化認證實作通常包含標準、經過良好測試的協定所能避免的漏洞。
  2. 永遠不要 hardcode secrets :hardcoded secrets 代表了單一故障點,可以危害整個生態系統。
  3. 假設枚舉是可能的 :設計系統時應假設攻擊者可以枚舉所有可能的值。
  4. 縱深防禦 :實施多層次的安全控制,以減輕任何單一漏洞的影響。

由於行動應用程式持續處理日益敏感的資料和功能,對所有元件(包括第三方 SDKs)進行徹底的資安測試對於防止此類重大漏洞至關重要。