как внедрить код в изображение
Вредоносный код в картинке: киберпреступники и стеганография
Сейчас «читает между строк» говорят про человека, который умеет понимать намеки и скрытые смыслы. Между тем когда-то в буквальном смысле между строк действительно передавали тайные сообщения, написанные невидимыми чернилами. Прием, когда автор послания прячет секретную информацию внутри чего-то на первый взгляд совершенно невинного, называется стеганография и применяется с незапамятных времен.
В отличие от криптографии, которая делает сообщение нечитаемым без знания ключа для расшифровки, задача стеганографии в том, чтобы посторонние вовсе не догадались о существовании сообщения. Как и многие другие методы работы с информацией, сегодня стеганография используется и в цифровых технологиях.
Как работает цифровая стеганография?
Спрятать секретное послание можно практически в любой цифровой объект — текстовый документ, лицензионный ключ, расширение файла. С его помощью можно защитить от копирования уникальный контент ресурса. Один из самых удобных «контейнеров» — медиафайлы (картинки, аудио, видео и так далее). Они обычно достаточно большие по размеру, а значит, и «довесок» может быть не таким маленьким, как в документе Word.
Секретную информацию можно записать в метаданные файла или же прямо в его основное содержимое, например, в картинку. С точки зрения компьютера она представляет собой набор из сотен тысяч точек-пикселей. У каждого пикселя есть «описание» — информация о его цвете. Для формата RGB, который используется в большинстве цветных картинок, это описание занимает в памяти 24 бита. Если в описании некоторых или даже всех точек 1–3 бита будет занято секретной информацией, на картинке в целом изменения будут неразличимы. А за счет огромного числа пикселей всего в изображение вписать можно довольно много данных. На нижних изображениях заметны области с высокой энтропией (высокой плотностью данных) — это и есть внедренное сообщение.
В большинстве случаев прячут информацию в пиксели и извлекают ее оттуда при помощи специальных утилит. Иногда для этой цели пишут собственные скрипты или добавляют нужную функциональность в программы другого назначения. А иногда пользуются готовыми кодами, которых в сети немало.
Как используют цифровую стеганографию?
Применений стеганографии в компьютерных технологиях немало. Прятать текст в картинку, видео или музыкальный трек можно и развлечения ради, и для защиты файла от нелегального копирования.
Скрытые водяные знаки — это тоже пример стеганографии. Однако первое, что приходит в голову, когда речь заходит о тайных посланиях как в физической, так и в цифровой форме, — это разнообразная тайная переписка и шпионаж.
Стеганография — находка для кибершпиона
Интерес злоумышленников к стеганографии был отмечен экспертами по кибербезопасности еще полтора года назад. Тогда в их поле зрения попали по меньшей мере три шпионских кампании, на командные серверы которых отправлялись данные под видом фото и видео. С точки зрения систем безопасности и сотрудников, следящих за исходящим трафиком, ничего подозрительного в том, что в Сеть загружают медиафайлы, не было. На это и рассчитывали преступники.
Еще одна шпионская программа получала команды через картинки. Злоумышленники общались со зловредом при помощи сообщений в твиттере, в которых серьезную информацию ищут в последнюю очередь. Очутившись на компьютере жертвы, зловред открывал опубликованный злоумышленниками пост и извлекал из смешной картинки инструкцию для дальнейших действий. Среди команд, к примеру, были такие:
Код в картинке
Вслед за кибершпионами к стеганографии стали чаще прибегать и другие киберпреступники: ведь в медиафайле можно спрятать не просто текст, а кусок вредоносного кода. Это не превращает картинку, музыку или видео в полноценного зловреда, однако позволяет скрыть от антивируса.
Так, злоумышленники распространяли через рекламные сети занятный баннер. Собственно рекламы на нем не было, он выглядел как полоска – небольшой белый прямоугольник. Зато он содержал скрипт, который выполнялся в браузере (в рекламный слот можно загружать скрипты, например, чтобы компании могли собирать статистику о просмотре объявления).
Этот скрипт распознавал цвет пикселей на картинке и записывал его в виде набора букв и цифр. Казалось бы, довольно бессмысленная задача, так как это был просто белый прямоугольник. Однако в глазах программы пиксели были почти белыми, и это «почти» она преобразовывала во вредоносный код, который тут же и исполняла.
Извлеченный из картинки код перенаправлял пользователя на сайт злоумышленников. Там жертву уже ждал троян, притворяющийся обновлением Adobe Flash Player, который затем скачивал другие вредоносные программы.
Обнаружить стеганографию очень трудно
Как отметили на форуме RSA, качественную стеганографию распознать крайне сложно. Избавиться от нее тоже не так-то просто: есть методы, позволяющие вшить сообщение в картинку настолько глубоко, что оно сохранится даже после того, как ее напечатают и снова отсканируют, уменьшат, увеличат или еще как-то отредактируют.
Однако, информацию — в том числе код — из изображений и видео извлекают при помощи специальной программы. То есть сами по себе медиафайлы ничего с вашего компьютера не украдут и на него не загрузят. Так что вы можете обезопасить свое устройство с помощью этих простых правил:
Как встроить код в изображение
Morty
Morty
Изображения, которые мы загружаем в Интернет – это не только фоточки, которые несут ваше представление о жизни, но также то, что может нести код, чтобы украсть наши шифры изображений и наши данные.
Например, файлы изображений начинаются с уведомления о текущем типе файла образа изображения. Обычно это что-то вроде маркера «Старт-изображение», последовательности или номера, который показывает, как он будет следовать. Файлы GIF начинаются с GIF87a или GIF89a, если рассматривать их как кодировку ISO 8859-1, начиная с шестнадцатеричного числа «47 49 46 38 37 61». После этих подписей соответствующий цвет соответствует цвету и расположению пикселей в изображении. Этих данных достаточно для создания полезного растрового изображения при добавлении в файл изображения. Однако в дополнение к этому видимому изображению метаданные, такие как EXIF, могут быть добавлены к частям маркера приложения после окончания данных изображения. После этих точек данные изображения открываются и закрываются как с помощью определенных представлений, так и после обработки данных не обрабатываются.
Вот так выглядит GIF-изображение, которое отображается как шестнадцатеричные значения:
Метаданные обычно появляются только при анализе пользователя, и таким же образом скрытый код отображается только в программе, которая ищет код для запуска. Эти данные часто содержат информацию о камере, информацию о местоположении или аналогичную информацию о фотографии, но в этом случае могут быть заполнены другим файлом или исполняемым кодом.
Несмотря на то, что скрытый контент включен в файл изображения, открывающие и закрывающие компоненты содержимого изображения по-прежнему ведут себя как стандартный образ, потому что достаточно отразить изображение программ просмотра изображений и эффективно отображать их.
Как можно использовать эту технику?
Веб-сайты, такие как форумы, хостинг-сайты или другие сайты с созданным пользователем контентом, обычно позволяют загружать изображения и публиковать текстовые или мультимедийные материалы. В некоторых случаях виджет, HTML-сценарий, можно добавлять к сообщениям пользователей или страницам. Сценарий может быть добавлен на сайт, который не должен быть из-за отсутствия проверки формы, используя сценарий escape, такой как HTML. Но даже если вы создаете свою собственную веб-страницу и файлы сценариев с других страниц, и эти сценарии пытаются получить доступ к куки-файлам в домене, где они размещены, дано. По этой причине, в качестве меры предосторожности, большинство сайтов не позволяют загружать файлы, но многие предоставляют вам изображения. И многие из них просто сохраняют изображение и висят (мы можем видеть наши). Поэтому, загружая JavaScript в изображение, он может размещаться на сайте сайта и выполняться с сервера сайта. Кроме того, этот метод значительно усложняет вредоносный JavaScript, даже если он используется на сервере, контролируемом атакующем, и может использоваться для покрытия действий фишинговой страницы.
Что мы можем сделать, запустив JavaScript, размещенный на целевом сервере?
JavaScript может содержать файлы cookie для проверки подлинности, быть уязвимыми для escape-последовательностей XSS для запуска большего количества JavaScript, загрузки программы в браузер и загрузки запросов или даже для кражи информации или кражи файлов cookie с других веб-сайтов, посещенных пользователем (при условии, что целевой компьютер обслуживает интеграцию, доступную на других веб-страницах) ). В этом примере простая проверка JavaScript используется для проверки работоспособности JavaScript, но это может быть изменено вредоносным кодом или даже инструментом BeEF.
1) Загрузка и установка Imagejs
Давайте начнем клон репозитория git проекта. Напишите команды в этих примерах на терминал Kalin Linux.
Как только мы скопировали репозиторий, мы перейдем в каталог, используя приведенную ниже команду.
cd imagejs
Наконец, для компиляции программы мы просто запускаем:
Создаем наш файл JavaScript с нашим кодом. Открываем блокнот и пишем туда для примера
echo «window.alert(«Ifud.ws rulez and putin huilo»);»
Сохраняем файл как script.js.
Код JavaScript может быть встроен в любые файлы GIF, BMP, WEBP, PNM или PGF. Однако, в зависимости от пределов, на которые должно загружаться изображение, мы должны думать о том, как создается изображение.
Если изображение загружается в качестве изображения аватара или профиля на веб-сайте, мы должны убедиться, что файл не слишком большой и разрешение превышает максимальный размер. Если изображение встроенного JavaScript с помощью JavaScript масштабируется или сжимается на веб-сайте, или если сайт удаляет данные EXIF, функция кода не поддерживается; поэтому, если вы загружаете картинку, которая соответствует требованиям сайтов, вы будете записывать ее так, как если бы она была в размещенном месте без каких-либо изменений.
После того, как вы нашли изображение, которое хотите вставить, вы можете разместить их, используя следующие параметры. Этот пример скрипта называется «script.js», а образ «image.jpg» – оба находятся в том же каталоге, что и программа imagejs, которую мы можем проверить, набрав ls.
Открываем блокнот и вставляем туда данный код HTML:
Сохраняем файл как script.html (важно чтоб этот файл и файл script.js.gif находились в одной директории). Открываем файл:
Тег был добавлен, чтобы показать, что изображение GIF по-прежнему полностью функционирует как изображение, но его можно пропустить, и скрипт будет работать.
Выполнение этого из файла работает, потому что файловые системы не задают типы файлов mime. Большинство современных веб-серверов устанавливают тип mime для файлов, которые они обслуживают, на основе расширения файла. Если это так, и веб-сервер задает тип mime изображения (например, image / gif), современные браузеры не будут пытаться запустить сценарий, потому что типы mime изображения не являются исполняемыми.
Теперь, когда мы нашли способ встраивать JavaScript в изображения, мы хотим посмотреть, что мы можем сделать с этим кодом JavaScript и как добавить его на живой веб-сайт.
Следующая команда попытается установить файлы cookie cookie на обслуживаемой веб-странице. Эти файлы cookie могут использоваться для имитации пользователей путем кражи их токенов аутентификации. Теперь, честно говоря, вещи, которые вы просто пишете сканерам, не приведут их к вам (вам придется написать команду, чтобы отправить их на свой сервер), но это будет служить доказательством простой концепции:
Встраиваем изображения — data:URL
Как ты, конечно, знаешь, браузер, встретив в HTML тег img, спешит отправить на сервер запрос для загрузки соответствующей картинки. Примерно так же обстоят дела и с CSS-свойством background-image. В общем, как только встречаем картинку — имеем дополнительный запрос (а это время и трафик, в том числе, потраченный на пересылку HTTP заголовка — около килобайта).
Вот такое вот поле для деятельности оптимизатору. Раз каждая картинка — это отдельный запрос, значит, чтобы уменьшить число запросов, нужно уменьшить число отдельных картинок. Поскольку просто выбросить картинки нам никто не даст, остается их объединять — склеивать разные изображения в один файл. Загружаются склеенные картинки одним запросом, а уже потом выбираются нужные участки (благо в CSS есть свойство background-position). Этот типичный подход описан в статье Спрайты: меньше картинок — больше скорость.
Схема data:URL
Но есть и другой вариант. А что, если картинки не клеить между собой, а внедрять прямо на место их использования — непосредственно в HTML и CSS файлы? Конечно, сами файлы при этом несколько «распухнут» — увеличатся в размерах, но зато все загрузится за один запрос. Выигрыш на заголовках может быть весьма ощутимым! И, как ты увидишь ниже, не только на заголовках.
Технически, такая «вклейка» возможна. Базируется она на схеме data:URL. Эта схема позволяет внедрить на веб-страницу данные так, как если бы они были подключены с помощью вызова внешних файлов. Оговорюсь, внедрение возможно не в произвольном месте, а именно в URL (например, в атрибуте src, тега img).
Сразу предупрежу — метод не панацея. Имеет ряд существенных ограничений. Но, давай по порядку.
Как это выглядит?
Все очень просто. Имеем следующий синтаксис:
На практике получатся примерно такие (или во много раз более длинные) громоздкие, но вполне валидные конструкции:
А что с кроссбраузерностью?
Закономерный вопрос. В настоящее время подавляющее большинство браузеров поддерживают эту технологию. Ни с Firefox, ни с Opera, ни с Safari, ни с IE8+ никаких проблем быть не должно.
Для IE6-7 существует альтернативное решение вставки изображений в виде mhtml-включений. Так что можно сказать, что все популярные браузеры позволяют использовать внедрение изображений.
Как обычно, не откладывая в долгий ящик, демо-пример странички с data:URL для фоновых картинок (CSS) и для тега img (HTML).
Как получить код?
С кроссбраузерностью разобрались, пример посмотрели. Теперь самое время, что бы кто-нибудь сказал: «Стоп! Что ты там за символы понавставлял в примере?! Где ты их взял? У меня же просто картинка! Файлик…».
Не беспокойся. За нас уже поработали. Существуют готовые сервисы, которым можно «скормить» файл-картинку и получить соответствующий ей код.
Про размеры картинок
Так как картинка вставляется в URL, на размер кода накладываются некоторые ограничения. Вообще говоря, по спецификации, браузеры обязаны поддерживать URL не менее 1Kb. На самом деле ситуация гораздо лучше. В ходе эксперимента выяснилось, что блок кода картинки до 32Kb можно смело использовать во всех популярных браузерах (FF, Opera, Chorme, Safari, IE6+).
При размере больше 32Kb пасует IE8. Но, если для IE8 использовать не data: URL, а MHTML-включение (как для IE6-7), то можно использовать код до 64Kb.
Важная оговорка! Цифры, приведенные выше, относятся именно к размеру base64-кода картинки. Фактический размер изначального изображения должен быть примерно на треть меньше. Таким образом, имеем безопасное ограничение для размера встраиваемой картинки примерно 20Kb.
В чем сила, брат?
Итак, можно с помощью специального сервиса превращать картинки в строки кода и вставлять их в HTML/CSS. Но это же уйма дополнительной работы! Для чего это все?
Преимуществ метода достаточно много. Вот перечень показавшихся наиболее существенными:
Обратная сторона медальки
Недостатков тоже хватает:
Где применять?
Перечень достоинств и недостатков весьма внушительный. Однозначно одобрить или забраковать данную технологию невозможно. Просто она имеет ограниченную область применения.
Очевидно, что выигрыш от использования data:URL для CSS существенно больше, чем для HTML (где теряем выгоды от кеша и получаем дополнительный геммор с IE6-7). Так же имеем очень существенную оговорку о максимальном размере закодированного файла (да еще для разных браузеров он разный).
Вывод напрашивается сам собой: встроенные, при помощи data:URL, изображения имеет смысл использовать в CSS, для небольших фоновых картинок — получим выигрыш в быстродействии.
Подходит для высоконагрузочных проектов, или проектов, где важна скорость загрузки.
О производительности
update 23.02.11 by Ksayri Применение data:URL способно существенно снизить (до 10 раз) производительность некоторых браузеров. В частности это касается Firefox (который тратит очень много ресурсов на обработку base64) и IE6-7 (которым приходится иметь дело с mhtml). Чем больше будет data:URL на странице, тем больше будет тормозить.
Поэтому сейчас оптимальным решением будет приметь сначала CSS спрайты, а затем преобразовывать минимальное количество спрайтов в data:URL.
Декодирование JPEG для чайников
[FF D8]
Вам когда-нибудь хотелось узнать как устроен jpg-файл? Сейчас разберемся! Прогревайте ваш любимый компилятор и hex-редактор, будем декодировать это:
Специально взял рисунок поменьше. Это знакомый, но сильно пережатый favicon Гугла:
Последующее описание упрощено, и приведенная информация не полная, но зато потом будет легко понять спецификацию.
Даже не зная, как происходит кодирование, мы уже можем кое-что извлечь из файла.
[FF D8] — маркер начала. Он всегда находится в начале всех jpg-файлов.
Следом идут байты [FF FE]. Это маркер, означающий начало секции с комментарием. Следующие 2 байта [00 04] — длина секции (включая эти 2 байта). Значит в следующих двух [3A 29] — сам комментарий. Это коды символов «:» и «)», т.е. обычного смайлика. Вы можете увидеть его в первой строке правой части hex-редактора.
Немного теории
Закодированные данные располагаются поочередно, небольшими частями:
Каждый блок Yij, Cbij, Crij — это матрица коэффициентов ДКП (так же 8×8), закодированная кодами Хаффмана. В файле они располагаются в таком порядке: Y00Y10Y01Y11Cb00Cr00Y20.
Чтение файла
Файл поделен на секторы, предваряемые маркерами. Маркеры имеют длину 2 байта, причем первый байт [FF]. Почти все секторы хранят свою длину в следующих 2 байта после маркера. Для удобства подсветим маркеры:
Маркер [FF DB]: DQT — таблица квантования
Оставшимися 64-мя байтами нужно заполнить таблицу 8×8.
Приглядитесь, в каком порядке заполнены значения таблицы. Этот порядок называется zigzag order:
Маркер [FF C0]: SOF0 — Baseline DCT
Этот маркер называется SOF0, и означает, что изображение закодировано базовым методом. Он очень распространен. Но в интернете не менее популярен знакомый вам progressive-метод, когда сначала загружается изображение с низким разрешением, а потом и нормальная картинка. Это позволяет понять что там изображено, не дожидаясь полной загрузки. Спецификация определяет еще несколько, как мне кажется, не очень распространенных методов.
Находим Hmax=2 и Vmax=2. Канал i будет прорежен в Hmax/Hi раз по горизонтали и Vmax/Vi раз по вертикали.
Маркер [FF C4]: DHT (таблица Хаффмана)
Эта секция хранит коды и значения, полученные кодированием Хаффмана.
Следующие 16 значений:
Количество кодов означает количество кодов такой длины. Обратите внимание, что секция хранит только длины кодов, а не сами коды. Мы должны найти коды сами. Итак, у нас есть один код длины 1 и один — длины 2. Итого 2 кода, больше кодов в этой таблице нет.
С каждым кодом сопоставлено значение, в файле они перечислены следом. Значения однобайтовые, поэтому читаем 2 байта:
Далее в файле можно видеть еще 3 маркера [FF C4], я пропущу разбор соответствующих секций, он аналогичен вышеприведенному.
Построение дерева кодов Хаффмана
Мы должны построить бинарное дерево по таблице, которую мы получили в секции DHT. А уже по этому дереву мы узнаем каждый код. Значения добавляем в том порядке, в каком указаны в таблице. Алгоритм прост: в каком бы узле мы ни находились, всегда пытаемся добавить значение в левую ветвь. А если она занята, то в правую. А если и там нет места, то возвращаемся на уровень выше, и пробуем оттуда. Остановиться нужно на уровне равном длине кода. Левым ветвям соответствует значение 0, правым — 1.
Деревья для всех таблиц этого примера:
В кружках — значения кодов, под кружками — сами коды (поясню, что мы получили их, пройдя путь от вершины до каждого узла). Именно такими кодами закодировано само содержимое рисунка.
Маркер [FF DA]: SOS (Start of Scan)
Байт [DA] в маркере означает — «ДА! Наконец-то то мы перешли к финальной секции!». Однако секция символично называется SOS.
[00], [3F], [00] — Start of spectral or predictor selection, End of spectral selection, Successive approximation bit position. Эти значения используются только для прогрессивного режима, что выходит за рамки статьи.
Отсюда и до конца (маркера [FF D9]) закодированные данные.
Закодированные данные
Последующие значения нужно рассматривать как битовый поток. Первых 33 бит будет достаточно, чтобы построить первую таблицу коэффициентов:
Нахождение DC-коэффициента
1) Читаем последовательность битов (если встретим 2 байта [FF 00], то это не маркер, а просто байт [FF]). После каждого бита сдвигаемся по дереву Хаффмана (с соответствующим идентификатором) по ветви 0 или 1, в зависимости от прочитанного бита. Останавливаемся, если оказались в конечном узле.
2) Берем значение узла. Если оно равно 0, то коэффициент равен 0, записываем в таблицу и переходим к чтению других коэффициентов. В нашем случае — 02. Это значение — длина коэффициента в битах. Т. е. читаем следующие 2 бита, это и будет коэффициент:
Нахождение AC-коэффициентов
1) Аналогичен п. 1, нахождения DC коэффициента. Продолжаем читать последовательность:
2) Берем значение узла. Если оно равно 0, это означает, что оставшиеся значения матрицы нужно заполнить нулями. Дальше закодирована уже следующая матрица. В нашем случае значение узла: 0x31.
Читать AC-коэффициенты нужно пока не наткнемся на нулевое значение кода, либо пока не заполнится матрица.
В нашем случае мы получим:
Вы заметили, что значения заполнены в том же зигзагообразном порядке? Причина использования такого порядка простая — так как чем больше значения v и u, тем меньшей значимостью обладает коэффициент Svu в дискретно-косинусном преобразовании. Поэтому, при высоких степенях сжатия малозначащие коэффициенты обнуляют, тем самым уменьшая размер файла.
Аналогично получаем еще 3 матрицы Y-канала…
Но! Закодированные DC-коэффициенты — это не сами DC-коэффициенты, а их разности между коэффициентами предыдущей таблицы (того же канала)! Нужно поправить матрицы:
Теперь порядок. Это правило действует до конца файла.
… и по матрице для Cb и Cr:
Вычисления
Квантование
Вы помните, что матрица проходит этап квантования? Элементы матрицы нужно почленно перемножить с элементами матрицы квантования. Осталось выбрать нужную. Сначала мы просканировали первый канал. Он использует матрицу квантования 0 (у нас она первая из двух). Итак, после перемножения получаем 4 матрицы Y-канала:
… и по матрице для Cb и Cr.
Обратное дискретно-косинусное преобразование
Формула не должна доставить сложностей. Svu — наша полученная матрица коэффициентов. u — столбец, v — строка. Cx = 1/√2 для x = 0, а в остальных случаях = 1. syx — непосредственно значения каналов.
Приведу результат вычисления только первой матрицы канала Y (после обязательного округления):
Ко всем полученным значениям нужно прибавить по 128, а затем ограничить их диапазон от 0 до 255:
Например: 138 → 266 → 255, 92 → 220 → 220 и т. д.
YCbCr в RGB
4 матрицы Y, и по одной Cb и Cr, так как мы прореживали каналы и 4 пикселям Y соответствует по одному Cb и Cr. Поэтому вычислять так: YCbCrToRGB(Y[y,x], Cb[y/2, x/2], Cr[y/2, x/2]):
Вот полученные таблицы для каналов R, G, B для левого верхнего квадрата 8×8 нашего примера:
Конец
Вообще я не специалист по JPEG, поэтому вряд ли смогу ответить на все вопросы. Просто когда я писал свой декодер, мне часто приходилось сталкиваться с различными непонятными проблемами. И когда изображение выводилось некорректно, я не знал где допустил ошибку. Может неправильно проинтерпретировал биты, а может неправильно использовал ДКП. Очень не хватало пошагового примера, поэтому, надеюсь, эта статья поможет при написании декодера. Думаю, она покрывает описание базового метода, но все-равно нельзя обойтись только ей. Предлагаю вам ссылки, которые помогли мне: