Unhide a Hidden GPO

Lately, i read on a very good write-up regarding to GPO's abuse. The name of the article is "GPO abuse - You can't see me" and you can read it here . The first question that came to my mind was "Why i can't see you?". Many times GUI is more convenient than PowerShell but in that case i found PowerShell better for me as well as i didn't want to use PowerView. I wanted to create something from the scratch and the biggest motivation is education. So i started to ask myself "if you compromise a host during an assessment, how you can enumerate GPOs without PowerView?". Remember that you are a low privileged user, so may you are not be able to import modules and you can't import ActiveDirectory to Windows 10 and use Get-AD****. The correct answer is "ADSI" and luckily for me, doesn't need to be an expert to use it for simple requests as mine.
On this post i will try enumerate domain's GPO using ADSI as well as to unhide hidden GPOs without using a GUI.

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

Figure 1- Domain Enumeration

Okay, now we know something about the domain and we will use this information to get into deeper. A policy can be linked to Domains, OUs, Sites, so the next question that arises to my mind was "How can i use PowerShell to find all Domains, OUs, Sites and get the linked policies?".

(([adsisearcher]'(objectcategory=organizationalunit)').FindAll()).Path | %{if(([ADSI]"$_").gPlink){Write-Host "[+] OU Path:"([ADSI]"$_").Path;$a=((([ADSI]"$_").gplink) -replace "[[;]" -split "]");for($i=0;$i -lt $a.length;$i++){if($a[$i]){Write-Host "Policy Path[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).Path;Write-Host "Policy Name[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).DisplayName} };Write-Output "`n" }}\


But while i was moving on, i managed to execute a more efficient script. On the following script, i could also use a sub-domain  (but in my case i have only Root), now we are able to get linked policies for OUs. Furthermore, after some updates,i use the following script to get not only applied policies for OUs but also for Root Domain.

 (([adsi]'LDAP://DC=adsecurity,DC=lab'),(([adsisearcher]'(objectcategory=organizationalunit)')).findall()).Path | %{if(([ADSI]"$_").gPlink){Write-Host "[+] OU Path:"([ADSI]"$_").Path;$a=((([ADSI]"$_").gplink) -replace "[[;]" -split "]");for($i=0;$i -lt $a.length;$i++){if($a[$i]){Write-Host "Policy Path[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).Path;Write-Host "Policy Name[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).DisplayName} };Write-Output "`n" }}

Figure 2 - OUs and Root domain linked Policies


Alternatively, you can get Root domain's linked policies by using the following:

(([adsisearcher]'').SearchRooT).Path | %{if(([ADSI]"$_").gPlink){Write-Host "[+] Domain Path:"([ADSI]"$_").Path;$a=((([ADSI]"$_").gplink) -replace "[[;]" -split "]");for($i=0;$i -lt $a.length;$i++){if($a[$i]){Write-Host "Policy Path[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).Path;Write-Host "Policy Name[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).DisplayName} };Write-Output "`n" }}


Figure 3 - Root Domain linked policies 


Lastly, you can get domain's Sites and subnet Sites linked policies using the following:

$sitesDN = "LDAP://CN=Sites,"+$([ADSI]"LDAP://RootDSE").Get("ConfigurationNamingContext")
$subnetsitesDN = "LDAP://CN=Subnets,CN=Sites,"+$([ADSI]"LDAP://RootDSE").Get("ConfigurationNamingContext")
([ADSI]$sitesDN).Children.Path | %{if(([ADSI]"$_").gPlink){Write-Host "[+] Domain Site Path:"([ADSI]"$_").Path;$a=((([ADSI]"$_").gplink) -replace "[[;]" -split "]");for($i=0;$i -lt $a.length;$i++){if($a[$i]){Write-Host "Domain Site Policy Path[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).Path;Write-Host "Domain Site Policy Name[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).DisplayName} };Write-Output "`n" }}


Figure 4 - Domain's Sites and subnet Sites linked Policies

Now, lets try create a new policy, linked it to the "Domain Controllers OU" and give it the appropriate permissions as the @huykha10 did to his write-up. After following the steps, we will use the second script as we did on figure 2 but before using this script, i want to moving you on, one step before that and observe how initially i enumerate a hidden policy. 

Figure 5 - Unhide hidden Policy (1)

After that results i knew that the script is giving me correct results but i had to made some changes in order to replace the null policy name and the Policy path as well.

(([adsi]'LDAP://DC=adsecurity,DC=lab'),(([adsisearcher]'(objectcategory=organizationalunit)')).findall()).Path | %{if(([ADSI]"$_").gPlink){Write-Host "[+] OU Path:"([ADSI]"$_").Path;$a=((([ADSI]"$_").gplink) -replace "[[;]" -split "]");for($i=0;$i -lt $a.length;$i++){if($a[$i]){Write-Host "Policy Path[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).Path;Write-Host "Policy Name[$i]:"([ADSI]($a[$i]).Substring(0,$a[$i].length-1)).DisplayName} };Write-Output "`n" }}

Figure 6 - Unhide hidden Policy (2)


Mitigation

Now that we found how to detect a hidden policy, it is time to delete them from the Domain Controller. We are domain admins how hard could be the deletion of a GPO?

Figure 7 - Delete backdoorGPO (1)

Ooops...from the figure 7, we can observe that we can not delete/edit policy's permissions! But we are able to change GPO's permissions or delete it from "Active Directory Users and Computers"

Figure 8 - Edit GPO's permissions

After the deletion of "backdoorGPO" the gplink of "Domain Controllers" will be a null value and we are free to link a new one.

Figure 9 - Domain Controllers null "gPlink"

Comments

Popular posts from this blog

Hiding Data Using White Space (Steganography)

Basic Pivoting with Cobaltstrike and Metasploit