WebAssembly is a low-level assembly language that can process binary formats on the web. It runs on the web but feels like native execution, and can work together with JavaScript to achieve both high-performance processing and rapid implementation.
This document outlines methods for testing and finding security vulnerabilities in WebAssembly applications.
WebAssembly is a low-level assembly language that can process binary formats on the web. It works together with JavaScript and has the following characteristics:
#include <stdio.h>
#include <sys/uio.h>
#define WASM_EXPORT __attribute__((visibility("default")))
WASM_EXPORT
int main(void) {
printf("Hello World\n");
}
/* External function that is implemented in JavaScript. */
extern void putc_js(char c);
/* Basic implementation of the writev sys call. */
WASM_EXPORT
size_t writev_c(int fd, const struct iovec *iov, int iovcnt) {
size_t cnt = 0;
for (int i = 0; i < iovcnt; i++) {
for (int j = 0; j < iov[i].iov_len; j++) {
putc_js(((char *)iov[i].iov_base)[j]);
}
cnt += iov[i].iov_len;
}
return cnt;
}
WebAssembly operates the same way in each browser, creating an environment where both traditional binary attacks and web hacking techniques can be applied.
WebAssembly applications have the following security testing points:
WebAssembly files typically have a .wasm
extension and are provided in binary format. For analysis, you need to decompile them and examine the related JavaScript code together.
Methods to find WebAssembly files:
Finding WebAssembly instances in JavaScript: Like SWF and ActiveX, since they're ultimately handled in JS, function or address information remains in the code.
Tracking WebAssembly loading functions:
// Using instantiateStreaming
WebAssembly.instantiateStreaming(fetch('simple.wasm'), importObject)
.then(results => {
// Do something with the results!
});
// Using XMLHttpRequest
request = new XMLHttpRequest();
request.open('GET', 'simple.wasm');
request.responseType = 'arraybuffer';
request.send();
request.onload = function() {
var bytes = request.response;
WebAssembly.instantiate(bytes, importObject).then(results => {
results.instance.exports.exported_func();
});
};
WASM file headers start with .asm
:
hexdump -C fail.wasm
00000000 00 61 73 6d 01 00 00 00 01 85 80 80 80 00 01 60 |.asm...........`|
00000010 00 01 7f 03 82 80 80 80 00 01 00 06 81 80 80 80 |................|
00000020 00 00 07 8b 80 80 80 00 01 07 66 61 69 6c 5f 6d |..........fail_m|
00000030 65 00 00 0a 8d 80 80 80 00 01 87 80 80 80 00 00 |e...............|
00000040 41 01 41 00 6d 0b |A.A.m.|
00000046
Installing tools for WASM decompilation:
git clone --recursive https://github.com/WebAssembly/wabt
apt install clang # (if clang is not available)
make
Object dump:
./wasm-objdump -xd fail.wasm
fail.wasm: file format wasm 0x1
Section Details:
Type[1]:
- type[0] () -> i32
Function[1]:
- func[0] sig=0 <fail_me>
Global[0]:
Export[1]:
- func[0] <fail_me> -> "fail_me"
Code Disassembly:
00003a <fail_me>:
000040: 41 01 | i32.const 1
000042: 41 00 | i32.const 0
000044: 6d | i32.div_s
000045: 0b | end
Decompile to C:
./wasm2c fail.wasm
You can access WebAssembly objects in the browser's developer tools:
// Check WebAssembly object
WebAssembly
// CompileError: function CompileError()
// Global: function Global()
// Instance: function Instance()
// LinkError: function LinkError()
// Memory: function Memory()
// Module: function Module()
// RuntimeError: function RuntimeError()
// Table: function Table()
// compile: function compile()
// compileStreaming: function compileStreaming()
// instantiate: function instantiate()
// instantiateStreaming: function instantiateStreaming()
// validate: function validate()
You can directly call WASM functions from JavaScript for testing. This is identical to SWF or ActiveX analysis methods.
While WebAssembly provides memory safety, the following vulnerabilities can still occur:
Vulnerabilities that can occur at the interface between WASM and JavaScript:
When fetching WASM files using fetch, etc., the browser's SOP is forcibly applied:
fetch('https://www.hahwul.com')
// Cross-origin request blocked: Same-origin policy blocks remote resources
However, since WASM can pass data to JavaScript:
WABT (WebAssembly Binary Toolkit):
git clone --recursive https://github.com/WebAssembly/wabt
make
Included tools:
wasm-objdump
: WASM file structure analysiswasm2c
: Decompile WASM to C codewasm2wat
: Convert WASM to text formatwat2wasm
: Convert text format to WASMEmscripten:
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
Browser DevTools: All major browsers support WebAssembly debugging