Веб-безопасность/Уязвимости FileUpload

Материал из SecSem Wiki
Версия от 13:20, 7 октября 2021; Romankholin94 (обсуждение | вклад) (Подключение файлов)
Перейти к навигации Перейти к поиску

Введение

Загрузка файлов пользователями - довольно частая часть функциональности веб-приложения (аватарки, хостинг файлов, посылка вложений вместе с сообщениями и т. д.). Небезопасная реализация этого механизма может приводить к очень серьезным уязвимостям. Загрузка файлов необходима многим сервисам, таким как файлообменники, социальные сети, онлайн-конверторы и редакторы.

Самый простой способ реализации механизма загрузки файлов - просто создавать на файловой системе сервера файл с таким же содержимым как файл, отправленный пользователем (проще говоря, "класть" присланный пользователем файл куда-то на файловую систему сервера). После загрузки файл либо доступен пользователю по прямой ссылке (т. е. определенные URL-адреса отображаются на загруженные файлы и существует URL, обращение к которому ведет к отдаче загруженного файла), либо как то еще обрабатывается веб-приложением. Само веб-приложение во многом состоит из файлов на диске, поэтому добавление файлов на сервер может быть небезопасным, так как (в общем случае) может изменить логику работы приложения. Соответственно, могут появляться следующие уязвимости:

  • Remote Code Execution
  • перезапись или удаление файлов
  • Local File Read, SSRF
  • Denial of Service
  • Information Disclosure
  • XSS

PHP

PHP (PHP: Hypertext Preprocessor) — скриптовый язык общего назначения, интенсивно применяемый для разработки веб-приложений.

Привет, мир!

Простейшая программа, выводящая надпись "Привет, мир!":

<?php print("Привет, мир!");

Каждый скрипт должен начинаться с <?php и желательно (но не всегда обязательно) на  ?> . Пример кода, где  ?> обязателен:

<html>
  <head>
    <title>Наша первая php страница</title>
  </head>
  <body>
    <h1><?php print("Привет, мир"); ?></h1>
  </body>
</html>

Переменные

В синтаксисе PHP имя переменной записывается латинскими символами, но первым символом всегда должен быть знак доллара $, после чего идёт имя. Не допускается начинать имя переменной с цифры, а также использовать любые значения, кроме букв алфавита и знака подчеркивания.

Правильные переменные:

$age
$favorite_color
$name2

Неправильные переменные:

age
$42
$my-age

Переменные бывают нескольких типов:

$favorite_color = "green"; // строка
$favorite_number = 42; // целое число
$favorite_float_number = 0.5; // число с плавающей точкой
$favorite_bool = true; // boolean

Числа можно складывать, вычитать, делить, умножать опреаторы стандартные, как и во всех языках: +, -, *, /, % - плюсь, минус, умножить, делить (деление не целочисленное), остаток от деления.

Конкатенация

Конкатенация - "cклейка" нескольких строчек в одну.

<?php
$united_string = "Мой любимый цвет - " . $favorite_color . ", а любимое число - " . $favorite_number;
print($united_string);

В коде выше $favorite_number - число. Поэтому данная переменная приводится к типу данных "строка", после чего уже происходит конкатенация.

Ввод данных

Данная функциональность редко используется, но нам это нужно было для решения задач на лекии. readline - функция ввода данных:

<?php
$line = readline();
print($line);

Массивы

Массив — структура данных, хранящая набор значений (элементов массива), идентифицируемых по индексу.

В обычных массивах в PHP индекс значение - целое число. Нумеруются массивы с 0:

<?php
$fav_shows = ["game of thrones", "american horror story", "walking dead"];

В ассоциативных массивах в качестве индекса может использоваться не только число. Например, строка:

<?php
$user = ['age' => 42, 'name' => 'Иннокентий', 'fav_shows' => ["game of thrones", "american horror story", "walking dead"] ];

Условный оператор

Синтаксис: if (условие):

if ($gender == "мужчина") {
    print("Приветствую тебя, мой Господин!");
} else if ($gender == "женщина") {
    print("Приветствую тебя, о Госпожа!");
} else {
    print("Не хватает гендеров.");
}
  • оператор присваивания: =
  • операторы сравнения: ==, ===, !=, !==, >, <, <=, >=
  • and, or - логические и и или соответственно

Циклы

Синтаксис while: while (условие_остановки):

<?php
$i = 0;
while ($i < 10) {
    print($i);
    $i = $i + 1;
}

Синтаксис for: for (команда,_выполняемая_до_цикла; условие_осатновки; команда,_выполняемая_в_ конце_каждой_операции):

<?php
for ($x = 0; $x < 10; ++$x) {
    print($x);
}

Для асоциативных массивах есть цикл foreach:

