ParseScheduledTasksXML Hands on Labs¶
Start in the browser with a short investigation on fictional workstation WS-042. Practise the PowerShell command, read the CSV, check the source XML and match an action start event. Then try the Full Lab with supplied evidence in a Windows VM.
- No installation
- Guided investigation
- Real tool workflow
- Training pack included
Interactive Lab¶
Beginner · about 10 minutes · no installation. A PowerShell alert appeared after WS-042 restarted. Find the task behind it and explain what the evidence shows. Start in PowerShell: open Show command, enter it at the prompt and press Enter. Then read the CSV, task XML and log. Each step tells you what to do next.
The console follows the PowerShell screenshot and the maintainer's script. ParseScheduledTasksXML runs in PowerShell. Timeline Explorer is the separate application used to view its CSV. The browser recreates the actions needed for this exercise with fictional evidence.
Full Lab¶
ParseScheduledTasksXML Full Lab
Case SUSA-PST-072 / follow the task
Find the task. Check the log. Explain what happened.
A PowerShell alert appeared after workstation WS-042 restarted. Use the real parser to read four collected task files. Find the enabled PowerShell task, check when it starts, and leave a short finding for the next analyst.
Beginner · about 30 minutes. Expand one step at a time. Each activity ends with a small result you can save.
Tick a checkpoint after saving its files. This page remembers your ticks in this browser; it cannot check your VM.
Investigation brief¶
Four task XML files, a file hash manifest, a readable event table and an inert script sample. All describe fictional WS-042.
Real case note: This pack is fictional. You will run the real parser on real XML files. The event table contains synthetic excerpts, not an EVTX log. In an incident, use a collected Tasks folder and original logs from the affected computer.
Before you start¶
Need: a Windows 10 or 11 training VM, Windows PowerShell 5.1, ParseScheduledTasksXML installed at C:\Tools\ParseScheduledTasksXML\ParseScheduledTasksXML.ps1, and Timeline Explorer for the CSV. Keep a place outside the VM for the finished case folder.
Download: SUSA-PST-072 training pack. Save it in the VM's Downloads folder with that filename. No malware is included. Do not register the task files or run the script sample; you only read them.
Follow: open Windows PowerShell in the VM. Run the blocks in order in the same window and check each Look for result. The commands create a new case folder; they do not change Task Scheduler.
Need setup help?
Follow Install and open to download and review the parser. If your tool or download is in a different folder, change $pstTool or $pstPack in the first block. If execution policy blocks the reviewed parser, use the process only session described in that guide; start that session before Activity 1. Keep the tool folder separate from the case files. For Timeline Explorer, use the official download page above, keep its release folder intact and install the .NET Desktop Runtime required by that release.
Activity 1: Prepare the case¶
1. Unpack the evidence into a new case folder
Keep the downloaded ZIP. It is your source package. These commands check its SHA-256, a fingerprint of the file's contents, then extract it. Use a new folder if you have already done this lab.
$pstCase = 'C:\Cases\SUSA-PST-072'
$pstPack = "$env:USERPROFILE\Downloads\SUSA-PST-072-training.zip"
$pstTool = 'C:\Tools\ParseScheduledTasksXML\ParseScheduledTasksXML.ps1'
if (-not (Test-Path -LiteralPath $pstTool -PathType Leaf)) { throw 'Install the parser first, or correct pstTool.' }
if (Test-Path -LiteralPath $pstCase) { throw 'Case folder exists. Choose a new pstCase folder.' }
$pstPackHash = (Get-FileHash -LiteralPath $pstPack -Algorithm SHA256).Hash
if ($pstPackHash -ne 'BDDAE2E12131BCBD9367F3697052EB448EDDEBBA0B4BE2765BF40711FC80259B') {
throw 'Training ZIP hash does not match. Download the pack again.'
}
Expand-Archive -LiteralPath $pstPack -DestinationPath $pstCase -ErrorAction Stop
New-Item -ItemType Directory -Path "$pstCase\derived", "$pstCase\notes" -ErrorAction Stop | Out-Null
$pstPackHash | Set-Content -LiteralPath "$pstCase\notes\pack-sha256.txt"
Get-ChildItem -LiteralPath "$pstCase\source\Tasks" -File | Select-Object Name, Length
Look for: four files under source\Tasks. They have no .xml extension, but contain XML. derived is the separate folder for the parser's CSV; notes is for your saved checks.
2. Check the file hashes and record the tool
A manifest lists the supplied files and their hashes. Check it before parsing, then save the check and your tool details.
$pstManifest = Import-Csv -LiteralPath "$pstCase\manifest.csv"
$pstChecked = @(foreach ($pstItem in $pstManifest) {
$pstFile = Join-Path $pstCase $pstItem.RelativePath
$pstActual = (Get-FileHash -LiteralPath $pstFile -Algorithm SHA256 -ErrorAction Stop).Hash
[pscustomobject]@{
RelativePath = $pstItem.RelativePath
Expected = $pstItem.SHA256
Actual = $pstActual
Match = ($pstActual -eq $pstItem.SHA256)
}
})
$pstChecked | Export-Csv -LiteralPath "$pstCase\notes\integrity-check.csv" -NoTypeInformation
$pstChecked | Select-Object RelativePath, Match | Format-Table -AutoSize
if ($pstChecked.Count -ne 6 -or @($pstChecked | Where-Object Match -EQ $false).Count) {
throw 'One or more files failed the check. Keep the result and inspect the pack.'
}
Get-FileHash -LiteralPath $pstTool -Algorithm SHA256 |
Export-Csv -LiteralPath "$pstCase\notes\parser-sha256.csv" -NoTypeInformation
@(
'Case: SUSA-PST-072 / fictional training evidence'
'Evidence host: WS-042 / event times: UTC'
"Analyst VM: $env:COMPUTERNAME"
"Analyst time zone: $((Get-TimeZone).Id)"
"Review started: $(Get-Date -Format o)"
"Parser: $pstTool"
'Parser source: https://github.com/vikas891/My-Scripts/blob/master/General/ParseScheduledTasksXML.ps1'
) | Set-Content -LiteralPath "$pstCase\notes\case-context.txt"
Look for: six rows with Match set to True. This checks the four task files, the event table and the script sample. Add the parser's download date or Git commit to case-context.txt.
A hash is missing or does not match
Check that you downloaded the ZIP from this page and extracted it into the folder named in $pstCase. Do not edit source files to make them match. Keep the failed check, download a fresh pack and choose a new case folder before retrying. Matching hashes check this training copy, not the authenticity of a real incident acquisition.
Next: turn the four task files into a CSV.
Continue to parsingActivity 2: Find the task¶
Two tasks mention PowerShell. Check whether both are enabled and whether they run the same script.
Returning after Activity 1?
Open Windows PowerShell in the same VM. Set these two paths to your saved case and installed tool, then continue below. Do not extract the pack again.
1. Run the parser and save the console session
-Path points to the task files. -Output points to an existing folder for the CSV. A transcript saves your command and the console messages.
Start-Transcript -LiteralPath "$pstCase\notes\parser-session.txt" -ErrorAction Stop
& $pstTool -Path "$pstCase\source\Tasks" -Output "$pstCase\derived"
Stop-Transcript
Look for: the CSV's full path in yellow. Its filename ends with _ParsedScheduledTasks_XML.csv; the prefix is the run time in UTC. The parser reads XML and exports fields. It does not run the commands described in the tasks.
The parser does not run
Confirm that $pstTool points to ParseScheduledTasksXML.ps1, the source folder contains the four files, and derived exists. For a policy error, follow the process only session in Install and open. Keep the error in your notes. If the transcript is still active after a failure, enter Stop-Transcript before retrying.
2. Count the CSV rows and save its hash
An export message does not guarantee that every file parsed. This script suppresses individual parsing errors, so check the result.
$pstCsvFiles = @(Get-ChildItem -LiteralPath "$pstCase\derived" -File -Filter '*_ParsedScheduledTasks_XML.csv')
if ($pstCsvFiles.Count -ne 1) { throw 'Expected one CSV. Check the derived folder and identify the run you want.' }
$pstCsv = $pstCsvFiles[0].FullName
$pstRows = @(Import-Csv -LiteralPath $pstCsv)
$pstInputCount = @(Get-ChildItem -LiteralPath "$pstCase\source\Tasks" -File -Recurse).Count
"Source files: $pstInputCount / CSV rows: $($pstRows.Count)"
Get-FileHash -LiteralPath $pstCsv -Algorithm SHA256 |
Export-Csv -LiteralPath "$pstCase\notes\csv-sha256.csv" -NoTypeInformation
if ($pstInputCount -ne 4 -or $pstRows.Count -ne 4) { throw 'Expected four files and four rows in this pack. Inspect the inputs and output.' }
$pstRows | Select-Object Task_Name, Principals_UserContext, Enabled | Format-Table -AutoSize
Look for: Source files: 4 / CSV rows: 4 and four task names. Equal counts are a useful check; you will also compare the selected row with its XML.
The count is different, or I parsed twice
Keep the output and transcript. Check each source file is readable XML. For multiple CSVs, read the timestamped names and transcript to select the correct run. Set $pstCsv to that file's full path and load it with $pstRows = @(Import-Csv -LiteralPath $pstCsv). Keep any retry as a separate run; do not overwrite your first output or transcript. Do not copy this pack's expected count to a real case.
3. Find PowerShell in Timeline Explorer and save the row
Open Timeline Explorer, choose File → Open, and select the CSV in derived. Type powershell in Find. Read the two matching rows, scrolling right to see Enabled and Action_Arguments.
Find the row where Enabled is true and the command runs C:\ProgramData\SystemHealth\Updater.ps1. Principals_UserContext is S-1-5-18, the SYSTEM account. The other PowerShell task is disabled. Then save the full selected row:
$pstLead = @($pstRows | Where-Object Task_Name -EQ '\SystemHealthUpdate')
if ($pstLead.Count -ne 1) { throw 'Expected one SystemHealthUpdate row. Check the CSV.' }
$pstLead | Export-Csv -LiteralPath "$pstCase\notes\selected-task.csv" -NoTypeInformation
$pstLead | Format-List Task_Name, Principals_UserContext, LogonType, Enabled, Action_Arguments
Look for: \SystemHealthUpdate, S-1-5-18, ServiceAccount, true, and the full PowerShell command. A name that sounds like maintenance is not proof that the task is legitimate or malicious.
Next: check when the task starts and match the log entry.
Continue to the evidence checksActivity 3: Check and explain¶
Returning in a new PowerShell window?
Set $pstCase to your saved case folder. Activity 3 reads the saved files; no second parser run is needed.
1. Read the original task XML
The CSV does not include triggers. Read the task file as text; do not import it into Task Scheduler.
Look for: BootTrigger, S-1-5-18, Enabled=true, powershell.exe and the same arguments as selected-task.csv. BootTrigger means Windows startup. The XML shows what the task was set to do, not that it ran. The Author and Date values inside XML do not prove who created the task or the actual creation time.
2. Match the task with an action start entry
The supplied table is a synthetic event excerpt, not an EVTX file. It represents entries from Microsoft-Windows-TaskScheduler/Operational on the same fictional workstation. Keep both matching entries so you can compare registration with an action start.
$pstEvents = Import-Csv -LiteralPath "$pstCase\supporting\TaskScheduler-Operational.csv"
$pstMatches = @($pstEvents | Where-Object TaskName -EQ '\SystemHealthUpdate')
$pstMatches | Export-Csv -LiteralPath "$pstCase\notes\matched-events.csv" -NoTypeInformation
$pstMatches | Format-Table TimeCreatedUTC, EventId, TaskName, ActionName -AutoSize
Look for: two rows. Event 106 at 03:00:00Z records registration. Event 200 at 03:10:04Z records the PowerShell action starting. Match the task name and action, not just the event ID. No completion event or process log is supplied, so script success remains unknown.
3. Inspect the sample and write a short finding
Read the script sample without running it:
Look for: comments only. This is an inert stand in, not the script from a real alert. It cannot show whether a real payload stole data or did anything harmful.
Paste this template into Notepad, fill it with your actual results, and save it as notes\handover.txt. A clearly explained missing result is better than an invented success.
Case: SUSA-PST-072 / fictional WS-042
Question: Which task could start PowerShell after a restart?
Source ZIP and checked files:
Parser source, download date or commit, and SHA-256:
Parser command and saved CSV path:
Source-file count, CSV-row count, and CSV SHA-256:
Selected task: name, account, Enabled and complete command.
XML: trigger and source task-file path.
Log: matching task, action, event ID and UTC time.
Finding: What does the definition show? What does the log add?
Limits: What is still unknown? Note any failed checks.
Next check: What original script or process/PowerShell logs are needed?
A finding you can support: the XML defines an enabled SYSTEM task that starts PowerShell at boot; the matching training entry records that action starting. It does not establish script completion, harmful behavior or a human creator. In a real case, read the original script and check process and PowerShell logs next.
Optional: what else would you check in a real incident?
Keep the original Task Scheduler EVTX and compare the same task instance across start and completion events. Check the original script's contents, hash and path, and process or PowerShell logs. For missing or hidden tasks, compare the collected XML with registry TaskCache records. The task name, SYSTEM account or a missing log entry alone is not a verdict. ZIP extraction also changes the working files' timestamps; do not treat this pack's TaskFile_LastModifiedTime as an original endpoint time.
Case closeout4. Check your saved files before closing the VM
Check the three groups in the checklist below. Open handover.txt and confirm another analyst can locate the CSV, selected task and matching event rows.
Copy the source ZIP and the complete case folder outside the VM. Open the copied handover and CSV before restoring the VM snapshot. Close Timeline Explorer and PowerShell when finished. No task was registered or started by this lab, so there is no training task to delete.
Finish by checking the case folder someone else will receive.
Review the evidence checklistFull Lab evidence checklist¶
- 1Source and checksThe original ZIP,
source/,supporting/,manifest.csv,README.txtand the context, integrity and parser hash notes. - 2Parser resultThe timestamped CSV in
derived/,parser-session.txt,csv-sha256.csvandselected-task.csv. - 3Finding
matched-events.csvandhandover.txtwith the XML trigger, UTC event time, gaps and a next check.
Your finish line: a saved task lead, the XML and log checks, and a finding someone else can review.