查看原文
其他

安全研发启蒙课:低成本实现的被动扫描工具

Z3r0ne_ Yak Project 2023-04-27


背景

之前有师傅提过,怎么在关闭Yakit时让Yak引擎在后台运行,只使用被动扫描功能,这样就可以节省前端的性能损耗。

这个需求用Yaklang就完全可以实现,实际上Yakit的大部分功能也是通过Yaklang编写的,下面就详细介绍下如何使用Yaklang编写一个被动扫描脚本。

被动扫描原理

被动扫描的流程如图,MITM指中间人,在被动扫描过程中起到类似HTTP代理的作用。在浏览器中将代理设置为MITM Server地址,那浏览器的所有Web请求都将请求到MITM Server,然后MITM去请求目标网站,再将网站响应返回给浏览器,完成一次代理请求。

MITM在代理浏览器请求目标网站,等待目标网站响应时,镜像了一份请求,发给Hook模块,Hook模块加载被动扫描插件,将请求参数传入扫描插件,对本次请求的网站做扫描。用户只需要通过浏览器访问目标网站即可实现漏洞扫描。


概览整个流程,MITM Server的作用只是获取到浏览器的请求,发送给Hook,那不使用这种HTTP代理的方式获取浏览器请求,抓网卡的HTTP流量,发送给Hook行不行?不行,因为HTTPS的请求是加密的,网卡抓到的流量是加密后的请求,无法解析。而MITM作为一个HTTP代理,既可以作为服务端与客户端交互(所以需要安装证书,解决HTTPS请求的问题),也可以作为客户端与目标网站交互,可以获取到整个流程的所有明文数据。

函数介绍

了解了被动扫描原理后,再看下Yaklang提供了那些库函数可以实现上述流程,如图是hook库和mitm库


mitm.Start()可以让我们启动一个mitm server,通过mitm.callback()设置回调,所有的流量都会经过这个回调函数,这样我们就可以获取所有请求流量,然后调用mitm插件做漏洞扫描。

hook.NewMixPluginCaller()方法会创建一个manager,暂且将这个manager理解为插件管理对象吧,刚创建的manager是空的,需要通过LoadPlugin方法通过插件名加载插件。

下一步就是获取到所有mitm插件。yakit的插件仓库的本地插件都存在本地数据库中,所以我们可以调用db.YieldYakScriptAll()方法获取所有插件。例

println("所有MITM插件")yakScriptsChan = db.YieldYakScriptAll()for yakscript = range yakScriptsChan{ if yakscript.Type == "mitm"{ println(yakscript.ScriptName) }}// 所有MITM插件// 参数发现// Dog// 被动指纹检测// 基础 XSS 检测// 敏感信息获取// 基础 SQL 注入检测:No Protection// ThinkPHP RCE 被动扫描// SSRF HTTP Public// Spring Actuator 敏感信息泄漏// Shiro 指纹识别 + 弱密码检测// ......

编写脚本

有了上述库函数的支持,我们就可以开始编写脚本了

首先创建一个manager

// 创建managermanager, err = hook.NewMixPluginCaller()if err != nil { log.Info("build mix plugin caller failed: %s", err) die(err)}

获取所有插件,并加载到manager

// 从数据库获取所有插件yakScriptsChan = db.YieldYakScriptAll()// 筛选mitm插件for yakscript = range yakScriptsChan{ if yakscript.Type == "mitm"{ manager.LoadPlugin(yakscript.ScriptName) }}

启动MITM Server,这里使用的默认证书,也就是yakit的证书,免得再装证书了。

// 启动MITM Servermitm.Start(8083,mitm.useDefaultCA(true),mitm.callback( func(isHttps,url,req,rsp){ log.info("检测到请求: %s",url) },))

运行脚本测试下yak test.yak

浏览器设置代理并访问百度,如图收到请求

1镜像流量

发现回调函数是同步运行的,回调函数内的代码运行会导致网页请求卡住。在介绍被动扫描原理时说过,这里应该是镜像一份流量发送给被动扫描插件,所以这里需要做一些优化,目标是不影响mitm server做代理。

