Skip to content

Commit 9b60069

Browse files
author
r0BIT
committed
fix: resolve double-counting and password age display bugs
Bug B1 - Double-counting in credential validation: - validate_specific_tasks() was storing each result twice (SMB path AND RPC path) - Now only stores with original SMB path; lookup code already handles both formats Bug B2 - Password age data not displayed: - LDAP pwdLastSet was converted to timezone-naive datetime - Task dates are parsed as timezone-aware (UTC) - Comparison failed with 'can't compare offset-naive and offset-aware datetimes' - Fixed by adding tz=timezone.utc to datetime creation in batch_get_user_attributes()
1 parent 70dd7a5 commit 9b60069

File tree

3 files changed

+10
-9
lines changed

3 files changed

+10
-9
lines changed

taskhound/smb/task_rpc.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,8 @@ def validate_specific_tasks(
368368
log_debug(f"Validating credentials for task: {rpc_path} (SMB: {original_path})")
369369
run_info = self.get_task_run_info(rpc_path)
370370
if run_info:
371-
# Store with BOTH paths for correlation
371+
# Store with original SMB path for correlation with crawled tasks
372372
results[original_path] = run_info
373-
results[rpc_path] = run_info
374373
else:
375374
log_debug(f"Could not get run info for {rpc_path}")
376375

taskhound/utils/sid_resolver.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -888,17 +888,19 @@ def batch_get_user_attributes(
888888
if filetime > 0:
889889
# Windows FILETIME is 100-nanosecond intervals since 1601
890890
unix_ts = (filetime - 116444736000000000) / 10000000
891-
from datetime import datetime
892-
entry_attrs["pwdLastSet"] = datetime.fromtimestamp(unix_ts)
891+
from datetime import datetime, timezone
892+
# Use UTC timezone for consistency with task date parsing
893+
entry_attrs["pwdLastSet"] = datetime.fromtimestamp(unix_ts, tz=timezone.utc)
893894
except (ValueError, OSError):
894895
pass
895896
elif attr_name.lower() == "lastlogon":
896897
try:
897898
filetime = int(attr_vals[0]) if attr_vals else 0
898899
if filetime > 0:
899900
unix_ts = (filetime - 116444736000000000) / 10000000
900-
from datetime import datetime
901-
entry_attrs["lastLogon"] = datetime.fromtimestamp(unix_ts)
901+
from datetime import datetime, timezone
902+
# Use UTC timezone for consistency
903+
entry_attrs["lastLogon"] = datetime.fromtimestamp(unix_ts, tz=timezone.utc)
902904
except (ValueError, OSError):
903905
pass
904906
elif attr_name.lower() == "objectsid":

tests/test_task_rpc.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ def test_validate_task_returns_none(self, mock_get_info):
628628

629629
@patch.object(TaskSchedulerRPC, 'get_task_run_info')
630630
def test_validate_preserves_original_path(self, mock_get_info):
631-
"""Test that results contain original SMB paths."""
631+
"""Test that results contain original SMB paths only (not duplicated with RPC path)."""
632632
mock_info = TaskRunInfo(
633633
task_path="\\TestTask",
634634
last_run=datetime(2024, 1, 1),
@@ -643,9 +643,9 @@ def test_validate_preserves_original_path(self, mock_get_info):
643643
original_path = "Windows\\System32\\Tasks\\TestTask"
644644
results = self.rpc.validate_specific_tasks([original_path])
645645

646-
# Should have both original SMB path and RPC path
646+
# Should have only the original SMB path (no duplication with RPC path)
647647
assert original_path in results
648-
assert "\\TestTask" in results
648+
assert len(results) == 1 # Only one entry, not duplicated
649649

650650

651651
class TestTaskSchedulerRPCConnect:

0 commit comments

Comments
 (0)