Beyond good ol’ Run key, Part 61

3.5 years ago in the part 4 of the series we talked about various Registry keys related to debuggers.

Today we will implement one.

One of the important system processes present in Windows since Vista is the Local Session Manager that runs from the file called lsm.exe. In Windows 10 the service was moved from a separate service process to lsm.dll that is loaded under svchost.exe. I don’t discuss Vista and Win8 details below, because who is using them anyway, but… it should work there too.

The lsm process has a little secret.

When it starts it checks for a presence of the following entries:

  • HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server
    • DebugTS=1 or 2 or 3
    • LSMBreakOnStart=1 or 2 or 3 (Windows 10 only)
    • Debugger = any string

The value of DebugTS (or LSMBreakOnStart) determines how LSM process will behave:

  • 1 – it will check if the user or kernel mode debugger is running, and if any of them is present it will just call DebugBreak to break into debugger session.
  • 2 – if the user mode debugger is not present, the LSM will actually launch it (guess what this post is all about…).
  • 3 – similar to 1.

For the option 2, the LSM will launch the NTSD debugger process using the following command line syntax:

  • ntsd -d -G -x -p <LSM pid>

Yup. The \windows\system32 path is not even specified, so the ntsd just needs to be placed in any location covered by the PATH environment variable.

And in case you are wondering, the ntsd will be launched with the full SYSTEM privileges.

Windows 7:

Window 10:

In order for things to work you need to implement ntsd.exe as a debugger to mimic the expected behavior. It has to take the PID from the command line, or retrieve the parent’s PID via NtQueryInformationProcess (or other means) and attach itself to LSM process. It then needs to create a debugging loop that will ensure the lsm.exe continues to work… and a system starts with the ntsd.exe running in a background. Since the ntsd is launched in an insecure way, it is most likely a subject to a path companion attack (both via PATH and the ntsd.com). I have not tested the latter, cuz it’s 2:00am.

Beyond good ol’ Run key, Part 60

Today I’ll describe a little secret – a stealthy autostart key that is not very well-known and is very reliable since it is used by… well… the Windows Update.

Yup, it’s a pretty handy one…

This is the key:

  • HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Setup\ServiceStartup

It has two interesting properties:

  • It can be used as a persistence mechanism
  • It can also mysteriously make files re-appear on the system as the mechanism is used to update files and as such, the entries are being used to copy files during the system start (by the ‘svchost.exe -k netsvcs’ process)

This is an example entry:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Setup\ServiceStartup\malware.dll
"RegistrationFlags"=dword:00000001
"CacheFile"="C:\\test\\malware.dll"
"TargetFile"="C:\\WINDOWS\\system32\\malware.dll"

The entry will force the file:

  • C:\test\malware.dll

to be loaded.

It will be also copied to:

  • C:\WINDOWS\system32\malware.dll

Note that you can also simply ignore the copying and drop the file anywhere, and just point ‘CacheFile’ and ‘TargetFile’ to it.

If you look at the first screenshot you will notice the Procmon logs show a reference to the ’20MUIFixUp’ entry.

Interestingly, this entry can be used to just copy files, so adding a reg combo as below:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Setup\ServiceStartup\malware.dll
"20MUIFixUp"=dword:00000001
"CacheFile"="C:\\test\\malware.dll"
"TargetFile"="C:\\WINDOWS\\system32\\malware.dll"

will create ‘C:\WINDOWS\system32\malware.dll’ anytime you boot the system (but the file won’t be loaded/executed). The’ 20MUIFixUp’ entry in the Registry will be deleted afterwards (so it needs to be re-created).

Interestingly, the information about the loaded DLLs can be found inside the Windows Update log file:

  • c:\WINDOWS\WindowsUpdate.log

Here’s an excerpt:

Misc    Registering binary: C:\WINDOWS\system32\regsvr32.exe  /s "C:\WINDOWS\system32\malware.dll"
Misc    FATAL: Self registration of C:\WINDOWS\system32\malware.dll failed, error = 0x8024D007
Setup    FATAL: Failed to register C:\WINDOWS\system32\malware.dll: error 0x8024d007
Setup    FATAL: ProcessDelayedCopies failed: 0x8024d007
Service    FATAL: Failed to fix up registration at service startup: 0x8024d007

My test DLL was not prepared correctly (missing exported ‘DllInstall’ – see below), but the DLL still loads.

At least on Windows XP.

Okay, what about newer versions of Windows?

First, let’s fix my DLL.

To do so, I actually add two exported functions:

  • DllInstall, and
  • DllRegisterServer.

Why?

Because the ‘RegistrationFlags’ parameter can be equal either to ‘1’ or ‘2’.

If it is 1, the ‘DllInstall’ API will be called. Otherwise, it will be the ‘DllRegisterServer’ API.

When these DLLs are loaded and respective functions are called the Windows Update service logs the appropriate information to the ‘WindowsUpdate.log’ file – let’s have a look at the two examples:

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Setup\ServiceStartup\malware.dll
"RegistrationFlags"=dword:00000001
"CacheFile"="C:\\test\\malware.dll"
"TargetFile"="C:\\test\\malware.dll"

and

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Setup\ServiceStartup\malware.dll
"RegistrationFlags"=dword:00000002
"CacheFile"="C:\\test\\malware.dll"
"TargetFile"="C:\\test\\malware.dll"

When Windows Update service is restarted, you will get the following log entries inside the WindowsUpdate.log:

"RegistrationFlags"=dword:00000001
Misc    Registering binary: C:\Windows\system32\regsvr32.exe  /s "C:\test\malware.dll"
Agent      * WU client version 7.6.7600.256
Agent      * Base directory: C:\Windows\SoftwareDistribution
Agent      * Access type: No proxy
Agent      * Network state: Connected

"RegistrationFlags"=dword:00000002
Misc    Registering binary: C:\Windows\system32\regsvr32.exe  /s /n /i "c:\test\malware.dll"
Agent      * WU client version 7.6.7600.256
Agent      * Base directory: C:\Windows\SoftwareDistribution
Agent      * Access type: No proxy
Agent      * Network state: Connected

The trick works like a charm for Windows 7. I don’t see it working on Windows 10 though, but perhaps it has to be triggered some other way.