PDA

Просмотр полной версии : Еще раз о шеллах в картинках


Helios
05.08.2007, 02:46
Читал мануал по пхп с комментариями, наткнулся на один пост, который дал повод для размышления.

Автор использует этот скрипт для подгонки всех подгружаемых изображений:


<?php
class picture
{
var $save_dir; //where file will be saved
var $filename="spacer.gif"; //default file name initially
var $error_message=""; //string to be output if neccesary
var $width; //height of final image
var $height; //width of final image

function picture($save_directory, $file_array, $max_width, $max_height)
{
$this->save_dir = $save_directory;
$this->width = $max_width;
$this->height = $max_height;

//--change filename to time - make it unique
$temp_filename = $file_array['name'];
$ext = explode('.',$temp_filename);
$ext = $ext[count($ext)-1];
$temp_filename = time().".".$ext;

//--check that it's a jpeg or gif
if (preg_match('/^(gif|jpe?g)$/',$ext)) {
// resize in proportion
list($width_orig, $height_orig) = getimagesize($file_array['tmp_name']);
if ($this->width && ($width_orig < $height_orig)) {
$this->width = ($this->height / $height_orig) * $width_orig;
} else {
$this->height = ($this->width / $width_orig) * $height_orig;
}

$image_p = imagecreatetruecolor($this->width, $this->height);

//handle gifs and jpegs separately
if($ext=='gif'){
$image = imagecreatefromgif($file_array['tmp_name']);
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $this->width, $this->height, $width_orig, $height_orig);
imagegif($image_p, $this->save_dir.$temp_filename, 80);
}
else
{
$image = imagecreatefromjpeg($file_array['tmp_name']);
imagecopyresampled($image_p, $image, 0, 0, 0, 0, $this->width, $this->height, $width_orig, $height_orig);
imagejpeg($image_p, $this->save_dir.$temp_filename, 80);
}

imagedestroy($image_p);
imagedestroy($image);

$this->filename=$temp_filename;

}else{
$this->error_message.="<br> file is not a jpeg or gif picture <br>";
}
}
}


Но чем не способ фильтровать полученные файлы?
Проблема только одна: для работы требуется поддержка GD в пхп, но в данный момент на большинстве хостингов она есть.

Вот набросанный за пару минут класс для фильтрации картинок:


<?php
/**
* Класс для проверки изображений
*
*/
class SecureImage
{
/**
* Картинка
* @var resource
*/
private $img = null;

/**
* Данные о картинке
* @var array
*/
private $data = null;

/**
* Тип изображения
* @var string
*/
private $type = null;

/**
* Конструктор класса
*
* @param string $s_patch Папка для сохранения картинки
*/
public function __construct($image_data)
{
$this->data = $image_data;
}

/**
* Деструктор класса
*/
public function __destruct()
{
if ($this->img)
{
imagedestroy($this->img); // освобождаем память
}
}

/**
* Проверка изображения
*
* @param array $image Массив с данными о загруженом файле из $_FILES
* @return boolean Результат проверки
*/
public function check()
{
$ext = explode('.',$this->data['name']);
$ext = trim(strtolower($ext[count($ext)-1]));

switch ($ext)
{
case 'gif':
$this->img = @imagecreatefromgif($image['tmp_name']);
$this->type = 'gif';
break;

case 'jpeg':
case 'jpg':
$this->img = @imagecreatefromjpeg($image['tmp_name']);
$this->type = 'jpg';
break;

case 'png':
$this->img = @imagecreatefrompng($image['tmp_name']);
$this->type = 'png';
break;
}

if ($this->img)
{
return true; // и правда, картинка
}
else
{
return false;// шелл батькович
}
}

/**
* Созранение изображения
*
* @param string $patch Путь для сохранения
* @return boolean Результат сохранения
*/
public function save($patch)
{
if (!$this->img) { return false; }
switch ($this->type)
{
case 'gif':
return imagegif($this->img, $patch); // на случай, если прав на запись в указанном месте нет
break;

case 'jpg':
return imagejpeg($this->img, $patch);
break;

case 'png':
return imagepng($this->img, $patch);
break;

default:
return false;
break;
}
}

/**
* Возвращает расширение для картинки
*/
public function getExtension()
{
return $this->type;
}
}


Вот пример его использования:


