Стеганография 2020: вещь в себе. Прячем иголку в стоге сена.

В наше время шифрование является краеугольным камнем цифровой безопасности и использование его стойких методов отличный...

Стеганография 2020: вещь в себе. Прячем иголку в стоге сена.

В наше время шифрование является краеугольным камнем цифровой безопасности и использование его стойких методов отличный способ защитить свои данные от чужих глаз. Но шифрование – не единственный и не всегда уместный путь защиты.

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

Боишься?

Тогда самое время вспомнить о старой доброй стенганографии. Слово стеганография произошло от греческих
– steganos (тайна) и graphy (запись), посему её можно назвать тайнописью. Её задача: сокрытие самого факта существования секретного сообщения.

Данная наука впервые появилась в Египте. Ее использовали для передачи важной государственной информации. Для этого стригли раба налысо и били бедняге тату на затылке. Когда волосы
отрастали, посланца отправляли в путь.

Например, сообщение может быть скoрыто внутри изображения, или картинка скрыта внутри аудиоклипа или закодирована в блоге или даже в трансляции. Хитрость заключается в том, чтобы сделать “конверт” как не вызывающим подозрений и не привлекающим внимания.

Это нетривиальная задача. Независимо от типа “конверта” и “полезной информации”, которые вы выбираете, факт, внедрения одного файла в другой, неизбежно повлечет аномалии, которые могут быть обнаружены аналитиком. Как и в случае с криптографией, это игра в кошки-мышки между “стеганографами” и “стеганалитистами”. Наиболее эффективные методы часто приводят к низкой пропускной способности в соотношения размера полезной информации и её конверта. Это может вызвать вопросы. Сколько фотографий твоих котиков ты можешь иметь, не вызывая подозрений? Или почему все твои кантаты Баха звучат как со старого патифона?

Еще одно сходство с криптографией состоит в том, что если аналитик выяснит, как извлечь “сообщение” из “конверта”, то тайное станет явным и отрицать отсутствие сокрытой информации уже не получится.

Стеганографию эффективнее всего использовать не вместо криптографии, а вместе с ней. Такое сочетание позволяет скрыть как саму информацию, так и факт ее хранения или передачи. 

Так как же быть?

Надо в буквальном смысле “спрятать иголку в стоге сена”. То есть в том, чтобы спрятать зашифрованное “сообщение” в очень большой объем случайных данных таким образом, чтобы было невозможно определить, какие данные (если они вообще есть!) были внедрены. Естественно, должен существовать алгоритм извлечения отдельных фрагментов “скрытых” данных без выявления других частей.

Поскольку случайные данные по определению уже случайны, у аналитика нет ориентира, с которого начинать разгадывать загадку. Аналитик увидит большое количество случайных данных, но не определит, какие именно данные внедрены.

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

Стеганографическое ПО

Для начала давайте рассмотрим существующее ПО. Далее мы изучим принципы стеганографии в деталях и сами напишем небольшую программу.

DeepSound

DeepSound внедряет файлы любого типа и автоматически рассчитывает доступное для них место в зависимости от размера контейнера и настроек качества аудио. При использовании MP3 доступное место для стегосообщения показывается больше самого контейнера, но это иллюзия. Независимо от исходного формата файла новый контейнер создается только в одном из несжатых форматов: WAV, APE или FLAC. Поэтому размер первоначального контейнера значения не имеет. В итоге сообщение будет занимать какой-то процент от объема несжатого аудиофайла. К сожалению, последняя версия этой программы вышла в ноябре 2015 года.

Программа может просто поместить любой файл внутри музыкального, или предварительно зашифровать его по алгоритму AES с длиной ключа 256 бит. Опытным путем было установлено, что предельная длина пароля — всего 32 символа. Мои обычные пароли были длиннее и приводили к ошибке (an unhandled exception).

В один контейнер можно поместить любое количество файлов, пока не заполнится счетчик свободного места. Его количество зависит от степени качества (то есть вносимых в аудиофайл искажений). Всего доступны три настройки: высокое, обычное и низкое качество. Каждая из них увеличивает полезный объем контейнера вдвое. Однако рекомендую не жадничать и всегда использовать максимальное качество — так сложнее будет обнаружить скрытый файл. Извлекается стегосообщение после выбора соответствующего контейнера вручную. Если использовалось шифрование, то без ввода пароля программа не покажет даже название скрытого файла. Кириллические символы в на- званиях файлов не поддерживаются. При извлечении они заменяются на XXXX, однако на содержимое файла это никак не влияет. DeepSound умеет конвертировать MP3 и CDA, поэтому мы легко можем преобразовать исходный файл из MP3 в WAV и сравнить два контейнера: пустой и заполненный.

