Direct naar inhoud

Privilege escalation

CWE-269OWASP A01:2021Bijgewerkt 31 augustus 20266 min leestijd

Privilege escalation is een kwetsbaarheid waarbij een gebruiker rechten verkrijgt die hem niet toekomen, meestal die van een beheerder. Dat lukt wanneer de applicatie de rol overneemt uit het verzoek of een beheerdersroute niet apart controleert. De oplossing is de rol serverzijdig uit de sessie bepalen, bij elk verzoek opnieuw.

Bijna elke applicatie kent meerdere soorten gebruikers: een klant, een medewerker, een beheerder. Zolang de applicatie zelf vaststelt wie wat is, gaat dat goed. Laat zij die beslissing over aan het binnenkomende verzoek, of schermt zij de beheerderskant niet apart af, dan promoveert een gewone gebruiker zichzelf. Hieronder leest u hoe privilege escalation ontstaat en wat u eraan doet.

Wat is privilege escalation?

Privilege escalation is een kwetsbaarheid waarbij een gebruiker rechten verkrijgt die hem niet zijn toegekend, doordat de applicatie zijn rol uit een onbetrouwbare bron overneemt of een bevoorrechte functie niet apart controleert. Onder testers heet dit kortweg privesc, een afkorting van privilege escalation.

Een alledaagse vergelijking: een bezoekerspas waarop u zelf uw functie mag invullen. Bij de deur van de directiekamer kijkt de bewaker uitsluitend naar die pas en niet naar het personeelssysteem. Wie er “directeur” op schrijft, loopt naar binnen.

Vakmensen onderscheiden twee richtingen. Bij horizontale escalatie bereikt een gebruiker de gegevens van een gelijke, wat neerkomt op een IDOR. Bij verticale escalatie, het onderwerp van dit artikel, klimt hij een niveau omhoog, meestal naar beheerder. In de OWASP Top 10 vallen beide onder A01:2021, Broken Access Control; MITRE registreert de onderliggende fout als CWE-269, Improper Privilege Management.

Twee oorzaken verklaren het merendeel van de bevindingen: een rol die de applicatie klakkeloos overneemt uit het verzoek, en een beheerdersroute die wel verborgen is maar niet beschermd.

Hoe werkt een privilege-escalation-aanval?

Een aanvaller heeft niets meer nodig dan een eigen, legitiem account. Ziet hij in een antwoord een veld als role, isAdmin of permissions terugkomen, dan stuurt hij dat veld mee in zijn volgende verzoek.

Kwetsbaar:

// PATCH /api/profile: de gebruiker werkt zijn eigen profiel bij
app.patch("/api/profile", requireLogin, async (req, res) => {
  // Het volledige verzoeklichaam gaat ongefilterd naar de database
  await db.users.update({ id: req.session.userId }, req.body);
  res.json({ ok: true });
});

// GET /admin/users: het beheerdersoverzicht
app.get("/admin/users", requireLogin, async (req, res) => {
  // Ingelogd zijn is genoeg; de rol wordt nergens gecontroleerd
  res.json(await db.users.findAll());
});

De eerste route schrijft alles wat binnenkomt naar het gebruikersrecord: de ontwikkelaar dacht aan een naam en een telefoonnummer, maar ook het rolveld wordt geaccepteerd. De aanvaller stuurt dit:

PATCH /api/profile HTTP/1.1
Host: app.example.com
Content-Type: application/json
Cookie: session=8f2a4c1b

{"displayName":"Mark","role":"admin"}

Het antwoord is een gewone 200. Bij de volgende aanvraag leest de applicatie de rol beheerder uit haar eigen database, want daar staat hij nu. Deze variant heet mass assignment: een raamwerk dat velden automatisch aan een object koppelt, koppelt ook wat de gebruiker nooit had mogen zetten.

De tweede route toont het andere patroon: zij controleert of iemand is ingelogd, maar niet wie hij is. Het pad staat niet in het menu van een gewone gebruiker en juist daarop vertrouwt de ontwikkelaar. Een aanvaller vindt zulke paden in de JavaScript-bundel, waarin de beheerdersschermen vaak worden meegeleverd, of met een lijst gangbare namen. Dat heet forced browsing.

De veilige variant draait beide om: de schrijfkant accepteert alleen wat een gebruiker over zichzelf mag zetten, de leeskant bepaalt de rol serverzijdig.

Veilig:

const PROFIELVELDEN = ["displayName", "locale", "phone"];

app.patch("/api/profile", requireLogin, async (req, res) => {
  // Allowlist: alleen deze velden bereiken de database, role staat er niet bij
  const patch = {};
  for (const veld of PROFIELVELDEN) {
    if (veld in req.body) patch[veld] = req.body[veld];
  }
  await db.users.update({ id: req.session.userId }, patch);
  res.json({ ok: true });
});

// De rol komt uit de opslag, op basis van de sessie, bij elk verzoek opnieuw
function requireRole(rol) {
  return async (req, res, next) => {
    const user = await db.users.findById(req.session.userId);
    if (!user || !user.roles.includes(rol)) return res.status(404).end();
    next();
  };
}

// Deny by default op de hele beheerdersboom, niet per losse route
app.use("/admin", requireLogin, requireRole("admin"));

app.get("/admin/users", async (req, res) => {
  res.json(await db.users.findAll());
});

De allowlist maakt het onmogelijk dat een onbekend veld de database bereikt. De rolcontrole haalt de gebruiker bij elk verzoek uit de opslag en kijkt naar de rollen die daar staan, niet naar iets wat de client heeft meegestuurd. Doordat die controle aan de hele beheerdersboom hangt, is een nieuwe route automatisch beschermd. Het antwoord 404 in plaats van 403 verraadt bovendien niet welke beheerderspaden bestaan.

Legt u de rol bij het inloggen vast in de sessie of in een JWT, dan blijft een ingetrokken beheerdersrol geldig tot dat token verloopt: wie u zojuist heeft gedegradeerd, houdt zijn rechten. Haal de rol daarom per verzoek op, of houd de levensduur kort en zorg dat u tokens actief kunt intrekken.

Wat is de impact van privilege escalation?

Verticale escalatie geeft een aanvaller de sleutels van de applicatie. Wat een beheerder mag, mag hij: alle klantgegevens inzien en exporteren, accounts aanmaken en verwijderen, facturen aanpassen. Vaak maakt hij meteen een tweede beheerdersaccount aan, zodat hij binnen blijft nadat het gat is gedicht.

Het blijft zelden bij de applicatie. Beheerdersschermen bieden functies die dieper gaan: een bestandsupload, een template-editor, een instelling die een systeemcommando aanroept. Die functionaliteit is minder streng getest, juist omdat alleen vertrouwde medewerkers erbij zouden komen, en vormt zo een opstap naar remote code execution.

Zakelijk gaat het om een datalek met meldplicht, om fraude wanneer betalingen te wijzigen zijn, en om een audittrail die zijn waarde verliest, omdat handelingen op naam staan van een ogenschijnlijk gewone gebruiker. De classificatie loopt van hoog tot kritiek, afhankelijk van hoe ver de overgenomen rol reikt. Een gebruiker die moderator van zijn eigen team wordt, is vervelend; dezelfde fout in een omgeving waar de beheerdersrol over alle klanten gaat, is een incident van de zwaarste categorie.

Hoe spoort u privilege escalation op?

Begin bij de velden die de applicatie over een gebruiker teruggeeft: vraag uw eigen profiel op en let op alles wat naar rechten ruikt, zoals role, isAdmin of permissions. Stuur die velden vervolgens mee in een update, ook als het formulier ze niet toont, en kijk of ze blijven staan. Doe hetzelfde bij de registratie, die vaak een compleet object accepteert.

Test daarna de routes zelf. Neem het verkeer van een beheerdersaccount op, herhaal elk verzoek met de sessie van een gewone gebruiker en noteer wat anders dan met 401 of 403 wordt beantwoord; Burp Suite met de extensie Autorize doet dit systematisch. Zoek beheerderspaden in de JavaScript-bundel en in oude API-versies, en wissel ook van methode: soms is de GET afgeschermd en de POST op hetzelfde pad niet.

Scanners vinden een beheerderspagina die zonder inloggen bereikbaar is, maar zij kennen uw rollenmodel niet en zien dus niet dat gebruiker B mag wat alleen beheerder A mocht. Dat blijft handwerk. AssistSec neemt deze controle mee in een penetratietest en test daarbij standaard met accounts op verschillende rechtenniveaus.

Hoe voorkomt u privilege escalation?

  • Bepaal de rol serverzijdig uit de sessie. Lees hem bij elk verzoek uit uw eigen opslag en accepteer nooit een rol of rechtenlijst uit het verzoek.
  • Werk met een allowlist van schrijfbare velden. Bind een verzoeklichaam nooit rechtstreeks aan uw datamodel, maar benoem welke velden een gebruiker over zichzelf mag zetten.
  • Weiger standaard. Hang de autorisatie aan de hele beheerdersboom in plaats van aan losse routes, zodat een nieuwe endpoint beschermd is voordat iemand eraan denkt.
  • Controleer per handeling, niet per scherm. Een knop verbergen is geen autorisatie; elke aanroep erachter heeft zijn eigen controle nodig.
  • Vraag om herbevestiging bij zware handelingen. Het toekennen van een rol of het wijzigen van een e-mailadres verdient een extra authenticatiestap.
  • Houd rechten actueel en tokens kort. Zorg dat een ingetrokken rol direct effect heeft en dat u sessies en tokens actief kunt intrekken.
  • Log rolwijzigingen en test het scenario. Een gebruiker die zichzelf beheerder maakt, hoort een alarm op te leveren; een integratietest die met account B de beheerdersroutes opvraagt, houdt de fout weg.

Bronnen

Veelgestelde vragen

Wat is het verschil tussen horizontale en verticale privilege escalation?

Bij horizontale escalatie bereikt een gebruiker de gegevens van een gelijkwaardige gebruiker; dat is in de kern een IDOR. Bij verticale escalatie klimt hij een niveau omhoog, meestal naar beheerder. Verticale escalatie weegt doorgaans zwaarder, omdat beheerdersfuncties over alle gebruikers gaan en niet over één account. Beide vallen in de OWASP Top 10 onder A01:2021, Broken Access Control.

Is privilege escalation hetzelfde als broken access control?

Niet helemaal. Broken access control is de overkoepelende categorie uit de OWASP Top 10; privilege escalation is de uitkomst waarbij een gebruiker een hoger rechtenniveau bereikt. IDOR en mass assignment zijn andere verschijningsvormen van dezelfde categorie.

Beschermt een JWT tegen privilege escalation?

Alleen als u hem correct verifieert. Een ondertekend token voorkomt dat de client de rol in de payload aanpast, maar het helpt niet wanneer uw server het algoritme none accepteert of de handtekening niet controleert. Bovendien blijft een rol in een token geldig tot het verloopt, dus een ingetrokken beheerdersrol werkt zonder revocatie gewoon door.

Waarom is een verborgen beheerderspagina geen beveiliging?

Omdat het pad vindbaar is. Beheerdersroutes staan vaak gewoon in de JavaScript-bundel die iedere bezoeker binnenhaalt, en anders vindt een aanvaller ze met een lijst gangbare namen. Alleen een controle aan de serverkant, bij elk verzoek, houdt hem tegen.

Verwante artikelen

Druk op / om te zoeken · Esc