scheduled_tasks.hidden returns the task's Enabled state instead of Hidden (Windows)
Bug report
What operating system and version are you using?
version = 10.0.26200
build = 26200
platform = windowsWhat version of osquery are you using?
version = 5.23.1The code path is unchanged on master (5a4516d).
What steps did you take to reproduce the issue?
Query the table and compare it against the Task Scheduler API.
SELECT name, enabled, hidden FROM scheduled_tasks;Get-ScheduledTask | Select-Object TaskName,
@{n='Enabled'; e={$_.Settings.Enabled}},
@{n='Hidden'; e={$_.Settings.Hidden}}For a controlled check, register two tasks with opposite Hidden/Enabled combinations:
$act = New-ScheduledTaskAction -Execute cmd.exe
# visible + enabled
Register-ScheduledTask -TaskName osq_visible_enabled -Action $act `
-Settings (New-ScheduledTaskSettingsSet)
# hidden + disabled
Register-ScheduledTask -TaskName osq_hidden_disabled -Action $act `
-Settings (New-ScheduledTaskSettingsSet -Hidden -Disable)SELECT name, enabled, hidden FROM scheduled_tasks
WHERE name LIKE 'osq\_%' ESCAPE '\';What did you expect to see?
hidden should reflect ITaskSettings::Hidden, i.e. the task registered
with -Hidden should be the one reported as hidden:
+---------------------+---------+--------+
| name | enabled | hidden |
+---------------------+---------+--------+
| osq_hidden_disabled | 0 | 1 |
| osq_visible_enabled | 1 | 0 |
+---------------------+---------+--------+What did you see instead?
hidden tracks enabled, so the values come out inverted relative to the
Hidden setting that was actually applied:
+---------------------+---------+--------+
| name | enabled | hidden |
+---------------------+---------+--------+
| osq_hidden_disabled | 0 | 0 |
| osq_visible_enabled | 1 | 1 |
+---------------------+---------+--------+The same holds over a full enumeration. Running osqueryi as an
administrator returned 292 rows, and hidden == enabled on 292/292 of them.
Matching those rows by name against Get-ScheduledTask (207 could be paired):
hidden vs Settings.Enabled 207 / 207 agree
hidden vs Settings.Hidden 48 / 207 agreeThe 48 are coincidences — tasks that happen to be both enabled and hidden, or both disabled and visible.
Root cause
The hidden column is populated from get_Enabled():
VARIANT_BOOL hidden = false;
pRegisteredTask->get_Enabled(&hidden);
r["hidden"] = hidden ? INTEGER(1) : INTEGER(0);The same getter fills enabled a few lines above (L97-L99), so the two
columns can never differ. get_Hidden does not appear anywhere in the file.
IRegisteredTask has no Hidden property — it is on ITaskSettings,
reached via IRegisteredTask::get_Definition() ->
ITaskDefinition::get_Settings() -> ITaskSettings::get_Hidden(). The
function already calls get_Definition() at L143 to enumerate actions, so
the settings object is available at that point.
The line has been present since the table was introduced in #3153 (April
2017, 6a90db47be); subsequent commits to this file are licensing, refactor,
leak and type fixes that do not touch this assignment.
Impact
Any query filtering on hidden silently selects every enabled task instead.
Hidden scheduled tasks — commonly used for persistence and defense evasion —
cannot be identified from this table at all, and the column gives a false
sense that they can.
Source: osquery/osquery