XML de tareas programadas de Windows, elemento a elemento
Cómo leer el XML de una tarea programada: RegistrationInfo, Triggers, Principals, Settings, Actions y las horas locales sin zona de Date y StartBoundary.
Resumen. El XML de una tarea tiene cinco secciones que importan: RegistrationInfo (quién y cuándo, según lo declarado), Triggers (cuándo se dispara), Principals (qué cuenta y qué privilegios), Settings (oculta, habilitada, límites) y Actions (qué se ejecuta). Dos trampas: Date y StartBoundary suelen estar en hora local sin desfase, y Author es texto libre. Lea el archivo como un conjunto de afirmaciones y contrástelas con TaskCache y con los logs de eventos.
El formato está documentado en el Task Scheduler Schema de Microsoft, con el espacio de nombres http://schemas.microsoft.com/windows/2004/02/mit/task. Este artículo lo lee desde el punto de vista de un investigador. Dónde se encuentran los archivos se explica en ubicación y adquisición de las tareas programadas.
Un ejemplo mínimo
Una tarea registrada con schtasks en un puesto de trabajo suele tener este aspecto. Es una versión recortada de la muestra ficticia FIN-WKS-07 que se usa en todo este sitio, configurada en UTC+02:00:
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2026-09-14T12:09:58.4413872</Date>
<Author>FIN-WKS-07\svc_backup</Author>
<Description>Intel graphics telemetry</Description>
<URI>\IntelGfxTelemetry</URI>
</RegistrationInfo>
<Triggers>
<BootTrigger><Enabled>true</Enabled></BootTrigger>
<TimeTrigger>
<Repetition><Interval>PT1H</Interval></Repetition>
<StartBoundary>2026-09-14T12:15:00</StartBoundary>
<Enabled>true</Enabled>
</TimeTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>S-1-5-18</UserId>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<Hidden>true</Hidden>
<Enabled>true</Enabled>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\ProgramData\Intel\m64.exe</Command>
<Arguments>-svc</Arguments>
</Exec>
</Actions>
</Task>
El archivo en disco suele estar en UTF-16LE con marca de orden de bytes (BOM), aunque la declaración XML a veces circula copiada como UTF-8. Un parser que dé por hecho UTF-8 mostrará caracteres ilegibles; eso ya es motivo suficiente para usar una herramienta diseñada para este formato.
RegistrationInfo: los metadatos declarados
| Elemento | Qué indica | Hasta qué punto fiarse |
|---|---|---|
URI | Ruta de la tarea en la biblioteca, p. ej. \Microsoft\Windows\Defrag\ScheduledDefrag | Debería coincidir con la ruta del archivo y con la clave Tree de TaskCache; conviene anotar cualquier discrepancia |
Author | Quién registró la tarea | Texto libre. La consola y schtasks escriben DOMAIN\user; los instaladores suelen escribir el nombre de un fabricante; algunas tareas de Microsoft usan una referencia a un recurso del tipo $(@%SystemRoot%\system32\...) |
Date | Fecha de registro | Normalmente hora local, sin desfase (véase más abajo) |
Description | Descripción legible | Texto libre; los atacantes copian la redacción de Microsoft |
SecurityDescriptor | SDDL opcional para la tarea | Poco habitual en el XML; el descriptor efectivo es el valor SD de TaskCache |
Source, Version, Documentation | Opcionales | De vez en cuando útiles para atribuir la tarea a un fabricante |
Notas de profesionales como las Windows forensic artifacts de Psmths señalan que el Author de una tarea creada en remoto puede revelar la cuenta de origen, pero nada impide que un XML importado lleve un autor falsificado. Compárelo con el SubjectUserName del evento 4698 o 106 cuando disponga de ellos.
Triggers: cuándo se dispara
El esquema admite hasta 48 desencadenadores por tarea: BootTrigger, LogonTrigger, RegistrationTrigger, IdleTrigger, TimeTrigger, CalendarTrigger, EventTrigger y SessionStateChangeTrigger. Todos comparten Enabled, StartBoundary, EndBoundary, Repetition y ExecutionTimeLimit.
Lectura desde el punto de vista de la investigación:
- Los desencadenadores Boot y Logon son persistencia clásica. Un
LogonTriggersinUserIdse dispara para cualquier usuario. - RegistrationTrigger se dispara una sola vez, cuando se registra la tarea: una forma habitual de ejecutar algo de inmediato aparentando ser un trabajo programado.
- Un TimeTrigger con un intervalo de
Repetitioncorto (por ejemploPT5M) es un patrón similar a un beacon. - EventTrigger incluye una consulta XPath en
Subscription; puede hacer que una tarea se dispare ante un ID de evento concreto, algo fácil de pasar por alto. - SessionStateChangeTrigger (
SessionUnlock,RemoteConnect) es menos frecuente y merece una segunda mirada.
Principals: qué cuenta y qué privilegios
| Elemento | Valores | Significado |
|---|---|---|
UserId | Nombre de cuenta o SID (S-1-5-18 es SYSTEM, S-1-5-19 LOCAL SERVICE, S-1-5-20 NETWORK SERVICE) | El contexto de seguridad |
GroupId | Nombre de grupo o SID | Se ejecuta para los miembros de un grupo, p. ej. S-1-5-32-545 (Users) |
LogonType | InteractiveToken, Password, S4U, InteractiveTokenOrPassword | Cómo inicia sesión la cuenta; véase S4U |
RunLevel | LeastPrivilege, HighestAvailable | Si se usa un token de administrador; véase nivel de ejecución |
Una tarea que no es de Microsoft y se ejecuta como S-1-5-18 con HighestAvailable es la combinación que hay que revisar primero. Un LogonType Password significa que se almacenaron credenciales para que la tarea pueda ejecutarse con el usuario desconectado; la guía de Microsoft sobre el evento 4698 recomienda alertar sobre ello porque un administrador puede recuperar la contraseña almacenada.
Settings: los interruptores que ocultan o limitan
Los elementos que conviene leer primero son Hidden, Enabled, ExecutionTimeLimit (PT0S significa sin límite), MultipleInstancesPolicy, StartWhenAvailable, DisallowStartIfOnBatteries, WakeToRun, RunOnlyIfNetworkAvailable y DeleteExpiredTaskAfter.
Hidden=true solo oculta la tarea en la consola si no está marcada la opción "Show Hidden Tasks". No la oculta a schtasks, a la API ni al registro. Las técnicas de ocultación más potentes están en el registro; consulte Tarrask y las tareas programadas ocultas. DeleteExpiredTaskAfter junto con un EndBoundary hace que una tarea se borre a sí misma: que más tarde no esté no demuestra que nunca existiera.
Actions: qué se ejecuta
| Acción | Contenido | Notas |
|---|---|---|
Exec | Command, Arguments, WorkingDirectory | La inmensa mayoría de las tareas |
ComHandler | ClassId (CLSID), Data opcional | Ejecuta un objeto COM, normalmente una DLL cargada en taskhostw.exe; véase acción COM handler |
SendEmail | Servidor, To, Subject... | Obsoleta según Microsoft; poco frecuente en sistemas modernos |
ShowMessage | Title, Body | Obsoleta; poco frecuente |
El elemento Actions tiene un atributo Context que nombra el principal. Se admiten hasta 32 acciones y se ejecutan en orden, así que léalas todas: una primera acción inofensiva puede preceder a una segunda maliciosa.
En el caso de ComHandler, resuelva el CLSID en el hive SOFTWARE (Classes\CLSID\{...}\InprocServer32) para encontrar la DLL. Secuestrar un CLSID que ya usa una tarea legítima es una forma de persistencia más discreta que crear una tarea nueva.
La hora: valores locales sin zona
El esquema define Date, StartBoundary y EndBoundary como xs:dateTime. La API de scripting documenta el formato YYYY-MM-DDTHH:MM:SS(+-)HH:MM con desfase (Trigger.StartBoundary). En la práctica, las tareas creadas desde la consola o con schtasks no almacenan desfase, y la hora es la hora local de la máquina en el momento en que se guardó la tarea. Algunas tareas almacenan una Z o un desfase explícito; la mayoría no.
Esto importa en cuanto se compara con fuentes en UTC. En el ejemplo del evento 4698 de Microsoft, el TimeCreated del evento es 2015-09-23T02:03:06Z y el Date del XML es 2015-09-22T19:03:06.9258653: el mismo instante, con siete horas de diferencia.
Formas de recuperar el desfase:
- TaskCache:
DynamicInfoguarda un FILETIME de creación en UTC para la misma tarea; la diferencia conDatees el desfase, redondeado al cuarto de hora más cercano. Consulte análisis forense del registro TaskCache. - Hive SYSTEM:
TimeZoneInformationindica la zona configurada; recuerde que el horario de verano se aplica según la fecha del suceso, no la fecha de la recogida. - Evento 4698: el
TimeCreateden UTC frente alDateincluido en el evento.
El Scheduled Tasks Parser aplica automáticamente el primer método: compara las fechas de registro del XML con los FILETIME de TaskCache, propone un desfase y le permite modificarlo.
StartBoundary tiene un matiz adicional: indica cuándo se activa el desencadenador, que puede ser en el pasado (las tareas creadas "ahora" suelen recibir el minuto de creación) o muy en el futuro. No es una hora de ejecución. Las horas de ejecución proceden de DynamicInfo en TaskCache y de los eventos 200/201.
Preguntas frecuentes
¿En qué zona horaria está el Date del XML de una tarea programada?
Normalmente en ninguna. El esquema admite un desfase, pero las tareas creadas desde la consola o con schtasks suelen almacenar la hora local sin desfase alguno. Deduzca el desfase a partir de una fuente en UTC, como los FILETIME de TaskCache o el TimeCreated del evento 4698, antes de compararla con otras evidencias.
¿El campo Author demuestra quién creó la tarea?
No. Author es texto libre que escribe aquello que registró la tarea. La consola y schtasks lo rellenan con la cuenta que la crea, pero un script o un XML importado pueden poner cualquier cosa. Trátelo como una afirmación que debe verificarse con los logs de eventos.
¿Qué significa Hidden=true?
La tarea no aparece en la consola del Programador de tareas salvo que el usuario active Show Hidden Tasks. Sigue apareciendo en schtasks y a través de la API. Muchas tareas de Microsoft están ocultas, así que importa sobre todo en tareas que no son de Microsoft.