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

SharePasswordWith
со свойством
isacl: false
.

Запускаем скрипт и загружаем данные в базу (рис. 4.29):

..\Passwordspray.ps1

CheckReusedPassword -Password Qwerty123

..\neo4j_uploaddata.ps1

UploadData -file.\PasswordSpray_23032024113245.log


Рис. 4.29. Запуск скриптов


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

MATCH p=(n: User)-[r: SharePasswordWith]->(m: User) RETURN p


Рис. 4.30. Общие пароли для пользователей


Запустим обновленную версию BloodHound и проверим отображение нашей связи в поиске путей:


Рис. 4.31. Поиск пути от victim до admin


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

MATCH (n: User) WHERE n.password = "Qwerty123" SET n.owned = TRUE

И теперь можно перейти во вкладку Analysis и выполнить запрос

Shortest Path from Owned Principals
.

Доступность хостов

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

Для проверки доступности хоста используется несколько способов, самый простой – это ответ на ping. Однако этот метод не дает точного результата, так как ответ на

ping
может быть заблокирован межсетевым экраном или разрешен, но все остальные порты заблокированы пограничным межсетевым экраном при сегментации сети. Поэтому вторым фактором доступности будут открытые порты. В среде Active Directory таким портом будет 445. Не стоит забывать, что при сегментировании сети администраторы создают так называемые jump-сервера с терминальным доступом, которые позволяют получать доступ к отдельным хостам и сетям.

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

В принципе данный раздел не подразумевает какой-либо настройки лаборатории. Единственное, что можно сделать, – это выключить одну виртуальную машину.

Сбор информации

В нашей небольшой сети можно все проверить руками, но для больших сетей это может быть долго. Поэтому напишем свой powershell-скрипт

CheckAccessible.ps1
, который будет собирать всю необходимую информацию и на ее основании устанавливать свойство
accessible
.

Алгоритм скрипта будет следующим:

● Выбрать все незаблокированные компьютеры из домена.

● Проверить ответ на ICMP.

● Проверить сетевую доступность по портам 445 и 3389.

● Провести анализ результатов.

● Сформировать вывод результатов.

function CheckAccessible {

# Создаем файл отчета

[string]$OutFile = "CheckAccessible_" + $(Get-Date -f ddMMyyyyhhmmss) +".log"

# С помощью ADSI запрашиваем все незаблокированные

# компьютеры в Active Directory

$searcher = [adsisearcher]' (&(objectCategory=computer)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'

$searcher.PageSize = 1000

$computers=$searcher.FindAll()

foreach($computer in $computers)

{

# Получаем SID объекта

$SID = (New-Object System.Security.Principal.SecurityIdentifier($computer.Properties.objectsid.Item(0),0)).Value

# Получаем IP-адрес(а)

try{

$IPAddress = [System.Net.Dns]::GetHostaddresses($computer.properties.name.Item(0) +"." + $env: USERDNSDomain).IPAddressToString

} catch {

$IPAddress = ""

}

# Проверяем ответ на ICMP

$timeout = 1000

$obj = New-Object System.Net.NetworkInformation.Ping

try

{

$pingcheck = $obj.Send($computer.properties.name, $timeout)

if($pingcheck.Status -eq "Success")

{

$pingable = "True"

}

Else

{

$pingable = "False"

}

} catch {

$pingable = "False"

}

# Проверяем доступность порта 445

$tcpClient = New-Object System.Net.Sockets.TcpClient

$p445 = $tcpClient.ConnectAsync($computer.properties.name.Item(0), "445").Wait("1000")

$tcpClient.Close()

# Формирование условий доступности

if(($p445 -eq $true) -and ($pingable -eq "True"))

{

$accessible = "True"

}

elseif(($p445 -eq $false) -and ($pingable -eq "True"))

{

$accessible = "False"

}

elseif(($p445 -eq $true) -and ($pingable -eq "False"))

{

$accessible = "True"

}

else

{

$accessible = "False"

}

# Проверяем доступность порта 3389

$tcpClient = New-Object System.Net.Sockets.TcpClient

$p3389 = $tcpClient.ConnectAsync($computer.properties.name.Item(0), "3389").Wait("1000")

$tcpClient.Close()

if($p3389 -eq $true)

{

$rdp = "True"

}

else

{

$rdp = "False"

}

# Создаем Cypher-запросы

if($IPAddress -eq "")

{

Add-Content $OutFile "MATCH (c: Computer) WHERE c.objectid = '$SID' SET c.accessible = $accessible, c.pingable = $pingable, c.rdp = $rdp;"

}

else

{

# Создаем коллекцию для IP-адресов

$collectionIP = '" {0}"' -f ($IPAddress -join '","')

Add-Content $OutFile "MATCH (c: Computer) WHERE c.objectid = '$SID' SET c.accessible = $accessible, c.ipaddress = [$collectionIP], c.pingable = $pingable, c.rdp = $rdp;"

}

}

}

Запустим скрипт, дождемся завершения его работы и загрузим полученные результаты в neo4j с помощью скрипта или браузера neo4j (рис. 4.32).


Рис. 4.32. Запуск скриптов


Теперь можно проверить результат работы.

OPTIONAL MATCH (c: Computer) WHERE c.accessible = FALSE RETURN c.name, c.accessible


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


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

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

Переходим в каталог

src\components\
и открываем на редактирование файл
Graph.jsx
. В нем находим строку
node.highvalue = data.properties.highvalue;
и после нее добавляем следующий код:

node.highvalue = data.properties.highvalue;