Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/creds_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def check_creds(self) -> str:
Check given credentials

Returns string with:
- status: "success"
- status: "Success"
- error: Error message
"""
result = {
Expand Down Expand Up @@ -67,7 +67,7 @@ def check_creds(self) -> str:
authenticationChoice='sasl'
)

result['status'] = "success"
result['status'] = "Success"
result['error'] = "None"
return result

Expand Down
16 changes: 15 additions & 1 deletion core/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import os
import sys
import socket
import string
import datetime

try:
import dns.resolver
Expand Down Expand Up @@ -88,7 +90,7 @@ def prepare(self) -> Dict:

# Check if given credential is valid
creds_status = self.cred_checker.check_creds()
if creds_status['status'] != "success":
if creds_status['status'] != "Success":
print(f"[*] Given credential looks invalid: {creds_status['error']}\nExitting ...")
return {
'status': "Invalid_credential",
Expand Down Expand Up @@ -137,6 +139,18 @@ def prepare(self) -> Dict:
# Normal flow: parse targets from scratch
print("[*] Parsing targets...")
self.all_targets = self.target_parser.parse_targets()

# Write target to file - for better observability
base_path = os.path.dirname(os.path.abspath(self.config.output_file))
timestamp = format(datetime.datetime.now(), '%Y%m%d_%H%M%S')
target_file = os.path.join(base_path, f"target_{timestamp}.txt")
try:
with open(target_file, 'w') as f:
f.write('\n'.join(self.all_targets) + '\n')
print(f"\n[+] Scan target list written to: {target_file}")
print(f" Contains {len(self.all_targets)} scan target(s)")
except Exception as e:
print(f"\n[!] Error writing target list to {target_file}: {e}")

if not self.all_targets:
print("[!] No targets to scan")
Expand Down
17 changes: 14 additions & 3 deletions core/target_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,19 @@ def parse_targets(self) -> List[str]:
if self.config.audit_mode or self.config.coerce_all:
self._enumerate_ad()

return sorted(list(self.targets))
non_tier0 = self.targets - self.tier0_assets

aligned = list(self.tier0_assets)
aligned += list(non_tier0)

print(f"[+] Tier-0: {len(self.tier0_assets)}; Non-Tier0: {len(non_tier0)}; Totally {len(aligned)} targets")

return aligned
# return sorted(list(self.targets))

def _parse_target(self, target: str):
"""Parse a single target specification"""

# Check for CIDR notation
if '/' in target:
self._parse_cidr(target)
Expand Down Expand Up @@ -360,7 +368,7 @@ def _on_computer(item):
try:
#page_size now reads from self.config.ad_page_size so the --ad-page-size flag actually takes effect on this code path
# add SimplePagedResultsControl to get full results.
page_size = getattr(self.config, 'ad_page_size', 1000)
page_size = getattr(self.config, 'ad_page_size', 1000)
paged_control = ldapasn1_impacket.SimplePagedResultsControl(size=page_size)
conn.search(
searchBase=search_base,
Expand Down Expand Up @@ -438,6 +446,9 @@ def _on_computer(item):
print(f" - {dc}")
elif self.config.verbose >= 2:
print(f"[+] Found {len(dc_hostnames)} Domain Controller(s)")

for dc in sorted(dc_hostnames):
self.tier0_assets.add(dc.upper())
else:
if self.config.krb_dc_only:
print("[!] Warning: Could not enumerate Domain Controllers, --krb-dc-only may not work correctly")
Expand Down
23 changes: 13 additions & 10 deletions relayking.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -175,13 +175,13 @@ def main():
characters = string.ascii_letters + string.digits
random_string = "".join(random.choice(characters) for _ in range(10))
output_path = base_name + "_" + random_string
print(f"Testing log file creation with filename '{output_path}' ... ")
print(f"[+] Testing log file creation with filename '{output_path}' ... ")
try:
with open(output_path, 'w') as f:
f.write("check")
print(f"Success. Deleting it ... ")
print(f"[+] Success. Deleting it ... ")
os.remove(output_path)
print(f"Done.")
print(f"[+] Done.")
except Exception as e:
print(f"\n[!] Error writing {output_path}: {e}")
ready_to_output = False
Expand All @@ -194,7 +194,7 @@ def main():
ready_to_output = False

if(ready_to_output == False):
print("Cannot generate output file - we should not resume")
print("[!] Cannot generate output file - we should not resume")
return

# Save output config to session for future resume
Expand All @@ -204,8 +204,8 @@ def main():
scanner = RelayKingScanner(config, session=session)
status = scanner.prepare()

# grouping
print(f"hosts: {status['number_of_target']} / max_scangroup: {config.max_scangroup} / split_into: {config.split_into} / skip: {config.skip}")
if(status['status'] != "Success"):
return

if(config.max_scangroup == 0) and (config.split_into == 1):
#print("default - all")
Expand All @@ -227,7 +227,7 @@ def main():
else:
idxlen = int(math.log10(split_into-1))+1

print(f"Targets have been split into {split_into} groups. Each group has {group_size} hosts. Totally {status['number_of_target']} targets to be scanned")
print(f"[+] Targets have been split into {split_into} groups. Each group has {group_size} hosts. Totally {status['number_of_target']} targets to be scanned")

# Determine which groups to skip (session-completed or --skip)
completed_groups = session.get_completed_groups() if session else set()
Expand All @@ -239,19 +239,19 @@ def main():
e_idx = 0
for i in range(split_into):
if (i < config.skip):
print(f"Skipping group {i}: Skip to group {config.skip}")
print(f"[+] Skipping group {i}: Skip to group {config.skip}")
continue

if i in completed_groups:
print(f"Skipping group {i}: Already completed (from session)")
print(f"[+] Skipping group {i}: Already completed (from session)")
continue

s_idx = i * group_size
e_idx = (i+1) * group_size
if(e_idx > status['number_of_target']):
e_idx = status['number_of_target']

print(f"Group {i} of {split_into}: Scanning {group_size}(or less) hosts with index {s_idx} to {e_idx} of total {status['number_of_target']}")
print(f"[+] Group {i} of {split_into}: Scanning {group_size}(or less) hosts with index {s_idx} to {e_idx} of total {status['number_of_target']}")
results = scanner.scan(s_idx, e_idx)
# Stamp elapsed time before formatting so the formatter can include it in the report
results['scan_duration'] = time.time() - start_time
Expand All @@ -261,6 +261,9 @@ def main():
if session:
session.mark_group_complete(i)

# Wait to see if additional Ctrl-C
time.sleep(1)

except KeyboardInterrupt:
# Session is saved by scanner's KeyboardInterrupt handler
if session:
Expand Down
Empty file modified verify_installation.py
100755 → 100644
Empty file.