Одной из важных особенностей Natch является возможность пометки данных и отслеживания их продвижения по системе. На данный момент можно помечать файлы, сеть (в том числе и локальную), трафик от USB устройств. Способы пометки описаны ниже, воспользоваться ими пользователю нужно после того, как создан проект и записан сценарий работы с объектом оценки. Про запись сценария подробно в разделе Запись сценария, или коротко в разделе Запись сценария работы пошагового руководства по работе с Natch.
Для удобства редактирования конфигурационных файлов сценариев предусмотрена команда natch edit taint.
Будет выведен список доступных сценариев, затем нужный файл откроется в выбранном пользователем
текстовом редакторе.
Natch может помечать отдельные файлы в гостевой системе, в расчет берутся операции чтения и отображения в память.
Для пометки файлов в конфигурационном файле помеченных данных предназначена секция [TaintFile]. В секции присутствует параметр list, куда можно записать список файлов для пометки.
Если файлов больше одного, их необходимо разделять точкой с запятой без пробелов.
Пример:
[TaintFile]
list=sample.txt;hello.txtС помощью символа '*' в конце имени файла можно указать множество файлов для пометки. Например, /home/* -- пометка всех файлов в каталоге home, /dev/tty* - пометка всех данных, вводимых в каждый из терминалов (tty1, tty2 и т.д.). Если путь начинается не с корневого каталога, то он охватывает одноименные пути на разных расположениях. Например, Pictures/1.jpg помечает файлы и на пути /home/user/Pictures/1.jpg, и на пути /home/root/Pictures/1.jpg.
Исполняемые файлы помечаться таким способом не будут, так как они не открываются для чтения пользовательским кодом, а отображаются прямо в адресное пространство ядерными функциями. Аналогично не будет детектироваться работа драйвера жесткого диска с местом хранения заданного файла.
Natch способен помечать весь сетевой трафик, который приходит в виртуальную машину извне. Пометить полностью локальный трафик на данный момент можно через пометку сокетов (подробнее в разделе Пометка сокетов).
Для управления пометкой пакетов используется секция [Ports] конфигурационного файла taint.cfg
(подробнее в разделе Конфигурационный файл для помеченных данных секция Ports).
Пакеты помечаются целиком, вместе с заголовком второго уровня. Для пометки можно фильтровать пакеты по протоколу 3 уровня или по выбранным портам, если используются протоколы TCP или UDP. Список портов можно посмотреть в википедии.
Возможны следующие варианты работы:
- Помечать все входные пакеты. ip_protocol=-1 + (dst=-1 или src=-1)
- Помечать все UDP-пакеты. ip_protocol=17 + (dst=-1 или src=-1)
- Помечать все TCP-пакеты. ip_protocol=6 + (dst=-1 или src=-1)
- Помечать все HTTP-пакеты от внешнего веб-сервера. ip_protocol=6 + src=80
- Помечать все SNMP-пакеты к внутреннему серверу. ip_protocol=17 + dst=161
- Помечать все ICMP-пакеты. ip_protocol=1
- Помечать другие IP-пакеты, относящиеся к выбранному протоколу 3 уровня. ip_protocol=x, где x можно взять из таблицы
При необходимости отслеживать трафик по всем портам, в полях dst/src секции [Ports] следует указать значение -1. Если хотя бы в одном поле будет -1, будет отслеживаться весь трафик.
Если трафик зашифрован, то его пометка не даёт ожидаемого результата, потому что раскодирование данных меняет их, а пометки в таком случае сбрасываются.
В Natch обрабатываются некоторые из функций декодирования, чтобы сохранить пометки. В дальнейшем эти возможности будут вынесены в конфигурационные файлы или скрипты.
Сейчас помечаются выходные данные следующих функций, если их аргументы помечены:
inflateиз библиотекиlibzEVP_DecryptUpdateиevp_EncryptDecryptUpdateиз библиотекиlibcryptossl_open_app_dataиз библиотекиlibthird_party_boringsslu_strToLowerиu_strToUpperиз библиотекиlibicuuccrypto/tls.(*halfConn).decryptиcrypto/tls.(*halfConn).encryptиз библиотеки языка Gomygithub.libinneed.workers.dev/klauspost/compress/s2.DecodeDigitsToUInt64иTranscodeToUtf8из .NET
Если нужно избежать пометки заголовков пакетов, или помечать небольшую часть полезной нагрузки, или фильтровать пакеты не только по порту, нужно использовать параметр json в секции [Ports].
Этот файл содержит массив записей, описывающий пометку фрагментов пакетов. Каждая запись должна иметь такие поля:
- Поле
icount. Шаг сценария, во время которого был получен пакет. - Поле
id. Номер пакета, если на одном шаге сценария пришло несколько пакетов. - Поле
offset. Смещение помечаемого фрагмента пакета. - Поле
size. Размер помечаемого фрагмента пакета.
Все записи должны быть отсортированы по icount + id. Если для одного пакета нужно пометить несколько фрагментов, должно быть несколько записей в файле, по одной на фрагмент.
Значения icount и id можно получить из файла network.json, где они перечислены в том же порядке, как и пакеты в файле network.pcap.
Удобнее всего генерировать json файл для пометки с помощью скрипта, читающего содержимое пакетов и их метаданные из network.json. Ниже приведён пример такого скрипта. Он генерирует конфигурационный файл для пометки полезной нагрузки (без заголовков) TCP-пакетов, приходящих на порт 8000.
Скрипт создаёт файл с названием, переданным в качестве третьего параметра. Этот файл должен быть размещён в каталоге сценария (где лежит taint.cfg) и прописан в качестве параметра json секции [Ports].
#!/usr/bin/python3
import sys
import json
import dpkt
filtered = []
#########################
def taint_tcp_payload(eth, meta):
offset = eth.__hdr_len__ + eth.ip.__hdr_len__ + len(eth.ip.opts) + eth.ip.tcp.__hdr_len__ + len(eth.ip.tcp.opts)
filtered.append({'icount': meta['icount'], 'id': meta['id'], 'offset': offset, 'size': len(eth.ip.tcp.data)})
#########################
if len(sys.argv) < 4:
print(f'usage: {sys.argv[0]} network.pcap network.json filtered.json')
exit()
pcap = []
with open(sys.argv[1], 'rb') as f:
pcap = dpkt.pcap.Reader(f).readpkts()
jsdata = []
with open(sys.argv[2], 'r') as file:
jsdata = json.load(file)
if len(pcap) != len(jsdata):
print('Error: Len of pcap and json differ')
exit()
# перебор всех существующих пакетов
for i in range(len(pcap)):
js = jsdata[i]
ts, buf = pcap[i]
# обработка только непустых TCP-пакетов
eth = dpkt.ethernet.Ethernet(buf)
if not isinstance(eth.data, dpkt.ip.IP):
continue
ip = eth.data
if not isinstance(ip.data, dpkt.tcp.TCP):
continue
tcp = ip.data
if not tcp.data or len(tcp.data) == 0:
continue
# выбираем нужные пакеты и добавляем к списку отфильтрованных
if tcp.dport == 8000:
taint_tcp_payload(eth, js)
# вывод конфига для Natch
with open(sys.argv[3], 'w') as file:
file.write(json.dumps(filtered, indent=4))На данный момент поддерживается только для Linux.
В Natch реализована возможность пометки локальных сокетов. Для этого используется поле list (то же что и для пометки файлов) в секции TaintFile конфигурационного файла помеченных данных. Чтобы помечать сокеты, следует указать название нужного протокола в качестве имени файла. Поддерживаются следующие варианты: TCP, UDP, TCPv6, UDPv6, UNIX. Это имена, которые присваиваются файлам сокетов самим ядром Linux.
По умолчанию будет выполняться пометка всех сокетов указанного протокола. Предусмотрела возможность фильтрации с помощью следующего синтаксиса:
<socket_name>|parameters;
Параметры следует указывать в определенном виде.
Для сетевых сокетов указывается ip адрес в традиционной форме записи IPv4 или IPv6 (например, 127.0.0.1 и ::FFFF:10.0.2.2). Через пробел опционально указывается порт сокета. Подразумевается, что эти адрес и порт относятся к месту назначения, с которым данный сокет взаимодействует. Пометка по параметрам "слушающих" сокетов на текущий момент не реализована, поэтому при необходимости помечать сокеты, создаваемые сервером для каждого клиента, необходимо указать выдаваемые им системой номера портов.
Для UNIX сокетов после вертикальной черты указывается соответствующее их адресу имя файла или уникальная строка. Указать для пометки конкретный безымянный UNIX сокет на текущий момент не представляется возможным.
Ниже приведены примеры, указываемых имен файлов:
TCPv6 # пометка всех tcpv6 сокетов
UDPv6|2a00:1450:4010:c0b::71 0 # пометка udpv6 сокета по ip адресу и порту
UDP|127.0.0.1 # пометка udp сокетов по ip адресу
TCP|173.194.220.105 80 # пометка tcp сокетов по ip адресу и порту
UNIX|server123.sock # пометка unix сокетов по имени
Пример секции TaintFile с указанными сокетами:
[TaintFile]
list=TCP|173.194.220.105 80;UDP|127.0.0.1;UNIX|server123.sock;UDPv6|2a00:1450:4010:c0f::69В Natch есть возможность помечать пакеты от реальных USB устройств, "проброшенных" в гостевую систему. При этом можно отследить весь путь данных, начиная с драйверов и ядра и заканчивая прикладным ПО.
У используемого подхода есть ряд ограничений:
- Асинхронные устройства (мышь, клавиатура и т.п.) не поддерживаются. Можно подключать камеры, флешки, ключи шифрования.
- Устройство должно быть подключено с самого начала работы эмулятора. То есть, в командной строке.
Для проброса USB устройств в виртуальную машину потребуется сделать ряд действий:
- В хостовой системе создать файл /lib/udev/rules.d/90-udev.rules с содержимым:
SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", MODE="0666"- Выполнить команды:
sudo udevadm control --reload
sudo udevadm triggerТак же помимо обычной настройки конфигурационных файлов, в этом случае требуется вручную внести изменения в скрипты запуска инструмента.
Для работы с USB устройством в файл qemu_opts.ini (в конфигурацию запуска виртуальной машины) необходимо добавить строку:
-usb -device usb-ehci,id=ehci -device usb-host,hostbus=X,hostaddr=Y,bus=ehci.0
Где вместо X и Y у параметров hostbus и hostaddr соответственно нужно указать параметры реального USB устройства, которое будет проброшено в
виртуальную машину.
Узнать эти параметры можно с помощью команды lsusb.
Пример вывода команды:
Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 059: ID 05ac:12a8 Apple, Inc. iPhone5/5C/5S/6
Bus 001 Device 056: ID 0951:1665 Kingston Technology Digital DataTraveler SE9 64GB
Bus 001 Device 055: ID 05e3:0608 Genesys Logic, Inc. Hub
В этом выводе параметр Bus это hostbus, а Device -- hostaddr.
Если мы хотим пробросить флешку Kingston из представленного вывода, то командная строка будет выглядеть следующим образом:
-usb -device usb-ehci,id=ehci -device usb-host,hostbus=1,hostaddr=56,bus=ehci.0
Строка будет одинаковой как для режима записи, так и для воспроизведения, однако, в режиме воспроизведения устройство не будет подключено, поэтому при переподключении устройства или его отсутствии нет необходимости править параметры.
После того как сценарий был записан, необходимо раскомментировать (или добавить) в taint.cfg секцию USB:
[USB]
on=true