Desmistificando o smuggling de requisições HTTP
Sam Sanoop
30 de junho de 2020
0 minutos de leituraO smuggling de requisições HTTP é um tipo interessante de vulnerabilidade que ganhou popularidade no último ano. Ela pode permitir que um invasor explore recursos específicos do protocolo HTTP/1.1 para contornar proteções de segurança, realizar ataques de phishing e obter informações confidenciais de requisições que não são suas.
Vale destacar também que o smuggling de requisições recebeu ampla atenção da comunidade nos últimos meses, com vários relatórios de bug bounty que pagaram altas recompensas. A Snyk observou que, até o momento, 18 vulnerabilidades relacionadas ao smuggling de requisições HTTP em dependências haviam sido divulgadas em 2020.
A vulnerabilidade foi discutida pela primeira vez pela Watchfire em seu white paper de 2005, intitulado “HTTP Request Smuggling”. Mais tarde, o trabalho foi ampliado pelo pesquisador Regis Leroy e discutido por James Kettle, da Portswigger Security, durante a BlackHat USA 2019, quando recebeu ainda mais atenção da mídia.
Esta publicação busca desmistificar a vulnerabilidade e fornecer detalhes sobre como corrigi-la para mantenedores de projetos de código aberto que desejam corrigir o smuggling de requisições HTTP em seus projetos.
Contexto
Quando novos ataques e vulnerabilidades ganham popularidade e repercussão na mídia, bibliotecas de código aberto costumam ser alvo de invasores que buscam explorá-las em ambientes reais. Muitas vezes, pode levar semanas ou anos para corrigir essas vulnerabilidades nas bibliotecas, devido à falta de conhecimento sobre como remediá-las. Para tornar a comunidade de código aberto mais segura, a equipe de segurança da Snyk pesquisa vulnerabilidades, como o smuggling de requisições HTTP, para identificar falhas em dependências de código aberto e oferecer recomendações práticas de correção aos mantenedores.
Uma dessas iniciativas da Snyk buscou identificar o impacto do smuggling de requisições HTTP no ecossistema de dependências de código aberto e resultou na descoberta de várias vulnerabilidades. Saiba mais sobre as vulnerabilidades identificadas aqui.
A Snyk constatou que faltam fontes públicas com orientações adequadas para corrigir o smuggling de requisições HTTP. Além disso, observamos que os mantenedores precisaram lançar várias versões até corrigir e resolver a vulnerabilidade adequadamente. Com esta publicação, queremos preencher essa lacuna e oferecer orientações detalhadas a qualquer mantenedor que precise de ajuda com esse problema.
Entenda a vulnerabilidade de smuggling de requisições HTTP
Para entender o smuggling de requisições HTTP, primeiro é preciso compreender os tópicos a seguir:
Keep-Alive e pipelining
O conceito de Keep-Alive e pipelining foi publicado inicialmente na RFC 2616.
O cabeçalho Keep-Alive é um cabeçalho hop-by-hop que fornece informações sobre uma conexão persistente. Em servidores web, o Keep-Alive pode ser especificado no cabeçalho “Connection”, permitindo que o servidor mantenha um soquete/conexão TCP aberto. Com esse cabeçalho, várias requisições e respostas podem usar uma única conexão, reduzindo a sobrecarga e melhorando o desempenho do servidor web. Hoje, todos os navegadores e servidores oferecem suporte a esse recurso.
O pipelining é outro recurso introduzido na RFC 2616. Ele permite que um servidor web processe requisições de forma assíncrona — como um fluxo no formato primeiro a entrar, primeiro a sair — em vez de processar cada requisição individualmente. Assim, é possível enviar uma requisição sem esperar a chegada da resposta anterior.
Content-Length e Transfer-Encoding
As requisições HTTP podem ter um corpo de mensagem. A presença desse corpo em uma requisição é indicada por um campo de cabeçalho Content-Length ou Transfer-Encoding. Esses cabeçalhos são usados para delimitar mensagens, indicando ao servidor onde uma mensagem termina e outra começa.
O Content-Length, especificado na RFC 7230, seção 3.3.2, é um cabeçalho HTTP que indica o tamanho do corpo da entidade da requisição. Ele é comum em requisições HTTP POST, que têm um corpo de dados. Vale observar que, em geral, requisições GET não devem conter o cabeçalho Content-Length, pois não têm corpo.
O Transfer-Encoding, também especificado na RFC 7230, foi criado para permitir o envio de dados binários por HTTP. O Transfer-Encoding tem várias diretivas; esta publicação se concentra na diretiva chunked.
A diretiva chunked permite enviar dados em uma série de blocos, com o tamanho desses blocos especificado em formato hexadecimal, seguido por um retorno de carro e uma quebra de linha. A diretiva chunked termina com 0 e uma sequência vazia. Veja abaixo um exemplo de requisição chunked.
Smuggling de requisições
Uma arquitetura moderna de servidor web geralmente inclui vários servidores web, além de balanceadores de carga e servidores WSGI. Um diagrama simples para visualizar essa estrutura seria:

As vulnerabilidades de smuggling de requisições HTTP surgem quando o frontend e o backend interpretam de forma diferente os limites de uma requisição HTTP, causando uma dessincronização entre eles. Isso acontece porque várias bibliotecas de frontend e backend se desviam das especificações RFC ao lidar com os cabeçalhos Content-Length e Transfer-Encoding. O corpo de uma requisição HTTP pode ser delimitado de acordo com esses dois cabeçalhos, e é aí que ocorrem desvios da especificação. Como resultado, parte de uma requisição é anexada à seguinte ou inserida nela de forma oculta, fazendo com que a resposta da requisição manipulada seja enviada a outro usuário.
Essa vulnerabilidade pode ser explorada para realizar ataques de phishing, envenenamento de cache, Cross-Site Scripting (XSS) e muito mais. James Kettle publicou mais informações sobre a exploração dessa vulnerabilidade no ano passado, durante a BlackHat USA 2019, sob o título “HTTP Desync Attacks: Request Smuggling Reborn”. Esta publicação se concentra nas duas técnicas de smuggling de requisições mais comuns:
CL:CL: técnica de ataque com Content-Length duplicado
CL:TE: ataque com Content-Length e Transfer-
Encoding
CL:CL: técnica de ataque com Content-Length duplicado
De acordo com a RFC 7230, seção 3.3.3#4:
“Se uma mensagem for recebida sem Transfer-Encoding e com vários campos de cabeçalho Content-Length com valores diferentes, ou com um único campo de cabeçalho Content-Length com um valor inválido, a delimitação da mensagem será inválida e o destinatário DEVE tratá-la como um erro irrecuperável”
No entanto, atualmente, a maioria dos middlewares e servidores web lida de forma permissiva com requisições GET que contêm um corpo. Além disso, a RFC 7231, seção 4.3#4.3.1 afirma: “Um payload em uma mensagem de requisição GET não tem semântica definida; o envio de um corpo de payload em uma requisição GET pode levar algumas implementações existentes a rejeitar a requisição”. Isso indica que muitos servidores e proxies podem aceitar esse comportamento. Em alguns casos, isso pode resultar em ataques de smuggling de requisições. Esta publicação explora uma variação desse ataque.
Quando uma requisição contém dois cabeçalhos Content-Length, diferenças na implementação entre o frontend e o backend quanto a qual deles deve ter prioridade podem permitir ataques de smuggling.

Na requisição HTTP acima, uma requisição com dois cabeçalhos Content-Length é enviada a um alvo cujo frontend é um proxy ou balanceador de carga. O proxy respeita o primeiro Content-Length e dá prioridade a ele, tratando a requisição manipulada como parte do corpo da requisição, embora uma requisição GET não devesse ter corpo e tenham sido fornecidos dois cabeçalhos Content-Length. Ao processar a requisição no backend, o primeiro cabeçalho Content-Length é ignorado e o segundo recebe prioridade. Como o segundo Content-Length foi definido como zero, o backend não espera um corpo de requisição, e a requisição /reqsmuggle é tratada como outra requisição em pipelining. Assim, outro usuário pode receber a resposta dessa requisição manipulada.
CL:TE: técnica de ataque com Content-Length e Transfer-Encoding
Essa técnica popular, descoberta por James Kettle, do PortSwigger Labs, consiste em incluir um cabeçalho Content-Length e um cabeçalho Transfer-Encoding em uma única requisição HTTP e manipulá-los para que um proxy frontend e um servidor backend priorizem esses cabeçalhos e processem a requisição manipulada de maneiras diferentes. Veja a seguir um exemplo de requisição que demonstra essa vulnerabilidade.
Quando um proxy frontend processa essa requisição, ele respeita o cabeçalho Content-Length e dá prioridade a ele em vez do cabeçalho Transfer-Encoding. Já o servidor backend dá prioridade ao cabeçalho Transfer-Encoding. Por isso, o backend processa a requisição até o caractere 0. Como resultado, a requisição GET 404 é tratada como uma requisição separada em pipelining.
Esse problema ocorre porque o frontend dá prioridade ao cabeçalho Content-Length em vez do Transfer-Encoding. De acordo com a RFC 7230, seção 3.3.3#3, se uma mensagem com Content-Length e Transfer-Encoding for aceita, o cabeçalho Transfer-Encoding substitui o Content-Length. O frontend não segue essa regra.

Quando o frontend dá prioridade ao cabeçalho Transfer-Encoding, talvez seja possível contornar essa limitação inserindo um cabeçalho Transfer-Encoding malformado em vez de um válido. Veja alguns exemplos abaixo:
Várias bibliotecas HTTP aceitam diferentes variações do cabeçalho Transfer-Encoding e as normalizam para melhorar a experiência do cliente. Portanto, ao entender quais variações do cabeçalho Transfer-Encoding são normalizadas pelo servidor backend, talvez seja possível passar um cabeçalho Transfer-Encoding malformado pelo frontend e realizar um ataque de smuggling CL:TE.
Os exemplos acima apenas demonstram a vulnerabilidade. Em um cenário de ataque real, é possível criar uma requisição manipulada para realizar ataques de phishing ou roubar dados da requisição de uma vítima e enviá-los a um servidor controlado pelo invasor.
Escopo e equívocos
Durante esta pesquisa, a Snyk identificou vários casos em que a complexidade evidente dos múltiplos vetores de ataque levou a equívocos sobre o smuggling de requisições HTTP nos relatos enviados por pesquisadores aos mantenedores de projetos de código aberto. Nem sempre pesquisadores e mantenedores entendiam se a correção era responsabilidade da biblioteca mantida por eles ou se o problema deveria ser resolvido em uma dependência usada por essa biblioteca.
Alguns dos desafios que precisaram de esclarecimentos adicionais foram:
Proxies, balanceadores de carga e outros componentes classificados como frontend são os mais afetados por essa vulnerabilidade. No entanto, um ataque de smuggling de requisições bem-sucedido exige a exploração de um frontend e um backend vulneráveis.
A responsabilidade pela correção cabe tanto ao mantenedor do backend quanto ao do frontend. É possível argumentar que normalizar cabeçalhos malformados deveria ser um comportamento esperado do backend, tornando-o mais tolerante a erros do usuário, e que o verdadeiro problema são os proxies que encaminham essas requisições sem normalizá-las antes. Porém, dado o vasto ecossistema de dependências e o grande número de bibliotecas que podem se enquadrar nos critérios de backend ou frontend, o ideal é que ambas as partes tentem corrigir o problema.
Middlewares, servidores web com recursos de proxy reverso e servidores WSGI/de alto desempenho se enquadram nos critérios de backend. Já um servidor web que apenas processa requisições malformadas, mas não oferece suporte a pipelining, não se enquadra.
Muitas vezes, relatos de segurança identificam um problema como smuggling de requisições HTTP porque os servidores respondem a várias requisições enviadas, e isso aparece como duas respostas separadas. Vale observar que muitos servidores oferecem suporte a Keep-Alive e pipelining — isso, por si só, não caracteriza uma vulnerabilidade de smuggling de requisições HTTP. Esse é o caso da CVE-2020-12440, relatada para o NGINX.
Correção
Os problemas de contrabando de solicitações HTTP ocorrem devido a desvios das especificações HTTP atuais e ao fato de várias bibliotecas não seguirem a RFC 7230. Além disso, corrigir essa vulnerabilidade pode ser complicado, dependendo de você ser responsável pela manutenção de um projeto de frontend ou backend. Por isso, a Snyk pesquisou as correções implementadas por projetos de código aberto e as categorizou da seguinte forma. Para simplificar, todos os pontos de correção são apresentados junto com uma explicação dos tipos de ataque de contrabando que ajudam a evitar. No cenário ideal, todos os pontos mencionados abaixo devem ser adotados como parte de uma solução de defesa em profundidade.
Priorize Transfer-Encoding em vez de Content-LengthCorreção: Esta correção evita ataques CL:TE e TE:CLEscopo: Frontend, backendDetalhes: Quando uma solicitação com os cabeçalhos Transfer-Encoding: chunked e Content-Length é recebida, o cabeçalho Transfer-Encoding deve ter prioridade sobre Content-Length. Isso é indicado na seção 3.3.3#3 da RFC 7230.
Rejeite solicitações com Content-Length e Transfer-Encoding, bem como cabeçalhos Content-Length duplicadosCorreção: Esta correção evita ataques CL:CL, CL:TE e TE:CLEscopo: Backend, frontend, backend, bibliotecas upstreamDetalhes: Esta pode ser considerada uma alternativa melhor à solução “Priorize Transfer-Encoding em vez de Content-Length”. Plataformas de execução como o Node.js adotaram essa solução para corrigir o contrabando de solicitações: solicitações com ambos os cabeçalhos recebem uma resposta HTTP 400.
A Snyk considera essa técnica uma solução ideal para evitar problemas de contrabando. No entanto, é preciso avaliar como a correção será aplicada, principalmente se for implementada em uma biblioteca HTTP de baixo nível ou em um mecanismo upstream do qual dependem vários pacotes, como os de frontend.
Ao corrigir problemas de contrabando de solicitações HTTP, é preciso seguir a RFC 7230 #3.3.3. A seção 3.3.3#3 da RFC 7230 afirma:
“Se uma mensagem for recebida com os campos de cabeçalho Transfer-Encoding e Content-Length, Transfer-Encoding prevalece sobre Content-Length. Essa mensagem pode indicar uma tentativa de realizar contrabando de solicitações (seção 9.5) ou divisão de respostas (seção 9.4) e deve ser tratada como um erro. O remetente DEVE remover o campo Content-Length recebido antes de encaminhar essa mensagem para um sistema downstream.”
No entanto, isso difere do que está especificado na RFC 2616 4.4#3:
“Se uma mensagem for recebida com os campos de cabeçalho Transfer-Encoding e Content-Length, o segundo DEVE ser ignorado.”
Vale observar que a RFC 2616 4.4#3 está obsoleta e foi substituída pela RFC 7230. A RFC 7230 deve ser considerada ao implementar uma correção. Portanto, ao processar solicitações HTTP com os cabeçalhos Content-Length e Transfer-Encoding, o comportamento correto é o frontend remover o cabeçalho Content-Length antes de encaminhar a solicitação para um backend downstream, e não retornar uma resposta HTTP 400. O mesmo deve ser feito com solicitações que contenham vários cabeçalhos Content-Length, conforme indicado na RFC 7230#3.3.2.
Como mencionado na seção 3.3.2 da RFC 7230, se uma solicitação HTTP for recebida com vários cabeçalhos Content-Length com valores diferentes, o problema pode ser corrigido com uma resposta HTTP 400 ou substituindo os valores duplicados por um único campo Content-Length válido. A Snyk recomenda que bibliotecas HTTP de baixo nível optem por substituir vários cabeçalhos por um único cabeçalho válido.
Rejeite cabeçalhos Transfer-Encoding malformados e processe corretamente vários valores TECorreção: Esta correção evita ataques TE:TE.Escopo: Frontend, backendDetalhes: Se tanto o frontend quanto o backend priorizarem o cabeçalho Transfer-Encoding, isso poderá permitir ataques de contrabando em que um invasor insere dois cabeçalhos Transfer-Encoding: um é ignorado pelo frontend e processado pelo backend, ou vice-versa. Por isso, as seguintes variações de cabeçalho devem ser rejeitadas.

