티스토리 뷰
















윈도우 버퍼 오버플로우 익스플로잇 개발 - 3  
 
 





윈도우 버퍼 오버플로우의 특징
유 닉스와 같은 시스템에서 버퍼 오버플로우는 리턴 주소를 대부분 추측에 의해 계산하는 경우가 많고, 여러 번 시도할 수 있을 경우 무차별 공격(brute forcing)을 하기도 한다. 반면 윈도우 시스템은 유닉스와는 조금 다른 양상을 보인다.

리턴 주소 문제
윈 도우 시스템의 경우 스택을 오버플로우시켰을 경우 해당 함수에서 리턴하는 시점에 ebx, esp 등과 같은 레지스터에 셸 코드가 들어 있는 버퍼의 주소가 들어 있는 경우가 많다. 따라서 대부분의 경우 리턴 코드는 ‘jmp esp’나 ‘jmp ebx’와 같은 바이트 문자열이 존재하는 위치로 정해진다. 이러한 코드가 존재하는 곳으로 일단 제어권을 넘길 수만 있다면 esp나 ebx가 공격자가 보낸 셸 코드의 위치를 저장하고 있으므로 성공 확률이 높아지게 된다.

윈도우 셸 코드의 특징
윈 도우 셸 코드는 일반적인 유닉스 셸 코드와는 다른 특수한 형태를 띤다. 유닉스 셸 코드가 특정 대상에 대해 항상 호환성을 가지는 반면 윈도우 셸 코드는 그러한 호환성이 없다. 이러한 특징은 윈도우의 아키텍처와 관련이 있다. 유닉스에서는 시스템 콜을 통해 커널의 서비스를 받기 위해 인터럽트를 사용하는 데 반해 윈도우에서는 DLL을 통해 커널의 서비스를 받는다. 사용자 공간에서 인터럽트를 통해 시스템 콜 서비스를 받는 것은 거의 불가능하다. 시스템 서비스를 제공하는 DLL로는 kernel32.dll이 있다. 이 DLL을 통해 시스템과 관련된 대부분의 서비스를 받을 수 있다. <그림 1>은 PE 파일의 포맷이다.
다음은 IMAGE_DATA_DIRECTORY의 구조다. 데이터 디렉토리의 구조는 <그림 2>와 같다.



<그림 1> PE 파일 포맷



<그림 2> 데이터 디렉토리 구조


typedef struct _IMAGE_DATA_DIRECTORY {
DWORD RVA;
DWORD Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;

WebDAV 익스플로잇
지금부터 최근에 필자가 만든 흥미로운 익스플로잇인 WebDAV 익스플로잇을 중심으로 윈도우 버퍼 오버플로우의 실체를 살펴보자. 이 익스플로잇에서는 유니코드 리턴 주소를 써야 하는 문제를 재미있게 해결했다.
WebDAV 는 웹 컨텐츠 저작(authoring)을 위한 프로토콜로서 마이크로소프트에 의해 표준이 만들어졌고, 주로 IIS 서버의 컨텐츠 관리를 위해 사용한다. ntdll.dll의 버퍼 오버플로우 버그는 WebDAV를 통해 익스플로잇이 가능했다. WebDAV 메쏘드들에 아주 긴 문자열을 보냈을 경우 버퍼가 오버플로우되어 원하는 코드를 실행할 수 있는 버그가 있었다. 이 버그는 버그트랙(Bugtraq) 7116으로 ntdll.dll의 버그 CVE ID는 CAN-2003-0109이다.
ntdll.dll 버퍼 오버플로우는 ntdll.dll의 RtlDosPathNameToNt PathName 함수에서 발생하고 특정 길이의 문자열이 전달됐을 경우에만 버퍼 오버플로우가 발생했다. 또한 이 RtlDosPathName ToNtPathName 함수를 쓰는 프로그램이 많으므로 그러한 프로그램은 모두 같은 버그에 노출됐다고 볼 수 있다. 이 버그는 윈도우 2000 서비스 팩 3까지의 모든 윈도우 시스템에서 구동되는 IIS 5.0 서버 시스템에 영향을 미친다.



<화면 1> inetinfo가 크래시



<화면 2> esp(스팩) 덤프

사용 가능한 유니코드 찾기
이 익스플로잇의 결과 버퍼가 오버플로우된다. 오버플로우된 버퍼는 유니코드 형태다. 따라서 ‘AAA...’와 같은 문자열은 ‘A|00| A|00|A|00|...’와 같이 변형된다. 그런데 영문판 윈도우와는 달리 한글판 등 유니코드 언어권의 윈도우에서는 %u 문자열을 통해 해당 언어 팩에서 유효한 유니코드 문자열을 메모리에 입력할 수 있다. 다음은 익스플로잇 테스트 코드를 보냈을 경우의 예다. 일단 inetinfo.exe가 크래시된다(<화면 1>). 크래시된 상태에서 <화면 2>와 같이 esp(스택)을 덤프해 보면 ZZZZ...가 Z|00|Z|00|Z|00|...와 같이 널 문자가 추가된 형태로 변환됐음을 알 수 있다. <리스트 1>은 이렇게 유효한 유니코드의 집합을 찾는 프로그램이다.

<리스트 1> 유효한 유니코드 집합 찾기
// uniprint.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include
#include
#include

int main(int argc, char* argv[])
{
unsigned char i;
unsigned char j;

for(i=0;i<255;i++)
{
for(j=0;j<255;j++)
{
char string_to_copy[3];
WCHAR src[256]={0,};
char dest[256]={0,};

string_to_copy[0]=i;
string_to_copy[1]=j;
string_to_copy[2]=0;
memcpy(src,string_to_copy,strlen(string_to_copy));
BOOL lpUsedDefaultChar;

WideCharToMultiByte(CP_ACP,0,src,1,dest,256,NULL,&lpUsedDefaultChar);

if(!lpUsedDefaultChar)
{
printf("%.2x%.2x ",j,i);
}
}
}

return 0;
}


출처: http://www.imaso.co.kr/?doc=bbs/gnuboard.php&bo_table=article&wr_id=45&page=8