Efficiently uncover hidden attack paths in scheduled tasks. TaskHound helps!

TaskHoundFinding and documenting Scheduled Task Credentials in Windows systems during penetration tests was tedious and often incomplete. Until now.

A personal tour by ProSec penetration tester @r0BIT through the tool he developed.

TL; DR

TaskHound lists scheduled Windows tasks, extracts their XML fields (UserId, Command, Arguments, LogonType, etc.) and prioritizes tasks that contain stored credentials of privileged users.

For remote scans you need SMB access to C$\Windows\System32\Tasks (typically local administrator). Alternatively, TaskHound can be run offline against previously manually exported data.

Compare the "PasswordLastChanged" attribute from your Active Directory against the task creation date. For increased reliability, additional sources can be checked (event logs or task history via WMI).

Host availability. TaskHound can only detect targets that were actually online at the time of the scan. A direct comparison of domain-joined systems to previously scanned systems is in development. Password freshness detection still needs refinement.

Table of Contents

Created, forgotten, later exploited. Stored Cedentials in Scheduled Tasks

Occasionally (often) in our penetration tests we encounter scheduled tasks on compromised Windows systems that are executed in the context of privileged users and whose passwords (credentials) are stored on the system.

Once we have informed the customer about this and shown them how quickly this can create holes in their painstakingly built tiering concept, the same question usually follows:

“Was that all of them, or are there more?”

Finding the answer to this usually ends with unleashing a whole armada of PowerShell or Python scripts of dubious origin across the domain, collecting all the task XMLs, unleashing the next wave of scripts to parse the information, and ultimately obtaining CSV files that are just as unwieldy. Sounds like fun, doesn't it?

From a purely offensive perspective, things don't get any better. Consider this scenario: You've already compromised around 20 machines and are looking for ways to use them to fully compromise the domain. To avoid having to read dozens of Scheduled Task XMLs, you simply throw a DPAPI dump at everything you've compromised so far and hope for the best.

Even if that works, further questions remain unanswered:

  • Is the user, in whose context the task runs, even a “high-value” target?

  • Is the user's password stored in the Scheduled Task still valid?

This is exactly where I wanted to start. To make those nights in front of the computer easier for myself and others, when you feel like you have to read such texts 100 times and then rightly question your career decisions:

				
					<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2025-09-18T23:04:37.3089851</Date>
    <Author>DEMO\Administrator</Author>
    <URI>\HIGH_VAL_WITH_CREDS</URI>
  </RegistrationInfo>
  <Triggers>
    <CalendarTrigger>
      <StartBoundary>2025-09-18T23:04:16</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <RunLevel>HighestAvailable</RunLevel>
      <UserId>DEMO\Administrator</UserId>
      <LogonType>Password</LogonType>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <StartWhenAvailable>false</StartWhenAvailable>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>false</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="Author">
    <Exec>
      <Command>C:\Windows\System32\cmd.exe</Command>
      <Arguments>/c whoami</Arguments>
    </Exec>
  </Actions>
</Task>
				
			

Introducing: TaskHound

This is where TaskHound comes in. The tool takes the tedious task of cleaning up the data deluge off your hands and delivers what matters most at that moment: tasks with PrivEsc potential.

You can start either remotely via SMB or offline with previously manually exported data, let the tool do the work, and receive either a focused list of worthwhile targets or BloodHound/OpenGraph JSON ready for direct import. This saves you the hassle of blindly scanning DPAPI dumps and instead gives you concrete starting points for lateral movement, privilege escalation, or immediate hardening. Depending on your perspective.

In the next section, I will show you the general workflow and how you can use the exports in BloodHound.

Windows Privileged Scheduled Task Discovery Tool for fun and profit.

TaskHound hunts down scheduled tasks in Windows environments that run in the context of privileged users. It enumerates tasks via SMB, analyzes the task XML files, and identifies attack vectors through BloodHound Export Support.

