7

渗透基础——获得域用户的登录信息

 2 years ago
source link: https://3gstudent.github.io/%E6%B8%97%E9%80%8F%E5%9F%BA%E7%A1%80-%E8%8E%B7%E5%BE%97%E5%9F%9F%E7%94%A8%E6%88%B7%E7%9A%84%E7%99%BB%E5%BD%95%E4%BF%A1%E6%81%AF
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

渗透基础——获得域用户的登录信息

01 Jun 2022

0x00 前言


在域渗透中,获得了域控制器权限后,需要获得域用户的登录信息,包括域用户登录的IP地址和登录时间。通常使用的方法是查看域控制器的登录日志(Eventid=4624)。然而,人工从登录日志(Eventid=4624)中筛选出域用户登录的IP地址和登录时间需要耗费大量时间,不仅无效数据多,而且需要多次判断,所以我们需要编写程序来实现这个功能。

在实际使用过程中,为了能够适配多种环境,还需要支持本地和多种协议的远程登录。于是本文将要分享我的实现方法,开源两个工具,记录细节。

0x01 简介


本文将要介绍以下内容:

  • 通过EventLogSession实现
  • 通过WMI实现

0x02 通过EventLogSession实现


通过查询资料发现,通过EventLogSession不仅支持解析本地日志内容,还支持通过RPC远程解析日志,下面介绍关于EventLogSession的开发细节

1.输出Eventid=4624的日志内容

C Sharp实现代码:

using System;
using System.Diagnostics.Eventing.Reader;
namespace Test1
{
    class Program
    {
        static void Main(string[] args)
        {
            var session = new EventLogSession();
            string LogName = "Security";
            string XPathQuery = "*[System/EventID=4624]";
            EventLogQuery eventLogQuery = new EventLogQuery(LogName, PathType.LogName, XPathQuery)
            {
                Session = session,
                TolerateQueryErrors = true,
                ReverseDirection = true
            };
            using (EventLogReader eventLogReader = new EventLogReader(eventLogQuery))
            {
                eventLogReader.Seek(System.IO.SeekOrigin.Begin, 0);
                do
                {
                    EventRecord eventData = eventLogReader.ReadEvent();
                    if (eventData == null)
                        break;
                    Console.WriteLine(eventData.FormatDescription());                    
                    eventData.Dispose();
                } while (true);
            }
        }
    }
}

以上代码能够查询本地日志并输出日志的完整内容

2.xml格式解析

为了便于提取内容,可以选择将输出内容转换为xml格式

关键代码:

Console.WriteLine(eventData.ToXml());

输出内容示例:

<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'><System><Provider Name='Microsoft-Windows-Security-Auditing' Guid='{54849625-5478-4994-A5BA-3E3B0328C30D}'/><EventID>4624</EventID><Version>1</Version><Level>0</Level><Task>12544</Task><Opcode>0</Opcode><Keywords>0x8020000000000000</Keywords><TimeCreated SystemTime='2022-01-01T06:46:34.839752100Z'/><EventRecordID>393748</EventRecordID><Correlation/><Execution ProcessID='508' ThreadID='3448'/><Channel>Security</Channel><Computer>dc1.test.com</Computer><Security/></System><EventData><Data Name='SubjectUserSid'>S-1-0-0</Data><Data Name='SubjectUserName'>-</Data><Data Name='SubjectDomainName'>-</Data><Data Name='SubjectLogonId'>0x0</Data><Data Name='TargetUserSid'>S-1-5-21-254706111-4049838133-2416586677-2102</Data><Data Name='TargetUserName'>test1</Data><Data Name='TargetDomainName'>TEST</Data><Data Name='TargetLogonId'>0xa222fb5</Data><Data Name='LogonType'>3</Data><Data Name='LogonProcessName'>NtLmSsp </Data><Data Name='AuthenticationPackageName'>NTLM</Data><Data Name='WorkstationName'></Data><Data Name='LogonGuid'>{00000000-0000-0000-0000-000000000000}</Data><Data Name='TransmittedServices'>-</Data><Data Name='LmPackageName'>NTLM V2</Data><Data Name='KeyLength'>128</Data><Data Name='ProcessId'>0x0</Data><Data Name='ProcessName'>-</Data><Data Name='IpAddress'>192.168.1.3</Data><Data Name='IpPort'>45624</Data><Data Name='ImpersonationLevel'>%%1833</Data></EventData></Event>

从xml格式中,可直接提取出EventRecordID,关键代码:

XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(eventData.ToXml());
XmlNodeList recordid = xmldoc.GetElementsByTagName("EventRecordID");
Console.WriteLine(recordid[0].InnerText);

提取TargetUserName需要先取出Data的内容,再做一个筛选,关键代码:

XmlNodeList data = xmldoc.GetElementsByTagName("Data");
foreach (XmlNode value in data)
{
    if (value.OuterXml.Contains("TargetUserName"))
    {
        Console.WriteLine(value.InnerText);
    }
}

这里我们一共需要筛选出以下属性:

  • TargetUserSid
  • TargetDomainName
  • TargetUserName
  • IpAddress

在做字符匹配时,由于格式固定,所以我们可以从固定偏移位置得到对应的属性,避免多次判断,提高查询效率

关键代码:

XmlNodeList data = xmldoc.GetElementsByTagName("Data");
String targetUserSid = data[4].InnerText;
String targetDomainName = data[6].InnerText;
String targetUserName = data[5].InnerText;
String ipAddress = data[18].InnerText;

3.筛选判断条件

为了筛选出有效登录信息,这里对targetUserSidipAddress的长度做了判断,targetUserSid长度需要大于9,ipAddress长度需要大于8

关键代码:

XmlNodeList data = xmldoc.GetElementsByTagName("Data");
String targetUserSid = data[4].InnerText;
String targetDomainName = data[6].InnerText;
String targetUserName = data[5].InnerText;
String ipAddress = data[18].InnerText;
if (targetUserSid.Length > 9 && ipAddress.Length > 8)
{
    Console.WriteLine(targetUserSid);
    Console.WriteLine(targetDomainName);
    Console.WriteLine(targetUserName);
    Console.WriteLine(ipAddress);
}

4.支持筛选指定时间内的日志

可以通过修改搜索条件实现,关键代码:

string XPathQuery = "(Event/System/EventID=4624) and Event/System/TimeCreated/@SystemTime >= '2022-01-26T02:30:39' and Event/System/TimeCreated/@SystemTime <= '2022-01-26T02:31:00'";

至此,通过EventLogSession解析本地登录日志的实现代码如下:

using System;
using System.Diagnostics.Eventing.Reader;
using System.Xml;
namespace Test1
{
    class Program
    {
        static void Main(string[] args)
        {
            var session = new EventLogSession();
            string LogName = "Security";
            string XPathQuery = "(Event/System/EventID=4624) and Event/System/TimeCreated/@SystemTime >= '2022-01-26T02:30:39' and Event/System/TimeCreated/@SystemTime <= '2022-01-26T02:31:00'";

            EventLogQuery eventLogQuery = new EventLogQuery(LogName, PathType.LogName, XPathQuery)
            {
                Session = session,
                TolerateQueryErrors = true,
                ReverseDirection = true
            };
            int flagTotal = 0;
            int flagExist = 0;
            using (EventLogReader eventLogReader = new EventLogReader(eventLogQuery))
            {
                eventLogReader.Seek(System.IO.SeekOrigin.Begin, 0);
                do
                {
                    EventRecord eventData = eventLogReader.ReadEvent();
                    if (eventData == null)
                        break;
                    flagTotal++;
                    XmlDocument xmldoc = new XmlDocument();
                    xmldoc.LoadXml(eventData.ToXml());
                    XmlNodeList recordid = xmldoc.GetElementsByTagName("EventRecordID");                 
                    XmlNodeList data = xmldoc.GetElementsByTagName("Data");
                    String targetUserSid = data[4].InnerText;
                    String targetDomainName = data[6].InnerText;
                    String targetUserName = data[5].InnerText;
                    String ipAddress = data[18].InnerText;
                    if (targetUserSid.Length > 9 && ipAddress.Length > 8)
                    {
                        Console.WriteLine("[+] EventRecordID: " + recordid[0].InnerText);
                        Console.WriteLine("    TimeCreated  : " + eventData.TimeCreated);
                        Console.WriteLine("    UserSid:       " + targetUserSid);
                        Console.WriteLine("    DomainName:    " + targetDomainName);
                        Console.WriteLine("    UserName:      " + targetUserName);
                        Console.WriteLine("    IpAddress:     " + ipAddress);
                        flagExist++;
                    }
                    eventData.Dispose();
                } while (true);
                Console.WriteLine("Total: " + flagTotal + ", Exist: " + flagExist);
            }
        }

    }
}

