Идём по киберследу: Анализ защищенности Active Directory c помощью утилиты BloodHound — страница 10 из 46

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

Совет

В некоторых случаях использование

WITH
не очевидно, но ошибки, выдаваемые neo4j, могут указать на использование оператора
WITH
.

Определение переменных

Оператор

WITH
может применяться для определения переменных, которые затем будут использоваться в условиях. Например:

WITH 'USER@DOMAIN.LOCAL' AS varUser

MATCH (u: User) WHERE u.name = varUser RETURN u

Или в сочетании с регулярными выражениями:

WITH '(?i)domain user.*' as regex

MATCH (g: Group) WHERE g.name =~ regex RETURN g

Добавление и изменение свойств

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

ToBoolean
принимает как строковое, так и числовое значение.



У времени есть другие типы данных –

Date
,
Duration
,
LocalTime
,
LocalDateTime
и
Time
. Если строка не соответствует формату даты и времени, ее нельзя преобразовать в тип даты или времени простым способом, потребуются манипуляции со строкой для преобразования ее в нужный формат.

WITH [i in split("20/03/2024", "/") | toInteger(i)] AS dateComponents

RETURN date({day: dateComponents[0], month: dateComponents[1], year: dateComponents[2]}) AS date

Работа с большинством типов будет рассмотрена далее.

Получение всех свойств узла

Задача не частая, но можно получить все свойства узла в

RETURN
, указав
properties()
:

MATCH (u: User) RETURN properties(u) LIMIT 1

Изменение свойств

Для изменения свойства объекта используется оператор

SET
. Самый простой пример – изменение свойства
Compromised
. В базе
neo4j
это свойство называется
owned
, оно определяется булевым значением
false
или
true
. Чтобы изменить это свойство, нужно выполнить запрос Cypher:

MATCH (u: User) WHERE u.name = "USER@DOMAIN.LOCAL" SET u.owned = TRUE RETURN u.name, u.owned

Добавление нового свойства

Новое свойство также добавляется оператором

SET
. Например, после выполнения техники Kerberoasting был успешно подобран пароль, и эту информацию можно добавить в базу
neo4j
. Запрос Cypher будет таким:

MATCH (u: User) WHERE u.name = "USER@DOMAIN.LOCAL" SET u.password = "Qwerty123" RETURN u.name, u.password

Интерфейс BloodHound отображает новые свойства в разделе Extra Properties, поэтому изменять код самого BloodHound для отображения новых свойств не требуется.

Внимание

Новые свойства отображаются только после повторного запроса узла.

Можно одновременно добавить или изменить несколько свойств. Свойства и их значения указываются через запятую. Например, можно добавить свойство

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

MATCH (u: User) WHERE u.name = "USER@DOMAIN.LOCAL" SET u.password = "Qwerty123", u.owned=TRUE RETURN u.name, u.password, u.owned

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

MATCH (u: User) WHERE u.name = "USER@DOMAIN.LOCAL" SET u.‛user password‛ = "Qwerty123" RETURN u.name, u.‛user password‛

Удаление свойств

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

password
. Для удаления свойства в Cypher используется оператор
REMOVE
:

MATCH (u: User) WHERE u.name = "USER@DOMAIN.LOCAL" REMOVE u.password RETURN u.name, u.password

Вместо удаления свойства можно установить для него нулевое значение:

MATCH (u: User) WHERE u.name = "USER@DOMAIN.LOCAL" SET u.password = NULL RETURN u.name, u.password

Внимание

Установка нулевого значения может быть не всегда удачным вариантом, если используется оператор

EXISTS
для проверки наличия свойства.

Добавление метки к узлу

Как говорилось ранее, метки позволяют объединять узлы по общему признаку. Также существует возможность добавлять дополнительные метки. Как и свойство, метка добавляется с помощью оператора

SET
, но вместо знака равенства используется двоеточие.

Например, мы можем добавить дополнительную метку для всех серверов (имеется в виду Windows Server) и уже оперировать данными на основе этой метки:

MATCH (c: Computer) WHERE c.operatingsystem CONTAINS "Server" SET c: Server

А теперь проверим, какие пользователи и группы имеют права локального администратора на серверах:

MATCH p=(n)-[r: MemberOf|AdminTo*1..]->(m: Server) RETURN p


Рис. 3.15. Результат запроса


В разделе Overview можно заметить, что появилась метка

Server
.

Можно указать несколько меток, которые также будут разделяться двоеточиями.

Удаление метки

Метка удаляется, так же как и свойство, с помощью оператора

REMOVE
.

MATCH (n: Server) REMOVE n: Server

Несколько меток удаляются через разделение двоеточием.

Работа со списками

Списки позволяют хранить несколько значений в одном поле. В BloodHound таким полем является

serviceprincipalnames.

Выводятся списки в квадратных скобках в кавычках, через запятую:

["var1","var2"]
. Как и в других языках, список имеет индекс, и к элементам списка можно обращаться по его индексу.

Метки объектов тоже являются списком. Например, если выполнен запрос для поиска, какие объекты имеют привилегии локального администратора, и нужно понять, что это за объект – пользователь, группа или компьютер, можно выполнить запрос Cypher:

MATCH(n)-[r: AdminTo]->(c: Computer) RETURN n.name, labels(n), c.name


Рис. 3.16. Получение меток


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

labels(n)[0]
. В результате вывод будет более красивым.


Рис. 3.17. Получение метки по индексу


Размер списка

С помощью функции

size
можно получить размер списка, другими словами, получить количество элементов в списке. Например, получить количество записей в
serviceprincipalnames
:

MATCH (c: Computer) RETURN c.name, size(c.serviceprincipalnames)


Рис. 3.18. Результаты подсчета элементов в списке


Добавление списка к свойству

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

comp
:

MATCH (c: Computer {name: "COMP.DOMAIN.LOCAL"}) SET c.ports = ["445", "3389"]

RETURN c.name, c.ports


Рис. 3.19. Результат добавления списка к свойству


Добавление элементов в список

Добавить элемент в список можно с помощью знака плюс (

+
). Например, добавим еще один порт к нашему списку:

MATCH (c: Computer {name: "COMP.DOMAIN.LOCAL"}) SET c.ports = c.ports + "5985"

RETURN c.name, c.ports