Go to the GitHub repository

Workflow & Features

The general workflow with TaskHound is incredibly simple and can be explained quickly:

				
					+----------------------+      +---------------------+
|    SMB Verbindung    | ---> | Local-Admin-Rechte? |
+----------------------+      +---------------------+
                                         |
                                         v
+----------------------+      +----------------------+
|     XML Parsing      | <--- |   Tasks einsammeln   |
+----------------------+      +----------------------+
           |                          
           v                          
+----------------------+      +----------------------+
|     Filter-Logik     | ---> |  High Value Analyse  |
+----------------------+      +----------------------+   
                                          |
                                          v
                              +----------------------+
                              |        Output        |
                              +----------------------+
				
			

We used previously obtained local admin privileges to... C$ To mount the target system's drive, navigate to the C:\Windows\System32\Tasks Folders, we recursively grab everything inside, throw the results into a parser and analyze various fields that are of interest to us.

				
					+------------+----------------------------------------+--------------------------------------------+
| Feldname   | Zweck                                  | Mögliche Werte                             |
+------------+----------------------------------------+--------------------------------------------+
| UserId     | Das Benutzerkonto, unter dem der Task  | DOMAIN\User                                |
|            | ausgeführt wird                        | SID                                        |
|            |                                        | SamAccountName                             |
+------------+----------------------------------------+--------------------------------------------+
| Command    | Die ausführbare Datei oder das Skript, | C:\Windows\System32\cmd.exe                |
|            | das der Task startet                   |                                            |
+------------+----------------------------------------+--------------------------------------------+
| Arguments  | Kommandozeilen-Parameter, die an das   | /c whoami                                  |
|            | Kommando übergeben werden              |                                            |
+------------+----------------------------------------+--------------------------------------------+
| LogonType  | Bestimmt, ob Anmeldedaten gespeichert  | Password                                   |
|            | sind oder nicht                        | InteractiveToken                           |
|            |                                        | S4U                                        |
+------------+----------------------------------------+--------------------------------------------+
| Author     | Wer den Task erstellt oder registriert | DOMAIN\User                                |
|            | hat                                    | SID                                        |
|            |                                        | SamAccountName                             |
+------------+----------------------------------------+--------------------------------------------+
| Date       | Erstellungsdatum des Tasks             | Zeitstempel                                |
+------------+----------------------------------------+--------------------------------------------+
| Enabled    | Gibt an, ob der Task noch aktiv ist    | Boolean (True)                             |
|            | oder nicht                             | Boolean (False)                            |
+------------+----------------------------------------+--------------------------------------------+
| Und weitere|                                        |                                            |
| ...        |                                        |                                            |
+------------+----------------------------------------+--------------------------------------------+

				
			

The results are then filtered according to a defined set of rules, with some tasks being skipped by default. For example:

  • Tasks under C:\Windows\System32\Tasks\Windows

    • This is where Windows' default tasks are located by default, which are rarely of particular value from an attacker's perspective.

  • Tasks without saved passwords (LogonType != Password)

    • These tasks run automatically at the scheduled time, but only if the responsible user is logged in at that time.

  • Tasks that are called in the context of local entities

    • SID S-1-5-18 aka LocalSystem Here's an example. These tasks do run with NT\SYSTEM Permissions, but only locally on the machine. Since we already need local administrative permissions to use TaskHound anyway, this is only relevant in exceptional cases.

Once the preparation is complete and ideally tasks whose passwords are stored in the system have been identified, the actual core of the tool comes into play: BloodHound Integration.

BloodHound Integration

TaskHound offers various ways to process the collected tasks and filter them specifically for high-value targets.

There are several ways to integrate the data:

  • Live DB Connector for Legacy BloodHound and BloodHound Community Edition

  • Offline compatibility by parsing previously generated exports

  • BloodHound OpenGraph integration for the visual presentation of the results

