Skip to content

Commit 027e520

Browse files
Merge pull request Pennyw0rth#912 from Pennyw0rth/marshall-kerberoast-user-list-fix
Allow kerberoasting on specific users
2 parents 53fbac1 + ca52cc0 commit 027e520

7 files changed

Lines changed: 49 additions & 52 deletions

File tree

nxc/helpers/args.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,18 @@ def __call__(self, parser, namespace, values, option_string=None):
2424
# Set an attribute to track whether the value was explicitly set
2525
setattr(namespace, self.dest, values)
2626
setattr(namespace, f"{self.dest}_explicitly_set", True)
27+
28+
29+
def get_conditional_action(baseAction):
30+
class ConditionalAction(baseAction):
31+
def __init__(self, option_strings, dest, **kwargs):
32+
x = kwargs.pop("make_required", [])
33+
super().__init__(option_strings, dest, **kwargs)
34+
self.make_required = x
35+
36+
def __call__(self, parser, namespace, values, option_string=None):
37+
for x in self.make_required:
38+
x.required = True
39+
super().__call__(parser, namespace, values, option_string)
40+
41+
return ConditionalAction

nxc/protocols/ldap.py

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1039,8 +1039,27 @@ def kerberoasting(self):
10391039
f.write(line + "\n")
10401040
return
10411041

