Active Directory 列舉 - 手動與自動

 Active Directory 列舉 - 手動與自動


Normal User : stephanie (Member of Remote Desktop Group)

Domain FQDN : corp.com

Client : Windows 11

機器 IP

192.168.45.168 kali

192.168.208.70 dc1

192.168.208.72 web04

192.168.208.73 files04

192.168.208.74 client74

192.168.208.75 client75

192.168.208.76 client76


一開始使用網域帳號 Stephanie / LegmanTeamBenzoin! RDP 登入到 Client 75

└─$ xfreerdp /u:stephanie /d:corp.com /v:192.168.208.75  

會出現要我們輸入密碼,這時再輸入密碼即可,結果 GG



反覆測試後要加上忽略憑證,以及密碼直接帶入,但因為密碼有兩個驚嘆號,所以加上單引號

└─$ xfreerdp /u:stephanie /p:'LegmanTeamBenzoin!!' /d:corp.com /v:192.168.208.75 /cert:ignore





net user /domain 列舉網域的使用者帳號





管理者通常喜歡在帳號的前面或是後面加上 admin 的字眼,所以從上圖中我們首先接著列舉使用者帳號 jeffadmin,net user jeffadmin /domain

結果馬上中獎是 Domain Admins 群組成員





列舉網域的群組, net group /domain





透過 net 指令列舉特定網域群組 "Sales Department"

指令 net group "Sales Department" /domain




管理部門的使用者 net group "Management Department" /domain,只有 jen 一個帳號





練習題的 flag 藏在列舉網域群組中








使用 PowerShell 和 .NET 類別列舉 Active Directory


LDAP 路徑的原型 LDAP://HostName[:PortNumber][/DistinguishedName]

完整的 LDAP 路徑需要三個參數:HostName、 PortNumber、DistinguishedName


Powershell 簡單列舉 Domain 腳色指令,一般使用者都可以列舉 

