Notes while working through the (excellent) Sektor7 windows persistence course. Important point: don’t just rely on the notes here. They’re mainly reminders for me :) It’s the templates and tools as well as extra context that makes the training really valuable. (Tools include things like scripts to AES encrypt and dynamically decrypt the payloads associated with the persistence techniques).

Low privilege:

Start folder:

  • c:\Users\UserName\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
  • %APPDATA% probably an easier way to go about it. Like copy myimplant.exe "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"

Registry keys:

  • HKCU\Software\Microsoft\Windows\CurrentVersion\Run (or RunOnce). Add REG_SZ
  • Note, there is the machine equivalent, but this is the low priv section of the notes. If you had admin rights you could also set HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run

Logon Scripts:

Shortcut Modification:

  • Modifying the .lnk files that the user interacts with regularly.
  • They are not straight up text files and you can’t just modify them directly to point somewhere else.
  • The course script provides an example of a shortcut creator that will replace a lnk on the desktop with one that is the same except for the target. The new target is a script that launches the payload and then the intended shortcut target.


  • HKCU\ControlPanel\Desktop contains the screensaver keys (if configured).
  • We add SCRNSAVE.EXE as a string value pointing at ourimplant.exe
  • We also add the timeout before the “screensaver” should execute - ScreenSaveTimeOut
  • Keep in mind GPO might override this.

PowerShell Profile:

DLL Proxy

  • We’re hoping to replace something in the search path with our code.
  • dll loader is checking for dll already in memory first, then HKLM\SYSTEM\CurrentControlSet\Session Manager\KnownDLLs, then the applications folder, then c:\windows\system32 and SysWow64 (depending), then c:\windows\system, c:\windows, current directory and finally things in %PATH%.
  • You want to be careful here to create a module that can pass off to the real .dll if breaking the application is a concern.
  • Procmon is a good way to find out if the application is a good candidate that is calling .dll’s. Result contains ‘NOT FOUND’ and the process name is a good starting filter.
  • dumpbin (SDK tools) can allow us to inspect the imported functions. We need to be careful though, sometimes there are additional functions required (via getProcAddress). If you wanted to be mega safe you could do all the exported functions from a dll (found using dumpbin).
  • Sektor7 demo and template for creating the proxy .dll is the way to go here. The lazy way is to let the app crash, but if we find a reference to a dll with only a few functions we can proxy to the real dll without too much effort.
  • Remember to be careful with 32 bit v 64 bit with the .dll.


  • .dll is created in one language but designed to be usable by developers in others
  • via Application Binary Interface (ABI)
  • Identified by Unique Id (and referenced by Id)
  • COM objects can live on a different machine, transparent to the application; appears like a regular local function call.
  • For Process Loading: (Program.exe -> OLE32.DLL) – Service Control Manager (SCM) – Registry (HKCR which is first HKCM\Software\Classes and then HKLM\Sofware\Classes) – the .dll referenced in the registry path. Then, the app loads it like a ‘normal’ dll. We see these as InProcServer.
  • We may be able to hijack at the HKCU lookup
  • For Local COM Loading: Similar, but the COM local proxy inside the process will use RPC to communicate with a separate local process on the machine (still specified in HKCR and referenced via the SCM). We see these as LocalServer in the registry.
  • For Network COM Loading: similar, but the SCM on the hosting machine will call out to the remote SCM on the machine hosting the COM Server. The application will call out via RPC.
  • HKCR\CLSID is the place to check out the registrations (the .dll’s linked to id’s). But remember that it is made up of HKLM and HKCU components. The HKCU\Sotftware\Classes\CLSID section is writable low priv, but it is a much smaller set of COM Id’s than we’d see in the local machine section.
  • We can find some interesting COM persistence in our scheduled tasks. When we export all the tasks we can inspect for LogonTrigger types.
  • We also know that although most of the com objects are in HKLM, we can hijack by adding to HKCU because that location is checked first. Sektor7 provide a template for creating a hijack .dll. (Implementing the DllGetClassObject function). But to take it even further, they show the proxy technique to make the system function ‘as normal’.

Higher privilege:

Scheduled Task

  • Standard: schtasks /create /tn 'Mytasks\go' /sc daily /st 10:00 /tr 'c:\mything\mything.exe'
  • Standard (created as admin): schtasks /create /sc onlogon /tn "BonzaiBuddyUpdate" /tr 'c:\mything\mything.exe'
  • To create admin level task we export the definition via schtasks /query /tn BonzaiBuddyUpdate /xml then edit the principal section we edit the to HighestAvailable. Then we delete the existing with `schtasks /delete /f /tn BonzaiBuddyUpdate` and create again with `schtasks /create /tn BonzaiBuddyUpdate /xml task.xml` with the edited task. (If we don't do this we end up with a task that _could_ run as admin, but wont by default).
  • It’s also possible to configure the task with multiple actions which would allow the original action to happen.

New or Modified Services

  • Either create a new one, or modify an existing service.
  • sc create UpdaterService binpath= "c:\ourImplant.exe" start= auto
  • Default for this will be local system.
  • sc query UpdaterService to confirm.
  • We need to make sure our implant is compiled as a service though, else SCM is going to give up on the process reasonably quickly. Sektor7 provide a template. There are SCM related functions that we should have and they’ve provided them. (We just need to implement the RunMe section of the template and optionally include AES Encryption function for payload - also provided)
  • We could modify an existing service instead via sc config UpdaterService binpath= "c:\somethingelse.exe"

Image File Execution Options

  • IFEO is around to make debugging easier (attach the debugger or tools to process in various scenarios).
  • Debugger, Exit, or Verifier options.
  • Debugger allows us to start a process under a debugger (but instead we use our binary)
  • Exit allows the same on exit
  • Verifier options are the old school compat options we used to touch via verifier.exe that allow us to hook specific functions.
  • HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
  • We create a subkey for the process we want to start under a ‘debugger’ (our persistence implant).
  • The challenge here is that we are effectively starting a ‘debugger’ now, passing the real process as a parameter to the debugger, but since our implant is not really a debugger the actual process wont get a chance to start. We’d either have to implement this, or choose a process that wouldn’t be noticed.
  • To do the same on exit, we set up a ReportingMode key in the same place.
  • To do the same for verifier we use VerifierDlls but we need to remember that the .dll needs to live in system32 or syswow AND we need to use a specific dll type that Sektor7 provide a template for (it has a DLL_PROCESS_VERIFIER event case configured; we use that to create a thread for the persistence module). We also need to set GlobalFlag to make this work. You can play with this stuff with verifier to see legitimate uses.

Application Shims

  • Windows AppCompat
  • Hook address table in the process, patch app code, inject dll’s to make it work across OS.
  • More relevant to 32 bit process (limited 64 bit support)
  • Inject dll is the app compat option from the App Compat toolkit we leverage.
  • Once we create the shim we need to install it via sdbinst OurShim.sdb (sdbinst-u OurShim.sdb to remove)

WMI Event Subscription

  • “Windows Management Instrumentation (WMI) is the Microsoft implementation of Web-Based Enterprise Management (WBEM), which is an industry initiative to develop a standard technology for accessing management information in an enterprise environment. WMI uses the Common Information Model (CIM) industry standard to represent systems, applications, networks, devices, and other managed components. CIM is developed and maintained by the Distributed Management Task Force (DMTF).”
  • Made up of filters, consumers and bindings.
  • WMI event subscriptions persist over reboot.
  • wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter GET /format:list to see the current event filters.
  • wmic /NAMESPACE:"\\root\subscription" PATH __EventCOnsumer GET /format:list to see current event consumers.
  • wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding GET /format:list to see bindings
  • To set up WMI based persistence:
wmic /NAMESPACE:"\\root\subscription" PATH __EventFilter CREATE Name="INFilter", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="Select * From __InstanceCreationEvent Within 15 Where (TargetInstance Isa 'Win32_Process' And TargetInstance.Name = 'wordpad.exe')"

wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer CREATE Name="INConsumer", WorkingDirectory="C:\rto\PERS\implant", CommandLineTemplate="c:\rto\PERS\implant\implant.exe"

wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding CREATE Filter="__EventFilter.Name=\"INFilter\"", Consumer="CommandLineEventConsumer.Name=\"INConsumer\""
  • WMI runs as SYSTEM in session 0 by default, so testing implants might not be visible (but you’ll see with procexp)
  • remove:
wmic /NAMESPACE:"\\root\subscription" PATH __FilterToConsumerBinding WHERE Filter="__EventFilter.Name='INFilter'" DELETE
wmic /NAMESPACE:"\\root\subscription" PATH  __EventFilter WHERE Name="INFilter" DELETE
wmic /NAMESPACE:"\\root\subscription" PATH CommandLineEventConsumer WHERE Name="INConsumer" DELETE

AppCert DLL’s

  • Loaded during the first call of any win32 functions that load process like CreateProcess*
  • In HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls we add a dll to AppCert
  • It’s a special DLL type though (template provided) that has a CreateProcessNotify function.
  • Once configured, you can see them in the “AppInit” section of autoruns:


AppInit DLL’s

  • Very similar to AppCert DLL
  • HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows and add a dll to AppInit_DLLs. We also need to modify LoadAppInit_DLLs to 1
  • Any valid DLL will work with this one though (unlike AppCert DLL approach). BUT! You need to be careful how it is used. Because every process will load the config we define. This includes the process we are creating, and the process it creates and so on… (infinite). Shellcode might be a safer approach here - creating a process would be bad.

Netsh Helper DLLs

  • Microsoft added extensibility to NetSh (helper dll)
  • Downside: relies on running netsh. You’re looking for enterprise with scripts.
  • We need to use a specific DLL type that exports a function called InitHelperDll (but its otherwise similar to standard dll)
  • netsh.exe add helper c:\temp\nethelp.dll to add the .dll (which adds a registry entry). remove to get rid of it.


  • HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Winlogon we look at shell which is the system shell being used; userinit initializes the user session.
  • Interestingly, they can store more than one application in the value like explorer.exe,backdoor.exe (where backdoor is also in system32).
  • Unlike Shell, userinit will take a full path.

Time Providers

  • Inward (get time) and Outwards (provide time) both controlled by the SCM.
  • HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\W32Time\TimeProviders\TimeProv. You’ll see NTPClient, NTPServer usually, but might also see others for VM software etc on a standard machine. To set up a new one we create a new key and define DllName (backdoor), Enabled (flag) and InputProvider (flag).
  • The course provides a template .dll that will provide the three required functions TimeProvopen etc.
  • We restart w32tm to get the provider loaded.
  • Since this is service based it will run in session 0 (wont be visible in testing except via procexp etc)

Port Monitors

  • Very similar to time providers, but on the printing side of the house.
  • dll that can pass commands to the printer.
  • Use the template provided by Sektor7 which implements the functions required. It has to export an InitializePrintMonitor2 function at the least, but there’s also other implementation requirements that make the template particularly handy in this case.
  • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Print\Monitors\PortMonitor just takes a driver dll value as a string. It needs to live in System32 (cant be a path).
  • AddMonitor API is an alternate method for configuration. []](

Local Security Authority (LSA)

  • Dangerous one :) We’ll load inside lsass and in addition to being system, we’ll have access to whatever is in the process.
  • If LSASS is running protected it should kill this though, because it requires signed (by Microsoft) code. See
  • One approach is to use “SSPs & AuthPkgs”, we’ll be looking in here HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa under SecurityPackages and Authenticationackages. More information is available on MSDN:
  • The SSP approach will require us to implement specific functions but the template provided by Sektor7 is a perfect start.
  • When configuring the registry for multi string type to support this, don’t forget the \0 zero byte delim between packages.
  • Password Filters are another approach in this space. They show up in the same spot HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa but under Notification Packages which is a multi-string value (remember the \0 delim).
  • Sektor7 provide the template which implements the basic format of a password filter.
  • LSASS will load the password filters on reboot and execute the persistence code.