├── 0001-logstash-input-syslog.conf
├── 0099-logstash-copy-original-message.conf
├── 0420-logstash-windows-wef.conf
├── 0511-logstash-powershell.conf
├── 9998-logstash-parse-failures.conf
├── 9999-logstash-output.conf
├── ARP Table to Event
├── ETW Tracing Consumption Example
├── README.md
├── WMI Events To Windows Events
├── add-cuckoo-id-to-winlogs.txt
├── dns-debug-log-gpo-steps.txt
├── enable-wmi-tracing-via-gpo.txt
├── install-and-or-upgrade-sysmon-and-config.ps1
└── nxlog.conf
/0001-logstash-input-syslog.conf:
--------------------------------------------------------------------------------
1 | input {
2 |
3 | udp {
4 | port => 8530
5 | type => "windows-dns"
6 | }
7 |
8 | tcp {
9 | port => 8530
10 | type => "windows-dns"
11 | }
12 |
13 | tcp {
14 | port => 8531
15 | type => "windows-wef"
16 | }
17 |
18 | udp {
19 | port => 8531
20 | type => "windows-wef"
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/0099-logstash-copy-original-message.conf:
--------------------------------------------------------------------------------
1 | filter {
2 | # Keep the base message before any edits, in case we have a parse failure / error in logstash config therefore we only want to keep this
3 | mutate {
4 | copy => { "message" => "_original_message" }
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/0420-logstash-windows-wef.conf:
--------------------------------------------------------------------------------
1 | filter {
2 |
3 | if [type] == "windows-wef" {
4 |
5 | json {
6 | source => "message"
7 | tag_on_failure => "_jsonparsefailure"
8 | }
9 |
10 | mutate {
11 | # Base
12 | add_field => {
13 | # Since it may be possible for windows time to be changed many hours, days, weeks, months, or years in the past to avoid time based monitoring (ie:in in a SIEM where looking for last 48 hours for suspicious events or etc) lets use the timestamp that the event was received.. This will also allow us to not to have to worry about converting certain sites that are not UTC and different time zones all over back to UTC too.
14 | "[@meta][log][timestamp]" => "%{@timestamp}"
15 | "[@meta][log][type]" => "windows-wef"
16 | "[@meta][event_type]" => "endpoint"
17 | "[@meta][log][host_ip]" => "%{[@meta][log][host_name]}"
18 | }
19 |
20 | # Remove NXLog fields
21 | # Remove other windows field we do not want
22 | remove_field => [
23 | "port",
24 | "SourceModuleType",
25 | "type",
26 | "message",
27 | "SourceModuleName",
28 | "SeverityValue",
29 | "SourceName",
30 | "Keywords",
31 | "OpcodeValue",
32 | "ProviderGuid",
33 | "SeverityValue",
34 | "Version"
35 | ]
36 | # Rename some base fields
37 | rename => {
38 | "Hostname" => "[@meta][log][host_name]"
39 | "RecordNumber" => "[win][log][record_num]"
40 | "EventType" => "[win][log][type]"
41 | "Opcode" => "[win][log][opcode]"
42 | "host" => "[@meta][log][forwarder]"
43 | "Severity" => "[@meta][log][level]"
44 | }
45 | lowercase => [
46 | "[@meta][log][host_name]",
47 | "[win][log][type]",
48 | "[win][log][opcode]",
49 | "[@meta][log][level]"
50 | ]
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/0511-logstash-powershell.conf:
--------------------------------------------------------------------------------
1 | filter {
2 |
3 | if [@meta][log][type] == "windows-wef" {
4 |
5 | # PowerShell Operational Only
6 | if [Channel] == "Microsoft-Windows-PowerShell/Operational" {
7 |
8 | # EventID 4103
9 | if [EventID] == 4103 {
10 |
11 | mutate {
12 |
13 | add_field => {
14 | "PayLoadInvocation" => "%{Payload}"
15 | "PayLoadParams" => "%{Payload}"
16 | }
17 |
18 | gsub => [
19 | # Normalize ContextInfo
20 | "ContextInfo", " ", "",
21 | "ContextInfo", " = ", "="
22 | ]
23 |
24 | }
25 |
26 | # Parse ContextInfo
27 | kv {
28 |
29 | source => "ContextInfo"
30 | field_split => "\r\n"
31 | value_split => "="
32 | remove_char_key => " "
33 | allow_duplicate_values => false
34 | # Set only allowed keys/fields incase ever an error parsing where something could contain a similar value_split of "="
35 | include_keys => [ "Severity", "HostName", "HostVersion", "HostID", "HostApplication", "EngineVersion", "RunspaceID", "PipelineID", "CommandName", "CommandType", "ScriptName", "CommandPath", "SequenceNumber", "User", "ConnectedUser", "ShellID" ]
36 |
37 | }
38 |
39 | mutate {
40 |
41 | gsub => [
42 | # Prepare Payload CommandInvocation parsing
43 | "PayLoadInvocation", "CommandInvocation\(.*\)", "CommandInvocation",
44 | "PayLoadInvocation", "ParameterBinding.*\r\n", "",
45 |
46 | # Prepare Payload ParameterBinding parsing
47 | "PayLoadParams", "CommandInvocation.*\r\n", "",
48 | "PayLoadParams", "ParameterBinding\(\S+\): ", "|||SPLITMEHEHE|||",
49 |
50 | # Remove any commandinvocation and parameterbinding and any other known fields/keys and leave a remaining Payload field
51 | "Payload", "CommandInvocation.*\r\n", "",
52 | "Payload", "ParameterBinding.*\r\n", ""
53 | ]
54 |
55 | }
56 |
57 | # Parse payload field for all CommandInvocations
58 | # Also add a field for non alphanumeric characters via https://twitter.com/jackcr/status/884875719972728833
59 | kv {
60 |
61 | source => "PayLoadInvocation"
62 | field_split => "\n"
63 | value_split => ":"
64 | allow_duplicate_values => false
65 | target => "[ps]"
66 | include_keys => [ "CommandInvocation" ]
67 |
68 | }
69 |
70 | ruby {
71 |
72 | code => "
73 | params_split = event.get('PayLoadParams').split('|||SPLITMEHEHE|||')
74 | params_split = params_split.drop(1)
75 | params_split_length = params_split.length
76 | all_names = Array.new
77 | all_values = Array.new
78 | all_values_non_alphanumeric = Array.new
79 |
80 | for param in params_split
81 | slice_and_dice = param.index('; value=')
82 | name = param.slice(6..slice_and_dice-2)
83 | value = param.slice(param.index('value=')..-1)[6..-1]
84 | value = value.strip
85 | value[0] = ''
86 | value[-1] = ''
87 | value_non_alphanumeric = value.gsub(/[A-Za-z0-9\s]+/i, '')
88 | all_names.push(name)
89 | all_values.push(value)
90 | all_values_non_alphanumeric.push(value_non_alphanumeric)
91 | end
92 |
93 | all_names = all_names.uniq
94 | all_values = all_values.uniq
95 | event.set('[ps][param][name]', all_names)
96 | event.set('[ps][param][value]', all_values)
97 | event.set('[ps][param][value_nonalphanumeric]', all_values_non_alphanumeric)
98 | "
99 |
100 | }
101 |
102 | # Cleanup and Conversions
103 | mutate {
104 |
105 | # Normalize ContextInfo field names
106 | rename => {
107 | "CommandName" => "[ps][command][name]"
108 | "CommandPath" => "[ps][command][path]"
109 | "CommandType" => "[ps][command][type]"
110 | "ConnectedUser" => "[ps][connected_user][full]"
111 | "EngineVersion" => "[ps][version][full]"
112 | "HostApplication" => "[ps][src][application]"
113 | "HostID" => "[ps][src][host_id]"
114 | "HostName" => "[ps][src][name]"
115 | "HostVersion" => "[ps][src][version]"
116 | "PipelineID" => "[ps][pipeline_id]"
117 | "RunspaceID" => "[ps][runspace_id]"
118 | "ScriptName" => "[file][name]"
119 | "SequenceNumber" => "[ps][seq_num]"
120 | "ShellID" => "[ps][src][id]"
121 | "User" => "[ps][user][full]"
122 | "[ps][CommandInvocation]" => "[ps][invocation]"
123 | "Payload" => "[ps][remaining_payload]"
124 | }
125 |
126 | # Remove unwanted fields
127 | remove_field => [
128 | "Severity",
129 | "EventType",
130 | "Keywords",
131 | "message",
132 | "Message",
133 | "Opcode",
134 | "port",
135 | "SeverityValue",
136 | "SourceModuleName",
137 | "SourceModuleType",
138 | "Version",
139 | "ContextInfo",
140 | "PayLoadInvocation",
141 | "PayLoadParams"
142 | ]
143 |
144 | # Set correct value types
145 | convert => { "[ps][pipeline_id]" => "integer" }
146 | convert => { "[ps][seq_num]" => "integer" }
147 | lowercase => [
148 | "[ps][command][name]",
149 | "[ps][command][type]",
150 | "[ps][src][application]",
151 | "[ps][src][id]",
152 | "[ps][src][name]",
153 | "[ps][user][full]"
154 | ]
155 |
156 | }
157 |
158 | }
159 |
160 | # EventID 4104
161 | else if [EventID] == 4104 {
162 |
163 | # Sometimes ScriptBlockText will not be parsed from the Message field. When this happens the other parameters (appear) to also never be parsed (ie: ScriptBlockId etc)
164 | # So check if ScriptBlockText exists and if it does not then we will want to parse the parameters from the Message field
165 | if [ScriptBlockText] {
166 | mutate {
167 | remove_field => [
168 | "Message"
169 | ]
170 | }
171 | }
172 | else {
173 | # Lets use GSUB to make sure we can get things to split on / make it easier more efficient to split on
174 | grok {
175 | match => {
176 | "Message" => "^Creating Scriptblock text \(%{INT:MessageNumber} of %{INT:MessageTotal}\):\r\n%{GREEDYDATA:ScriptBlockText}\r\n\r\nScriptBlock ID: %{UUID:ScriptBlockId}\r\nPath: %{DATA:Path}$"
177 | }
178 | break_on_match => true
179 | keep_empty_captures => false
180 | named_captures_only => true
181 | tag_on_failure => [ "_grokparsefailure", "_parsefailure" ]
182 | tag_on_timeout => "_groktimeout"
183 | # Timeout 1.5 seconds
184 | timeout_millis => 1500
185 | remove_field => [ "Message" ]
186 | }
187 | }
188 |
189 | mutate {
190 | rename => {
191 | "Path" => "[file][name]"
192 | "ScriptBlockText" => "[ps][script_block][text]"
193 | "ScriptBlockId" => "[ps][script_block][id]"
194 | "MessageNumber" => "[ps][script_block][msg_num]"
195 | "MessageTotal" => "[ps][script_block][msg_total]"
196 | }
197 | copy => { "Domain" => "[src_user][domain]"}
198 | }
199 | # Fingerprint the Script Block Text ---- useful for finding reoccuring scripts we want to exclude
200 | fingerprint {
201 | source => [ "[ps][script_block][text]" ]
202 | method => "SHA1"
203 | target => "[@meta][fp][ps][script_block][sha1]"
204 | key => "logstash"
205 | }
206 | # Fingerprint Script Block Text and UserID/[@meta][src_user][sid] ---- because sometimes certain accounts should not run certain scripts, so filtering just Script Block Text could be a problem. Also, don't want to use AccountName because a local user with $X name could have same name as a domain user!
207 | fingerprint {
208 | source => [ "[ps][script_block][text]", "[@meta][src_user][sid]" ]
209 | concatenate_sources => true
210 | method => "SHA1"
211 | target => "[@meta][fp][ps][script_block_and_sid][sha1]"
212 | key => "logstash"
213 | }
214 | # Fingerprint UserID/[@meta][src_user][sid] and FileName---- because ScriptBlockText gets chopped up sometimes. Use this with caution for filtering
215 | if [file][name] {
216 | fingerprint {
217 | source => [ "[file][name]", "[@meta][src_user][sid]" ]
218 | concatenate_sources => true
219 | method => "SHA1"
220 | target => "[@meta][fp][ps][file_name_and_sid][sha1]"
221 | key => "logstash"
222 | }
223 | }
224 | }
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/9998-logstash-parse-failures.conf:
--------------------------------------------------------------------------------
1 | filter {
2 | # In case [tags] are being used for something else and or geoip lookup failures, then we do NOT want to just assume [tags] means something we don't want to index.
3 | # However, we also do NOT want to perform the expensive multi or statement on everything that does not have [tags]
4 | if [tags] {
5 | if "_parsefailure" in [tags] or "_jsonparsefailure" in [tags] or "_grokparsefailure" in [tags] or "_dissectfailure" in [tags] or "_groktimeout" in [tags] or "_rubyexception" in [tags] or "_dateparsefailure" in [tags] or "_jdbcstreamingfailure" in [tags] or "_elasticsearch_lookup_failure" in [tags] or "_urldecodefailure" in [tags] or "_csvparsefailure" in [tags] or "_xmlparsefailure" in [tags] {
6 | # After setting the event as a parse failure, then we need to remove all events except for what was set in 00*-.conf and above
7 | prune {
8 | whitelist_names => [ "$@timestamp^", "@timestamp", "$_original_message^", "_original_message", "^tags$", "$%{[@meta][event_type]}^", "${[@meta][log][timestamp]}^", "$[@meta][log][level]^", "$[@meta][log][timestamp]^" ]
9 | }
10 | mutate {
11 | add_field => {
12 | "[@meta][event_type]" => "_parsefailure"
13 | "[@meta][log][timestamp]" => "%{@timestamp}"
14 | }
15 | }
16 | }
17 | }
18 | # Remove the original message that we copied in 101-*.conf if no parse failure
19 | if [@meta][event_type] != "_parsefailure" {
20 | mutate { remove_field => "_original_message"}
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/9999-logstash-output.conf:
--------------------------------------------------------------------------------
1 | output {
2 |
3 | if [@meta][event_type] != "_parsefailure" {
4 |
5 | if [@meta][event_type] == "network" {
6 | elasticsearch {
7 | hosts => [ "127.0.0.1:9200" ]
8 | index => "logs-network-%{[@meta][log][type]}-%{+YYYY.MM.dd}"
9 | document_type => "%{[@meta][log][type]}"
10 | }
11 | }
12 |
13 | else if [@meta][log][type] == "windows-wef" {
14 | elasticsearch {
15 | hosts => [ "127.0.0.1:9200" ]
16 | index => "logs-endpoint-%{[@meta][log][type]}-%{+YYYY.MM.dd}"
17 | document_type => "%{[@meta][log][type]}"
18 | }
19 | }
20 |
21 | else {
22 | elasticsearch {
23 | hosts => [ "127.0.0.1:9200" ]
24 | index => "indexme-%{+YYYY.MM.dd}"
25 | }
26 | }
27 | }
28 |
29 | else {
30 | elasticsearch {
31 | hosts => [ "127.0.0.1:9200" ]
32 | index => "parse-failures-%{+YYYY.MM.dd}"
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ARP Table to Event:
--------------------------------------------------------------------------------
1 | if([System.Diagnostics.EventLog]::SourceExists("ARP Status Script")){
2 | $x = Get-NetIPInterface | where-object {$_.ConnectionState -eq "Connected" -and $_.InterfaceAlias -notlike "*VMware*" -and $_.InterfaceAlias -notlike "*Loopback*"} | get-netneighbor | select-object -property InterfaceAlias,LinkLayerAddress,IPAddress
3 | $x = [system.String]::Join("`r`n", $x)
4 | write-eventlog -logname "System" -source "ARP Status Script" -message $x -EventId 10001
5 | }Else{
6 | $x = Get-NetIPInterface | where-object {$_.ConnectionState -eq "Connected" -and $_.InterfaceAlias -notlike "*VMware*" -and $_.InterfaceAlias -notlike "*Loopback*"} | get-netneighbor | select-object -property InterfaceAlias,LinkLayerAddress,IPAddress
7 | $x = [system.String]::Join("`r`n", $x)
8 | New-EventLog –LogName "System" –Source “ARP Status Script”
9 | write-eventlog -logname "System" -source "ARP Status Script" -message $x -EventId 10001
10 | }
11 |
--------------------------------------------------------------------------------
/ETW Tracing Consumption Example:
--------------------------------------------------------------------------------
1 | https://github.com/acalarch/ETL-to-EVTX/blob/master/etl-to-evtx.ps1
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WinLogsZero2Hero talk links and scripts
2 | This repo is code used from our talks at Derbycon 7, BSides Detroit 2017, and Bloomcon 0x2.
3 |
4 | Slides for the talk are here: https://bit.ly/WinLogsZero2Hero
5 |
6 | The 3 live presentations are here:
7 |
8 | Bloomcon: https://youtu.be/H3t_kHQG1Js?t=1m44s
9 |
10 | Derbycon: https://www.youtube.com/watch?v=8AKxt-5RB6w
11 |
12 | BSides Detroit: https://www.youtube.com/watch?v=jiHP0nQoAfs
13 |
14 |
15 |
16 | This script has configurations/examples for:
17 | * Deployming Sysmon where it will check version and upgrade if new or install if non-existent or restart/start if stopped/disabled/not-running.
18 |
19 | * Cuckoo Sandbox Windows Event collections
20 |
21 | * Logstash enrichment examples for PowerShell
22 |
23 | * ETW (Event Tracing for Windows) implementation for WMI and consumption via WEF
24 |
25 | * DNS Debug Log consumption via WEF
26 |
27 | * Example of collecting ARP table continously via WEF
28 |
--------------------------------------------------------------------------------
/WMI Events To Windows Events:
--------------------------------------------------------------------------------
1 | #GET LIST OF ALL WMI EVENTS Get-WmiObject -Namespace "root" -Query "SELECT * FROM meta_class WHERE __This ISA '__Event'"
2 | #http://www.powershellmagazine.com/2013/01/15/pstip-list-all-wmi-event-classes/
3 | #Good Tutorial
4 | #https://learn-powershell.net/2013/08/14/powershell-and-events-permanent-wmi-event-subscriptions/
5 | #More Namespaces
6 | #Get-WMIObject -class __Namespace -namespace root | Format-Table name
7 |
8 | $filter = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance();
9 | $filter.QueryLanguage = "WQL";
10 | $filter.Query = "SELECT * FROM __InstanceOperationEvent WHERE BLAH";
11 | $filter.Name = "My Filter Name";
12 | $filter.EventNamespace = 'root\StandardCimv2';
13 | $result = $instanceFilter.Put();
14 | $filterx = $result.Path;
15 | $consumer = ([wmiclass]"\\.\root\subscription:NTEventLogEventConsumer").CreateInstance();
16 | $consumer.Name = 'ArpConsumer';
17 | $consumer.Category = 1
18 | $consumer.EventType=4;
19 | $consumer.EventID = 7754;
20 | $consumer.SourceName = "TEST ARP";
21 | $consumer.NumberOfInsertionStrings = 2;
22 | $consumer.InsertionStringTemplates = {"This is my message: %TargetInstance.MyProperty%"};
23 | $result = $instanceConsumer.Put()
24 | $consumerx = $result.Path
25 | $binding = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance()
26 | $binding.Filter = $newFilter
27 | $binding.Consumer = $newConsumer
28 | $result = $instanceBinding.Put()
29 | $bindingx = $result.Path
30 |
--------------------------------------------------------------------------------
/add-cuckoo-id-to-winlogs.txt:
--------------------------------------------------------------------------------
1 | # Add the following to The cuckoo manager in $CUCKOODIR/cuckoo/analyzer/windows/analyzer.py
2 | file_path_id_for_win_logs = "C:\\id.txt"
3 | cust_file = open(file_path_id_for_win_logs, 'w')
4 | cust_file.write(str(self.config.id))
5 | cust_file.close
6 | # Then you will add the file path above the nxlog.conf
7 |
--------------------------------------------------------------------------------
/dns-debug-log-gpo-steps.txt:
--------------------------------------------------------------------------------
1 | # Set DNS Debug Logging.
2 | # In order to set log immediately use "0x8000" vs windows default 8KB buffer. So, you would specify "0x8000f321"
3 | # Just import the scheduled task IN ORDER of loglevel, then logfile, then logsize!
4 | # Max size is 500000000 500MB but use 50000000 for 50MB
5 | 1) Create GPO called "audit-dns-via-debug"
6 | 2) Under "Scope" tab set "Security Filtering to "Authenticated Users"
7 | 3) Under "Details" tab set "GPO Status" to "User configuration settings disabled"
8 | 4) Create
9 | 4.1) Task named "set_dns_debug_log"
10 | # Then for each task set following:
11 | 4.a) When running the task, user the following user account:"NT AUTHORITY/SYSTEM"
12 | 4.b) Check "Run whether user is logged on or not" and "Run with highest privileges"
13 | 4.c) Check "Hidden"
14 | 4.d) Configure for:"Windows Vista or Windows Server 2008"
15 | 4.e) Any Triggers you want, that make sense
16 | 4.f) Any Conditions you want, that make sense
17 | 4.g) Any Settings you want, that make sense
18 | 4.h) Common: check "Remove this item when it is no longer applied"
19 | 4.i) Common: check "Item-level targeting" and use:
20 | Match value data
21 | Any
22 | HKEY_LOCAL_MACHINE
23 | SYSTEM\ControlSet001\services\DNS
24 | Start
25 | REG_DWORD
26 | 00000002 Hexadeciaml
27 | 5) For 4) need to set following under actions IN ORDER! IN ORDER! Don't ask how long took to troubleshoot why putting all the dnscmd commands in one line was not working
28 | 4.1) run:dnscmd paramter:/Config /LogLevel 0x8000f321
29 | 4.2) run:dnscmd paramter:/Config /LogFilePath "%WINDIR%\System32\dns\Dns.log"
30 | 4.3) run:dnscmd paramter:/Config /LogFileMaxSize 50000000
31 | #TODO:In scheduled task set to only run if connected to DOMAIN! using either site or start if only network in the scheduled task itself under Condition tab
32 |
33 | ######### Windows DNS Debug Logging
34 | # %WINDIR%\System32\dns\Dns.log"
35 | # Debug Logging settings:
36 | # Log packets for debugging
37 | # Packet direction: Outgoing TCP & UDP
38 | # Packet direction: Incoming TCP & UDP
39 | # Packet contents: Queries/Transfer Request & Response
40 | # Packet contents: Updates Request & Response
41 | # Message logging key (for packets - other items use a subset of these fields):
42 | # Field # Information Values
43 | # ------- ----------- ------
44 | # 1 Date
45 | # 2 Time
46 | # 3 Thread ID
47 | # 4 Context
48 | # 5 Internal packet identifier
49 | # 6 UDP/TCP indicator
50 | # 7 Send/Receive indicator
51 | # 8 Remote IP
52 | # 9 Xid (hex)
53 | # 10 Query/Response R = Response
54 | # blank = Query
55 | # 11 Opcode Q = Standard Query
56 | # N = Notify
57 | # U = Update
58 | # ? = Unknown
59 | # 12 [ Flags (hex)
60 | # 13 Flags (char codes) A = Authoritative Answer
61 | # T = Truncated Response
62 | # D = Recursion Desired
63 | # R = Recursion Available
64 | # 14 ResponseCode ]
65 | # 15 Question Type
66 | # 16 Question Name
67 |
--------------------------------------------------------------------------------
/enable-wmi-tracing-via-gpo.txt:
--------------------------------------------------------------------------------
1 | 1) Create GPO called "audit-wmi-tracing"
2 | 2) Under the "Scope" tab set "Security Filtering to "Authenticated Users"
3 | 3) Under the "Details" tab set "GPO Status" to "User configuration settings disabled"
4 | 4) In the "Settings" tab edit the configuration and browse to "Preferences > Control Panel Settings > Scheduled Tasks" and right click, select New, "Scheduled Task (Windows Vista and later)"
5 | 5) Under the "General" tab
6 | Action:Replace
7 | Name:set_wmi_trace
8 | When running the task, use the following user account:NT AUTHORITY/SYSTEM
9 | Run only when user is logged on:CHECK
10 | Run with highest privileges:CHECK
11 | Hidden:CHECK
12 | 6) Under the "Triggers" tab, select anything that you desire for your environment.
13 | #recommend using a daily trigger
14 | 7) Under the "Actions" tab create the following "New" actions:
15 | 7.a)
16 | Action:Start a program
17 | Program/script:%SystemRoot%\system32\wevtutil.exe
18 | Add arguments (optional):sl "Microsoft-Windows-WMI-Activity/Trace" /e:false /q:true
19 | 7.b)
20 | Action:Start a program
21 | Program/script:%SystemRoot%\system32\wevtutil.exe
22 | Add arguments (optional):sl "Microsoft-Windows-WMI-Activity/Trace" /rt:false
23 | 7.c)
24 | Action:Start a program
25 | Program/script:%SystemRoot%\system32\wevtutil.exe
26 | Add arguments (optional):sl "Microsoft-Windows-WMI-Activity/Trace" /ms:35000000
27 | 7.d)
28 | Action:Start a program
29 | Program/script:%SystemRoot%\system32\wevtutil.exe
30 | Add arguments (optional):sl "Microsoft-Windows-WMI-Activity/Trace" /e:true /q:true
31 | 8) Under the "Conditions" tab, select anything that you desire for your environment.
32 | #recommend just leaving the defaults
33 | 9) Under the "Settings" tab, select anything that you desire for your environment.
34 | #recommend to check the boxes for stop task if it runs longer than 1 hour and if task does not end than force it to close
35 | 10) Under the "Conditions" tab, select anything that you desire for your environment.
36 | Remove this item when it is no longer applied:CHECK
37 |
--------------------------------------------------------------------------------
/install-and-or-upgrade-sysmon-and-config.ps1:
--------------------------------------------------------------------------------
1 | # Ensure to change the $ADDomainName to your domain name on line 3;
2 | # Ensure to complete the path to the directory where you will keep Sysmon.exe including the preceding "\"; Sysmon64.exe; and the config sysmonconfig.xml on line 7;
3 | # Only change these variables and no other variables :)
4 | $ADDomainName = "domain.local"
5 | $SysVolSysmonPath = "\\$ADDomainName\sysvol\$ADDomainName\"
6 | $LocalSysmonPath = "$Env:SystemRoot\Temp\"
7 | $SysVolSysmonConfig = "$SysVolSysmonPath\sysmonconfig.xml"
8 | $LocalSysmonConfig = "$LocalSysmonPath\sysmonconfig.xml"
9 | $LogFileForScript = "$LocalSysmonPath\sysmon-install-log.txt"
10 |
11 | # Set time that script started
12 | $ScriptRunTime = ([DateTime]::Now.AddHours(-$NumberOfHoursToQuery))
13 |
14 | # If log file does not already exist create it, so the rest of the script can just use add-content
15 | $LogFileExists = Test-Path $LogFileForScript
16 | if ($LogFileExists -eq $false)
17 | {
18 | Set-Content $LogFileForScript "" -NoNewline
19 | }
20 |
21 | Function main{
22 |
23 | # Determines if OS is 64 or 32-bit
24 | if([System.IntPtr]::Size -eq 4) {$Sysmon = "Sysmon.exe"} else {$Sysmon = "Sysmon64.exe"}
25 | $SysVolSysmonPE = "$SysVolSysmonPath\$Sysmon"
26 |
27 |
28 | # Finds sysmon.exe at $SysVolSysmonPath; Is the share available and is sysmon available on the share to update?#
29 | $SysVolSysmonAvailable = Test-Path "$SysVolSysmonPE"
30 | $SysVolSysmonConfigAvailabe = Test-Path "$SysVolSysmonConfig"
31 |
32 | # Set local variables for when we copy config and PE to device
33 | $LocalSysmonPE = "$LocalSysmonPath\$Sysmon"
34 |
35 | if(($SysVolSysmonAvailable -eq $true) -and ($SysVolSysmonConfigAvailabe -eq $true))
36 | {
37 |
38 | $SysmonVersionAvailable=[System.Diagnostics.FileVersionInfo]::GetVersionInfo($SysVolSysmonPE).FileVersion
39 |
40 | # Finds sysmon.exe at C:\windows\sysmon.exe
41 | $InstalledSysmon = Test-Path $Env:windir\sysmon.exe
42 | # When you remove sysmon, it doesn't remove the EXE.. so do another check :)
43 | $InstalledSysmonAsAService = get-service -Name "sysmon" -ErrorAction SilentlyContinue
44 | # We want to log that for some reason sysmon.exe exists on the host but it is not a service :0
45 | # Usually this means that at one point it was installed
46 | if (-Not $InstalledSysmonAsAService)
47 | {
48 | Add-Content $LogFileForScript "$ScriptRunTime ---- Service was uninstalled at some point! May want to figure out why..."
49 | }
50 |
51 | if (($InstalledSysmon -eq $true) -and ($InstalledSysmonAsAService)) {
52 |
53 | # Get current sysmon version
54 | $SysmonVersion=[System.Diagnostics.FileVersionInfo]::GetVersionInfo("$Env:windir\sysmon.exe").FileVersion
55 |
56 | # Convert strings to integers for comparison
57 | [double]$intSysmonAvailable = [convert]::ToDouble($SysmonVersionAvailable)
58 | [double]$intSysmonVersion = [convert]::ToDouble($SysmonVersion)
59 |
60 | # If sysvol version is greater than current version update#>
61 | if($intSysmonAvailable -gt $intSysmonVersion)
62 | {
63 | # Copy sysmon locally, for install performance and incase network drops during install
64 | cmd /c "copy /V $SysVolSysmonPE $LocalSysmonPE"
65 | cmd /c "copy /V $SysVolSysmonConfig $LocalSysmonConfig"
66 | # Make sure copies where successful
67 | if((Test-Path $LocalSysmonPE) -and (Test-Path $LocalSysmonConfig))
68 | {
69 | # #TODO:what to do?! Way older versions of Sysmon used to put the EXE/PE in %WINDIR%/System32/Sysmon.exe -- so need to just call system sysmon to do uninstall...
70 | cmd /c "$LocalSysmonPE -u"
71 | cmd /c "$LocalSysmonPE -accepteula -i $LocalSysmonConfig"
72 | Add-Content $LogFileForScript "$ScriptRunTime ---- Updated PE."
73 | }
74 |
75 | else {
76 | Add-Content $LogFileForScript "$ScriptRunTime ---- Failed to copy sysmon items."
77 | exit
78 | }
79 | }
80 |
81 | # If sysmon drv last write time is later than last write time of sysvol config file, update the config.
82 |
83 | # Obtain sysvol sysmonconfig.xml last write time
84 | $sysvolconfiglastwrite = (get-item $SysVolSysmonConfig).LastWriteTime
85 |
86 | # Obtain current configuration last write time, this is available in the registry
87 | $key = get-item "HKLM:\SYSTEM\CurrentControlSet\Services\SysmonDrv\Parameters"
88 | $localconfiglastwrite = ($key | Get-RegistryKeyTimestamp).LastWriteTime
89 |
90 | # Finally, compare both write times. If the lastwrite for the sysvol config is greater than the lastwrite for the registry.. update config
91 | if($sysvolconfiglastwrite -gt $localconfiglastwrite)
92 | {
93 | # Copy config locally, for install performance and incase network drops during install
94 | cmd /c "copy /V $SysVolSysmonConfig $LocalSysmonConfig"
95 | # Make sure network copy was successful
96 | if(Test-Path $LocalSysmonConfig)
97 | {
98 | cmd /c "$LocalSysmonPE -c $LocalSysmonConfig"
99 | Add-Content $LogFileForScript "$ScriptRunTime ---- Updated configuration."
100 | }
101 | else {
102 | Add-Content $LogFileForScript "$ScriptRunTime ---- Failed to copy sysmon items."
103 | exit
104 | }
105 | }
106 | }
107 | # Sysmon is not installed, so install with config :)
108 | else
109 | {
110 | # Copy sysmon locally, for install performance and incase network drops during install
111 | cmd /c "copy /V $SysVolSysmonPE $LocalSysmonPE"
112 | cmd /c "copy /V $SysVolSysmonConfig $LocalSysmonConfig"
113 | # Make sure copies where successful
114 | if((Test-Path $LocalSysmonPE) -and (Test-Path $LocalSysmonConfig))
115 | {
116 | cmd /c "$LocalSysmonPE -accepteula -i $LocalSysmonConfig"
117 | Add-Content $LogFileForScript "$ScriptRunTime ---- First install."
118 | }
119 | else {
120 | Add-Content $LogFileForScript "$ScriptRunTime ---- Failed to copy sysmon items."
121 | exit
122 | }
123 | }
124 | }
125 | else{
126 | Add-Content $LogFileForScript "$ScriptRunTime ---- Failed to find sysmon items in sysvol."
127 | }
128 | # Ensure sysmon services are running
129 | try{
130 | $SysmonService = get-service -Name "sysmon" -ErrorAction STOP
131 | $SysmonDrvService = get-service -Name "sysmondrv" -ErrorAction STOP
132 | if($SysmonService.Status -ne "running"){Add-Content $LogFileForScript "$ScriptRunTime ---- Service was stopped, starting sysmon PE."; start-service -name "sysmon" -ErrorAction Stop}
133 | if($SysmonDrvService.Status -ne "running"){Add-Content $LogFileForScript "$ScriptRunTime ---- Driver was stopped, starting sysmon driver.";start-service -name "sysmondrv" -ErrorAction Stop}
134 | }
135 | catch{
136 | Add-Content $LogFileForScript "$ScriptRunTime ---- Failed restarting and or getting status of sysmon services."
137 | exit
138 | }
139 |
140 | }
141 |
142 | Function Get-RegistryKeyTimestamp {
143 | <#
144 | .SYNOPSIS
145 | Retrieves the registry key timestamp from a local or remote system.
146 |
147 | .DESCRIPTION
148 | Retrieves the registry key timestamp from a local or remote system.
149 |
150 | .PARAMETER RegistryKey
151 | Registry key object that can be passed into function.
152 |
153 | .PARAMETER SubKey
154 | The subkey path to view timestamp.
155 |
156 | .PARAMETER RegistryHive
157 | The registry hive that you will connect to.
158 |
159 | Accepted Values:
160 | ClassesRoot
161 | CurrentUser
162 | LocalMachine
163 | Users
164 | PerformanceData
165 | CurrentConfig
166 | DynData
167 |
168 | .NOTES
169 | Name: Get-RegistryKeyTimestamp
170 | Author: Boe Prox
171 | Version History:
172 | 1.0 -- Boe Prox 17 Dec 2014
173 | -Initial Build
174 |
175 | .EXAMPLE
176 | $RegistryKey = Get-Item "HKLM:\System\CurrentControlSet\Control\Lsa"
177 | $RegistryKey | Get-RegistryKeyTimestamp | Format-List
178 |
179 | FullName : HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa
180 | Name : Lsa
181 | LastWriteTime : 12/16/2014 10:16:35 PM
182 |
183 | Description
184 | -----------
185 | Displays the lastwritetime timestamp for the Lsa registry key.
186 |
187 | .EXAMPLE
188 | Get-RegistryKeyTimestamp -Computername Server1 -RegistryHive LocalMachine -SubKey 'System\CurrentControlSet\Control\Lsa' |
189 | Format-List
190 |
191 | FullName : HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa
192 | Name : Lsa
193 | LastWriteTime : 12/17/2014 6:46:08 AM
194 |
195 | Description
196 | -----------
197 | Displays the lastwritetime timestamp for the Lsa registry key of the remote system.
198 |
199 | .INPUTS
200 | System.String
201 | Microsoft.Win32.RegistryKey
202 |
203 | .OUTPUTS
204 | Microsoft.Registry.Timestamp
205 | #>
206 | [OutputType('Microsoft.Registry.Timestamp')]
207 | [cmdletbinding(
208 | DefaultParameterSetName = 'ByValue'
209 | )]
210 | Param (
211 | [parameter(ValueFromPipeline=$True, ParameterSetName='ByValue')]
212 | [Microsoft.Win32.RegistryKey]$RegistryKey,
213 | [parameter(ParameterSetName='ByPath')]
214 | [string]$SubKey,
215 | [parameter(ParameterSetName='ByPath')]
216 | [Microsoft.Win32.RegistryHive]$RegistryHive,
217 | [parameter(ParameterSetName='ByPath')]
218 | [string]$Computername
219 | )
220 | Begin {
221 | #region Create Win32 API Object
222 | Try {
223 | [void][advapi32]
224 | } Catch {
225 | #region Module Builder
226 | $Domain = [AppDomain]::CurrentDomain
227 | $DynAssembly = New-Object System.Reflection.AssemblyName('RegAssembly')
228 | $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
229 | $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('RegistryTimeStampModule', $False)
230 | #endregion Module Builder
231 |
232 | #region DllImport
233 | $TypeBuilder = $ModuleBuilder.DefineType('advapi32', 'Public, Class')
234 |
235 | #region RegQueryInfoKey Method
236 | $PInvokeMethod = $TypeBuilder.DefineMethod(
237 | 'RegQueryInfoKey', #Method Name
238 | [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
239 | [IntPtr], #Method Return Type
240 | [Type[]] @(
241 | [Microsoft.Win32.SafeHandles.SafeRegistryHandle], #Registry Handle
242 | [System.Text.StringBuilder], #Class Name
243 | [UInt32 ].MakeByRefType(), #Class Length
244 | [UInt32], #Reserved
245 | [UInt32 ].MakeByRefType(), #Subkey Count
246 | [UInt32 ].MakeByRefType(), #Max Subkey Name Length
247 | [UInt32 ].MakeByRefType(), #Max Class Length
248 | [UInt32 ].MakeByRefType(), #Value Count
249 | [UInt32 ].MakeByRefType(), #Max Value Name Length
250 | [UInt32 ].MakeByRefType(), #Max Value Name Length
251 | [UInt32 ].MakeByRefType(), #Security Descriptor Size
252 | [long].MakeByRefType() #LastWriteTime
253 | ) #Method Parameters
254 | )
255 |
256 | $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
257 | $FieldArray = [Reflection.FieldInfo[]] @(
258 | [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
259 | [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
260 | )
261 |
262 | $FieldValueArray = [Object[]] @(
263 | 'RegQueryInfoKey', #CASE SENSITIVE!!
264 | $True
265 | )
266 |
267 | $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
268 | $DllImportConstructor,
269 | @('advapi32.dll'),
270 | $FieldArray,
271 | $FieldValueArray
272 | )
273 |
274 | $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)
275 | #endregion RegQueryInfoKey Method
276 |
277 | [void]$TypeBuilder.CreateType()
278 | #endregion DllImport
279 | }
280 | #endregion Create Win32 API object
281 | }
282 | Process {
283 | #region Constant Variables
284 | $ClassLength = 255
285 | [long]$TimeStamp = $null
286 | #endregion Constant Variables
287 |
288 | #region Registry Key Data
289 | If ($PSCmdlet.ParameterSetName -eq 'ByPath') {
290 | #Get registry key data
291 | $RegistryKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($RegistryHive, $Computername).OpenSubKey($SubKey)
292 | If ($RegistryKey -isnot [Microsoft.Win32.RegistryKey]) {
293 | Throw "Cannot open or locate $SubKey on $Computername"
294 | }
295 | }
296 |
297 | $ClassName = New-Object System.Text.StringBuilder $RegistryKey.Name
298 | $RegistryHandle = $RegistryKey.Handle
299 | #endregion Registry Key Data
300 |
301 | #region Retrieve timestamp
302 | $Return = [advapi32]::RegQueryInfoKey(
303 | $RegistryHandle,
304 | $ClassName,
305 | [ref]$ClassLength,
306 | $Null,
307 | [ref]$Null,
308 | [ref]$Null,
309 | [ref]$Null,
310 | [ref]$Null,
311 | [ref]$Null,
312 | [ref]$Null,
313 | [ref]$Null,
314 | [ref]$TimeStamp
315 | )
316 | Switch ($Return) {
317 | 0 {
318 | #Convert High/Low date to DateTime Object
319 | $LastWriteTime = [datetime]::FromFileTime($TimeStamp)
320 |
321 | #Return object
322 | $Object = [pscustomobject]@{
323 | FullName = $RegistryKey.Name
324 | Name = $RegistryKey.Name -replace '.*\\(.*)','$1'
325 | LastWriteTime = $LastWriteTime
326 | }
327 | $Object.pstypenames.insert(0,'Microsoft.Registry.Timestamp')
328 | $Object
329 | }
330 | 122 {
331 | Throw "ERROR_INSUFFICIENT_BUFFER (0x7a)"
332 | }
333 | Default {
334 | Throw "Error ($return) occurred"
335 | }
336 | }
337 | #endregion Retrieve timestamp
338 | }
339 | }
340 |
341 | main
342 |
--------------------------------------------------------------------------------
/nxlog.conf:
--------------------------------------------------------------------------------
1 | define ROOT C:\Program Files (x86)\nxlog
2 | Moduledir %ROOT%\modules
3 | CacheDir %ROOT%\data
4 | Pidfile %ROOT%\data\nxlog.pid
5 | SpoolDir %ROOT%\data
6 | LogFile %ROOT%\data\nxlog.log
7 |
8 |
9 | Module xm_json
10 |
11 |
12 |
13 | Module xm_fileop
14 |
15 |
16 |
17 | Module im_msvistalog
18 | Query \
19 | \
20 | \
21 | \
22 | \
23 | \
24 | \
25 | \
26 | \
27 | \
28 | \
29 | \
30 | \
31 | \
32 | \
33 | \
34 | \
35 | \
36 | \
37 | \
38 | \
39 | \
40 | \
41 | \
42 | \
43 | \
44 | \
45 | \
46 | \
47 | \
48 | \
49 | \
50 | \
51 | \
52 | \
53 | \
54 | \
55 | \
56 | \
57 | \
58 |
59 |
60 |
67 |
68 |
69 | Path in => out
70 |
71 |
--------------------------------------------------------------------------------