Snaffler

Post-Exploitation v1.0.244 · 27.02.2026 активный

Инструмент для обнаружения файловых ресурсов в сети Windows — помогает находить потенциально открытые файлы и учётные данные.

v1.0.244
27.02.2026 current

Установка
git clone https://github.com/SnaffCon/Snaffler.git
cd Snaffler
dotnet build -c Release
показать оригинал переведено ИИ

Snaffler

ko-fi

A dictionary definition of "snaffle".

Для чего это нужно?

Snaffler — это инструмент для пентестеров и красных команд, который помогает находить вкусные иголки (в основном учётные данные, но он гибкий) в куче ужасно скучных стогов сена (огромной среде Windows/AD).

Возможно, он также будет полезен другим людям для других задач, но он явно НЕ предназначен для использования в качестве инструмента «аудита».

Я не хочу читать всё это!!!

Фух, ладно. Но мы не несем ответственности за результаты. Мы написали для вас весь этот текст, но это нормально. Мы не злы, просто разочарованы.

snaffler.exe -s -o snaffler.log

Что он делает?

В общем и целом — он получает список Windows-компьютеров из Active Directory, затем распускает свои щупальца Snaffler по всем ним, чтобы понять, на каких из них есть сетевые папки и можно ли их читать.

Затем ЕЩЁ БОЛЬШЕ щупалец Snaffler перечисляет все файлы в этих папках и использует ИСКУССТВЕННЫЙ ИНТЕЛЛЕКТ МАШИН для определения, какие из них могут заинтересовать маленького грязного хакера вроде вас.

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

Как это выглядит?

Вот так!

Как им пользоваться?

Если вы «просто запустите EXE на компьютере, присоединённом к домену, в контексте пользователя домена» (как это делали с Grouper2, сразу после чего запускали его со всеми флагами подробного вывода/отладки, чтобы он выплёвывал сотни мегабайт трассировок стека), то он, по сути, ничего не сделает. Это наша шуткаTM над теми, кто не читает README-файлы, потому что мы монстры.

ОДНАКО... если вы добавите правильные заклинания, он активирует упомянутый И.И.М., и пути к файлам, где могут находиться «вкусности», выпадут сами собой.

Основные заклинания:

-o Включает вывод результатов в файл. Скорее всего, это вам нужно, если вы не используете -s. Например: -o C:\users\thing\snaffler.log

-s Включает вывод результатов в стандартный вывод, как только они найдены. Скорее всего, это вам нужно, если вы не используете -o.

-v Управляет уровнем детализации. Доступные опции: Trace (максимальная детализация), Debug (менее детализированный, меньше ошибок), Info (ещё менее детализированный, по умолчанию) и Data (только результаты). Например: -v debug

-m Включает и назначает каталог для вывода, чтобы Snaffler автоматически копировал (или «Snaffлил», если угодно) все найденные файлы, которые ему понравились.

-l Максимальный размер файлов (в байтах) для «Snaffлинга». По умолчанию — 10000000, что примерно 10 МБ.

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

-n Отключает обнаружение компьютеров, принимает список хостов через запятую или файл с хостами для поиска сетевых папок и файлов. Примечание: если вы указываете файл, нужно передать путь, например C:\targets.txt или .\targets.txt.

-y Форматирует вывод в виде TSV.

-b Пропускает правила И.И.М., которые находят менее интересные вещи. Настраивается числом от 0 до 3.

-f Ограничивает Snaffler поиском сетевых папок через DFS (Distributed File System) — это должно быть намного незаметнее, чем по умолчанию, при этом охватывая самые большие сетевые папки во многих организациях.

-a Пропускает перечисление файлов, просто выдаёт список доступных для чтения сетевых папок на целевых хостах.

-u Заставляет Snaffler получать список имён учётных записей из AD, выбирать те, которые выглядят наиболее интересными, и использовать их в правиле поиска.

-d Домен для поиска компьютеров, на которых нужно искать сетевые папки, а в них — файлы. Просто.

-c Контроллер домена для запроса списка компьютеров домена.

-r Максимальный размер файла (в байтах) для поиска интересных строк внутри. По умолчанию — 500 КБ.

-j Сколько байт контекста показывать до и после найденных строк в файлах, например -j 200.

-z Путь к файлу конфигурации, который определяет все вышеперечисленные параметры и многое другое! Подробности ниже. Используйте -z generate, чтобы создать пример файла конфигурации .\default.toml.

-t Тип лога, который вы хотите получить на выходе. На данный момент поддерживаются опции: plain и JSON. По умолчанию — plain.

-x Максимальное количество потоков для использования. Не устанавливайте значение ниже 4, иначе всё сломается.

-p Путь к каталогу с файлами правил в формате .toml. Snaffler загрузит все эти файлы вместо стандартного набора правил.

Что означают все эти записи в логе?

Надеемся, что этот аннотированный пример поможет:

Эту запись в логе следует читать примерно слева направо так:

  • примерно в 7:37
  • Snaffler нашёл файл, который, по его мнению, заслуживает вашего внимания
  • он оценил его как "Красный", второй по интересности уровень
  • он соответствует правилу с именем "KeepConfigRegexRed"
  • вы можете его прочитать, но не изменить
  • точное регулярное выражение, которое совпало, находится в красном поле
  • размер файла — 208 кБ
  • он был последний раз изменён 10 января 2020 года в 15:45
  • файл можно найти по пути в фиолетовом поле

... а остальная часть строки (серая) — это небольшой фрагмент контекста из файла, где было найдено совпадение.

В этом случае мы нашли значения ASP.NET validationKey и decryptionKey, которые могут позволить нам выполнить RCE в веб-приложении через какие-то уязвимости десериализации. Ура!

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


Как инструмент решает, какие файлы хорошие, а какие скучные?

Ответ «так просто, что это почти ложь»:

Каждый метод поиска файлов по магии L.A.I.M. делает следующее:

  • Поиск по точному совпадению расширения файла: любой файл с расширением, соответствующим соответствующему словарю, будет возвращён. Это предназначено для расширений файлов, которые почти всегда содержат «вкусности», например .kdbx, .vmdk, .ppk и т. д.
  • Поиск по точному совпадению имени файла (регистр не учитывается). Это предназначено для имён файлов, которые почти всегда содержат «вкусности», например id_rsa, shadow, NTDS.DIT и т. д.
  • Поиск по точному совпадению расширения файла (ещё один словарь), ЗАТЕМ «grep» содержимого любых совпадающих файлов на наличие определённых ключевых слов (ещё один словарь). Это предназначено для расширений файлов, которые иногда содержат «вкусности», но где, скорее всего, будет много мусора, который нужно отсеять. Например, web.config иногда содержит учётные данные базы данных, но часто содержит скучную конфигурацию IIS и не содержит паролей. Это, например, найдёт все файлы с расширением .config, а затем будет искать в них строки, включая, но не ограничиваясь: connectionString, password, PRIVATE KEY и т. д.
  • Поиск по частичному совпадению имени файла (о боже, ещё больше словарей). Это в основном предназначено для поиска файлов вроде Jeff's Password File 2019 (Copy).docx или Privileged Access Management System Design - As-Built.docx, путём совпадения с любым файлом, имя которого содержит подстроки passw, handover, secret, secure, as-built и т. д.
  • Также есть списки исключений, чтобы пропускать все файлы с определёнными расширениями или любые файлы, путь к которым содержит заданную строку.

Реальный ответ:

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

Эти правила могут быть очень простыми, например: «если у файла расширение .kdbx, сообщи мне о нём» или «если путь содержит windows\sxs, то перестань просматривать подкаталоги и файлы в этом пути».

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

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

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


* второе правило ищет внутри файлов с помощью регулярных выражений, предназначенных для поиска жестко закодированных учетных данных в коде PowerShell. * третье правило ищет внутри файлов с помощью регулярных выражений, предназначенных для поиска жестко закодированных учетных данных в командах cmd.exe, которые можно найти в файлах .bat или .cmd, так как они также часто используются в скриптах PowerShell.

Этот подход также позволяет поддерживать относительно управляемый и читаемый набор правил, а кроме того, значительно упрощает для конечного пользователя (вас) настройку стандартных правил или создание собственных наборов правил.

Я не хочу писать правила, это звучит сложно и скучно.

Вы правы, так и было.

Snaffler поставляется с набором правил по умолчанию, встроенных в .exe. Вы можете увидеть их в ./Snaffler/SnaffRules/DefaultRules.

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

