@@ -62,35 +62,36 @@ def _get_os_category(os_string: str) -> str:
6262 return 'unknown'
6363
6464
65- # Fixed baseline activity patterns for Phase 1 (no LLM expansion)
65+ # Fixed baseline activity patterns (no LLM expansion)
6666# Format: (activity_type, probability)
67+ # Phase 5.6: Widened probability gaps for user diversity scoring
6768BASELINE_PATTERNS = {
6869 'developer' : [
69- ('logon' , 0.8 ), # 80% chance of logon
70- ('process_code' , 0.6 ), # 60% chance of code editor
71- ('connection_git' , 0.4 ), # 40% chance of git operation
72- ('process_build' , 0.3 ), # 30% chance of build
73- ('process_user_apps' , 0.25 ), # 25% chance of user app activity
70+ ('logon' , 0.7 ),
71+ ('process_code' , 0.75 ), # Dominant: code editors
72+ ('connection_git' , 0.5 ), # Heavy git usage
73+ ('process_build' , 0.45 ), # Frequent builds
74+ ('process_user_apps' , 0.15 ), # Minimal app usage
7475 ],
7576 'executive' : [
7677 ('logon' , 0.9 ),
77- ('connection_web' , 0.7 ),
78- ('connection_email' , 0.6 ),
79- ('process_user_apps' , 0.5 ), # 50% chance of Office/browser activity
78+ ('connection_web' , 0.8 ), # Dominant: browsing
79+ ('connection_email' , 0.75 ), # Heavy email
80+ ('process_user_apps' , 0.7 ), # Heavy Office/apps
8081 ],
8182 'analyst' : [
8283 ('logon' , 0.85 ),
83- ('process_query' , 0.5 ),
84- ('connection_db' , 0.4 ),
85- ('process_user_apps' , 0.3 ),
84+ ('process_query' , 0.7 ), # Dominant: database queries
85+ ('connection_db' , 0.6 ), # Heavy DB connections
86+ ('process_user_apps' , 0.45 ), # Moderate apps (Excel, etc.)
8687 ],
8788 'sysadmin' : [
8889 ('logon' , 0.9 ),
89- ('process_code' , 0.3 ),
90+ ('process_system' , 0.65 ), # Dominant: system tools
91+ ('process_code' , 0.35 ),
9092 ('process_query' , 0.3 ),
91- ('connection_web' , 0.3 ),
92- ('process_system' , 0.4 ),
93- ('process_user_apps' , 0.2 ),
93+ ('connection_web' , 0.2 ),
94+ ('process_user_apps' , 0.1 ), # Minimal app usage
9495 ],
9596 'default' : [
9697 ('logon' , 0.75 ),
@@ -194,6 +195,29 @@ def _get_os_category(os_string: str) -> str:
194195 'default' : {'process_code' : 0.15 , 'process_build' : 0.05 , 'process_user_apps' : 0.6 , 'process_system' : 0.2 },
195196}
196197
198+ # Per-persona app subsets for process_user_apps (Phase 5.6: user diversity)
199+ # Each persona favors a different mix of applications from PROCESS_TEMPLATES['process_user_apps']
200+ # Index references into PROCESS_TEMPLATES['process_user_apps']:
201+ # 0=Chrome, 1=Firefox, 2=Outlook, 3=Word, 4=Excel, 5=Edge, 6=Teams, 7=OneDrive, 8=Acrobat, 9=7-Zip
202+ PERSONA_APP_INDICES = {
203+ 'developer' : [0 , 6 , 7 , 9 ], # Chrome, Teams, OneDrive, 7-Zip
204+ 'executive' : [2 , 3 , 5 , 6 , 8 ], # Outlook, Word, Edge, Teams, Acrobat
205+ 'analyst' : [0 , 4 , 2 , 6 , 8 ], # Chrome, Excel, Outlook, Teams, Acrobat
206+ 'sysadmin' : [1 , 5 , 6 , 9 ], # Firefox, Edge, Teams, 7-Zip
207+ 'default' : [0 , 2 , 6 , 7 ], # Chrome, Outlook, Teams, OneDrive
208+ }
209+
210+ # Per-persona app subsets for Linux process_user_apps
211+ # Index references into PROCESS_TEMPLATES_LINUX['process_user_apps']:
212+ # 0=firefox, 1=thunderbird, 2=git, 3=docker, 4=pytest, 5=ssh, 6=curl, 7=kubectl
213+ PERSONA_APP_INDICES_LINUX = {
214+ 'developer' : [0 , 2 , 3 , 4 , 6 ], # firefox, git, docker, pytest, curl
215+ 'executive' : [0 , 1 ], # firefox, thunderbird
216+ 'analyst' : [0 , 5 , 6 ], # firefox, ssh, curl
217+ 'sysadmin' : [2 , 3 , 5 , 6 , 7 ], # git, docker, ssh, curl, kubectl
218+ 'default' : [0 , 2 , 5 , 6 ], # firefox, git, ssh, curl
219+ }
220+
197221# Zeek connection state distribution with matching history strings (Phase 5.1)
198222# Format: (conn_state, weight, history_string)
199223CONN_STATE_DISTRIBUTION = [
@@ -740,20 +764,23 @@ def generate_process(
740764 'Level' : 0 ,
741765 'EventRecordID' : self ._get_next_event_record_id (),
742766 'ExecutionProcessID' : 4 ,
743- 'ExecutionThreadID' : _get_rng ().randint (100 , 500 ),
767+ 'ExecutionThreadID' : _get_rng ().randint (100 , 9999 ),
744768 # Process variant fields
745769 'SubjectUserSid' : self ._get_sid (user .username ),
746770 'SubjectUserName' : user .username ,
747771 'SubjectDomainName' : 'CORP' ,
748772 'SubjectLogonId' : logon_id ,
749- 'NewProcessId' : f'0x{ pid :x} ' , # Hex format
773+ 'NewProcessId' : f'0x{ pid :x} ' ,
750774 'NewProcessName' : process_name ,
751- 'TokenElevationType' : '%%1936 ' , # Limited token
752- 'ProcessId' : f'0x{ parent_pid :x} ' , # Parent PID in hex
775+ 'TokenElevationType' : '%%1938 ' , # Limited token (UAC filtered)
776+ 'ProcessId' : f'0x{ parent_pid :x} ' ,
753777 'CommandLine' : command_line ,
778+ 'TargetUserSid' : self ._get_sid (user .username ),
754779 'TargetUserName' : user .username ,
755780 'TargetDomainName' : 'CORP' ,
756781 'TargetLogonId' : logon_id ,
782+ 'ParentProcessName' : r'C:\Windows\explorer.exe' ,
783+ 'MandatoryLabel' : 'S-1-16-8192' , # Medium integrity
757784 }
758785 self .emitters ['windows_event_security' ].emit_event (event_data )
759786
@@ -1064,6 +1091,8 @@ def generate_system_process(
10641091
10651092 if os_category == 'windows' :
10661093 sid = self .sid_registry .get (username , 'S-1-5-18' ) if self .sid_registry else 'S-1-5-18'
1094+ system_logon_ids = {'SYSTEM' : '0x3e7' , 'LOCAL SERVICE' : '0x3e5' , 'NETWORK SERVICE' : '0x3e4' }
1095+ logon_id = system_logon_ids .get (username , '0x3e7' )
10671096 event_data = {
10681097 'EventID' : 4688 ,
10691098 'TimeCreated' : time ,
@@ -1072,18 +1101,22 @@ def generate_system_process(
10721101 'Level' : 0 ,
10731102 'EventRecordID' : self ._get_next_event_record_id (),
10741103 'ExecutionProcessID' : 4 ,
1075- 'ExecutionThreadID' : _get_rng ().randint (100 , 999 ),
1104+ 'ExecutionThreadID' : _get_rng ().randint (100 , 9999 ),
10761105 'SubjectUserSid' : sid ,
10771106 'SubjectUserName' : username ,
10781107 'SubjectDomainName' : 'NT AUTHORITY' ,
1079- 'SubjectLogonId' : '0x3e7' ,
1080- 'NewProcessId' : hex ( pid ) ,
1108+ 'SubjectLogonId' : logon_id ,
1109+ 'NewProcessId' : f'0x { pid :x } ' ,
10811110 'NewProcessName' : process_name ,
1082- 'TokenElevationType' : '%%1936' ,
1083- 'ProcessId' : hex ( parent_pid ) ,
1111+ 'TokenElevationType' : '%%1936' , # Default token (no UAC split for SYSTEM)
1112+ 'ProcessId' : f'0x { parent_pid :x } ' ,
10841113 'CommandLine' : command_line ,
1085- 'ParentProcessName' : '' ,
1086- 'MandatoryLabel' : 'S-1-16-16384' ,
1114+ 'TargetUserSid' : sid ,
1115+ 'TargetUserName' : username ,
1116+ 'TargetDomainName' : 'NT AUTHORITY' ,
1117+ 'TargetLogonId' : logon_id ,
1118+ 'ParentProcessName' : r'C:\Windows\System32\services.exe' ,
1119+ 'MandatoryLabel' : 'S-1-16-16384' , # System integrity
10871120 }
10881121 if 'windows_event_security' in self .emitters :
10891122 self .emitters ['windows_event_security' ].emit_event (event_data )
@@ -1289,16 +1322,26 @@ def execute_baseline_activity(
12891322 # Phase 2.10: OS-aware process template selection
12901323 os_category = _get_os_category (system .os )
12911324 if os_category == 'windows' and activity_type in PROCESS_TEMPLATES :
1292- # Use Windows process templates
1293- process_name , command_line = _get_rng ().choice (PROCESS_TEMPLATES [activity_type ])
1325+ # Phase 5.6: Per-persona app pool for user diversity
1326+ pool = PROCESS_TEMPLATES [activity_type ]
1327+ if activity_type == 'process_user_apps' :
1328+ persona_key = (user .persona or 'default' ).lower ()
1329+ indices = PERSONA_APP_INDICES .get (persona_key , PERSONA_APP_INDICES ['default' ])
1330+ pool = [pool [i ] for i in indices if i < len (pool )]
1331+ process_name , command_line = _get_rng ().choice (pool )
12941332 # Phase 5.1: Substitute username placeholder in paths
12951333 process_name = process_name .replace ('{username}' , user .username )
12961334 command_line = command_line .replace ('{username}' , user .username )
12971335 self .generate_process (user , system , time , logon_id , process_name , command_line )
12981336
12991337 elif os_category == 'linux' and activity_type in PROCESS_TEMPLATES_LINUX :
1300- # Use Linux process templates
1301- process_name , command_line = _get_rng ().choice (PROCESS_TEMPLATES_LINUX [activity_type ])
1338+ # Phase 5.6: Per-persona app pool for Linux user diversity
1339+ pool = PROCESS_TEMPLATES_LINUX [activity_type ]
1340+ if activity_type == 'process_user_apps' :
1341+ persona_key = (user .persona or 'default' ).lower ()
1342+ indices = PERSONA_APP_INDICES_LINUX .get (persona_key , PERSONA_APP_INDICES_LINUX ['default' ])
1343+ pool = [pool [i ] for i in indices if i < len (pool )]
1344+ process_name , command_line = _get_rng ().choice (pool )
13021345 self .generate_process (user , system , time , logon_id , process_name , command_line )
13031346
13041347 # Also generate bash history for Linux
0 commit comments