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

baseQuery={

'MATCH p=(n)-[r]->(u1:Share
{objectid:$objectid}) WHERE r.isfsacl=true'

}

end={label}

distinct

/>

property='Unrolled Object Controllers'

target={objectId}

baseQuery={

'MATCH p=(n)-[r: MemberOf*1..]->(g: Group)-[r1:CanWrite|CanModify|FullControl]->(u: Share

{objectid:$objectid}) WITH LENGTH(p)

as pathLength, p, n WHERE NONE (x in NODES(p
)[1..(pathLength-1)] WHERE x.objectid =
u.objectid) AND NOT n.objectid = u.objectid'

}

end={label}

distinct

/>

property='Transitive Object Controllers'

target={objectId}

baseQuery={

'MATCH (n) WHERE NOT n.objectid=$objectid
MATCH p = shortestPath((n)-[r1:Member
Of|CanWrite|CanModify|FullControl*1..]->(u1:Share {objectid:$objectid}))'

}

end={label}

distinct

/>

);

};

// Заменяем на ShareNodeData

ShareNodeData
.propTypes = {};

// Заменяем на ShareNodeData

export default withAlert()(
ShareNodeData
);

Сохраняем измененный файл и собираем приложение:

npm run build: win32

Запускаем обновленную версию BloodHound, выполняем запрос Cypher в Raw Query:

MATCH (s: Share) RETURN s


Рис. 4.53. Результат отображения общего ресурса


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

Для дальнейшей работы нам потребуется доработать лабораторию и создать несколько общих ресурсов на доступных хостах. Выполним следующие действия:

● На контроллере домена в корне диска

C
создадим директорию
Share
.

● В директории

Share
создадим директорию
Backup
.

● В директории

Backup
создадим директорию
Test
.

● Сделаем директорию

Share
общей, добавим полные права для пользователя
user
.

● Добавим полные права для пользователя

victim
на директорию
Backup
.

● Добавим права на запись пользователю

admin
на директорию
Test
.

Удалим наш тестовый общий ресурс:

MATCH (s: Share) DELETE s

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

Теперь приступим к созданию автоматизированной утилиты для сбора информации об общих ресурсах и формирования Cypher-запросов. Напишем скрипт на Powershell, который назовем

GetSharesInfo.ps1.

За основу возьмем один из вариантов скрипта

Get-NetShare.ps1
, который использует WinAPI, –
NetShareEnum
и добавим дополнительный функционал.

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

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

dnshostname
.

● С помощью WinAPI

NetShareEnum
проверить наличие общих ресурсов на каждом компьютере.

● Проверить доступность общего ресурса для текущего пользователя.

● Получить ACL для общего ресурса.

● Если есть доступ, выполнить проверку поддиректорий с глубиной 2.

● Выполнить проверку доступности поддиректорий.

● Получить ACL для поддиректории.

● Для ACL установить атрибут

isfsacl
.

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

function Get-SharesInfo()

{

# Подключаем библиотеку netapi32.dll

Add-Type @"

using System;

using System.Runtime.InteropServices;

using System.Text;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

public struct SHARE_INFO_1

{

[MarshalAs(UnmanagedType.LPWStr)]

public string shi1_netname;

public uint shi1_type;

[MarshalAs(UnmanagedType.LPWStr)]

public string shi1_remark;

}

public static class NetApi32

{

[DllImport("netapi32.dll", SetLastError = true)]

public static extern int NetApiBufferFree(IntPtr Buffer);

[DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]

public static extern int NetShareEnum(

StringBuilder servername,

int level,

ref IntPtr bufptr,

uint prefmaxlen,

ref int entriesread,

ref int totalentries,

ref int resume_handle);

}

"@

# Получаем имя домена

$DomainObject = [System.DirectoryServices. ActiveDirectory.Domain]::GetCurrentDomain()

$DomainName = $DomainObject.name.toUpper()

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

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

# Выполняем ADSI-запрос для всех незаблокированных компьютеров с атрибутом dnshostname

$computers = ([adsisearcher]' (&(objectCategory=computer) (dnshostname=*)(!(userAccountCont rol:1.2.840.113556.1.4.803:=2)))').FindAll()

foreach($computer in $computers)

{

# Получаем имя и SID компьютера

$ComputerName = $computer.Properties.name.Item(0)

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

# Выполняем вызов Win API NetShareEnum

$pBuffer = [IntPtr]::Zero

$entriesRead = $totalEntries = $resumeHandle = 0

$result = [NetApi32]::NetShareEnum(

$ComputerName, # servername

1, # level

[Ref] $pBuffer, # bufptr

[UInt32]::MaxValue, # prefmaxlen

[Ref] $entriesRead, # entriesread

[Ref] $totalEntries, # totalentries

[Ref] $resumeHandle # resumehandle

)

if (($result -eq 0) -and ($pBuffer -ne [IntPtr]::Zero) -and ($entriesRead -eq $totalEntries))