A Mongoose egy Object Data Modeling (ODM) könyvtár a MongoDB-hez, amely leegyszerűsíti az adatbázis interakciókat a Node.js alkalmazásokban. A Mongoose egy sémaalapú megoldás biztosításával lehetővé teszi a JavaScript objektumok leképezését MongoDB dokumentumokra, egy absztrakciós rétegként működve, amely segít strukturálni az adatokat a könnyebb kezelés és érvényesítés érdekében. A Mongoose olyan funkciókkal, mint az egyéni logika végrehajtásához szükséges middleware és egy intuitív lekérdezés-építő rendszer, növeli a MongoDB-vel való munka hatékonyságát. A Mongoose, amelyet "elegáns MongoDB objektummodellezés Node.js számára" néven írnak le, 27K csillagot gyűjtött a GitHubon, ami tükrözi a széles körű használatát és elismerését a fejlesztők körében.
OPSWAT ösztöndíjprogram és kritikus sebezhetőségek felfedezése
Az OPSWAT Critical Infrastructure Cybersecurity Graduate Fellowship Program, amelynek székhelye Vietnamban van, a végzős hallgatóknak gyakorlati tapasztalatot nyújt a kritikus infrastruktúrák biztonságának biztosításában. A program részeként az ösztöndíjasok lehetőséget kapnak a kiberbiztonsági sebezhetőségek elemzésére és kezelésére, az OPSWAT szakértőivel együttműködve, hogy valós kihívásokat oldjanak meg olyan területeken, mint a rosszindulatú programok felderítése, a fájlbiztonság és a fenyegetések megelőzése.
Az OPSWAT ösztöndíjprogram során a résztvevők szisztematikusan vizsgálják és reprodukálják az ismert CVE-ket különböző termékekben, könyvtárakban és operációs rendszerekben. E kezdeményezés részeként Dat Phung - egyik kiváló ösztöndíjasunk - a Mongoose vizsgálatát választotta, mivel az széles körben elterjedt a termelési környezetekben. 2024 novemberében a könyvtár alapos elemzése során felfedezett egy kritikus sebezhetőséget a Mongoose-ban. A sebezhetőség lehetővé tette a támadó számára, hogy kihasználja a $where értéket, ami potenciálisan távoli kódvégrehajtáshoz (RCE) vezethet a Node.js alkalmazáskiszolgálón. A Mongoose azonnali bejelentését követően a 8.8.3-as verzió részeként javítócsomagot adtak ki, és a CVE-2024-53900 azonosítót közzétették a National Vulnerability Database (NVD) adatbázisban.
CVE-2024-53900 és CVE-2025-23061 Idővonal
- 2024. november 7: Dat Phung kritikus sebezhetőséget azonosított a Mongoose-ban, és biztonsági jelentést küldött a Snyknek.
- 2024. november 26: A Mongoose kiadta a 8.8.3-as verziót a sebezhetőség kijavítására.
- 2024. december 2: A National Vulnerability Database (NVD) közzétette a CVE-2024-53900 azonosítót erre a sebezhetőségre vonatkozóan.
- 2024. december 17: Dat Phung a Mongoose 8.8.3 javítás elemzése során egy olyan megkerülést talált, amely továbbra is lehetővé tette az RCE (Remote Code Execution) végrehajtását. Részletes biztonsági jelentést küldött a Tideliftnek.
- 2025. január 13: A Mongoose kiadta a 8.9.5-ös verziót, amely egy továbbfejlesztett javítást vezetett be, amely hatékonyan kezelte a megkerülést.
- 2025. január 15: A National Vulnerability Database (NVD) hivatalosan közzétette a CVE-2025-23061-et, hangsúlyozva az újonnan azonosított sebezhetőség súlyosságát.
Mongoose's Populate() metódusa
A Mongoose egy hasznos funkciót is biztosít, a populate() funkciót, amely javítja a dokumentumok közötti kapcsolatokkal való munkát. Míg a MongoDB ≥ 3.2-es verziókban a $lookup aggregációs operátor a joinokhoz rendelkezésre áll, a Mongoose populate() egy sokkal hatékonyabb alternatívát kínál a hivatkozás automatikus helyettesítésére a kapcsolódó dokumentumok megfelelő adataival. Ez különösen hasznos a különböző MongoDB gyűjtemények közötti kapcsolatok kezelésében, például amikor az egyik dokumentum a _id-je alapján hivatkozik egy másikra. [2]
A Mongoose séma definiálásakor egy mezőt úgy lehet beállítani, hogy egy másik modellre hivatkozzon a ref opcióval. A populate() metódus ezután arra szolgál, hogy a hivatkozott mezőt (egy ObjectId) a kapcsolódó modell teljes dokumentumával helyettesítse. Például egy könyvesbolt alkalmazásban a bookSchema szerző mezője az Author dokumentumra, a review mező pedig a Reviews dokumentumra hivatkozik. A populate() metódus lehetővé teszi a fejlesztők számára, hogy a könyvmodell lekérdezésekor az author mezőt (amely egy ObjectId) a teljes Author dokumentummal helyettesítsék.
A populate() lehetővé teszi a fejlesztők számára, hogy a szerző mezőt (amely egy ObjectId) a teljes Szerző dokumentummal helyettesítsék a könyvmodell lekérdezésekor:
Továbbá a Mongoose populate() metódusa támogatja az egyéni lekérdezéseket annak meghatározására, hogy mely kapcsolódó dokumentumok kerülnek lekérdezésre és hogyan. Az olyan tulajdonságok, mint a match és az options lehetővé teszik a fejlesztők számára, hogy szűrjék, rendezzék, korlátozzák és kihagyják a kapcsolódó dokumentumokat, rugalmas adatlekérdezési lehetőségeket kínálva.
CVE-2024-53900 Elemzés
Az OPSWAT Cybersecurity Graduate Fellowship program részeként, miközben a Mongoose-t elemezte az ismert CVE-k reprodukálása érdekében, Dat Phung átfogó felülvizsgálatot végzett a populate() metódus belső működéséről, amely kulcsszerepet játszik a MongoDB dokumentumok közötti kapcsolatok kezelésében. A populate() metódus támogatja mind a string, mind az objektum argumentumokat, és a fejlesztők a match opcióval speciális szűrőket alkalmazhatnak a lekérdezett adatokra:
A fenti példában a match opció egy szűrőobjektum, amely tartalmazhat MongoDB lekérdezési operátorokat, ahogyan azt a Query and Projection Operators - MongoDB Manual v8.0 című kézikönyv részletezi. Az egyik figyelemre méltó operátor a $where, amely lehetővé teszi a JavaScript futtatását közvetlenül a MongoDB szerveren. Ez a végrehajtás a MongoDB szerveren azonban korlátozott, csak az alapvető műveleteket és függvényeket támogatja.
Dat Phung mélyrehatóan elemezte a Mongoose forráskódját, hogy megértse a populate() metódus munkafolyamatát. Megállapította, hogy miután az alkalmazás meghívja a modellen a populate() metódust, a populate() függvény elindul. Ezen a függvényen belül a Mongoose meghívja a _execPopulateQuery() függvényt, amely a $where operátorral végrehajtja a lekérdezést a MongoDB szerveren. Ezt követően az idegen gyűjteményből az összes dokumentumot lekérdezi a következő lépésekben történő betelepítéshez.
Az adatok MongoDB-ből történő lekérdezése után a Mongoose végrehajtja a _done() callback függvényt, amely az adatok előkészítése érdekében meghívja az _assign() függ vényt, mielőtt a két modellt az assignVals() függvény meghívásával "összekapcsolja".
A sebezhetőség akkor merülhet fel, amikor a lekérdezett adatokat a Mongoose assignVals() függvénye dolgozza fel. Ez a függvény ellenőrzi, hogy a match opció egy tömb-e, és ha igen, akkor minden egyes operátort átad a sift() függvénynek. A sift() függvény, amely egy azonos nevű külső könyvtárból van importálva, ezeket a lekérdezéseket lokálisan, az alkalmazáskiszolgálón dolgozza fel. Ez a helyi feldolgozás biztonsági kockázatot jelent, különösen a felhasználó által vezérelt bemenet kezelése esetén.
Ennek további vizsgálatához Dat Phung módosította a match opció értékeit, hogy biztosítsa a feltételek teljesülését, és ezáltal meghívta a sift() függvényt az adatáramlás további elemzéséhez.
A feltétel megadása után a $where operátor átadásra került a sift() függvénynek.
A sift könyvtár egy könnyű JavaScript segédprogram, amely adatgyűjtemények, például tömbök vagy JSON objektumok szűrésére és lekérdezésére szolgál MongoDB-szerű szintaxis használatával. A hivatalos dokumentáció szerint "a Sift egy apró könyvtár a MongoDB-lekérdezések JavaScriptben történő használatához". A sift() függvény a MongoDB-szerű szűrési műveleteket az alkalmazáskiszolgálón értékeli ki az adatbázis-kiszolgáló helyett, ami jelentős biztonsági kockázatoknak teheti ki a rendszert, amikor nem megbízható bemenetet dolgoz fel.
Folytatva az elemzést, a munkatársunk egy problémát azonosított a sift könyvtár createDefaultQueryTester() függvényében. Ez a függvény a match tömb minden egyes műveletét futtatható JavaScript függvényekké alakítja át, amelyeket aztán a MongoDB dokumentumadatok helyi szűrésére és feldolgozására használunk. Ennek érdekében a createDefaultQueryTester() a createNamedOperation() függvényt hívja meg, és argumentumként átadja a match tömbben szereplő műveleteket, például a $where-t.
A createNamedOperation a match tömb minden egyes művelete esetében ellenőrzi, hogy a művelet támogatott-e, majd átadja a megfelelő függvénynek.
Ha a művelet $where, akkor egy JavaScript függvény generálódik a nyers "params" érték felhasználásával, amely a $where operátorból származik a match tömbben, és a felhasználó által vezérelhető.
CVE-2024-53900: Exploitation Details
Míg a MongoDB korlátozza a JavaScript függvények végrehajtását a $where művelet segítségével, ahogyan azt korábban elemeztük, a sift() függvény lehetővé teszi, hogy ezeket a függvényeket ilyen korlátozások nélkül lehessen végrehajtani. A bemeneti érvényesítés és korlátozás hiánya jelentős biztonsági sebezhetőséget vezet be, mivel a "params" érték - amelyet közvetlenül a felhasználói bemenet vezérel - kihasználható, ami potenciálisan kódinjekciós támadásokhoz vezethet. E probléma alaposabb vizsgálatához Dat Phung a következő lekérdezést állította össze:
Kezdetben a lekérdezés egy másik folyamat végrehajtása nem sikerült, ami a következő hibát eredményezte:
Ez a hiba azt jelzi, hogy a Mongoose megpróbálja végrehajtani a $where műveletet a MongoDB szerveren, mielőtt átadná a vezérlést a sift() függvénynek. A MongoDB $where záradékában a JavaScript függvényekre vonatkozó korlátozások miatt azonban hiba lép fel, ami megakadályozza a lekérdezés végrehajtását. Ennek eredményeképpen a Mongoose megállítja a folyamatot, mielőtt az elérné a sift() függvényt.
A korlátozás megkerülése érdekében a mi Fellow-nk az alkalmazáskiszolgálón lévő "globális" változót használta, amely nem létezik a MongoDB szerveren. Ez a megközelítés lehetővé tette számára, hogy megkerülje a MongoDB szerveren lévő korlátozást, és a lekérdezés elérje a sift() függvényt:
Ezzel az értékkel, amikor a Mongoose végrehajtja a $where műveletet a MongoDB-n, a "global" változó hiánya a terner operátor (typeof global != "undefined" ?global.process.mainModule.constructor._load("child_process").exec("calc") : 1) 1-et ad vissza, megakadályozva ezzel, hogy a MongoDB hibát dobjon. Következésképpen a lekérdezés problémamentesen végrehajtódik a MongoDB szerveren.
Amikor azonban ugyanez az érték eléri a sift() függvényt, amely azon az alkalmazáskiszolgálón fut, ahol a "globális" változó elérhető, a következő függvény létrehozását indítja el:
Távoli kódvégrehajtás (RCE) Koncepcióteszt
A blog elején bemutatott alkalmazási példában, ha egy támadó a következő kérést küldené, sikeresen végre tudna hajtani egy távoli kódvégrehajtási (RCE) támadást:
A videó bemutatja a CVE-2024-53900 koncepciót, amely a 8.8.3 előtti Mongoose verziókat érinti, és amelyből hiányzik a megfelelő bemeneti érvényesítés a $where operátorral való visszaélés megakadályozására a sift könyvtár mellett.
Hiányos javítás és CVE-2025-23061
Dat Phung biztonsági jelentése alapján a Mongoose bevezetett egy javítást, amelynek célja a korábban azonosított sebezhetőség (CVE-2024-53900) megoldása a nyilvánosságra hozatal előtt. A vonatkozó javítás(Automattic/mongoose@33679bc) egy olyan ellenőrzést adott hozzá, amely tiltja a $where használatát a populate() parancsnak átadott match tulajdonságon belül.
Ez a részlet ellenőrzi, hogy a populate() függvénybe átadott match tulajdonság egy tömb-e. Ha igen, a kód végigmegy a tömb minden egyes objektumán, hogy megnézze, tartalmazza-e a $where operátort. Ha $where-t észlel, hibaüzenet jelenik meg, megakadályozva a rosszindulatú hasznos teher továbbterjedését a kockázatos sift() függvénybe.
Ennek eredményeképpen a CVE-2024-53900 kihasználó hasznos teher nem teljesíti ezt az ellenőrzést, mivel a match tömb egyik objektuma $where-t tartalmaz, ami megakadályozza, hogy elérje a sift() funkciót.
Míg ez a frissítés helyesen blokkolja a $where közvetlen használatát egyetlen beágyazási szinten belül, nem észleli a $where-t , amikor az $or operátorba van ágyazva - ezt a struktúrát a MongoDB és a sift könyvtár is teljes mértékben támogatja.
Ennek eredményeképpen a támadó $where-t $or alá fészkelheti, hogy kikerülje a javítás egyszintű ellenőrzését. Mivel a Mongoose csak az egyes objektumok legfelső szintű tulajdonságait vizsgálja a match tömbben, a megkerülő hasznos teher észrevétlen marad, és végül eléri a szitáló könyvtárat, lehetővé téve a rosszindulatú RCE-t.
A CVE-2025-23061 koncepciójának igazolása
A javítás hiányos jellegének illusztrálására Dat Phung újraépítette a példalkalmazást a Mongoose 8.9.4-es verziójával (a 8.8.3-asnál újabb). A $whereegy $or záradékon belüli beágyazásával a támadó sikeresen megkerülheti az ellenőrzést és elérheti a RCE-t.
A proof-of-concept exploit bemutatja, hogy a 8.9.5 előtti Mongoose verziókban hogyan lehet a CVE-2025-23061-et kiváltani, lehetővé téve a támadó számára tetszőleges kód futtatását a kiszolgálón:
Enyhítés és iránymutatás
A fent említett sebezhetőségek csökkentése érdekében kérjük, győződjön meg róla, hogy rendszere a Mongoose legújabb verziójára van frissítve.
A MetaDefender Core az SBOM motor segítségével képes észlelni ezt a sebezhetőséget
OPSWAT MetaDefender Core, amely fejlett SBOMSoftware Bill of Materials) képességekkel van felszerelve, lehetővé teszi a szervezetek számára, hogy proaktív módon kezeljék a biztonsági kockázatokat. A MetaDefender Core a szoftveralkalmazások és függőségeik átvizsgálásával azonosítja az ismert sebezhetőségeket, például a CVE-2024-53900 és a CVE-2025-23061 azonosító kódokat a felsorolt összetevőkön belül. Ez lehetővé teszi a fejlesztői és biztonsági csapatok számára, hogy prioritásként kezeljék a javítási erőfeszítéseket, csökkentve a potenciális biztonsági kockázatokat, mielőtt azokat a rosszindulatú szereplők kihasználhatnák.
Az alábbiakban a CVE-2024-53900 és a CVE-2025-23061 képernyőképét láthatjuk, amelyeket a MetaDefender Core az SBOM segítségével észlelt:
Ezen túlmenően a CVE-ket a következő eszközökkel is fel lehet fedezni MetaDefender Software Supply Chain, amely a MetaDefender Core és az SBOM segítségével azonosítja ezeket a sebezhetőségeket.