The Silk Wasm: Obfuscating HTML Smuggling with Web Assembly

The article demonstrates using WebAssembly (Wasm) to perform HTML smuggling by compiling Go-based dropper code into a .wasm module that decodes an embedded, encrypted payload client-side and reconstructs a malicious file for download. The author provides a proof-of-concept tool, SilkWasm, and outlines compilation, HTML integration, and size/obfuscation techniques. #SilkWasm #WebAssembly

Keypoints

  • HTML smuggling embeds an encoded payload (commonly base64) inside an HTML page and reconstructs the original file in the victim’s browser to avoid network-based download detections.
  • Traditional smuggling flow: user visits smuggle page → page contains encoded blob → script decodes/decrypts blob → script reconstructs and triggers download/presentation of the file.
  • WebAssembly (Wasm) enables writing smuggler code in system languages (Go, Rust, C++) and compiling to a binary-like format that is harder for proxies to read than JavaScript.
  • Wasm-based smuggling can bypass some detection tools because the compiled .wasm appears as raw bytes and the decoding logic can run in the Wasm module rather than plaintext JavaScript.
  • SilkWasm is a PoC tool that automates encrypting the payload, filling Go templates, and producing a .wasm smuggle; it includes flags to try to reduce Wasm size (and supports tinygo).
  • Implementation requires embedding Go’s wasm_exec.js runtime, instantiating the .wasm module in the page, calling the exported function to obtain a Uint8Array, creating a Blob/URL, and triggering a download via a user action.
  • Recommended improvements: compile with tinygo or Rust to shrink output, minify/obfuscate runtime and page JS, and trigger downloads on realistic user events to evade automated scanners.

MITRE Techniques

  • [T1027] Obfuscated Files or Information – embedding and encoding payloads within the page to hide them: ’embedding the malicious file within the page, usually in a base64 encoded string.’
  • [T1059.007] JavaScript – using browser scripts to instantiate Wasm, call exported functions, and manipulate browser APIs to reconstruct/download files: ‘the page once opened, runs a script which decodes (and maybe also decrypts) the base64 blob.’
  • [T1204] User Execution – relying on user interaction to trigger reconstruction and download of the payload so the file is executed or saved locally: ‘The file … is presented to the user as though it was an ordinary file download.’

Indicators of Compromise

  • [File name] target/download artifacts – maliciousmacro.doc (example embedded payload filename), test.wasm (example compiled Wasm binary)
  • [HTML/JS file] smuggling page and runtime – smuggle.html (example smuggle page), wasm_exec.js (Go runtime required to run Go-compiled Wasm)
  • [Repository/Reference URL] tooling and source – https://github.com/kopp0ut/godropit/ (original tool reference), https://www.netspi.com/blog/technical/adversary-simulation/obfuscating-html-smuggling-with-web-assembly/ (original blog post)

HTML smuggling embeds an encoded payload inside a webpage (commonly base64) and uses client-side code to decode/decrypt and reconstitute the original file locally so no external file GET occurs. The canonical flow is: user opens smuggle.html → page holds an encoded blob → an on-page script or module decodes/decrypts that blob → the decoded bytes are converted to a Blob/URL and programmatically downloaded or presented to the user (often via a created anchor and .click()).

The Wasm-based variant compiles decryption/reconstruction logic from system languages (Go in the example) into a .wasm module, which is less readable to network proxies than plaintext JavaScript. The provided Go template implements AES decryption of a base64-encoded payload, converts the decrypted bytes into a Uint8Array via js.CopyBytesToJS, and exports a function to return that array to the page. Build with: GOOS=js GOARCH=wasm go build -o test.wasm smuggle.go. SilkWasm automates encrypting the file, populating the Go template, producing the .wasm, and offers flags to attempt Wasm size reduction (tinygo support). Integrate in HTML by including Go’s wasm_exec.js, instantiating the Wasm module (WebAssembly.instantiateStreaming(fetch(“file.wasm”), go.importObject).then(…)), calling the exported function, wrapping the returned bytes in a Blob, and creating a download link to trigger the file save.

To improve stealth and delivery reliability, compile with tinygo or Rust to reduce .wasm size, inline/minify or obfuscate wasm_exec.js and page scripts, and only trigger reconstruction on realistic user events (form submit, button click) to avoid automated crawlers. From a defensive perspective, detection focuses on the same browser APIs used to save files (Blob creation, URL.createObjectURL, programmatic click) and on anomalous Wasm modules that perform decode/decrypt operations; restricting downloads and enforcing application allow-listing reduces the technique’s effectiveness.

Read more: https://www.netspi.com/blog/technical/adversary-simulation/obfuscating-html-smuggling-with-web-assembly/