Sfruttare la vulnerabilità MD5 in questo modulo PHP?

42

Mi sono esercitato su argomenti relativi alla sicurezza e mi sono imbattuto in questo problema che non capisco affatto. Riceverai un modulo con un input di nome pass , e questo è il codice che devi bypassare:

<?php
error_reporting(0);
session_save_path('/home/mawekl/sessions/');
session_start();
echo '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>';
echo 'This task is as hard to beat as the castle which you can see on the bottom.<br>';
echo '<pre>';
include('./ascii.txt');
echo '</pre><br>';

$_SESSION['admin_level']=1;

if (!isset($_POST['pass']) || md5($_POST['pass'])!='castle')
{
    echo '<b>Wrong or empty password.</b><br>';
    $_SESSION['admin_level']=0;
}

Se entra nell'istruzione if finale, perdi (è necessario renderlo così $_SESSION['admin_level'] rimane a 1 ).

Qualsiasi aiuto è apprezzato, grazie!

Chiarimento:

Non riesco a modificare il codice che ho postato. È una sfida. Tutto quello che posso fare è inviare una password attraverso un input il cui nome è "pass". Sì, so che md5 dovrebbe restituire una stringa lunga di 32 caratteri. Questa è la sfida.

    
posta Tom 11.06.2016 - 22:32
fonte

7 risposte

59

Prova a inviare una richiesta HEAD.

Suppongo che con ascii.txt incluso, l'output dello script sia appena sopra un numero piacevole come 4096 byte, un valore output_buffering comune. Una volta che lo script ha scritto output_buffering byte, è necessario svuotare il buffer di output prima di continuare.

Normalmente questo funziona e lo script continua, ma se il tipo di richiesta è HEAD, non c'è un posto dove andare e l'esecuzione si interrompe, si spera nel mezzo del messaggio "password errata" che significa che admin_level non è mai impostato torna a 0.

Se ascii.txt è un file che puoi controllare, dovrai modificare le dimensioni in modo che i numeri si estendano per superare output_buffering mentre scrivi il messaggio "password errata".

Ecco un articolo su questa tecnica . Potrebbe non essere sempre applicabile in base alla versione / configurazione di PHP, ma l'ultimo esempio nel documento è così simile a questo problema che mi aspetterei che fosse la soluzione prevista.

    
risposta data 12.06.2016 - 07:02
fonte
27

In questo caso il difetto di sicurezza non è in MD5. E l'idea è di non ottenere if (!isset($_POST['pass']) || md5($_POST['pass'])!='castle') da valutare a favore dell'hacker. La vulnerabilità appare quando si blocca lo script. I privilegi di amministratore non sono condizionali. Indipendentemente dall'aumento dell'autorizzazione dell'utente, imposta 1 su $_SESSION['admin_level'] . PHP è procedurale. Pertanto, se si riesce a segnalare la seguente istruzione IF, l'autorizzazione non viene solo impostata ma rimane persistente. Questo è ora disponibile per il resto del programma in quanto è memorizzato nella sessione. Anche se lo script si blocca e si verifica un errore. Pertanto, se l'istruzione IF esiste solo durante un accesso, l'utente malintenzionato può visitare tutti gli altri script limitati come un livello admin impostato su 1.

Quindi cronologia degli eventi:

  • L'attaccante inserisce dati errati o un eccesso di dati nella richiesta POST
  • Script viene eseguito
  • Script fornisce un livello_mministratore di 1 che è memorizzato nella sessione
  • Script bomb in fase di valutazione
  • L'attaccante accede a un'altra pagina / script che cerca di vedere se $_SESSION['admin_level'] è 1

Questo potrebbe essere evitato se lo script ha reso la parte del valore della sessione dell'istruzione IF:

if (!isset($_POST['pass']) || md5($_POST['pass'])!='castle')
{
    echo '<b>Wrong or empty password.</b><br>';
    $_SESSION['admin_level']=0;
} else {
    $_SESSION['admin_level']=1;
}

Tuttavia c'è ancora molto altro da fare per proteggere questo codice.

    
risposta data 12.06.2016 - 03:13
fonte
17

