【问题标题】:Prevent privilege escalation when a user utilizes an ADSI script当用户使用 ADSI 脚本时防止权限提升
【发布时间】:2021-10-27 14:04:05
【问题描述】:

我创建了一个脚本,允许用户更改另一个用户的密码(如果获得许可)。该脚本使用 ADSI,只能通过启动 power shell GUI 的快捷方式访问。我用来更改密码的代码如下。

$pwd = "Password1"
    $user = (([adsisearcher]"(&(objectCategory=User)(samaccountname=$username))").findall()).properties.distinguishedname
    $oUser = [adsi]“LDAP://$user”
    $ouser.psbase.invoke(“SetPassword”,$pwd)
    $ouser.psbase.CommitChanges()

脚本本身被锁定以进行编辑,用户访问它的唯一方法是通过我给他们的快捷方式。一位上级同事喜欢该脚本,但担心权限提升我的问题是,如果我将用户的密码更改能力限制为仅限某个 OU,他们是否能够通过此脚本提升权限?我们的 OU 的设置方式每个人都包含在他们的 OU 中,除非他们是主管或董事。此脚本仅供主管使用。

编辑:我意识到放置完整代码而不是仅仅放置密码更改代码对我来说是有益的。在这里。

#---------------------------------------------------------[Initialisations]--------------------------------------------------------
# Init PowerShell Gui
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing

# Hide PowerShell Console
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
$consolePtr = [Console.Window]::GetConsoleWindow()
[Console.Window]::ShowWindow($consolePtr, 0)
#---------------------------------------------------------[Form]--------------------------------------------------------
$stayopen =  3
while($stayopen -ne  2){
[System.Windows.Forms.Application]::EnableVisualStyles()
$LocalResetPassForm                    = New-Object system.Windows.Forms.Form
$LocalResetPassForm.ClientSize         = '480,300'
$LocalResetPassForm.text               = "Reset Staff Password"
$LocalResetPassForm.BackColor          = "#ffffff"
$LocalResetPassForm.TopMost            = $false


$Errorhandleform                       = New-Object system.Windows.Forms.Form
$Errorhandleform.ClientSize            = '480,300'
$Errorhandleform.text                  = "Reset Staff Password"
$Errorhandleform.BackColor             = "#ffffff"
$Errorhandleform.TopMost               = $false


$Titel                           = New-Object system.Windows.Forms.Label
$Titel.text                      = "Reset Staff Password"
$Titel.AutoSize                  = $true
$Titel.width                     = 25
$Titel.height                    = 10
$Titel.location                  = New-Object System.Drawing.Point(20,20)
$Titel.Font                      = 'Microsoft Sans Serif,13'


$Description                     = New-Object system.Windows.Forms.Label
$Description.text                = "Clicking Change Password will change the Password of the account to Password1 and unlock the user's account"
$Description.AutoSize            = $false
$Description.width               = 450
$Description.height              = 50
$Description.location            = New-Object System.Drawing.Point(20,50)
$Description.Font                = 'Microsoft Sans Serif,10'

$Errorhandle                     = New-Object system.Windows.Forms.Label
$Errorhandle.text                = "We are unable to find the Username you provided please try again."
$Errorhandle.AutoSize            = $false
$Errorhandle.width               = 450
$Errorhandle.height              = 50
$Errorhandle.location            = New-Object System.Drawing.Point(20,50)
$Errorhandle.Font                = 'Microsoft Sans Serif,10'


$TryAgainBtn                   = New-Object system.Windows.Forms.Button
$TryAgainBtn.BackColor         = "#ffffff"
$TryAgainBtn.text              = "Try Again"
$TryAgainBtn.width             = 140
$TryAgainBtn.height            = 30
$TryAgainBtn.location          = New-Object System.Drawing.Point(330,250)
$TryAgainBtn.Font              = 'Microsoft Sans Serif,10'
$TryAgainBtn.ForeColor         = "#000"
$TryAgainBtn.Visible           = $true
$TryAgainBtn.DialogResult      = [System.Windows.Forms.DialogResult]::OK
$TryAgainBtn.AcceptButton = $ResetPassBtn


$ResetPassBtn                   = New-Object system.Windows.Forms.Button
$ResetPassBtn.BackColor         = "#ffffff"
$ResetPassBtn.text              = "Change Password"
$ResetPassBtn.width             = 140
$ResetPassBtn.height            = 30
$ResetPassBtn.location          = New-Object System.Drawing.Point(330,250)
$ResetPassBtn.Font              = 'Microsoft Sans Serif,10'
$ResetPassBtn.ForeColor         = "#000"
$ResetPassBtn.Visible           = $true
$ResetPassBtn.DialogResult      = [System.Windows.Forms.DialogResult]::OK
$LocalResetPassForm.AcceptButton = $ResetPassBtn
$LocalResetPassForm.Controls.Add($ResetPassBtn)


$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(20,100)
$label.Size = New-Object System.Drawing.Size(320,20)
$label.Text = 'Enter The Staff Members username in the space Below:'
$LocalResetPassForm.Controls.Add($label)

$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(20,120)
$textBox.Size = New-Object System.Drawing.Size(300,20)
$LocalResetPassForm.Controls.Add($textBox)

$cancelBtn                       = New-Object system.Windows.Forms.Button
$cancelBtn.BackColor             = "#ffffff"
$cancelBtn.text                  = "Cancel"
$cancelBtn.width                 = 90
$cancelBtn.height                = 30
$cancelBtn.location              = New-Object System.Drawing.Point(230,250)
$cancelBtn.Font                  = 'Microsoft Sans Serif,10'
$cancelBtn.ForeColor             = "#000"
$cancelBtn.DialogResult          = [System.Windows.Forms.DialogResult]::Cancel

$LocalResetPassForm.CancelButton = $cancelBtn
$LocalResetPassForm.Controls.Add($cancelBtn)
$LocalResetPassForm.controls.AddRange(@($Titel,$Description,$cancelBtn))
$LocalResetPassForm.Topmost      = $true
$LocalResetPassForm.Add_Shown({$textBox.Select()})
$result                          = $LocalResetPassForm.ShowDialog()
$stayopen =  2
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
    $username                    = $textBox.Text




    #find if user exists
    $finduser                    = (([adsisearcher]"(&(objectCategory=User)(samaccountname=$username))").findall()).properties.distinguishedname
    #if user does not exist
    if (!$finduser)
    { 
    #open error window and set try again button
    $Errorhandleform.controls.AddRange(@($Errorhandle,$TryAgainBtn))
    $tryagain                    = $Errorhandleform.ShowDialog()
    if ($tryagain -eq [System.Windows.Forms.DialogResult]::OK)
      {
       $stayopen =  3
      }
    }

    #Change the Password
    $pwd = "Password1"
    $user = (([adsisearcher]"(&(objectCategory=User)(samaccountname=$username))").findall()).properties.distinguishedname
    $ouser = [adsi]“LDAP://$user”
    $ouser.psbase.invoke(“SetPassword”,$pwd)
    $ouser.lockoutTime = 0
    $ouser.pwdLastSet = 0
    $ouser.psbase.CommitChanges()

    
    
}
}

