Некоторые картинки не загружаются из РФ и РК, используйте VPN.

четверг, 28 декабря 2023 г.

Обновление пароля при входе на сервер RDP

Попалась интересная задачка: генерировать пароль для RDP и выдавать его в телеграм боте.

Ну тут множество готовых решений есть, но решили сделать свое. Плюс я внес корректив в план и описал его так:

  1. Пользователь приходит на работу
  2. Открывает ТГ, проваливается в бота и запрашивает пароль (команда=имя)
  3. Бот смотрит на сервере файл с именем команды и возвращает содержимое
  4. Пользователь заходит на сервер
  5. На сервере срабатывает триггер и запускает скрипт
  6. Скрипт генерирует пароль и отправляет его на сервер бота в файл
  7. Пользователь пошел кушац, комп заблокировался
  8. Пользователь вернулся и начинает с шага 2
Отмечу тот факт, что пароль меняет не сервер бота по запросу, а конечный сервер при входе пользователя, так мы повышаем шансы смены пароля и запись его на сервере с ботом.

Сразу ремарка, в доменных сетях это тот еще геморрой, так как смена пароля приведет к запросу пароля при попытке доступа к ресурсам (Exchange, SMB)

LogonType подробно описан здесь, а это запрос для настраиваемого просмотра событий. Мне нужно было выдернуть только подключение (в т.ч. и вход) к сеансу RDP и навесить выполнение скрипта (ниже)

<QueryList>
  <Query Id='0' Path='Security'>
    <Select Path='Security'>*[System[(EventID=4624)] and EventData[Data[@Name='LogonType']=10 and (Data[@Name='TargetUserName']!='SYSTEM' and Data[@Name='TargetUserName']!='Администратор')]]</Select>
  </Query>
</QueryList>

А это простенький скрипт. К сожалению щелчком пальцев нельзя выдернуть имя пользователя, я решил этот вопрос так:

Param(
    [string]$userName
)
$FileLog="C:\usr\reloadCred"
$date=$(Get-date -Format "dd.MM.yyyy HH:mm")
$dateEvent= $(Get-date -Format "yyyy-MM-ddTHH:mm:ss.fffZ")

function Generate-Password {
    param (
        [Parameter(Mandatory)]
        [int] $length,
        [int] $amountOfNonAlphanumeric = 1
    )
    Add-Type -AssemblyName 'System.Web'
    return [System.Web.Security.Membership]::GeneratePassword($length, $amountOfNonAlphanumeric)
}

function GetUsernameFromEventList {
    $userName=""
	$query=@"
<QueryList>
  <Query Id='0' Path='Security'>
    <Select Path='Security'>*[System[(EventID=4624) and TimeCreated[@SystemTime<='$dateEvent']] and EventData[Data[@Name='LogonType']=10 and (Data[@Name='TargetUserName']!='SYSTEM' and Data[@Name='TargetUserName']!='Администратор')]]</Select>
  </Query>
</QueryList>
"@
	$result = Get-WinEvent -FilterXML $query -MaxEvents 1  
    if ($result.count -eq 1) {
        $eventXML = ([xml]$result[0].ToXml()).Event
        $userName=($eventXML.EventData.Data | Where-Object {$_.Name -eq "TargetUserName"}).'#text'
    }else{
        Out-File -FilePath $FileLog -InputObject $("$date	Events not found") -Width 50 -Append -Force
    }
	return $userName
}
if (!$userName){
    $userName=GetUsernameFromEventList
} else{
    $check=(Get-LocalUser).Name -Contains $userName
    if (!$check){
        Write-host("$userName does not exist")
        $userName=""
    }
}
if ( $userName -ne "") {
    $NewPassword=Generate-Password 10
    $Secure_NewPassword = ConvertTo-SecureString $NewPassword -AsPlainText -Force
    
    try{
        Set-LocalUser -Password $Secure_NewPassword -Name $userName -ErrorAction Stop
    }
    catch {
        Write-Host("FAIL")
        exit
    } 
    $command=$("echo '$NewPassword' >/pswd/$($userName.toLower())")
    Write-Host($str)
    Out-File -FilePath $FileLog -InputObject $("$date	$userName	$NewPassword	$command") -Width 50 -Append -Force
    Start-Process -FilePath "C:\Program Files (x86)\Bitvise SSH Client\sexec.exe" -ArgumentList "-profile=`"C:\USR\wms_bot.tlp`"","-cmd=`"$command`"" -Wait -RedirectStandardOutput C:\usr\reloadCred2
}

У скрипта есть косяк, он выдергивает события по времени равные или старше момента запуска, т.о. мы пытаемся избежать некорректной обработки "если два пользователя вошли с малым интервалом".

В конце скрипта идет отправка пароля на сервер при помощи настроенного профиля Bitvise с ssh ключом.

Также скрипт можно запустить вручную с указанием имени пользователя как параметр

Для лучшей работы не забываем настраивать автоотключения сеанса при простое.


Ремарка по поводу реализации, со стороны бота можно настроить api и запись в БД, но это удорожание разработки бота.


ВАЖНО

При установке на другой комп, также не в домене, выяснилось, что событие 4624 с кодом 10 регистрируется только при входе. При подключении к уже созданному сеансу регистрируется событие с кодом 7. Я пока не могу понять, почему так.

Поэтому в коде и в фильтре надо поправить:

Data[@Name='LogonType']=10

на

(Data[@Name='LogonType']=10 or Data[@Name='LogonType']=7)

Но, событие с кодом 7 может вызываться несколько раз за одно подключение, поэтому варик так себе

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

Отправить комментарий