Deserialização insegura

1. Introdução à Deserialização Insegura

Serialização é o processo de converter objetos ou estruturas de dados em um formato que pode ser armazenado ou transmitido (como uma string ou bytes). Deserialização é o processo inverso: reconstruir o objeto original a partir desse formato serializado.

A deserialização insegura ocorre quando uma aplicação desserializa dados recebidos de fontes não confiáveis sem validação adequada. Isso permite que um atacante injete objetos maliciosos que podem alterar o fluxo de execução do programa, levando a ataques como execução remota de código (RCE), negação de serviço (DoS) ou bypass de autenticação.

Este vetor de ataque frequentemente se relaciona com Server-Side Request Forgery (SSRF) e XML External Entity (XXE), pois a deserialização pode ser usada como ponto de entrada para explorar outras vulnerabilidades na infraestrutura.

2. Como Funciona o Ataque de Deserialização Insegura

O mecanismo básico envolve a injeção de um objeto serializado malicioso que, ao ser desserializado, executa código arbitrário ou manipula o estado da aplicação.

Exemplo simplificado em PHP

<?php
class MaliciousClass {
    public $command = 'id';

    public function __wakeup() {
        system($this->command);
    }
}

// Payload malicioso criado pelo atacante
$payload = 'O:14:"MaliciousClass":1:{s:7:"command";s:2:"id";}';
$object = unserialize($payload); // Executa system("id")
?>

Exemplo simplificado em Java

import java.io.*;

public class Exploit implements Serializable {
    private void readObject(ObjectInputStream ois) throws Exception {
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc.exe");
    }
}

// Código vulnerável
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dados.bin"));
Object obj = ois.readObject(); // Executa código malicioso

Diferença entre payloads e gadget chains

  • Payloads diretos: O atacante controla diretamente a classe que será desserializada (raro em aplicações reais).
  • Gadget chains: O atacante usa classes existentes na aplicação que, quando combinadas em uma sequência específica, executam código malicioso.

3. Principais Linguagens e Bibliotecas Vulneráveis

PHP

unserialize() com entrada controlada pelo usuário é extremamente perigoso. A função __wakeup() e __destruct() podem ser exploradas.

// Código vulnerável
$data = $_GET['data'];
$object = unserialize($data); // NUNCA faça isso!

Java

ObjectInputStream.readObject() é o método clássico. Bibliotecas como Jackson (com enableDefaultTyping()), XStream e Fastjson também apresentam vulnerabilidades conhecidas.

// Jackson vulnerável
ObjectMapper mapper = new ObjectMapper();
mapper.enableDefaultTyping();
Object obj = mapper.readValue(jsonString, Object.class);

Python

pickle.loads() é inerentemente inseguro com dados não confiáveis. yaml.load() também permite execução de código arbitrário.

import pickle
import yaml

# Perigoso
data = b"cos\nsystem\n(S'id'\ntR."
pickle.loads(data)

# Perigoso
yaml.load("!!python/object/apply:os.system ['id']")

.NET

BinaryFormatter.Deserialize(), JavaScriptSerializer.Deserialize() e LosFormatter são vulneráveis quando usados com dados não confiáveis.

// Código vulnerável em C#
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream(data);
object obj = formatter.Deserialize(stream);

4. Gadget Chains e Exploração Avançada

Gadget chains são sequências de classes legítimas presentes no classpath da aplicação que, quando encadeadas, permitem execução de código arbitrário. O atacante não precisa injetar classes próprias; ele apenas constrói um objeto serializado que, ao ser desserializado, ativa a cadeia.

Exemplo real: Apache Commons Collections

A biblioteca Apache Commons Collections possui classes como InvokerTransformer e ChainedTransformer que, combinadas, permitem executar qualquer método em qualquer objeto.