此外,用户只能访问以该行为目标 C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy Bypass -File "T:\Scripts\passwordgui.ps1" 的快捷方式

【问题讨论】:

  • 从安全的角度来看,这听起来很可疑。密码应由用户自己设置/创建(例如,在首次登录时/密码重置后)授权人可能能够重置/使密码无效,但不应对其具有写入(或读取)权限。安全性应该发生在 AD 访问级别,而不是通过(隐藏?)脚本。
  • 我同意@iRon 但除此之外.. [1] 您的脚本使用$pwd 作为自定义变量名。你不应该使用它,因为它是一个Automatic Variable[2] 您正在使用大引号 (yuck),在代码中您需要避免这种情况。始终使用直引号。
  • 我应该将 $ pwd 更改为 $password 吗?我不太明白这真正改变了什么。还要感谢您指出我没有注意到的弯引号!授权人根本没有对密码或脚本的写入或读取权限。任何人都拥有的唯一访问权限是一个快捷方式,该快捷方式将打开一个要求输入用户名的 GUI,并将该用户名的密码更改为“密码”,并且只有在授权人允许密码更改权限的情况下。

标签: powershell active-directory elevated-privileges adsi


【解决方案1】:

尝试使用上述 PowerShell 并进行了修改,但它对我不起作用

您可以采用一种解决方法,通过授予特定 OU 的读取和重置用户密码的委派权限,允许特定用户更改其他用户的密码。

第 1 步:将 OU 的委派权限授予任何特定用户以更改同一 OU 用户的密码。

第 2 步本地允许本地该特定用户登录到 Active Directory 用户和计算机,并重置特定 OU 的其他用户的密码(在那个您已授予委托权限)。

参考:Allow log on locally - security policy setting (Windows 10) - Windows security | Microsoft Docs

步骤 3:使用特定用户登录 VM 以重置其他用户的密码

可以使用特定用户更改 OU 用户的密码

【讨论】:

  • 感谢您的意见!我计划实施您的第一步作为控制哪个用户可以更改哪个OU的密码的方法。用户自己无权访问活动目录,他们只能访问我给他们的快捷方式。我发布了我的完整代码以及上面的快捷方式代码,以便您可以对其进行测试并查看您的想法。
  • 缺少将 OU 的委派权限授予特定用户以读取和重置您正在输入的另一个用户的密码的代码。允许本地本地到该特定用户登录到 Active Directory 用户和计算机中的代码以及。
最近更新 更多