Desserialização insegura na prática

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:

GitHub - crowsec-edtech/Lab-insecure-desserialization: Lab de desserialização insegura
Lab de desserialização insegura. Contribute to crowsec-edtech/Lab-insecure-desserialization development by creating an account on GitHub.

Referências

Insecure deserialization | Web Security Academy
In this section, we’ll cover what insecure deserialization is and describe how it can potentially expose websites to high-severity attacks. We’ll highlight ...
OWASP Top Ten 2017 | A8:2017-Insecure Deserialization | OWASP Foundation
A8:2017-Insecure Deserialization on the main website for The OWASP Foundation. OWASP is a nonprofit foundation that works to improve the security of software.

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!