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

..\neo4j_uploaddata.ps1

UploadData -file.\users.txt


Рис. 4.17. Выполнение скриптов


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

MATCH p=(l: LocalUser)-[r: AdminTo]->(c: Computer) RETURN p


Рис. 4.18. Результат выполнения запроса


Если выполнить этот запрос в браузере neo4j и посмотреть на связь между

SERVER
и
Test
, то мы обнаружим, что данная связь имеет свойство
ispotential
.


Рис. 4.19. Свойство для связи AdminTo


Локальный администратор и групповые политики

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


Настройка лаборатории

Для начала подготовим лабораторию. Мы не будем создавать сами политики, просто сделаем условные обозначения:

● Создадим две OU с именами

Test1
и
Test2
.

● В этих OU создадим по одному объекту «компьютер» с именами

comp1
и
comp2
.

● Создадим по одной групповой политике для каждой OU. Первую назовем

Set Admin Password
, а вторую
Create Local Admin
.

● Очистим базу neo4j.

● Запустим SharpHound и загрузим новые данные в BloodHound (рис. 4.20).

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

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

MERGE (:LocalUser {name: "ADMINISTRATOR",

objectid: toUpper(randomUUID()), password: "Qwerty123",

hash: ToUpper("329b074c0058ccf1ba2e4705382963ff")})

MERGE (:LocalUser {name: "LOCALADMIN",

objectid: toUpper(randomUUID()), password: " P@ssw0rd",

hash: ToUpper("e19ccf75ee54e06b06a5907af13cef42")})


Рис. 4.20. Создание групповых политик


Внимание

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

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

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

Для начала получим свойства

objectid
для локальных пользователей и групповых политик (рис. 4.21).

MATCH (m: LocalUser) RETURN m.name, m.objectid

UNION

MATCH (m: GPO) WHERE m.name CONTAINS "ADMIN" RETURN m.name, m.objectid

Внимание

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

4.21. Получение objectid узлов


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

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

Теперь необходимо связать новые узлы с групповыми политиками и указать связь

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

MATCH (g: GPO {objectid:"41A40F36-E64C-4963–9D23-B51F446A5204"})

MATCH (l: LocalUser {objectid:"6160668F-96C4–4247–9095–0C048826320E"})

MERGE (g)-[: SetPassword]-(l)

Проверим, что связь создалась:

MATCH p=((g: GPO {objectid:"41A40F36-E64C-4963–9D23-B51F446A5204"})-[r: SetPassword]->(l: LocalUser)) RETURN p


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


Следующим шагом свяжем узел

LocalUser
с компьютерами, к которым применяется групповая политика:

MATCH (g: GPO {objectid:"41A40F36-E64C-4963–9D23-B51F446A5204"})-[: GPLink|Contains*1..]->(c: Computer)

MATCH (l: LocalUser {objectid:"6160668F-96C4–4247–9095–0C048826320E"})

MERGE (l)-[: AdminTo]-(c)

Проверим, что новая связь создалась:

MATCH p=((l: LocalUser)-[r: AdminTo]->(c: Computer)) RETURN p


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


А теперь проверим всю цепочку:

MATCH p=((g: GPO)-[r: SetPassword|AdminTo*1..]->(c: Computer)) RETURN p


Рис. 4.24. Результат проверки всей цепочки


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

objectid
, а связь
SetPassword
будет заменена на
CreateUser.

MATCH (g: GPO {objectid: "CB7B245F-20FE-44EC-A540-D4D4932EEE35"})

MATCH (l: LocalUser {objectid:"8485A134–70A4–41F7-BCAA-1DF2F4ECC36B"})

MERGE (g)-[: CreateUser]-(l)

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

MATCH (g: GPO {objectid: "CB7B245F-20FE-44EC-A540-D4D4932EEE35"})-[: GPLink|Contains*1..]->(c: Computer)

MATCH (l: LocalUser {objectid:"8485A134–70A4–41F7-BCAA-1DF2F4ECC36B"})

MERGE (l)-[: AdminTo]-(c)

И проверим всю цепочку:

MATCH p=((g: GPO)-[r: CreateUser|AdminTo*1..]->(c: Computer)) RETURN p


Рис. 4.25. Результат проверки всей цепочки


Добавление новых связей в BloodHound

Если выполнить предыдущий запрос в BloodHound через Raw Query, то мы получим аналогичный результат.


Рис. 4.26. Запрос в BloodHound


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

Test2,
в которой находится компьютер
comp2
. Это связано с тем, что запросы в BloodHound выполняются с перечислением всех связей, указанных в файле
AppContainer.jsx
в массиве
fullEdgeList
.


Рис. 4.27. Запрос через поиск путей


Если связь

AdminTo
существует в BloodHound «из коробки», то связей
SetPassword
и
CreateUser
нет и нам требуется их добавить.

Открываем файл

AppContainer.jsx
, который находится в директории
src
, находим строчку
fullEdgeList
и добавляем в конец массива наши связи:

const fullEdgeList = [

'DCSync',

'SyncLAPSPassword'
,

'SetPassword',

'CreateUser'

];

Сохраняем файл и теперь открываем файл

index.js
в том же каталоге, находим строчку
global.appStore
и двигаемся до
edgeScheme
. Там добавляем:

global.appStore = {

dagre: true,

edgeScheme:{

SyncLAPSPassword:'tapered',

DumpSMSAPassword:'tapered'
,

SetPassword:'tapered',

CreateUser:'tapered'

},


Листаем код до

lowResPalette
и в
edgeScheme
добавляем:

lowResPalette:{

edgeScheme:{

DumpSMSAPassword:'line'
,

SetPassword:'line',

CreateUser:'line'
,

},

Находим строчку