Здесь нас ждет приятный сюрприз: размеры файлов идентичны, а их содержимое различается сразу после заголовка. Байты отличаются почти везде через один, причем на малые значения. Скорее всего, перед нами реализация алгоритма LSB (Least Significant Bit — наименьший значащий бит). Суть его в том, что скрываемый файл кодируется как изменения младших битов в отдельных байтах контейнера. Это приводит к небольшим искажениям (изменению оттенка пикселя в BMP и частоты звука в WAV), которые человек обычно не воспринимает. Чем больше контейнер по отношению к скрываемому файлу, тем меньше вероятность обнаружить последний. Такой алгоритм не оставляет явных указателей на наличие внедренного файла. Предположить его наличие может только статистический анализ шумов (акустических, яркостных, цветовых и прочих), но это уже совсем другой уровень стегоанализа. DeepSound уже вполне годится для сокрытия важной информации (кроме гостайны, разумеется). Можно применять и встроенное шифрование, но, насколько качественно оно реализовано, никто не знает, потому что открытого аудита у программы не было. Поэтому надежнее будет предварительно поместить секретные файлы в какой-нибудь надежный криптоконтейнер (например, VeraCrypt), а затем уже прятать его внутри аудиофайла. Если ты будешь использовать в качестве контейнеров уникальные аудиофайлы, то побайтно сравнить их будет не с чем и твою “матрешку” вряд ли кто-нибудь сможет найти. Просто запиши в тот же каталог несколько гигабайтов теплого несжатого звука для лучшей маскировки.

HALLUCINATE

Rомпактная (34 Кб) утилита написана на Java и не требует установки. В качестве контейнера она поддерживает форматы BMP и PNG, что делает ее гораздо удобнее Anubis. Картинки PNG используются сегодня куда чаще, чем BMP. Их полно даже во временных каталогах браузера, поэтому лежать одиноким и очень заметным файлом на диске такой контейнер точно не будет.

Интерфейс у Hallucinate прост и функционален. Требуется выбрать контейнер, указать скрываемый в нем файл и желаемую степень качества итоговой картинки. Доступны восемь вариантов. Чем сильнее огрубляется исходное изображение, тем больше в нем можно спрятать, но тем заметнее становятся артефакты. Выберем в настройках лучшее качество и проиллюстрируем эту разницу, повторив операцию с файлом BMP.

Визуально картинки слева и справа не отличаются. Однако программа Beyond Compare показывает разницу между ними в кадре по центру. Текстовый файл закодирован как изменения яркости отдельных пикселей, равномерно распределенных по всему кадру. Только в самых темных и самых светлых участках они кучкуются плотно. При побайтном сравнении файлов эта же разница в шестнадцатеричном формате выглядит знакомо: тот же алгоритм LSB, что и у DeepSound. Графический файл или звуковой — в данном случае значения не имеет. В оба формата вносятся минимальные искажения, неразличимые без специальных методов сравнения. Обнаружить их без исходного файла (имея на руках только контейнер) довольно сложно. Никаких явных указателей на внедрение стегосообщения он не содержит. Выдает скрытый файл только частотный анализ, но хорошо работает этот метод только для обнаружения крупных «матрешек». Мелкий файл в большой картинке остается практически незаметным. Извлекается скрытый файл буквально в два клика. Достаточно выбрать контейнер (HAL-file в терминологии автора программы), нажать Decode и указать место для сохранения файла.

JHIDE

JHide – еще одна подобная программа на Java. Компактной ее не назовешь, она занимает почти три мегабайта. Однако в отличие от Hallucinate, помимо BMP и PNG, она поддерживает TIFF, а также позволяет использовать защиту паролем.

Сравнение утилитой Beyond Compare показывает едва различимые отличия. В первую секунду их не видно вообще. Надо прибавить яркости и присмотреться, чтобы разглядеть на черном фоне равномерно рассеянные темно-синие точки.

Сравнение в hex-кодах показывает все тот же алгоритм LSB, однако его реализация здесь более удачная. Измененные пиксели не группируются крупными блоками от начала файла, а равномерно рассеяны по всему контейнеру. Так гораздо сложнее обнаружить скрытое сообщение в картинке. При малом размере стегосообщения это практически невозможно сделать, не имея для сравнения оригинал (пустой контейнер).

