Lateral Movement using WSHController/WSHRemote objects (IWSHController and IWSHRemote interfaces)

Re-discovering old tricks is fun, especially in a context of learning the very desirable know-how about all the possible evasion tricks and stealth techniques that should be well known to both red and blue teams. And especially tricks that allow lateral movement.

If you are wondering why I am saying ‘re-discovering’ it is because what I am going to cover in this post has been used in the past a lot, and googling around you can find references to code as early as 1989!

I came across it while reading about various Windows interfaces and these 2 caught my attention:

(or WSHController and WSHRemote as they are referred all over the place), and immediately realized that it’s yet another, not so well-known, lateral movement technique.

After few unsuccessful tries, I made it work and am presenting to you a quick & dirty recipe so you can try to replicate it in your lab.

This is the trick in action (left side – target system, right side – attacker):

You can re-use the code pasted on Microsoft site, and adapt it to your needs (a.k.a. edit the name of the remote computer and file name of the script):

strRemoteComputer = "RASServer01"
strWorkerScript = "MapNetworkDrive.vbs"
Set objWshController = WScript.CreateObject("WshController")
Set objRemoteScript = _
objWshController.CreateScript(strWorkerScript, strRemoteComputer)
objRemoteScript.Execute

Do While Not objRemoteScript.Status = 2
   Wscript.Sleep(100)
   Wscript.Echo "Remote script not yet complete."
Loop

When you try it for the first time, you will fail.

Why?

Lots of reasons. It turns out this feature needs a bit of preparation before it can be used.

After poking around and reading what other people did to make it work, I put these ideas together:

  • Use Admin account to execute the two actions described next (wscript doesn’t return an error if it can’t write Registry keys!)
  • Run the following command on both server and client (some sites suggest client only, but you do need to register it on the server too!)
    • wscript -regserver
  • Ensure that the client (target machine) has the following Registry setting enabled:
    • HKLM\SOFTWARE\Microsoft\Windows Script Host\
      Settings\Remote=1 (Reg_SZ)

That’s it. Now the script should work.

If you are worrying that running the ‘wscript -regserver’ puts this trick in a catch 22 department (we need to run one process remotely first to run our script) don’t worry. The ‘wscript -regserver’ adds a bunch of Registry keys, and values – they can be as well added using remote Registry functions and this doesn’t require running processes remotely at all!

Here’s a high-level list of these keys – if you want detailed values you can grab them from a regshot session on your test lab box:

  • HKLM\SOFTWARE\Classes\CLSID\{6F201542-B482-11D2-A250-00104BD35090}
  • HKLM\SOFTWARE\Classes\Interface\{6F201541-B482-11D2-A250-00104BD35090}
  • HKLM\SOFTWARE\Classes\Interface\{83EA33C0-CD14-11D2-A252-00104BD35090}
  • HKLM\SOFTWARE\Classes\Interface\{8A9EA2C0-D348-11D2-A253-00104BD35090}
  • HKLM\SOFTWARE\Classes\TypeLib\{6F201540-B482-11D2-A250-00104BD35090}
  • HKLM\SOFTWARE\Classes\WSHRemote

From a forensic perspective you will want to pay attention to these artifacts:

  • Registry artifacts described above (Remote value + Classes entries)
  • Files created in a user temporary directory – if launched on the localhost
    • %TEMP%\wsh*.tmp
    • %TEMP%\wsh*.tmp.vbs
  • Files created in a Windows temporary directory of the target system (if launched on the remote host)
    • C:\Windows\Temp\wsh*.tmp
    • C:\Windows\Temp\wsh*.tmp.vbs
  • Presence of a process wscript.exe spawn via svchost.exe:
    • C:\WINDOWS\system32\wscript.exe -Embedding

In terms of Event Logs, the only activity I see is a number of Events logged in the Security logs:

  • 4672: Special privileges assigned to new logon.
  • 4624: An account was successfully logged on.
  • 4634: An account was logged off.

So, seeing this triplet in a short time span may be a good indicator of ongoing lateral movement using this technique.

There is also another bit. Since you can use this trick on the localhost it could be used to break the process tree (as seen by e.g. EDR solutions), and potentially evade some sandbox analysis (processes not spawn directly by the analyzed sample(s) or their child processes may be sometimes ignored unless the sandbox is aware of the evasion trick and monitors its use).

Beyond good ol’ Run key, Part 84

This is just a blurb to include this particular technique in the series. It’s not new at all, and it has been covered in the past by others e.g. recent ‘Windows Operating System Archaeology’ preso by @enigma0x3 and @subTee. IMHO it’s still worth documenting it anyway.

COM Objects are created when the program calls one of the instantiation APIs e.g. CoCreateInstance. The API takes the CLSID, finds its entry in the Registry (under CLSID node) and loads the DLL or EXE associated with it (it’s a bit more complex than that, but that’s the gist of it).

Now, if the CLSID  entry contains the key ‘TreatAs’ the instantiation process will change – the API will pick up a CLSID that ‘TreatAs’ points to and instantiate this object instead. This allows anyone to hook all the instantiation events of a particular COM object and as a result load anytime that object is istantiated. Which sounds like a nice persistence trick…

This is actually a documented feature and it’s described on MSDN in a help for the CoTreatAsClass API and the TreatAs key itself; the internals were also described in this Microsoft blog post.

So… anytime you see ‘TreatAs’ key, ensure it’s not a COM hijack 🙂