Ho visto una sfida simile da qualche parte. Prova a inviare qualcosa come 'QNKCDZO' come input. md5('QNKCDZO') è '0e830400451993494058024219903391' (notare il prefisso 0e nell'hash che indica la notazione scientifica di un numero) e poiché il codice usa l'operatore != (invece del tipo sensitive !== ) PHP (solo versioni precedenti? in realtà lo trasmetterà su un numero prima di confrontarlo. Ovviamente, entrambi valuteranno a 0 e vincerai.

Modifica: ruakh ha ragione. Questo non funziona, perché sia l'hash che la stringa 'castle' dovrebbero apparire come un numero in PHP affinché il cast si verifichi.

    
risposta data 12.06.2016 - 02:19
fonte
9

Non ho potuto provare in PHP 5.5.9, e quello che ho intenzione di proporre non funziona sul mio PHP 5.6.22 (ma avrei potuto commettere un errore stupido).

L'unica soluzione che riesco a capire è se MD5 restituisce qualcosa che è trattato come un numero . Ho cercato su Google e ho trovato qualcosa che sembra pertinente .

    
risposta data 12.06.2016 - 01:08
fonte
4

Vedo che hai accettato la soluzione di gabedwrds (che è eccellente, comunque), ma volevo pubblicare una soluzione potenziale che ho usato in passato quando facevo qualche pratica di pentesting interessante. ( Non sono affatto un esperto in quel campo, ma a volte sono un hobbista. )

Se noterai che lo script non imposta alcuna politica per la gestione di un aborto da parte dell'utente. Di default (se non diversamente configurato nel PHP ini), se un utente interrompe la connessione mentre una pagina si sta caricando, lo script PHP termina immediatamente e interrompe l'elaborazione. Per questo motivo, se la connessione è giusta, è possibile interrompere la connessione tra $_SESSION['admin_level']=1; e la valutazione del condizionale, forzando così il server ad uscire immediatamente dallo script, senza terminare la valutazione condizionale che causerebbe admin_level da ripristinare su zero.

Ovviamente, questo dipende interamente dalla configurazione di PHP che non ha ignore_user_abort=1 . Quindi, se è impostato per continuare anche se un utente si interrompe, i privilegi verranno comunque ripristinati.

Per aumentare la tua finestra di opportunità per utilizzare questo exploit, potresti aumentare le dimensioni del tuo pass di payload / dati variabili. md5 deve eseguire l'hash in blocchi, quindi aumentando la dimensione dei dati di pass , farai in modo che la funzione md5 impieghi più tempo a calcolare l'hash, fornendo così un ampio intervallo di tempo in cui devi abortire collegamento.

    
risposta data 13.06.2016 - 23:11
fonte
2

Simile a quello che altri hanno detto, PHP ha una dimensione massima della memoria. Se inserissi i dati POST che erano abbastanza grandi da usare ALMOST tutta la memoria di PHP, allora potrebbe essere in crash quando arriva esattamente a questa riga se ha esaurito la memoria:

echo 'Password errata o vuota.'

Questo è basato su tre principi:

1) Quando le sessioni sono abilitate, PHP non scarica l'output, invece lo memorizza tutto nella RAM fino a quando il rendering della pagina è terminato.

2) PHP ha una dimensione massima della RAM allocata per ogni richiesta e, una volta raggiunto, blocca il tuo script.

3) Quando vengono scritte nuove righe, utilizza più RAM, utilizzando la RAM rimanente.

Dati i fatti precedenti, se metti più dati POST HTTP nella tua richiesta HTTP (es: $ _POST ["data"]="largestring"), potresti innescare un errore di memoria prima di $ _SESSION ["admin_level "] = 0 viene eseguito.

In uno scenario reale, questo è altamente improbabile poiché PHP ha anche una dimensione massima del corpo POST, la dimensione massima dell'intestazione e qualsiasi contenitore del server lo stia ospitando (nginx, apache, ecc.) in genere ha anche limiti massimi. Probabilmente si raggiungono tutti questi limiti massimi, ma se configurato male, è possibile ottenere $ _SESSION ["admin_level"] = 1 diventando effettivamente un amministratore quando si accede a qualsiasi altra pagina del sito Web utilizzando questo metodo.

Aggiornamento: Un altro metodo simile sarebbe quello di inserire una password grande in modo che la funzione md5 () non riesca durante l'elaborazione o qualcosa del genere, causandone nuovamente l'arresto anomalo perché esaurisce la memoria. Dipende da come md5 () funziona internamente. Probabilmente sta facendo una COPIA del campo $ _POST ["pass"], quindi se $ _POST ["pass"] era gigantesco, allora potrebbe essere molto facile far sì che questa copia sia ciò che utilizza tutta la RAM e lo fa crash .

    
risposta data 13.06.2016 - 20:10
fonte
2

Supponendo di poter modificare il file ./ascii.txt, inserisco questo testo all'interno:

<?php

override_function('md5', '$a', 'return "castle";');

E dovrebbe funzionare.

    
risposta data 14.06.2016 - 13:49
fonte

Leggi altre domande sui tag