Программа сама пытается максимально сжать скрываемый файл перед его помещением в контейнер. Поэтому извлекается он всегда в формате ZIP, а спрятанный файл находится уже внутри этого архива. Парольную защиту необходимо деактивировать перед распаковкой вручную — jHide сама не покажет, надо ли его вводить. Это тоже плюс, поскольку исключает возможность использования утилиты для проверки изображений на наличие в них скрытых файлов.

Утилита иногда игнорирует вводимое имя файла и извлекает его с шаблонным именем stego_%name%.bmp, однако эту недоработку можно и простить. Содержимое файла считывается ей без искажений.

OPENPUFF

Самая сложная утилита в этом обзоре — OpenPuff. Ее последняя версия (4.00) поддерживает не только сокрытие одних файлов внутри других, но и работу со стегометками произвольного формата. Ей даже можно выделить несколько процессорных ядер, если предстоит большой объем работы.

В отличие от других утилит, поддерживающих парольную защиту скрываемого сообщения, OpenPuff умеет использовать для шифрования криптографически стойкий генератор псевдослучайных чисел (CSPRNG — Cryptographically secure pseudorandom number generator). Если простого пароля недостаточно, то поставь флажки напротив полей B и C, а затем введи в них три разных пароля длиной от 8 до 32 символов. На их основе CSPRNG сгенерирует уникальный ключ, которым и будет зашифровано сообщение.

Мелкие файлы можно хранить в картинках и аудиозаписях, а крупные (например, криптоконтейнеры) удобнее прятать в видеозаписях — OpenPuff поддерживает MP4, MPG, VOB и множество других форматов. Максимальный размер скрываемого файла — 256 Мбайт.

Применение CSPRNG на малых файлах сильно увеличивает итоговый размер стегосообщения. Поэтому разница между пустым и заполненным контейнером становится слишком очевидной. Мы снова видим, что измененные пиксели в основном распределяются равномерно, однако на самых светлых и самых темных участках они образуют крупные блоки. Если бы таких блоков не было, результат был бы больше похож на артефакты, получаемые при сжатии при помощи JPEG. Побайтное сравнение тоже дает очень характерную картину. Несмотря на малый размер скрываемого файла, в контейнере изменены значения у большинства пикселей. Если jHide хватило 330 байт для записи стегосообщения, то OpenPuff использовал для этой же задачи более 170 Кбайт. 

 С одной стороны, это плюс: нет прямой корреляции между размером сообщения и числом измененных пикселей. Анализ такого контейнера существенно усложняется. С другой стороны, на создание контейнера приходится затратить дополнительные усилия, что может оттолкнуть неискушенного пользователя. Другой режим работы программы — запись и чтение стегометок. Это скрытые строки длиной до 32 символов, которые можно использовать для защиты авторского права. Например, спрятать копирайт в фотографии, музыкальном файле или документе. Работает эта функция исключительно просто. Пишешь произвольную стегометку в верхней части окна и указываешь ниже файлы, в которые ее надо добавить. Исходные файлы останутся нетронутыми, а их копии с меткой сохранятся в указанном тобой каталоге.

При возникновении любых правовых споров просто запускаешь OpenPuff и показываешь изумленному оппоненту ранее внедренную метку.

Сложности возникают в том случае, если файл подвергался модификации. Даже простое конвертирование в другой формат стирает стегометку. Не удается ее считать и в том случае, если файл был снова приведен к исходному формату. Стойкие стегометки существуют, но внедрять их умеют только отдельные программы. Как правило, они привязаны к какому-то конкретному оборудованию (например, модели камеры).

OPENSTEGO

Программа работает в Windows и Linux. Она поддерживает BMP, PNG, JPG, GIF и WBMP. Заполненный контейнер всегда сохраняется в формате PNG. OpenStego занимает всего 203 Кбайт, но после знакомства с Hallucinate это уже не впечатляет. Формально утилита требует установки, хотя может быть легко превращена в портейбл-версию. OpenStego привлекает тем, что поддерживает парольную защиту и тоже умеет внедрять стегометки (правда, эта функция пока что имеет статус «бета»).

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

Интересно, что программа никак не проверяет правильность вводимого пароля при извлечении стегосообщения из контейнера. Она честно пытается собрать извлекаемый файл в любом случае и всегда сообщает, что операция прошла успешно. В действительности же спрятанный файл будет извлечен только после ввода правильного пароля. В остальных случаях произойдет ошибка и файл не запишется. Такой подход слегка затрудняет использование классических методов перебора паролей, в которых следующая комбинация подставляется после неудачной проверки предыдущей. Однако маркер успешного извлечения все же есть. Достаточно указать в качестве каталога пустой и перебирать пароли до тех пор, пока в нем не появится файл. Было бы лучше записывать любой результат извлечения как файл — это бы повысило уровень защиты. Внедрение стегометок в этой программе происходит не как в других. Сначала генерируется сигнатура, которая сохраняется в отдельный файл с расширением SIG. Какую-то осмысленную информацию в ней записать нельзя — это просто уникальный битовый набор, вроде приватного ключа.

