Web Assembly(웹 어셈블리)는 웹에서 바이너리 포맷을 처리할 수 있는 로우 레벨의 어셈블리 언어입니다. 웹 위에서 동작하지만 네이티브단에서 동작하는 느낌으로 돌 수 있고, Javascript와 함께 동작할 수 있어 고성능 처리와 빠른 구현 모두 어느정도 잡을 수 있는 언어입니다.
이 문서는 이러한 Web Assembly에 대해 테스팅하고 보안적인 문제를 찾는 방법에 대해 정리합니다.
Structure
WebAssembly 개요
WebAssembly는 웹에서 바이너리 포맷을 처리할 수 있는 로우 레벨의 어셈블리 언어입니다. JavaScript와 함께 동작하며, 다음과 같은 특징을 가집니다:
- 네이티브에 가까운 성능
- JavaScript와의 상호 운용성
- 브라우저의 샌드박스 환경에서 안전한 실행
- C, C++, Rust 등 다양한 언어에서 컴파일 가능
기본 예제
#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
- WebAssembly: https://webassembly.org/docs/
- MDN WebAssembly: https://developer.mozilla.org/ko/docs/WebAssembly
- WebAssembly Text Format: https://webassembly.github.io/spec/core/text/index.html
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 파일을 찾는 방법:
-
JavaScript에서 WebAssembly instance 찾기: SWF, ActiveX 처럼 어차피 JS에서 핸들링하기 때문에 결국 코드에는 관련 함수나 주소 정보가 남아있습니다.
-
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();
});
};
- 브라우저 개발자 도구: 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 분석과 동일한 방식입니다.
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
- 정적 분석: WABT 도구를 사용한 WASM 파일 분석
- 동적 분석: 브라우저 개발자 도구를 통한 런타임 분석
- 인터페이스 테스팅: JavaScript-WASM 간 데이터 흐름 분석
- 메모리 분석: Linear memory 접근 패턴 확인