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

MATCH(u: User) WHERE u.description =~ "(?i).*(парол|passw).*" RETURN u.name, u.description

Оператор RETURN

Для возврата результатов используется оператор

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

Внимание

В одном запросе может быть только один

RETURN
, за исключением использования операторов
CALL
или
UNION
и
UNION ALL
.

Cypher поддерживает несколько типов возврата результатов. Рассмотрим их.

Примеры вывода результатов

В виде узла

MATCH (d: Domain) RETURN d

В виде графа

MATCH p=(g: Group)-[: GenericAll]-(c: Computer) RETURN p

или

MATCH (g: Group)-[r: GenericAll]-(c: Computer) RETURN g, r, c

Получить все узлы и связи:

MATCH (g: Group)-[r: GenericAll]-(c: Computer) RETURN *

В виде таблицы свойств объектов

MATCH (u: User) RETURN u.name, u.description

По умолчанию название таблицы выводится в виде переменной и названия свойства через точку. Это отображение можно изменить с помощью оператора

AS
:

MATCH (u: User) RETURN u.name as Name, u.description AS Description

В виде списка

MATCH(u: User) RETURN collect(u.name)

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

MATCH (u: User)-[r: MemberOf|AdminTo*1..]->(c: Computer) RETURN u.name, collect(c.name)

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

Уникальные записи, оператор DISTINCT

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

DISTINCT
.

MATCH (u: User)-[r: MemberOf|AdminTo*1..]->(c: Computer) RETURN u.name, collect(DISTINCT c.name)

Оператор COUNT

Для подсчета элементов используется оператор

COUNT
. Например, для подсчета количества пользователей в базе данных будет использоваться следующий Cypher-запрос:

MATCH (u: User) RETURN count(u)

Другой пример – нужно посчитать, какие узлы имеют права

Owns
и на скольких узлах:

MATCH (u)-[r: Owns]->(c) RETURN u.name, count(c)

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

MATCH p=(g: Group)-[r: GenericAll]->(c: Computer) return count(r)

Еще один интересный запрос:

MATCH p=AllShortestPaths((g: User)-[*1..3]->(c: Computer)) return count(p)

Здесь мы ищем все возможные связи от группы пользователей до группы компьютеров с переходами от 1 до 3 и считаем общее количество элементов.

Оператор ORDER BY

Оператор

ORDER BY
позволяет упорядочивать вывод данных, а оператор
DESC
позволяет изменять направление от большего к меньшему. Например, вывести имена всех пользователей домена в алфавитном порядке:

MATCH (u: User) RETURN u.name ORDER BY u.name

Или узнать, у какого пользователя больше связей AdminTo:

MATCH (u: User)-[r: MemberOf|AdminTo*1..]->(c: Computer) RETURN u.name, count(c) AS Comps ORDER BY Comps DESC

Оператор LIMIT

Оператор

LIMIT
используется для ограничения количества выводимых элементов. Например, нужно получить первые пять узлов, в которых есть слово ADMIN:

MATCH (n) WHERE n.name CONTAINS "ADMIN" RETURN n LIMIT 5

Как говорилось в разделе описания интерфейса BloodHound, поиск по части названия ограничен 10 узлами, и там как раз используется оператор

LIMIT
.

Оператор SKIP

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

Result
view max rows
в настройках или использовать оператор
SKIP
. Например, получить список пользователей, у которых атрибут
description
не пустой, и пропустить первую тысячу записей:

MATCH (u: User) WHERE u.description IS NOT NULL RETURN u.name, u.description SKIP 1000

Случайная выборка

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

MATCH (g: Group) RETURN g, rand() as rand ORDER BY rand LIMIT 5

Операторы UNION и UNION ALL

Операторы

UNION
и
UNION ALL
используются для объединения результатов двух разных запросов (рис. 3.13–3.14). Важное условие использования
UNION
заключается в том, чтобы названия результатов при выводе совпадали.

Например, выведем пользователей и компьютеры двумя разными запросами:

MATCH (m: User) RETURN m

UNION

MATCH (m: Computer) RETURN m

Разница между

UNION
и
UNION ALL
в том, что в первом случае повторы опускаются, а во втором они показываются.

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

MATCH (u: User)-[r: MemberOf]->(g: Group) WHERE g.objectid ENDS WITH '-512' RETURN u.name

UNION

MATCH (u: User)-[r: MemberOf]->(g: Group) WHERE g.objectid ENDS WITH '-513' RETURN u.name


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


MATCH (u: User)-[r: MemberOf]->(g: Group) WHERE g.objectid ENDS WITH '-512' RETURN u.name

UNION ALL

MATCH (u: User)-[r: MemberOf]->(g: Group) WHERE g.objectid ENDS WITH '-513' RETURN u.name


Рис. 3.14. Результат использования UNION ALL


Во втором случае пользователи

ADMIN
и
ADMINISTRATOR
в списке появляются дважды.

Объединение путей

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

RETURN
:

MATCH p1=(u: User)-[r: MemberOf*1..]->(g: Group {name: "DOMAIN ADMINS@DOMAIN.LOCAL"})

MATCH p2=(g)-[r1:AdminTo]->(c: Computer)

RETURN p1, p2

Оператор WITH

Как говорилось ранее, в некоторых запросах оператор

RETURN
может быть заменен на
WITH
. Оператор
WITH
позволяет передавать результаты из одного запроса в другой, где они используются для определения отправных узлов.

Внимание

Оператор WITH влияет на область видимости переменных. Если не указать их, то Cypher выдаст ошибку, что переменные не определены. Можно использовать * для указания всех переменных в предыдущем запросе.

Оператор

WITH
можно использовать для манипуляций данными перед выводом информации.

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

DEFAULT DOMAIN CONTROLLERS POLICY.

MATCH (o: GPO)-[r: GPLink*0..]->(n) WHERE o.gpcpath CONTAINS "6AC1786C-016F-11D2–945F-00C04FB984F9"

MATCH (n)-[r1:Contains*1..]->(c: Computer) WITH collect (c.name) AS c1

MATCH (c2:Computer) WHERE NOT c2.name IN c1

RETURN c2.name

В этом запросе на первом шаге мы получаем все

OU
, к которым применяется групповая политика, на следующем шаге определяем все компьютеры, которые принадлежат