Инструмент для обнаружения файловых ресурсов в сети Windows — помогает находить потенциально открытые файлы и учётные данные.
Инструмент для извлечения паролей, хешей, PIN-кодов и Kerberos-тикетов из памяти Windows. Поддерживает Pass-the-Hash, Pass-the-Ticket, Golden/Silver Ticket, …
Кросс-платформенный инструмент для извлечения сохранённых паролей из десятков приложений: браузеры, почтовые клиенты, SSH-клиенты, мессенджеры, БД, VPN, …
Компактный инструмент для исследования дампинга учётных данных — изучает защиту LSASS и методы обнаружения доступа к …
C#-порт части функциональности Mimikatz для работы с Windows DPAPI.
git clone https://github.com/SnaffCon/Snaffler.git cd Snaffler dotnet build -c Release

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 загрузит все эти файлы вместо стандартного набора правил.Надеемся, что этот аннотированный пример поможет:

Эту запись в логе следует читать примерно слева направо так:
... а остальная часть строки (серая) — это небольшой фрагмент контекста из файла, где было найдено совпадение.
В этом случае мы нашли значения ASP.NET validationKey и decryptionKey, которые могут позволить нам выполнить RCE в веб-приложении через какие-то уязвимости десериализации. Ура!
Примечание: после создания этого скриншота Sh3r4 добавил функцию, чтобы добавлять текущего пользователя и имя хоста в начало каждой строки. Но я не хочу переделывать скриншот.
Каждый метод поиска файлов по магии L.A.I.M. делает следующее:
.kdbx, .vmdk, .ppk и т. д.id_rsa, shadow, NTDS.DIT и т. д.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 варианта.
DefaultRules, затем соберите новую версию Snaffler. Файлы .toml в этом каталоге будут встроены в .exe в качестве ресурсов и загружены во время выполнения, если вы не укажете другие правила для использования.-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"
Надеюсь, это передаёт идею. Я рекомендую взять некоторые из стандартных правил и поэкспериментировать с ними, пока вы не почувствуете, что хорошо разобрались в этом.
Многие пользователи хотели возможность просматривать форматы файлов, которые не являются простым текстом, например, документы 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!

Часть кода для перечисления сетевых ресурсов была «снаффлена» (понял шутку?) из SharpShares, написанного невероятно полезным Дуайтом Хонштейном. (https://github.com/djhohnstein/SharpShares/) Профиль Дуайта на GitHub похож на тот удивительный задний ряд в магазине инструментов, где лежит куча вещей, от которых думаешь: «О, круто, не могу дождаться, когда у меня появится повод попробовать это по-настоящему…» — обязательно загляните туда.
Хотя код не брался (в основном потому, что он на Ruby, лол), мы украли кучу классных идей из plunder2 (http://joshstone.us/plunder2/)
Словарные списки также были отобраны из аналогичных инструментов, таких как trufflehog, shhgit, gitrobber и graudit.
Фух, нет. Это шумно как черт.
Слушайте, давайте так: если вы уверены, что можете запустить 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 — мы постараемся помочь.