Всем привет) я можно сказать мало-опытный по взломам) любитель. Попался мне как то сайт с объявлениями и вот получилось залить шелл через загрузку картинок. С помощью Tamper data подменил формат файла. Пользовался я этой дыркой много месяцев, оставался не замеченным. Даже создал на всякий случай мини шеллы в разных места и запрятал от глаз админа. Но вот спустя почти 7 месяцев админ походу засек меня и удалил все. Плюс закрыл дыру по заливке картинок. Я в свое время успел закачать много файлов php себе на комп. И вот в поисках новых багов я нашел еще один загрузчик картинок который есть на сайте. Собственно в чем вопрос: Кто что посоветует по обходу фильтрации в этом скрипте. Я особо в коде не силен, так по мелочи. Сайт полностью самописный! Тут подмена не проходит естественно, при заливке к имени файла добавляется Id. И кроме .jpg ничего не хочет... Может кто подскажет? Приведу его код: <?php include "dbconnect.php"; $num_of_uploads = 3; $file_types_array = array ( "JPG", "jpg"); $max_file_size = 1048576; $upload_dir = "./pictures/"; require ('imgresize.php'); function uploaderFILES ($num_of_uploads, $file_types_array, $max_file_size = 1048576, $upload_dir) { if (!is_numeric ($max_file_size)) { $max_file_size = 1048576; } foreach ($_FILES["file"]["error"] as $key => $value) { if ($_FILES["file"]["name"][$key] != "") { echo $_FILES["file"]["name"][$key]; $filename = htmlspecialchars ($_FILES['file']['name'][$key], ENT_QUOTES); if ($value == UPLOAD_ERR_OK) { $result07 = mysql_query ("select max(id_picture) from picture;"); $row07 = mysql_fetch_array ($result07); $number = $row07['max(id_picture)'] + 1; //$uploaddir = "picture/"; //echo "<br><a href=picture/$filename>$filename</a><br>"; //$url="$uploaddir$filename"; //------------------------------------------------------------- $origfilename = $_FILES["file"]["name"][$key]; //echo "$filename[0]_$number<br>"; $filename = $_FILES["file"]["name"][$key]; //уcho "$filename<br>"; //echo "$filename<br>"; $filename = explode (".", $_FILES["file"]["name"][$key]); //echo "$filename[0] $filename[1]<br>"; $filenameext = $filename[count ($filename) - 1]; unset ($filename[count ($filename) - 1]); $filename = implode (".", $filename); $filename = substr ($filename, 0, 15) . "." . $filenameext; //echo "END=$filename<br>"; //изменяем название файла $filename = str_replace (".", "_id_$number.", $filename); $file_ext_allow = FALSE; for ($x = 0; $x < count ($file_types_array); $x++) { if ($filenameext == $file_types_array[$x]) { $file_ext_allow = TRUE; } } // for if ($file_ext_allow) { if ($_FILES["file"]["size"][$key] < $max_file_size) { if (move_uploaded_file ($_FILES["file"]["tmp_name"][$key], $upload_dir . $filename)) { //--------------------------------------------------------------- // $picturesize = getimagesize ("$upload_dir$filename"); $picturelink = "$upload_dir$filename"; /* if ($picturesize[0] > 640 || $picturesize[1] > 480) { img_resize($url , $picturelink, 640, 480) ; } else { img_resize($url,$picturelink, $picturesize[0], $picturesize[1]) ; } */ img_resize ($picturelink, $picturelink, 100, 75); //echo("$url ;$picturelink; $picturelinksmall"); //unlink($url); //--------------------------------------------------------------- echo("File uploaded successfully. - <a href='" . $upload_dir . $filename . "' target='_blank'>" . $filename . "</a><br />"); } else { echo('<font color="#FF0000">' . $origfilename . "</font> was not successfully uploaded <br />"); } } else { echo('<font color="#FF0000">' . $origfilename . "</font> was too big, not uploaded <br />"); } } // if else { echo('<font color="#FF0000">' . $origfilename . " </font>had an invalid file extension, <br />"); } } else { echo('<font color="#FF0000">' . $origfilename . " </font>was not successfully uploaded <br />"); } // else } } }
Код говно, но дыр в нём нет. Если ты конечно каким-то образом не протолкнёшь в $file_types_array расширение php. Ещё тут есть странность: foreach ($_FILES["file"]["error"] as $key => $value) - таких массивов при загрузке файлов пхп не создаёт. Похоже движок как-то изменяет массив $_FILES. Но всёравно это не даёт тебе шансов на взлом.
Да на счет я так и подозревал что мало шансов... Хотя в название файла можно вставить любой символ, почти любой. Я уже и старый загрузчик фото где мне удалось подмену сделать смотрю, он он там по закрывал почти все и сейчас если файл не картинка просто не грузит его. Раньше еще получалось загрузить когда он закрыл только формат .php я подменял на .phtml это прокатывало. Потом он добавил еще и его. А вот сейчас вообще не хочет грузить ничего кроме картинки и Mime type тоже проверяет хорошо. Ну и запрещенные символы. Видимо он его еще модифицировал. <?php error_reporting (0); session_name ('bla_x'); session_start (); header ("Content-type: text/html"); function img_resize ($src, $dest, $width, $height, $rgb = 0xF4FFFF, $quality = 100) { if (!file_exists ($src)) return false; $size = getimagesize ($src); if ($size === false) return false; $format = strtolower (substr ($size['mime'], strpos ($size['mime'], '/') + 1)); $icfunc = "imagecreatefrom" . $format; if (!function_exists ($icfunc)) return false; $x_ratio = $width / $size[0]; $y_ratio = $height / $size[1]; $ratio = min ($x_ratio, $y_ratio); $use_x_ratio = ($x_ratio == $ratio); $new_width = $use_x_ratio ? $width : floor ($size[0] * $ratio); $new_height = !$use_x_ratio ? $height : floor ($size[1] * $ratio); $new_left = $use_x_ratio ? 0 : floor (($width - $new_width) / 2); $new_top = !$use_x_ratio ? 0 : floor (($height - $new_height) / 2); $isrc = $icfunc ($src); $idest = imagecreatetruecolor ($width, $height); imagefill ($idest, 0, 0, $rgb); imagecopyresampled ($idest, $isrc, $new_left, $new_top, 0, 0, $new_width, $new_height, $size[0], $size[1]); imagejpeg ($idest, $dest, $quality); imagedestroy ($isrc); imagedestroy ($idest); return true; } if ($_FILES['file']['error'] == 0 && $_FILES['file']['size'] > 0) { if ($_FILES['file']['size'] < 2097152) { if (preg_match ("/^[А-Яа-яЁё\w\s\.*+-]+$/i", $_FILES['file']['name'])) { $savedir = '../temp_files/'; if (count ($_SESSION['file']) < 10) { // Создание имени файла $name = rand (1000, 10000) . '_bla_' . date ("Y-m-d H:i:s"); $exp = explode (".", $_FILES['file']['name']); $namefile = $name . '.' . $exp[count ($exp) - 1]; //блокируем расширение $blacklistExtentions = array(".php", ".php3", ".php4", ".sh"); foreach ($blacklistExtentions as $item) { if(preg_match("/$item\$/i", $namefile)) { echo '08'; //неверное расширение файла exit(); } } //проверяем картинка это или нет /* * $imageinfo = getimagesize($_FILES['userfile']['tmp_name']); if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg') { //echo "Sorry, we only accept GIF and JPEG images\n"; exit; } */ /* if ($_SERVER['HTTP_X_REAL_IP'] == '46.160.29.110') { $imageinfo = getimagesize($_FILES['file']['tmp_name']); if($imageinfo['mime'] != 'image/pjpeg' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/png') { echo "Sorry, we only accept GIF and JPEG images\n"; exit; } }*/ $filename = $savedir . $namefile; if ($_FILES['file']['type'] == 'image/pjpeg' || $_FILES['file']['type'] == 'image/gif' || $_FILES['file']['type'] == 'image/jpeg' || $_FILES['file']['type'] == 'image/png') { if (!file_exists ($filename)) { if (move_uploaded_file ($_FILES['file']['tmp_name'], $filename)) { img_resize ($filename, $filename, 420, 420); echo '<div style="float: left; display: inline; margin-bottom: 10px;"><span style="font-size: 12px;">[<a href="" style="font-size: 12px; color: red" onClick="delete_picture(\'' . $namefile . '\', \'0\'); return false;">Удалить</a>]</span><br> <img src="./imager.php?filename=' . $namefile . '" border="0" style="margin-right: 10px;" align="left"></div>'; @$_SESSION['file'][] = $namefile; } else { echo '04'; // При загрузке файла произошла ошибка } } else { echo '03'; // Файл с таким именем уже существует } } else { echo '02'; // mime тип не соответствует разрешённым } } else { echo '06'; // Количество картинок превышает 6 } } else { echo '05'; // В названии файла содержаться недопустимые символы } } else { echo '07'; // Файл большого размера } } else { echo '01'; // Выберите картинку
Код выше это предпоследняя версия, он походу после этого что то изменил в нем. К сожалению исходника imager.php нет. Этот скрипт показывает в маленьком размере изображение после загрузки.
Есть еще пароли от админки. Но воспользоваться не могу... Там при в ходе стоит проверка по ip адресу и в админку можно попасть только с определенного ip. Я так понимаю чуть ли не с домашнего ПК админа. Может кто знает как подменить именно на нужный IP?
если глаза мне не врут Code: if (preg_match ("/^[А-Яа-яЁё\w\s\.*+-]+$/i", $_FILES['file']['name'])) ^ пропустит любое имя файла, без спецсимволов Code: $blacklistExtentions = array(".php", ".php3", ".php4", ".sh"); foreach ($blacklistExtentions as $item) { if(preg_match("/$item\$/i", $namefile)) { echo '08'; //неверное расширение файла exit(); ^ валидация по черному списку можно попробовать загрузить phml Code: if ($_FILES['file']['type'] == 'image/pjpeg' || $_FILES['file']['type'] == 'image/gif' || $_FILES['file']['type'] == 'image/jpeg' || $_FILES['file']['type'] == 'image/png') { ^ нужно грузить валидную картинку Code: img_resize ($filename, $filename, 420, 420); ^ попробовать обойти этим https://rdot.org/forum/showthread.php?t=2780 Code: $name = rand (1000, 10000) . '_bla_' . date ("Y-m-d H:i:s"); ^ если имя файла не рандомится(говоришь код меняли), ресайз можно попробовать обойти загрузкой нескольких больших файлов, может поймаешь момент когда он ещё не начал ресайз
Тогда так потыкай, возможно прочитаешь патченную версию загрузчика. Каким образом определил? + ещё pht можно попробовать
Сейчас любой файл гружу не важно какой формат пусто ничего не пишет. Раньше хоть ошибка была, а сейчас ничего, только можно изображения грузить. Имя картинки рандомиться (bla потом дата и время, а потом уже формат файла) и загружаеться в папку temp_files. Потом после подачи обьявления этот файл копируеться в папку pictures и опять перемейновуеться c номером id. На счет расайза не пробовал
Я определил сначала зашел по паролю оно написало что ошибка ip так вот, в папке с админкой скачал тогда еще index.php вот его код <?php /* * Кодировка и уровень ошибок * и старт сессии */ header ("Content-Type: text/html; charset=cp1251"); error_reporting (E_ALL); session_name ('bla_admin'); session_start (); /* * Ключ доступа, * обеспечивает безопастность файлов */ define ("_CONFIG_KEY", TRUE); /* * Доступ к админке */ define ("_ADMIN_KEY", TRUE); /* * Конфгурационный файл */ include_once $_SERVER['DOCUMENT_ROOT'] . '/admin/config.php'; /* * Общие функции */ include_once _CONFIG_ROOT . 'lib/default.php'; /* * Файл переменных */ include _CONFIG_ROOT . 'variables.php'; /* * Локаль */ include _CONFIG_ROOT . 'langs/' . _CONFIG_LANG . '.php'; /* * Подключение к БД */ include_once _CONFIG_ROOT . 'lib/mysql.php'; /* * Подключаем memcache */ include_once __DIR__ . '/lib/memcache/memcache_get_data.php'; /* * Обработка потока - выводимых данных */ include_once _CONFIG_ROOT . 'lib/view.php'; /* * Транслит кирилицы */ include_once _CONFIG_ROOT .'lib/trans.php'; /* * Авторизация */ if (empty ($_SESSION['admin-n854gnvn78g9035hg45g7928g745g9nfdjg52guernjhg7h5gqeirjgnadskfjgh572gbn'])) { include _CONFIG_ROOT . 'sequrity/enter.php'; } /* * Разрешение доступа указанным IP */ if (!isTest()) { header ("HTTP/1.1 404 Not Found"); exit (file_get_contents ($_SERVER['DOCUMENT_ROOT'] . "/admin/error/404.html")); } /* * Главное меню */ include_once _CONFIG_ROOT . 'main_menu.php'; ob_start (); /* * Переключатель страниц */ switch ($GET['1']) { // главная case '1': include _CONFIG_ROOT . 'modules/main/router.php'; break; // Объявления case 'posts': include _CONFIG_ROOT . 'modules/posts/router.php'; break; // Жалобы case 'complaints': include _CONFIG_ROOT . 'modules/complaints/router.php'; break; // Платежка case 'payment_system': include _CONFIG_ROOT . 'modules/payment_system/router.php'; break; // пользователи case 'users': include _CONFIG_ROOT . 'modules/users/router.php'; break; // Рассылка писем case 'mail_delivery': include _CONFIG_ROOT . 'modules/mail_delivery/router.php'; break; // FAQ case 'faq': include _CONFIG_ROOT . 'modules/faq/router.php'; break; // Статистика case 'statistics': include _CONFIG_ROOT . 'modules/statistics/router.php'; break; // Выход из админки case 'exit': include _CONFIG_ROOT . 'sequrity/exit.php'; break; // если нет схождений - вывести 404 default : header ("HTTP/1.1 404 Not Found"); exit (file_get_contents (_CONFIG_ROOT . 'error/404.html')); } $content = ob_get_contents (); ob_end_clean (); /* * Шапка */ define ("_SITE_HEADER", _CONFIG_ROOT . 'skins/tpl/header.tpl'); /* * Подвал */ define ("_SITE_FOOTER", _CONFIG_ROOT . 'skins/tpl/footer.tpl'); /* * Файл вставки собранного контента */ include _CONFIG_ROOT . 'skins/tpl/index.tpl'; /* * Закрываем соединение с базой */ mysql_close ($link);