Skip to content

Commit 051fa81

Browse files
authored
Merge pull request Pennyw0rth#492 from Adamkadaban/mssql-rid-brute
Add rid-brute flag to mssql protocol
2 parents 95b1af5 + 07c15cd commit 051fa81

3 files changed

Lines changed: 47 additions & 0 deletions

File tree

nxc/protocols/mssql.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
from impacket import tds, ntlm
1717
from impacket.krb5.ccache import CCache
18+
from impacket.dcerpc.v5.dtypes import SID
1819
from impacket.tds import (
1920
SQLErrorException,
2021
TDS_LOGINACK_TOKEN,
@@ -416,3 +417,46 @@ def handle_mssql_reply(self):
416417
else:
417418
_type = f"{key['Type']:d}"
418419
return f"(ENVCHANGE({_type}): Old Value: {record['OldValue'].decode('utf-16le')}, New Value: {record['NewValue'].decode('utf-16le')})"
420+
421+
def rid_brute(self, max_rid=None):
422+
entries = []
423+
if not max_rid:
424+
max_rid = int(self.args.rid_brute)
425+
426+
try:
427+
# Query domain
428+
domain = self.conn.sql_query("SELECT DEFAULT_DOMAIN()")[0][""]
429+
430+
# Query known group to determine raw SID & convert to canon
431+
raw_domain_sid = self.conn.sql_query(f"SELECT SUSER_SID('{domain}\\Domain Admins')")[0][""]
432+
domain_sid = SID(bytes.fromhex(raw_domain_sid.decode())).formatCanonical()[:-4]
433+
except Exception as e:
434+
self.logger.fail(f"Error parsing SID. Not domain joined?: {e}")
435+
436+
so_far = 0
437+
simultaneous = 1000
438+
for _j in range(max_rid // simultaneous + 1):
439+
sids_to_check = (max_rid - so_far) % simultaneous if (max_rid - so_far) // simultaneous == 0 else simultaneous
440+
if sids_to_check == 0:
441+
break
442+
443+
# Batch query multiple sids at a time
444+
sid_queries = [f"SELECT SUSER_SNAME(SID_BINARY(N'{domain_sid}-{i:d}'))" for i in range(so_far, so_far + sids_to_check)]
445+
raw_output = self.conn.sql_query(";".join(sid_queries))
446+
447+
for n, item in enumerate(raw_output):
448+
username = item[""]
449+
if username == "NULL":
450+
continue
451+
rid = so_far + n
452+
self.logger.highlight(f"{rid}: {username}")
453+
entries.append(
454+
{
455+
"rid": rid,
456+
"domain": domain,
457+
"username": username.split("\\")[1],
458+
}
459+
)
460+
461+
so_far += simultaneous
462+
return entries

nxc/protocols/mssql/proto_args.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,6 @@ def proto_args(parser, parents):
2929
tgroup.add_argument("--put-file", nargs=2, metavar=("SRC_FILE", "DEST_FILE"), help="Put a local file into remote target, ex: whoami.txt C:\\\\Windows\\\\Temp\\\\whoami.txt")
3030
tgroup.add_argument("--get-file", nargs=2, metavar=("SRC_FILE", "DEST_FILE"), help="Get a remote file, ex: C:\\\\Windows\\\\Temp\\\\whoami.txt whoami.txt")
3131

32+
mapping_enum_group = mssql_parser.add_argument_group("Mapping/Enumeration", "Options for Mapping/Enumerating")
33+
mapping_enum_group.add_argument("--rid-brute", nargs="?", type=int, const=4000, metavar="MAX_RID", help="enumerate users by bruteforcing RIDs")
3234
return parser

tests/e2e_commands.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ netexec winrm TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --check-p
213213
##### MSSQL
214214
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # Need a space at the end for kerb regex
215215
netexec {DNS} mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS # Need a space at the end for kerb regex
216+
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --rid-brute
216217
##### MSSQL PowerShell
217218
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X ipconfig
218219
netexec mssql TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS -X ipconfig --force-ps32

0 commit comments

Comments
 (0)