В марте мы писали об атаках XDSpy на российские организации. LNK-файлы, которые использовались в этой атаке, были модифицированы так, что при их обработке многие парсеры «падали». Например, это касается LECmd, LnkParse3 и ExifTool, который используется на VirusTotal (скриншот 1).
Разберемся, в чем кроется проблема и как ее исправить 🔧
В общих чертах структура LNK-файла выглядит следующим образом:
1.
SHELL_LINK_HEADER с интересующим нас значением LinkFlags. Оно задает присутствие тех или иных структур в LNK-файле и выглядит так:typedef struct
{
uint32 HasLinkTargetIDList : 1; // есть LINKTARGET_IDLIST
uint32 HasLinkInfo : 1; // есть LINKINFO
uint32 HasName : 1; // есть STRING_DATA
uint32 HasRelativePath : 1; // есть STRING_DATA
uint32 HasWorkingDir : 1; // есть STRING_DATA
uint32 HasArguments : 1; // есть STRING_DATA
uint32 HasIconLocation : 1; // есть STRING_DATA
uint32 IsUnicode : 1; // строки в UTF-16
// ... skipped
} LinkFlags;
2. [Optional]
LINKTARGET_IDLIST. 3. [Optional]
LINKINFO.4. [Optional]
STRING_DATA.STRING_DATA — это массив структур
StringData, каждая из которых имеет следующий формат:•
CountCharacters (количество символов в строке, поэтому для юникода количество байт будет CountCharacters × 2).•
String (строка, которая в соответствии с документацией MUST NOT be NULL-terminated).Структура
STRING_DATA содержит Description (Name), RelativePath, WorkingDir, Arguments и IconLocation в том порядке, который указан в LinkFlags.🫱 Именно в
CountCharacters и кроется проблема для парсеров, которые на него полагаются. Чтобы их «уронить» добрые дяденьки устанавливают большое значение в CountCharacters, например 0xFFFF. String делают длиной до 260 символов, а следующую StringData располагают ровно через 260 символов (в UTF-16 это 520 байт).Как оказалось, Windows «под капотом» ограничивает длину строк
Description, RelativePath и WorkingDir 260 символами. В итоге мы имеем следующее:• Парсеры «падают» из-за некорректных значений (считают смещение
0xFFFF и попадают на бессмысленные байты).• Windows ограничивается 260 символами, правильно попадая на следующую
StringData, и запускает LNK-файл без проблем.• Ограничение в 260 символов точно подразумевается для
Description, что было неочевидно, RelativePath и WorkingDir.• Для
Arguments такого ограничения нет или порог сильно выше, что уже логично. При этом в свойствах LNK-файла при нажатии правой кнопкой будет отображаться только 260 первых символов команды.• Windows считает последний байт нулевым, даже если это не так, хотя из документации можно было предположить, что последний символ тоже учитывается.
🕵️♀️ В варианте LNK-файла от XDSpy атакующие «сломали» таким образом строку
WorkingDir и установили ее значение в C:\Windows\System32\<пробелы> с длиной 260 символов (скриншот 2). Здесь можно посмотреть, как выглядит такой файл на VT (скриншот 3).---- WorkingDir ----
0x0 CountCharacters (0XFFFF)
0x2 String (до 0x208) // C:\Windows\System32_______
---- Arguments ----
0x20A CountCharacters // Парсер Windows идет сюда
0x20C String // Arguments
...
0xFFFF RandomData // Обычные парсеры идут сюда
Чтобы исправить это недоразумение, нужно проверять размер
CountCharacters для релевантных строк и ограничивать смещение до следующей StringData 260 символами с учетом флага isUnicode.#tip #win
@ptescalator


