mirror of
https://github.com/nikaiw/VMkatz
synced 2026-09-25 07:51:06 +00:00
Filter false positive LSASS sessions from garbage memory
Add LUID and username validation to reject phantom sessions caused by wrong MSV struct offsets being applied to valid memory. Filters: - LUID must have high 32 bits zero (real Windows LUIDs are 32-bit) - Username must contain at least one alphanumeric char, no file paths or control characters Tested on SilverFort-AD (Server 2022 DC, 32GB): eliminates 3 false sessions while preserving all 10 legitimate ones.
This commit is contained in:
+30
-2
@@ -277,7 +277,7 @@ fn walk_session_buckets(
|
||||
.read_win_unicode_string(current + offsets.domain)
|
||||
.unwrap_or_default();
|
||||
|
||||
if !username.is_empty() && luid != 0 {
|
||||
if is_plausible_luid(luid) && is_plausible_username(&username) {
|
||||
let (logon_type, session_id, logon_time, logon_server, sid) =
|
||||
extract_session_metadata(vmem, current, offsets);
|
||||
let info = MsvSessionInfo {
|
||||
@@ -333,7 +333,7 @@ fn walk_session_list(
|
||||
.read_win_unicode_string(current + offsets.domain)
|
||||
.unwrap_or_default();
|
||||
|
||||
if !username.is_empty() && luid != 0 {
|
||||
if is_plausible_luid(luid) && is_plausible_username(&username) {
|
||||
let (logon_type, session_id, logon_time, logon_server, sid) =
|
||||
extract_session_metadata(vmem, current, offsets);
|
||||
let info = MsvSessionInfo {
|
||||
@@ -356,6 +356,34 @@ fn walk_session_list(
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate that a LUID value looks like a real Windows logon session LUID.
|
||||
/// Windows LUIDs are 64-bit but in practice the high 32 bits are always zero
|
||||
/// for logon sessions. Well-known LUIDs: SYSTEM=0x3e7, LOCAL_SERVICE=0x3e5,
|
||||
/// NETWORK_SERVICE=0x3e4, ANONYMOUS=0x3e6. User sessions start at ~0x4000+.
|
||||
fn is_plausible_luid(luid: u64) -> bool {
|
||||
// High 32 bits must be zero for any real logon session LUID
|
||||
luid != 0 && (luid >> 32) == 0
|
||||
}
|
||||
|
||||
/// Validate that a session username looks plausible (not garbage memory).
|
||||
/// Rejects strings with control characters, backslashes (file paths),
|
||||
/// or non-ASCII outside the BMP common range.
|
||||
fn is_plausible_username(name: &str) -> bool {
|
||||
if name.is_empty() || name.len() > 256 {
|
||||
return false;
|
||||
}
|
||||
// Reject file paths (e.g. "C:\Windows\system32\kerberos.DLL")
|
||||
if name.contains('\\') || name.contains('/') {
|
||||
return false;
|
||||
}
|
||||
// Reject strings with control characters (except space) or null bytes
|
||||
if name.chars().any(|c| c < ' ' && c != '\t') {
|
||||
return false;
|
||||
}
|
||||
// Require at least one ASCII alphanumeric character — real usernames have one
|
||||
name.chars().any(|c| c.is_ascii_alphanumeric() || c == '$' || c == '-')
|
||||
}
|
||||
|
||||
/// Insert or merge a session into the map. When re-discovering a LUID,
|
||||
/// enrich with richer metadata (prefer non-zero logon_time, non-empty SID, etc.).
|
||||
fn merge_session(map: &mut std::collections::HashMap<u64, MsvSessionInfo>, new: MsvSessionInfo) {
|
||||
|
||||
Reference in New Issue
Block a user