Az Apache SF nem rég kiadta a Struts keretrendszer legfrissebb, 2.3.1-es változatát, amely több kritikus, akár kódfuttatásra alkalmas hibát orvosol. Ezekről részletes leírást adott ki a SEC Consult illetve szintén értékes információkhoz juthatunk velük kapcsolatban Johannes Dahse blogjáról. Nézzük hát, mik voltak a problémák, és hogyan lehetett őket kihasználni!
A Strust keretrendszer az OpenSymphony XWork és OGNL könyvtárait használja. Az XWork-ről őszintén szólva semmi épeszű dokumentációt nem találtam, de nekünk most elég, ha annyit tudunk róla, hogy ez adja a Struts gerincét és néhány biztonsági beállítást ezen keresztül kell eszközölni. Az OGNL egy egyszerű, Java kódra leképződő nyelv, melyet a HTTP paraméterek értelmezéséhez használnak. A "product.id=1" OGNL kifejezés például a getProduct().setId(1) Java utasítássá alakul, de alapjáraton tetszőleges metódus, konstruktor és adattag hozzáférhető segítségével.
Alapjáraton, ugyanis a Struts megtiltja a statikus és nem statikus metódokhoz történő hozzáférést, emellett pedig egy fehérlistát is alkalmaz a paraméternevek szűréséhez:
acceptedParamNames = "[a-zA-Z0-9\\.\\]\\[\\(\\)_'\\s]+";
Az első probléma ott jelentkezik, hogy utóbbi fehérlistát egyszerűen nem alkalmazzák a Struts CookieInterceptor osztályában, ezért a kódfuttatás nagyjából triviálisan elérhető, ha engedélyezett a sütik nevének feldolgozása. Először engedélyeznünk kell a statikus metódusokhoz történő hozzáférést az XWork allowStaticMethodAccess tulajdonságán keresztül, majd meghívhatjuk a szokásos exec()-t:
Cookie: (#_memberAccess["allowStaticMethodAccess"]\u003dtrue)(x)=1; \ x[@java.lang.Runtime@getRuntime().exec('calc')]=1
Ez eddig laza volt, de lássuk, mi a helyzet akkor, ha a süti nevek értelmezése nincs beállítva! Ilyenkor meg kell küzdeni a fehérlistával, hogy URL-ben is tudjunk kung-fuzni. Mint kiderült, ez sem teljesen lehetetlen, mivel megadott karakterkészlettel setter metódusok hívhatók:
name=foo
x[name('foo')]=1
sőt, konstruktorok is, melyeknek egyetlen String paraméterük van:
x[new java.lang.String('foo')]
Innen már csak egy lépés, hogy tetszőleges fájlt létrehozzunk (vagy kiüssük a tartalmát). Az egyetlen baj, hogy a könyvtárstruktúrában nem tudunk mozogni a karakterkészlet megkötései miatt. Ezek a megkötések azonban csak a paraméternevekre és nem az értékekre vonatkoznak :)
x[new java.io.FileWriter(message)]=1&message=C:/test.txt
Ha esetleg valaki nem látná meg a lehetőséget egy egyszerű fájllétrehozásban: ilyen módon remekül gajra lehet vágni a rendelkezésreállást (kinullázod a servleteket vagy a konfigokat), kis szerencsével az integritás is megborítható, például ha fontos üzleti adatok leledzenek az alkalmazásszerver által hozzáférhető fájlokban (meg persze a naplók törlése sem mindig haszontalan), de a csillagok kedvező együttállása esetén akár kódfuttatás is elérhető (2. oldal, 1. hiba - ez itt a reklám helye :).
Johannes szerint a fenti módszerrel elméletileg akár írni is lehet a fájlokba, de megoldást nem közöl, a probléma továbbgondolása tehát ránk marad. Emellett érdemes lehet annak is utánajárni egy kicsit, hogy milyen elterjedt projektek használják még ezt a remek OGNL könyvtárat - az Apache kódjait nem amatőrök fejlesztik, máshol ennél csak gyengébb korlátozásokra számíthatunk!
A SEC Consult jelentése a fentieken túl még két hibára tér ki: Az egyik a Struts régebbi változatait érinti, és abból adódik, hogy a paraméterfeldolgozás közben keletkező kivételek kezelésekor a paraméter értékek is OGNL kifejezésként értelmeződnek, melyekre az itt tárgyalt korlátozások egyálalánnem vonatkoznak. A másik probléma debug módban hagyott alkalmazásokat érint, ilyen esetben a debug paraméteren keresztül elérhető a DebuggingInterceptor, mellyel ugyanazt lehet eljátszani, mint a CookieInterceptorral.