How to Hack Web Assembly

Web Assembly(웹 어셈블리)는 웹에서 바이너리 포맷을 처리할 수 있는 로우 레벨의 어셈블리 언어입니다. 웹 위에서 동작하지만 네이티브단에서 동작하는 느낌으로 돌 수 있고, Javascript와 함께 동작할 수 있어 고성능 처리와 빠른 구현 모두 어느정도 잡을 수 있는 언어입니다.

이 문서는 이러한 Web Assembly에 대해 테스팅하고 보안적인 문제를 찾는 방법에 대해 정리합니다.

Structure

WebAssembly 개요

WebAssembly는 웹에서 바이너리 포맷을 처리할 수 있는 로우 레벨의 어셈블리 언어입니다. JavaScript와 함께 동작하며, 다음과 같은 특징을 가집니다:

  • 네이티브에 가까운 성능
  • JavaScript와의 상호 운용성
  • 브라우저의 샌드박스 환경에서 안전한 실행
  • C, C++, Rust 등 다양한 언어에서 컴파일 가능

WebAssembly 구조도

기본 예제

#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;
}

API Documents

Hack Mechanism

각 브라우저에서 WebAssembly는 동일한 방식으로 동작하며, 전통적인 바이너리 공격과 웹 해킹 기법이 모두 적용될 수 있는 환경입니다.

Security Testing Points

WebAssembly로 만들어진 어플리케이션은 다음과 같은 보안 테스팅 포인트를 가집니다:

  • 전통적인 바이너리 관련 공격들: BOF, FSB, OOB 등 알려진 기법들이 적용될 수 있지만, WebAssembly 자체적으로 보안 로직이 적용되어 있어서 ROP 등은 어렵습니다.
  • 웹 공격들: 기존 웹 공격과 차이가 있다면, 추가적으로 웹 페이로드나 공격코드가 C단을 거쳐서 넘어올 수 있다는 점입니다.
  • Indirect function call: XSS 등으로 JS 제어권을 가졌을 때 할 수 있는 공격 벡터가 확장됩니다.

Code audit

WebAssembly 파일은 일반적으로 .wasm 확장자를 가지며, 바이너리 형태로 제공됩니다. 분석을 위해서는 이를 디컴파일하고 관련 JavaScript 코드를 함께 살펴봐야 합니다.

Find WASM files

WebAssembly 파일을 찾는 방법:

  1. JavaScript에서 WebAssembly instance 찾기: SWF, ActiveX 처럼 어차피 JS에서 핸들링하기 때문에 결국 코드에는 관련 함수나 주소 정보가 남아있습니다.

  2. WebAssembly 로딩 함수 추적:

// instantiateStreaming 사용
WebAssembly.instantiateStreaming(fetch('simple.wasm'), importObject)
.then(results => {
  // Do something with the results!
});

// 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();
  });
};
  1. 브라우저 개발자 도구: Sources 탭에서 wasm 파일을 직접 확인할 수 있습니다.

WASM 파일 구조

WASM 파일의 헤더는 .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

Decompile WASM & find info

WASM 디컴파일을 위한 도구 설치:

git clone --recursive https://github.com/WebAssembly/wabt
apt install clang     # (clang이 없는 경우)
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

Live audit

Test with devtools

웹 브라우저의 개발자 도구에서 WebAssembly 객체에 접근할 수 있습니다:

// WebAssembly 객체 확인
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()

Function testing

JavaScript에서 WASM function을 직접 호출하여 테스트할 수 있습니다. 이는 SWF나 ActiveX 분석과 동일한 방식입니다.

Function testing

A major weakness

Memory Safety Issues

WebAssembly는 메모리 안전성을 제공하지만, 여전히 다음과 같은 취약점이 발생할 수 있습니다:

  • Buffer overflow in linear memory
  • Integer overflow/underflow
  • Out-of-bounds memory access

JavaScript Interface Vulnerabilities

WASM과 JavaScript 간의 인터페이스에서 발생할 수 있는 취약점들:

  • Type confusion in parameter passing
  • Improper input validation
  • Memory corruption through shared memory

SOP (Same-Origin Policy) 이슈

WASM 파일을 가져올 때 fetch 등을 이용하기 때문에 강제로 브라우저 SOP를 적용받게 됩니다:

fetch('https://www.hahwul.com')
// 교차 출처 요청 차단: 동일 출처 정책으로 인해 원격 자원을 차단

하지만 WASM에서 JavaScript로 데이터를 줄 수 있기 때문에:

  • SOP를 우회할 수 있는 CORS 적용 범위 확인
  • 동일 도메인에 파일 업로드가 가능한지 확인 (공격자가 재구성한 WASM 파일 로드 가능성)

🛠 Environment

Essential Tools

WABT (WebAssembly Binary Toolkit):

git clone --recursive https://github.com/WebAssembly/wabt
make

포함된 도구들:

  • wasm-objdump: WASM 파일 구조 분석
  • wasm2c: WASM을 C 코드로 디컴파일
  • wasm2wat: WASM을 텍스트 포맷으로 변환
  • wat2wasm: 텍스트 포맷을 WASM으로 변환

Emscripten:

git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest

Browser DevTools: 모든 주요 브라우저에서 WebAssembly 디버깅 지원

Testing Methodology

  1. 정적 분석: WABT 도구를 사용한 WASM 파일 분석
  2. 동적 분석: 브라우저 개발자 도구를 통한 런타임 분석
  3. 인터페이스 테스팅: JavaScript-WASM 간 데이터 흐름 분석
  4. 메모리 분석: Linear memory 접근 패턴 확인

Articles

References