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

Written on February 26, 2023