21 августа 2017 г.

PowerShell: удалённое выполнение в безопасном режиме

В продолжение предыдущей статьи.

Итак,
 если вы просрали пароль на KES10 и имеете только удалённый доступ к хосту.
 или же, если вам надо запустить какую-то команду в безопасном  режиме

Потребуется некоторая предварительная подготовка

1. Создать на хосте пользователя с правами локального администратора
2. Создать на хосте папку (например, c:\Scripts) и скопировать в неё (только!) необходимые скрипты.

Ваши скрипты безопасного режима должны соответствовать маске SafeMode-*.ps1. Скрипт ищет их в той же папке, где находится сам.

Если же вам надо запустить что-то другое, это что-то другое можно указать в параметре -SafeCommand скрипта. Например, вы можете указать свой CMD файл и вписать в него всё,
что хотели.

Использование

Execute-SafeMode.ps1 -UserName Admin -DomainName MyDomain -Password AdminPassword -RebootImmediately -SafeCommand "cmd.exe /c c:\windows\temp\1.tmp"

Execute-SafeMode.ps1 -UserName Admin -Password AdminPassword

Параметры

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

  DomainName - необязательный. Если не указан, то принимается название хоста.

  Password - обязательный. пароль пользователя с административными правами

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

  SafeCommand - необязательный. команда для безопасного режима

Порядок работы скрипта

При запуске в нормальном режиме
  •  скрипт устанавливает себя в ключ RunOnce для запуска в безопасном режиме
  • скрипт устанавливает указанные логин и пароль для автоматического входа (AutoAdminLogon)
  • скрипт устанавливает флаг загрузки в безопасном режиме (с помощью bcdedit)
  • скрипт выполняет перезагрузку, если указан флаг немедленного исполнения
При запуске в безопасном режиме
  • происходит автоматический вход в систему под указанной учётной записью
  • происходит автоматический запуск скрипта
  • скрипт выполняет команду, переданную в параметре SafeCommand
  • скрипт ищет рядом с собой файлы по маске SafeBoot-*.ps1 и последовательно выполняет их. Порядок выполнения не определён
  • скрипт удаляет параметры автоматического входа в систему (AutoAdminLogon)
  • скрипт устанавливает флаг загрузки в нормальном режиме
  • скрипт выполняет перезагрузку

# Execute-SafeMode.ps1
# LastUpdate: 21.08.2017 17:30:04
# 
[CmdletBinding()]
Param(
    [string]$UserName          = "",
    [string]$Domain            = "",
    [string]$Password          = "",
    [switch]$RebootImmediately = $false,
    [string]$SafeCommand       = ""
)

$SaveVerbosePreference = $VerbosePreference
$VerbosePreference = 'Continue'
$SaveCurrentDir = Get-Location


$keyRunOnce = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce"
$keyWinlogon = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"

$runEntry   = "*ExecuteSafeMode"

$SaveVerbosePreference = $VerbosePreference
$VerbosePreference = 'Continue'

$PluginScriptMask = "SafeMode-*.ps1"


$MyScript = $MyInvocation.MyCommand.Path 
$WorkDir = Split-Path ($MyInvocation.MyCommand.Path) -Parent
$SafeBootScript = "$WorkDir\safeboot.cmd"
$LogFile = [IO.Path]::ChangeExtension($MyInvocation.MyCommand.Path, "log")

Set-Location $WorkDir


$IsNormalBoot = (Get-WmiObject -Class Win32_ComputerSystem | 
    Select-Object -ExpandProperty BootupState) -like '*Normal*'



function myeval($cmd) 
{
    if ($cmd[0] -eq '"')    
    {
        Invoke-Expression "& $cmd"
    }
    else
    {
        Invoke-Expression $cmd 
    }
}

function Write-RegistryString($Path, $Name, $Value)
{
    Remove-ItemProperty -Path $Path -Name $Name | Out-Null
    New-ItemProperty -Path $Path -Name $Name -Value $Value -PropertyType String
}

Write-Verbose "Arguments is"
Write-Verbose "`tUserName          : $UserName"
Write-Verbose "`tDomain            : $Domain"
Write-Verbose "`tPassword          : $Password"
Write-Verbose "`tRebootImmediately : $RebootImmediately"
Write-Verbose "`tSafeCommand       : $SafeCommand"
Write-Verbose ""
Write-Verbose "`tIsNormalBoot      : $IsNormalBoot"


# проверка аргументов
if ($IsNormalBoot) 
{
    if ( [string]::IsNullOrEmpty($UserName) )
    {
        throw New-Object System.ArgumentException("Parameter -UserName must not be NULL")
    }
    if ( [string]::IsNullOrEmpty($Password) )
    {
        throw New-Object System.ArgumentException("Parameter -Password must not be NULL")
    }
}


if ($IsNormalBoot)
{
    # создаём файл safeboot.cmd с указанной командой
    $CommandLine = "PowerShell.exe -NoProfile -ExecutionPolicy Bypass -File {0} -SafeCommand `"{1}`"" -f $MyScript, $SafeCommand
    $CommandLine | Set-Content -Path $SafeBootScript -Force

    # устанавливаем в HKLM\\RunOnce наш скрипт safeboot.cmd
    if ( !(Test-Path -Path $keyRunOnce) )
    {
        New-Item -Path $keyRunOnce -Force
    }

    Remove-ItemProperty -Path $keyRunOnce -Name $runEntry -Force | Out-Null
    New-ItemProperty -Path $keyRunOnce -Name $runEntry -Value $SafeBootScript -PropertyType String -Force | Out-Null

    # назначаем AutoAdminLogon пользователя
    if ( !(Test-Path -Path $keyWinlogon) ) 
    {
        New-Item -Path $keyWinlogon -Force | Out-Null
    }
    Remove-ItemProperty -Path $keyWinlogon -Name "AutoAdminLogon" | Out-Null
    New-ItemProperty -Path $keyWinlogon -Name "AutoAdminLogon" -Value "1" -PropertyType String -Force

    if ( [string]::IsNullOrEmpty($Domain) ) 
    {
        $Domain = $env:COMPUTERNAME
    }

    Remove-ItemProperty -Path $keyWinlogon -Name "DefaultDomainName" -Force | Out-Null
    New-ItemProperty -Path $keyWinlogon -Name "DefaultDomainName" -Value $Domain 
        -PropertyType String -Force | Out-Null
    Remove-ItemProperty -Path $keyWinlogon -Name "DefaultUserName" -Force | Out-Null
    New-ItemProperty -Path $keyWinlogon -Name "DefaultUserName" -Value $UserName 
        -PropertyType String -Force | Out-Null
    Remove-ItemProperty -Path $keyWinlogon -Name "DefaultPassword" -Force | Out-Null
    New-ItemProperty -Path $keyWinlogon -Name "DefaultPassword" -Value $Password 
        -PropertyType String -Force | Out-Null

    # устанавливаем следующую перезагрузку в безопасном режиме
    & bcdedit /set `{current`} safeboot minimal

    # перегружаемся
    if ($RebootImmediately)
    {
        Restart-Computer -Force
    }
} 
else # Safe Mode
{
    # удаляем Auto Logon пользователя
    if ( !(Test-Path -Path $keyWinlogon) ) 
    {
        New-Item -Path $keyWinlogon -Force
    }
    Remove-ItemProperty -Path $keyWinlogon -Name "AutoAdminLogon" | Out-Null
    New-ItemProperty -Path $keyWinlogon -Name "AutoAdminLogon" -Value "0" -PropertyType String | Out-Null

    Remove-ItemProperty -Path $keyWinlogon -Name "DefaultPassword" -Force | Out-Null
    Remove-ItemProperty -Path $keyWinlogon -Name "DefaultDomainName" -Force | Out-Null
    Remove-ItemProperty -Path $keyWinlogon -Name "DefaultUserName" -Force | Out-Null

    # устанавливаем следующую перезагрузку в нормальный режим
    & bcdedit /deletevalue `{current`} safeboot

    # удаляем SafeBoot.cmd
    Remove-Item -Path $SafeBootScript -Force | Out-Null

    # выполняем команду пользователя
    if ( ![string]::IsNullOrEmpty($SafeCommand) )
    {
        try
        {
            "Invoke $SafeCommand :" | Out-File -FilePath $LogFile -Append -Encoding unicode
            myeval $SafeCommand | Out-File -FilePath $LogFile -Append -Encoding unicode
        }
        catch
        {
            "ERROR: $($_.Exception.Message)" | Out-File $LogFile -Append -Encoding unicode
        }
    }

    Get-ChildItem -Path $WorkDir -Filter "SafeMode-*.ps1" |
        Select-Object -ExpandProperty FullName |
        ForEach-Object {
            $SafeModePlugin = $_
            try
            {
                "Invoke $SafeModePlugin :" | Out-File -FilePath $LogFile -Append -Encoding unicode
                Invoke-Expression -Command $SafeModePlugin | Out-File -FilePath $LogFile -Append -Encoding unicode
                "Invoke $SafeModePlugin : OK" | Out-File -FilePath $LogFile -Append -Encoding unicode
            }
            catch
            {
                "ERROR: $($_.Exception.Message)" | Out-File -FilePath $LogFile -Append -Encoding unicode
            }
        }

    # перегружаемся 
    & shutdown.exe -r -f -t 3
}


$VerbosePreference = $SaveVerbosePreference
Set-Location $SaveCurrentDir

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