<?php
$user = [
    'Имя' => 'Евгений',
    'Возраст' => '27',
    'Род занятий' => 'Программист'
];
foreach ($user as $key => $value) {
    print($key . ': ');
    print($value . '<br>');
}

Функции

Синтаксис: имя_функции(аргументы_функции):

function is_even($number) {
    if ($number % 2 != 0) {
        return false;
    } else {
        return true;
    }
}
if (is_even(3)) {
    print("3 - четное число");
} else {
    print("3 - нечетное число");
}

Подключение файлов

Чтобы не писать всё в одном файле, используется команда require. Содержимое файла sub.php:

<?php
print("Привет, я содержимое из sub.php!");

Содержимое файла index.php:

<?php
require 'sub.php';
print("А я - index.php!");

После подключения sub.php, содержимого этого файла доступно в index.php. Есть также include. Отличия между require() и include() таковы, что require() возвращает FATAL ERROR, если файл не найден, include() же возвращает только WARNING.

Полезные константы

  • __DIR__ — полный путь к директории, в которой находится текущий сценарий
  • __FILE__ — полный путь к текущему сценарию
  • $_COOKIE — ассоциативный массив, содержащий куки
  • $_SESSION — ассоциативный массив, содержащий информацию о сессии
  • $_POST — ассоциативный массив, содержащий данные, полученные после POST запроса
  • $_GET — ассоциативный массив, содержащий данные, полученные после GET запроса

Примеры

1. Пусть есть сайт с формой: Клиент:

<form name="feedback" method="POST" action="form.php">
  <label>Ваше имя: <input type="text" name="name"></label>
  <label>Ваш email: <input type="text" name="email"></label>
  <label>Сообщение: <textarea name="message"></textarea></label>

  <input type="submit" name="send" value="Отправить">
</form>

При получении формы сервером, данные из неё можно получить следующим образом: Сервер:

<?php
if (isset($_POST)) {
    print("Имя: " . $_POST['name']);
    print("<br>Email: " . $_POST['email']);
    print("<br>Сообщение: " . $_POST['message']);
}

2. Другой пример - отправка формы: Клиент:

<form name="file_upload" method="POST" action="form.php" enctype="multipart/form-data">
  <label>Ваш аватар: <input type="file" name="avatar"></label>
  <input type="submit" name="send" value="Отправить файл">
</form>

Работать с файлом ожно следующим образом: Сервер:

<?php
if (isset($_FILES['avatar'])) {
    $file = $_FILES['avatar'];

    print("Загружен файл с именем " . $file['name'] . " и размером " . $file['size'] . " байт");
}

Если не переместить файл в постаянную папку - файл будет удален:

$current_path = $_FILES['avatar']['tmp_name'];
$filename = $_FILES['avatar']['name'];
$new_path = dirname(__FILE__) . '/' . $filename;

move_uploaded_file($current_path, $new_path);

SQL

Так же можно работать с базами данных. mysqli_connect - функция для подключения к mysql-серверу, mysqli_query - отправка sql-запроса:

<?php
$link = mysqli_connect("localhost", "root", "");

$sql = 'INSERT INTO cities SET name = "Санкт-Петербург"';
$result = mysqli_query($link, $sql);

if ($result == false) {
    print("Произошла ошибка при выполнении запроса");
}

web shell

Простейшим примером является загрузка файла с расширением .php на сайт, работающий на PHP (в качестве проверки можно попробовать обратиться по адресу cite.com/index.php и не получить ошибки). По умолчанию PHP-сайты работают так что пользователь обращается по URL к файлу (URL сайта, начиная с первого /, отображаются на определенную директорию на сервере, она называется web root) и, если этот файл имеет расширение php (или другое расширение из предопределенного списка расширений для PHP-файлов), то содержимое этого файла выполняется как код на PHP, вывод этого кода возвращается пользователю в ответе (кстати, так работает не только PHP, но и ASP.NET Web Forms, JSP, ASP). В результате, если пользователь может загрузить на сайт файл c расширением .php и обратиться к нему по URL (т. е. этот файл будет создан где-то внутри web root), то, если сервер специально не настроен чтобы файлы из директории для загрузок не выполнялись, PHP-код в нём будет выполнен. Соответственно, загрузив на уязвимый сайт файл shell.php с кодом

42 * 66613;
<?php system('ls -lsa'); ?>

и, получив ссылку на него (к примеру, /uploads/5c8e82bb8ad66/attack.php), обратится по ней, то в результате этот PHP-код выполнится и в ответе сервера будет произведение чисел 42 и 66613, а также список всех файлов в длинном формате (функция PHP system работает так же как одноимённая функция библиотеки Си - то есть выполняет команду shell). В итоге атакующий получает выполнение произвольного кода на сервере (RCE).