The final workflow is generally independent of the data source used: We map the UserId and the LogonType of a task and can deduce from this whether a task is particularly interesting for us as attackers or whether it is rather secondary data.

In addition, TaskHound includes extra checks and features, such as an automatic download of the encrypted DPAPI credential blobs.

At this point, I would simply refer to the tool usage:

				
					TTTTT  AAA   SSS  K   K H   H  OOO  U   U N   N DDDD
  T   A   A S     K  K  H   H O   O U   U NN  N D   D
  T   AAAAA  SSS  KKK   HHHHH O   O U   U N N N D   D
  T   A   A     S K  K  H   H O   O U   U N  NN D   D
  T   A   A SSSS  K   K H   H  OOO   UUU  N   N DDDD

                     by 0xr0BIT

usage: taskhound [-h] [-u USERNAME] [-p PASSWORD] [-d DOMAIN] [--hashes HASHES] [-k] [-t TARGET] [--targets-file TARGETS_FILE] [--dc-ip DC_IP] [--offline OFFLINE]
                 [--bh-data BH_DATA] [--bh-live] [--bh-user BH_USER] [--bh-password BH_PASSWORD] [--bh-connector BH_CONNECTOR] [--bhce | --legacy]
                 [--bh-save BH_SAVE] [--bh-opengraph] [--bh-output BH_OUTPUT] [--bh-no-upload] [--bh-set-icon] [--bh-force-icon] [--bh-icon BH_ICON]
                 [--bh-color BH_COLOR] [--include-ms] [--include-local] [--include-all] [--unsaved-creds] [--credguard-detect] [--loot] [--dpapi-key DPAPI_KEY]
                 [--no-ldap] [--ldap-user LDAP_USER] [--ldap-password LDAP_PASSWORD] [--ldap-domain LDAP_DOMAIN] [--plain PLAIN] [--json JSON] [--csv CSV]
                 [--opengraph OPENGRAPH] [--backup BACKUP] [--no-summary] [--debug]

TaskHound - Scheduled Task privilege checker with optional High Value enrichment

options:
  -h, --help            show this help message and exit

Authentication options:
  -u, --username USERNAME
                        Username (required for online mode)
  -p, --password PASSWORD
                        Password (omit with -k if using Kerberos/ccache)
  -d, --domain DOMAIN   Domain (required for online mode)
  --hashes HASHES       NTLM hashes in LM:NT format (or NT-only 32-hex) to use instead of password
  -k, --kerberos        Use Kerberos authentication (supports ccache)

Target options:
  -t, --target TARGET   Single target
  --targets-file TARGETS_FILE
                        File with targets, one per line
  --dc-ip DC_IP         Domain controller IP (required when using Kerberos without DNS)

Scanning options:
  --offline OFFLINE     Offline mode: parse previously collected XML files from directory (no authentication required)
  --bh-data BH_DATA     Path to High Value Target export (csv/json from Neo4j)
  --include-ms          Also include \Microsoft scheduled tasks (WARNING: very slow)
  --include-local       Include tasks running as local system accounts (NT AUTHORITY\SYSTEM, S-1-5-18, etc.)
  --include-all         Include ALL tasks (equivalent to --include-ms --include-local --unsaved-creds) - WARNING: VERY SLOW AND NOISY!
  --unsaved-creds       Show scheduled tasks that do not store credentials (unsaved credentials)
  --credguard-detect    EXPERIMENTAL: Attempt to detect Credential Guard status via remote registry (default: off). Only use if you know your environment supports
                        it.

