IrfanView Legacy JPM.dll 분석 회고: Heap OOB Write
현재 IrfanView 기본 설정 영향 항목이 아닌, 비활성화된 legacy JPM.dll 플러그인 대상 퍼징 분석 회고입니다. 디코딩 루프가 할당 버퍼 경계가 아니라 비트스트림 종료 조건에 의존하며 발생한 heap OOB write 크래시를 분석한 기록입니다.
공개 범위 메모 : 이 글은 현재 IrfanView 기본 설정에서 악용 가능한 취약점 공개가 아닙니다. 개발자 답변에 따르면 JPM.dll 플러그인은 약 2년 전부터 기본 비활성화된 legacy plugin이며, 사용자가 수동으로 활성화하고 IrfanView의 보안 경고를 무시해야만 도달 가능한 코드입니다. 따라서 본문은 현재 제품 advisory가 아니라 WinAFL 기반 파일 포맷 파서 퍼징과 크래시 분석 과정을 정리한 연구 회고로 봐야 합니다.
개발자 답변과 현재 영향 범위
제보 후 IrfanView 개발자는 해당 JPM 플러그인이 현재 IrfanView에서 기본 활성화되어 있지 않으며, 이 문제를 현재 IrfanView의 보안 취약점으로 보지 않는다고 답변했습니다. 이 내용을 반영해 본 글에서는 해당 케이스를 비활성화된 legacy 플러그인 분석 기록 으로 정리합니다.
개요
IrfanView의 JPM 처리 플러그인인 JPM.dll 에서 JPM 파일을 파싱하던 중 heap buffer overflow가 발생하는 케이스를 확인했다. 입력 파일은 JPEG 2000 Part 6 계열의 JPM 컨테이너이며, WinAFL + DynamoRIO 기반 coverage-guided fuzzing으로 발견했다.
이번 크래시는 단순한 NULL dereference가 아니라, 디코딩 결과가 heap buffer의 끝을 넘어 기록되는 형태로 관찰되었다. 다만 개발자 답변 기준 현재 기본 활성화 코드가 아니므로, 본문에서는 재현 가능한 크래시와 원인 분석 범위까지만 다룬다.
테스트 환경
Target module : JPM.dll Process : 32-bit process OS : Windows 11 Pro x64 10.0.26200 Discovery : WinAFL + DynamoRIO 크래시 샘플 : fuzzer7id00222300EXCEPTIONACCESSVIOLATION.jpm Exception : Access violation (0xc0000005)
크래시 샘플은 정상적인 JPM 시그니처로 시작한다.
00000000: 00 00 00 0c 6a 50 20 20 0d 0a 87 0a 00 00 00 14 00000010: 66 74 79 70 6a 70 6d 20 00 00 00 00 6a 70 6d 20 00000020: 00 00 00 1d 6d 68 64 72 00 00 00 01 01 01 00 00
초반 박스 흐름은 jP , ftyp , mhdr , pcol , pagt 등 JPM 컨테이너 구조를 따르지만, 이후 박스 길이와 하위 데이터가 파서를 비정상 경로로 유도한다.
크래시
Exception: Access violation (0xc0000005) Address: 0x61d4e3d2 Module: JPM.dll Base: 0x61cf0000
Crash instruction: mov dword ptr [ecx], eax
ECX = 0x09114000
ECX 가 정확히 페이지 경계에 걸린 주소였고, Page Heap 환경에서 guard page에 닿으며 크래시가 발생했다. 이 패턴은 할당 버퍼의 끝을 넘어 연속 쓰기가 진행되다가 탐지된 형태로 볼 수 있다.
호출 흐름
fuzzReadJPMW!fuzzme JPM!ReadJPMW+0x94b JPM!ShowPlugInSaveOptionsW+0x138b2 JPM!ShowPlugInSaveOptionsW+0x1a144 JPM!ShowPlugInSaveOptionsW+0x28ba3 JPM!ShowPlugInSaveOptionsW+0x2cdad JPM!ShowPlugInSaveOptionsW+0x2d065 sub1005E160
크래시 지점은 sub1005E160 , IDA 기준으로는 0x1005E3D2 부근이다.
원인 분석
핵심은 할당 크기와 디코딩 루프의 종료 조건이 서로 다른 기준을 사용한다는 점이다.
sub1005E160 은 파일 헤더 쪽 크기 필드에서 값을 읽어 4 v47 크기의 heap buffer를 할당한다. 그런데 실제 출력 루프는 이 할당 크기를 기준으로 멈추지 않고, 입력 비트스트림 안에서 특정 종료 마커가 나올 때까지 계속 진행한다.
즉 공격자는 다음 두 요소를 분리해서 조작할 수 있다.
헤더의 크기 필드: 작은 버퍼가 할당되도록 유도
댓글