Yakit+Melto实现API安全扫描

渗透技巧 1年前 (2023) admin
197 0 0

Yakit+Melto实现API安全扫描

Yakit+Melto实现API安全扫描

踩了不少坑,写个文档记录下。

如果要复制底代码请访问飞书链接:https://c6k50tuyg6.feishu.cn/wiki/D2XEwjOb3iZzaKkMOvgcBFudnNb?from=from_copylink

Melto介绍以及安装部署

https://github.com/metlo-labs/metlo/

官方介绍:

Metlo 是一款开源 API 安全工具,只需不到 15 分钟即可完成设置,它可以管理API、检测危险行为并实时阻止恶意流量。

  • 实时检测 API 攻击。

  • 自动阻止恶意行为者。

  • 创建 API 端点和敏感数据清单。

  • 在投入生产环境前主动测试 API。

我是部署在内网,通过cf的tunnel功能实现外网https安全访问。

melto支持多种语言、多种云原生环境的流量接入。

Yakit+Melto实现API安全扫描

接入Yakit

插件我已经写好了,动动小手复制一下。

yakit_output(MITM_PARAMS)

#-----------------------MITM Hooks I/O-------------------------
/*
#如何使用插件参数?

## 例如,如果你设置了一个参数为 url_keyword 的参数,可以通过 MITM_PARAMS 来使用它!
urlKeyword = MITM_PARAMS["url_keyword"]

# 如何输出给 Yakit 给用户查看?

yakit_output(i: any) // 可以只输出到 "Console 界面"
yakit_save(i: any) // 可以输出并保存到数据库中,在 "插件输出" 中查看
*/
#----------------MITM Hooks Test And Quick Debug-----------------
/*
# __test__ 是 yakit mitm 插件用于调试的函数 【注意:这个函数在 MITM hooks劫持环境下不会被导入】

在这个函数中,你可以使用 yakit.GenerateYakitMITMHooksParams(method: string, url: string, opts ...http.Option) 来方便的生成可供 hooks 调用的参数,参考代码模版中的用法~

*/

#--------------------------WORKSPACE-----------------------------
__test__ = func() {
results, err := yakit.GenerateYakitMITMHooksParams("GET", "http://192.168.0.18:8080/admin/")
if err != nil {
return
}


isHttps, url, reqRaw, rspRaw, body = results

mirrorHTTPFlow(results...)
mirrorFilteredHTTPFlow(results...)
mirrorNewWebsite(results...)
mirrorNewWebsitePath(results...)
mirrorNewWebsitePathParams(results...)
}

# mirrorHTTPFlow 会镜像所有的流量到这里,包括 .js / .css / .jpg 这类一般会被劫持程序过滤的请求
mirrorHTTPFlow = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }

# mirrorFilteredHTTPFlow 劫持到的流量为 MITM 自动过滤出的可能和 "业务" 有关的流量,会自动过滤掉 js / css 等流量
mirrorFilteredHTTPFlow = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }

# mirrorNewWebsite 每新出现一个网站,这个网站的第一个请求,将会在这里被调用!
mirrorNewWebsite = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }

# mirrorNewWebsitePath 每新出现一个网站路径,关于这个网站路径的第一个请求,将会在这里被传入回调
mirrorNewWebsitePath = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) {
api = "https://hostname/api/v1/log-request/single"
key = ""

destinationHost,destinationPort,err = str.ParseStringToHostPort(url)
die(err)
freq, err = fuzz.HTTPRequest(req, fuzz.https(isHttps))
params = freq.GetGetQueryParams()
pathList = []
for k, v := range params {
pathList = append(pathList, {"name": v.Name(), "value": v.Value()[0]})
}


var keys = freq.GetHeaderKeys()
headerList = []
for i, k := range keys {
value := freq.GetHeader(k)
headerList = append(headerList, {"name": k, "value": value})
}


parseResp, err = str.ParseBytesToHTTPResponse(rsp)
die(err)

respHeader = []
for k, v := range parseResp.Header {
respHeader = append(respHeader, {"name": k, "value": str.Join(v, ";")})
}


rspBpdy,err=str.ExtractBodyFromHTTPResponseRaw(rsp)
die(err)
postBody = {"request": {"url": {"host": destinationHost, "path": freq.GetPath(), "parameters": pathList}, "headers": headerList, "method": freq.GetMethod(), "body": string(body)}, "response": {"status": parseResp.StatusCode, "headers": [{"name": "content-type", "value": "application/json; charset=utf-8"}], "body": string(rspBpdy)}, "meta": {"environment": "production", "incoming": true, "source": "127.0.0.1", "sourcePort": 17929, "destination": destinationHost, "destinationPort": destinationPort,"metloSource": "proxy"}}
rsp, err = http.Post(
api,
http.header("Content-Type", "application/json; charset=UTF-8"),
http.header("Authorization", key),
http.json(postBody),
)
die(err)
}

# mirrorNewWebsitePathParams 每新出现一个网站路径且带有一些参数,参数通过常见位置和参数名去重,去重的第一个 HTTPFlow 在这里被调用
mirrorNewWebsitePathParams = func(isHttps /* bool */, url /* string */, req /* []byte */, rsp /* []byte */, body /* []byte */) { }

# hijackHTTPRequest 每一个新的 HTTPRequest 将会被这个 HOOK 劫持,劫持后通过 forward(modifed) 来把修改后的请求覆盖,如果需要屏蔽该数据包,通过 drop() 来屏蔽
# ATTENTION-DEMO:
# hijacked = str.ReplaceAll(string(req), "abc", "bcd")
# 1. forward(hijacked):确认转发
# 2. drop() 丢包
# 3. 如果 forward 和 drop 都没有被调用,则使用默认数据流
# 4. 如果 drop 和 forward 在一个劫持中都被调用到了,以 drop 为准
/*
# Demo2 Best In Practice
hijackHTTPRequest = func(isHttps, url, req, forward, drop) {
if str.Contains(string(req), "/products/plugins/plugin_11") {
forward(str.ReplaceAll(string(req), "/products/plugins/plugin_11", "/products/plugins/plugin_create"))
}

if str.Contains(string(req), "/products/plugins/plugin_12") {
drop()
}
}
*/
hijackHTTPRequest = func(isHttps, url, req, forward /* func(modifiedRequest []byte) */, drop /* func() */) { }

# hijackSaveHTTPFlow 是 Yakit 开放的 MITM 存储过程的 Hook 函数
# 这个函数允许用户在 HTTP 数据包存入数据库前进行过滤或者修改,增加字段,染色等
# 类似 hijackHTTPRequest
# 1. hijackSaveHTTPFlow 也采用了 JS Promise 的回调处理方案,用户可以在这个方法体内进行修改,修改完通过 modify(flow) 来进行保存
# 2. 如果用户不想保存数据包,使用 drop() 即可
#
/**
案例:

hijackSaveHTTPFlow = func(flow, modify, drop) {
if str.Contains(flow.Url, "/admin/") {
flow.Red() # 设置颜色
modify(flow) # 保存
}
}
*/

hijackSaveHTTPFlow = func(flow /* *yakit.HTTPFlow */, modify /* func(modified *yakit.HTTPFlow) */, drop /* func() */) { }


在这里生成一个Key即可。

Yakit+Melto实现API安全扫描

key和api地址复制到插件中,正常启动即可,不出意外可能会出意外,接收到流量说明平台部署没问题。

平台优势

以下是测试数据

API统计分析

Yakit+Melto实现API安全扫描

API管理

Yakit+Melto实现API安全扫描

Yakit+Melto实现API安全扫描

漏洞测试

Yakit+Melto实现API安全扫描

api统计

Yakit+Melto实现API安全扫描

敏感信息识别结果

Yakit+Melto实现API安全扫描

这四个功能还在beta阶段

Yakit+Melto实现API安全扫描

  • 支持自定义敏感信息规则

  • 支持自定义提醒

Yakit+Melto实现API安全扫描

结语

这个工具如果用于做漏洞扫描的话,规则不丰富,不如yakit,我觉得它的作用主要在于:

  • 可视化、可视化、可视化

  • API管理(数据持久化存储)

  • 敏感信息识别,支持自定义规则,这点很好

  • 可以作为API安全产品类别一个参考。



原文始发于微信公众号(RedTeaming):Yakit+Melto实现API安全扫描

版权声明:admin 发表于 2023年10月31日 上午12:52。
转载请注明:Yakit+Melto实现API安全扫描 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...