长亭百川云 - 文章详情

Yakit+Melto实现API安全扫描

RedTeaming

91

2024-07-13

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

如果要复制底代码请访问飞书链接: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

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

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 PracticehijackHTTPRequest = 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即可。

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

平台优势

以下是测试数据

API统计分析

API管理

漏洞测试

api统计

敏感信息识别结果

这四个功能还在beta阶段

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

  • 支持自定义提醒

结语

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

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

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

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

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

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备 2024055124号-2