Heap Overflow Exploit 공격 기법중 하나인 VTable Overwrite 기법입니다.

BOF 공부를 시작하면서 가장 어렵게 생각했던 VTable Overwrite 분석입니다.

사용된 디버거: x64dbg(다운로드)



00403430은 VTable을 가르키는 포인터가 됩니다.


VTable을 확인해보면 Class에서 선언한 순서대로 함수를 가르키고 있습니다.

리틀엔디언 형태를 빅엔디언으로 바꾸어 읽어보면 00401150(setName), 004011A0(printName), 004011C0(getName)이 됩니다.


call bookmanager.401150

VTable에서 확인한 결과 401150은 setName() 함수임을 알 수 있습니다. setName() 함수로 진입합니다.


setName() 함수의 역할은 건너뛰고 setName() 함수안에서 printName()과 getName()을 호출하는 장면부터 분석해보도록 하겠습니다.


mov eax, dword ptr ss:[ebp-8]     (사진에서 첫줄이 잘렸습니다. 이 부분부터 분석이 진행됩니다.)

ebp-8은 stack 영역으로, mybook의 VTable을 eax로 복사합니다. (eax: 00403430)

mov edx, dword ptr ds:[eax]         

eax가 가르키는 값인 00402190을 edx로 복사합니다. (edx: 00402190)

mov ecx, dword ptr ss:[ebp-8]         

ebp-8은 stack 영역으로, mybook의 VTable을 ecx로 복사합니다. (ecx: 00403430)

mov eax, dword ptr ds:[edx+4]

edx+4(00402194)가 가르키는 값인 004011A0은 두번째 함수인 printName(), eax로 복사합니다.

call eax                 

call eax(004011A0)

mov ecx, dword ptr ss:[ebp-8]         

printName()을 호출하여 ecx가 변경되었으므로 다시 ecx에 mybook의 VTable을 복사합니다. (ecx: 004034300)

mov edx, dword ptr ds:[ecx]         

ecx가 가르키는 값인 00402190을 edx로 복사합니다. (edx: 00402190)

mov ecx, dword ptr ss:[ebp-8]

mov eax, dword ptr ds:[edx+8]

edx+8(00402198)이 가르키는 값인 004011C0은 세번째 함수인 getName(), eax로 복사합니다.

call eax                 

call eax(004011C0)


취약점이 존재하는 부분은 contents[1000] 입니다.

fgets를 통해 1500byte까지 내용을 가져오지만 buffer의 크기는 1000byte입니다.

contents의 내용을 1000byte를 초과하여 1004byte까지 작성한다면 mybook의 VTable을 변조시킬 수 있습니다.

contents의 내용을 VFUNC1 + VFUNC2 + NOP + SHELLCODE + DUMMY로 1000byte를 채운 후 나머지 4byte를 contents의 시작주소로 채워 Exploit을 작성합니다.

Exploit을 작성 후 변조된 &VTable과 VFUNC입니다.


프로그램 및 Exploit 출처 : https://cafe.naver.com/secuholic


'System Hacking' 카테고리의 다른 글

Function Pointer Overwrite 분석  (0) 2018.12.03

Heap Overflow Exploit 공격 기법중 하나인 Function Pointer Overwrite 공격 기법입니다.

사용된 디버거: x64dbg(다운로드)


프로그램의 취약점인 parse 함수의 memcpy 부분입니다.

004114F1부터 memcpy 인자를 Stack에 Push 해줍니다.


call vul_http_server.4110CD

memcpy를 호출하는 함수입니다. memcpy를 호출하기 전 Stack의 상태는 위와 같습니다.

Stack의 FILO 구조로 memcpy(void *dest, const void *src, size_t count) 를 Stack에 Push 하였습니다.

dest는 416120임을 확인가능하므로 buffer의 시작지점은 416120임을 알 수 있습니다.

Dump 창을 buffer의 시작지점으로 이동한 뒤 스크롤을 내리다보면 log_func의 위치에 도달합니다.

log_func,의 위치는 416310에서 + 4byte 된 416314 입니다.


Python을 이용하여 A Packet을 503개 발송한 결과입니다. (시작 시 /를 포함하는 결과이므로 504에서 /를 제외한 503개)

Heap 영역인 log_func가 41 41 41 41 로 변조된 것을 보아 Exploit이 가능함을 알 수 있습니다.


Exploit 코드는 NOP + SHELLCODE + DUMMY + RET 형태로 작성합니다.

RET은 buffer의 시작 지점에서 4byte 추가하여 시작합니다.

Exploit을 작동시킨 뒤 call memcpy부터 디버깅을 시작, 덤프창을 416120으로 buffer의 시작지점으로 이동합니다.


buffer에 정상적으로 NOP, SHELLCODE, DUMMY, RET이 대입된것을 확인할 수 있습니다.


log_func를 buffer의 위치로 변조하여 NOP Sled에 진입, SHELLCODE를 정상적으로 작동시키는 모습을 확인할 수 있습니다.


프로그램 및 Exploit 출처 : https://cafe.naver.com/secuholic

'System Hacking' 카테고리의 다른 글

VTable Overwrite 분석  (0) 2018.12.03

EAX란?

(Extended Accumulator)

AX 레지스터의 확장(Extended) 레지스터로 함수에서 return 값을 전달하는데 사용된다.

어셈블리어에서 가장 많이 쓰이는 레지스터로 모든 연산에 주로 사용되는 변수


EDX란?

(Extended Data)

EAX와 거의 흡사하나 return 값을 전달하는 용도로는 사용되지 않는다.

기본 연산과 복잡한 연산에 주로 사용되는 변수


ECX란?

(Extended Count)

반복문에서의 카운트를 담당하는 용도로 사용된다. 카운트가 필요없는 코드에서는 변수로 사용이 가능하다.

for (int i=0; i<=10; i++) 에서 int i에 해당한다.

어셈블리어에서의 반복문은 10에서 i-- 로 진행된다. ECX에 반복할 횟수가 미리 지정되어있음.


EBX란?

(Extended Base)

특정 용도로 만들어진 레지스터가 아닌, 레지스터가 더 필요할때 사용된다.

EAX, ECX, EDX 레지스터가 부족할 때 사용됨


사용된 디버거 : x64dbg (https://sourceforge.net/projects/x64dbg/files/snapshots/)

디버깅 환경 : Windows 10 64bit


x64dbg를 이용할 경우 "이 파일을 디버깅하기 위해서 x32dbg를 사용합니다!" 라는 문구와 함께 해당 파일을 오픈할 수 없으므로 x32dbg를 이용하여 파일을 오픈합니다.


파일을 오픈한 뒤 F9를 눌러 프로그램을 실행시켜줍니다.


crackme3.exe의 Entry Point는 00401000입니다.

프로그램의 시작과 함께 스택에 MessageBoxA 함수에 들어갈 인자를 채웁니다.

push 0                                                                                                           <<uType MB_OK

push crackme3.402000                    402000:"abex' 3rd crackme"                               <<lpCaption

push crackme3.402012                    402012:"Click OK to check for the keyfile."            <<lpText

push 0                                                                                                           << hWnd

call <crackme3.MessageBoxA>        

스택은 FILO 구조로 이루어져 있으므로 원형 순서의 반대로 대입합니다.

오른쪽 아래 스택 프레임 화면을 보면 정상적으로 대입된것을 확인할 수 있습니다.


push 0                                 | TemplateFile

push 80                                | FlagsAndAttributes

push 3                                 | CreationDisposition

push 0                                 | SecurityAttributes

push 0                                 | ShareMode

push 80000000                          | DesiredAccess

push crackme3.4020B9                   | 4020B9:"abex.l2c" File Name

call <crackme3.CreateFileA>            

CreateFileA 함수를 이용하여 "abex.l2c" 라는 key 파일을 오픈합니다.


지금은 "abex.l2c" 라는 파일이 존재하지 않으므로 EAX가 FFFFFFFF으로 변경되었습니다.

cmp eax, FFFFFFFF

je crackme3.401075 << 이곳에서 점프를 진행합니다.

점프 결과 파일을 찾을 수 없다는 메시지를 띄운 뒤 ExitProcess 함수를 통해 프로그램이 종료됩니다.


필요한 key 파일의 이름을 알았으니 crackme3.exe와 같은 위치에 "abex.l2c" 라는 파일을 만들고, Ctrl + F2를 눌러 프로그램을 다시 로드해준 뒤 Call CreateFileA 까지 진행하면 EAX의 값이 2F0으로 변경된 것을 확인할 수 있습니다.


cmp 구문을 쉽게 통과하였지만 GetFileSize라는 함수가 우리를 기다리고있습니다.

0040102F 에서 mov dword ptr ds:[4020CA],eax 구문을 통해 eax의 값을 4020CA의 값으로 복사하였고

0040103B 에서 GetFileSize에 필요한 인자로 스택에 넣어주어 "abex.l2c" 파일의 크기를 확인한 뒤 eax에 대입합니다.

방금 우리가 만든 key 파일은 아무 내용도 적혀있지 않은 파일이므로

00401049 의 분기점에서 점프를 진행, The found file is not a valid keyfile 이라는 메시지를 호출하게 됩니다.


파일의 크기는 바로 윗줄인

00401046 에서 cmp eax, 12 로 비교를 진행하고있습니다. hex의 12는 Decimal의 18이므로 쓰레기값으로 18바이트를 채워준 뒤 프로그램을 다시 로드합니다.


* 주의할 점 : 한글은 한 글자에 2byte를 차지하기 때문에 한글을 이용하여 크기를 채울 경우 9개만 적어야합니다 *


다시 로드한 뒤 프로그램을 진행하면 Yep, keyfile found! 라는 메시지와 함께 프로그램이 종료됩니다.


+ Recent posts