Недавно узнал о новой технике, которую стали использовать злоумышленники для обхода средств защиты. Заcчёт того, что разные программы для обработки ZIP архивов обрабатывают объединенные архивы по-разному такая атака и появилась в вооружении злоумышленников. Прежде всего нужно немного разобраться как устроена структура zip файлов и программ архиваторов.
Из чего состоит zip архив?
Он состоит из трех структур.
Общая структура zip файла представленная в программе 010 editor
Пример записи файлов, содержащие метаданные о сжатых файлах
Пример центрального каталога
Пример конца записи центрального каталога
Атака
Разобравшись со структурой, перейдем к с самой атаке. На примере трех архиваторов: 7zip, WinRAR, проводник файлов Windows увидим разницу в их интерпретации объединенных архивов.
Далее рассмотрим, как ведут себя программы при открытии созданного нами архива.
1. Проводник файлов Windows. Не справляется с поставленной задачей и не открывает нам наши файлы.
2. 7zip. Как мы видим, отображается содержимое только первого архива test.zip
3. WinRAR. Раскрывает malware.zip, считывая второй центральный каталог, и отображает файл, в котором у нас содержится «полезная» нагрузка.
Как можно заметить из структуры нашего комбинированного архива, теперь каждая структура идет не по порядку, как это было с архивом, состоящим из файлов (record [0], record [1], dirEntry [0], dirEntry [1], endlocator), а сначала идет структура первого zip файла, а затем уже полностью второго (record [0], dirEntry [0], endlocator [0], record [1], dirEntry [1], endlocator [1]). Именно поэтому разные архиваторы по-разному обрабатывают такой архив.
Структура combined.zip архива
Таким образом, средства обнаружения могут не «ругаться» на объединенные архивы, не найдя в них угрозы. Однако, она легко может там скрываться!
Защититься, можно используя рекурсивные алгоритмы распаковки, в отведенной для тестирования среде. Так, разархивировав архив и получив файлы по отдельности можно вычислить из них хэши и проверить уже их в различных песочницах (VirusTotal, AnyRun). К примеру, можно использовать представленный ниже скрипт на языке Python. На вход передаем наш комбинированный архив и создаем пустую папку для выходных файлов. Результат: получаем все наши созданные файлы в первозданном виде!
Результат работы программы
Алгоритм
Из чего состоит zip архив?
Он состоит из трех структур.
- Записи файлов, в которых представлены сами сжатые файлы или папки. В каждой записи содержатся метаданные (алгоритм сжатия, дата и время создания, название, размер)
- Центральный каталог служит индексом архива. Он содержит центральный заголовок файла для каждого элемента архива, копию метаданных из локального заголовка файла, а также дополнительную информацию, такую как смещение элемента в Zip-файле. Состоит из центральных заголовков файлов, записанных друг за другом, по одному для каждого элемента архива.
- В конце файла, сразу после центрального каталога следует запись EOCD, обозначающая конец записи центрального каталога. Она содержит размер и позицию центрального каталога, а также может содержать комментарии обо всём архиве. Это начальная точка чтения zip файла.
Общая структура zip файла представленная в программе 010 editor
Пример записи файлов, содержащие метаданные о сжатых файлах
Пример центрального каталога
Пример конца записи центрального каталога
Атака
Разобравшись со структурой, перейдем к с самой атаке. На примере трех архиваторов: 7zip, WinRAR, проводник файлов Windows увидим разницу в их интерпретации объединенных архивов.
- Используем следующие команды для создания файлов:
echo "text1" > first.txt
echo "malware" > second.txt - Создаем архивы с нашими файлами:
7zz a test1.zip first.txt
7zz a malware.zip second.txt - Создаем общий архив внутри которого лежат 2 архива:
cat test1.zip malware.zip > combined.zip
Далее рассмотрим, как ведут себя программы при открытии созданного нами архива.
1. Проводник файлов Windows. Не справляется с поставленной задачей и не открывает нам наши файлы.
2. 7zip. Как мы видим, отображается содержимое только первого архива test.zip
3. WinRAR. Раскрывает malware.zip, считывая второй центральный каталог, и отображает файл, в котором у нас содержится «полезная» нагрузка.
Как можно заметить из структуры нашего комбинированного архива, теперь каждая структура идет не по порядку, как это было с архивом, состоящим из файлов (record [0], record [1], dirEntry [0], dirEntry [1], endlocator), а сначала идет структура первого zip файла, а затем уже полностью второго (record [0], dirEntry [0], endlocator [0], record [1], dirEntry [1], endlocator [1]). Именно поэтому разные архиваторы по-разному обрабатывают такой архив.
Структура combined.zip архива
Таким образом, средства обнаружения могут не «ругаться» на объединенные архивы, не найдя в них угрозы. Однако, она легко может там скрываться!
Защититься, можно используя рекурсивные алгоритмы распаковки, в отведенной для тестирования среде. Так, разархивировав архив и получив файлы по отдельности можно вычислить из них хэши и проверить уже их в различных песочницах (VirusTotal, AnyRun). К примеру, можно использовать представленный ниже скрипт на языке Python. На вход передаем наш комбинированный архив и создаем пустую папку для выходных файлов. Результат: получаем все наши созданные файлы в первозданном виде!
Результат работы программы
Алгоритм
Код:
import os
import zipfile
def split_and_extract_combined_zip(file_path, output_dir):
with open(file_path, "rb") as f:
content = f.read()
zip_signature = b"\x50\x4B\x03\x04"
zip_positions = []
pos = content.find(zip_signature)
while pos != -1:
zip_positions.append(pos)
pos = content.find(zip_signature, pos + 1)
if not zip_positions:
print(f"Файл {file_path} не содержит ZIP-архивов.")
return
print(f"Найдено {len(zip_positions)} ZIP-архива в файле {file_path}.")
for i, start_pos in enumerate(zip_positions):
end_pos = zip_positions[i + 1] if i + 1 < len(zip_positions) else len(content)
archive_data = content[start_pos:end_pos]
temp_zip_path = os.path.join(output_dir, f"archive{i + 1}.zip")
with open(temp_zip_path, "wb") as temp_zip_file:
temp_zip_file.write(archive_data)
extract_to = os.path.join(output_dir, f"files_{i + 1}")
os.makedirs(extract_to, exist_ok=True)
with zipfile.ZipFile(temp_zip_path, "r") as zip_ref:
zip_ref.extractall(extract_to)
for root, _, files in os.walk(extract_to):
for file in files:
nested_zip_path = os.path.join(root, file)
if zipfile.is_zipfile(nested_zip_path):
split_and_extract_combined_zip(nested_zip_path, os.path.join(extract_to, "nested"))