После внедрения стегометки создается новый и визуально идентичный файл изображения, в котором она «растворяется». Процесс верификации сводится к проверке наличия указанной сигнатуры внутри файла. Если она сохранилась полностью, то совпадение будет стопроцентным. Если же файл подвергался модификации, то стегометка может быть частично утрачена. Метод задумывался как попытка внедрения стойких водяных знаков, однако в текущей реализации он практически бесполезен. Программа показывает нулевой процент соответствия уже после небольшой обрезки картинки и пересохранения в PNG с большим сжатием.

RARJPEG

Скрывать одни файлы внутри других можно и безо всяких стеганографических утилит. Конечно, это будет не аккуратное «растворение» по алгоритму LSB, а простое слияние, однако у этого известного в узких кругах метода есть и свои преимущества. Во-первых, он доступен без дополнительных инструментов. Во-вторых, позволяет легко передать любой файл, загрузив его как графический на какой-нибудь сайт (например, хостинг или имиджборд). Смысл метода в том, что графические файлы (в частности, JPEG) начина- ют интерпретироваться сразу с заголовка, в то время как архивы считываются только с метки начала архива. Сама метка может располагаться внутри файла где угодно, так как, помимо обычных архивов, есть многотомные и самораспаковывающиеся. В качестве эксперимента упакуем все программы из сегодняшнего обзора в архив ZIP и добавим этот архив к файлу Wallpaper.jpg, создав новую картинку: Wallpaper-x.jpg. Просто запустим консоль Windows и напишем:

type Wallpaper.jpg Steg.zip > wallpaper-x.jpg

На выходе получится объединенный файл wallpaper-x.jpg. Его можно просмотреть как картинку или же открыть любым архиватором, который поддерживает формат ZIP. Если изменить расширение файла на ZIP, то он откроется файловым менеджером как каталог. Можно даже обойтись без переименования, а сразу использовать архивный плагин через команду быстрой распаковки (например, {ALT}+{F9} в Total Commander). Все файлы из такой «картинки» будут извлечены без проблем. Описанный трюк известен давно и работает также с некоторыми другими форматами файлов (как графических, так и архивов), однако наибольшую популярность получило сочетание RAR + JPEG.

Сделай сам

Ок. А как же обстоят дела на практике? Все проще, чем ты думаешь. Для создания собственной стеганографической утилиты нам потребуется знание Python.

Первое, что нам нужно, это очень большой объем случайных данных. В cлучае полномасштабного шифрования это может быть и целый раздел диска, заполненный случайными данными, и названный, например swap. Для целей этой статьи подойдет и просто большой файл.

В линуксе очень просто создать любой объем случайных данных с помощью команды dd. Для тестов будем использовать флешку подключенную как / media/x/y

dd if=/dev/urandom of=/media/x/y/bigfile bs=1M

После выполнения комманды файл займёт всю флешку.

import os
from hashlib import sha256

# вычисляем id блока и hash
def calcblock(bts,numblocks):
    hsh=sha256(bts)
    block=int.from_bytes(hsh.digest(),byteorder='little',signed=False)%numblocks
    return block, bytes(hsh.hexdigest(),"utf-8")

# шифруемся
def encryptdata(value,pwd,blocksize):
    # хэш SHA256 пароля всегда будет 32 байта
    pwsh=sha256(bytes(pwd,"utf-8")).digest()
    if(len(value)<blocksize):
        # добиваем пробелами, если короткий
        val=value.decode("utf-8").ljust(blocksize)
        value=bytes(val,"utf-8")
        return bytes(a ^ b for a, b in zip(value, pwsh))

# расшифровываемся
def decryptdata(value,pwd,blocksize):
    # хэш пароля SHA256 всегда будет 32 байта
    pwsh=sha256(bytes(pwd,"utf-8")).digest()
    if(len(value)<blocksize):
        # добиваем пробелами, если коротко
        val=value.decode("utf-8").ljust(blocksize)
        value=bytes(val,"utf-8")
        try:
            val=bytes(a ^ b for a, b in zip(value, pwsh)).decode("utf-8")
        except:
            val="End of message"
        return val

