Skip to content

Conversation

@1r0BIT
Copy link

@1r0BIT 1r0BIT commented Sep 24, 2025

Description

This PR aims at introducing a new module "taskhound" to enumerate and categorize Scheduled Tasks on remote systems.

Key Features:

  • Identifies tasks running with privileged accounts

  • Detects tasks with stored credentials vs token-based logon

  • Includes comprehensive filtering options (exclude default tasks under \Windows\ and Default Local SIDs like S-1-5-18, etc. unless enabled via option

  • Dual BloodHound format support + Auto-Detect (Legacy + BHCE)

  • Rudimentary Tier 0 detection with AdminSDHolder and isTierZero flags for BHCE, SID Mapping for Legacy

  • Password age analysis for DPAPI dump viability

  • Output options (plain,csv,json)

  • Backup Functionality to save raw XMLs

  • Language-independent group membership analysis (Because I was really dumb earlier. Languages change, SIDs are eternal)

  • See https://github.com/1r0BIT/TaskHound for original Repo (And some more features)

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Deprecation of feature or functionality
  • This change requires a documentation update
  • This requires a third party update (such as Impacket, Dploot, lsassy, etc)

Setup guide for the review

Setup only requires a (domain joined) windows machine with a few Scheduled Tasks to test features.
1x Task from a High Value User with Stored Creds
1x Task from a non High Value User with Stored Creds
1x Any Scheduled Task without Stored Creds

You can generate a suitable export for the json/csv parsing using the following cyphers:

BHCE (JSON only):

MATCH (n)
WHERE coalesce(n.system_tags, "") CONTAINS "admin_tier_0"
   OR n.highvalue = true
MATCH p = (n)-[:MemberOf*1..]->(g:Group)
RETURN p;

Legacy (JSON only because of all_props):

MATCH (u:User {highvalue:true})
OPTIONAL MATCH (u)-[:MemberOf*1..]->(g:Group)
WITH u, properties(u) as all_props, collect(g.name) as groups, collect(g.objectid) as group_sids
RETURN u.samaccountname AS SamAccountName, all_props, groups, group_sids
ORDER BY SamAccountName

Then just export as csv or json and feed it to taskhound.

Screenshots (if appropriate):

Screenshot 2025-10-01 at 00 07 12

Checklist:

  • I have ran Ruff against my changes (via poetry: poetry run python -m ruff check . --preview, use --fix to automatically fix what it can)
  • I have added or updated the tests/e2e_commands.txt file if necessary (new modules or features are required to be added to the e2e tests)
  • New and existing e2e tests pass locally with my changes
  • If reliant on changes of third party dependencies, such as Impacket, dploot, lsassy, etc, I have linked the relevant PRs in those projects
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation (PR here: https://github.com/Pennyw0rth/NetExec-Wiki)

- New SMB module to enumerate Windows scheduled tasks
- Identifies tasks running with privileged accounts
- Detects tasks with stored credentials vs token-based logon
- Legacy BloodHound integration for high-value user identification
- Supports CSV and JSON BloodHound exports
- Includes comprehensive filtering options
Copilot AI review requested due to automatic review settings September 24, 2025 20:44
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new SMB module called "taskhound" that enumerates Windows scheduled tasks to identify privileged tasks with stored credentials. The module analyzes tasks running under high-value user accounts and distinguishes between tasks that store credentials versus those using token-based authentication.

  • Implements comprehensive scheduled task enumeration over SMB protocol
  • Integrates with BloodHound data to identify high-value user tasks
  • Provides filtering options to exclude default Microsoft tasks and system accounts

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
tests/e2e_commands.txt Adds taskhound module to end-to-end test suite
nxc/modules/taskhound.py New module implementing scheduled task enumeration with BloodHound integration

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

1r0BIT and others added 2 commits September 24, 2025 22:47
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
@Dfte
Copy link
Contributor

Dfte commented Sep 25, 2025

Man that's really hot ahah! Love it!!

@NeffIsBack
Copy link
Member

Thanks for the PR, looks cool!

@1r0BIT
Copy link
Author

1r0BIT commented Sep 27, 2025

Hey there @NeffIsBack :).
I added some features to the original project. Would you mind if I commit them here aswell before further checks are done?

@NeffIsBack
Copy link
Member

Hey there @NeffIsBack :). I added some features to the original project. Would you mind if I commit them here aswell before further checks are done?

Absolutely! Feel free to add anything you think is useful.

r0BIT added 2 commits September 30, 2025 23:55
… works on my machine :P)

Some things still need improvement. Like the Auto-Detection for BHCE/Legacy BloodHound is currently dependent on the existence of specific attributes like isTierZero for BHCE. But it works for now.

Features Added:
- Dual BloodHound format support + Auto-Detect (Legacy + BHCE)
- Rudimentary Tier 0 detection with AdminSDHolder and isTierZero flags for BHCE, SID Mapping for Legacy
- Password age analysis for DPAPI dump viability
- Output options (plain,csv,json)
- Backup Functionality to save raw XMLs
- Language-independent group membership analysis (Because I was really dumb earlier. Languages change, SIDs are eternal)
@1r0BIT
Copy link
Author

1r0BIT commented Sep 30, 2025

Soooooo, finally done :D. Lot's of changes. I hope nothing breaks (It works on my machine :P)

Some things still need improvement. Like the Auto-Detection for BHCE/Legacy BloodHound is currently dependent on the existence of specific attributes like isTierZero for BHCE. But it works for now.

Features Added:

  • Dual BloodHound format support + Auto-Detect (Legacy + BHCE)
  • Rudimentary Tier 0 detection with AdminSDHolder and isTierZero flags for BHCE, SID Mapping for Legacy
  • Password age analysis for DPAPI dump viability
  • Output options (plain,csv,json)
  • Backup Functionality to save raw XMLs
  • Language-independent group membership analysis (Because I was really dumb earlier. Languages change, SIDs are eternal)

@1r0BIT 1r0BIT requested a review from Copilot September 30, 2025 22:13
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 15 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

1r0BIT and others added 10 commits October 1, 2025 07:37
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
1r0BIT and others added 5 commits October 1, 2025 07:38
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
Co-authored-by: Copilot <[email protected]>
Signed-off-by: r0BIT <[email protected]>
@NeffIsBack
Copy link
Member

Hey thanks for the update. A few things that should be changed:

  • Please use meaningful commit messages, so that it is clear why that commit was made and what it supposes to change
  • I don't think we should parse raw BloodHound data. We do have a bloodhound connector which we could use to interact with the database itself. There is no way of retrieving if a user is part of T0 at the moment, but feel free to add that. The code is located in /nxc/helpers/bloodhound.py and so far only setting a user or host to "owned" is implemented. Related: BloodhoundCE owned new tag #616

@1r0BIT
Copy link
Author

1r0BIT commented Oct 1, 2025

Hey,
sorry for the convoluted commit messages. I was on the phone today earlier and just checked the Copilot output. It essentially just removed commented out docstrings that showed some weird behaviour while testing.

As for the connector: Great idea! I'll get to work on that :). But would it be possible to keep both? I actually run into scenarios quite often where the box I'm executing netexec from has no easy way of communicating directly with a bloodhound db. I think we could get the best of both worlds there. What do you think?

@NeffIsBack
Copy link
Member

Hey, sorry for the convoluted commit messages. I was on the phone today earlier and just checked the Copilot output. It essentially just removed commented out docstrings that showed some weird behaviour while testing.

No worries, but would be nice for the future. I might squash merge now to not have 20+ the same commit in the history.

As for the connector: Great idea! I'll get to work on that :). But would it be possible to keep both? I actually run into scenarios quite often where the box I'm executing netexec from has no easy way of communicating directly with a bloodhound db. I think we could get the best of both worlds there. What do you think?

Perhaps, but i think this is the wrong place for it. Currently we have the live connector to the database and if we would decide to integrate an offline version of this, the code should not sit in one specific module. I think we should use existing infrastructure for now and if we decide to add an offline parser this should happen in a separate PR and somewhere accessible for the entire application.

@1r0BIT
Copy link
Author

1r0BIT commented Oct 23, 2025

