Burp Web Academy API Testing
0x01. PortSwigger Web Security Academy
PortSwigger Web Security Academy 是 Burp Suite 官方推出的免费 Web 安全学习靶场,在学习 Web 安全知识的同时,还可以练习 Burp Suite 的实战技能。
本篇文章讲解 Web Security Academy 之中的 API Testing。
0x02. API Testing
2.1 Lab: Exploiting an API endpoint using documentation
To solve the lab, find the exposed API documentation and delete
carlos
. You can log in to your own account using the following credentials:wiener:peter
.
登陆后,在个人页面发现了如下代码:
1 | <form class='login-form' name='email-change-form' onsubmit='changeEmail(this, event)' action='/api/user'> |
有两个路径值得注意:
/api/user
/resources/js/api/changeEmail.js
此外,在 /resources/js/api/changeEmail.js
中 changeEmail
的定义如下:
1 | const changeEmail = (form, e) => { |
访问 /api/user/wiener
,返回如下信息:
1 | {"username":"wiener","email":"wiener@normal-user.net"} |
访问 /api/user
,返回如下信息:
1 | {"error":"Malformed URL: expecting an identifier"} |
访问 /api
,返回如下信息:
Verb | Endpoint | Parameters | Response |
---|---|---|---|
GET | /user/[username: String] | { } | 200 OK, User |
DELETE | /user/[username: String] | { } | 200 OK, Result |
PATCH | /user/[username: String] | {“email”: String} | 200 OK, User |
所以,可以构造如下请求删除用户:
1 | DELETE /api/user/carlos |
返回信息:
1 | 200 OK |
2.2 Lab: Exploiting server-side parameter pollution in a query string
To solve the lab, log in as the
administrator
and deletecarlos
.
登陆页面点击找回密码,发现加载了 /static/js/forgotPassword.js
,关键代码如下:
1 | forgotPwdReady(() => { |
这里支持一个名为 reset-token
的参数,如果检测到,则重定向时将参数名修改为 reset_token
,注意二者的区别。题目提示服务端参数污染,需要测试一下这个参数的行为。实际上,经过测试并没有什么有用的线索。
尝试提交用户名找回密码,POST 请求发送如下参数:
1 | csrf=G3cTq9CZ9OqRuvsGC8A4NAY4aGYjxl0H&username=administrator |
测试:
username=xxx
返回Invalid username.
username=administrator&test=666
正常返回username=administrator%26test=666
返回Parameter is not supported.
- 这里
%26
即&
,说明test
也被后端处理了
- 这里
username=administrator#
返回Field not specified.
- 这里
#
把后续参数屏蔽了(URL 中的#
),提示缺少Field
- 这里
username=administrator%26field=666
返回Invalid field.
- 确实存在
field
字段,但是值还不确定,可以使用 Burp Intruder 进行爆破 - 在 Burp Intruder 中,Payload Options 选择
Add from list ...
,添加 Server-side variable names 参数列表
- 确实存在
发现 email
和 username
都是有效的 field
字段值:
1 | csrf=G3cTq9CZ9OqRuvsGC8A4NAY4aGYjxl0H&username=administrator%26field=email |
回到最前面,尝试使用 field=reset_token
,结果如下:
1 | csrf=G3cTq9CZ9OqRuvsGC8A4NAY4aGYjxl0H&username=administrator%26field=reset_token |
拿到了重置密码的 Token,直接重置 administrator
的密码,随后删除 carlos
用户即可。
2.3 Lab: Finding and exploiting an unused API endpoint
To solve the lab, exploit a hidden API endpoint to buy a Lightweight l33t Leather Jacket. You can log in to your own account using the following credentials:
wiener:peter
.
进入商品页面,会请求 /resources/js/api/productPrice.js
,关键代码:
1 | const loadPricing = (productId) => { |
进一步请求 /api/products/1/price
,返回价格信息:
1 | GET /api/products/1/price HTTP/2 |
根据题目的提示,可能需要更改 HTTP Method,那么先看看 HTTP Method 都有哪些?
Method | Safe | Idempotent | Cacheable |
---|---|---|---|
GET | Yes | Yes | Yes |
HEAD | Yes | Yes | Yes |
OPTIONS | Yes | Yes | No |
TRACE | Yes | Yes | No |
PUT | No | Yes | No |
DELETE | No | Yes | No |
POST | No | No | Conditional |
PATCH | No | No | Conditional |
CONNECT | No | No | No |
经测试,发现除了 PATCH
返回 Unauthorized
之外,其他方法均返回 Method Not Allowed
。
登录账号后,再次测试 PATCH
,提示:
1 | {"type":"ClientError","code":400,"error":"Only 'application/json' Content-Type is supported"} |
说明需要发送数据过去,改成如下请求:
1 | PATCH /api/products/1/price |
返回:
1 | 200 OK |
刷新商品页面,价格已经是 0
,此时购买商品即可。如果购物车里有原价商品,需要先清空购物车。
2.4 Lab: Exploiting a mass assignment vulnerability
To solve the lab, find and exploit a mass assignment vulnerability to buy a Lightweight l33t Leather Jacket. You can log in to your own account using the following credentials:
wiener:peter
.
登陆后,加购物车,提交订单,发送如下请求:
1 | POST /api/checkout |
购物车页面也会触发请求 GET /api/checkout HTTP/2
,响应如下:
1 | 200 OK |
尝试触发如下请求:
1 | POST /api/checkout |
直接就 OK 了。所谓 Mass Assignment,不过是自行增加一些隐藏的参数。
2.5 Lab: Exploiting server-side parameter pollution in a REST URL
To solve the lab, log in as the
administrator
and deletecarlos
.
尝试找回密码,发现 API /forgot-password
,而且 username
字段可以路径穿越,比如:
1 | POST /forgot-password |
返回如下请求(carlos
的信息):
1 | 200 OK |
当然,这个路径穿越使用 Burp Scanner 也可以快速扫出来。
进一步测试,发现 username=../users/administrator
也可以,说明上一级目录是 users
。
通过请求 username=../../v1/users/administrator
,可以确认再上一级目录是 v1
。实际上,v2
也是存在的。
而测试 username=../../../..
时,开始返回 Not Found
。
测试 &username=../../../../openapi.json%23
,返回如下信息:
1 | { |
原来 API 路径是 /api/internal/v1/users/{username}/field/{field}
。
请求 username=administrator/field/reset-token%23
,提示:
1 | { |
之后测试到 username=../../v1/users/administrator/field/passwordResetToken%23
,得到 Token 如下:
1 | { |
reset-token
和 passwordResetToken
来自 /static/js/forgotPassword.js
,关键代码如下所示:
1 | forgotPwdReady(() => { |
访问 /forgot-password?passwordResetToken=3wux5qfa2tgzxyx6sp6zqp9c8cucjgah
重置管理员密码即可。
0x03. 小结
- API 路径回溯访问
- 比如从
/api/user/carlos
一步一步回退到/api
- 比如从
- 服务器在拿到 URL 参数时,可能会和其他参数进行拼接操作,再转发给后端服务器进一步处理
- 这里可能存在参数注入问题
- HTTP Method
- 理解 Mass Assignment Vulnerability(自行增加一些隐藏的参数)
- Burp Scanner 使用方法
- 常见 API 路径,以及 API 文档名称
/api/internal/v1/users/{username}/field/{field}
openapi.json
0x04. RESTful API
关于 RESTful API,网上有很多介绍,这里不再赘述。前面的题目中,有两个是关于 Server-side Parameter Pollution(服务端参数污染)的,因为后端可能涉及到 URL 重写和转发,所以还是可以理解的。就和 HTTP 请求走私 一样,现代 Web 架构中,服务端可能存在多个不同的组件或节点,就会涉及到请求的转发、重写等操作。