Без проблем, вы огромный чудак. У вас есть 2 варианта.

  1. Отредактируйте или замените правила в каталоге DefaultRules, затем соберите новую версию Snaffler. Файлы .toml в этом каталоге будут встроены в .exe в качестве ресурсов и загружены во время выполнения, если вы не укажете другие правила для использования.
  2. Создайте каталог и поместите туда кучу своих файлов правил, затем запустите Snaffler с параметром -p .\path\to\rules. Snaffler проанализирует все файлы .toml в этом каталоге и использует полученный набор правил. Это также сработает, если у вас все правила будут в одном большом файле .toml.

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

Это пример правила, которое заставит Snaffler игнорировать все файлы и подкаталоги ниже каталога с определённым именем.

[[ClassifierRules]]
EnumerationScope = "DirectoryEnumeration" # This defines which phase of the discovery process we're going to apply the rule.
                                          # In this case, we're looking at directories.
                                          # Valid values include ShareEnumeration, DirectoryEnumeration, FileEnumeration, ContentsEnumeration
RuleName = "DiscardLargeFalsePosDirs" # This can be whatever you want. We've been following a rough naming scheme, but you can call it "Stinky" if you want. ¯\_(ツ)_/¯
MatchAction = "Discard"# What to do with things that match the rule. In this case, we want to discard anything that matches this rule.
                        # Valid options include: Snaffle (keep), Discard, Relay (example of this below), and CheckForKeys (example below)
Description = "File paths that will be skipped entirely." # Not used in the code, just a place for notes really.
MatchLocation = "FilePath" # What part of the file/dir/share to look at to check for a match. In this case we're looking at the whole path.
                           # Valid options include: ShareName, FilePath, FileName, FileExtension, FileContentAsString, FileContentAsBytes,
                           # although obviously not all of these will apply in all EnumerationScopes.
WordListType = "Contains" # What matching logic to apply, valid options are: Exact, Contains, EndsWith, StartsWith, or Regex.
                          # Under the hood these all get turned into regexen one way or another.
MatchLength = 0
WordList = [
  # A list of strings or regex patterns to use to match. If using regex patterns, WordListType must be Regex.
    "\\\\puppet\\\\share\\\\doc",
    "\\\\lib\\\\ruby",
    "\\\\lib\\\\site-packages",
    "\\\\usr\\\\share\\\\doc",
    "node_modules",
    "vendor\\\\bundle",
    "vendor\\\\cache",
    "\\\\doc\\\\openssl",
    "Anaconda3\\\\Lib\\\\test",
    "WindowsPowerShell\\\\Modules",
    "Python27\\\\Lib"
]
Triage = "Green" # If we find a match, what severity rating should we give it. Valid values are Black, Red, Yellow, Green. This value is ignored for Discard MatchActions.

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

В этом случае я в основном избавляюсь от шрифтов, изображений, CSS и т. д.

[[ClassifierRules]]
EnumerationScope = "FileEnumeration" # We're looking at the actual files, not the shares or dirs or whatever.
RuleName = "DiscardExtExact" # just a name
MatchAction = "Discard" # We're discarding these
MatchLocation = "FileExtension" # This time we're only looking at the file extension part of the file's name.
WordListType = "Exact" # and we only want exact matches.
WordList = [".bmp", ".eps", ".gif", ".ico", ".jfi", ".jfif", ".jif", ".jpe", ".jpeg", ".jpg", ".png", ".psd", ".svg", ".tif", ".tiff", ".webp", ".xcf", ".ttf", ".otf", ".lock", ".css", ".less"] # list of file extensions.

Вот пример очень простого правила для того, что нам нравится и что мы хотим сохранить.

[[ClassifierRules]]
EnumerationScope = "FileEnumeration" # Still looking at files
RuleName = "KeepExtExactBlack" # Just a name
MatchAction = "Snaffle" # This time we are 'snaffling' these. This usually just means send it to the output,
                       # but if you turn on the appropriate option it will also grab a copy.
MatchLocation = "FileExtension" # We're looking at file extensions again
WordListType = "Exact" # With Exact Matches
WordList = [".kdbx", ".kdb", ".ppk", ".vmdk", ".vhdx", ".ova", ".ovf", ".psafe3", ".cscfg", ".kwallet", ".tblk", ".ovpn", ".mdf", ".sdf", ".sqldump"] # and a bunch of fun file extensions.
Triage = "Black" # these are all big wins if we find them, so we're giving them the most severe rating.