Обход методов защиты и атака

Валидация имени файла

Черный список ресширений

Обходится с помощью использования двойных расширений (shell.php.png) или малоизвестный расширений (.php5, .phtml, .shtml, .htaccess). Кроме того, возможна инъекция нулевого байта (shell.php%00.jpg) или регистрозависимость при проверке (PHp3, .aSp).

Белый список расширений

Спасает от использования атакующим малоизвестных и регистрозависимых расширений, но все ещё обходится с помощью двойных расширений или инъекции нулевого байта.

Санитайзеры

Оставляет большую степень свободы. Например, если санитайзер удаляет вхождения подстрок типа .php, то использования имени файла вида shell.p.phphp позволяет после загрузки получить файл с именем shell.php.

Валидация Content-Type

Обходится изменением заголовка Content-Type, например, с помощью перехватывающего прокси (burp suite). Соответсвенно, в запросе

POST / HTTP/1.1
Host: easy.fu.khashaev.ru
Content-Length: 200
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="info.php"
Content-Type: text/php
 <?php phpinfo(); ?>
------WebKitFormBoundary--

строчка Content-Type: text/php может быть заменена на строчку Content-Type: image/png.

Прочее

.htaccess - конфигурационный файл веб-сервера Apache, позволяющий управлять работой веб-сервера и настройками сайта с помощью различных параметров (директив) без изменения основного конфигурационного файла веб-сервера.

Server Side Includes - язык для динамической «сборки» веб-страниц на сервере из отдельных составных частей и выдачи клиенту полученного HTML-документа. Имеет расширения .shtml, .stm или .shtm. Например, при загрузке и вызове file.shtml, содержащего код

<!-- #exec cmd="ls" -->

атакующему будет выведен список файлов данной директории.

Static HTML

При возможности загрузить на сервис статический HTML, создается HTML-страница, содержащая вредоносный код (например, крадущего cookie), и ссылка на этот файл отправляется жертве.

Path Traversal

Атака, основанная на использовании специфически составленных имен файлов (например, ../config.ini), позволяющих выйти за пределы директории хранения файлов. Может привести к переписыванию исходных фалов веб-приложения, конфигурационных или системных файлов. Атака возможна, если не используется функция basename (возвращает последний компонент имени из указанного пути) или её аналог.

Filename XSS

Сервис уязвим к XSS, если допускается имя файла вида <script>alert(1)</script>.

Полиглоты

Полиглотами называют файлы, которые могут быть корректно распознаны как файлы разных типов. Можно встраивать

  • JavaScript в JPEG, GIF, PNG, PDF
  • HTML в что-нибудь
  • PHP в JPEG , GIF, PNG

Структура JPEG

Все jpeg-файлы имеют схожую структуру. Рассмотрим в качестве примера изображение

Mini.jpg

В hex-редакторе оно выглядит следующим образом

Hex print.png

В структуре jpeg-файлов содержатся служебные маркеры, состоящие из двух байт и всегда начинающиеся с FF.

  • FF D8 - маркер начала jpeg-файла;
  • FF DA - маркер начала секции Start of Scan:
    • в двух байтах хранится длинна заголовка изображения [00 0C];
    • заголовок изображения [03 01 00 02 11 03 11 00 3F 00];
    • непосредственно информацию о самом изображении;
  • FF D9 - маркер конца jpeg-файла.

Image Magic

ImageMagick — набор программ для чтения и редактирования файлов множества графических форматов, свободное и кроссплатформенное ПО. Может использоваться с языками Perl, C, C++, Python, Ruby, PHP, Node.js, Pascal, Java, Delphi, в скриптах командной оболочки или самостоятельно. ImageMagick обладает широким набором возможностей (конвертация, маскирование, увеличение контрастности и т.п.) и поддерживает работу с различными типами файлов, но так же в нем находили различные уязвимости:

  • RCE [CVE-2016-3714]
  • SSRF [CVE-2016-3718]
  • File deletion [CVE-2016-3715]
  • File moving [CVE-2016-3716]
  • Local File Read [CVE-2016-3717]

CVE - Common Vulnerabilities and Exposures - база данных общеизвестных уязвимостей информационной безопасности.

Был создан сайт ImageTragick, посвященный описанию некоторых уязвимостей ImageMagic.


Пример.

Допустим, что есть некоторый онлайн-сервис, позволяющих переводить файлы в разные форматы, использующий библиотеку ImageMagic. Тогда, создав файл exploit.mvg с содержанием

push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/
image.jpg " ; | ls  "-la)'
pop graphic-context

и выполнив запрос на преобразование файла, аналогичный команде

$ convert exploit.mvg out.png

атакующий получит список всех файлов директории.

Стенды

Ссылки