摘要
代码解释
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用户的简单登录日志
python winlogon.py administrator:1qaz@WSX@192.168.136.129 -tuser administrator -loglevel all #使用135端口获取远程主机administrator用户的完整登录日志