[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()





我們將上述的指令改成參數並寫入到一個 *.ps1 檔案中如下

在 powershell 編輯器下可以直接看到輸出的結果與上圖一樣





放在該使用者桌面取名為 enumeration.ps1





存檔後讓我們直接執行該檔案再測試一次,結果 GG,主要錯誤顯示

cannot be loaded because running scripts is disabled on this system





故要先執行繞過執行策略 powershell -ep bypass 再執行一次該檔案,無問題地輸出




從輸出的結果可以看到 PdcRoleOwner 是 DC1.corp.com

PDC 的主機名是我們需要的,由於 LDAP 路徑需要 PdcRoleOwner 屬性中的主機名,因此我們可以直接從網域物件中提取名稱。如果我們稍後在腳本中需要來自域物件的更多信息,我們將暫時保留 $domainObj並創建一個名為 $PDC 的新變量,該變量將從 $domainObj 變量中保存的 PdcRoleOwner 屬性中提取值 "DC1.corp.com",作法如下

 

# Store the domain object in the $domainObj variable
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()

# Store the PdcRoleOwner name to the $PDC variable
$PDC = $domainObj.PdcRoleOwner.Name

# Print the $PDC variable
$PDC






再執行一次 enumeration.ps1,結果正確無誤地顯示出 PDC 的 Hostname





LDAP 第三個參數是 DN,所以接下來要透過腳本自動提取 DN

可以使用 ([adsi]'').distinguishedName,正確顯示 DC=corp,DC=com




一樣將 DN 變成為一個變數,將剛剛的腳本修改如下並打印出 DN 查看是否正確


# Store the domain object in the $domainObj variable
$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()

# Store the PdcRoleOwner name to the $PDC variable
$PDC = $domainObj.PdcRoleOwner.Name

# Store the Distinguished Name variable into the $DN variable
$DN = ([adsi]'').distinguishedName

# Print the $DN variable
$DN





有了主機名與 DN 可以直接寫成我們要的 LDAP 腳本了如下


$PDC = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().PdcRoleOwner.Name
$DN = ([adsi]'').distinguishedName 
$LDAP = "LDAP://$PDC/$DN"
$LDAP


輸出結果是 LDAP://DC1.corp.com/DC=corp,DC=com 符合預期




新增了 $direntry 變量,它封裝了我們獲得的 LDAP 路徑

$dirsearcher 變數包含 $direntry 變量,並使用該資訊作為 SearchRoot

指向 DirectorySearcher 將運行 FindAll() 方法的層次結構如下


$PDC = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().PdcRoleOwner.Name
$DN = ([adsi]'').distinguishedName 
$LDAP = "LDAP://$PDC/$DN"

$direntry = New-Object System.DirectoryServices.DirectoryEntry($LDAP)

$dirsearcher = New-Object System.DirectoryServices.DirectorySearcher($direntry)
$dirsearcher.FindAll()






接著設定 $dirsearcher.filter 進行過濾

並指定過濾條件為 "samAccountType=805306368"

將枚舉域中的所有使用者


$PDC = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().PdcRoleOwner.Name
$DN = ([adsi]'').distinguishedName 
$LDAP = "LDAP://$PDC/$DN"

$direntry = New-Object System.DirectoryServices.DirectoryEntry($LDAP)

$dirsearcher = New-Object System.DirectoryServices.DirectorySearcher($direntry)
$dirsearcher.filter="samAccountType=805306368"
$dirsearcher.FindAll()






接著新增 foreach 迴圈列出每一個帳號屬性 Properties

這個完整的腳本將搜尋 AD 並根據我們選擇的 samAccountType 過濾結果

然後將結果放入新的 $result 變數中。然後它將根據兩個 foreach 循環進一步過濾結果

第一個循環將提取儲存在 $result 中的物件並將它們放入 $obj變數中

第二個循環將提取每個物件的所有屬性並將資訊儲存在 $prop 變數中

然後該腳本將列印 $prop 並在終端中顯示輸出

然後讓每個帳號透過 ------------------------------- 來區隔與好讀


$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$PDC = $domainObj.PdcRoleOwner.Name
$DN = ([adsi]'').distinguishedName 
$LDAP = "LDAP://$PDC/$DN"

$direntry = New-Object System.DirectoryServices.DirectoryEntry($LDAP)

$dirsearcher = New-Object System.DirectoryServices.DirectorySearcher($direntry)
$dirsearcher.filter="samAccountType=805306368"
$result = $dirsearcher.FindAll()

Foreach($obj in $result)
{
    Foreach($prop in $obj.Properties)
    {
        $prop
    }

    Write-Host "-------------------------------"
}






在輸出的帳號中很多,其中有一個名稱是 jeffadmin,我們對他特別有興趣






所以接下來要做的跟上面一樣,只是輸出時過濾僅輸出帳號是 jeffadmin 這個帳號的相關群組

首先更改了過濾器以使用 name 屬性僅顯示 jeffadmin 的資訊

此外將 .memberof 新增至 $prop 變量,用以僅顯示 jeffadmin 所屬的群組


$domainObj = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$PDC = $domainObj.PdcRoleOwner.Name
$DN = ([adsi]'').distinguishedName 
$LDAP = "LDAP://$PDC/$DN"

$direntry = New-Object System.DirectoryServices.DirectoryEntry($LDAP)
$dirsearcher = New-Object System.DirectoryServices.DirectorySearcher($direntry)
$dirsearcher.filter="name=jeffadmin"
$result = $dirsearcher.FindAll()

Foreach($obj in $result)
{
    Foreach($prop in $obj.Properties)
    {
        $prop.memberof
    }

    Write-Host "-------------------------------"
}


可以看到他是 Domain Admin






為了使腳本更加靈活,允許我們透過命令列添加所需的參數

例如,我們可以讓腳本接受我們希望列舉的 samAccountType 作為命令列參數

在最頂部,我們使用我們選擇的名稱宣告函數本身,在本例中為 LDAPSearch

然後動態取得所需的 LDAP 路徑連接字串並將其新增至 $DirectoryEntry 變數

然後 DirectoryEntry 和我們的 $LDAPQuery 參數被輸入到 DirectorySearcher 中

最後執行搜尋並將輸出新增到一個陣列中,該陣列根據我們的需要顯示在我們的終端中


function LDAPSearch {
    param (
        [string]$LDAPQuery
    )

    $PDC = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain().PdcRoleOwner.Name
    $DistinguishedName = ([adsi]'').distinguishedName

    $DirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry("LDAP://$PDC/$DistinguishedName")

    $DirectorySearcher = New-Object System.DirectoryServices.DirectorySearcher($DirectoryEntry, $LDAPQuery)

    return $DirectorySearcher.FindAll()

}


將上述函數存檔為 function.ps1 要使用該函數,我們將其導入記憶體

Import-Module .\function.ps1





在 PowerShell 中,現在可以使用 LDAPSearch 命令(我們聲明的函數名稱)從 AD 獲取資訊






也可以直接搜尋 Object Class,它是定義物件類型的 AD 元件

使用 objectClass=group 來列出網域中的所有群組





要枚舉域中可用的每個群組並顯示使用者成員,我們可以將輸出透過管道傳輸到一個新變數中

並使用 foreach 循環來列印群組的每個屬性。這允許我們選擇我們感興趣的特定屬性。

例如專注於 CN 和 member 屬性

foreach ($group in $(LDAPSearch -LDAPQuery "(objectCategory=group)")) {$group.properties | select {$_.cn}, {$_.member}}





列舉出的群組中 Sales Department 中的成員包含 Development Department





透過之前寫好的 LDAPSearch 單獨列舉出 Sales Department 再次確認一次

先做一個變數為 $sales 為 LDAPSearch 依據 Sales Department 的群組輸出

然後接著輸出 $sales 裡面的 member 屬性

$sales = LDAPSearch -LDAPQuery "(&(objectCategory=group)(cn=Sales Department))"

$sales.properties.member

可以看到確實包含了 Development Department





以同樣的手法這次查看 Development Department 的群組成員,使用 $dev 為變數

$dev = LDAPSearch -LDAPQuery "(&(objectCategory=group)(cn=Development Department*))"

$dev.properties.member

這次出現了管理部門為群組成員





再次以同樣的手法這次查看 Management Department 的群組成員,使用 $manage 為變數

$manage = LDAPSearch -LDAPQuery "(&(objectCategory=group)(cn=Management Department*))"

$manage.properties.member

看到僅剩一個 jen 一般使用者,但這個使用者權限是很大的因為它屬於管理部門群組

管理部門群組又屬於開發部門群組,開發部門群組又隸屬業務部門群組

所以該帳號擁有三個群組所擁有的權限







練習

使用新開發的 PowerShell 腳本列舉網域群組,從Service Personnel開始。解開嵌套群組,然後列舉嵌套群組的最後一個直接使用者成員的屬性以獲得標誌。

同樣手法(注意腳本不能放在桌面否則匯入 Module 會失敗)

$group = LDAPSearch -LDAPQuery "(&(objectCategory=group)(cn=Service Personnel))"

$group.properties.member

發現使用者 Billing




繼續列舉

$group = LDAPSearch -LDAPQuery "(&(objectCategory=group)(cn=Billing))"

$group.properties.member



繼續

$group = LDAPSearch -LDAPQuery "(&(objectCategory=group)(cn=Customer support))"

$group.properties.member

順便 net user /domain 確認 michelle 是使用者物件了






enumeration.ps1 改成該使用者 michelle




run 就拿到 flag 了





留言

這個網誌中的熱門文章

Challenge 0 - Secura(2)

濫用 Windows 庫文件(Library File)

Challenge 0 - Secura(1)