競態條件(Race Condition)發生在兩個流程同時根據某個初始條件競爭執行,但條件在過程中已被另一方改變——兩者卻都依舊完成。

經典銀行轉帳例子#

  1. 你帳戶有 $500,想全數轉給朋友
  2. 用手機 App 送出 $500 的轉帳請求
  3. 過 10 秒還在處理,你不耐煩,改用筆電登入——看到餘額仍是 $500,再次送出 $500 轉帳
  4. 兩個請求在幾秒內陸續完成
  5. 你的帳戶 $0,朋友卻說收到 $1,000

真實銀行有嚴格交易鎖定不會這樣,但這個故事說明核心:「檢查條件」與「執行動作」之間有時間差,期間若條件被改變,兩個流程都會以舊條件走完。

Web 應用中的成因#

HTTP 請求看似瞬間完成,但伺服器需要:

  • 重新驗證身份
  • 載入資料、跑業務邏輯
  • 寫回資料庫

任何「先查再寫」的流程,在「查詢」與「寫入」之間都可能被另一個請求插入。

案例 1:Accepting a HackerOne Invite Multiple Times#

漏洞描述#

HackerOne 邀請函的設計:每個邀請是一條唯一 token URL,理論上只能被使用一次。流程大概是:

  1. 在資料庫查找 token
  2. 套用業務邏輯,把帳號加入專案
  3. 標記 token 已使用

本書作者用三個帳號(A、B、C)測試:A 建立專案邀請 B,他在兩個瀏覽器分別以 B 和 C 登入並打開同一封邀請;把兩個瀏覽器的「Accept」按鈕排在螢幕同位置,幾乎同時按下

Figure 15-1: 兩個堆疊的瀏覽器視窗顯示同一份 HackerOne 邀請

第一次失敗、第二次成功——兩個帳號都用同一封邀請加入專案。

Takeaways#

簡單的競態條件可以靠手動同步點擊測試(按鈕對齊、極速雙擊)。複雜流程則需自動化工具——詳見下一個案例。

案例 2:Exceeding Keybase Invitation Limits#

漏洞描述#

Keybase 限制每個註冊使用者只能送 3 張邀請。研究者 Josip Franjković 推測流程:

  1. 收到邀請請求
  2. 檢查資料庫該使用者剩餘邀請數
  3. 產生 token、寄信
  4. 扣減邀請數

他用 Burp Intruder 同時送出多個邀請請求,繞過 3 張限制送出 7 張邀請。Keybase 後來用 lock(鎖機制)限制資源並發存取。

Takeaways#

Burp Intruder 可以指定 payload 列表(多個 Email)並在同一時間送出多個請求。任何「有上限」的功能(試用配額、註冊次數、優惠券領取)都值得試。

案例 3:HackerOne Payments Race Condition#

  • 難度:低
  • 回報日期:2017-04-12
  • 獎金:$1,000

漏洞描述#

HackerOne 把同一天的多筆獎金合併成一次 PayPal 付款。流程使用背景工作(background job)

  • 收 HTTP 請求 → 把支付任務丟進佇列 → 背景批次跑

研究者 Jigar Thakkar 發現:若兩位 HackerOne 使用者註冊同一個 PayPal Email,HackerOne 會合併獎金、丟一筆給該 Email。但若有人在「合併好」與「背景送出」之間改成另一個 PayPal Email,背景工作會把這筆款項送到新地址——而原 Email 也仍會收到(依照當時實作觀察)。

Time of Check vs Time of Use(TOCTOU)」——條件檢查與條件使用之間的時差,是背景工作型競態的核心。

Takeaways#

看到網站「動作完成後過一陣子才執行」就是背景工作的訊號。試著在「丟進佇列」與「執行」之間改變條件,看新條件是否會被使用。

案例 4:Shopify Partners Race Condition#

背景#

Shopify Partners 平台讓開發者向商店申請協作存取。Email 驗證流程:

  • Shopify 寄一封驗證信
  • 點擊內含 URL 後,伺服器把 Email 標記為已驗證

研究者 Tanner Emek 從 @uzsunny 的舊報告中得知:當兩個 partner 帳號用同一 Email 對同一商店申請存取時,Shopify 程式會自動把店家既有的員工帳號轉為協作者帳號——可能不需店家確認。

漏洞描述#

Emek 結合競態條件,把 Email 換成他不擁有的 cache@hackerone.com

  1. 用自己的 Email 註冊 partner,收到驗證信但不立刻點
  2. 在 Partners 平台改 Email 為 cache@hackerone.com,用 Burp 攔截改 Email 的請求
  3. 點驗證連結,用 Burp 攔截驗證請求
  4. 用 Burp 幾乎同時送出兩個請求

伺服器處理時,把驗證流程套用到新 Email 上——成功讓 cache@hackerone.com 在 Shopify 系統中被驗證為他擁有。再用 @uzsunny 的轉換漏洞,無需店家同意即可存取任何該 Email 對應的 Shopify 商店。

Shopify 修復方式:對帳號紀錄加 lock;強制協作者請求須由店家同意。

Takeaways#

  • 修復過的漏洞要回頭再試——同一塊功能可能藏著新的入口
  • 任何「驗證系統」都是測試重點,特別是「Email/手機號變更 + 驗證 token」的組合

章末總結#

  • 競態條件出現在「先查條件 → 再做動作」的流程,且查與做之間有時間差
  • 高 CP 值的測試場景:
    • 一次性使用的 token(邀請、優惠券、密碼重設)
    • 有配額/上限的功能
    • 背景工作(付款、Email、訊息推播)
    • 帳號驗證、Email 變更
  • 工具:Burp Intruder 一次送出多個請求、自寫腳本控制毫秒級併發
  • 修復後的功能仍要回頭測——新的程式碼可能是新的攻擊面