freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

安全研发启蒙课:低成本实现的被动扫描工具
yaklang 2022-09-21 13:32:05 65825
所属地 四川省

背景

之前有师傅提过,怎么在关闭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

// 创建manager
manager, 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 Server
mitm.Start(8083,mitm.useDefaultCA(true),mitm.callback(
    func(isHttps,url,req,rsp){
       log.info("检测到请求: %s",url)
    },
))

镜像流量

发现回调函数是同步运行的,回调函数内的代码运行会导致网页请求卡住。在介绍被动扫描原理时说过,这里应该是镜像一份流量发送给被动扫描插件,所以这里需要做一些优化,目标是不影响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 Server
mitm.Start(port,mitm.useDefaultCA(true),mitm.callback(
    func(isHttps,url,req,rsp){
        channel <- [isHttps,url,req,rsp]
    },
))

调用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)

优化日志信息

调用插件时发现有些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)
    }
})

最终脚本

yakit.AutoInitYakit()
// 设置日志级别
loglevel("info")
// 参数
port = cli.Int("port", cli.setRequired(true),cli.setDefault(8083))

// 创建manager
manager, err = hook.NewMixPluginCaller()
if err != nil {
    log.Info("build mix plugin caller failed: %s", err)
    die(err)
}
// 设置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)
    }
})
// 加载插件
yakScriptsChan = db.YieldYakScriptAll()
for yakscript = range yakScriptsChan{
    if yakscript.Type == "mitm"{
        manager.LoadPlugin(yakscript.ScriptName)
    }
}
// 启动MITM Server
channel = 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 Server
mitm.Start(port,mitm.useDefaultCA(true),mitm.callback(
    func(isHttps,url,req,rsp){
        channel <- [isHttps,url,req,rsp]
    },
))

测试脚本

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

总结

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

# 网络安全 # 网络安全技术
本文为 yaklang 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
yaklang LV.8
做难而正确的事!
  • 153 文章数
  • 105 关注者
独立SyntaxFlow功能?IRify,启动!
2025-03-31
那我问你,MCP是什么?回答我!
2025-03-24
SyntaxFlow实战CVE漏洞?那很好了
2025-03-14
文章目录