Perhaps, but i think this is the wrong place for it. Currently we have the live connector to the database and if we would decide to integrate an offline version of this, the code should not sit in one specific module. I think we should use existing infrastructure for now and if we decide to add an offline parser this should happen in a separate PR and somewhere accessible for the entire application.

Hey there! Just a quick update. The Connector for TaskHound itself is now finished an in testing. If you want to give it a spin, just checkout the specific branch. Once that one has been battle-tested and merged to main on the primary repo I'll get to work on the netexec specific integration.

Would that work for you? :)

@NeffIsBack
Copy link
Member

I think so yes. Currently there just shouldn't be a separate bh parsing logic in the PR. Querying/identifying/parsing the tasks should be the first step for the module. Any BH stuff could be added later on.

@1r0BIT
Copy link
Author

1r0BIT commented Nov 3, 2025

Understood. Let me strip the logic for now so the PR can go into testing and I'll open an enhancement once the OpenGraph Integration has been battle-tested.

@1r0BIT
Copy link
Author

1r0BIT commented Nov 7, 2025

I am currently reworking the entire module with a "barebone" approach. For convenience and the maximum value (without needing a bloodhound connector) I would like to leave a rudimentary LDAP lookup in place that does the following:

  • Convert SID from SchedTask (if encountered) to samaccountname
  • Group Membership Lookup via samaccountname
  • Check for general "Tier 0" memberships and mark the Task accordingly

Would that be ok?

@NeffIsBack
Copy link
Member

Sounds good. We should probably use the existing check_if_admin function which probably needs to be altered a bit to accept an optional user instead of using the logged in one. Just an idea, if the implementation isn't that easy we could still use most of the functions code tho.

- Simplified barebone module focusing on task enumeration and LDAP-based privilege checking
- changed ldap.py protocol with check_if_admin(username) and resolve_sid(sid) functions
- Now Supports --local-auth with separate LDAP credentials via LDAP_USER/LDAP_PASS/LDAP_DOMAIN options
- Automatic task XML backup to ~/.nxc/logs/taskhound_backup/ for later offline analysis
- TIER-0 task detection based on privileged group membership (Domain Admins, Enterprise Admins, etc.)
@1r0BIT 1r0BIT requested a review from Copilot November 10, 2025 21:35
Copilot finished reviewing on behalf of 1r0BIT November 10, 2025 21:40
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 14 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

1r0BIT and others added 2 commits November 11, 2025 20:47
Implemented fixes from Copilot's automated code review:

Security Fixes:
- Fixed LDAP injection in resolve_sid() - added SID regex validation
- Fixed LDAP injection in check_if_admin() - escape username in filter
- Created escape_ldap_filter() function implementing RFC 4515 escaping
- Fixed path traversal vulnerability - validate backup paths with abspath
- Documented XXE vulnerability limitation (trusted source mitigation)

Bug Fixes:
- Fixed system account detection - now checks 'nt authority\' not 'nt '
- Added domain FQDN validation - requires proper format like 'example.local'
- Improved exception handling - specific types before broad Exception
- Added module __init__() - prevents AttributeError on early calls

Code Quality:
- Fixed typo in ldap.py - 'domaine' → 'domain'
- Moved import re to module level for better performance
- Cleaned up all linting issues (whitespace, quotes, docstrings)

Per Neffs comment, implemented LDAP connection logic directly
instead of fragile importlib dynamic imports.
@1r0BIT 1r0BIT requested a review from Copilot November 11, 2025 20:31
Copilot finished reviewing on behalf of 1r0BIT November 11, 2025 20:33
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@1r0BIT
Copy link
Author

1r0BIT commented Nov 11, 2025

@NeffIsBack Done :).
Everything should work as intended now.

Complete overlooked Copilot review item - implement hash-based LDAP authentication.

Changes:
- Detect nthash/lmhash from SMB connection object
- Use ldap_conn.login() with hash parameters when password empty
- Enables LDAP privilege detection with -H flag

Fixes issue where module couldn't determine task privileges when using
NetExec's -H flag for hash-based authentication.
@NeffIsBack NeffIsBack mentioned this pull request Nov 13, 2025
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants