Upgrade Header Smuggling

Support HackTricks

H2C Smuggling

HTTP2 Over Cleartext (H2C)

H2C, 또는 http2 over cleartext는 표준 HTTP 연결을 지속적인 연결로 업그레이드하여 일시적인 HTTP 연결의 규범에서 벗어납니다. 이 업그레이드된 연결은 평문 HTTP의 단일 요청 특성과는 달리 지속적인 통신을 위해 http2 이진 프로토콜을 사용합니다.

밀수 문제의 핵심은 리버스 프록시의 사용에서 발생합니다. 일반적으로 리버스 프록시는 HTTP 요청을 처리하고 백엔드로 전달한 후 백엔드의 응답을 반환합니다. 그러나 HTTP 요청에 Connection: Upgrade 헤더가 포함되어 있을 때(웹소켓 연결에서 일반적으로 볼 수 있음), 리버스 프록시는 클라이언트와 서버 간의 지속적인 연결을 유지하여 특정 프로토콜에서 요구하는 지속적인 교환을 용이하게 합니다. H2C 연결의 경우, RFC 준수를 위해 세 가지 특정 헤더가 필요합니다:

Upgrade: h2c
HTTP2-Settings: AAMAAABkAARAAAAAAAIAAAAA
Connection: Upgrade, HTTP2-Settings

취약점은 연결을 업그레이드한 후 리버스 프록시가 개별 요청을 관리하지 않게 되어 연결 설정 후 라우팅 작업이 완료되었다고 가정할 때 발생합니다. H2C 스머글링을 이용하면 H2C 연결이 성공적으로 시작된 경우 요청 처리 중 적용된 리버스 프록시 규칙(예: 경로 기반 라우팅, 인증 및 WAF 처리)을 우회할 수 있습니다.

취약한 프록시

취약점은 리버스 프록시가 Upgrade 및 때때로 Connection 헤더를 처리하는 방식에 따라 달라집니다. 다음 프록시는 프록시 패스 중 이러한 헤더를 본질적으로 전달하여 H2C 스머글링을 본질적으로 가능하게 합니다:

  • HAProxy

  • Traefik

  • Nuster

반대로, 이러한 서비스는 프록시 패스 중 두 헤더를 본질적으로 전달하지 않습니다. 그러나 불안전하게 구성될 수 있어 UpgradeConnection 헤더의 필터링되지 않은 전달을 허용할 수 있습니다:

  • AWS ALB/CLB

  • NGINX

  • Apache

  • Squid

  • Varnish

  • Kong

  • Envoy

  • Apache Traffic Server

악용

모든 서버가 H2C 연결 업그레이드에 필요한 헤더를 본질적으로 전달하지 않는다는 점에 유의해야 합니다. 따라서 AWS ALB/CLB, NGINX 및 Apache Traffic Server와 같은 서버는 자연스럽게 H2C 연결을 차단합니다. 그럼에도 불구하고, 일부 백엔드가 표준을 준수하지 않을 수 있으므로 Connection: Upgrade 변형을 테스트해 볼 가치가 있습니다. 이 변형은 Connection 헤더에서 HTTP2-Settings 값을 제외합니다.

proxy_pass URL에 지정된 특정 경로(예: http://backend:9999/socket.io)와 관계없이 설정된 연결은 기본적으로 http://backend:9999로 설정됩니다. 이를 통해 이 기술을 활용하여 해당 내부 엔드포인트 내의 모든 경로와 상호작용할 수 있습니다. 따라서 proxy_pass URL에 경로를 지정하는 것은 접근을 제한하지 않습니다.

도구 h2csmuggler by BishopFoxh2csmuggler by assetnote는 H2C 연결을 설정하여 프록시가 부과한 보호를 우회하려는 시도를 용이하게 하여 프록시로 보호된 리소스에 접근할 수 있게 합니다.

이 취약점에 대한 추가 정보, 특히 NGINX와 관련하여 이 상세 리소스를 참조하십시오.

웹소켓 스머글링

웹소켓 스머글링은 프록시를 통해 접근 가능한 엔드포인트에 HTTP2 터널을 생성하는 것과 달리, 잠재적인 프록시 제한을 우회하고 엔드포인트와 직접 통신하기 위해 웹소켓 터널을 설정합니다.

시나리오 1

이 시나리오에서는 공개 웹소켓 API와 접근할 수 없는 내부 REST API를 제공하는 백엔드가 악의적인 클라이언트의 공격 대상이 됩니다. 공격은 여러 단계로 진행됩니다:

  1. 클라이언트는 잘못된 Sec-WebSocket-Version 프로토콜 버전을 헤더에 포함하여 리버스 프록시로 업그레이드 요청을 보냅니다. 프록시는 Sec-WebSocket-Version 헤더를 검증하지 못하고 업그레이드 요청이 유효하다고 믿고 이를 백엔드로 전달합니다.

  2. 백엔드는 Sec-WebSocket-Version 헤더의 잘못된 프로토콜 버전을 나타내는 상태 코드 426으로 응답합니다. 리버스 프록시는 백엔드의 응답 상태를 간과하고 웹소켓 통신 준비가 완료되었다고 가정하여 클라이언트에게 응답을 전달합니다.

  3. 결과적으로 리버스 프록시는 클라이언트와 백엔드 간에 웹소켓 연결이 설정되었다고 잘못 믿게 되지만, 실제로 백엔드는 업그레이드 요청을 거부했습니다. 그럼에도 불구하고 프록시는 클라이언트와 백엔드 간에 열린 TCP 또는 TLS 연결을 유지하여 클라이언트가 이 연결을 통해 비공식 REST API에 무제한으로 접근할 수 있게 합니다.

영향을 받는 리버스 프록시에는 문제를 해결하지 않기로 결정한 Varnish와 업그레이드 메커니즘이 변경된 Envoy 프록시 버전 1.8.0 이하가 포함됩니다. 다른 프록시도 취약할 수 있습니다.

시나리오 2

이 시나리오는 공개 웹소켓 API와 건강 검사를 위한 공개 REST API, 그리고 접근할 수 없는 내부 REST API를 가진 백엔드를 포함합니다. 공격은 더 복잡하며 다음 단계로 진행됩니다:

  1. 클라이언트는 건강 검사 API를 트리거하기 위해 POST 요청을 보내고 추가 HTTP 헤더 Upgrade: websocket을 포함합니다. NGINX는 리버스 프록시로서 이를 Upgrade 헤더만 기반으로 표준 업그레이드 요청으로 해석하고 요청의 다른 측면을 무시하며 백엔드로 전달합니다.

  2. 백엔드는 건강 검사 API를 실행하고 공격자가 제어하는 외부 리소스에 접근하여 상태 코드 101이 포함된 HTTP 응답을 반환합니다. 이 응답은 백엔드에서 수신되어 NGINX로 전달되며, 프록시는 상태 코드만 검증하여 웹소켓 연결이 설정되었다고 잘못 생각하게 만듭니다.

경고: 이 기술의 복잡성은 상태 코드 101을 반환할 수 있는 엔드포인트와 상호작용할 수 있는 능력을 요구함에 따라 증가합니다.

결국 NGINX는 클라이언트와 백엔드 간에 웹소켓 연결이 존재한다고 믿게 됩니다. 실제로는 그런 연결이 존재하지 않으며, 건강 검사 REST API가 목표였습니다. 그럼에도 불구하고 리버스 프록시는 연결을 열어두어 클라이언트가 이를 통해 비공식 REST API에 접근할 수 있게 합니다.

대부분의 리버스 프록시는 이 시나리오에 취약하지만, 악용은 일반적으로 낮은 심각도로 간주되는 외부 SSRF 취약점의 존재에 따라 달라집니다.

실습

https://github.com/0ang3el/websocket-smuggle.git에서 두 시나리오를 테스트할 수 있는 실습을 확인하십시오.

참고 문헌

HackTricks 지원하기

Last updated