基于impacket的远程登录日志获取工具-winlogon

基于impacket的远程登录日志获取工具-winlogon

H1ng 内网渗透 评论0次 2025-05-12 2025-05-12
59

摘要

在网络安全与系统管理的场景中,获取目标主机的登录日志对于监控用户活动、排查安全问题至关重要。本文聚焦于使用 Python 的 Impacket 库来远程获取目标主机特定用户的登录日志的代码实现,详细阐述其开发过程与功能特性。
在开发过程中,首先建立 SMB 连接以与目标主机进行通信,并利用 Impacket 库提供的功能创建 WMI 客户端,从而执行 WMI 查询语句来获取安全日志。查询语句针对特定的事件 ID(4624 和 4648)进行筛选,这些事件 ID 与用户登录相关,同时根据指定的用户名进行日志筛选。
在处理查询结果时,由于 Impacket 返回的结果集具有特殊的结构,需要使用特定的方法(如 Next ())来迭代结果,并正确访问 WMI 对象的属性(如使用 getProperties () 方法)。对于事件消息,还实现了提取 IP 地址的功能,通过正则表达式匹配不同格式的事件消息来获取源 IP 地址,以提供更详细的登录信息。
该脚本的主要功能是远程查询目标主机上特定用户的 RDP 登录日志。通过命令行参数指定目标主机、用户名、域、用于 WMI 连接的用户名和密码,脚本能够连接到目标主机的 WMI 服务,查询相关的安全日志,并输出符合条件的日志记录。输出内容包括事件时间、事件 ID、源 IP 地址以及事件消息等关键信息,方便管理员对登录活动进行分析。

代码解释

WQL查询

使用Win32_NTLogEvent查询4624和4648两个ID的日志信息。

SELECT * FROM Win32_NTLogEvent 
WHERE LogFile = 'Security' 
AND (EventCode = 4624 OR EventCode = 4648)
AND Message LIKE '%张三%'

impacket中使用ExecQuery方法进行WQL查询,代码如下:

SQL = f"""
        SELECT * FROM Win32_NTLogEvent 
        WHERE LogFile = 'Security' 
        AND (EventCode = 4624 OR EventCode = 4648)
        AND Message LIKE '%{self.__tuser}%'
        """
dcom = DCOMConnection(addr, self.__username, self.__password, self.__domain, self.__lmhash, self.__nthash,
                      self.__aesKey, oxidResolver=True, doKerberos=self.__doKerberos, kdcHost=self.__kdcHost, remoteHost=self.__remoteHost)
try:
    iInterface = dcom.CoCreateInstanceEx(wmi.CLSID_WbemLevel1Login, wmi.IID_IWbemLevel1Login)
    iWbemLevel1Login = wmi.IWbemLevel1Login(iInterface)
    iWbemServices = iWbemLevel1Login.NTLMLogin('//./root/cimv2', NULL, NULL)
    iWbemLevel1Login.RemRelease()

    results = iWbemServices.ExecQuery(SQL)

返回结果处理

返回结果是一个IEnumWbemClassObject类,使用next方法进行遍历,保存到events数组中。

if not results:
    print(f"未找到用户 '{self.__tuser}' 的RDP登录日志")
    return

events = []
count = 0

# 使用Next()方法获取结果
while True:
    try:
        event = results.Next(0xffffffff, 1)[0]
        events.append(event)
        count += 1
    except:
        break


if count == 0:
    print(f"未找到用户 '{self.__tuser}' 的RDP登录日志2")
    dcom.disconnect()
    sys.exit(0)

# 输出结果
print(f"找到 {count} 条与用户 '{username}' 相关的RDP登录记录:")
print("-" * 80)

遍历后的events数组中都是IWbemClassObject对象,使用getProperties方法获取对象的属性值。

for event in events:
    # 解析事件时间
    try:
        #获取属性值
        time_str = event.getProperties()['TimeGenerated']['value']
        # 转换Windows时间格式为Python可解析的格式
        match = re.match(r"(\d{14}\.\d{6})([+-]\d{3})", time_str)
        if not match:
            raise ValueError("Invalid Windows time format")

        datetime_str, tz_str = match.groups()
        dt = datetime.strptime(datetime_str, '%Y%m%d%H%M%S.%f%z')
        # 处理时区偏移
        tz_sign = tz_str[0]
        tz_hours = int(tz_str[1:])
        offset = timedelta(hours=tz_hours) if tz_sign == "+" else timedelta(hours=-tz_hours)
        dt = dt.replace(tzinfo=timezone(offset))

        formatted_time = dt
    except (ValueError, AttributeError, KeyError) as e:
        formatted_time = time_str

    # 获取事件ID和消息
    try:
        event_id = event.getProperties()['EventCode']['value']
    except (AttributeError, KeyError):
        event_id = "未知"

    try:
        message = event.getProperties()['Message']['value']
    except (AttributeError, KeyError):
        message = "消息不可用"

    # 提取IP地址
    ip_address = self.extract_ip_address(message)

    print(f"时间: {formatted_time}")
    print(f"事件ID: {event_id}")
    print(f"源IP: {ip_address}")
    if(self.__loglevel == "all"):
        print(f"消息: {message[:500]}...")  # 截断长消息
    print("-" * 80)

脚本使用

参数说明

以下参数为脚本新增的,tuser必须指定,loglevel有默认值,非必须。其余参数与impacket横向脚本的参数保持一致。
-tuser     指定需要查询的用户名
-loglevel  指定日志显示等级,默认为normal,仅输出登录时间、登录事件ID、登录源IP;设置为all会显示详细信息(最多显示500字符)
-USESMB    是否使用445端口,默认为false,只使用135端口

使用方法举例

python winlogon.py administrator:1qaz@WSX@192.168.136.129 -tuser administrator  #使用135端口获取远程主机administrator用户的简单登录日志

image.png

python winlogon.py administrator:1qaz@WSX@192.168.136.129 -tuser administrator -loglevel all  #使用135端口获取远程主机administrator用户的完整登录日志

image.png

猜您喜欢

3文章个数(个)
1本月更新(个)
1本周更新(个)
1今日更新(个)