channel = make(chan []var)for i = 0; i < 30; i+=1{ go fn(){ for c = range channel{ isHttps,url,req,rsp = c log.info("检测到请求: %s",url) } }()}// 启动MITM Servermitm.Start(port,mitm.useDefaultCA(true),mitm.callback( func(isHttps,url,req,rsp){ channel <- [isHttps,url,req,rsp] },))
2
调用MITM插件

manager.MirrorHTTPFlowEx(isScanPort,isHttps,url,req,rsp,body) 方法可以调用所有已经加载的MITM插件,设置第一个参数可以启用端口扫描,剩余参数和MITM插件的参数一样。

// 因为回调函数的req和rsp类型是go的原生类型*http.Request和*http.Response类型req,err = http.dump(req)if err!= nil{ log.error(err) return}rsp,err = http.dump(rsp)if err!= nil{ log.error(err) return}body,err = str.ExtractBodyFromHTTPResponseRaw(rsp)if err!= nil{ log.error(err) return}manager.MirrorHTTPFlowEx(false,isHttps,url,req,rsp,body)
3

优化日志信息

调用插件时发现有些MITM插件的输出使用yakit_ouput方法,不能在控制台输出,实际上yakit_ouput也是通过hook,将参数传给Feedback方法输出信息。manager提供了一个SetFeedback方法,通过自定义Feedback就可以自定义输出信息,如下:

manager.SetFeedback(func(i){ msg = json.loads(i.Message) data = msg.content.data level = msg.content.level switch msg.content.level{ case "info": log.info(data) case "error": log.error(data) default: log.info("收到信息,不支持的信息类型: [%s] %s",level,data) }})
4


最终脚本
yakit.AutoInitYakit()// 设置日志级别loglevel("info")// 参数port = cli.Int("port", cli.setRequired(true),cli.setDefault(8083))
// 创建managermanager, err = hook.NewMixPluginCaller()if err != nil { log.Info("build mix plugin caller failed: %s", err) die(err)}// 设置feedbackmanager.SetFeedback(func(i){ msg = json.loads(i.Message) data = msg.content.data level = msg.content.level switch msg.content.level{ case "info": log.info(data) case "error": log.error(data) default: log.info("收到信息,不支持的信息类型: [%s] %s",level,data) }})// 加载插件yakScriptsChan = db.YieldYakScriptAll()for yakscript = range yakScriptsChan{ if yakscript.Type == "mitm"{ manager.LoadPlugin(yakscript.ScriptName) }}// 启动MITM Serverchannel = make(chan []var)for i = 0; i < 30; i+=1{ go fn(){ for c = range channel{ isHttps,url,req,rsp = c req,err = http.dump(req) if err!= nil{ log.error(err) return } rsp,err = http.dump(rsp) if err!= nil{ log.error(err) return } body,err = str.ExtractBodyFromHTTPResponseRaw(rsp) if err!= nil{ log.error(err) return } manager.MirrorHTTPFlowEx(false,isHttps,url,req,rsp,body) } }()}// 启动MITM Servermitm.Start(port,mitm.useDefaultCA(true),mitm.callback( func(isHttps,url,req,rsp){ channel <- [isHttps,url,req,rsp] },))
5



测试脚本

运行脚本,默认代理开在8083端口。跑出来的漏洞可以在Yakit的风险与漏洞中看,如图



总结

这个简陋的脚本只是一个演示,除了可以调用mitm插件,manager.MirrorHTTPFlowEx还可以调用Nuclei插件、Yak插件、端口扫描插件等,师傅们可以按需调用相应插件。

更新通知!



往期推荐


基础设施:Yaklang Java字节码能力支持


Web Fuzzer 高级进阶:支持前端 AES-ECB 加密Web 安全测试实战


CVE-2020-2551 深入 IIOP 检测与 Non-Java(Yak) 利用


Yaklang XSS 检测启发式算法(被动扫描插件)


Low Code, Full Turing: Yaklang 分布式引擎 SaaS 化

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存