No entanto, vale observar que ainda é possível que invasores encontrem uma variação malformada do cabeçalho de codificação “chunked” que não esteja documentada acima. Portanto, essa medida, por si só, não deve ser considerada suficiente para corrigir o problema. Uma abordagem melhor é rejeitar solicitações com Content-Length e Transfer-Encoding, além de rejeitar cabeçalhos malformados.
Por fim, também vale observar que, se o valor “chunked” for especificado junto com outros valores de Transfer-Encoding, como “gzip” ou “deflate”, isso não deve ser ignorado: Transfer-Encoding deve ter prioridade sobre Content-Length. Um exemplo dessa vulnerabilidade pode ser visto na CVE-2019-16786. Se uma solicitação HTTP for enviada com os seguintes valores de Transfer-Encoding, o valor “chunked” deve ser identificado corretamente e ter prioridade:
Transfer-Encoding: gzip, chunked
Em um cabeçalho Transfer-Encoding, vários valores podem ser listados, separados por vírgula. No entanto, pode acontecer de um frontend identificar apenas o valor “gzip” e, por isso, priorizar o cabeçalho Content-Length, enquanto o backend processa a solicitação como chunked, resultando em um ataque CL:TE. A Snyk recomenda verificar corretamente quando vários valores forem especificados e priorizar Transfer-Encoding se “chunked” estiver presente. Além disso, se vários cabeçalhos Transfer-Encoding forem especificados, o valor “chunked” só deve aparecer como último valor, depois dos outros valores de codificação de transferência. Isso está especificado na seção 3.3.1 da RFC 7230.
Conclusão
Em resumo, o contrabando de solicitações HTTP pode ser uma vulnerabilidade difícil de entender e corrigir. Esperamos que este artigo ajude pessoas responsáveis pela manutenção de projetos a criar correções eficientes e proteger seus projetos de código aberto.
Também vale destacar que este artigo aborda apenas duas técnicas de contrabando de solicitações HTTP e não detalha os diferentes cenários de exploração. O artigo a seguir, do pesquisador de segurança ZeddYu, aborda o contrabando de solicitações HTTP em profundidade e detalha cada vetor de ataque: Zeddy Yu: Ajude você a entender o contrabando de HTTP em um artigo.
Comece a jogar Capture the Flag
Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.



