摘要
代码解释
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用户的完整登录日志


