JS Object - SpiderMonkey
Exploit Knowledge
- Exploit은 JS Object로 임의 주소에 접근한 후 Stack Pivoting이라는 기법을 이용해 진행된다
- 이에 사용되는 JavaScript의 Object인 JS Object가 ArrayBuffer Object와 DataView Object이다
- Adobe는 SpiderMonkey라는 JavaScript Engine을 사용해 PDF에 내장된 JS code를 compile한다. 이 블로그는 SpiderMonkey Engine에서 JS Object에 관해 기술하였다.
1. ArrayBuffer Object
- Reference 타입의 데이터 형태로 Object가 Heap에 할당된다.
- 고정된 길이의 연속된 메모리 공간을 할당해 사용한다.
1) ArrayBuffer Object의 특징
- 메모리에서 사용할 바이트 수를 명시하며 선언한다
var arrbuf = new ArrayBuffer(bytes)
-
Array와 달리 무작위 위치의 데이터에 접근 할 수 없다.
-
다시 말해, arrbuf[0], arrbuf[1], … 등의 형태로 데이터에 접근할 수 없다.
-
view
역할을 하는 객체 (TypedArray)를 사용한다-
TypedArray : 특정 자료형으로 값을 읽을 수 있다.
-
C언어의 Union과 유사한 기능을 수행한다고 볼 수 있다.
-
Int8Array : 8 bit의 정수로 값에 접근한다.
-
Uint8Array : 8 bit의 음이 아닌 정수로 값에 접근한다.
-
…
-
-
-
[그림 1] 16 byte ArrayBuffer Object를 생성하는 모습 (상) 및 생성된 ArrayBuffer의 TypedArray (하)
2) ArrayBuffer Header
- ArrayBuffer Object는 0x10 byte 크기의 header를 가지고 있다.
[그림 2] ArrayBuffer Object의 구조
-
flags : 해당 ArrayBuffer와 관련된 flag 값이 저장된다.
- initializedLength
- ArrayBuffer Data의 길이로
[ArrayBuffer Object 이름].byteLength
값이다.length = arrbuf.byteLength
- ArrayBuffer Data의 길이로
-
capacity : 해당 ArrayBuffer Object로 DataView Object가 생성되었을 경우 DataView Object의 주소이다.
- length
2. DataView Object
- ArrayBuffer를 다루기 위한 API를 제공한다.
⇒ DataView Object로 ArrayBuffer의 이진 데이터를 읽거나 쓸 수 있다.
var dv = new DataView(ArrayBuffer_Object)
-
set
`????`(offset, value, little_endian)
: ArrayBuffer Data 시작 위치부터 offset 위치에
????
형식으로 value를 저장한다.- little_endian == true : little_endian으로 저장한다.
- little_endian == false : big_endian으로 저장한다.
-
get
`????`(offset, little_endian)
-
ArrayBuffer Data 시작 위치부터 offset 위치의 값을
????
형식으로 읽는다.
- little_endian == true : little_endian으로 메모리를 읽는다.
- little_endian == false : big_endian으로 메모리를 읽는다.
-
dv.setInt16(offset, value, little_endian)
var result = dv.getInt16(offset, little_endian)
// ArrayBuffer, DataView 예시 코드
// [1] Assign 32 bytes
var arrbuf = new ArrayBuffer(32)
// [2] Print length of arrbuf : 32
console.log(arrbuf.byteLength)
// [3] get DataView object of arrbuf
var dv = new DataView(arrbuf)
// [3] Change 16 byte value at arrbuf with offset 16 byte by little-endian
dv.setInt16(16, 255, true)
// [4] Print the 16 byte value of first half of arrbuf
console.log(dv.getInt16(0, true))
// [5] Print the 16 byte value of second half of arrbuf
console.log(dv.getInt16(16, true))
[그림 3] ArrayBuffer, DataView 예시 코드 실행 결과
+) DataView Object가 존재하지 않는 Property를 참조하면 DataView Object는 getProperty 함수를 호출해 부모 Object로부터 해당 Property를 검색한다.
3. JS Object
- SpiderMonkey에서는 모든 Object가 ObjectImpl Object를 참조한다.
⇒ Object인 DataView Object 역시 ObjectImpl Object에서 사용하는 메서드를 사용할 수 있다.
[그림 4] SpiderMonkey의 Object 계층 구조도 (ex:DataView Object는 ArrayBufferView Object 참조)
- JS Object 중 배열을 요소로 갖고 있지 않은 객체(ex :
DataView
)는elements_
field에emptyElementsHeader
의 base address를 저장한다.- 이때,
emptyElementsHeader
는 static instance로 선언되어 EScript.api의 data 영역에 존재한다.[그림 5] ArrayBuffer → DataView → emptyElementsHeader까지의 참조도
- 이때,
2. DataView Object에서도 언급했듯 DataView Object가 존재하지 않는 property를 참조하면 DataView Object는 getProperty 함수를 호출해 해당 Property 검색한다.
[그림 6] getProperty 함수의 주소를 찾는 과정을 나타낸 참조도