Carve ‘em all: как выжать данные из почти мертвой файловой системы

  • #Карвинг

О чем материал

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

Ранее материал был представлен в сжатом виде в Telegram-канале ESCalator.

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

Практика показывает, что извлечь данные MFT, логи ОС или юзерские артефакты в состоянии, пригодном для обработки классическими инструментами, можно далеко не всегда. Это порождает необходимость применения карвинга, то есть восстановления/реконструкции поврежденных файлов. В ряде случаев коммерческие продукты, реализующие эту технику, дают неплохие результаты, но в целом их область применения достаточно ограниченна.

Тем не менее шансы есть, значит, нет повода останавливаться!

Единичные записи EVTX 

Для карвинга событий журналов ОС Windows хорошо зарекомендовала себя техника «забудь про BinXML». Единичные записи событий можно искать по следующему шаблону (см. табл. 1) — он обеспечивает соблюдение минимально необходимых условий, в том числе контроль целостности записи, наличие ненулевого тела после исключения 28 служебных байтов, а также «разумный» размер (очевидно, что он не может превышать размера чанка, который составляет 64 КБ).

Смещение, байтРазмер, байтПолеУсловия
04recordSignature= 0x2A2A0000
44recordSize> 0x1C, < 0x10000
88recordNumber 
168writtenTime 
24recordSize-28recordBody 
recordSize-44controlSize= recordSize
Таблица 1. Шаблон для поиска единичных записей EVTX

Для контроля наличия заголовков файлов и чанков в теле анализируемого файла можно отслеживать появление соответствующих сигнатур по мере сканирования — речь о строках «ElfFile»/«ElfChnk». Соответственно, если заголовок чанка не был обнаружен в пределах предыдущих 64 КБ файла до нахождения единичной записи события, велика вероятность, что запись не восстановится (как правило, в результатах коммерческих продуктов и при конвертировании восстановленных EVTX-файлов запись действительно отсутствует).

Полученное при обнаружении записи поле переменной длины recordBody представляет собой описание события в формате BinXML — то есть может использовать ссылки на общие для всего чанка шаблоны (и вообще является достаточно неприятным для синтаксического анализа). Тем не менее зачастую оно начинается с последовательности байтов 0x0f0101000c01 («fragmentHeader» + «templateInstance»), и в таком случае возможно применение следующего шаблона (см. табл. 2).

Смещение, байтРазмер, байтПолеУсловия
01templateSignature= 0x01
14templateID 
54templateDataOffset 
94nextTemplateOffset 
1316templateGUID 
294emplateDataSize 
334fragmentHeader?? = 0x0f010100
Таблица 2. Шаблон для анализа templateInstance

Поскольку поля templateDataOffset и nextTemplateDataOffset содержат смещения относительно начала чанка, воспользоваться ими в ряде случаев невозможно. Соответственно, любой нормальный человек должен их просто игнорировать. Данные для подстановки в шаблон описываются достаточно просто: это четырехбайтовое количество значений и последовательность четырехбайтовых дескрипторов, каждый из которых состоит из двух байтов размера, одного байта типа данных и пустого байта. Сразу после этого набора располагаются собственно описываемые дескрипторами данные. 

Описание типов тоже достаточно прозаично: вплоть до кода 0x15 находятся стандартные строковые, булевы и численные типы, NULL, SID, GUID и DateTime. Это означает, что для извлечения значений-подстановок достаточно провести анализ фрагмента данных, который начинается либо в смещении 33 + templateDataSize (в случае, когда шаблон находится прямо в теле сообщения), либо в смещении 9 — когда он размещается сразу после ссылки на шаблон.

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

Типичный результат описанных выше упражнений в безумии представлен далее. Очевидно, в контексте всего вышеизложенного мы имеем запись о событии из журнала LocalSessionManager с кодом EventID = 21 (начало RDP-сессии) в совокупности с двумя временными метками, названием УЗ и IP-адресом источника подключения. Значение поля ChunkOffset, равное -1, говорит о том, что в пределах предыдущих 64 КБ файла «зашифрованного» образа диска сигнатуры чанка не нашлось, и в полном соответствии с приведенными ранее утверждениями другие инструменты такой записи не обнаружили.

{"Offset":51646126528,"RecordNum":67239,"WrittenTime":"2025-11-02T08:31:59.2354557Z","Size":544,"Body":"[4 0 0 21 null HexInt64=0x0000000000000010 2025-11-02 08:31:59.2354557 +0000 UTC GUID={f420d564-13d8-45be-a22a-792106830000} 1108 34852 67239 0 SID:S-1-1-18 null Microsoft-Windows-TerminalServices-LocalSessionManager GUID={5d896912-022d-40aa-a3a8-4fa5515c76d7} Microsoft-Windows-TerminalServices-LocalSessionManager/Operational [companyX\\user01 117 192.168.5.221]]","ChunkOffset":-1}

В итоге подобная техника позволяет получить существенно больше данных, нежели при попытке восстановления файлов или чанков EVTX. Часто, имея дату, код события EventID, некие строки и тип журнала, вполне можно понять, о чем все-таки идет речь.

Далее представлены результаты восстановления записей по трем зашифрованным файлам жестких дисков виртуальных машин, задействованных в реальных расследованиях (время работы можно оценить из расчета 1 мин / 10 ГБ). 

