Угон COM. Как работает кража сессии через механизм COM

Содержание статьи

  • Logon Sessions
  • Session Moniker
  • Запуск процесса в чужой сессии
  • Утечка хеша пароля при смене обоев
  • Выводы

В Windows каж­дому поль­зовате­лю при вхо­де на устрой­ство при­писы­вает­ся своя сес­сия. Как угнать сес­сию поль­зовате­ля, если жер­тва решила зай­ти на уже взло­ман­ное устрой­ство? В этой статье поз­накомим­ся еще с одним спо­собом повыше­ния при­виле­гий — через кра­жу сес­сий с помощью COM-клас­сов.

Дав­ным‑дав­но, ког­да небо было голубее, деревья зеленее, а чай сла­ще, я писал статью «Пос­тавщик небезо­пас­ности. Как Windows рас­кры­вает пароль поль­зовате­ля». В ней мы под­робно рас­смот­рели этап вхо­да поль­зовате­ля в сис­тему, начиная от при­зем­ления пятой точ­ки за компь­ютер в офи­се и закан­чивая получе­нием дос­тупа к рабоче­му сто­лу.

Впро­чем, в той статье я перечис­лил далеко не все спо­собы воз­дей­ствия на поль­зовате­ля. Сущес­тву­ет еще одна ата­ка — кра­жа сес­сии. И осу­щес­твим мы ее с помощью COM!

 

Logon Sessions

Итак, нач­нем с неболь­шого лик­беза. Logon Session (она же прос­то сес­сия) — это как куки бра­узе­ра. Впол­не понят­ная вещица, по которой сис­тема может однознач­но опре­делить, какой поль­зователь к ней обра­щает­ся.

От­лича­ются сес­сии по уни­каль­ному номеру, имя ему LUID (Locally Unique IDentifier). Собс­твен­но, это все, что нуж­но Windows для иден­тифика­ции сес­сии поль­зовате­ля.

typedef struct _LUID { ULONG LowPart; LONG HighPart;} LUID, *PLUID;

  • LowPart — содер­жит необ­ходимое чис­ловое зна­чение;
  • HighPart — обыч­но нолик.

Изу­чить сущес­тву­ющие Logon-сес­сии мож­но через API LsaEnumerateLogonSessions().

Я дос­таточ­но под­робно опи­сывал эту фун­кцию и ее исполь­зование в статье про GIUDA. Для раз­нооб­разия перепи­шем на C#.

using System;using System.Runtime.InteropServices;using System.Security.Principal;class Program{ [DllImport("Secur32.dll", SetLastError = false)] private static extern int LsaEnumerateLogonSessions(out ulong LogonSessionCount, out IntPtr LogonSessionList); [DllImport("Secur32.dll", SetLastError = false)] private static extern int LsaGetLogonSessionData(IntPtr LogonSession, out IntPtr ppLogonSessionData); [DllImport("Secur32.dll")] private static extern uint LsaFreeReturnBuffer(IntPtr buffer); [StructLayout(LayoutKind.Sequential)] private struct LSA_UNICODE_STRING { public ushort Length; public ushort MaximumLength; public IntPtr Buffer; } [StructLayout(LayoutKind.Sequential)] private struct SECURITY_LOGON_SESSION_DATA { public uint Size; public LUID LogonId; public LSA_UNICODE_STRING UserName; public LSA_UNICODE_STRING LogonDomain; public LSA_UNICODE_STRING AuthenticationPackage; public uint LogonType; public uint Session; public IntPtr Sid; public long LogonTime; } [StructLayout(LayoutKind.Sequential)] private struct LUID { public uint LowPart; public int HighPart; } private static string GetString(LSA_UNICODE_STRING unicodeString) { return Marshal.PtrToStringUni(unicodeString.Buffer); } static void Main() { var result = LsaEnumerateLogonSessions(out var count, out var luidPtr); if (result != 0) { Console.WriteLine("LsaEnumerateLogonSessions failed"); return; } var iter = luidPtr; for (ulong i = 0; i < count; i++) { result = LsaGetLogonSessionData(iter, out var sessionDataPtr); if (result == 0) { var sessionData = Marshal.PtrToStructure<SECURITY_LOGON_SESSION_DATA>(sessionDataPtr); var userName = GetString(sessionData.UserName); var domainName = GetString(sessionData.LogonDomain); Console.WriteLine($"UserName: {userName}"); Console.WriteLine($"LogonDomain: {domainName}"); Console.WriteLine("---------------------------"); LsaFreeReturnBuffer(sessionDataPtr); } iter = IntPtr.Add(iter, Marshal.SizeOf(typeof(LUID))); } LsaFreeReturnBuffer(luidPtr); }}

При­мер работы прог­раммы

Ви­дим стан­дар­тный импорт необ­ходимых фун­кций через PInvoke с пос­леду­ющим вызовом в опре­делен­ном поряд­ке.

Источник: xakep.ru

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *