Desserialização insegura na prática
Nesse artigo, vamos falar sobre a desserialização insegura (especificamente no PHP), e como podemos obter RCE através dessa vulnerabilidade. É recomendado que você estude Orientação a Objetos antes de estudar desserialização insegura.
O que é desserialização e serialização?
Em alguns códigos POO, precisamos serializar objetos para alguns propósitos, e utilizamos a desserialização para conseguirmos chamar propriedades e métodos do objeto que foi serializado. Nesse artigo, veremos um código que possui um objeto para a autenticação do usuário, e ele será serializado e desserializado para verificar os dados.
Código de exemplo:
<?php
class Auth
{
private $username;
private $role;
public function __construct($username, $role)
{
$this->username = $username;
$this->role = $role;
}
public function getRole()
{
return $this->role;
}
public function getName()
{
return $this->username;
}
public function getAll()
{
return (object)["username" => $this->username, "role" => $this->role];
}
}
$auth = new Auth("john","guest");
$serialized = serialize($auth);
$user = unserialize($serialized);
$user_name = $user->getName();
$user_role = $user->getRole();
$user_all = $user->getAll();
echo "Ola {$user_name}, atualmente você está no cargo {$user_role}";
?>
No código acima, vemos que o objeto Auth possui 2 propriedades: "username, role". O método "__construct" recebe 2 parâmetros que serão armazenados nas propriedades. Logo abaixo, vemos que foi definido 3 métodos: "getRole, getName, getAll". O método "getRole" retorna o valor da propriedade "role", o "getName" retorna o valor da propriedade "username", e o método"getAll" retorna um objeto contendo as propriedades "username" e "role".
Abaixo do objeto, vemos que o código instancia o objeto Auth especificando "john" e "guest" como parâmetros para "__construct". Logo depois, o código serializa esse objeto e armazena dentro da variável "$serialized".
Para recuperarmos os dados armazenados no objeto serializado, precisamos desserializá-lo e armazená-lo em uma variável "$user". Após isso, podemos acessar os métodos e as propriedades do objeto serializado:
$user = unserialize($serialized);
$user_name = $user->getName();
$user_role = $user->getRole();
$user_all = $user->getAll();
Podemos obter e alterar os valores das propriedades do objeto da seguinte maneira:
$user = unserialize($serialized);
echo $user->username; //nome antigo (john)
$user->username = "John Doe"; // alterando o valor da propriedade para (John Doe)
echo $user->username; // novo nome (John Doe)
Como a desserialização pode afetar a aplicação?
Imagine um cenário onde a aplicação utiliza serialização e desserialização do objeto Auth para fazer a autenticação dos usuários, isso seria algo comum e aparentemente sem vulnerabilidades, porém se o usuário conseguir alterar o objeto serializado, isso permitiria com que alterássemos os valores das propriedades do objeto, assim conseguindo definir novas roles e alterar o username.
Explorando desserialização insegura
A desserialização insegura ocorre quando um usuário tem acesso em alguma função de desserialização, como a "unserialize()". Antes de começar a exploração, vamos estudar a "sintaxe" de um objeto serializado:
O:4:"Auth":2:{s:8:"username";s:8:"John Doe";s:4:"role";s:5:"guest";}
Explorando
Na imagem abaixo, vemos uma página que possui um objeto serializado e um campo para enviarmos esse objeto:
Enviando o objeto, a aplicação nos retorna um json:
O nosso objetivo é alterar a "role" e o "username" desse objeto para "admin". Para fazermos isso, basta alterarmos o valor "guest" e "John Doe" para "admin", porém precisamos modificar o número que indica a quantidade de caracteres contidas na string. Nesse caso, "admin" possui 5 caracteres, então o objeto ficaria da seguinte maneira:
Objeto fornecido: O:4:"Auth":2:{s:8:"username";s:8:"John Doe";s:4:"role";s:5:"guest";}
Novo objeto: O:4:"Auth":2:{s:8:"username";s:5:"admin";s:4:"role";s:5:"admin";}
Enviando o novo objeto:
Obtendo RCE através de uma desserialização insegura
Quando temos uma desserialização insegura, conseguimos acessar qualquer objeto do código. No PHP existem alguns magic methods como a "__construct", "__destruct", "__wakeup", "__toString", etc... Quando chamamos um objeto através da desserialização, alguns magic methods são "invocados" mesmo sem especificá-los no código, dentre eles: "__destruct", "__wakeup", "__toString". É claro que para eles serem chamados eles precisam ser declarados dentro do objeto.
Code Review
Vemos que o objeto "Log" possui o magic method "__destruct", e dentro desse método vemos que a linha 41 executa um comando no sistema concatenando a propriedade "$logfile". Se conseguirmos inserir conteúdo nessa propriedade, podemos explorar command injection na aplicação. Uma maneira de fazermos isso seria pela desserialização insegura. Para isso, vamos criar um código PHP para gerar o objeto serializado com a payload de command injection:
Executando o código acima, obtemos o seguinte output:
O:3:"Log":1:{s:7:"logfile";s:4:"| id";}
Veja que o nome do objeto mudou para "Log" e ele contém apenas 1 propriedade. O nome dessa propriedade contém 7 caracteres "logfile", e o valor contido nessa propriedade possui "4" caracteres "| id".
Enviando esse objeto serializado na aplicação, vemos que o comando "id" foi executado no sistema:
Como podemos nos proteger da desserialização insegura?
Para nos protegermos da desserialização insegura, devemos ficar atentos nas funções "unserialize()" para não permitir com que um usuário tenha controle sobre essa função.
Onde praticar?
No hackingclub temos diversas máquinas de desserialização insegura. Diversos cenários exigindo com que o jogador faça code review e crie payloads customizadas.
O ambiente mostrado de exemplo neste artigo está disponível no github da CrowSec:
Referências
Onde praticar Hacking ?
O Hacking Club é uma plataforma de treinamento em cybersecurity, que permite você aprender hacking de forma totalmente prática.
Temos mais de 50 ambientes com vulnerabilidades reais com write-ups para você treinar e aprender hacking. Semanalmente lançamos máquinas gratuitas para você praticar e se desafiar no hacking!