Еще раз о шеллах в картинках

Discussion in 'PHP' started by Helios, 5 Aug 2007.

  1. Helios

    Helios Elder - Старейшина

    Joined:
    14 Jan 2007
    Messages:
    414
    Likes Received:
    180
    Reputations:
    103
    Читал мануал по пхп с комментариями, наткнулся на один пост, который дал повод для размышления.

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

    PHP:
    <?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$image0000$this->width$this->height$width_orig$height_orig);
                                
    imagegif($image_p$this->save_dir.$temp_filename80);
                            }
                            else
                            {
                                
    $image imagecreatefromjpeg($file_array['tmp_name']);                             
                                
    imagecopyresampled($image_p$image0000$this->width$this->height$width_orig$height_orig);                            
                                
    imagejpeg($image_p$this->save_dir.$temp_filename80);
                            }

                            
    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:
    <?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;
        }
    }
    Вот пример его использования:

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

    В общем, хочу узнать ваше мнение
     
    #1 Helios, 5 Aug 2007
    Last edited: 5 Aug 2007
    1 person likes this.
  2. banned

    banned Banned

    Joined:
    20 Nov 2006
    Messages:
    3,324
    Likes Received:
    1,194
    Reputations:
    252
    Как тебе отобразаятся изображения в .psd формате?
    Бразуер предлагает скачать файл, но он никак не отобразится как картинка..
    .psd файл вроде - исходник изображения в фотошопе...
     
  3. inv

    inv Banned

    Joined:
    3 Aug 2007
    Messages:
    261
    Likes Received:
    143
    Reputations:
    -58
    А что если переименовать jpg в png ?(сорец не смотрел)
    Форматов изображений очень много
     
    1 person likes this.
  4. A110ut

    A110ut Elder - Старейшина

    Joined:
    31 Dec 2005
    Messages:
    505
    Likes Received:
    263
    Reputations:
    92
    ждем обещаного в ирц мегатру кода by Z

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

    Helios Elder - Старейшина

    Joined:
    14 Jan 2007
    Messages:
    414
    Likes Received:
    180
    Reputations:
    103
    Гема в irc доказывал, что проверка содержимого не состоятельна, т.к. благодаря специфике форматов изображений можно вставить нужный код в различные коментарии, etc в тело картинки.

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

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

    Вот этим кодом преобразовал файл с встроенным куда только можно кодом <?php phpinfo(); ?> (image.jpg)
    PHP:
    $im imagecreatefromjpeg('image.jpg');
    imagejpeg($im'newimage.jpg');
    После сохранения были вырезаны все мета данные, и размер избражения изменился:
    image.jpg: 3.75 кб
    newimage.jpg: 2.33 кб

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

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    727
    Reputations:
    266
    А можно просто не трахать мозг и не проверять файлы с именами *.png, *.jpeg, *.jpg, *.gif вообще.
     
  7. A110ut

    A110ut Elder - Старейшина

    Joined:
    31 Dec 2005
    Messages:
    505
    Likes Received:
    263
    Reputations:
    92
    да собственно никто етого делать и не заставляет. просто пример как можно поиздевается с ГД. сели не уверен в отсутствии локального инклуда в скриптах, то можно перестраховаться таким способом. хотя в етом случае можно разводить демагогию по поводу "ты хyевый программер если предполагаешь саму возможность ошибки".
    в крайнем случае, всегда можно оправдать подобные телодвижения довольно неслабым уменьшением размера полученной картинки =) кстати, если не ошибаюсь на nnm используется что-то подобное.
     
    #7 A110ut, 5 Aug 2007
    Last edited: 5 Aug 2007
    1 person likes this.
  8. gemaglabin

    gemaglabin Green member

    Joined:
    1 Aug 2006
    Messages:
    772
    Likes Received:
    842
    Reputations:
    1,369
    Я не знал что библиотека gd режет все лишнее поэтому вот окончательный код имхо.Правда если мы имеем дело с залитием например видео то wmv не прописан в майм тайпах апача по умолчанию и поэтому файл shell.php.wmv будет интерпретироваться именно как php , так что проверка по расширению имхо наилучшая.

    http://underwater.itdefence.ru/safe_upload.txt
     
    2 people like this.
  9. nerezus

    nerezus Banned

    Joined:
    12 Aug 2004
    Messages:
    3,191
    Likes Received:
    727
    Reputations:
    266
    А зачем такие сложности?
    Ну допустим у тебя шелл внутри картинки. И что с того?
    Я еще понимаю, можно что из-за кривых bb-кодов(человеческий фактор) допустить дыру, но вот из-за инклюдов....
     
  10. gemaglabin

    gemaglabin Green member

    Joined:
    1 Aug 2006
    Messages:
    772
    Likes Received:
    842
    Reputations:
    1,369
    Нер я тоже не понимаю как люди допускают ошибки типа инклуда но ведь даже очень хорошие программисты ошибались.х3
     
  11. George767

    George767 Elder - Старейшина

    Joined:
    10 Jan 2006
    Messages:
    129
    Likes Received:
    25
    Reputations:
    -1
    Я на своих проектах юзаю следующий алгоритм
    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>Все чисто");
    }

    ?>
     
    #11 George767, 6 Aug 2007
    Last edited: 6 Aug 2007
  12. SQLHACK

    SQLHACK Остались только слоны

    Joined:
    27 Sep 2006
    Messages:
    437
    Likes Received:
    372
    Reputations:
    407
    1. Эта проверка идёт лесом. пример тому.
    <IMG DYNSRC="javas cript:al ert('XSS')">

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

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

    Так что либо дорабатывай, либо забудь об этой защите ;)
     
    _________________________
    #12 SQLHACK, 6 Aug 2007
    Last edited: 6 Aug 2007
  13. George767

    George767 Elder - Старейшина

    Joined:
    10 Jan 2006
    Messages:
    129
    Likes Received:
    25
    Reputations:
    -1
    Эта проверка чисто для экспериментов (хз каких))

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

    SQLHACK Остались только слоны

    Joined:
    27 Sep 2006
    Messages:
    437
    Likes Received:
    372
    Reputations:
    407
    А ведь есть ещё и технические поля.
    Где можно сохранить. Хз как передендеринг, удалит их, или нет.
     
    _________________________
  15. Helios

    Helios Elder - Старейшина

    Joined:
    14 Jan 2007
    Messages:
    414
    Likes Received:
    180
    Reputations:
    103
    Как я уже говорил, Gd считывает и сохраняет только графические данные, используемые для построения растра, т.е. все техничесие поля остаются неудел.
     
  16. SQLHACK

    SQLHACK Остались только слоны

    Joined:
    27 Sep 2006
    Messages:
    437
    Likes Received:
    372
    Reputations:
    407
    сорри не увидел первую страницу разговора. Теперь всё ясно.
     
    _________________________