Объем диска, ГБПримененный злодеями
шифровальщик
Количество восстановленных записейДоля записей вне чанковОшибки целостностиОшибки распознавания фрагмента
50babyk57900432,2 %1296317
137LockBit13528340,45 %989185
59mimic1986165,79 %358941
Таблица 3. Результаты восстановления записей EVTX

Схожий подход может применяться и для живых систем, где атакующие предприняли попытку удаления данных с целью сокрытия следов перемещения.

Записи MFT

Для поиска остаточных данных таблицы MFT (помимо восстановления единичных записей стандартного формата) в ряде случаев неплохо работает техника поиска единичных атрибутов. Иногда они даже в живой системе позволяют обнаружить следы присутствия вредоносных файлов, которых нет в других артефактах — например, в файле подкачки (pagefile.sys).

Смещение, байтРазмер, байтПолеУсловия
04recordSignature= FILE
162sequenceNumber>= 0
202attributeOffset> 40
Таблица 4. Шаблон для поиска записей MFT

В таблице атрибутов, начинающейся в указанном смещении, и следует искать интересующие нас данные. Каждый атрибут начинается со стандартного заголовка (4 байта типа, 4 байта размера и т. д.), а завершается таблица словом 0xFFFFFFFF в поле типа атрибута. В общем-то, этого вполне достаточно: перебирая атрибуты, можно извлечь экземпляры с типами 0x30 и 0x10 — в первом приближении для наших целей этого достаточно. Атрибут 0x30, то есть $FILE_NAME, содержит ID родительской директории, четыре метки времени (C-M-MFTM-A), имя файла и его размеры (выделенный на диске и реальный). Атрибут 0x10, то есть $STANDARD INFORMATION, включает четыре метки времени (C-M-MFTM-A) и еще некоторое количество не слишком ценной информации. Следует отметить, что одной записи MFT могут соответствовать несколько различных атрибутов 0x30, что позволяет контролировать адекватность получаемых данных и корректировать единичные ошибки.

Смещение, байтРазмер, байтПолеУсловия
08parentDirectory>= 0
88creationTime*
168modificationTime*
248mftModificationTime*
328accessTime*
408allocatedSize 
488realSize>= 0
641nameLength> 0, < 1000
Таблица 5. Шаблон для поиска атрибутов $FILE_NAME

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

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

Смещение, байтРазмер, байтПолеУсловия
04recordLength> 60
42majorVersion> 0, < 4
62minorVersion> 0, < 20
328timeStamp*
404changeReason 
524fileAttributes> 0, < 0x500000
562fileNameLength> 0, %2==0, < 300
582fileLengthOffset> 0, < 70
Таблица 6. Шаблон для поиска записей USN
Рисунок 1. История одной успешной атаки в разрезе USN-записей

PE-файлы

Для поиска исполняемых файлов в образе диска хорошо работает тактика определения заголовка PE c последующей оценкой размера файла по совокупному размеру секций. При небольшом усложнении можно реализовать более точный алгоритм, близкий к классической дихотомии: энтропия конечных фрагментов файла определяется «снизу вверх» до момента перехода порогового значения (эмпирически определенное значение составляет 3), при этом размер оценочного фрагмента последовательно уменьшается при соблюдении паддинга. В ряде случаев такой способ позволяет получить исполняемые файлы с совпадением по хешу, однако даже без этого усложнения исполняемые файлы ВПО или их фрагменты прекрасно детектируются с помощью YARA-правил.

Смещение, байтРазмер, байтПолеУсловия
02dosSignature= MZ
604peSignatureOffset> 0, < 0x200
peSignatureOffset4peSignature= PE\x00\x00
peSignatureOffset+62numberOfSections> 0, < 100
peSignatureOffset+84timeStamp 
peSignatureOffset+202optionalHeaderSize> 0, < 0xFFF0

peSignatureOffset+24+

optionalHeaderSize+i*40+20

4+4

rawDataPointer,

rawDataSize

 
Таблица 7. Шаблон для поиска PE-файлов

Файлы реестра

Для восстановления файлов реестра можно использовать поиск по заголовку с определением размера файла по полю HiveBinsDataSize. В ряде случаев такое приближение также позволяет получить набор фрагментов, пригодных для обработки классическими инструментами. Характерной особенностью в этом случае является наличие в заголовке даты модификации и типа файла (primary/log), а также имени файла в UTF16 размером 64 байта. Несмотря на то, что для некоторых потенциально интересных с точки зрения форензики файлов (UsrClass.dat) структура пути не позволяет с достаточной точностью определить исходное размещение, для большинства случаев имеющейся информации вполне достаточно.

Смещение, байтРазмер, байтПолеУсловия
04regSignature= regf
128modificationTime*
204majorVersion< 3
244minorVersion< 10
284fileType< 3
404hiveBinsDatasize 
Таблица 8. Шаблон для поиска файлов реестра

Текстовые фрагменты

Наконец, прекрасно зарекомендовала себя техника поиска в образе диска читаемых текстовых фрагментов, ассоциируемых с ранее выявленным инструментарием атакующих. В ряде случаев она позволяет восстановить точные пути, командлайны и логи использованных инструментов, включая пароли, имена УЗ и другие приятные мелочи. Для решения подобной задачи можно применить, например, алгоритм Ахо — Корасик, но для регистронезависимого и допускающего различные кодировки поиска его нужно модифицировать.

***

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

Ищите, и обрящете, как написано в одной известной книге.

Мы дěлаем Positive Research → для ИБ-экспертов, бизнеса и всех, кто интересуется ✽ {кибербезопасностью}