// Gadget chain simplificada
Transformer[] transformers = new Transformer[] {
    new ConstantTransformer(Runtime.class),
    new InvokerTransformer("getMethod", 
        new Class[] {String.class, Class[].class},
        new Object[] {"getRuntime", new Class[0]}),
    new InvokerTransformer("invoke",
        new Class[] {Object.class, Object[].class},
        new Object[] {null, new Object[0]}),
    new InvokerTransformer("exec",
        new Class[] {String.class},
        new Object[] {"calc.exe"})
};
Transformer chain = new ChainedTransformer(transformers);

Ferramentas de exploração

  • ysoserial: Gera payloads para Java (suporta múltiplas gadget chains como CommonsCollections, Spring, Jackson)
  • PHPGGC: Gera gadget chains para PHP
  • gadgetinspector: Ferramenta da Cisco para encontrar gadget chains em código Java

5. Consequências de uma Deserialização Bem-Sucedida

  • Execução remota de código (RCE): O atacante executa comandos no servidor
  • Escalação de privilégios: Acesso a funcionalidades restritas
  • Movimentação lateral: Uso do servidor comprometido para atacar outros sistemas
  • Exfiltração de dados: Leitura de arquivos sensíveis ou bases de dados
  • Negação de serviço (DoS): Criação de objetos que consomem recursos infinitos

6. Boas Práticas de Prevenção para Desenvolvedores

Nunca desserializar dados de fontes não confiáveis

Esta é a regra mais importante. Se possível, evite completamente a deserialização de dados externos.

Uso de formatos seguros

Prefira JSON com bibliotecas seguras:

// Python seguro
import json
data = json.loads(trusted_input)  # Não permite execução de código

// Java seguro com Jackson
ObjectMapper mapper = new ObjectMapper();
MyClass obj = mapper.readValue(json, MyClass.class);  // Sem default typing

Implementação de allowlists

Valide o tipo de objeto antes de desserializar:

// PHP com allowlist
$allowed_classes = ['User', 'Product'];
$object = unserialize($data, ['allowed_classes' => $allowed_classes]);

Alternativas seguras

  • Python: Use pickle apenas para dados internos
  • Rust: Use serde com tipos estáticos
  • Java: Considere usar protocol buffers ou JSON em vez de serialização nativa

7. Medidas de Mitigação em Arquitetura e Infraestrutura

Isolamento de processos

Execute serviços que realizam deserialização em contêineres ou sandboxes com privilégios mínimos.

Monitoramento e logging

Implemente detecção de tentativas de deserialização suspeitas:

// Exemplo de log para análise
2024-01-15 10:30:45 WARN  Deserialization attempt from IP 192.168.1.100
Payload size: 45KB, Object type: java.util.HashMap

Atualização de dependências

Mantenha bibliotecas atualizadas e remova aquelas com gadget chains conhecidas (ex: versões antigas do Apache Commons Collections).

Uso de WAF

Configure regras para bloquear payloads comuns de deserialização:

# Regra ModSecurity para detectar payloads PHP
SecRule ARGS "@rx O:\d+:" "id:12345,phase:2,deny,status:403"

8. Testes e Validação de Segurança Contra Deserialização

Testes manuais

Identifique endpoints que aceitam dados serializados (formato binário, base64, XML, YAML) e teste com payloads conhecidos.

Ferramentas de scanning

Burp Suite com extensões como "Java Deserialization Scanner" ou "PHP unserialize Payload Generator".

Exemplo de teste de penetração

# Envio de payload PHP para teste
GET /api/user?data=O:14:"MaliciousClass":0:{} HTTP/1.1
Host: vulnerable-app.com

# Resposta esperada se vulnerável: erro ou execução de código

Checklist de revisão de código

  • [ ] Identificar todas as funções de deserialização (unserialize, pickle.loads, readObject)
  • [ ] Verificar se a entrada é controlada pelo usuário
  • [ ] Avaliar se há validação de tipo ou allowlists
  • [ ] Verificar dependências com gadget chains conhecidas
  • [ ] Testar com payloads de deserialização

Referências