Bu blog yazımda Hack The Box'ın, File Upload Attacks — Skills Assesment labını ele alacağım. Verilen lab bilgisine göre erken geliştirme aşamalarında olan bir e-ticaret sitesi üzerinde sızma testi yapıyoruz.

None

Sitede çalışan tek şey Contact Us sayfası ve bir feedback formu. Client side validation'lara bakmak için dev console'dan front end kodlarına bakıyorum. Sadece .jpg .jpeg ve .png dosyaları kabul ediliyor.

None

Burp Suite'ten gelen response'ta hedef sunucu Apache çalıştırıyor. Apache'nin varsayılan PHP konfigürasyonunda geçtiği üzere, .phar, .php ve .phtml uzantıları sunucuda PHP olarak çalıştırılır.

None

Bu bilgiden yola çıkarak ve client side validation'ı da göz önünde bulundurarak dosya uzantısını .phar.php olarak değiştirince sunucu dosyayı kabul ediyor. Kabul ediyor fakat yüklenen dosyayı sunucu ne yapıyor?

Bunu öğrenebilmek için bir şekilde kaynak kodları okuyabilmek gerekiyor. Tam bu noktada aklıma XXE zafiyeti geliyor, eğer sunucuya .svg dosyası yükleyebilirsek XXE payload'larıyla kaynak kodlara erişebiliriz.

None

Content-Type Header'ını test ettiğimde image/svg+xml'i kabul ediyor ve .svg.jpg uzantısıyla /etc/passwd'yi okuyan bir XXE payload'u gönderdiğimde çalışıyor. Web root'u bilmediğimiz için bu payload'la devam edilemez.

None

PHP ile index.php'yi base64 olarak alıyorum. Gelen base64 string'ini kopyalayıp terminalde decode ettiğimde ise kaynak kod ile bakışıyoruz.

None
index.php
None
submit.php
None
script.js

Uygulamanın kaynak kodları matruşka gibi açılıyor. Bütün dosyaları aynı şekilde alıp okuyorum. Ve son olarak upload.php dosyasını buluyoruz.

<?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";
}

Dananın kuyruğunun koptuğu yer ise burası, upload.php. Yüklenen dosyaya ne yapıldığının cevabı tam olarak burda. Dosyanın nereye gittiği, nasıl adlandırılığı ve çeşitli validation'ları içeriyor. Blacklist'e baktığımda, .phar uzantısı engellenmemiş. Bu durumda leblebi4.phar.jpg olarak yüklediğimiz dosya, başına "251018_" şeklinde yıl/gün/ay eklenerek, /contact/user_feedback_submissions/ dizinine koyuluyor. Burdan sonrası ise .phar.jpg uzantısıyla basit bir web shell yükleyip code execution'a gitmek. Örnek olarak: system($_REQUEST['cmd']);

None

Ve son olarak leblebi4.jpg :)

None