Это правило практически то же самое, но мы смотрим на всё имя файла. Просто!

[[ClassifierRules]]
EnumerationScope = "FileEnumeration"
RuleName = "KeepFilenameExactBlack"
MatchAction = "Snaffle"
MatchLocation = "FileName"
WordListType = "Exact"
WordList = ["id_rsa", "id_dsa", "NTDS.DIT", "shadow", "pwd.db", "passwd"]
Triage = "Black"

А это правило немного хитрее, посмотрите на это...

[[ClassifierRules]]
EnumerationScope = "FileEnumeration" # we're looking for files...
RuleName = "KeepCertContainsPrivKeyRed"
MatchLocation = "FileExtension" # specifically, ones with certain file extensions...
WordListType = "Exact"
WordList = [".der", ".pfx"] # specifically these ones...
MatchAction = "CheckForKeys" # and any that we find, we're going to parse them as x509 certs, and see if the file includes a private key!
Triage = "Red" # cert files aren't very sexy, and you'll get huge numbers of them in most wintel environments, but this check gives us a way better SNR!

Хорошо, вот где начинается самое интересное. У нас здесь пара правил в цепочке.

Файлы с расширениями, соответствующими первому правилу, будут отправлены ко второму правилу, которое будет искать в них (т. е. String.Contains()) содержимое из определённого списка слов.

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

[[ClassifierRules]]
EnumerationScope = "FileEnumeration" # this one looks at files...
RuleName = "ConfigGrepExtExact"
MatchLocation = "FileExtension" # specifically the extensions...
WordListType = "Exact"
WordList = [".yaml", ".xml", ".json", ".config", ".ini", ".inf", ".cnf", ".conf"] # these ones.
MatchAction = "Relay" # Then any files that match are handed downstream...
RelayTargets = ["KeepConfigGrepContainsRed"] # To the rule with this RuleName! This can also be an array of RuleNames if you want to get real wild and start writing branching rulesets.

[[ClassifierRules]]
RuleName = "KeepConfigGrepContainsRed" # Anyway, this is the target rule. Following a naming convention really helps to make sure you're using the right targets.
EnumerationScope = "ContentsEnumeration" # this one looks at file content!
MatchAction = "Snaffle" # it keeps files that match
MatchLocation = "FileContentAsString" # it's looking at the contents as a string (rather than a byte array)
WordListType = "Contains" # it's using simple matching
WordList = ["password=", " connectionString=\"", "sqlConnectionString=\"", "validationKey=", "decryptionKey=", "NVRAM config last updated"]
Triage = "Red"

Надеюсь, это передаёт идею. Я рекомендую взять некоторые из стандартных правил и поэкспериментировать с ними, пока вы не почувствуете, что хорошо разобрались в этом.

ЧТО ТАКОЕ "UltraSnaffler"???

Многие пользователи хотели возможность просматривать форматы файлов, которые не являются простым текстом, например, документы Word, PDF, .eml и т. д. К сожалению, самая удобная библиотека для реализации этой функциональности увеличивала конечный размер файла Snaffler.exe примерно на 1200%, что было проблемой для многих популярных методов выполнения в памяти, у которых были ограничения на максимальный размер файла.

Решением стал UltraSnaffler — это просто второй файл .sln, который подключает необходимую библиотеку и соответствующий код. Соберите UltraSnaffler.sln, получите UltraSnaffler.

ПРЕДУПРЕЖДЕНИЕ: В стандартные правила Snaffler не входят правила для поиска внутри документов Office или PDF, потому что мы обнаружили, что очень сложно написать такие правила, которые не будут выполняться годами в типичной корпоративной среде. Будьте осторожны: поиск внутри этих документов занимает гораздо больше времени, чем поиск внутри обычных текстовых файлов, а в типичной среде будет огромное количество малоценных документов Office и PDF.

Как работает конфигурационный файл?

На самом деле, это очень классно, на мой взгляд.

Если вы добавите -z generate в конец командной строки Snaffler, Snaffler сериализует объект конфигурации (включая все аспекты конфигурации, заданные вашими аргументами) в файл конфигурации .toml, который вы затем сможете легко отредактировать (или нет) и использовать в дальнейшем по своему усмотрению.

Например, если вы выполните:

