libinjection in the browser with wasm
libinjection is a small standalone C library for scanning input strings for possible SQL Injection or Cross Side Scripting (XSS). Libraries like libinjection are commonly used with Web Application Firewalls (WAF) for validating/sanitizing client side requests before they encounter server side logic dealing with external/internal resources like database access or server side API calls.
Compiling wasm
I compiled Libinjection source with clang with wasm target (target=wasm32
).
The compilation line was:
clang \
--target=wasm32 \
--no-standard-libraries \
-Wl,--export-all -Wl,--no-entry \
-o libinjection.wasm \
string.c \
./libinjection_html5.c \
./libinjection_xss.c \
./libinjection_sqli.c
I added a few C utilities from libc
like memcpy
/strnlen
etc into a module (string.c
) for compiling with --no-standard-libraries
. Other better approaches include linking with wasi-libc (which include release assets for their sdk). Building generated the .wasm
file for use in the browser application.
There’s a lot of great references around the web with hello world
wasm projects. I found this one especially helpful.
Calling from JavaScript
I really need a crash course in JavaScript memory and interoperability with wasm
. Emscripten has good utilities for passing data back and forth, but since I was embedding this in a single page of html sans third-party libs, I built something more basic.
Converting from a string value retrieved from the DOM to a UInt8
array for passing to a C function:
function convertFromString(string) {
const encoder = new TextEncoder();
const bytes = encoder.encode(string)
const buffer = new Uint8Array(gWasm.instance.exports.memory.buffer, gMemory, bytes.byteLength + 1)
buffer.set(bytes);
return buffer
}
Where the gWasm
is WebAssembly module compiled and instantiated from thelibinjection.wasm
WebAssembly file.
const importObject = {imports: {}};
(async () => {
gWasm = await WebAssembly.instantiateStreaming(fetch('libinjection.wasm'), importObject);
})();
The resulting buffer is passed to the C function:
buffer = convertFromString(document.getElementById("form_field").value);
xssVal = gWasm.instance.exports.libinjection_xss(buffer.byteOffset, buffer.length);
xssField = document.getElementById("xss_show_result");
if (xssVal) {
...
The entirety of the html/code is embedded in the markdown for this blog post.
Example
In the form below try samples like:
default=<script>alert(document.cookie)</script>
user=-1+union+select+1,2,3,(SELECT+user_pass+FROM+wp_users+WHERE+ID=1)
Input
libinjection version:
XSS Result:
SQLI Result:
SQLI Fingerprint:
Is this Useful?
NOPE! Not really. It’s just a little example of porting a small C library to wasm for use in the browser. This provides no protection from injection or XSS. It could be useful for validating strings that could be flagged as potential false positives from WAF’s that use libraries like libinjection server-side.
References
- libinjection source: libinjection
- WebAssemble.Memory: https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/Memory
- Compiling C to WebAssembly and Running It - without Emscripten: https://depth-first.com/articles/2019/10/16/compiling-c-to-webassembly-and-running-it-without-emscripten/
- How to Pass Strings Between JavaScript and WebAssembly: https://rob-blackbourn.github.io/blog/webassembly/wasm/strings/javascript/c/libc/wasm-libc/clang/2020/06/20/wasm-string-passing.html