본 포스팅은 기존 제작된 익스플로잇을 무작정 따라하는 내용을 담고 있습니다.

(천만 스크립트 키드 양성을 위한 그날까지..!) 

 


 

유형 : Stack Buffer Overflow

타겟 프로그램 : BlazeDVD Pro Player 6.1

테스트 환경 : Windows XP SP3 Eng

링크 : http://www.exploit-db.com/exploits/32737/ (제작자 : Deepak Rathore)

설명 : Playlist를 열때 Stack Buffer Overflow를 일으킵니다.


[재현]

  

[그림1 - 프로그램 실행 화면]

 

링크에서 타겟 프로그램과 Exploit을 다운로드 합니다.

Perl 설치가 귀찮으신 분은blazeExpl.plf<-- 이것을 다운로드 해 주세요.

 

[그림2 - Playlist Exploit 파일]

해당 파일을 Hex Editor로 열면 위의 그림과 같이 나옵니다.

위의 박스친 부분의 주소를 사용자PC에 맞게 고쳐줘야 하는데, ASLR이 적용되지 않은 모듈에서 jmp esp를 찾으면 됩니다.

 

[그림3 - JMP ESP 찾기]

저는 0x7C86467B에 있는 jmp esp를 사용했습니다.

 

[그림4 - Exploit 성공]

Playlist 를 열어서 그림4와 같이 나오면 성공입니다.

[분석] 

[그림5 - 버퍼를 스택에 복사]

Playlist를 로드하면 위의 코드에서 스택에 Playlist의 버퍼를 복사합니다. 

(스샷을 연속적으로 찍지 않아서 스택의 주소가 그림마다 다릅니다. 양해를..)

[그림6 - 스택 비교]

위의 그림은 스택 버퍼가 쓰여지기 전과 후의 모습입니다.

 [그림7 - RETN]

계속해서 Step over를 하면 최종적으로 그림7과 같은 코드를 만나게 되어 리턴합니다.

 

[그림 8 - RETN 시 스택]

RETN 직전 스택을 보면 ESP가 0x7C86467B를 가리키고 있는데 해당 주소는 JMP ESP 를 담고 있습니다.

말 그대로 ESP로 점프하라는 것이며, 한번 더 Step over 하면 스택에 0x10이 더해진 0x12F0D0으로 이동합니다.

 

[그림 9 - 쉘코드]

NOP후 쉘코드가 실행됩니다.

테스트 환경 : Windows XP SP3 Eng / Python 2.7.1

관련 링크 : http://www.exploit-db.com/exploits/32585/

취약점 유발 파일 :victim.m3u

 

해당 취약점은 Read Only 영역에 Writing을 시도할 때 발생합니다. (즉, stack limit를 뚫고 입력하는 상황)

PoC를 실행시키면 victim.m3u가 생성되는데, 이것을 AudioCoder에 넣고 돌리면 계산기가 실행됩니다.

정크 코드 'A'에 걸려서 메모리가 뻑난(?) 모습입니다. 해당 영역은 Read Only 입니다.

 

SEH 를 확인해보니 Overwrite 되었습니다. 바로 쉘코드로 점프합니다.

 

 

Internet Explorer 전용 DLL 인젝터입니다.

Internet Explorer 7.0 이상의 경우 iexplore.exe가 iexplore.exe를 실행하는 구조로 되어있는데

부모 프로세스가 iexplore.exe인 경우에만 인젝션을 합니다.

사용 방법 : 폴더를 하나 생성한 뒤 Injector.exe와 인젝션 할 DLL 한 개를 같이 넣습니다.

그럼 인젝터가 자동으로 dll을 인식합니다.

 

 Injector.zip

 

버그는 댓글로 남겨주세요.

 

작업 히스토리

Version 1.0 [2014/04/17]

Internet Explorer 7.0 이상 지원

32비트 iexplore.exe 지원 (64비트 동작 안함)

 

 

 VC++에서는 문자열을 다룰 때 char나 wchar_t 로 선언합니다.

그러나 드라이버 개발을 하다보면 STRING이나 UNICODE_STRING 로 선언된 것을 많이 볼 수 있는데요.

특이하게도 문자열을 구조체로 표현합니다.

각각 길이, 최대 길이(NULL을 포함하는), 문자열 포인터를 표현합니다.

DbgPrint에서 구조체를 직접 출력할 수 있는 특이점이 있습니다. 아래 %wZ 출력을 확인해 주세요.

문자열의 초기화는 RtlInitUnicodeString()를 사용하며, 아래와 같이 PSEUDO 코드를 작성할 수 있습니다.

UnicodeString->Buffer = 초기화 문자열;

UnicodeString->Length = wcslen(초기화 문자열) ;
UnicodeString->MaximumLength = UnicodeString->Length + sizeof(UNICODE_NULL) ;

아래 코드는 동적으로 할당 및 해제 하는 방법입니다. Buffer만 동적으로 할당하니 결국 malloc()과 같다고 볼 수 있습니다.

이번에는 메모리 동적 할당에 대해 알아보겠습니다.

Ring3에서는 동적으로 메모리를 할당할 때 malloc이나 HeapAlloc 같은 함수를 사용해보셨을겁니다.

그러나 드라이버에서는 이 함수를 사용할 수 없습니다.

그럼 어떻게 동적 메모리를 생성할 수 있을까요?

커널엔 메모리 풀이라는 것이 있는데 이것이 커널모드 힙입니다.

커널에서 힙을 할당하기 위해선 ExAllocatePool 이라는 함수를 사용합니다.

주목해볼만한건 인자로 NonPagedPool을 주었는데요. 인자 값으로 아래 두가지가 있습니다.

NonPagedPool : 항상 메모리에 존재

PagedPool : 페이지 아웃 혹은 인이 될 수 있음

페이지 아웃이 되면 공포의 BSoD가 뜨겠죠? 그것을 방지하기 위해서 NonPagedPool을 인자로 주어야 합니다.

 

잘 뜹니다~

주의하실 점은 동적 메모리를 반드시 해제해 주어야 합니다. 안그러면 커널 메모리에서 둥둥 떠다닙니다~

저는 WDK를 처음 접하고 코딩을 했을때, Ring3 어플리케이션에서 쓰이는 printf, malloc 같은 함수들을 사용하지 못해서 적잖이 당황한 기억이 있습니다.

그래서 WDK관련 책들을 샀는데, 입문자가 보기엔 너무 생소하고 난해한 용어들이 많아 바로 책을 덮어버렸습니다.

책을 덮고.. 독자적으로 여러 소스들을 분석하여 얻은 결론 하나, 내부 동작 원리에 대해 잘 몰라도 개발할 수 있다는 것입니다.

물론 심도있는 개발을 위해 전부 필요한 내용이지만, 일단 진입장벽부터 넘어야 하지 않겠습니까?

우선 들어가기 전에 이 글을 읽는 대상은..

1. Ring3 에서 c/c++ 기반 시스템 프로그래밍 개발 가능한 분

2. 윈도우즈 동작 메커니즘에 대해 어느정도 이해하고 계신 분

3. 위의 두가지 조건에 충족하며 드라이버 제작을 처음으로 해보시려는 분

따라서 아주 기초적이고 구구절절한 내용은 생략합니다.

그럼, 아주 심플한 제작부터 시작해보겠습니다.

드라이버 제작에 앞서 필수적인 파일 2가지가 있습니다. 아래 두가지 파일을 작성 후 프로젝트 폴더에 저장하면 됩니다.

MAKEFILE

!INCLUDE $(NTMAKEENV)\makefile.def

SOURCES

TARGETNAME=simple
TARGETPATH=.\sys
TARGETTYPE=DRIVER
SOURCES=simple.c

그 다음, simple.c를 작성합니다.

 

정말로 이렇게 심플한 코드가 실행이 될까요? 책에선 드라이버 스택이 어쩌고 저쩌고.. 익스텐션이 어쩌고저쩌고..

뭐 일단, 빌드를 해 봅니다.

 

 

빌드된 sys는 \sys\i386 경로에 있을 것 입니다. (xp로 빌드한 경우) 그럼 이 드라이버를 커널에 올려 봅시다.

드라이버를 상주시키는 파일은 구글에서 instdrv로 검색하시면 나옵니다. 로깅 뷰어는 DebugView를 사용하면 됩니다.

 

 

짜잔~ 실행이 되었군요. VC++ 로 작성하였던 내용과 별반 다르지 않군요.

이제부터 이 내용을 뼈대삼아 살을 붙여나갈 예정입니다.

 

브라우저 취약점 쉘코드를 작성하여, html의 unescape 부분에 %ue8fc%u00c3%u0000%u8960 이런 형태로 붙여넣었습니다. 그런데 작동이 되지 않았습니다.

원인을 찾아봤는데, bad character라고 하여 \x00 이 들어가면 동작이 되지 않던 것이었습니다.

이는 메타스플로잇에서 공개한 브라우저 취약점 공격 PoC에서도 확인할 수 있습니다.

http://www.exploit-db.com/exploits/23785/

 

 

아래는 메타스플로잇에서 제공하는 어셈블리어 코드로 쉘코드를 생성하는 방법입니다. LoadLibrary기능을 하는 쉘코드이며, generate 로 생성이 가능합니다. bad char 0x00 이 포함되어있어서 브라우저에서 정상동작하지 않습니다. 

https://github.com/rapid7/metasploit-framework/blob/master/external/source/shellcode/windows/x86/src/single/single_loadlibrary.asm

그대로 디버거에 붙여넣은 화면입니다.  

 

아래는 옵션을 주어 0x00을 제거하였습니다. 디폴트로 시카타가 나이라는 인코더가 선택됩니다. (일본어로, "어쩔수가 없지". 이름을 재미있게 지었군요. 

 그대로 디버거에 붙여넣은 화면입니다.  

디코더가 처음 부분에 삽입되어 있어서 한 라인씩 실행하다 보면, 평문으로 복호화가 됩니다. 

# 쉘코드 인코딩 하는 방법

msfencode라는 프로그램이 내장되어 있습니다.

옵션 설명

-e x86/shikata_ga_nai : 인코더 선택

-b '\x00' : Bad Character 제거

-i /root/loadlibrary.bin : 경로명 (쉘코드를 바이너리로 저장해주세요)

-t c : C언어 변수 타입으로 출력

 Windows 7은 기본적으로 DLL이 랜덤한 주소로 매핑되지만, 컴파일 시 고정된 주소를 설정할 수 있습니다. JAVA 1.6버전의 MSVCR71.dll이 그 대표적인 예 인데, 항상 0x7C340000에 매핑됩니다.

EMET 에는 MandatoryASLR이라는 기능이 있는데, 이 옵션을 키면 NON-ASLR DLL을 랜덤한 주소로 매핑시켜 줍니다.

이렇게 주소가 바뀌게 되면, ROP 시 NON-ASLR DLL을 사용할 수 없게 됩니다.

 

 

EMET 4.1에서는 ROP가 주요 함수들을 호출하여 공격하는 것을 막기 위하여 주요 함수들에 후킹을 걸어 감시합니다.

Deep Hooks 라는 기능이 있는데 이것은 좀 더 많은 API를 커버하는 기능입니다.

예를 들어, VirtualProtect를 호출하면 VirualProtect->VirtualProtectEx->NtProtectVirtualMemory 순으로 호출하는데요, Deep Hooks 기능을 키면 NtProtectVirtualMemory를 후킹합니다. 이걸 옵션으로 뺀 이유가, 다른 시큐리티 회사들이 호환성 문제 때문에 클레임을 걸었다는군요.

아래는 비교 표 입니다. Undocumented API를 커버하여 주네요. 

 

Deep Hooks가 기본 값이 아니기 때문에, 공격자들은 VirtualProtect가 아닌 NtProtectVirtualMemory를 호출하여 우회할 수도 있습니다.

또한 Deep Hooks를 키면 Kernelbase도 추가로 후킹을 합니다.

일반적으로 Ring 3에서는 fs:[0] 과 같은 명령으로 세그먼트 디스크립터가 가리키는 메모리에 접근할 수 있지만, 세그먼트 디스크립터에는 접근할 수 없습니다.

그러나 OllyDbg에서는 아래 그림과 같이 해당 인덱스가 가리키는 세그먼트 디스크립터의 베이스 주소를 표시해주고 있습니다.

Ring 3에서는 일반적인 방법으로는 베이스 주소를 구할 수 없으며, 약간의 트릭이 필요합니다. GetThreadSelectorEntry라는 API를 사용해서 LDT_ENTRY 구조체를 채운 다음, BaseHi + BaseMid + BaseLow 를 통해 베이스 주소를 만들 수 있습니다. LDT_ENTRY는 WinNT.h에 정의되어 있으므로 따로 선언할 필요가 없습니다. (아래 예시는 이해를 돕기위해)

 

위의 방법은 이메일을 통해 올리디버거 제작자인 Oleh Yuschuk에게 문의해서 알아낸 방법이며, 자기가 알기로는 이에 해당하는 64비트 API는 없다고 하네요.

그러나 현재 개발 중인 64비트 올리디버거 스크린샷을 보면 Base Address가 일부 표시되고 있으니 다른 방식으로 구하는 방법이 있는 것 같습니다.

FS 의 경우 비교적 쉽게 구할 수 있는데, FS의 디스크립터가 TEB를 표시하고 있으므로 NtCurrentTeb() 혹은 mov eax, fs:[0x18] 로 베이스 주소를 구할 수 있습니다.

 

+ Recent posts