5.支持远程登录

关键代码:

String server = "192.168.1.1";
String domain = "TEST";
String user = "Administrator";
String password = "Password@123";
SecureString securePwd = new SecureString();
foreach (char c in password)
{                
    securePwd.AppendChar(c);
}
var session = new EventLogSession(server, domain, user, securePwd, SessionAuthentication.Negotiate);
 

将以上代码整合,得出最终代码,代码已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpGetUserLoginIPRPC.cs

代码支持以下功能:

  • 可使用csc.exe进行编译,支持3.5和4.0
  • 支持本地和远程日志解析,远程日志解析使用RPC方式
  • 支持判断条件,可筛选指定日期
  • 自动提取信息:EventRecordID、TimeCreated、UserSid、DomainName、UserName和IpAddress

0x03 通过WMI实现


1.wmi语法测试

查询语法和属性名称可以借助wbemtest进行研究,关于wbemtest的用法可参考之前的文章《渗透基础——WMIC的使用》

查询Security日志需要管理员权限运行wbemtest

点击Query...

查询所有Security日志:

Select * from Win32_NTLogEvent Where Logfile = 'Security'

查询Eventid=4672的所有日志:

Select * from Win32_NTLogEvent Where Logfile = 'Security' AND EventCode = 4624

查询EventRecordID=113438的日志:

Select * from Win32_NTLogEvent Where Logfile = 'Security' AND RecordNumber = 113438

结合以上信息,我们不难写出wmic的查询命令:

wmic /namespace:\\root\cimv2 path win32_ntlogevent where "Logfile='Security' AND RecordNumber = 113438"

筛选出我们需要的信息:

wmic /namespace:\\root\cimv2 path win32_ntlogevent where "Logfile='Security' AND EventCode = 4624" get RecordNumber,TimeGenerated,Message

RecordNumber对应为EventRecordID,TimeGenerated为日志创建时间,Message中需要筛选出以下内容:

  • Security ID
  • Account Domain
  • Account Name
  • Source Network Address

筛选功能我们可以通过批处理或者Powershell实现,但是考虑到兼容性和后续利用接口的统一,这里同样选择C Sharp实现

2.输出RecordNumber=131的日志内容

实现代码:

using System;
using System.Management;
namespace Test2
{
    class Program
    {
        static void Main(string[] args)
        {
            String queryString = "SELECT * FROM Win32_NTLogEvent Where Logfile = 'Security' AND RecordNumber = 131";
            ManagementScope s = new ManagementScope("root\\CIMV2");
            SelectQuery q = new SelectQuery(queryString);
            ManagementObjectSearcher mos = new ManagementObjectSearcher(s, q);
            foreach (ManagementObject o in mos.Get())
            {
                PropertyDataCollection searcherProperties = o.Properties;
                foreach (PropertyData sp in searcherProperties)
                {
                    Console.WriteLine("Name = {0, -20}, Value = {1, -20}", sp.Name, sp.Value);
                }
            }
        }
    }
}

从输出结果中发现,同wmic的命令执行结果相同:

RecordNumber对应为EventRecordID,TimeGenerated为日志创建时间,Message中需要筛选出以下内容:

  • Security ID
  • Account Domain
  • Account Name
  • Source Network Address

3.筛选判断条件

为了筛选出有效登录信息,这里对targetUserSidipAddress的长度做了判断,targetUserSid长度需要大于9,ipAddress长度需要大于8

关键代码:

String Message = o.GetPropertyValue("Message").ToString();
int pos1 = Message.LastIndexOf("Security ID");
int pos2 = Message.LastIndexOf("Account Name");
int pos3 = Message.LastIndexOf("Account Domain");
int pos4 = Message.LastIndexOf("Logon ID");
int pos5 = Message.LastIndexOf("Source Network Address");
int pos6 = Message.LastIndexOf("Source Port");
int length1 = pos2 - pos1 - 16;
int length2 = pos4 - pos3 - 20;
int length3 = pos3 - pos2 - 17;
int length4 = pos6 - pos5 - 27;
if (length1 < 0 || length2 < 0 || length3 < 0 || length4 < 0)
    continue;