def writefsys(fsys,fname,pwd,blocksize,numblocks):
    # вычисляем первый блок как хэш именифайла+пароля
    block, hshval=calcblock(bytes(pwd+fname,'utf-8'),numblocks)
    outf=open(fsys,"r+b")
    with open(fname, "rb") as inf:
        while True:
                value = inf.read(blocksize)
                if value == b'':
                    break # конец файла
                # print(“Пишем в блок “+str(block))
                # print(value.decode("utf-8"))
                byts=encryptdata(value,pwd,blocksize)
                outf.seek(block*blocksize)
                outf.write(byts)
                # следующий блок основан на хэше этого блока
                block, hshval=calcblock(hshval,numblocks)
            # это просто маркер файла
    value=bytes("End of message".ljust(blocksize),"utf-8")
    # print(“Writing to block “+str(block))
    # print(value.decode("utf-8"))
    byts=encryptdata(value,pwd,blocksize)
    outf.write(byts)
    inf.close()
    outf.close()

def readfsys(fsys,fname,pwd,blocksize,numblocks):
        value=""
        rc=""
        # вычисляем первый блок как хэш имени файла+пароля
        block, hshval=calcblock(bytes(pwd+fname,'utf-8'),numblocks)
        with open(fsys, "rb") as inf:
            while True:
            #print("читаем из блока "+str(block))
                inf.seek(block*blocksize)
                binarydata=inf.read(blocksize)
                value=decryptdata(binarydata,pwd,blocksize)
                if value.startswith("End of message"):
                    break
            rc += value
            #следующий блок основывается на хэше этого блока
            block, hshval = calcblock(hshval, numblocks)
        inf.close()
        return rc

#запускаемся
if __name__ == '__main__':

fsys = "/media/x/y/bigfile" #файл с нашей файловой системой

fname="./secretmessage.txt #файл с шифруемым сообщением
pwd="To Heloise" #пароль шифрования
bsz=32 #используемый размер блока

fsz=os.path.getsize(fsys) #размер файла fsys
blknum=int(fsz / bsz)-1 #количество блоков в файловой системе
writefsys(fsys, fname, pwd, bsz, blknum) #пишем содержимое файла в файловую систему
msg=readfsys(fsys, fname, pwd, bsz, blknum) #читаем его
print(msg) #показываем шифрованное сообщение

Разбираемся.

Исполнение кода начинается с 86 строки.

Первым делом нам надо подсчитать количество доступных блоков с учетом размера файла и фиксированного размера блока, который мы же и задали.

Нам также надо будет указать файл в который мы хотим спрятать нашу маленькую файловую систему. За это отвечает функция writefsys(). Сначала мы вычисляем ID начального блока путем вычисления хеша SHA256 секретного ключа и имени фала по модулю количества доступных блоков. После этого мы шифруем первый блок данных и записываем его. Наш алгоритм – простейшее XOR шифрование с ключом. Если ты хочешь получить что то более надежное, я рекомендую использовать AES. После этого мы находим ID следующего блока путем подсчета хеша SHA256 предыдущего блока и записываем данные. И так далее.

В результате информация случайно распределяется по файловой системе и если стеганалист не знает имени файла и секретного ключа, возможности восстановить её нет.

Мы можем спрятать сразу несколько файлов вызывая writefsys() с другим именем файла и ключем.

Когда нам нужно извлечь наш файла мы повторяем процесс с точностью до наоборот, используя функцию readfsys(). Как и раньше мы вычисляем ID начального блока используя ключ и имя файла. После этого мы читаем этот блок и расшифровавыем его. Как и прежде хэш SHA256 даст нам нахождение следующего блока и так далее.

Уникальные ключ и имя файла гарантирует нам то, что извлечение одного файла не повлечет за собой извлечение всех остальных. Более того, такой подход даже не даст намека, о том, что остальные файлы существуют. Чувствуете разницу?

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

А может кто то это уже делал?

Да, существует несколько достаточно развитых стеганографических файловых систем, самая известная из которых StegFS. Это файловая система для Linux, основанная на FUSE и реализованная на С.

Существует также идея под названием Mnemosyne. Это стеганографическая система peer-to-peer. Мне не известно об её реализации.

Стеганография – мощный инструмент, для сохранения конфиденциальности данных. Это не шифрование. Но вместе с шифрованием стеганография тебя сможет спасти от многих проблем.

Берегите себя и свою информацию.

Очень злой админ
Очень злой админ Автор статьи

Админ сайта. Публикует интересные статьи с других ресурсов, либо их переводы. Если есть настроение, бывает, что пишет и что-то своё.