HTTP/2 downgrading
HTTP/2 downgrading
HTTP/2 hâlâ görece yeni bir protokol olduğu için, onu destekleyen web server’lar çoğu zaman yalnızca HTTP/1 konuşabilen legacy back-end infrastructure ile iletişim kurmak zorunda kalır. Bu nedenle, front-end server ’ların gelen her HTTP/2 request ’i HTTP/1 syntax kullanarak yeniden yazması yaygın bir uygulama hâline gelmiştir. Böylece, isteğin HTTP/1 eşdeğeri oluşturulur.
Bu şekilde “downgraded” edilen request, ilgili back-end server ’a iletilir.

HTTP/2 downgrading genel bakış
HTTP/1 konuşan back-end bir response döndürdüğünde, front-end server bu süreci tersine çevirerek, yanıtı tekrar HTTP/2 response formatına dönüştürür ve istemciye gönderir.
Bu yaklaşımın çalışmasının sebebi, her iki protokol sürümünün de temelde aynı bilgiyi farklı şekilde temsil etmesi dir. Bir HTTP/1 message içindeki her öğenin, HTTP/2 içinde yaklaşık bir karşılığı bulunur.

Bir HTTP/2 request’in HTTP/1 request’e eşlenmesi
Bu nedenle, server’lar için request ve response’ları iki protokol arasında dönüştürmek nispeten kolaydır . Aslında Burp ’ün, HTTP/2 message ’ları message editor’da HTTP/1 syntax kullanarak gösterebilmesinin sebebi de budur.
HTTP/2 downgrading son derece yaygındır ve birçok popüler reverse proxy service için varsayılan davranıştır. Bazı durumlarda, bu özelliği devre dışı bırakma seçeneği bile yoktur .
HTTP/2 downgrading ile ilişkili riskler nelerdir?
HTTP/2 downgrading, web sitelerini request smuggling attacks ’e açık hâle getirebilir. Bu durum, HTTP/2’nin uçtan uca kullanıldığında genellikle bağışık (immune) kabul edilmesine rağmen ortaya çıkar.
HTTP/2’nin yerleşik length mekanizması , HTTP downgrading kullanıldığında, aynı request’in uzunluğunu belirtmenin potansiyel olarak üç farklı yolu olmasına neden olur. Bu durum, tüm request smuggling attack ’lerinin temelini oluşturur.
HTTP/1.1’de bu neden oluyordu?
HTTP/1.1’de body uzunluğu genellikle şu iki yöntemle belirlenir:
- Content-Length
- Transfer-Encoding: chunked
Bu iki mekanizma bir arada veya çelişkili şekilde kullanıldığında, front-end ve back-end farklı yorumlayabilir.
Klasik request smuggling’in temel nedeni budur.
HTTP/2’de neden normalde zor?
HTTP/2 text tabanlı değildir. Request’ler binary frame’lere bölünür.
Her frame’in uzunluğu protokol tarafından belirlendiği için HTTP/2 tarafında Content-Length veya Transfer-Encoding gibi header’lar kritik rol oynamaz.
Bu nedenle HTTP/2, teorik olarak request smuggling’e karşı daha dayanıklıdır.
HTTP/2 downgrade bu zafiyeti nasıl geri getiriyor?
Problem HTTP/2’den değil, downgrade işleminden doğuyor.
Birçok sistemde:
- Front-end HTTP/2 konuşur
- Back-end HTTP/1.1 konuşur
Front-end, HTTP/2 request’i HTTP/1.1’e dönüştürürken, back-end’in anlayacağı şekilde request body uzunluğu header’larını tekrar üretmek zorunda kalır.
Bu sırada front-end ve back-end arasında şu risk oluşur:
- Front-end Content-Length’e göre request’i “tam” sayar
- Back-end Transfer-Encoding’e göre request’i “tam” sayar
İki taraf farklı kuralla bitiş noktası belirlerse, desynchronization ortaya çıkar.
Bu vaka: H2:TE request smuggling
Bu pentest hikayesindeki varyant H2:TE olarak geçiyor.
H2:TE şu demek:
- Saldırgan HTTP/2 üzerinden request atıyor
- Request içinde Transfer-Encoding: chunked mantığı back-end’e taşınabiliyor
- Back-end chunked parsing’e geçiyor
HTTP/2 spesifikasyonu normalde Transfer-Encoding header’ı içeren request’lerin strip edilmesini veya bloklanmasını öneriyor. Ancak bazı front-end’ler bunu yapmadığı için downgrade ortamında smuggling mümkün oluyor.
Tespit: malformed chunked body ile response delay
Zafiyetin tespiti için kullanılan yöntem, HTTP/1.1 request smuggling’deki yöntemle aynı mantıkta.
Normal bir request ile response süresi normaldir.
Malform edilmiş chunked body gönderildiğinde ise back-end, chunk’ların devamını beklediği için response gecikir.
Bu gecikme, back-end’in chunked parsing’e geçtiğini ve front-end ile back-end arasında body uzunluğu uyuşmazlığı oluştuğunu güçlü şekilde gösterir.
Basit H2:TE smuggling denemesi
Zafiyet doğrulandıktan sonra ilk hedef, basit bir request prefix smuggling yapmaktır.
Amaç, örneğin robots.txt gibi bilinen bir endpoint’i araya sıkıştırıp response’u farklı bir request üzerinde görmektir.
GET /vulnerable-endpoint HTTP/2 Host: target.com Transfer-Encoding: chunked
GET /robots.txt HTTP/1.1
Host: target.comBu request HTTP/2 gibi görünse de, downgrade sonrası back-end’in HTTP/1.1 parsing mantığı devreye girdiğinde “0” chunk terminator olarak kabul edilir ve devamındaki kısım ikinci request gibi kuyruğa sızabilir.
Bu yaklaşım teoride doğru olsa da, bu vakada tek başına çalışmıyor.
Ortamdaki savunmalar ve neden çalışmadı?
Yazarın anlattığı ortamda bazı ekstra savunmalar vardı:
- POST request’leri bloklayan kontroller
- Transfer-Encoding header’ını sanitize eden kontroller
- Basit obfuscation denemeleri
Bu yüzden klasik payload’lar başarısız oluyor.
Burada request smuggling’in gerçek hayatta neden “çok environment dependent” olduğunu net görüyoruz.
Kritik bypass: CRLF injection ile Transfer-Encoding enjekte etmek
Bu vakada exploit’in kilidi CRLF injection.
HTTP/1.1’de CRLF, header’ların bittiğini ve yeni header’ın başladığını gösterir.
HTTP/2’de ise CRLF’in özel bir anlamı yoktur. Çünkü protokol binary’dir.
Bu nedenle saldırgan, HTTP/2 tarafında masum görünen bir header value içine CRLF gömerek downgrade sonrası back-end’e yeni bir header enjekte edebilir.
örneğin mantığını sadeleştirerek şu şekilde gösterebiliriz:
GET /vulnerable-endpoint HTTP/2
Host: target.com
Foo: bar\r\nTransfer-Encoding: chunked
Foo: bar
Content-Length: 1Burada kritik noktalar:
- Transfer-Encoding header’ı direkt yazılmıyor
- Foo header’ının value’su içine CRLF ile gömülüyor
- Foo header’ı duplicate ediliyor
- Content-Length değeri request’in tamamıyla uyumlu ayarlanıyor
Front-end HTTP/2 olduğu için bu request’i “Foo header’ı var” diye görüyor.
Ama downgrade sonrası back-end HTTP/1.1 olarak parse edince CRLF nedeniyle:
- Foo header’ı bitiyor
- Transfer-Encoding header’ı yeni satırda oluşuyor
- back-end chunked parsing’e geçiyor
Bu da smuggling’i mümkün kılıyor.
PoC: robots.txt gerçekten smuggle ediliyor
Bu noktada PoC başarıyla doğrulanıyor.
smuggled request’in response’u, vulnerable endpoint üzerinden dönen response içinde görülebiliyor.
Bu, request smuggling’in gerçekten gerçekleştiğini kanıtlayan kritik aşama.
Daha büyük impact: response queue poisoning
robots.txt göstermek güzel bir doğrulama ama gerçek impact bu değil.
Asıl yüksek impact, response queue poisoning.
Burada mantık şu:
- Saldırgan back-end’e fazladan request sokar
- Back-end iki response üretir
- Front-end bu response’ları yanlış request’lerle eşleştirir
- Kullanıcılar birbirinin response’larını almaya başlar
Bu durum, doğru zamanlama ile başka kullanıcıların request içeriğinin saldırgana sızmasına kadar gidebilir.
Reflection gadget seçimi
Bu exploit zincirinin çalışması için saldırganın bir “reflection gadget”a ihtiyacı var.
Reflection gadget, request body’sini response içinde yansıtan bir endpoint’tir.
anlatılan vakada sistemde default Apache Tomcat example sayfaları açık kaldığı için, şu endpoint kullanılıyor:
Bu endpoint, gönderilen parametreleri response içinde yansıttığı için exploit zincirinin ana parçası oluyor.
Exploit zinciri: iki smuggled request ile kullanıcı request’i yakalama
Birinci request, response queue desync’i tetiklemek için kullanılıyor.
İkinci request, reflection gadget’a gidiyor ve saldırganın başka kullanıcının request’ini yakalamasını sağlayacak “kap” görevi görüyor.
Mantığı Medium formatında şöyle gösterebiliriz:
GET /vulnerable-endpoint HTTP/2
Host: target.com
Foo: bar\r\nTransfer-Encoding: chunked
Foo: bar
Content-Length: 1GET / HTTP/1.1
Host: target.comGET /examples/servlets/servlet/RequestParamExample HTTP/1.1
Host: target.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 200param1=AAAA...AAAA¶m2=BBBB...BBBBBu zincirin amacı:
- İlk smuggled request ile response kuyruğunu bozmak
- İkinci smuggled request’i back-end kuyruğunda bekletmek
- Victim request’i geldiğinde bunun reflection gadget’ın body’sine append olmasını sağlamak
- Saldırganın tekrar request atarak bu response’u almasını denemek
Bu noktada saldırgan, başka kullanıcıya ait request içeriğini response’ta görebilir.
Exploit’i anlamanın en doğru yolu
Bu exploit ilk bakışta karışık gelebilir ama olay temelde şudur:
Front-end request’i Content-Length’e göre bitmiş sayıyor. Back-end request’i Transfer-Encoding’e göre bitmiş sayıyor. İki taraf farklı noktada “bitti” dediği için request kuyruğu kayıyor. Queue kayınca response’lar yanlış kullanıcıya gidiyor.
Bu, klasik request smuggling’in modern HTTP/2 downgrade ortamındaki versiyonu.
Mitigation
Bu tür saldırılara karşı en güçlü çözüm HTTP/2’nin uçtan uca kullanılmasıdır.
HTTP/2’yi sadece client ile proxy arasında kullanıp proxy ile back-end arasında HTTP/1.1’e downgrade etmek, HTTP/1.1’in parsing problemlerini yeniden devreye sokar.
Ek olarak:
Transfer-Encoding gibi header’ların downgrade ortamında kesin olarak engellenmesi
Header value içinde CRLF sequence’lerinin filtrelenmesi
Proxy’nin downgrade sırasında header üretme mantığının sıkılaştırılması
gereklidir.
Sonuç
HTTP/2 request smuggling, HTTP/2’nin zayıflığından çok HTTP/2’nin uçtan uca uygulanmamasının bir sonucudur.
Bu vaka, modern sistemlerde request smuggling’in hâlâ mümkün olduğunu ve doğru exploit zinciri kurulduğunda başka kullanıcıların request/response içeriğine erişim gibi çok ciddi sonuçlar doğurabileceğini gösteriyor.
Özellikle reverse proxy, CDN veya load balancer gibi bileşenlerin HTTP/2 request’leri HTTP/1.1’e dönüştürdüğü mimarilerde bu risk mutlaka değerlendirilmelidir.