1042-
# Building the search filter
1043-
searchFilter = "(&(servicePrincipalName=*)(!(objectCategory=computer)))"
1042+
if self.args.kerberoast_users:
1043+
target_usernames = []
1044+
for item in self.args.kerberoast_users:
1045+
if os.path.isfile(item):
1046+
try:
1047+
with open(item, encoding="utf-8") as f:
1048+
target_usernames.extend(line.strip() for line in f if line.strip())
1049+
except Exception as e:
1050+
self.logger.fail(f"Failed to read file '{item}': {e}")
1051+
else:
1052+
target_usernames.append(item.strip())
1053+
1054+
self.logger.info(f"Targeting specific users for kerberoasting: {', '.join(target_usernames)}")
1055+
1056+
# build search filter for specific users
1057+
user_filter = "".join([f"(sAMAccountName={username})" for username in target_usernames])
1058+
searchFilter = f"(&(servicePrincipalName=*)(!(objectCategory=computer))(|{user_filter}))"
1059+
else:
1060+
# default to all
1061+
searchFilter = "(&(servicePrincipalName=*)(!(objectCategory=computer)))"
1062+
10441063
attributes = [
10451064
"sAMAccountName",
10461065
"userAccountControl",

nxc/protocols/ldap/proto_args.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from nxc.helpers.args import DefaultTrackingAction, DisplayDefaultsNotNone
1+
from nxc.helpers.args import DefaultTrackingAction, DisplayDefaultsNotNone, get_conditional_action
2+
from argparse import _StoreAction
23

34

45
def proto_args(parser, parents):
@@ -12,9 +13,13 @@ def proto_args(parser, parents):
1213

1314
egroup = ldap_parser.add_argument_group("Retrieve hash on the remote DC", "Options to get hashes from Kerberos")
1415
egroup.add_argument("--asreproast", help="Output AS_REP response to crack with hashcat to file")
15-
egroup.add_argument("--kerberoasting", help="Output TGS ticket to crack with hashcat to file")
16+
kerberoasting_arg = egroup.add_argument("--kerberoasting", "--kerberoast", help="Output TGS ticket to crack with hashcat to file")
17+
kerberoast_users_arg = egroup.add_argument("--kerberoast-users", nargs="+", dest="kerberoast_users", action=get_conditional_action(_StoreAction), make_required=[], help="Target specific users for kerberoasting (usernames or file containing usernames)")
1618
egroup.add_argument("--no-preauth-targets", nargs=1, dest="no_preauth_targets", help="Targeted kerberoastable users")
1719

20+
# Make kerberoast-users require kerberoasting
21+
kerberoast_users_arg.make_required = [kerberoasting_arg]
22+
1823
vgroup = ldap_parser.add_argument_group("Retrieve useful information on the domain")
1924
vgroup.add_argument("--base-dn", metavar="BASE_DN", dest="base_dn", type=str, default=None, help="base DN for search queries")
2025
vgroup.add_argument("--query", nargs=2, help="Query LDAP with a custom filter and attributes")

nxc/protocols/smb/proto_args.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from argparse import _StoreTrueAction
2-
from nxc.helpers.args import DisplayDefaultsNotNone, DefaultTrackingAction
2+
from nxc.helpers.args import DisplayDefaultsNotNone, DefaultTrackingAction, get_conditional_action
33

44

55
def proto_args(parser, parents):
@@ -98,18 +98,3 @@ def proto_args(parser, parents):
9898
posh_group.add_argument("--no-encode", action="store_true", default=False, help="Do not encode the PowerShell command ran on target")
9999

100100
return parser
101-
102-
103-
def get_conditional_action(baseAction):
104-
class ConditionalAction(baseAction):
105-
def __init__(self, option_strings, dest, **kwargs):
106-
x = kwargs.pop("make_required", [])
107-
super().__init__(option_strings, dest, **kwargs)
108-
self.make_required = x
109-
110-
def __call__(self, parser, namespace, values, option_string=None):
111-
for x in self.make_required:
112-
x.required = True
113-
super().__call__(parser, namespace, values, option_string)
114-
115-
return ConditionalAction

nxc/protocols/ssh/proto_args.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from argparse import _StoreAction
2-
from nxc.helpers.args import DisplayDefaultsNotNone
2+
from nxc.helpers.args import DisplayDefaultsNotNone, get_conditional_action
33

44

55
def proto_args(parser, parents):
@@ -22,18 +22,3 @@ def proto_args(parser, parents):
2222
cgroup.add_argument("-x", metavar="COMMAND", dest="execute", help="execute the specified command")
2323

2424
return parser
25-
26-
27-
def get_conditional_action(baseAction):
28-
class ConditionalAction(baseAction):
29-
def __init__(self, option_strings, dest, **kwargs):
30-
x = kwargs.pop("make_required", [])
31-
super().__init__(option_strings, dest, **kwargs)
32-
self.make_required = x
33-
34-
def __call__(self, parser, namespace, values, option_string=None):
35-
for x in self.make_required:
36-
x.required = True
37-
super().__call__(parser, namespace, values, option_string)
38-
39-
return ConditionalAction

nxc/protocols/wmi/proto_args.py

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,5 @@ def proto_args(parser, parents):
2020
cgroup.add_argument("--exec-method", choices={"wmiexec", "wmiexec-event"}, default="wmiexec", help="method to execute the command. (default: wmiexec). [wmiexec (win32_process + StdRegProv)]: get command results over registry instead of using smb connection. [wmiexec-event (T1546.003)]: this method is not very stable, highly recommend use this method in single host, using on multiple hosts may crash (just try again if it crashed).")
2121
cgroup.add_argument("--exec-timeout", default=2, metavar="exec_timeout", dest="exec_timeout", type=int, help="Set timeout (in seconds) when executing a command, minimum 5 seconds is recommended. Default: %(default)s")
2222
cgroup.add_argument("--codec", default="utf-8", help="Set encoding used (codec) from the target's output (default: utf-8). If errors are detected, run chcp.com at the target & map the result with https://docs.python.org/3/library/codecs.html#standard-encodings and then execute again with --codec and the corresponding codec")
23-
return parser
24-
25-
26-
def get_conditional_action(base_action):
27-
class ConditionalAction(base_action):
28-
def __init__(self, option_strings, dest, **kwargs):
29-
x = kwargs.pop("make_required", [])
30-
super().__init__(option_strings, dest, **kwargs)
31-
self.make_required = x
3223

33-
def __call__(self, parser, namespace, values, option_string=None):
34-
for x in self.make_required:
35-
x.required = True
36-
super().__call__(parser, namespace, values, option_string)
37-
38-
return ConditionalAction
24+
return parser

tests/e2e_commands.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,9 @@ netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --users-ex
199199
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --groups
200200
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --get-sid
201201
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p '' --asreproast /tmp/output.txt
202-
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --kerberoasting /tmp/output2.txt
202+
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --kerberoasting /tmp/kerberoasting1.txt
203+
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --kerberoast /tmp/kerberoasting2.txt
204+
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --kerberoasting /tmp/kerberoasting3.txt --kerberoast-users test_user
203205
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --trusted-for-delegation
204206
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --admin-count
205207
netexec ldap TARGET_HOST -u LOGIN_USERNAME -p LOGIN_PASSWORD KERBEROS --gmsa

0 commit comments

Comments
 (0)