String targetUserSid = Message.Substring(pos1+14, length1);
String targetDomainName = Message.Substring(pos3 + 17, length2);
String targetUserName = Message.Substring(pos2 + 15, length3);
String ipAddress = Message.Substring(pos5 + 24, length4);
if (targetUserSid.Length > 9 && ipAddress.Length > 8)
{
    Console.WriteLine("[+] EventRecordID: " + o.GetPropertyValue("RecordNumber"));
    Console.WriteLine("    TimeCreated  : " + o.GetPropertyValue("TimeGenerated"));
    Console.WriteLine("    UserSid:       " + targetUserSid);
    Console.WriteLine("    DomainName:    " + targetDomainName);
    Console.WriteLine("    UserName:      " + targetUserName);
    Console.WriteLine("    IpAddress:     " + ipAddress);
}

4.支持筛选指定时间内的日志

可以通过修改搜索条件实现,关键代码:

String queryString = "SELECT * FROM Win32_NTLogEvent Where Logfile = 'Security' AND EventCode = 4624 AND TimeGenerated>=20210526 AND TimeGenerated<=20220426";

至此,通过WMI解析本地登录日志的实现代码如下:

using System;
using System.Management;
namespace Test2
{
    class Program
    {
        static void Main(string[] args)
        {
            String queryString = "SELECT * FROM Win32_NTLogEvent Where Logfile = 'Security' AND EventCode = 4624 AND TimeGenerated>=20210526 AND TimeGenerated<=20220426";
            ManagementScope s = new ManagementScope("root\\CIMV2");
            SelectQuery q = new SelectQuery(queryString);
            ManagementObjectSearcher mos = new ManagementObjectSearcher(s, q);
            int flagTotal = 0;
            int flagExist = 0;
            foreach (ManagementObject o in mos.Get())
            {
                flagTotal++;
                String Message = o.GetPropertyValue("Message").ToString();
                int pos1 = Message.LastIndexOf("Security ID");
                int pos2 = Message.LastIndexOf("Account Name");
                int pos3 = Message.LastIndexOf("Account Domain");
                int pos4 = Message.LastIndexOf("Logon ID");
                int pos5 = Message.LastIndexOf("Source Network Address");
                int pos6 = Message.LastIndexOf("Source Port");
                int length1 = pos2 - pos1 - 16;
                int length2 = pos4 - pos3 - 20;
                int length3 = pos3 - pos2 - 17;
                int length4 = pos6 - pos5 - 27;
                if (length1 < 0 || length2 < 0 || length3 < 0 || length4 < 0)
                    continue;
                String targetUserSid = Message.Substring(pos1+14, length1);
                String targetDomainName = Message.Substring(pos3 + 17, length2);
                String targetUserName = Message.Substring(pos2 + 15, length3);
                String ipAddress = Message.Substring(pos5 + 24, length4);
                {
                    Console.WriteLine("[+] EventRecordID: " + o.GetPropertyValue("RecordNumber"));
                    Console.WriteLine("    TimeCreated  : " + o.GetPropertyValue("TimeGenerated"));
                    Console.WriteLine("    UserSid:       " + targetUserSid);
                    Console.WriteLine("    DomainName:    " + targetDomainName);
                    Console.WriteLine("    UserName:      " + targetUserName);
                    Console.WriteLine("    IpAddress:     " + ipAddress);
                    flagExist++;
                }             
            }
            Console.WriteLine("Total: " + flagTotal + ", Exist: " + flagExist);
        }
    }
}

5.支持远程登录

关键代码:

var opt = new ConnectionOptions(); ;
opt.Username = "TEST\\Administrator";
opt.Password = "Password@123";
ManagementScope s = new ManagementScope("\\\\192.168.1.1\\root\\CIMV2", opt);

将以上代码整合,得出最终代码,代码已上传至github,地址如下:

https://github.com/3gstudent/Homework-of-C-Sharp/blob/master/SharpGetUserLoginIPWMI.cs

代码支持以下功能:

  • 可使用csc.exe进行编译,支持3.5和4.0
  • 支持本地和远程日志解析,远程日志解析使用WMI方式
  • 支持判断条件,可筛选指定日期
  • 自动提取信息:EventRecordID、TimeCreated、UserSid、DomainName、UserName和IpAddress

0x04 小结


本文介绍了获得域用户登录信息的实现细节,开源两个工具SharpGetUserLoginIPRPC.csSharpGetUserLoginIPWMI.cs,在通信效率上,RPC要快于WMI。


LEAVE A REPLY


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK