crAPI靶场解

在线靶场http://crapi.apisec.ai/login
也可本地docker搭建
关卡列表在https://owasp.org/www-project-crapi/

我是本地搭建。先注册一个靶场账号,登录,访问8025端口,打开邮件把自己的车辆信息加入自己账号中(在127.0.0.1:8888/verify-vehicle接口)

image

添加自己的dashboard显示自己的车的信息:

image

Challenges

BOLA Vulnerabilities

Challenge 1 - Access details of another user’s vehicle

拿到其他人的车辆信息

访问Community页面

image

随便点一个评论同时抓包,截取响应包,把响应体信息暂存

image

{"id":"gCqDCgdSaVBCCeBF2VVa2V","title":"Title 3","content":"Hello world 3","author":{"nickname":"Robot","email":"robot001@example.com","vehicleid":"4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5","profile_pic_url":"","created_at":"2025-07-13T02:43:17.7Z"},"comments":[],"authorid":3,"CreatedAt":"2025-07-13T02:43:17.7Z"}

返回dashboard
点击下面的刷新位置,同时抓包

image

image

把/identity/api/v2/vehicle/6aff27ea-c20f-44d5-b388-ade8b9ddc8ea/location这个接口的location那堆编码改成刚刚获取的vehicleid,也就是4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5,然后重放
image

获得如下信息

{"carId":"4bae9968-ec7f-4de3-a3a0-ba1b2ab5e5e5","vehicleLocation":{"id":3,"latitude":"37.746880","longitude":"-84.301460"},"fullName":"Robot","email":"robot001@example.com"}

其中vehicleLocation字段是车的地点,算是敏感信息

Challenge 2 - Access mechanic reports of other users

这个挑战是关于访问其他用户提交的报告。

点击Contact Mechanic并抓包

image

第一个包没啥用
image

但后面还有一个类似的包,它的响应体是其他用户的报表的简陋信息
image

{"mechanics":[{"id":1,"mechanic_code":"TRAC_JHN","user":{"email":"jhon@example.com","number":""}},{"id":2,"mechanic_code":"TRAC_JME","user":{"email":"james@example.com","number":""}}],"previous_offset":null,"next_offset":null}

但是要获取的是详细信息,因此继续寻找切口。
我们自己提交一个报表并抓包
image

image

这里返回

{"response_from_mechanic_api":{"id":7,"sent":true,"report_link":"http://127.0.0.1:8888/workshop/api/mechanic/mechanic_report?report_id=7"},"status":200}

先访问这个http://127.0.0.1:8888/workshop/api/mechanic/mechanic_report?report_id=7
看看是什么
image

逆天没有jwt,我不是从这个网站跳转的吗为什么没有,那就自己加上去

image

可看到这次是详细的报表信息了,那么用自己的jwt能不能访问别人的报表呢,试试看
参数report_id改成1

image

可以,那就完成这关了,其他用户的只要改report_id即可

image

Broken User Authentication

Challenge 3 - Reset the password of a different user

挑战 3 - 重置其他用户的密码
官方提示:

  • 在 crAPI 上查找其他用户的电子邮件地址
  • 蛮力可能是答案。如果您遇到任何保护机制,请记住利用 REST API 的可预测特性来查找更多类似的 API 终端节点。

鼠标移到头像旁边的三角触发的那个按钮可以跳转reset-password接口http://127.0.0.1:8888/reset-password,这个我试过了没用

image

请求包没啥信息,响应体也没用
但后来退出登录了一下,发现还有个forgot-password接口:

image

不出意外就是打这里了
刚刚第一关获取了其他用户的邮箱,这里就拿robot001@example.com这个用户来开刀
image
image

用自己邮箱重置密码看看,退出登录点忘记密码

image

去http://crapi.apisec.ai:8025/ 看验证码

image

image

官方说需要爆破,那这里发现是4位验证码,刚好就可以爆破

输入目标邮箱改密码,然后跳转到输入otp和新密码的界面抓包
image

添加payload爆破

image
一开始爆破发现全部是失败的,后来仔细分析一下官方提示,说“REST API 的可预测特性”,最后猜测可能是api版本可以修改,把路径的v3改成v2再爆破

image
image

成功了。这里提一下如果一开始先用v3版本api爆破再用v2,会导致两者都失败。因为otp是一次一密码的意思,0000-9999再v3版本用过了,就全部失效,。最后就算在v2的api爆破对了依然会返回500错误。也不知道多久重置,所以这边是重建了容器再打一次这关的

最后用刚刚改的密码!Ss12341234登录目标账号
这里登录后界面一片空白,不知道为什么,重建容器打一次依然是这样

image

只有抓包界面登录才看到效果
image

但是清除本地token存储和cookie后又能正常回到登录界面,继续做题

Excessive Data Exposure

Challenge 4 - Find an API endpoint that leaks sensitive information of other users

进入Community界面抓包,一个个包看,看到如下包
image

/community/api/v2/community/posts/recent?limit=30&offset=0这个接口就是所谓的敏感信息泄露接口。

Challenge 5 - Find an API endpoint that leaks an internal property of a video

个人中心上传一个视频
image

image

返回视频id,视频名字和conversion_params以及视频二进制流数据,一般来说id不能泄露。

Rate Limiting

Challenge 6 - Perform a layer 7 DoS using ‘contact mechanic’ feature

填一个报表,抓包"repeat_request_if_failed":改成true,"number_of_repeats"改成999999999999999999999999999

image

image
检测到你Dos了就得了

BFLA

Challenge 7 - Delete a video of another user

改视频名字同时抓包,方法改成options,删掉请求体

image

方法改成DELETE

image

{"message":"This is an admin function. Try to access the admin API","status":403}
说明/identity/api/v2/user/videos/52这个接口用delete方法访问就是删除视频的,但是要管理员

把路径中的user改成admin即可删除

image

Mass Assignment

Challenge 8 - Get an item for free

随便购买一个物品,然后退款抓包,响应包如下:

image

image

image

返回商店界面点击Past Orders按钮抓响应包得到:

image

{"orders":[{"id":6,"user":{"email":"123456789@qq.com","number":"18977513210"},"product":{"id":1,"name":"Seat","price":"10.00","image_url":"images/seat.svg"},"quantity":1,"status":"return pending","transaction_id":"312a961c-d8c1-46f1-b25b-90f1b5bead09","created_on":"2025-07-13T10:44:55.918691"}],"next_offset":null,"previous_offset":null,"count":1}

可以看到product的id是1,订单id是6

进入Past Orders界面,对购买的商品点击Order Details按钮,在商品详情界面(/orders?order_id=6)刷新再抓包,一个个看,抓到如下包

image

控制台源码搜索status状态看看有多少种,只搜到'delivered'和'Return'
Return是可以退货的状态,推测returned是退货完成状态

image

把刚刚那个包方法改成PUT,添加Content-Type: application/json,重放,payload如下

{"product_id":1,"quantity":1,"status":"returned"}

返回包显示状态已经修改
image

回界面看看
image

余额变回100了

image

Challenge 9 - Increase your balance by $1,000 or more

再买个椅子

image

根据上一关的方法抓到/workshop/api/shop/orders/{id}这个接口的包。添加payload:{"product_id":2,"quantity":10,"status":"delivered"}
和Content-Type: application/json请求头,方法改成PUT重发

image

回去看看是否生效,看到订单种商品数量是10个了
image

再回到订单列表界面(/past-orders)点击Return

image

用上一关的方法退货

image

发现余额多了100

image

后来发现关卡要求增加1000,那就把商品数量改成100来打一次这关即可

image
image

image

Challenge 10 - Update internal video properties

After solving the “Get an item for free” challenge, be creative and find a way to get refunded for an item you never returned, but this time try to get a bigger refund.

尝试找到允许您更改视频内部属性的接口。
第五关发现了视频有conversion_params属性

image

用改接口改conversion_params为-v codec h265,成功修改
image

SSRF

Challenge 11 - Make crAPI send an HTTP call to “www.google.com” and return the HTTP response.

这里测试ssrf,因为谷歌要FQ,较难看到效果,用本地起个python服务器代替了。

写一个报表提交
image
抓包改mechanic_api字段为服务器地址

image

终端这边是有反应不过是400。。。

image

NoSQL Injection

Challenge 12 - Find a way to get free coupons without knowing the coupon code.

这靶场后端用了mongdb,去问deepseek常见mongdb的NoSQL注入漏洞,得到一个{"$ne": ""}的payload,如下

image

上网搜了下文章,下面这样也得
image

使用后可以获得75元
image

问了下AI,如下回答

  1. {"$ne": ""} 的原理

$ne 表示"不等于"(not equal)

  • 当查询 {字段: {"$ne": ""}} 时,MongoDB会返回该字段不等于空字符串的所有文档
  • 在MongoDB中:
  • 如果字段存在且值不为空字符串,就符合条件
  • 如果字段不存在,也会被包含(因为"不存在"当然不等于空字符串)
  • 只有字段存在且值为空字符串的文档会被排除

所以它几乎会返回所有文档,除非有文档该字段明确设置为空字符串

  1. {"$gt": ""} 的原理

$gt 表示"大于"(greater than)

  • 当查询 {字段: {"$gt": ""}} 时,MongoDB会返回该字段值大于空字符串的所有文档
  • 在MongoDB的字符串比较规则中:
  • 任何非空字符串都"大于"空字符串
  • 不存在的字段被视为"null",在比较中比空字符串"小"

所以它会返回:

  • 所有该字段存在且值为非空字符串的文档
  • 但会排除:字段不存在或值为空字符串的文档
  1. 为什么都能查"全部"?

在实际攻击中,它们都能返回大量数据的原因是:

  1. $ne 的情况
  • 大多数文档要么字段有值(不为空),要么字段不存在
  • 很少有文档会特地把字段设为空字符串
  • 所以返回结果几乎是全部
  1. $gt 的情况
  • 虽然理论上会排除字段不存在的情况
  • 但在实际应用中,关键字段(如密码)通常都存在
  • 所以对必填字段使用 {"$gt": ""} 也能返回几乎所有记录
  1. 简单示例

假设有个用户集合:

[
{username: "admin", password: "123456"}, // 有密码
{username: "guest", password: ""},// 密码为空
{username: "test"}// 无password字段
]
  • db.users.find({password: {"$ne": ""}}) 会返回:

  • admin用户(密码不为空)

  • test用户(无password字段,视为不等于空)

  • 不返回guest用户(密码为空)

  • db.users.find({password: {"$gt": ""}}) 会返回:

  • 只有admin用户(密码大于空字符串)

  • 不返回guest(等于空)和test(无字段)

  1. 在攻击中的应用

在登录绕过攻击中:

  • 攻击者提交 {"$ne": ""}{"$gt": ""} 作为密码
  • 系统查询变成 db.users.find({username: "admin", password: {"$ne": ""}})
  • 只要admin用户的密码不为空(通常都是这样),就能匹配成功
  • 从而绕过密码验证

Challenge 13 - Find a way to redeem a coupon that you have already claimed by modifying the database

后端有postgresdb数据库,这是关系型数据库
输入正确兑换码抓包
image

第二个包如下
image

这里有sql注入{"coupon_code":"0'or '1'='1","amount":75}也能查到兑换码。

image

Challenge 14 - Find an endpoint that does not perform authentication checks for a user.

/workshop/api/shop/orders/x这个接口越权查看其他用户订单

image

Challenge 15 - Find a way to forge valid JWT Tokens

/dashboard界面抓包,直到出现如下的包
image

解码jwt得到

{"alg":"RS256"}.{"sub":"123456789@qq.com","iat":1752418223,"exp":1753023023,"role":"user"}.GBAzt9zSS3hXUYnSLMSYDpuYKnyu1m8t_HfpopmzxHzFLWGZFZdxUMuxbpf4U4twlnvXHVCh4Rd8bjvkluX2W-MOV50stWOjMB773c-rytZtBrzOPo-VPh2eKQN4z1epawoJrvfrwCdRhXL29eHOJJh5qftCrpyHJa0_6h2ZxcVqzKfgnPg30uOaLHzpiy3U1vzgHm1U0CnxNjd2qFl9-5x96ZcEE2jXPcH0njDq3ZD5WsajvPzF4lXQDxJL-uhiS5_X9W9lwzayD85N-pBV-_pUDWmNP9kPhSpvtEglEsG-qTS9Y8X2va0FX_N_vS3KzMJzVDdgITXSSGZEA_v6Gw

Header:
{"alg":"none"}
Payload: {"sub":"123456789@qq.com","iat":1752418223,"exp":1753023023,"role":"user"}
算法改成{"alg":"none"}
去除签名部分构造无验证算法jwt,如下:

eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0NTY3ODlAcXEuY29tIiwiaWF0IjoxNzUyNDE4MjIzLCJleHAiOjE3NTMwMjMwMjMsInJvbGUiOiJhZG1pbiJ9.

image

绕过成功

« 2 secret challenges »

There are two more secret challenges in crAPI, that are pretty complex, and for now we don’t share details about them, except the fact they are really cool.

/workshop/api/shop/products接口可以查看商品信息
image

改POST方法可添加商品
image

image

这里只发现1个,另一个没找到

方法总结

  1. ID替换越权:在请求中修改资源ID(如车辆ID、订单ID)尝试访问他人数据,例如将 /api/vehicle/自己ID/location 中的ID替换为他人ID。
  2. 水平越权探测:用普通账号访问相同层级的其他用户专属接口(如 /api/user/123→/api/user/456)。
  3. 垂直越权探测:普通用户尝试访问管理员接口(如 /user/videos/ 改为 /admin/videos/)。
  4. JWT算法篡改:将JWT头部 alg:RS256 改为 alg:none 并删除签名绕过验证。
  5. JWT声明篡改:解码JWT后修改 role:userrole:admin 提权。
  6. OTP爆破:对密码重置验证码接口进行4-6位数字爆破(需注意API版本差异,如 /v3//v2/)。
  7. 旧版本API探测:主动降级API路径版本号(如 /v3/reset 改为 /v2/reset)测试未修复漏洞。
  8. 批量分配攻击:添加未公开参数(如 statusquantity)覆盖服务端状态实现非法操作。
  9. 状态机篡改:修改订单状态(如 return pendingreturned)绕过业务流程。
  10. 参数污染:篡改非用户输入参数(如视频的 conversion_params)触发内部错误或未授权操作。有的时候可以把参数值设置为空看看。
  11. NoSQL注入:使用MongoDB运算符(如 {"$ne":""})绕过逻辑校验获取资源。
  12. SQL注入:经典Payload(' or 1=1--)测试字符串型参数。
  13. 枚举未授权端点:直接访问相似功能路径(如 /shop/orders/自己ID/shop/orders/他人ID)。
  14. 错误信息挖掘:从500错误响应提取堆栈信息、数据库类型或敏感路径。
  15. 过度数据泄露分析:对比正常响应与业务需求,标记多余返回字段(如内部属性、他人邮箱)。
  16. SSRF触发点探测:查找接收URL参数的API(如报告提交接口),尝试指向外部服务(http://attacker.com)。
  17. 速率限制绕过:通过参数控制请求循环(如 repeat_request_if_failed=true&number_of_repeats=999999)。
  18. 优惠券逻辑绕过:空值/永真条件(0' or '1'='1)兑换未知或已使用优惠码。
  19. HTTP方法篡改:GET请求改为POST/PUT/DELETE测试危险方法(如OPTIONS探测可用方法)。
  20. 业务流逆向:分析正常流程(购买→退款)并尝试跳过/逆转步骤(未发货直接退款)。
  21. 公开信息收集:从社区、错误页等接口收集邮箱、车辆ID等敏感信息用于攻击链。
  22. 自动化ID爆破:对低熵值ID(UUID、短数字)使用Intruder批量测试越权。
  23. 文件元数据篡改:上传文件时修改隐藏属性(如视频时长、转码状态)触发未预期行为。
  24. 响应对比分析:对比他人资源与自身资源的响应差异(如社区帖子接口暴露额外字段)。
  25. 接口方法拓展:对返回对象详情的GET接口,尝试POST/PUT方法创建/修改资源。
posted @ 2025-07-14 10:22  积分别忘C  阅读(51)  评论(0)    收藏  举报