BloodHound Live Connection:
  --bh-live             Use live BloodHound connection (parameters can be provided via CLI or bh_connector.config file)
  --bh-user BH_USER     BloodHound username (or set in config file)
  --bh-password BH_PASSWORD
                        BloodHound password (or set in config file)
  --bh-connector BH_CONNECTOR
                        BloodHound connector URI (default: http://127.0.0.1:8080, or set in config file). Examples: localhost, http://localhost:8080,
                        https://bh.domain.com, bolt://neo4j.local:7687. Supports both BHCE (http/https) and Legacy (bolt) protocols. If no protocol specified:
                        defaults to http:// for BHCE, bolt:// for Legacy.
  --bhce                Use BloodHound Community Edition (or set type=bhce in config)
  --legacy              Use Legacy BloodHound (or set type=legacy in config)
  --bh-save BH_SAVE     Save BloodHound query results to file (or set save_file in config)

BloodHound OpenGraph Integration:
  Automatically generate and optionally upload OpenGraph data to BloodHound CE (BHCE ONLY)

  --bh-opengraph        Generate BloodHound OpenGraph JSON files (auto-enabled if bh_connector.config has valid BHCE credentials). REQUIRES --bhce or type=bhce in
                        config - NOT compatible with Legacy BloodHound!
  --bh-output BH_OUTPUT
                        Directory to save BloodHound OpenGraph files (default: ./opengraph)
  --bh-no-upload        Generate OpenGraph files but skip automatic upload to BloodHound (files still saved)
  --bh-set-icon         Automatically set custom icon for ScheduledTask nodes after upload
  --bh-force-icon       Force icon update even if ScheduledTask icon already exists (requires --bh-set-icon)
  --bh-icon BH_ICON     Font Awesome icon name for ScheduledTask nodes (default: heart)
  --bh-color BH_COLOR   Hex color code for ScheduledTask node icon (default: #8B5CF6 - vibrant purple)

DPAPI Credential Decryption:
  --loot                Automatically download and decrypt ALL Task Scheduler credential blobs (requires --dpapi-key)
  --dpapi-key DPAPI_KEY
                        DPAPI_SYSTEM userkey from LSA secrets dump (hex format, e.g., 0x51e43225e5b43b25d3768a2ae7f99934cb35d3ea)

LDAP/SID Resolution options:
  Alternative credentials for SID lookups. Useful when you only have NTLM hashes or local admin access (domain="."). SID resolution can use lower-privilege
  plaintext credentials.

  --no-ldap             Disable LDAP queries for SID resolution (improves OPSEC but reduces user-friendliness)
  --ldap-user LDAP_USER
                        Alternative username for SID lookup (can be different from main auth credentials)
  --ldap-password LDAP_PASSWORD
                        Alternative password for SID lookup (plaintext only - hashes not supported)
  --ldap-domain LDAP_DOMAIN
                        Alternative domain for SID lookup (can be different from main auth domain)

Output options:
  --plain PLAIN         Directory to save normal text output (per target)
  --json JSON           Write all results to a JSON file
  --csv CSV             Write all results to a CSV file
  --opengraph OPENGRAPH
                        Directory to save BloodHound OpenGraph JSON files
  --backup BACKUP       Directory to save raw XML task files (per target)
  --no-summary          Disable summary table at the end of the run

Misc:
  --debug               Enable debug output (print full stack traces)

				
			

In action, it looks like this:

TaskHound – Initialization and First Results
TaskHound Initialization and First Results
Automatic upload to the connected BloodHound instance
Automatic upload to the connected BloodHound instance
Summary
Summary

Enter OpenGraph

You can't imagine how long I've dreamed of something like this. BloodHound's ability to visualize complex Active Directory relationships was an absolute game-changer right from its introduction. But being able to create my own nodes and edges... without to struggle through countless hurdles? And then to make the whole thing so accessible that else They don't have to overcome the same hurdles? That's simple. priceless.

TaskHound is currently creating the following new objects and connections:

  • Node — ScheduledTask: Represents the Scheduled Tasks themselves, with properties such as Command, Trigger, etc.
  • Edge — HasTask: Linked Computer → ScheduledTask (shows which tasks exist on which system).
  • Edge — HasTaskWithStoredCreds: A special relationship for tasks that contain stored credentials. Useful for directly identifying potential PrivEsc instances.
  • Edge — RunsAs: Linked ScheduledTask → User and indicates under which user context the task is being executed.

To quickly try out this feature, you can use the following Cypher query:

				
					// Find all computers that have ScheduledTasks with stored credentials
// and the user context they run as
  MATCH p = (c:Computer)-[:HasTaskWithStoredCreds]->(t:scheduledtask)-[:RunsAs]->(u)
  RETURN p
				
			
After the BloodHound import
After the BloodHound import

Encores

Of course, for all of you who care about OPSEC, there is also a suitable Beacon Object File (BOF) with the most essential core functions (Connect, Crawl, Parse, Backup):

https://github.com/1r0BIT/TaskHound/blob/main/BOF/README.md 

The pull request for AdaptixC2 has already been merged into the main branch and is rolled out by default with v0.10 of the extension kit:

https://github.com/Adaptix-Framework/Extension-Kit

TaskHound BOF Execution Demo
TaskHound BOF Execution Demo

The BOF was primarily developed for AdaptixC2. However, all the Beacon APIs used are compatible with Cobalt Strike. Therefore, it should be possible to adapt it to other common C2 frameworks without too much effort.

For all penetration tests where stealth/OPSEC is not so important or not a requirement: The pull request for the appropriate NetExec module is currently under review.

https://github.com/Pennyw0rth/NetExec/pull/933

NetExec Pull Request: TaskHound
NetExec Pull Request
TaskHound NetExec Tool Demo
Tool Demo

limitations

As with pretty much everything that's fun, TaskHound also has its limitations.

  • Checking whether the scheduled task's password is still valid and whether a DPAPI dump is worthwhile is more difficult than expected. So far, there's only one reliable indicator: if the value of PasswordLastChanged in BloodHound older than that CreationDate of the Scheduled Task.

    • The task XML does not contain a timestamp of the last modification, and unfortunately, changing the password in the task does not change the file's metadata.

    • One idea would be to extract the Windows event logs from the disk or query them via WMI and search the task history to compare the timestamp of the last successful execution. #OverEngineering

    • If you have better ideas, please submit a pull request! grinning face with smiling eyes

  • The TIER-0 classification is currently still based on the standard TIER-0 groups or attributes in Active Directory (AdminSDHolder, Domain Admins, Enterprise Admins, etc.).

    • This is a limitation of Legacy BloodHound, which only recognizes “High Value” or not.

    • BloodHound Community Edition at least makes a distinction in its attributes between highvalue and isTierZero, although that doesn't seem to work properly yet either, and I've implemented a rather strange solution for it.

Roadmap

Other

Please come up with a creative outro here. It's 01:00 AM and I've run out of coffee.

Related Links

Newsletter Form

Become a Cyber ​​Security Insider

Get early access and exclusive content!


OTHER CONTRIBUTIONS
Hacker in secret lair coding on laptop to break into secure computer systems and bypass security. Rogue engineer uses programming languages ​​on notebook to attack weak spots in company networks
Cyberattack via Notepad++

Following the Notepad++ cyberattack using a manipulated updater, IT security experts are warning of the threat to open-source projects. A proactive approach, including software supply chain management and behavioral attack detection, is essential for companies.

Read more "
Professionalize your attack workflow
Ethical hacking courses with personal mentoring

Table of Contents

Do you have any questions or additions? bring it on!
Write a comment and we will reply as soon as possible!

Your email address will not be published. Required fields are marked with *.

Share your feedback and help us improve our services!

Share your feedback and help us improve our services!

Take 1 minute to give us some feedback. This way we can ensure that our IT security solutions meet your exact needs.

PSN_KU_Cover
NewsLetter Form Pop Up New

Become a Cyber ​​Security Insider

Subscribe to our knowledge base and get:

Early access to new blog posts
Exclusive content
Regular updates on industry trends and best practices


Please accept the cookies at the bottom of this page to be able to submit the form!