mirror of
https://github.com/Yamato-Security/WELA.git
synced 2026-10-06 06:25:13 +02:00
Add a native local failed-logon audit probe (#444)
* Add native local nonexistent-account failed-logon probe * Match actual MSV1 local authentication event package * Refuse coerced identity and authentication receipt fields * Preserve explicit UTC DateTime receipts on older PowerShell7 * Reject unknown failed-logon probe options before dispatch
This commit is contained in:
1 parent
203fdfc942
commit
6d228fedef
14 files changed
+454
-1
No files matched your search
@@ -0,0 +1,39 @@
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.RegularExpressions;
|
||||
namespace Wela.FailedLogonProbe {
|
||||
public sealed class Attempt {
|
||||
public string UserName, Domain, StartedUtc, CompletedUtc, Clock;
|
||||
public int MissingAccountStatus, LogonType, LogonProvider, NativeError;
|
||||
public bool Succeeded;
|
||||
}
|
||||
public static class Native {
|
||||
[DllImport("kernel32.dll", ExactSpelling=true)] private static extern void GetSystemTimePreciseAsFileTime(out long value);
|
||||
[DllImport("Netapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] private static extern int NetUserGetInfo(string server,string user,int level,out IntPtr buffer);
|
||||
[DllImport("Netapi32.dll", ExactSpelling=true)] private static extern int NetApiBufferFree(IntPtr buffer);
|
||||
[DllImport("advapi32.dll", CharSet=CharSet.Unicode, ExactSpelling=true, SetLastError=true)]
|
||||
[return:MarshalAs(UnmanagedType.Bool)] private static extern bool LogonUserW(string user,string domain,string password,int type,int provider,out IntPtr token);
|
||||
[DllImport("kernel32.dll", ExactSpelling=true, SetLastError=true)]
|
||||
[return:MarshalAs(UnmanagedType.Bool)] private static extern bool CloseHandle(IntPtr handle);
|
||||
public static DateTime UtcNow(){long value;GetSystemTimePreciseAsFileTime(out value);return DateTime.FromFileTimeUtc(value);}
|
||||
public static Attempt Run(string nonce){
|
||||
if(!Regex.IsMatch(nonce??"","\\A[a-f0-9]{32}\\z"))throw new ArgumentException("A generated lowercase GUID nonce is required.");
|
||||
string user="WL"+nonce.Substring(0,18);IntPtr buffer=IntPtr.Zero;
|
||||
int missing;
|
||||
try{missing=NetUserGetInfo(null,user,0,out buffer);}finally{if(buffer!=IntPtr.Zero)NetApiBufferFree(buffer);}
|
||||
// Never attempt a known or unreadable real account, and never query a domain server.
|
||||
if(missing!=2221)throw new InvalidOperationException("Exact local account absence is not established; NetUserGetInfo="+missing);
|
||||
Attempt result=new Attempt();result.UserName=user;result.Domain=".";result.MissingAccountStatus=missing;result.LogonType=3;result.LogonProvider=2;result.Clock="GetSystemTimePreciseAsFileTime";
|
||||
IntPtr token=IntPtr.Zero;
|
||||
result.StartedUtc=UtcNow().ToString("o");
|
||||
try{
|
||||
// This fixed public dummy is not a credential. There is exactly one attempt.
|
||||
result.Succeeded=LogonUserW(user,".","WELA-public-noncredential",3,2,out token);
|
||||
result.NativeError=result.Succeeded?0:Marshal.GetLastWin32Error();
|
||||
result.CompletedUtc=UtcNow().ToString("o");
|
||||
}finally{if(token!=IntPtr.Zero && !CloseHandle(token))throw new Win32Exception(Marshal.GetLastWin32Error());}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user