$img = new SecureImage($_FILES['some_image']);
if ($img->check())
{
// и правда, картинка - продолжаем.
$img->save('/path/to/images/my_image.' . $img->getExtension());
}
else
{
// shell, как он есть, или ошибка в картинке
}


Недостаток в том, что поддерживаются только gif, jpeg и png. Хотя аватарок в psd я нигде и не видел...

В общем, хочу узнать ваше мнение

Isis
05.08.2007, 03:22
Как тебе отобразаятся изображения в .psd формате?
Бразуер предлагает скачать файл, но он никак не отобразится как картинка..
.psd файл вроде - исходник изображения в фотошопе...

inv
05.08.2007, 03:48
А что если переименовать jpg в png ?(сорец не смотрел)
Форматов изображений очень много

A110ut
05.08.2007, 04:36
ждем обещаного в ирц мегатру кода by Z

ps: Isis, было бы желание, а написать модуль для отображения какой либо графики не проблема

Helios
05.08.2007, 12:12
Гема в irc доказывал, что проверка содержимого не состоятельна, т.к. благодаря специфике форматов изображений можно вставить нужный код в различные коментарии, etc в тело картинки.

Предложенный мною метод переноса "изображений" из временной папки хорош тем, что благодаря особенности GD - эта библиотека не работает с мета-данными файлов, а использует только ту информацию, которая используется при построении растра.

И сохраняет GD, соответственно, только графическую информацию, т.е. вставки шеллкода в различных полях идут лесом.

Вот этим кодом преобразовал файл с встроенным куда только можно кодом <?php phpinfo(); ?> (image.jpg)

$im = imagecreatefromjpeg('image.jpg');
imagejpeg($im, 'newimage.jpg');