Snaffler.exe -s -o C:\mydir\snaffler.log -v trace -i \\host.lol.domain\share -p C:\users\someguy\myrules -z generate

Snaffler проанализирует все ваши многочисленные аргументы, преобразует их в объект конфигурации, сериализует этот объект в следующий файл конфигурации .toml:

PathTargets = ["\\\\host.lol.domain\\share"]
ComputerTargetsLdapFilter = "(objectClass=computer)"
ScanSysvol = true
ScanNetlogon = true
ScanFoundShares = true
InterestLevel = 0
DfsOnly = false
DfsShareDiscovery = false
DfsNamespacePaths = []
CurrentUser = "l0sslab\\l0ss"
RuleDir = "C:\\users\\someguy\\myrules"
MaxThreads = 60
ShareThreads = 20
TreeThreads = 20
FileThreads = 20
MaxFileQueue = 200000
MaxTreeQueue = 0
MaxShareQueue = 0
LogToFile = true
LogFilePath = "C:\\mydir\\snaffler.log"
LogType = "Plain"
LogTSV = false
Separator = 32
LogToConsole = true
LogLevelString = "trace"
ShareFinderEnabled = false
LogDeniedShares = false
DomainUserRules = false
DomainUserMinLen = 6
DomainUserNameFormats = ["sAMAccountName"]
DomainUserMatchStrings = ["sql", "svc", "service", "backup", "ccm", "scom", "opsmgr", "adm", "adcs", "MSOL", "adsync", "thycotic", "secretserver", "cyberark", "configmgr"]
DomainUsersWordlistRules = ["KeepConfigRegexRed"]
MaxSizeToGrep = 1000000
Snaffle = false
MaxSizeToSnaffle = 10000000
MatchContextBytes = 200

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

Это отстой, вы планируете сделать его менее отстойным?

Нет, это не отстой, это вы отстой.

И да, планируем.

Мы также собираемся: - Добавить парсинг архивных файлов, идеально обрабатывая их как обычную папку для поиска интересного. - Продолжать улучшать правила и регулярные выражения. Больше слов для словарных списков! string[] для трона string!

A dumb joke about wordlists.

У кого вы украли код?

Часть кода для перечисления сетевых ресурсов была «снаффлена» (понял шутку?) из SharpShares, написанного невероятно полезным Дуайтом Хонштейном. (https://github.com/djhohnstein/SharpShares/) Профиль Дуайта на GitHub похож на тот удивительный задний ряд в магазине инструментов, где лежит куча вещей, от которых думаешь: «О, круто, не могу дождаться, когда у меня появится повод попробовать это по-настоящему…» — обязательно загляните туда.

Хотя код не брался (в основном потому, что он на Ruby, лол), мы украли кучу классных идей из plunder2 (http://joshstone.us/plunder2/)

Словарные списки также были отобраны из аналогичных инструментов, таких как trufflehog, shhgit, gitrobber и graudit.

Это безопасно с точки зрения OPSEC? (Что бы это ни значило)

Фух, нет. Это шумно как черт.

Слушайте, давайте так: если вы уверены, что можете запустить BloodHound в стандартном режиме в вашей среде, то… эээ… да, брат… Это очень скрытно.

Я думал, вы используете этот инструмент на красных командах?

вздох

Хорошо, дам вам честный ответ.

В стандартном режиме Snaffler во многом похож на SharpHound. Он активно общается с AD по LDAP, а затем пытается подключиться по SMB ко всем Windows-машинам в домене. Такое поведение почти гарантированно приведет к вашему разоблачению в организации, где хоть что-то настроено более-менее нормально.

ОДНАКО…

Более целевые опции Snaffler (особенно -i) гораздо меньше вероятность вызовут срабатывания систем обнаружения.

Мне особенно нравится запускать Snaffler.exe -s -i C:\ на только что скомпрометированном сервере или рабочей станции, и пока что я не видел, чтобы такое поведение обнаруживалось.

Пока.

Как я могу помочь или получить помощь?

Если хотите обсудить в Slack, можете написать нам (@l0ss или @Sh3r4) в Slack BloodHound (вступить можно по ссылке https://bloodhoundgang.herokuapp.com/) или пообщаться с группой участников в канале #snaffler.

Также можете написать нам в Twitter — @mikeloss и @sh3r4_hax.

В противном случае создайте issue — мы постараемся помочь.

Войдите, чтобы оставить комментарий