
로컬 Docker 랩은 CtrlPanel 웹 설치 프로그램에서 CVE-2026-34234 인증되지 않은 RCE를 시연합니다. 취약 및 패치된 컨테이너, PoC 스크립트, 보안 연구 및 방어 검증을 위한 근본 원인 분석을 포함합니다.
CtrlPanel에서 CVE-2026-34234를 시연하기 위한 로컬 Docker 실습 환경입니다.
이 저장소는 다음을 비교합니다:
vuln: 다이제스트로 고정된 CtrlPanel 1.1.1patched: 다이제스트로 고정된 CtrlPanel 1.2.0실습 환경은 로컬 전용이며 서비스를 127.0.0.1에 바인딩합니다.
CVE-2026-34234는 CtrlPanel 웹 설치 프로그램의 인증되지 않은 RCE(원격 코드 실행)입니다.
이 문제는 두 가지 버그가 연쇄적으로 작용하여 발생합니다:
install.lock 게이트 전에 접근 가능했습니다.본 실습 환경에서는 취약한 컨테이너가 무해한 증명 명령을 실행하고 그 출력을 컨테이너 내부에 기록합니다. 패치된 컨테이너는 동일한 요청을 받지만 증명 파일을 생성하지 않습니다.
예상 결과:
vulnerable => proof file created
patched => no proof file
1.1.1에서의 취약한 셸 실행원래 취약 파일:
public/installer/src/functions/shell.php
1.1.1의 관련 업스트림 코드:
function run_console(string $command, ...) {
$path = dirname(__DIR__, 4);
$handle = proc_open("cd '$path' && bash -c 'exec -a ServerCPP $command'", ...);
}
문제점:
run_console()은 하나의 셸 명령 문자열을 받습니다.bash -c에 전달됩니다.원래 취약 파일:
public/installer/src/forms/pterodactyl.php
1.1.1의 관련 업스트림 동작:
run_console("php artisan settings:set 'PterodactylSettings' 'panel_url' '$url'", ...);
run_console("php artisan settings:set 'PterodactylSettings' 'admin_token' '$key'", ...);
run_console("php artisan settings:set 'PterodactylSettings' 'user_token' '$clientkey'", ...);
문제점:
url, key, clientkey는 설치 프로그램 POST 데이터에서 비롯됩니다.권고에 따르면 public/installer/index.php는 설치 프로그램 폼 로직을 로드/실행한 후에만 install.lock을 확인했습니다. 이로 인해 이미 설치된 인스턴스에서도 설치 프로그램 핸들러에 접근할 수 있었습니다.
수정 사항은 폼 핸들러가 로드되기 전에 install.lock 확인을 이동시킵니다.
패치된 동작:
if (file_exists('../../install.lock')) {
exit("The installation has been completed already. Please delete the File 'install.lock' to re-run");
}
원래 패치 파일:
public/installer/src/functions/shell.php
1.2.0의 관련 업스트림 코드:
function run_console(array $command, ...): string {
$cwd = $cwd ?? $path;
$handle = proc_open($command, $descriptors, $pipes, $cwd, null, $options);
}
이것이 문제를 해결하는 이유:
run_console()이 이제 argv 스타일 배열을 받습니다.$()와 같은 페이로드 문법이 셸 구문 대신 리터럴 입력으로 남습니다.1.2.0의 패치된 폼 동작은 배열 스타일 명령 실행을 사용합니다:
run_console(['php', 'artisan', 'settings:set', 'PterodactylSettings', 'panel_url', $url], ...);
run_console(['php', 'artisan', 'settings:set', 'PterodactylSettings', 'admin_token', $key], ...);
run_console(['php', 'artisan', 'settings:set', 'PterodactylSettings', 'user_token', $clientkey], ...);
127.0.0.1:8081 -> vulnerable CtrlPanel 1.1.1
127.0.0.1:8082 -> patched CtrlPanel 1.2.0
127.0.0.1:9100 -> fake Pterodactyl API
서비스:
vuln: 실제 CtrlPanel 1.1.1patched: 실제 CtrlPanel 1.2.0fake-api: 설치 프로그램 검사를 충족시키기 위해 사용되는 로컬 가짜 Pterodactyl APImysql_vuln / mysql_patched: 별도의 MariaDB 인스턴스redis_vuln / redis_patched: 별도의 Redis 인스턴스실습 환경은 CtrlPanel 애플리케이션 소스 코드를 수정하지 않습니다.
Dockerfile은 원래 컨테이너 진입점을 래핑하여 다음에 대한 Docker Desktop 런타임 권한을 정규화합니다:
/var/www/html/storage
/var/www/html/bootstrap/cache
권한을 수정한 후 래퍼는 원래 제품 진입점을 실행합니다.
기본 PoC:
poc/poc_http_only.py
속성:
docker exec를 사용하지 않음id, whoami, hostname도우미 스크립트:
poc/poc_lab.py
목적:
docker compose exec를 사용하여 컨테이너 내부의 증명 확인앱 컨테이너 내부의 증명 파일:
/var/www/html/storage/logs/cve_2026_34234_proof.txt
깨끗한 실습 환경 상태에서 시작:
docker compose down -v --remove-orphans
docker compose up -d --build
앱 컨테이너가 실행될 때까지 기다린 후 실행:
python3 poc/poc_lab.py
예상 출력:
== Testing vulnerable ==
proof_exists: True
result: PASS expected_proof=True
== Testing patched ==
proof_exists: False
result: PASS expected_proof=False
[+] Expected result reached:
vulnerable => proof file created
patched => no proof file
HTTP 전용 PoC를 취약한 대상에 전송:
python3 poc/poc_http_only.py --target http://127.0.0.1:8081
증명을 수동으로 확인:
docker compose exec vuln sh -lc 'cat /var/www/html/storage/logs/cve_2026_34234_proof.txt'
예상 증명:
uid=1000(laravel) gid=1000(laravel) groups=1000(laravel)
laravel
<container-hostname>
패치된 대상에 동일한 요청 실행:
python3 poc/poc_http_only.py --target http://127.0.0.1:8082
패치된 동작 확인:
docker compose exec patched sh -lc 'test -f /var/www/html/storage/logs/cve_2026_34234_proof.txt && cat /var/www/html/storage/logs/cve_2026_34234_proof.txt || echo "no proof file"'
예상:
no proof file
컨테이너, 네트워크 및 실습 볼륨 제거:
docker compose down -v
이 저장소는 교육용 보안 연구 및 방어적 검증 목적으로만 제공됩니다.
모든 시연은 제공된 로컬 Docker 실습 환경 내에서 실행되도록 의도되었습니다. 개념 증명은 파괴적 행위, 지속성, 자격 증명 도용, 데이터 유출 및 실제 환경 대상 지정을 피합니다.
명시적 권한 없이 이 프로젝트를 어떤 시스템에도 사용하지 마십시오. 저자는 이 자료로 인한 오용이나 손해에 대해 책임을 지지 않습니다.