После сохранения были вырезаны все мета данные, и размер избражения изменился:
image.jpg (http://heliosart.info/images/image.jpg): 3.75 кб
newimage.jpg (http://heliosart.info/images/newimage.jpg): 2.33 кб

На выходе имеем "чистую" картинку, что и требовалось.

nerezus
05.08.2007, 13:03
А можно просто не трахать мозг и не проверять файлы с именами *.png, *.jpeg, *.jpg, *.gif вообще.

A110ut
05.08.2007, 15:26
да собственно никто етого делать и не заставляет. просто пример как можно поиздевается с ГД. сели не уверен в отсутствии локального инклуда в скриптах, то можно перестраховаться таким способом. хотя в етом случае можно разводить демагогию по поводу "ты хyевый программер если предполагаешь саму возможность ошибки".
в крайнем случае, всегда можно оправдать подобные телодвижения довольно неслабым уменьшением размера полученной картинки =) кстати, если не ошибаюсь на nnm используется что-то подобное.

gemaglabin
05.08.2007, 18:59
Я не знал что библиотека gd режет все лишнее поэтому вот окончательный код имхо.Правда если мы имеем дело с залитием например видео то wmv не прописан в майм тайпах апача по умолчанию и поэтому файл shell.php.wmv будет интерпретироваться именно как php , так что проверка по расширению имхо наилучшая.

http://underwater.itdefence.ru/safe_upload.txt

nerezus
05.08.2007, 19:08
http://underwater.itdefence.ru/safe_upload.txt А зачем такие сложности?
Ну допустим у тебя шелл внутри картинки. И что с того?
Я еще понимаю, можно что из-за кривых bb-кодов(человеческий фактор) допустить дыру, но вот из-за инклюдов....

gemaglabin
05.08.2007, 19:34
Нер я тоже не понимаю как люди допускают ошибки типа инклуда но ведь даже очень хорошие программисты ошибались.х3

George767
06.08.2007, 10:21
Я на своих проектах юзаю следующий алгоритм
1 Проверка расширения
2 Проверка Mime типа
3 Проверка типа вообще (через exif_imagetype)
4 Проба ширины и высоты - имеют ли числовые значения
Далее можно пере-рендерить картинку как уже написал Helios:
$im = imagecreatefromjpeg('image.jpg');
imagejpeg($im, 'newimage.jpg', 100);

Если рендерить и менять ширину/высоту по какой-то причине нельзя, то можно проверсти вот такую проверочку содержимого картинки

<meta http-equiv="content-type" content="text/html; charset=windows-1251">
<?php

$result_of_scan="";

$uploaded_img="resized.jpg";
$fh=fopen($uploaded_img, 'r');
$contents_of_uploaded_img=fread($fh, filesize($uploaded_img));
fclose($fh);

$list_of_potential_xss=array('javascript', 'vbscript', 'expression', 'applet', 'blink', 'link', 'style', 'script', 'embed',

'object', 'iframe', 'frame', 'frameset', 'ilayer', 'layer', 'bgsound', 'title', 'base', 'onabort', 'onactivate',

'onafterprint', 'onafterupdate', 'onbeforeactivate', 'onbeforecopy', 'onbeforecut', 'onbeforedeactivate',

'onbeforeeditfocus', 'onbeforepaste', 'onbeforeprint', 'onbeforeunload', 'onbeforeupdate', 'onblur', 'onbounce',

'oncellchange', 'onchange', 'onclick', 'oncontextmenu', 'oncontrolselect', 'oncopy', 'oncut', 'ondataavailable',

'ondatasetchanged', 'ondatasetcomplete', 'ondblclick', 'ondeactivate', 'ondrag', 'ondragend', 'ondragenter', 'ondragleave',

'ondragover', 'ondragstart', 'ondrop', 'onerror', 'onerrorupdate', 'onfilterchange', 'onfinish', 'onfocus', 'onfocusin',

'onfocusout', 'onhelp', 'onkeydown', 'onkeypress', 'onkeyup', 'onlayoutcomplete', 'onload', 'onlosecapture', 'onmousedown',

'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onmousewheel', 'onmove',

'onmoveend', 'onmovestart', 'onpaste', 'onpropertychange', 'onreadystatechange', 'onreset', 'onresize', 'onresizeend',

'onresizestart', 'onrowenter', 'onrowexit', 'onrowsdelete', 'onrowsinserted', 'onscroll', 'onselect', 'onselectionchange',

'onselectstart', 'onstart', 'onstop', 'onsubmit', 'onunload', 'input', 'form', 'post','input', 'echo', 'background',

'alert', 'img src', 'table', 'Content-Type', 'charset', 'http-equiv', 'meta', 'body', 'document', 'style', 'cookie',

'false', 'true', 'delete', 'UTF-7', 'UTF-8', '.html', '.dhtml');


for($x = 0; $x < count($list_of_potential_xss); $x++)
{
preg_match("/".$list_of_potential_xss[$x]."/", $contents_of_uploaded_img, $matches);
if(count($matches) > 0)
{
$result_of_scan="true";
echo "$list_of_potential_xss[$x] - присутсвует<br><br>";
}
else
{
echo "$list_of_potential_xss[$x] - отсутсвует<br><br>";
}
}

if ($result_of_scan==="true")
{
echo ("<b><h1>XSS есть");
}
else
{
echo ("<b><h1>Все чисто");
}

?>

SQLHACK
06.08.2007, 10:52
1. Эта проверка идёт лесом. пример тому.
<IMG DYNSRC="javas cript:al ert('XSS')">

2. вроде разговор о шеллах, а чёт похоже на защиту от XSS, если ты имел ввиду XSS то читай первый пост.

А если ты говорил про пхп шелл то за GZIP'еный шелл склееный с картинкой ты тоже не спалишь этим скриптом.

Так что либо дорабатывай, либо забудь об этой защите ;)

George767
06.08.2007, 11:10
1. Эта проверка идёт лесом. пример тому.
<IMG DYNSRC="javas cript:al ert('XSS')">

2. вроде разговор о шеллах, а чёт похоже на защиту от XSS, если ты имел ввиду XSS то читай первый пост.

А если ты говорил про пхп шелл то за GZIP'еный шелл склееный с картинкой ты тоже не спалишь этим скриптом.

Так что либо дорабатывай, либо забудь об этой защите ;)

Эта проверка чисто для экспериментов (хз каких))

В любом случае проще пере-рендерить картинку и весь побочный код идет лесом.

SQLHACK
06.08.2007, 11:29
В любом случае проще пере-рендерить картинку и весь побочный код идет лесом.
А ведь есть ещё и технические поля.
Где можно сохранить. Хз как передендеринг, удалит их, или нет.

Helios
06.08.2007, 11:53
А ведь есть ещё и технические поля.
Где можно сохранить. Хз как передендеринг, удалит их, или нет.

Как я уже говорил, Gd считывает и сохраняет только графические данные, используемые для построения растра, т.е. все техничесие поля остаются неудел.

SQLHACK
06.08.2007, 12:48
сорри не увидел первую страницу разговора. Теперь всё ясно.