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

Функции для работы со строками

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

Функция REPLACE

Функция

replace
замещает один текст в строке другим. Простой пример – заменим в строке одно слово другим:

WITH 'BloodHound' AS Original

RETURN Original, replace(Original, 'Blood', 'Sharp') AS Replaced


Рис. 3.31. Результат замены


Другой пример: в статье мы хотим показать интересный вектор, и, чтобы не закрашивать имя домена на узлах, мы можем поменять его название на что-то нейтральное.

MATCH (c) SET c.name = replace(c.name, "CORP.LOCAL", "DOMAIN.LOCAL")

Функция SPLIT

Мы уже сталкивались с этой функцией в разделе про работу с датами. Функция

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

MATCH (u: User {name: "USER@DOMAIN.LOCAL"})

RETURN u.name AS name, SPLIT(u.name,'@') AS list


Рис. 3.32. Результат использования SPLIT


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

MATCH (u: User {name: "USER@DOMAIN.LOCAL"})

RETURN u.name AS name, split (u.name,'@')[0] AS name_list


Рис. 3.33. Получение имени из списка


Функции TOLOWER и TOUPPER

Функция

ToLower
переводит строку в строчные буквы, а функция
ToUpper
 – в заглавные. Функция
ToLower
полезна при формировании данных в табличном режиме для размещения в отчете, так как заглавные буквы смотрятся громоздкими, в то время как строчные буквы более аккуратны. Например, выгрузим имена компьютеров и отобразим их строчными буквами:

MATCH(c: Computer) RETURN toLower(c.name) AS name


Рис. 3.34. Использование функции ToLower


В BloodHound значимые свойства узла пишутся заглавными буквами, и функция

ToUpper
будет полезна при добавлении свойств к новым узлам. Например, свойство
objectid
используется для идентификации узла, для пользователей, компьютеров и групп используется
sid
в качестве идентификатора, для всех остальных объектов используется
guid
, и свойство
objectid
записывается заглавными буквами. В neo4j есть функция
randomUUID
для генерации
guid
, которую можно использовать при создании новых узлов, и, чтобы следовать стандарту BloodHound, ее надо записать заглавными буквами. Вот так будет выглядеть запрос с использованием функции
ToUpper
:

RETURN toUpper(randomUUID()) AS objectid


Рис. 3.35. Использование функции ToUpper


Функции LTRIM, RTRIM и TRIM

Функция

ltrim
удаляет все пробелы слева от строки,
rtrim
 – справа, а
trim
 – одновременно справа и слева:

WITH " BloodHound " as string

RETURN ltrim(string) AS 'Left Trim', trim(string) AS 'Trim', rtrim(string) AS 'Rigth Trim'


Рис. 3.36. Результат использования функции trim


Функции LEFT и RIGHT

Функция

left
отсчитывает слева указанное количество символов и возвращает их как результат, все остальное отбрасывается. Функция
right
делает все то же самое, только справа:

WITH "BloodHound" as string

RETURN left(string, 5), right(string,5)


Рис. 3.37. Результат использования функций left и right


Функция SUBSTRING

Функция

substring
возвращает от строки подстроку с указанной позицией и длиной. Например:

WITH "BloodHound" as string

RETURN string, substring(string, 3,4)


Рис. 3.38. Результат использования функции substring


Объединение строк

Может возникнуть ситуация, когда потребуется объединить две строки в одну. Для этого можно использовать знак

+.
Например, объединим два слова в одно, тогда запрос Cypher получится следующий:

WITH "Blood" AS s1, "Hound" AS s2

RETURN s1, s2, s1+s2 AS string


Рис. 3.39. Объединение двух слов


Функция TOSTRING

Функция

ToString
приводит к типу данных
string
. Рассмотрим простой пример: запросим текущую дату, воспользуемся функцией
ToString
и посмотрим на результаты. Чтобы получить тип данных, воспользуемся процедурой
apoc.meta.type
из плагина APOC:

WITH Date() AS date

RETURN date, apoc.meta.type(date), apoc.meta.type(toString(date))


Рис. 3.40. Результат использования функции ToString


Создание и удаление узлов и связей

Базовое использование Bloodhound не подразумевает создания новых элементов, тем не менее это важный функционал для будущих работ. Как ни странно, создание связей требует понимания работы оператора

MATCH
, условий выборки и установки свойств для узлов, и поэтому в книге этот раздел идет предпоследним в теме языка запросов Cypher.

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

Оператор CREATE

Для создания узлов используется оператор CREATE.


Рис. 3.41. Оператор Create


Запрос на создание узлов состоит из следующих элементов:

● 

var
 – переменная;

● 

Label
 – метка, необязательный элемент;

● 

prop1
и
prop2
 – названия свойств, а
val
 – их значения после двоеточия.

Свойства указываются в фигурных скобках и разделяются запятыми.

Внимание

Если идет цепочка запросов, то переменные должны отличаться или каждый запрос завершается точкой с запятой.

Теперь создадим узел с меткой

User
и свойством
name
, которое имеет значение
TEST
. Запрос Cypher будет следующим:

CREATE (u: User {name: "TEST"})

Если повторить этот запрос, то будет создан еще один пользователь с тем же именем, отличаться будет только

id
.

Выполним запрос, чтобы получить все созданные узлы.

MATCH (u: User) RETURN u


Рис. 3.42. Два новых пользователя


В инфраструктуре Active Directory нет одинаковых объектов. Даже если имена у них будут совпадать, все равно найдутся отличительные признаки, например

SID
или
Distinguishedname
.

Чтобы избежать создания повторных узлов, в ранних версиях использовалась связка

CREATE UNIQUE
, теперь ту же роль играет оператор
MERGE
.

Оператор MERGE

Принцип работы оператора

MERGE
точно такой же, как и у
CREATE
, с одним условием: он проверяет, существует ли такой элемент в базе.


Рис. 3.43. Оператор MERGE


Если мы выполним следующий запрос Cypher, то ничего не произойдет.

MERGE(u: User {name: "TEST"})

Давайте добавим новые элементы, но введем еще одно свойство

sid
:

MERGE(m: User {name: "TEST", sid:'U-001'})

MERGE(n: User {name: "TEST", sid:'U-002'})

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

sid
.


Добавление свойств при создании

Как говорилось выше, свойства передаются в фигурных скобках, но в операторе

MERGE
есть функция