
CVE-2025-49132에 대한 개념 증명 익스플로잇으로, 로케일 매개변수 주입을 통해 Pterodactyl Panel <= 1.11.10에서 인증되지 않은 원격 코드 실행을 가능하게 합니다. 체크 모드와 익스플로잇 모드를 포함합니다.
CVE-2025-49132에 대한 개념 증명(PoC) - Pterodactyl Panel <= 1.11.10에서 원격 코드 실행
이 PoC는 다음 자료를 참고했습니다.
# 웹 애플리케이션이 취약한지 확인
python3 poc.py -u <url> -m check
# 재미있는 작업 수행
python3 poc.py -u <url> -m exploit -p <pear_dir_경로> -t <쓸_경로>
LocaleController.php의 git diff를 v1.11.10과 v1.11.11 사이에서 분석하면 (링크), 컨트롤러가 locale 및 namespace 매개변수를 검증이나 정화 없이 수용한다는 것을 알 수 있습니다.
이것이 왜 위험한지 이해하기 위해 panel/routes/base.php에 구성된 라우팅을 살펴보겠습니다. 이 라우트는 인증 관련 미들웨어를 명시적으로 제거하여 인증되지 않은 사용자가 /locales/locale.json을 호출하고 locale 및 namespace와 같은 필요한 쿼리 매개변수를 제공할 수 있도록 합니다. 즉, 이제 이 매개변수들은 사용자에 의해 제어됩니다.
Route::get('/locales/locale.json', Base\LocaleController::class)
// Remove authentication middleware for this endpoint
->withoutMiddleware(['auth', RequireTwoFactorAuthentication::class]);
다시 LocalController.php로 돌아가면, 다음 코드 라인이 RCE 마법을 제공합니다.
$this->loader->load($locale, str_replace('.', '/', $namespace))
load 함수는 Laravel의 번역 프레임워크(여기)에서 가져옵니다:
public function load($locale, $group, $namespace = null) # 1
{
if ($group === '*' && $namespace === '*') { # 2
return $this->loadJsonPaths($locale);
}
if (is_null($namespace) || $namespace === '*') { # 3
return $this->loadPaths($this->paths, $locale, $group); # 4
}
return $this->loadNamespaced($locale, $group, $namespace);
}
load 함수가 3개의 인수를 받는 것을 볼 수 있습니다. 처음에는 혼란스러울 수 있지만, str_replace('.', '/', $namespace) 값은 실제로 두 번째 인수 $group에 전달됩니다.$namespace는 항상 NULL이므로 *가 될 수 없습니다. 마찬가지로 $group도 우리가 여기에 전달하는 것을 제어할 수 있으므로 *가 될 수 없습니다.$namespace가 null이므로 해당 분기가 선택되어 #4에서 loadPaths가 실행됩니다.위 내용을 이해한 후, 실제 마법이 일어나는 loadPaths 분석으로 넘어갑니다.
protected function loadPaths(array $paths, $locale, $group)
{
return (new Collection($paths))
->reduce(function ($output, $path) use ($locale, $group) {
if ($this->files->exists($full = "{$path}/{$locale}/{$group}.php")) { # 1
$output = array_replace_recursive($output, $this->files->getRequire($full)); # 2
}
return $output;
}, []);
}
.php를 추가하여 예상되는 번역 파일 이름을 구성합니다.getRequire()를 통해 로드되며, 이는 PHP 파일을 실행하고 배열을 반환합니다.namespace URL 매개변수를 제어할 수 있으므로 애플리케이션이 로드하려고 시도하는 파일에 영향을 줄 수 있습니다. .php 스크립트에 매핑되는 값을 제공하여 서버가 해당 스크립트를 실행하도록 할 수 있습니다. 예를 들어, phpinfo.php를 로드하려면 매개변수를 namespace=phpinfo로 설정하면 됩니다.
PoC는 pearcmd.php를 통해 임의 파일 쓰기를 시도합니다. 이 방법은 많은 유사한 PoC 및 보안 연구에서 사용되었습니다.
참고로 pearcmd.php는 다음과 같은 다양한 디렉터리에 위치할 수 있습니다.
/usr/share/php/pearcmd.php/usr/share/php/PEAR/pearcmd.php/usr/lib/php/pearcmd.php등등.
쓰여지는 파일에는 일반적인 악성 PHP 코드가 포함됩니다.
승인된 테스트 전용입니다. 소유하지 않거나 명시적 테스트 허가가 없는 시스템에서 사용하지 마십시오.
모든 관련 법률을 준수할 책임은 사용자에게 있으며, 저자는 오용에 대한 책임을 지지 않습니다.
Python requests 라이브러리는 제공된 URL의 문자를 인코딩하기 때문에 약간 지나치게 도움이 됩니다. 이 문제를 해결하는 데 오랜 시간을 소비했으며 결국 curl을 직접 사용하기로 했습니다. 이 문제에 대한 도움을 주실 수 있는 분이 계시면 감사하겠습니다.