深入浅出API测试|搜集分析与漏洞挖掘实战
原文链接: https://mp.weixin.qq.com/s/9-2C7vPHPHcTXBJWH9_tlg
所有动态网站都由 API 组成,因此 SQL 注入等经典 Web 漏洞可以归类为 API 测试。因此测试之前要尽可能的去测试功能点,观察代理流量,发现API目录收集信息,拓展攻击面。
例如,某个功能的请求的 API 端点是 /api/books
这会 API 进行交互,从图书馆中检索书籍列表:
GET /api/books HTTP/1.1
Host: example.com
除了明面上已使且可看见的功能外,可能还存在一些隐藏的或是权限不足导致的不展示的功能。 因此,可能会有些API
接口会被隐藏在JS
中没有被激活。这时候可以使用 JS Link Finder插件辅助分析Javascript
文件中的接口,具体可以看:https://mp.weixin.qq.com/s/qThRuLjF_S73nli7VWx9eA
除此之外,还可以通过FUZZ
对接口进行猜解,例如POST /api/user/update
接口,可能会存在/add
或者/delete
功能,或是将接口命名规则丢给GPT
让它帮忙生成相关路径。
发现 API开发文档并拓展攻击面
即使 API 文档未公开提供,仍可以通过FUZZ
或爬取的方式访问未授权的API开发文档手册,例如:
/api/swagger/v1
/api/swagger
/api
/swagger
/api-docs
/api.html
/swagger-ui
/swagger/codes
/api/index.html
/api/v2/api-docs
/v2/swagger.json
/swagger-ui/html
/distv2/index.html
/swagger/index.html
/sw/swagger-ui.html
/api/swagger-ui.html
/static/swagger.json
/user/swagger-ui.html
/swagger-ui/index.html
/swagger-dubbo/api-docs
/template/swagger-ui.html
/swagger/static/index.html
/dubbo-provider/distv2/index.html
/spring-security-rest/api/swagger-ui.html
/spring-security-oauth-resource/swagger-ui.html
github
中关于springboot
接口相关利用这篇文章非常详细:https://github.com/LandGrey/SpringBootVulExploit
API字典什么的我也没什么好东西,只推荐这Burp
插件,自动帮忙多递归扫描,它让我捡到的API文档数量已经数不过来了: github.com/F6JO/RouteVulScan
隐藏的 HTTP 方法
API
文档中一般规定了API
接口地址,允许的HTTP
方法与支持的内容类型。当然也有非一般情况,如:文档未更新或者细节问题,导致与实际不一致。因此API
端点可能潜在隐藏的HTTP
方法,所以测试所有潜在方法非常重要。
例如, /api/tasks
可能支持以下方法:
-
GET /api/tasks
- 检索任务列表。 -
POST /api/tasks
- 创建新任务。 -
DELETE /api/tasks/1
- 删除任务。
在
Intruder
中内置了名为HTTP verbs
的HTTP方法字典
隐藏的内容类型
在实际生产中,因为业务互交,数据需要多系统之间透传;由于系统之间框架语言不一致,经常会出现XML
和JSON
数据互转。这种情况下,可能会产生处理逻辑的差异。例如,API
在处理 JSON
数据时可能是安全的,但在处理 XML
时容易受到注入攻击。
在测速接口时候,要注意Content-Type
标头,然后相应的设置请求正文的格式,在BApp
中Content type converter插件可以快速的在 XML
和 JSON
中互转换。
使用自动化工具分析API文档
当发现到API文档后,可以获取到大量的API接口功能信息,除了可以手动构造外,还可以通过APIKIT、OpenAPI Parser、SoapUI 这类工具对API文档进行分析与参数构造。
自动化测试API接口时,注意手动测试增、删接口,不要影响网站运行。
Lab: 发现隐藏的API文档
通过暴露的API端点位置,想办法删除
carlos
账户;账户:wiener:peter
Lab: Exploiting an API endpoint using documentation | Web Security Academy (portswigger.net)
正常登陆,测试功能点。查看HTTP历史记录发现/api/user/wiener HTTP/2
这么一个目录请求,将winer
修改为carlos
发现可以正常取得信息 ,尝试只访问/api
路径。
![image](https://img-blog.csdnimg.cn/img_convert/cd92725515f7b4da32bb7cb9a46cb35f.png)
发现暴露了API文档信息。
根据DELETE
格式调用删除carlos
![image](https://img-blog.csdnimg.cn/img_convert/48a923b2fc5d125b235facfddb9f2dfd.png)
Lab: 找到未使用的API接口
利用隐藏的 API 端点购买Lightweight l33t Leather Jacket.
Lab: Finding and exploiting an unused API endpoint | Web Security Academy
走完所有功能流程,发现只有一个API
路径
![image](https://img-blog.csdnimg.cn/img_convert/22864df5bab93e870799bd11eeb5eb98.png)
使用内置content discover
扫描功能也只能发现这个路径。根据上面说的几点,拓展利用。 尝试HTTP
方法枚举,发现PATHCH
方法会报错内容类型错误
![image](https://img-blog.csdnimg.cn/img_convert/dc539a6677e8e9330d36dfa5699005df.png)
构造内容类型与方法发送,发现提示“price parameter missing in body”
![image](https://img-blog.csdnimg.cn/img_convert/5f1282b96cdd7b1521dc58e956621022.png)
添加上参数,实在不知道返回的price 0.01
是什么意思。
![image](https://img-blog.csdnimg.cn/img_convert/9da810351899001212b8151513318b12.png)
刷新购物车发现,是设置价格的。
![image](https://img-blog.csdnimg.cn/img_convert/0c43b25160b75cee2d0c8bcfaee6723c.png)
那么改为0
![image](https://img-blog.csdnimg.cn/img_convert/90ec4a543b44f5d4283edc9f0061cd6f.png)
Lab: Mass Assignment 批量赋值
Mass Assignment - OWASP Cheat Sheet Series --- 质量分配 - OWASP 备忘单系列
批量赋值(也称为自动绑定)可能会无意中创建隐藏参数。当Web框架自动将请求参数绑定到内部模型对象上的字段时(直接将用户输入参数赋值给模型实例),就会发生这种情况。因此,批量赋值可能会导致网站允许处理开发人员设置的隐藏参数。 通俗来说,就像是餐馆中的隐藏菜单,虽然菜单中没有展示,但是厨师也是可以制作的。
识别隐藏参数
漏洞成因是由于从模型对象字段中创建参数,这种情况下,通常可以通过手动查询API返回的模型对象来发现隐藏的参数。
例如,有这么个接口: /api/users/update
它允许用户更新用户名称和邮箱
{
"username": "wiener",
"email": "wiener@example.com",
}
而恰巧有这么一个接口:/api/users/{username}
返回以下信息🧐
{
"id": 123,
"username": "wiener",
"email": "wiener@example.com",
"isAdmin": "false"
}
这可能表示返回的id
和 isAdmin
参数与/api/users/update
接口更新的用户名和电子邮件参数,可能会作为隐藏参数一起绑定到内部User
对象。
测试批量赋值漏洞
SpringBoot 代码示例:
假设有一个用于编辑用户帐户信息的表单:
<form>
<input name="userid" type="text">
<input name="password" type="text">
<input name="email" text="text">
<input type="submit">
</form>
下面是表单绑定到的User
对象:
@Data
public class User {
private String userid;
private String password;
private String email;
private boolean admin;
//Getters & Setters
}
下面是处理请求的**Controller
**:
@RestController
public class UserController {
@PostMapping("/update")
public User updateUser(@RequestBody User user) {
System.out.println("Updating user: " + user);
return user;
}
}
正常提交html
会产生以下请求:
![image](https://img-blog.csdnimg.cn/img_convert/5aa4d54c5f01a78f3183990c5e29fce3.png)
利用Admin
隐藏参数:
![image](https://img-blog.csdnimg.cn/img_convert/4d3cc9ddd76bf3156f725367e3e5c342.png)
如果请求中的 Admin
值绑定到User
对象而没有进行充分的验证和清理,则可能会错误地向用户 test
授予管理员权限。
以下是Python Flask实现的效果:
from flask import Flask, request, jsonify
from werkzeug.security import generate_password_hash
app = Flask(__name__)
users = []
class User:
def __init__(self, username, password, email):
self.id = len(users)
self.username = username
self.password = generate_password_hash(password)
self.email = email
# 默认角色为user
self.isAdmin = False
@app.route("/regist", methods=["POST"])
def create_user():
data = request.get_json()
username = data["username"]
password = data["password"]
email = data["email"]
# 默认注册用户非admin
user = User(username, password, email)
# for user in users:
# if user.email == email:
# return jsonify({"error": "Email already exists"}), 403
users.append(user)
# 自动绑定所有其他参数到user对象上
for key, value in data.items():
if key != "password":
setattr(user, key, value)
return jsonify(user.__dict__)
if __name__ == "__main__":
app.run()
正常注册:
![image](https://img-blog.csdnimg.cn/img_convert/8cf7bf0fadc66f1a5bc55b5ffbcffb25.png)
利用
![image](https://img-blog.csdnimg.cn/img_convert/54833a15d18df14cfd92382128cc3030.png)
Lab:a mass assignment vulnerability
以下是Burp
靶场:
Lab: Exploiting a mass assignment vulnerability | Web Security Academy (portswigger.net)
走一圈功能点发现只有一个接口
![image](https://img-blog.csdnimg.cn/img_convert/d6b891ffafbafa557fe5ee455d717e8b.png)
其中支持GET
返回的数据和POST
提交的数据格式相似
![image](https://img-blog.csdnimg.cn/img_convert/42887d1c9847b35ec7c7f919ecebe85e.png)
先不管参数作用,直接一股脑全部拿来用作提交。
![image](https://img-blog.csdnimg.cn/img_convert/6efa272fecabfea4fe123641bd19289e.png)
发现没有变化,那么只能从value
入手,根据参数名直译得出chosen_discount
为已选折扣,其中percentage
百分比为0
,那么尝试修改为100
成功购买
![image](https://img-blog.csdnimg.cn/img_convert/d3638179666a77438247fbcb656a6b5e.png)
使用Param Miner
![image](https://img-blog.csdnimg.cn/img_convert/e3be60908f992718ea374ab9d7194449.png)
正常一切按照默认配置走,会发现Param miner
会自动在POST
请求中会自动添加Query
查询参数,结果就是Response
不支持POST
中携带Query
参数
![image](https://img-blog.csdnimg.cn/img_convert/8949e4c0faebea26f997b91823879e35.png)
这个具体解决办法就是在配置中取消params:query
的勾选☑️
![image](https://img-blog.csdnimg.cn/img_convert/b067764ca58222cf5c1b461331c3ee95.png)
但是工具存在bug
,这个功能并不生效,已经提交作者了。截止文章发表的时间Param miner
的版本是1.4f
,后续可能会修复这个问题。
所以还是推荐用GAP
方式收集参数再转换JSON
填充了
ref:
What is mass assignment? | Tutorial & examples | Snyk Learn
Mass Assignment - OWASP Cheat Sheet Series
更多文章关注公众号:
![image](https://img-blog.csdnimg.cn/img_convert/8279711ac2d5c30474fd36e7359e25ab.png)