├── Get-Splunk-KO-LookupReference-Health.ps1 ├── Get-Splunkbase-Downloads.ps1 ├── Get-Win32Exceptions-Match.ps1 ├── GetSplunkLookupInfo.ps1 ├── PerfmonSelectionsToSplunkInput.ps1 ├── Remove-UniversalForwarder-BrokenMSI.ps1 ├── Remove-UniversalForwarder-BrokenMSI.vbs ├── ReplaceDashboardText.ps1 ├── SigmaToSplunk. └── main.py ├── Splunk_log_cfg.vbs ├── SysmonDnsQueryAnalysis.xml ├── SysmonLoadTest.ps1 ├── TS-ListSplunkAppsByCategory.ps1 ├── TestAppDownload.ps1 ├── demo.gif ├── demo_csv_to_splunk.py ├── download_latest_splunkproduct_docs.py ├── splunk_ntfs_check_and_set.ps1 ├── tp-splunk-appreset.vbs ├── ts-splunk-appage.vbs └── ts-universalforwarder-health.vbs /Get-Splunk-KO-LookupReference-Health.ps1: -------------------------------------------------------------------------------- 1 | # https://docs.splunk.com/Documentation/Splunk/7.2.6/RESTTUT/RESTsearches 2 | 3 | function create-searchjob { 4 | 5 | 6 | param ($cred, $server, $port, $search) 7 | 8 | # This will allow for self-signed SSL certs to work 9 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 10 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 11 | 12 | $url = "https://${server}:${port}/services/search/jobs" # braces needed b/c the colon is otherwise a scope operator 13 | $the_search = "$($search)" # Cmdlet handles urlencoding 14 | $body = @{ 15 | search = $the_search 16 | } 17 | 18 | $response = Invoke-RestMethod -Method Post -Uri $url -Credential $cred -Body $body -TimeoutSec 300 19 | return $response 20 | } 21 | 22 | function check-searchjobstatus { 23 | 24 | 25 | param ($cred, $server, $port, $jobsid) 26 | 27 | # This will allow for self-signed SSL certs to work 28 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 29 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 30 | 31 | $url = "https://${server}:${port}/services/search/jobs/$($jobsid)" 32 | $response = Invoke-RestMethod -Method Post -Uri $url -Credential $cred -TimeoutSec 300 33 | return $response 34 | } 35 | 36 | function get-searchjob { 37 | 38 | 39 | param ($cred, $server, $port, $jobsid) 40 | 41 | # This will allow for self-signed SSL certs to work 42 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 43 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 44 | 45 | $url = "https://${server}:${port}/services/search/jobs/$($jobsid)/results/" 46 | $body = @{ 47 | output_mode = "csv" 48 | } 49 | 50 | $response = Invoke-RestMethod -Method Get -Uri $url -Credential $cred -Body $body -TimeoutSec 300 51 | return $response 52 | } 53 | 54 | 55 | 56 | # define splunk instance variables to use 57 | $server = "splunk-dev" 58 | $port = "8089" 59 | 60 | # collect credentials from user, securely, at runtime 61 | if (!($cred)) { $cred = Get-Credential -Message "enter splunk cred" -UserName "admin" } 62 | 63 | # define the splunk search to execute 64 | $theSearch = '| rest /services/data/transforms/lookups 65 | | regex type="(kvstore|file|geo|external)" 66 | | eval object=case(type=="kvstore",collection,type=="external",external_cmd,type=="file",filename,type=="geo",filename,1==1,"Unknown") 67 | | eval marker = $eai:acl.app$ + "|" + $eai:acl.sharing$ + "|" + title 68 | | table eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, object, author, eai:acl.owner, id, marker 69 | | join type=left marker 70 | [| rest /services/data/props/lookups 71 | | eval marker = $eai:acl.app$ + "|" + $eai:acl.sharing$ + "|" + transform 72 | | rename id as AutoLookup 73 | | table marker, AutoLookup] 74 | | eval AutoLookup=if(isnull(AutoLookup),"FALSE","TRUE") 75 | | table eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, object, AutoLookup, author, eai:acl.owner, id, marker 76 | | sort 0 eai:acl.app, eai:acl.sharing, title' 77 | 78 | # initiate the job 79 | $results_1 = create-searchjob -server $server -port $port -cred $cred -search $theSearch 80 | 81 | # check status of job on interval 82 | $counter = 0 83 | do 84 | { 85 | # sleep 86 | $counter++ 87 | Start-Sleep -Seconds 1 88 | 89 | $jobstatus = check-searchjobstatus -server $server -port $port -cred $cred -jobsid $results_1.response.sid 90 | 91 | #The state of the search. Can be any of QUEUED, PARSING, RUNNING, PAUSED, FINALIZING, FAILED, DONE. 92 | $dispatchState = ($jobstatus.entry.content.dict.key | ?{$_.Name -eq "dispatchState"})."#text" 93 | write-host (get-date) " - Current dispatch status is [$($dispatchState)]." 94 | 95 | } 96 | until ($dispatchState -match "(FAILED|DONE)") 97 | 98 | # assuming successful completion, get results as CSV into PoshObject 99 | $results_1 = get-searchjob -server $server -port $port -cred $cred -jobsid $results_1.response.sid 100 | $results_1 = ConvertFrom-Csv -InputObject $results_1 101 | 102 | # filter results to only include lookup objects of type file and keystore 103 | $results_1 = $results_1 | ?{$_.type -match "(file|kvstore)"} 104 | 105 | # initialize the array of finalized results 106 | $lookupRecords = @() 107 | 108 | # Get the size and rowcount of each object 109 | foreach ($result_1 in $results_1) { 110 | # define the splunk search to execute 111 | $theSearch = "| inputlookup $($result_1.title)" 112 | 113 | write-host (get-date) " - Invoking search [$($theSearch)]." 114 | 115 | # initiate the job 116 | $results_2 = create-searchjob -server $server -port $port -cred $cred -search $theSearch 117 | 118 | # check status of job on interval 119 | $counter = 0 120 | do 121 | { 122 | # sleep 123 | $counter++ 124 | Start-Sleep -Seconds 1 125 | 126 | $jobstatus = check-searchjobstatus -server $server -port $port -cred $cred -jobsid $results_2.response.sid 127 | 128 | #The state of the search. Can be any of QUEUED, PARSING, RUNNING, PAUSED, FINALIZING, FAILED, DONE. 129 | $dispatchState = ($jobstatus.entry.content.dict.key | ?{$_.Name -eq "dispatchState"})."#text" 130 | write-host (get-date) " - Current dispatch status is [$($dispatchState)]." 131 | 132 | } 133 | until ($dispatchState -match "(FAILED|DONE)") 134 | 135 | # assuming successful completion, get results as CSV into PoshObject 136 | $results_2 = get-searchjob -server $server -port $port -cred $cred -jobsid $results_2.response.sid 137 | $results_2 = ConvertFrom-Csv -InputObject $results_2 138 | 139 | # get the count of records 140 | $recordCount = if($results_2) { $results_2.count } else { 0 } 141 | 142 | # get the size of the object 143 | if ($results_2) { 144 | $randString = Get-Random -Minimum 10000 -Maximum 99999 145 | $tmpfile = "$env:TEMP\spltmp$($randString)" 146 | if (Test-Path -Path $tmpfile) { Remove-Item -Path $tmpfile -Force } 147 | $results_2 | Export-Csv -NoTypeInformation -Path $tmpfile 148 | $tmpFileInfo = Get-Item -Path $tmpfile 149 | $lookupRecordsHash = (Get-FileHash -Path $tmpfile -Algorithm MD5).hash 150 | $lookupRecordsSize = $tmpFileInfo.length 151 | if (Test-Path -Path $tmpfile) { Remove-Item -Path $tmpfile -Force } 152 | } else { 153 | $lookupRecordsSize = 0 154 | $lookupRecordsHash = 0 155 | } 156 | 157 | $info = @{ 158 | "eai:acl.app" = $result_1.'eai:acl.app' 159 | "eai:acl.sharing" = $result_1.'eai:acl.sharing' 160 | "eai:acl.perms.read" = $result_1.'eai:acl.perms.read' 161 | "title" = $result_1.title 162 | "type" = $result_1.type 163 | "AutoLookup" = $result_1.AutoLookup 164 | "object" = $result_1.object 165 | "author" = $result_1.author 166 | "eai:acl.owner" = $result_1.'eai:acl.owner' 167 | "id" = $result_1.id 168 | "marker" = $result_1.marker 169 | "recordCount" = $recordCount 170 | "recordSetSize" = $lookupRecordsSize 171 | "recordSetHash" = $lookupRecordsHash 172 | } 173 | 174 | $lookupRecords += New-Object -TypeName PSObject -Property $info 175 | 176 | } 177 | 178 | #$lookupRecords | Select-Object -Property eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, AutoLookup, object, recordCount, recordSetSize, recordSetHash, author, eai:acl.owner, id, marker | Sort-Object -Property RecordSetSize -Descending | Out-GridView -Title "Lookup Info" 179 | 180 | 181 | # get all of the lookups referenced in all of the savedsearches/dasboards 182 | $theSearch = '| rest /servicesNS/-/-/data/ui/views splunk_server=local 183 | | rename eai:appName as appName, eai:acl.owner as owner, eai:acl.sharing as sharing, eai:data as data, eai:type as type 184 | | fields type, appName, sharing, owner, title, updated, data, id 185 | | append 186 | [| rest/servicesNS/-/-/saved/searches splunk_server=local 187 | | eval type="search" 188 | | rename eai:acl.app as appName, eai:acl.owner as owner, qualifiedSearch as data 189 | | fields type, appName, sharing, owner, title, updated, data, id 190 | ] 191 | | regex data="(?msi)\|\s*(input)?lookup\s+" 192 | | rex field=data "\|\s*(input)?lookup\s+(append=\S+\s+)?(start=\d+\s+)?(max=\d+\s+)?(local=\S+\s+)?(update=\S+\s+)?(?[^\]\s|]+)" max_match=0 193 | | sort 0 appName, type, title 194 | | mvexpand lookup_name 195 | | regex lookup_name!="^(on|supports|whoisLookup|dnslookup)$" 196 | | table type, lookup_name, appName, sharing, owner, title, updated, data, id' 197 | 198 | 199 | write-host (get-date) " - Invoking search to retrieve lookup references in savedsearches/views." 200 | 201 | # initiate the job 202 | $results_3 = create-searchjob -server $server -port $port -cred $cred -search $theSearch 203 | 204 | # check status of job on interval 205 | $counter = 0 206 | do 207 | { 208 | # sleep 209 | $counter++ 210 | Start-Sleep -Seconds 1 211 | 212 | $jobstatus = check-searchjobstatus -server $server -port $port -cred $cred -jobsid $results_3.response.sid 213 | 214 | #The state of the search. Can be any of QUEUED, PARSING, RUNNING, PAUSED, FINALIZING, FAILED, DONE. 215 | $dispatchState = ($jobstatus.entry.content.dict.key | ?{$_.Name -eq "dispatchState"})."#text" 216 | write-host (get-date) " - Current dispatch status is [$($dispatchState)]." 217 | 218 | } 219 | until ($dispatchState -match "(FAILED|DONE)") 220 | 221 | # assuming successful completion, get results as CSV into PoshObject 222 | $results_3 = get-searchjob -server $server -port $port -cred $cred -jobsid $results_3.response.sid 223 | $results_3 = ConvertFrom-Csv -InputObject $results_3 224 | 225 | # check to see if there are references to broken lookups in savedsearches/views 226 | $Findings = @() 227 | foreach ($item in $results_3) { 228 | write-host "Checking lookup [$($item.lookup_name)] reference in $($item.id)." 229 | 230 | $MatchFound = $False 231 | $Staus = "WARN" 232 | $Message = "" 233 | 234 | foreach ($lookupRecord in $lookupRecords) { 235 | if ($item.lookup_name -match "^$($lookupRecord.title)$") { 236 | $MatchFound = $True 237 | if ($lookupRecord.recordCount -eq 0) { 238 | $Status = "WARN" 239 | $Message = "matching lookup has 0 records" 240 | write-host "`tWARNING: Found matching record but lookup has $($lookuprecord.recordCount) entries!" -ForegroundColor Red 241 | } else { 242 | $Status = "OK" 243 | $Message = "matching lookup has more than 0 records" 244 | write-host "`tFound matching record having $($lookuprecord.recordCount) entries." -ForegroundColor Green 245 | } 246 | 247 | } 248 | } 249 | if ($MatchFound -eq $False) { 250 | $Status = "WARN" 251 | $Message = "no matching lookup found" 252 | write-host "`tWARNING: Did not find a matching record.!" -ForegroundColor Red 253 | } 254 | 255 | $info = @{ 256 | "type" = $item.type 257 | "lookup_name" = $item.lookup_name 258 | "appName" = $item.appName 259 | "sharing" = $item.sharing 260 | "owner" = $item.owner 261 | "title" = $item.title 262 | "updated" = $item.updated 263 | "data" = $item.data 264 | "id" = $item.id 265 | "status" = $Status 266 | "message" = $Message 267 | } 268 | 269 | $Findings += New-Object -TypeName PSObject -Property $info 270 | 271 | } 272 | 273 | $findings | Select-Object -Property appName, title, type, sharing, lookup_name, status, message, owner, updated, id, data | Sort-Object -Property appName, title, type | Out-GridView -Title "Findings" 274 | 275 | 276 | # find lookups that are not referenced by any searches/views 277 | $LookupReferences = $results_3 | Group-Object -Property lookup_name 278 | $lookupRecords2 = @() 279 | foreach ($lookupRecord in $lookupRecords) { 280 | 281 | $MatchFound = $False 282 | foreach ($LookupReference in $LookupReferences) { 283 | if ($lookupRecord.title -eq $LookupReference.Name) { 284 | $MatchFound = $True 285 | $LookupReferenceCount = $LookupReference.Count 286 | } 287 | } 288 | if ($MatchFound -eq $False) { 289 | $LookupReferenceCount = 0 290 | } 291 | 292 | $info = @{ 293 | "eai:acl.app" = $lookupRecord.'eai:acl.app' 294 | "eai:acl.sharing" = $lookupRecord.'eai:acl.sharing' 295 | "eai:acl.perms.read" = $lookupRecord.'eai:acl.perms.read' 296 | "title" = $lookupRecord.title 297 | "type" = $lookupRecord.type 298 | "AutoLookup" = $lookupRecord.AutoLookup 299 | "object" = $lookupRecord.object 300 | "author" = $lookupRecord.author 301 | "eai:acl.owner" = $lookupRecord.'eai:acl.owner' 302 | "id" = $lookupRecord.id 303 | "marker" = $lookupRecord.marker 304 | "recordCount" = $lookupRecord.recordCount 305 | "recordSetSize" = $lookupRecord.recordSetSize 306 | "recordSetHash" = $lookupRecord.recordSetHash 307 | "referenceCount" = $LookupReferenceCount 308 | } 309 | 310 | $lookupRecords2 += New-Object -TypeName PSObject -Property $info 311 | 312 | 313 | write-host "Lookup record $($lookupRecord.title) was referenced $($LookupReferenceCount) times." 314 | } 315 | 316 | $lookupRecords2 | Select-Object -Property eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, AutoLookup, object, recordCount, recordSetSize, recordSetHash, referenceCount, author, eai:acl.owner, id, marker | Sort-Object -Property RecordSetSize -Descending | Out-GridView -Title "Lookup Info" -------------------------------------------------------------------------------- /Get-Splunkbase-Downloads.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | $DebugPreference = "Continue" 3 | #> 4 | 5 | $ProjectFolder = "C:\Apps\splunkbase" 6 | $SupportFolder = "$($ProjectFolder)\support" 7 | $AppsToTrack = "$($SupportFolder)\myapps.csv" 8 | $BookmarkFile = "$($SupportFolder)\LastModifiedDate.md" 9 | $CatalogFile = "$($ProjectFolder)\catalog.csv" 10 | 11 | # if catalog file is missing, start anew 12 | if (-not(test-path -path $CatalogFile)) { 13 | if (Test-Path -Path $BookmarkFile) { remove-item -Path $BookmarkFile } 14 | } 15 | # if bookmark file exists, get the date it contains 16 | if (Test-Path -Path $BookmarkFile) { 17 | [datetime]$BookmarkDate = Get-Content -Path $BookmarkFile 18 | } else { 19 | $BookmarkDate = Get-Date -Date "01/01/1970" 20 | } 21 | 22 | 23 | function Get-Splunkbase-SessionCookie { 24 | param($credFilePath) 25 | $cred = Import-Clixml $credFilePath 26 | $user = $cred.UserName 27 | $pass = [System.Net.NetworkCredential]::new("", $cred.Password).Password 28 | 29 | ## establish logon session to splunk via okta 30 | $BASE_AUTH_URL='https://account.splunk.com/api/v1/okta/auth' 31 | $Body = @{ 32 | username = $user 33 | password = $pass 34 | } 35 | $WebRequest = Invoke-WebRequest $BASE_AUTH_URL -SessionVariable 'Session' -Body $Body -Method 'POST' -UseBasicParsing 36 | if (!($WebRequest.StatusCode -eq "200")) { 37 | write-host "There was a problem authenticating to Splunk. Exit." 38 | exit 39 | } 40 | $ssoid_cookie = (($WebRequest.Content | ConvertFrom-Json).cookies).ssoid_cookie 41 | $sid = ($Session.Cookies.GetCookies("https://splunkbase.splunk.com") | ?{$_.Name -eq "sid"}).Value 42 | 43 | $Cookie = "sid=$($sid); SSOSID=$($ssoid_cookie)" 44 | 45 | return $Cookie 46 | } 47 | 48 | function Get-Splunkbase-Session { 49 | param($credFilePath) 50 | $cred = Import-Clixml $credFilePath 51 | $user = $cred.UserName 52 | $pass = [System.Net.NetworkCredential]::new("", $cred.Password).Password 53 | 54 | ## establish logon session to splunk via okta 55 | $BASE_AUTH_URL='https://account.splunk.com/api/v1/okta/auth' 56 | $Body = @{ 57 | username = $user 58 | password = $pass 59 | } 60 | $WebRequest = Invoke-WebRequest $BASE_AUTH_URL -SessionVariable 'Session' -Body $Body -Method 'POST' -UseBasicParsing 61 | if (!($WebRequest.StatusCode -eq "200")) { 62 | write-host "There was a problem authenticating to Splunk. Exit." 63 | exit 64 | } 65 | 66 | $ssoid_cookie = (($WebRequest.Content | ConvertFrom-Json).cookies).ssoid_cookie 67 | 68 | $cookie = New-Object System.Net.Cookie 69 | $cookie.Name = "SSOSID" 70 | $cookie.Value = $ssoid_cookie 71 | $cookie.Domain = ".splunk.com" 72 | $session.Cookies.Add($cookie); 73 | 74 | return $session 75 | } 76 | 77 | function Get-Splunkbase-AppInfo { 78 | param($AppID=4023,$session=$null) 79 | 80 | $WebRequest = Invoke-WebRequest -Uri "https://splunkbase.splunk.com/app/$($appid)/" -WebSession $session 81 | 82 | # EXTRACT OUT THE APP VERSION 83 | 84 | $element = $WebRequest.AllElements | ?{ 85 | $_.class -eq "u.item:1/1@*" -and 86 | $_.tagname -eq "DIV" -and 87 | $_.innerhtml -match "sb-selector=`"release-version`"" -and 88 | $_.innerhtml -match "u-for=`"download-modal`"" -and 89 | $_.innerhtml -match "checksum" -and 90 | $_.outertext -match "^Downloading"} 91 | 92 | 93 | # extract out the release version (sb-target value) 94 | $version = "unknown" 95 | $filename = "unknown" 96 | $m = $element.innerHTML | select-string -pattern 'sb-target=\"([^\"]+)\".*?checksum \(([^\)]+)\)' -AllMatches 97 | if ($m) { 98 | $version = $m.matches.captures[0].Groups[1].value 99 | $filename = $m.matches.captures[0].Groups[2].value 100 | } else { 101 | $version = "none" 102 | $filename = "none" 103 | } 104 | 105 | # EXTRACT OUT THE SPLUNK VERSIONS SUPPORTED BY THIS VERSION OF APP 106 | 107 | # get all container info 108 | $element = $WebRequest.AllElements | ?{ 109 | $_.class -eq "u.container:vspace-xs@* u.container:vpad-md" -and 110 | $_.tagname -eq "DIV" -and 111 | $_.innerhtml -match "/apps/#/version/" 112 | } 113 | 114 | # find the sb-release section associated with our known release and extract out the list of hyperlinks 115 | $splunkVersions = "unknown" 116 | $pattern = 'sb-target=\"' + $version + '\" sb-selector="release-version">Splunk Versions:\s(.*?) <\/SB-RELEASE-SELECT>' 117 | $m = $element.innerHTML | select-string -pattern $pattern -AllMatches 118 | if ($m) { 119 | 120 | $versionLinks = $m.Matches.captures[0].Groups[1].Value 121 | 122 | # extract out the splunk versions from the list of links 123 | $m = $versionLinks | select-string -pattern 'version\/([^\"]+)' -AllMatches 124 | 125 | # transform the array of links to a pipe delimited string 126 | [object]$CaptureValues = @() 127 | for ($i = 0; $i -le $m.matches.Count -1; $i++) 128 | { 129 | $CaptureValue = $m.Matches.Captures[$i].groups[1].Value 130 | $CaptureValues += $CaptureValue 131 | } 132 | [string]$splunkVersions = $CaptureValues -join "|" 133 | } else { 134 | $splunkVersions = "none" 135 | } 136 | 137 | # EXTRACT OUT THE CIM VERSIONS SUPPORTED BY THIS VERSION OF APP 138 | 139 | # get all container info 140 | $element = $WebRequest.AllElements | ?{ 141 | $_.class -eq "u.container:vspace-xs@* u.container:vpad-md" -and 142 | $_.tagname -eq "DIV" -and 143 | $_.innerhtml -match "/apps/#/version/" 144 | } 145 | 146 | # find the sb-release section associated with our known release and extract out the list of hyperlinks 147 | $CIMVersions = "unknown" 148 | $pattern = 'sb-target=\"' + $version + '\" sb-selector="release-version">CIM Versions:\s(.*?) <\/SB-RELEASE-SELECT>' 149 | $m = $element.innerHTML | select-string -pattern $pattern -AllMatches 150 | if ($m) { 151 | $versionLinks = $m.Matches.captures[0].Groups[1].Value 152 | 153 | # extract out the splunk versions from the list of links 154 | $m = $versionLinks | select-string -pattern 'cim\/([^\"]+)' -AllMatches 155 | 156 | # transform the array of links to a pipe delimited string 157 | [object]$CaptureValues = @() 158 | for ($i = 0; $i -le $m.matches.Count -1; $i++) 159 | { 160 | $CaptureValue = $m.Matches.Captures[$i].groups[1].Value 161 | $CaptureValues += $CaptureValue 162 | } 163 | [string]$CIMVersions = $CaptureValues -join "|" 164 | } else { 165 | $CIMVersions = "none" 166 | } 167 | 168 | # put all this juicy extracted content into an object to return from function 169 | $AppInfo = [pscustomobject]@{ 170 | version = $version 171 | filename = $filename 172 | splunkVersions = $splunkVersions 173 | CIMVersions = $CIMVersions 174 | id=$appid 175 | } 176 | 177 | # return new object with desired info 178 | return $AppInfo 179 | 180 | } 181 | 182 | # gather cred from credstore 183 | $mycredfile = "$($SupportFolder)\mycred.xml" 184 | if (-not(Test-Path -Path $mycredfile)) { 185 | Get-Credential -Message "Enter credential for Splunkbase" | Export-Clixml -path $mycredfile 186 | } 187 | $cred = Import-Clixml $mycredfile 188 | 189 | $Session = Get-Splunkbase-Session -credFilePath $mycredfile 190 | 191 | 192 | # first run just to get the amount of pages to iterate over. 193 | $url = "https://splunkbase.splunk.com/api/v1/app/?order=latest&limit=1&offset=0" 194 | $response = invoke-webrequest $url -WebSession $session -UseBasicParsing 195 | $content = $response.Content | ConvertFrom-Json 196 | 197 | # gather all of the content available over pages 198 | $results = @() 199 | for ($offset = 0; $offset -le $content.total; $offset += 100) 200 | { 201 | write-debug "Getting next 100 results from offset $($offset) [total=$($content.total)]" 202 | $url = "https://splunkbase.splunk.com/api/v1/app/?order=latest&limit=100&offset=$($offset)" 203 | $response = invoke-webrequest $url -WebSession $Session -UseBasicParsing 204 | $content = $response.Content | ConvertFrom-Json 205 | $results += $content.results 206 | } 207 | 208 | # Get current date/time of our query of splunkbase. We will commit this as our bookmark if we get to end of script 209 | $LastQueryDate = Get-Date 210 | 211 | # Import the last version of catalog from file if prsent. 212 | if (test-path -path $catalogfile) { $CachedCatalog = Import-csv -Path $CatalogFile } 213 | 214 | 215 | 216 | $counter = 0 217 | foreach ($item in $results) { 218 | $counter++ 219 | 220 | write-debug "[Item $($counter) of $($results.count)] - Gathering info on `"$($item.title)`" having uid `"$($item.uid)`"." 221 | 222 | # Check to see splunk base entry is newer than last evaluation 223 | if ([datetime]$item.updated_time -gt $BookmarkDate) { 224 | 225 | # scrape additional information about app (version, filename, splunk versions supported) from webpage for app on splunkbase 226 | $AppInfo = Get-Splunkbase-AppInfo -appid $item.uid 227 | 228 | # add discovered properties to the results array 229 | write-debug "`t-Scraped app version `"$($AppInfo.version)`", splunk versions `"$($appinfo.splunkVersions)`", and CIM versions `"$($appinfo.CIMVersions)`" to app record in results array." 230 | Add-Member -InputObject $item -MemberType NoteProperty -Force -Name "version" -Value $AppInfo.version 231 | Add-Member -InputObject $item -MemberType NoteProperty -Force -Name "splunkVersions" -Value $AppInfo.splunkVersions 232 | Add-Member -InputObject $item -MemberType NoteProperty -Force -Name "CIMVersions" -Value $AppInfo.CIMVersions 233 | 234 | } else { 235 | # Since entry is not newer, import app info from last update file instead of web (much faster) 236 | 237 | # pull up app record from object representing previously imported file 238 | $Entry = $CachedCatalog | ?{$_.uid -eq $item.uid} 239 | write-debug "`t-Imported app version `"$($Entry.version)`", splunk versions `"$($Entry.splunkVersions)`", and CIM versions `"$($Entry.CIMVersions)`" to app record in results array." 240 | 241 | # add version value to member of results array 242 | Add-Member -InputObject $item -MemberType NoteProperty -Force -Name "version" -Value $entry.version 243 | Add-Member -InputObject $item -MemberType NoteProperty -Force -Name "splunkVersions" -Value $entry.splunkVersions 244 | Add-Member -InputObject $item -MemberType NoteProperty -Force -Name "CIMVersions" -Value $entry.CIMVersions 245 | 246 | } 247 | 248 | } 249 | 250 | # commit results array appended with additional properties to file 251 | $results | Export-Csv -Path $CatalogFile -NoTypeInformation 252 | 253 | # update bookmark file with date of last run 254 | Set-Content -Path $BookmarkFile -Value $LastQueryDate.ToString() 255 | 256 | -------------------------------------------------------------------------------- /Get-Win32Exceptions-Match.ps1: -------------------------------------------------------------------------------- 1 |  2 | $tmpFile = "$($env:TEMP)\Win32ExceptionsList.csv" 3 | 4 | $Messages = @() 5 | $filter = "^DNS" 6 | 7 | for ($i = 0; $i -lt 65500; $i++) 8 | { 9 | $Message = [ComponentModel.Win32Exception] $i 10 | if (($i -eq 0) -or ($Message.Message -match $filter)) { 11 | $Info = @{ 12 | ErrorCode = $i 13 | Message = $Message.Message 14 | 15 | } 16 | $Messages += New-Object -TypeName PSObject -Property $Info 17 | } 18 | } 19 | 20 | $Selected = $Messages | Select-Object -Property ErrorCode, Message | Sort-Object ErrorCode| Out-GridView -PassThru -Title "Select a class of messages" 21 | 22 | if ($Selected) { 23 | if (Test-Path -Path $tmpFile) { Remove-Item -Path $tmpFile -Force } 24 | $Selected | Export-Csv -Path $tmpFile -NoTypeInformation 25 | Write-Host "output written to $($tmpFile)." 26 | & notepad.exe $tmpFile 27 | } 28 | 29 | <# SPLUNK CASE STATEMENT CREATE 30 | 31 | $tmpFile2 = "$($env:TEMP)\SPLCaseCreate,txt" 32 | $statement = "" 33 | 34 | $Selected = $Selected | Sort-Object -Property ErrorCode 35 | foreach ($item in $Selected) { 36 | if ($statement -eq "") { 37 | $statement = "eval statement = case(QueryStatus==`"$($item.ErrorCode)`",`"$($item.Message)`"" 38 | } else { 39 | $statement += ",QueryStatus==`"$($item.ErrorCode)`",`"$($item.Message)`"" 40 | } 41 | } 42 | 43 | $statement += ",1==1,`"Unknown`")" 44 | $statement 45 | 46 | if (Test-Path -Path $tmpFile2) { Remove-Item -Path $tmpFile2 -Force } 47 | $statement | Add-Content -Path $tmpFile2 48 | & notepad.exe $tmpFile2 49 | 50 | #> -------------------------------------------------------------------------------- /GetSplunkLookupInfo.ps1: -------------------------------------------------------------------------------- 1 | # https://docs.splunk.com/Documentation/Splunk/7.2.6/RESTTUT/RESTsearches 2 | 3 | function create-searchjob { 4 | 5 | 6 | param ($cred, $server, $port, $search) 7 | 8 | # This will allow for self-signed SSL certs to work 9 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 10 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 11 | 12 | $url = "https://${server}:${port}/services/search/jobs" # braces needed b/c the colon is otherwise a scope operator 13 | $the_search = "$($search)" # Cmdlet handles urlencoding 14 | $body = @{ 15 | search = $the_search 16 | } 17 | 18 | $response = Invoke-RestMethod -Method Post -Uri $url -Credential $cred -Body $body -TimeoutSec 300 19 | return $response 20 | } 21 | 22 | function check-searchjobstatus { 23 | 24 | 25 | param ($cred, $server, $port, $jobsid) 26 | 27 | # This will allow for self-signed SSL certs to work 28 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 29 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 30 | 31 | $url = "https://${server}:${port}/services/search/jobs/$($jobsid)" 32 | $response = Invoke-RestMethod -Method Post -Uri $url -Credential $cred -TimeoutSec 300 33 | return $response 34 | } 35 | 36 | function get-searchjob { 37 | 38 | 39 | param ($cred, $server, $port, $jobsid) 40 | 41 | # This will allow for self-signed SSL certs to work 42 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 43 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 44 | 45 | $url = "https://${server}:${port}/services/search/jobs/$($jobsid)/results/" 46 | $body = @{ 47 | output_mode = "csv" 48 | } 49 | 50 | $response = Invoke-RestMethod -Method Get -Uri $url -Credential $cred -Body $body -TimeoutSec 300 51 | return $response 52 | } 53 | 54 | 55 | 56 | # define splunk instance variables to use 57 | $server = "splunk-dev" 58 | $port = "8089" 59 | 60 | # collect credentials from user, securely, at runtime 61 | if (!($cred)) { $cred = Get-Credential -Message "enter splunk cred" -UserName "admin" } 62 | 63 | # define the splunk search to execute 64 | $theSearch = '| rest /services/data/transforms/lookups 65 | | regex type="(kvstore|file|geo|external)" 66 | | eval object=case(type=="kvstore",collection,type=="external",external_cmd,type=="file",filename,type=="geo",filename,1==1,"Unknown") 67 | | eval marker = $eai:acl.app$ + "|" + $eai:acl.sharing$ + "|" + title 68 | | table eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, object, author, eai:acl.owner, id, marker 69 | | join type=left marker 70 | [| rest /services/data/props/lookups 71 | | eval marker = $eai:acl.app$ + "|" + $eai:acl.sharing$ + "|" + transform 72 | | rename id as AutoLookup 73 | | table marker, AutoLookup] 74 | | eval AutoLookup=if(isnull(AutoLookup),"FALSE","TRUE") 75 | | table eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, object, AutoLookup, author, eai:acl.owner, id, marker 76 | | sort 0 eai:acl.app, eai:acl.sharing, title' 77 | 78 | # initiate the job 79 | $results_1 = create-searchjob -server $server -port $port -cred $cred -search $theSearch 80 | 81 | # check status of job on interval 82 | $counter = 0 83 | do 84 | { 85 | # sleep 86 | $counter++ 87 | Start-Sleep -Seconds 1 88 | 89 | $jobstatus = check-searchjobstatus -server $server -port $port -cred $cred -jobsid $results_1.response.sid 90 | 91 | #The state of the search. Can be any of QUEUED, PARSING, RUNNING, PAUSED, FINALIZING, FAILED, DONE. 92 | $dispatchState = ($jobstatus.entry.content.dict.key | ?{$_.Name -eq "dispatchState"})."#text" 93 | write-host (get-date) " - Current dispatch status is [$($dispatchState)]." 94 | 95 | } 96 | until ($dispatchState -match "(FAILED|DONE)") 97 | 98 | # assuming successful completion, get results as CSV into PoshObject 99 | $results_1 = get-searchjob -server $server -port $port -cred $cred -jobsid $results_1.response.sid 100 | $results_1 = ConvertFrom-Csv -InputObject $results_1 101 | 102 | # filter results to only include lookup objects of type file and keystore 103 | $results_1 = $results_1 | ?{$_.type -match "(file|kvstore)"} 104 | 105 | # initialize the array of finalized results 106 | $recordSet = @() 107 | 108 | # Get the size and rowcount of each object 109 | foreach ($result_1 in $results_1) { 110 | # define the splunk search to execute 111 | $theSearch = "| inputlookup $($result_1.title)" 112 | 113 | write-host (get-date) " - Invoking search [$($theSearch)]." 114 | 115 | # initiate the job 116 | $results_2 = create-searchjob -server $server -port $port -cred $cred -search $theSearch 117 | 118 | # check status of job on interval 119 | $counter = 0 120 | do 121 | { 122 | # sleep 123 | $counter++ 124 | Start-Sleep -Seconds 1 125 | 126 | $jobstatus = check-searchjobstatus -server $server -port $port -cred $cred -jobsid $results_2.response.sid 127 | 128 | #The state of the search. Can be any of QUEUED, PARSING, RUNNING, PAUSED, FINALIZING, FAILED, DONE. 129 | $dispatchState = ($jobstatus.entry.content.dict.key | ?{$_.Name -eq "dispatchState"})."#text" 130 | write-host (get-date) " - Current dispatch status is [$($dispatchState)]." 131 | 132 | } 133 | until ($dispatchState -match "(FAILED|DONE)") 134 | 135 | # assuming successful completion, get results as CSV into PoshObject 136 | $results_2 = get-searchjob -server $server -port $port -cred $cred -jobsid $results_2.response.sid 137 | $results_2 = ConvertFrom-Csv -InputObject $results_2 138 | 139 | # get the count of records 140 | $recordCount = if($results_2) { $results_2.count } else { 0 } 141 | 142 | # get the size of the object 143 | if ($results_2) { 144 | $randString = Get-Random -Minimum 10000 -Maximum 99999 145 | $tmpfile = "$env:TEMP\spltmp$($randString)" 146 | if (Test-Path -Path $tmpfile) { Remove-Item -Path $tmpfile -Force } 147 | $results_2 | Export-Csv -NoTypeInformation -Path $tmpfile 148 | $tmpFileInfo = Get-Item -Path $tmpfile 149 | $recordSetHash = (Get-FileHash -Path $tmpfile -Algorithm MD5).hash 150 | $recordSetSize = $tmpFileInfo.length 151 | if (Test-Path -Path $tmpfile) { Remove-Item -Path $tmpfile -Force } 152 | } else { 153 | $recordSetSize = 0 154 | $recordSetHash = 0 155 | } 156 | 157 | $info = @{ 158 | "eai:acl.app" = $result_1.'eai:acl.app' 159 | "eai:acl.sharing" = $result_1.'eai:acl.sharing' 160 | "eai:acl.perms.read" = $result_1.'eai:acl.perms.read' 161 | "title" = $result_1.title 162 | "type" = $result_1.type 163 | "AutoLookup" = $result_1.AutoLookup 164 | "object" = $result_1.object 165 | "author" = $result_1.author 166 | "eai:acl.owner" = $result_1.'eai:acl.owner' 167 | "id" = $result_1.id 168 | "marker" = $result_1.marker 169 | "recordCount" = $recordCount 170 | "recordSetSize" = $recordSetSize 171 | "recordSetHash" = $recordSetHash 172 | } 173 | 174 | $recordSet += New-Object -TypeName PSObject -Property $info 175 | 176 | } 177 | 178 | $recordSet | Select-Object -Property eai:acl.app, eai:acl.sharing, eai:acl.perms.read, title, type, AutoLookup, object, recordCount, recordSetSize, recordSetHash, author, eai:acl.owner, id, marker | Sort-Object -Property RecordSetSize -Descending | Out-GridView -Title "Lookup Info" 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | -------------------------------------------------------------------------------- /PerfmonSelectionsToSplunkInput.ps1: -------------------------------------------------------------------------------- 1 | $SamplingInterval = 1000 2 | $Interval = 300 3 | $StatTypes = "average;count;dev;min;max" 4 | $Index = "main" 5 | $ComputerName = $env:computername 6 | 7 | 8 | $Objects = Get-Counter -ListSet "*" -ComputerName $ComputerName 9 | $ObjectsView = $Objects 10 | 11 | 12 | $Records = @() 13 | 14 | $SelectedObjects = $ObjectsView | Select CounterSetName, Description | Sort-Object -Property CounterSetName, Description | Out-GridView -Title "Select objects of interest" -PassThru 15 | foreach ($Object in $SelectedObjects) { 16 | 17 | $SelectedObjectCounters = $Objects | ?{$_.CounterSetName -eq $Object.CounterSetName} 18 | 19 | $theseCounters = "" 20 | foreach ($SelectedObjectCounter in $SelectedObjectCounters.Counter) { 21 | 22 | $thisCounter = $SelectedObjectCounter -replace "\\.*\\","" 23 | 24 | if ($theseCounters -eq "") { 25 | $theseCounters = $thisCounter 26 | } else { 27 | $theseCounters += "; $($thisCounter)" 28 | } 29 | 30 | } 31 | 32 | $record = @{ 33 | 'object' = ($object.CounterSetName).Trim() 34 | 'counter' = ($theseCounters).Trim() 35 | } 36 | 37 | $records += New-Object -TypeName PSObject -Property $Record 38 | 39 | } 40 | 41 | # if records were selected, prepare sample splunk inputs and appent to file 42 | 43 | if ($records) { 44 | 45 | $randomNumber = Get-Random -Minimum 1 -Maximum 1000 46 | $tmpFile = "$env:temp\splunktmp_$($randomNumber).txt" 47 | if (Test-Path -Path $tmpFile) { Remove-Item -Path $tmpFile } 48 | 49 | 50 | foreach ($Record in $Records) { 51 | 52 | Add-Content -Path $tmpFile -Value "" 53 | Add-Content -Path $tmpFile -Value "[perfmon://$($record.object)]" 54 | Add-Content -Path $tmpFile -Value "#counters = $($record.counter)" 55 | Add-Content -Path $tmpFile -Value "counters = *" 56 | Add-Content -Path $tmpFile -Value "disabled = 0" 57 | Add-Content -Path $tmpFile -Value "interval = $($Interval)" 58 | Add-Content -Path $tmpFile -Value "object = $($record.object)" 59 | Add-Content -Path $tmpFile -Value "useEnglishOnly=true" 60 | Add-Content -Path $tmpFile -Value "mode = multikv" 61 | Add-Content -Path $tmpFile -Value "samplingInterval = $($SamplingInterval)" 62 | Add-Content -Path $tmpFile -Value "stats = $($statTypes)" 63 | Add-Content -Path $tmpFile -Value "index = $($Index)" 64 | 65 | } 66 | 67 | Start-Process -FilePath "Notepad.exe" -ArgumentList $tmpFile -Wait 68 | if (Test-Path -Path $tmpFile) { Remove-Item -Path $tmpFile } 69 | } 70 | 71 | 72 | -------------------------------------------------------------------------------- /Remove-UniversalForwarder-BrokenMSI.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Removes Splunk UniversalForwarder from host without windows installer assitance. 4 | .DESCRIPTION 5 | For use only in cases where MSI package install/uninstall routines fail. 6 | .NOTES 7 | Use at your own risk as last resort. 8 | #> 9 | 10 | $OrigVerbosePreference = $VerbosePreference 11 | $OrigDebugPreference = $DebugPreference 12 | $VerbosePreference = "Continue" 13 | $DebugPreference = "SilentlyContinue" 14 | 15 | ######################################## 16 | ### FUNCTIONS 17 | ######################################## 18 | 19 | 20 | function remove-installer-packagekeys { 21 | param ($ProductCode) 22 | 23 | if (!(test-path HKCR:)) { New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT } 24 | 25 | $Key = "HKCR:\Installer\Products\$($ProductCode)" 26 | if (test-path $Key) { 27 | write-verbose "found installer key: $($Key), removing it." 28 | get-item $Key | Remove-Item -Recurse 29 | } 30 | 31 | $Key = "HKCR:\Installer\Features\$($ProductCode)" 32 | if (test-path $Key) { 33 | write-verbose "found feature key: $($Key), removing it." 34 | get-item $Key | Remove-Item -Recurse 35 | } 36 | 37 | $keys = Get-ChildItem "HKCR:\Installer\UpgradeCodes" 38 | foreach ($Key in $Keys) { 39 | if ($Key.Property -eq $ProductCode) { 40 | Write-verbose "found upgrade key $($Key), removing it." 41 | $Key | Remove-Item -Recurse 42 | } 43 | } 44 | } # remove-regkey-hkcr 45 | 46 | ######################################## 47 | ### MAIN 48 | ######################################## 49 | 50 | ### IF SERVICE IS RUNNING, STOP IT 51 | $ServiceName = "SplunkForwarder" 52 | $Service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue 53 | if ($Service) { 54 | Write-Verbose "$($ServiceName) service is running, stopping it." 55 | $Service | Stop-Service -Force 56 | } 57 | 58 | 59 | 60 | ### IF SERVICE IS REGISTERED, DELETE IT 61 | $Service = Get-WmiObject -Class Win32_Service -Filter "Name='$($ServiceName)'" 62 | if ($Service) { 63 | Write-Verbose "$($ServiceName) service is present, deleting it." 64 | [void] $service.delete() 65 | } 66 | 67 | 68 | ### IF INSTALLATION DIRECTORY IS PRESENT, REMOVE IT 69 | $InstallDir = "C:\Program Files\SplunkUniversalForwarder" 70 | if (Test-Path -Path $InstallDir) { 71 | Write-Verbose "Found $($InstallDir), removing it." 72 | Remove-Item -Path $InstallDir -Recurse 73 | } 74 | 75 | 76 | ### IF DRIVERS ARE PRESENT, REMOVE THEM 77 | $SearchFor = "Splunk" 78 | $results = @() 79 | $keys = Get-ChildItem "HKLM:\System\CurrentControlSet\Services" 80 | foreach ($Key in $Keys) { 81 | $obj = New-Object psobject 82 | Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayName -Value $Key.GetValue("DisplayName") 83 | Add-Member -InputObject $obj -MemberType NoteProperty -Name Description -Value $Key.GetValue("Description") 84 | Add-Member -InputObject $obj -MemberType NoteProperty -Name Path -Value $Key.PSPath 85 | $results += $obj 86 | } 87 | $results = $results | where {(($_.DisplayName -match $SearchFor) -or ($_.Description -match $SearchFor))} 88 | foreach ($result in $results) { 89 | Write-Verbose "Found $($result.DisplayName) driver, removing." 90 | $result | Remove-Item -Recurse 91 | } 92 | 93 | 94 | ### IF UNINSTALL KEY IS PRESENT, REMOVE IT 95 | $results = @() 96 | $SearchFor = "UniversalForwarder" 97 | $keys = Get-ChildItem HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall 98 | foreach ($Key in $Keys) { 99 | $obj = New-Object psobject 100 | Add-Member -InputObject $obj -MemberType NoteProperty -Name GUID -Value $Key.pschildname 101 | Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayName -Value $Key.GetValue("DisplayName") 102 | Add-Member -InputObject $obj -MemberType NoteProperty -Name DisplayVersion -Value $Key.GetValue("DisplayVersion") 103 | Add-Member -InputObject $obj -MemberType NoteProperty -Name Path -Value $Key.PSPath 104 | $results += $obj 105 | } 106 | $results = $results | where {$_.DisplayName -match $SearchFor} 107 | foreach ($result in $results) { 108 | Write-Verbose "Found $($result.DisplayName) uninstall key, removing." 109 | $result | Remove-Item -Recurse 110 | } 111 | 112 | 113 | ### IF PRODUCT KEY IS PRESENT, REMOVE IT 114 | if (!(test-path HKCR:)) { New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT } 115 | $results = @() 116 | $SearchFor = "UniversalForwarder" 117 | $keys = Get-ChildItem HKCR:\Installer\Products 118 | foreach ($Key in $Keys) { 119 | $obj = New-Object psobject 120 | Add-Member -InputObject $obj -MemberType NoteProperty -Name Name -Value $Key.PSChildName 121 | Add-Member -InputObject $obj -MemberType NoteProperty -Name ProductName -Value $Key.GetValue("ProductName") 122 | $results += $obj 123 | } 124 | $results = $results | where {$_.ProductName -match $SearchFor} 125 | foreach ($result in $results) { 126 | $ProductCode = $Result.Name 127 | Write-Verbose "Found ProductCode $($ProductCode) for $($SearchFor) product, removing installer references." 128 | remove-installer-packagekeys -ProductCode $ProductCode 129 | } 130 | 131 | 132 | ### SET LOGGING LEVELS BACK TO ORIGINAL STATE 133 | $VerbosePreference = $OrigVerbosePreference 134 | $DebugPreference = $OrigDebugPreference -------------------------------------------------------------------------------- /Remove-UniversalForwarder-BrokenMSI.vbs: -------------------------------------------------------------------------------- 1 | option explicit 2 | 3 | Dim objShell :: Set objShell = CreateObject("WScript.Shell") 4 | Dim ServiceResults 5 | '### IF SERVICE IS RUNNING, STOP AND DELETE IT 6 | dim objWMIService, colListOfServices, objService 7 | Set objWMIService = GetObject("winmgmts:" _ 8 | & "{impersonationLevel=impersonate}!\\.\root\cimv2") 9 | Set colListOfServices = objWMIService.ExecQuery _ 10 | ("Select * from Win32_Service Where Name = 'SplunkForwarder'") 11 | For Each objService in colListOfServices 12 | wscript.echo "Removing service: " & objService.Name 13 | ServiceResults = objService.StopService() 14 | objService.Delete() 15 | wscript.sleep(5000) 16 | Next 17 | 18 | '### IF INSTALLATION DIRECTORY IS PRESENT, REMOVE IT 19 | Dim objFso :: Set objfso = CreateObject("Scripting.FileSystemObject") 20 | dim strSplunkHome :: strSplunkHome = "C:\Program Files\SplunkUniversalForwarder" 21 | if objFso.FolderExists(strSplunkHome) then 22 | wscript.echo "Removing folder: " & strSplunkHome 23 | objFSO.DeleteFolder(strSplunkHome) 24 | end if 25 | 26 | '### IF DRIVERS ARE PRESENT, REMOVE THEM 27 | Dim objReg, strKeyPath, arrSubKeys, subkey, arrValueNames, arrValueTypes, key, displayName, description 28 | Const HKLM = &H80000002 29 | strKeyPath = "SYSTEM\CurrentControlSet\Services" 30 | Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv") 31 | objReg.EnumKey HKLM, strKeyPath, arrSubKeys 32 | On Error Resume Next 33 | For Each key in arrSubKeys 34 | objReg.GetStringValue HKLM,strKeyPath & "\" & key,"DisplayName", displayName 35 | objReg.GetStringValue HKLM,strKeyPath & "\" & key,"Description", description 36 | If ((InStr(LCase(key),lcase("Splunk")) > 0) or (InStr(LCase(displayName),lcase("Splunk")) > 0) or (InStr(LCase(description),lcase("Splunk")) > 0)) Then 37 | if Is64 = True then 38 | DeleteRegistryKey "64","HKLM" & "\" & strKeyPath & "\" & key 39 | else 40 | DeleteRegistryKey "32","HKLM" & "\" & strKeyPath & "\" & key 41 | end if 42 | End If 43 | Next 44 | On Error Goto 0 45 | 46 | '### IF UNINSTALL KEY IS PRESENT, REMOVE IT 47 | dim sResults, sOsArch, bMatch, sInstalledArch, keyPath, key64Path 48 | sOsArch = GetOSSystemType() 49 | 50 | Dim strApplicationMatchString :: strApplicationMatchString = lcase(trim("UniversalForwarder")) 51 | 52 | Set objReg = Getx64RegistryProvider() 53 | keyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" 54 | key64Path = "SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall" 55 | 56 | 57 | ' list out 32-bit applications on a 32-bit system, or 64-bit applications 58 | ' on a 64-bit system. 59 | If RegKeyExists(objReg, HKLM, keyPath) Then 60 | objReg.EnumKey HKLM, keypath, arrSubKeys 61 | sResults = GetApplications(HKLM,keypath,arrSubKeys) 62 | If len(sResults)>1 Then 63 | if Is64 = True then 64 | DeleteRegistryKey "64","HKLM" & "\" & sResults 65 | else 66 | DeleteRegistryKey "32","HKLM" & "\" & sResults 67 | end if 68 | end if 69 | End If 70 | 71 | '### IF PRODUCT KEY IS PRESENT, REMOVE IT 72 | Dim ProductName 73 | Const HKCR = &H80000000 74 | strKeyPath = "Installer\Products" 75 | Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv") 76 | objReg.EnumKey HKCR, strKeyPath, arrSubKeys 77 | On Error Resume Next 78 | For Each key in arrSubKeys 79 | objReg.GetStringValue HKCR,strKeyPath & "\" & key,"ProductName", ProductName 80 | If InStr(LCase(ProductName),lcase("UniversalForwarder")) > 0 Then 81 | if Is64 = True then 82 | DeleteRegistryKey "64","HKCR" & "\" & strKeyPath & "\" & key 83 | else 84 | DeleteRegistryKey "32","HKCR" & "\" & strKeyPath & "\" & key 85 | end if 86 | End If 87 | Next 88 | On Error Goto 0 89 | 90 | 91 | 92 | Function GetApplications(HIVE, keypath,arrSubKeys) 93 | On Error Resume Next 94 | dim displayName, description 95 | For Each key in arrSubKeys 96 | objReg.GetStringValue HIVE,keyPath & "\" & key,"DisplayName", displayName 97 | objReg.GetStringValue HIVE,keyPath & "\" & key,"Description", description 98 | If (InStr(LCase(displayName),strApplicationMatchString) > 0) or (InStr(LCase(description),strApplicationMatchString) > 0) OR (InStr(LCase(key),strApplicationMatchString) > 0) Then 99 | GetApplications = keypath & "\" & key 100 | End If 101 | Next 102 | On Error Goto 0 103 | End Function 'GetApplications 104 | 105 | Sub DeleteRegistryKey(targetArch, targetKey) 106 | 107 | dim words, strHive, constHive, targetDefaultKey 108 | 'Target architecture is needed since 64-bit machines have different branches for 32 and 64 109 | targetArch = LCase(targetArch) 110 | targetKey = targetKey 111 | 112 | If targetArch = "64" And Not(Is64) Then 113 | WScript.Echo "64-bit registry unavailable on 32-bit system" 114 | WScript.Quit 115 | End If 116 | 117 | 'Split up strKey into the hive constant and the registry key 118 | words = Split(targetKey, "\") 119 | strHive = words(0) 120 | constHive = GetHiveConst(strHive) 121 | 122 | targetDefaultKey = Right(targetKey, Len(targetKey) - Len(strHive) -1) 123 | 124 | If strHive = "HKEY_USERS" Then 125 | ' go through each User's hive 126 | Dim arrSubKeys, strUserKey 127 | objReg.EnumKey constHive, "", arrSubKeys 128 | For Each strUserKey In arrSubKeys 129 | If Not InStr(strUserKey,"_Classes") > 0 Then ' ignore _Classes entries 130 | DeleteKey64or32orBoth objReg, targetArch, strUserKey & "\", targetDefaultKey, strHive, constHive 131 | End If 132 | Next 133 | Else ' was another hive 134 | DeleteKey64or32orBoth objReg, targetArch, "", targetDefaultKey, strHive, constHive 135 | End If 136 | 137 | 138 | End Sub 139 | 140 | Function DeleteKey64or32orBoth(objReg, targetArch, targetPreFix, targetKey, strHive, constHive) 141 | Dim targetWowKey 142 | targetWowKey = targetKey 143 | 'Catch the 32-bit entries for any 64-bit machines 144 | If Is64 Then 145 | If targetArch = "32" Or targetArch = "both" Then 146 | WScript.Echo "is64, but deleting 32. need to check for software/ and add wow6432node" 147 | 148 | If Left(LCase(targetWowKey), Len("software\")) = "software\" Then 149 | 'need to insert wow6432node 150 | targetWowKey = "software\wow6432node\" & Right(targetWowKey, Len(targetWowKey) - Len("software\")) 151 | End If 152 | 153 | If targetWowKey <> targetKey Then 154 | 'Catch Wow6432Node keys on 64-bit machines, if necessary 155 | DeleteKey objReg, constHive, targetPreFix & targetWowKey, targetArch, strHive 156 | Else 157 | ' Deleting a 64-bit value somewhere which is not under the Software key 158 | DeleteKey objReg, constHive, targetPreFix & targetKey, targetArch, strHive 159 | End If 160 | End If 161 | 162 | If targetArch = "64" Or targetArch = "both" Then 163 | 'Catch "64" and "both" for 64-bit machines 164 | DeleteKey objReg, constHive, targetPreFix & targetKey, targetArch, strHive 165 | End If 166 | Else 167 | 'Catch "32" and "both" for 32-bit machines, "64" on 32-bit machines already ruled out with Quit 168 | DeleteKey objReg, constHive, targetPreFix & targetKey, targetArch, strHive 169 | End If 170 | End Function ' DeleteKey64or32orBoth 171 | 172 | Function DeleteKey(ojReg, constHive, targetKey, targetArch, strHive) 173 | ' objReg.DeleteKey constHive, targetKey 174 | DeleteSubkeys constHive, targetKey 175 | 176 | If RegKeyExists(objReg, constHive, targetKey) Then 177 | WScript.Echo "Unable to delete key: " & targetKey 178 | Else 179 | WScript.Echo "Key deleted: " & targetKey 180 | End If 181 | End Function 182 | 183 | Function GetHiveConst(hive) 184 | Const HKEY_CLASSES_ROOT = &H80000000 185 | Const HKEY_CURRENT_USER = &H80000001 186 | Const HKEY_LOCAL_MACHINE = &H80000002 187 | Const HKEY_USERS = &H80000003 188 | 189 | Select Case UCase(hive) 190 | Case "HKLM" 191 | GetHiveConst = HKEY_LOCAL_MACHINE 192 | Case "HKEY_LOCAL_MACHINE" 193 | GetHiveConst = HKEY_LOCAL_MACHINE 194 | Case "HKCR" 195 | GetHiveConst = HKEY_CLASSES_ROOT 196 | Case "HKEY_CLASSES_ROOT" 197 | GetHiveConst = HKEY_CLASSES_ROOT 198 | Case "HKEY_CURRENT_USER" 199 | GetHiveConst = HKEY_CURRENT_USER 200 | Case "HKEY_USERS" 201 | GetHiveConst = HKEY_USERS 202 | End Select 203 | 204 | If IsEmpty(GetHiveConst) Then 205 | WScript.Echo "Invalid registry hive: " & hive 206 | WScript.Quit 207 | End If 208 | End Function 209 | 210 | Function Is64 211 | Dim objWMIService, colItems, objItem 212 | Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2") 213 | Set colItems = objWMIService.ExecQuery("Select SystemType from Win32_ComputerSystem") 214 | For Each objItem In colItems 215 | If InStr(LCase(objItem.SystemType), "x64") > 0 Then 216 | Is64 = True 217 | Else 218 | Is64 = False 219 | End If 220 | Next 221 | End Function 222 | 223 | 224 | 225 | 226 | ''' ---- Fix Function definition ---- ''' 227 | Function x64Fix 228 | ' This is a function which should be called before calling any vbscript run by 229 | ' the Tanium client that needs 64-bit registry or filesystem access. 230 | ' It's for when we need to catch if a machine has 64-bit windows 231 | ' and is running in a 32-bit environment. 232 | ' 233 | ' In this case, we will re-launch the sensor in 64-bit mode. 234 | ' If it's already in 64-bit mode on a 64-bit OS, it does nothing and the sensor 235 | ' continues on 236 | 237 | Const WINDOWSDIR = 0 238 | Const HKLM = &h80000002 239 | 240 | Dim objShell: Set objShell = CreateObject("WScript.Shell") 241 | Dim objFSO: Set objFSO = CreateObject("Scripting.FileSystemObject") 242 | Dim objSysEnv: Set objSysEnv = objShell.Environment("PROCESS") 243 | Dim objReg, objArgs, objExec 244 | Dim strOriginalArgs, strArg, strX64cscriptPath, strMkLink 245 | Dim strProgramFilesX86, strProgramFiles, strLaunchCommand 246 | Dim strKeyPath, strTaniumPath, strWinDir 247 | Dim b32BitInX64OS 248 | 249 | b32BitInX64OS = false 250 | 251 | ' we'll need these program files strings to check if we're in a 32-bit environment 252 | ' on a pre-vista 64-bit OS (if no sysnative alias functionality) later 253 | strProgramFiles = objSysEnv("ProgramFiles") 254 | strProgramFilesX86 = objSysEnv("ProgramFiles(x86)") 255 | ' WScript.Echo "Are the program files the same?: " & (LCase(strProgramFiles) = LCase(strProgramFilesX86)) 256 | 257 | ' The windows directory is retrieved this way: 258 | strWinDir = objFso.GetSpecialFolder(WINDOWSDIR) 259 | 'WScript.Echo "Windir: " & strWinDir 260 | 261 | ' Now we determine a cscript path for 64-bit windows that works every time 262 | ' The trick is that for x64 XP and 2003, there's no sysnative to use. 263 | ' The workaround is to do an NTFS junction point that points to the 264 | ' c:\Windows\System32 folder. Then we call 64-bit cscript from there. 265 | ' However, there is a hotfix for 2003 x64 and XP x64 which will enable 266 | ' the sysnative functionality. The customer must either have linkd.exe 267 | ' from the 2003 resource kit, or the hotfix installed. Both are freely available. 268 | ' The hotfix URL is http://support.microsoft.com/kb/942589 269 | ' The URL For the resource kit is http://www.microsoft.com/download/en/details.aspx?id=17657 270 | ' linkd.exe is the only required tool and must be in the machine's global path. 271 | 272 | If objFSO.FileExists(strWinDir & "\sysnative\cscript.exe") Then 273 | strX64cscriptPath = strWinDir & "\sysnative\cscript.exe" 274 | ' WScript.Echo "Sysnative alias works, we're 32-bit mode on 64-bit vista+ or 2003/xp with hotfix" 275 | ' This is the easy case with sysnative 276 | b32BitInX64OS = True 277 | End If 278 | If Not b32BitInX64OS And objFSO.FolderExists(strWinDir & "\SysWow64") And (LCase(strProgramFiles) = LCase(strProgramFilesX86)) Then 279 | ' This is the more difficult case to execute. We need to test if we're using 280 | ' 64-bit windows 2003 or XP but we're running in a 32-bit mode. 281 | ' Only then should we relaunch with the 64-bit cscript. 282 | 283 | ' If we don't accurately test 32-bit environment in 64-bit OS 284 | ' This code will call itself over and over forever. 285 | 286 | ' We will test for this case by checking whether %programfiles% is equal to 287 | ' %programfiles(x86)% - something that's only true in 64-bit windows while 288 | ' in a 32-bit environment 289 | 290 | ' WScript.Echo "We are in 32-bit mode on a 64-bit machine" 291 | ' linkd.exe (from 2003 resource kit) must be in the machine's path. 292 | 293 | strMkLink = "linkd " & Chr(34) & strWinDir & "\System64" & Chr(34) & " " & Chr(34) & strWinDir & "\System32" & Chr(34) 294 | strX64cscriptPath = strWinDir & "\System64\cscript.exe" 295 | ' WScript.Echo "Link Command is: " & strMkLink 296 | ' WScript.Echo "And the path to cscript is now: " & strX64cscriptPath 297 | On Error Resume Next ' the mklink command could fail if linkd is not in the path 298 | ' the safest place to put linkd.exe is in the resource kit directory 299 | ' reskit installer adds to path automatically 300 | ' or in c:\Windows if you want to distribute just that tool 301 | 302 | If Not objFSO.FileExists(strX64cscriptPath) Then 303 | ' WScript.Echo "Running mklink" 304 | ' without the wait to completion, the next line fails. 305 | objShell.Run strMkLink, 0, true 306 | End If 307 | On Error GoTo 0 ' turn error handling off 308 | If Not objFSO.FileExists(strX64cscriptPath) Then 309 | ' if that cscript doesn't exist, the link creation didn't work 310 | ' and we must quit the function now to avoid a loop situation 311 | ' WScript.Echo "Cannot find " & strX64cscriptPath & " so we must exit this function and continue on" 312 | ' clean up 313 | Set objShell = Nothing 314 | Set objFSO = Nothing 315 | Set objSysEnv = Nothing 316 | Exit Function 317 | Else 318 | ' the junction worked, it's safe to relaunch 319 | b32BitInX64OS = True 320 | End If 321 | End If 322 | If Not b32BitInX64OS Then 323 | ' clean up and leave function, we must already be in a 32-bit environment 324 | Set objShell = Nothing 325 | Set objFSO = Nothing 326 | Set objSysEnv = Nothing 327 | 328 | ' WScript.Echo "Cannot relaunch in 64-bit (perhaps already there)" 329 | ' important: If we're here because the client is broken, a sensor will 330 | ' run but potentially return incomplete or no values (old behavior) 331 | Exit Function 332 | End If 333 | 334 | ' So if we're here, we need to re-launch with 64-bit cscript. 335 | ' take the arguments to the sensor and re-pass them to itself in a 64-bit environment 336 | strOriginalArgs = "" 337 | Set objArgs = WScript.Arguments 338 | 339 | For Each strArg in objArgs 340 | strOriginalArgs = strOriginalArgs & " " & Chr(34) & strArg & Chr(34) 341 | Next 342 | ' after we're done, we have an unnecessary space in front of strOriginalArgs 343 | strOriginalArgs = LTrim(strOriginalArgs) 344 | 345 | ' If this is running as a sensor, we need to know the path of the tanium client 346 | strKeyPath = "Software\Tanium\Tanium Client" 347 | Set objReg=GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv") 348 | 349 | objReg.GetStringValue HKLM,strKeyPath,"Path", strTaniumPath 350 | 351 | ' WScript.Echo "StrOriginalArgs is:" & strOriginalArgs 352 | If objFSO.FileExists(Wscript.ScriptFullName) Then 353 | strLaunchCommand = Chr(34) & Wscript.ScriptFullName & Chr(34) & " " & strOriginalArgs 354 | ' WScript.Echo "Script full path is: " & WScript.ScriptFullName 355 | Else 356 | ' the sensor itself will not work with ScriptFullName so we do this 357 | strLaunchCommand = Chr(34) & strTaniumPath & "\VB\" & WScript.ScriptName & chr(34) & " " & strOriginalArgs 358 | End If 359 | ' WScript.Echo "launch command is: " & strLaunchCommand 360 | 361 | ' Note: There is a timeout limit here of 1 hour, as extra protection for runaway processes 362 | Set objExec = objShell.Exec(strX64cscriptPath & " //T:3600 " & strLaunchCommand) 363 | 364 | ' skipping the two lines and space after that look like 365 | ' Microsoft (R) Windows Script Host Version 366 | ' Copyright (C) Microsoft Corporation 367 | ' 368 | objExec.StdOut.SkipLine 369 | objExec.StdOut.SkipLine 370 | objExec.StdOut.SkipLine 371 | 372 | ' sensor output is all about stdout, so catch the stdout of the relaunched 373 | ' sensor 374 | Wscript.Echo objExec.StdOut.ReadAll() 375 | 376 | ' critical - If we've relaunched, we must quit the script before anything else happens 377 | WScript.Quit 378 | ' Remember to call this function only at the very top 379 | 380 | ' Cleanup 381 | Set objReg = Nothing 382 | Set objArgs = Nothing 383 | Set objExec = Nothing 384 | Set objShell = Nothing 385 | Set objFSO = Nothing 386 | Set objSysEnv = Nothing 387 | Set objReg = Nothing 388 | End Function 'x64Fix 389 | '------------ INCLUDES after this line. Do not edit past this point ----- 390 | '- Begin file: i18n/UTF8Decode.vbs 391 | '======================================== 392 | ' UTF8Decode 393 | '======================================== 394 | ' Used to convert the UTF-8 style parameters passed from 395 | ' the server to sensors in sensor parameters. 396 | ' This function should be used to safely pass non english input to sensors. 397 | '----- 398 | '----- 399 | Function UTF8Decode(str) 400 | Dim arraylist(), strLen, i, sT, val, depth, sR 401 | Dim arraysize 402 | arraysize = 0 403 | strLen = Len(str) 404 | for i = 1 to strLen 405 | sT = mid(str, i, 1) 406 | if sT = "%" then 407 | if i + 2 <= strLen then 408 | Redim Preserve arraylist(arraysize + 1) 409 | arraylist(arraysize) = cbyte("&H" & mid(str, i + 1, 2)) 410 | arraysize = arraysize + 1 411 | i = i + 2 412 | end if 413 | else 414 | Redim Preserve arraylist(arraysize + 1) 415 | arraylist(arraysize) = asc(sT) 416 | arraysize = arraysize + 1 417 | end if 418 | next 419 | depth = 0 420 | for i = 0 to arraysize - 1 421 | Dim mybyte 422 | mybyte = arraylist(i) 423 | if mybyte and &h80 then 424 | if (mybyte and &h40) = 0 then 425 | if depth = 0 then 426 | Err.Raise 5 427 | end if 428 | val = val * 2 ^ 6 + (mybyte and &h3f) 429 | depth = depth - 1 430 | if depth = 0 then 431 | sR = sR & chrw(val) 432 | val = 0 433 | end if 434 | elseif (mybyte and &h20) = 0 then 435 | if depth > 0 then Err.Raise 5 436 | val = mybyte and &h1f 437 | depth = 1 438 | elseif (mybyte and &h10) = 0 then 439 | if depth > 0 then Err.Raise 5 440 | val = mybyte and &h0f 441 | depth = 2 442 | else 443 | Err.Raise 5 444 | end if 445 | else 446 | if depth > 0 then Err.Raise 5 447 | sR = sR & chrw(mybyte) 448 | end if 449 | next 450 | if depth > 0 then Err.Raise 5 451 | UTF8Decode = sR 452 | End Function 453 | '- End file: i18n/UTF8Decode.vbs 454 | 455 | 456 | 457 | Sub DeleteSubkeys(HIVE, strKeyPath) 458 | dim arrSubkeys, strSubkey 459 | objReg.EnumKey HIVE, strKeyPath, arrSubkeys 460 | If IsArray(arrSubkeys) Then 461 | For Each strSubkey In arrSubkeys 462 | DeleteSubkeys HIVE, strKeyPath & "\" & strSubkey 463 | Next 464 | End If 465 | objReg.DeleteKey HIVE, strKeyPath 466 | End Sub 467 | 468 | Function GetOSSystemType() 469 | ' Returns the best available registry provider: 32 bit on 32 bit systems, 64 bit on 64 bit systems 470 | Dim objWMIService, colItems, objItem, iArchType, objCtx, objLocator, objServices, objRegProv 471 | Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2") 472 | Set colItems = objWMIService.ExecQuery("Select SystemType from Win32_ComputerSystem") 473 | For Each objItem In colItems 474 | If InStr(LCase(objItem.SystemType), "x64") > 0 Then 475 | iArchType = 64 476 | Else 477 | iArchType = 32 478 | End If 479 | Next 480 | GetOSSystemType = iArchType 481 | End Function ' Getx64RegistryProvider 482 | 483 | 484 | Function Getx64RegistryProvider 485 | ' Returns the best available registry provider: 32 bit on 32 bit systems, 64 bit on 64 bit systems 486 | Dim objWMIService, colItems, objItem, iArchType, objCtx, objLocator, objServices, objRegProv 487 | Set objWMIService = GetObject("winmgmts:\\.\root\CIMV2") 488 | Set colItems = objWMIService.ExecQuery("Select SystemType from Win32_ComputerSystem") 489 | For Each objItem In colItems 490 | If InStr(LCase(objItem.SystemType), "x64") > 0 Then 491 | iArchType = 64 492 | Else 493 | iArchType = 32 494 | End If 495 | Next 496 | 497 | Set objCtx = CreateObject("WbemScripting.SWbemNamedValueSet") 498 | objCtx.Add "__ProviderArchitecture", iArchType 499 | Set objLocator = CreateObject("Wbemscripting.SWbemLocator") 500 | Set objServices = objLocator.ConnectServer("","root\default","","",,,,objCtx) 501 | Set objRegProv = objServices.Get("StdRegProv") 502 | 503 | Set Getx64RegistryProvider = objRegProv 504 | End Function 505 | 506 | 507 | Function RegKeyExists(objReg, sHive, sRegKey) 508 | Dim aValueNames, aValueTypes 509 | If objReg.EnumValues(sHive, sRegKey, aValueNames, aValueTypes) = 0 Then 510 | RegKeyExists = True 511 | Else 512 | RegKeyExists = False 513 | End If 514 | End Function 515 | -------------------------------------------------------------------------------- /ReplaceDashboardText.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | .Synopsis 3 | Find and replace strings of concern in splunk dashbaords, with human review. 4 | .DESCRIPTION 5 | Lists dashbords and reports having specified pattern in search string 6 | Prompts user to select dashboard to update 7 | Shows user difference in preview and proposed new settings using Windiff 8 | Prompts user to confirm proposed changes 9 | Places accepted changes in clipboard 10 | Opens selected dashboard in browser for editing, where clipboard content and be pasted and saved. 11 | 12 | .TO DO 13 | - Add support for saved searches 14 | - Add support for case where kos of same title exist in diferring apps 15 | - Think of ways to reduce prompting 16 | - Add logging of changes made 17 | #> 18 | 19 | function get-splunk-search-results { 20 | 21 | param ($cred, $server, $port, $search) 22 | 23 | # This will allow for self-signed SSL certs to work 24 | [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true } 25 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 #(ssl3,SystemDefault,Tls,Tls11,Tls12) 26 | 27 | $url = "https://${server}:${port}/services/search/jobs/export" # braces needed b/c the colon is otherwise a scope operator 28 | $the_search = "$($search)" # Cmdlet handles urlencoding 29 | $body = @{ 30 | search = $the_search 31 | output_mode = "csv" 32 | } 33 | 34 | $SearchResults = Invoke-RestMethod -Method Post -Uri $url -Credential $cred -Body $body -TimeoutSec 300 35 | return $SearchResults 36 | } 37 | 38 | function GetMatches([string] $content, [string] $regex) { 39 | $returnMatches = new-object System.Collections.ArrayList 40 | ## Match the regular expression against the content, and 41 | ## add all trimmed matches to our return list 42 | $resultingMatches = [Regex]::Matches($content, $regex, "IgnoreCase") 43 | foreach($match in $resultingMatches) { 44 | $cleanedMatch = $match.Groups[1].Value.Trim() 45 | [void] $returnMatches.Add($cleanedMatch) 46 | } 47 | $returnMatches 48 | } 49 | 50 | # define splunk instance variables to use 51 | $server = "splunk-dev" 52 | $port = "8089" 53 | 54 | # Define the pattern to look for 55 | $Pattern = '(?i)(sourcetype\s?=\s?"?(xml)?wineventlog:[^\s]+)' 56 | 57 | # Define path to windiff tool, allowing for human review of changes: 58 | $windiff_filepath = 'C:\Program Files (x86)\Support Tools\windiff.exe' 59 | if (!(Test-Path -Path $windiff_filepath)) { 60 | write-host "Unable to verify support file in path $($windiff_filepath)." 61 | write-host 'Windiff is part of the the "Windows Server 2003 Resource Kit Tools" package which can be downloaded from https://www.microsoft.com/en-us/download/details.aspx?id=17657.' 62 | write-host 'Exiting.' 63 | exit 64 | } 65 | 66 | # Define path to preferred browser, which will later be used to open KOs for editing. 67 | $browser_filepath = 'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe' 68 | if (!(Test-Path -Path $browser_filepath)) { 69 | write-host "Unable to verify support file in path $($browser_filepath)." 70 | write-host 'Please update the browser_filepath variable in this script provide the path to your preferred browswer for administering splunk.' 71 | write-host 'Exiting.' 72 | exit 73 | } 74 | 75 | # collect credentials from user, securely, at runtime 76 | if (!($cred)) { $cred = Get-Credential -Message "enter splunk cred" -UserName "admin" } 77 | 78 | # define the splunk search which returns a noramlized set of fields for savedsearches and views matching pattern of concern 79 | $theSearch = '| rest /servicesNS/-/-/data/ui/views splunk_server=local 80 | | rename eai:appName as appName, eai:acl.owner as owner, eai:acl.sharing as sharing, eai:data as data, eai:type as type 81 | | fields type, appName, sharing, owner, title, updated, matching_values, data, id 82 | | append 83 | [| rest/servicesNS/-/-/saved/searches splunk_server=local 84 | | eval type="search" 85 | | rename eai:acl.app as appName, eai:acl.owner as owner, qualifiedSearch as data 86 | | fields type, appName, sharing, owner, title, updated, matching_values, data, id 87 | ] 88 | | regex data="(?msi)sourcetype\s?=\s?\"?(xml)?wineventlog:[^\s]+" 89 | | rex field=data "(?(?msi)sourcetype\s?=\s?\"?(xml)?wineventlog:[^\s]+)" 90 | | sort 0 appName, type, title' 91 | 92 | # perform the search and return results as object 93 | $results = get-splunk-search-results -server $server -port $port -cred $cred -search $theSearch 94 | if (!($results)) { 95 | write-host "no results found, exiting." 96 | exit 97 | } 98 | $results = ConvertFrom-Csv -InputObject $results 99 | 100 | # enumerate matching knowledge object (view or savedsearch) 101 | $records = @() 102 | foreach ($result in $results) { 103 | 104 | $Matches = GetMatches -content $result.data -regex $Pattern 105 | 106 | if ($Matches) { 107 | 108 | $data_newtext = $result.data 109 | $unique_matches = $matches | Select-Object -Unique 110 | foreach ($match in $unique_matches) { 111 | $match_newtext = $match -replace "sourcetype","source" 112 | $data_newtext = $data_newtext -replace $match,$match_newtext 113 | } 114 | 115 | 116 | $record = @{ 117 | 'appName' = $result.appName 118 | 'sharing' = $result.sharing 119 | 'userName' = $result.userName 120 | 'owner' = $result.owner 121 | 'title' = $result.title 122 | 'updated' = $result.updated 123 | 'match_count' = $Matches.count 124 | 'matches' = $Matches 125 | # 'url_edit' = $edit_url 126 | 'data' = $result.data 127 | 'new_data' = $data_newtext 128 | } 129 | 130 | $records += New-Object -TypeName PSObject -Property $Record 131 | 132 | } 133 | } 134 | 135 | 136 | $Selected = $records | Select-object title, updated, match_count, matches | Out-GridView -PassThru -Title 'Selected view to update.' 137 | if (!$Selected) { 138 | write-host "nothing selected, exiting." 139 | exit 140 | } else { 141 | foreach ($item in $selected) { 142 | $this_item_detail = $records | ?{$_.title -eq $item.title} 143 | 144 | # write orig content to a file 145 | $origfile = "$($env:temp)\$($this_item_detail.title).orig" 146 | if (Test-Path -Path $origfile) { Remove-Item -Path $origfile -Force } 147 | Add-Content -Path $origfile -Value $this_item_detail.data 148 | 149 | # write new content to a file 150 | $newfile = "$($env:temp)\$($this_item_detail.title).new" 151 | if (Test-Path -Path $newfile) { Remove-Item -Path $newfile -Force } 152 | Add-Content -Path $newfile -Value $this_item_detail.new_data 153 | 154 | # launch windiff to human review proposed change in content of two files 155 | Start-Process -filepath $windiff_filepath -argumentlist @($origfile,$newfile) -Wait 156 | 157 | # ask user if human review was acceptable and if so, do change. 158 | $Response = @("Yes - Put new content in my clipboard and open dashboard for editing in browser.","No - Lets defer the change for now") | Out-GridView -Title "Proceed with change to $($this_item_detail.title)?" -PassThru 159 | if ($Response -match "^Yes") { 160 | 161 | # build url which will enable open of dashboard in edit mode 162 | $edit_url = "http://$($server):8000/en-US/app/$($this_item_detail.appName)/$($this_item_detail.title)/editxml?" 163 | 164 | # writing new item data to clipboard 165 | $this_item_detail.new_data | clip 166 | 167 | # start new browser tab where admin can paste and save updated content 168 | Start-Process -filepath $browser_filepath -argumentlist @($edit_url) -Wait 169 | } 170 | 171 | 172 | } 173 | 174 | 175 | } 176 | -------------------------------------------------------------------------------- /SigmaToSplunk./main.py: -------------------------------------------------------------------------------- 1 | 2 | from sigma.rule import SigmaRule 3 | from sigma.pipelines.sysmon import sysmon_pipeline 4 | from sigma.backends.splunk import SplunkBackend 5 | from sigma.collection import SigmaCollection 6 | import sigma.exceptions as sigma_exceptions 7 | from datetime import date 8 | from pathlib import Path 9 | import xml.etree.cElementTree as ET 10 | import logging 11 | 12 | """ 13 | Note: 14 | forked from https://github.com/askkemp/Sigma-to-Massive-Slunk-Dashboard 15 | 16 | setup: 17 | create project folder 18 | git clone https://github.com/SigmaHQ/sigma.git into project folder 19 | git clone https://github.com/SigmaHQ/pySigma-backend-splunk.git & copy .\sigma subfolder into .\project\sigma folder 20 | git clone https://github.com/SigmaHQ/pySigma-pipeline-sysmon.git & copy .\sigma subfolder into .\project\sigma folder 21 | """ 22 | 23 | if __name__ == '__main__': 24 | 25 | # CONFIGURATION SETTINGS 26 | sigma_rule_folder = 'C:/Users/david/PycharmProjects/SigmaToSplunk/sigma/rules/windows' 27 | supported_rule_status = ['stable'] # 'experimental','test','stable','expired' 28 | supported_rule_status = ['experimental','test','stable','expired'] 29 | prepend_splunk_search = "(index=* AND (sourcetype=XmlWinEventLog OR sourcetype=WinEventLog))" 30 | 31 | # Sigma setup (import backends and pipelines) 32 | pipeline = sysmon_pipeline() 33 | backend = SplunkBackend(pipeline) 34 | 35 | # Identify all rule files to possibly process 36 | if Path(sigma_rule_folder).is_dir(): # True if folder 37 | sigma_files_gen = Path(sigma_rule_folder).glob('**/*.yml') 38 | files_on_disk = [x for x in sigma_files_gen if x.is_file()] 39 | print('Loaded {} yaml files from {}'.format(len(files_on_disk), sigma_rule_folder)) 40 | else: 41 | raise FileNotFoundError( 42 | print('No folder exists at {}'.format(sigma_rule_folder)) 43 | ) 44 | 45 | # set counter to track applicable rules 46 | matching_rule_count = 0 47 | 48 | # Iterate through rule files 49 | for sigma_file in files_on_disk: 50 | 51 | # open the rule file reading 52 | with sigma_file.open(mode='r') as f: 53 | 54 | try: 55 | sigma_obj = SigmaCollection.from_yaml(f) 56 | except: 57 | continue 58 | 59 | # handle possibility of more than one rule in object 60 | for rule in sigma_obj.rules: 61 | 62 | # only process rules with desired status 63 | if str(rule.status) in supported_rule_status: 64 | 65 | matching_rule_count += 1 66 | 67 | # some rules do not convert reliably so try this first 68 | try: 69 | converted_query = backend.convert(sigma_obj)[0] # should only be one 70 | except sigma_exceptions.SigmaConditionError as e: 71 | continue 72 | except sigma_exceptions.SigmaFeatureNotSupportedByBackendError as e: 73 | continue 74 | 75 | print('\nsigma_file: {}'.format(sigma_file)) 76 | print('rule_title: {}'.format(rule.title)) 77 | print('rule_level: {}'.format(rule.level)) 78 | print('rule_status: {}'.format(rule.status)) 79 | print('rule_description: {}'.format(rule.description)) 80 | 81 | for tag in rule.tags: 82 | print('rule_tag: {}'.format(tag)) 83 | 84 | for ref in rule.references: 85 | print('rule_ref: {}'.format(ref)) 86 | 87 | for fp in rule.falsepositives: 88 | print('rule_fp: {}'.format(fp)) 89 | 90 | 91 | converted_query = prepend_splunk_search + ' ' + converted_query 92 | print('rule_query: {}'.format(converted_query)) 93 | 94 | print('\nmatching rule count: {}'.format(matching_rule_count)) 95 | -------------------------------------------------------------------------------- /Splunk_log_cfg.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | Const ForReading = 1 4 | Const ForWriting = 2 5 | Const ForAppending = 8 6 | 7 | 8 | Dim objFSO, objFile 9 | Set objFSO = CreateObject("Scripting.FileSystemObject") 10 | 11 | Dim strLogConfigFile 12 | strLogConfigFile = "C:\Program Files\SplunkUniversalForwarder\etc\log.cfg" 13 | 14 | ' make sure the log configuration file exists before proceeding 15 | if CheckFileExists(strLogConfigFile)=False then 16 | wscript.echo "File (" & strLogConfigFile & ") does not exist. Exiting." 17 | wscript.quit 18 | End if 19 | 20 | ' if we have do not have a backup of the file, create one 21 | dim strLogConfigFileBak 22 | strLogConfigFileBak = strLogConfigFile & ".bak" 23 | if CheckFileExists(strLogConfigFileBak)=False then 24 | objFso.CopyFile strLogConfigFile,strLogConfigFileBak 25 | End if 26 | 27 | ' load dictionary of specs to change 28 | Dim objDictionary 29 | Set objDictionary = CreateObject("Scripting.Dictionary") 30 | objDictionary.CompareMode = vbTextCompare 31 | objDictionary.Add "category.DC:UpdateServerclassHandler","WARN" 32 | objDictionary.Add "category.DeployedServerclass","WARN" 33 | objDictionary.Add "category.MetricStoreCatalogBaseHandler=INFO","WARN" 34 | objDictionary.Add "category.MetricsHandler=INFO","WARN" 35 | objDictionary.Add "category.MetricSchemaProcessor=INFO","WARN" 36 | objDictionary.Add "category.MetricsRollupPolicyHandler=INFO","WARN" 37 | objDictionary.Add "category.TailingProcessor=INFO","WARN" 38 | objDictionary.Add "category.WatchedFile=INFO","WARN" 39 | objDictionary.Add "category.ChunkedLBProcessor=INFO","WARN" 40 | objDictionary.Add "category.MetricsProcessor=INFO","WARN" 41 | objDictionary.Add "category.TailReader=INFO","WARN" 42 | objDictionary.Add "category.CertStorageProvider=INFO","WARN" 43 | objDictionary.Add "category.Rsa2FA=INFO","WARN" 44 | objDictionary.Add "category.IndexerInit=INFO","WARN" 45 | objDictionary.Add "category.DC:DeploymentClient=INFO","WARN" 46 | objDictionary.Add "category.DC:PhonehomeThread=INFO","WARN" 47 | objDictionary.Add "category.DS_DC_Common=INFO","WARN" 48 | objDictionary.Add "category.IntrospectionGenerator:disk_objects=INFO","WARN" 49 | objDictionary.Add "category.ModularInputs=INFO","WARN" 50 | objDictionary.Add "category.PipelineComponent=INFO","WARN" 51 | objDictionary.Add "category.PrometheusMetricsHandler=INFO","WARN" 52 | objDictionary.Add "category.ProxyConfig=INFO","WARN" 53 | objDictionary.Add "category.ShutdownHandler=INFO","WARN" 54 | objDictionary.Add "category.SpecFiles=INFO","WARN" 55 | objDictionary.Add "category.TcpOutputProc=INFO","WARN" 56 | objDictionary.Add "category.TcpInputProc=INFO","WARN" 57 | objDictionary.Add "category.ScheduledViewsReaper=INFO","WARN" 58 | objDictionary.Add "category.BundlesSetup=INFO","WARN" 59 | objDictionary.Add "category.CascadingReplicationManager=INFO","WARN" 60 | objDictionary.Add "category.WorkloadManager=INFO","WARN" 61 | objDictionary.Add "category.ExecProcessor=INFO","WARN" 62 | objDictionary.Add "category.ExecProcessor:Introspect=INFO","WARN" 63 | objDictionary.Add "category.LicenseMgr=INFO","WARN" 64 | objDictionary.Add "category.LMConfig=INFO","WARN" 65 | objDictionary.Add "category.LMSlaveInfo=INFO","WARN" 66 | objDictionary.Add "category.LMStack=INFO","WARN" 67 | objDictionary.Add "category.LMStackMgr=INFO","WARN" 68 | objDictionary.Add "category.LMTracker=INFO","WARN" 69 | objDictionary.Add "category.LMTrackerDb=INFO","WARN" 70 | objDictionary.Add "category.ApplicationLicense=INFO","WARN" 71 | objDictionary.Add "category.ApplicationLicenseHandler=INFO","WARN" 72 | objDictionary.Add "category.ApplicationLicenseTracker=INFO","WARN" 73 | objDictionary.Add "category.ClusteringMgr=INFO","WARN" 74 | objDictionary.Add "category.PipeFlusher=INFO","WARN" 75 | objDictionary.Add "category.SHClusterMgr=INFO","WARN" 76 | objDictionary.Add "category.UiHttpListener=INFO","WARN" 77 | objDictionary.Add "category.FileAndDirectoryEliminator=INFO","WARN" 78 | objDictionary.Add "category.Metrics=INFO,metrics","WARN" 79 | objDictionary.Add "category.StatusMgr=INFO,metrics","WARN" 80 | objDictionary.Add "category.PeriodicHealthReporter=INFO,healthreporter","WARN" 81 | objDictionary.Add "category.Watchdog=INFO,watchdog_appender","WARN" 82 | objDictionary.Add "category.WatchdogActions=INFO,watchdog_appender","WARN" 83 | objDictionary.Add "category.Pstacks=INFO,watchdog_appender","WARN" 84 | objDictionary.Add "category.PstackServerThread=INFO,watchdog_appender","WARN" 85 | objDictionary.Add "category.PstackGeneratorThread=INFO,watchdog_appender","WARN" 86 | objDictionary.Add "category.WatchdogStacksUtils=INFO,watchdog_appender","WARN" 87 | objDictionary.Add "category.WatchdogInit=INFO,watchdog_appender","WARN" 88 | 89 | ' read the file into array 90 | Set objFile = objFSO.OpenTextFile(strLogConfigFile, ForReading) 91 | Dim arrFileLines() 92 | Dim i 93 | i = 0 94 | Do Until objFile.AtEndOfStream 95 | Redim Preserve arrFileLines(i) 96 | arrFileLines(i) = objFile.ReadLine 97 | i = i + 1 98 | Loop 99 | objFile.Close 100 | 101 | ' step through array (lines of file) 102 | Dim strLine 103 | Dim objItem 104 | Dim blnReplacementNeeded :: blnReplacementNeeded = False 105 | 106 | Dim arrNewFile() 107 | Dim j :: j = 0 108 | 109 | 110 | Dim blnSplunkdSection :: blnSplunkdSection = False 111 | For i = 0 to Ubound(arrFileLines) 112 | strLine = arrFileLines(i) 113 | 114 | ' determine if we are in the splunkd section or not 115 | if instr(1,ucase(strLine),"[")=1 then 116 | if instr(1,ucase(strLine),"[SPLUNKD]")<>0 then 117 | blnSplunkdSection = True 118 | else 119 | blnSplunkdSection = False 120 | end if 121 | end if 122 | 123 | ' only evaluate lines in splunkd section, which are not commented, and which have "=INFO" substring 124 | if blnsplunkdSection = True AND left(strLine,1)<>"#" AND instr(1,strLine,"=INFO")<>0 then 125 | 126 | ' Check to to see of log component (left side of = in line) matches an item (key) in list (dictionary) 127 | For Each objItem in objDictionary.keys 128 | if instr(1,ucase(strLine),ucase(objItem))=1 then 129 | blnReplacementNeeded = true 130 | strLine = replace(strLine,"=INFO","=" & objDictionary(objItem)) 131 | exit for 132 | end if 133 | Next 134 | end if 135 | 136 | Redim Preserve arrNewFile(j) 137 | arrNewFile(j) = strLine 138 | j = j + 1 139 | 140 | Next 141 | 142 | ' if there was a need to replace text, write new file array to file 143 | if blnReplacementNeeded = true then 144 | 145 | ' delete the file if exist 146 | if CheckFileExists(strLogConfigFile) then 147 | objFSO.deletefile strLogConfigFile,true 148 | end if 149 | 150 | ' write lines to replacement file 151 | dim objTextFile 152 | Set objTextFile = objFSO.OpenTextFile(strLogConfigFile,ForAppending, True) 153 | 154 | For j=0 to ubound(arrNewFile) 155 | objTextFile.writeline arrNewFile(j) 156 | Next 157 | 158 | ' release handle to file` 159 | objTextFile.Close 160 | 161 | end if 162 | 163 | 164 | Function CheckFileExists(strFile) 165 | Dim objFSO 166 | Set objFSO = CreateObject("Scripting.FileSystemObject") 167 | CheckFileExists = objFSO.FileExists(strFile) 168 | End Function 169 | 170 | -------------------------------------------------------------------------------- /SysmonDnsQueryAnalysis.xml: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | host="$host_filter$" source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=22 "$process_filter$" 6 | | fields _time host Description RuleName, UtcTime, ProcessGuid, ProcessId, QueryName, QueryStatus, QueryResults, Image 7 | | eval process_exec = replace(Image,"(.*\\\)(?=.*(\.\w*)$$|(\w+)$$)","") 8 | | eval process_exec=lower(process_exec) 9 | | search process_exec="$process_filter$" 10 | | rex field=QueryResults "type:\s+(?<QueryType>\d+)" 11 | | eval QueryType = case(QueryType=="1","A", 12 | QueryType=="2","NS", 13 | QueryType=="5","CNAME", 14 | QueryType=="6","SOA", 15 | QueryType=="12","PTR", 16 | QueryType=="15","MX", 17 | QueryType=="16","TXT", 18 | QueryType=="17","RP", 19 | QueryType=="18","AFSDB", 20 | QueryType=="24","SIG", 21 | QueryType=="25","KEY", 22 | QueryType=="28","AAAA", 23 | QueryType=="29","LOC", 24 | QueryType=="33","SRV", 25 | QueryType=="35","NAPTR", 26 | QueryType=="36","KX", 27 | QueryType=="37","CERT", 28 | QueryType=="39","DNAME", 29 | QueryType=="42","APL", 30 | QueryType=="43","DS", 31 | QueryType=="44","SSHFP", 32 | QueryType=="45","IPSECKEY", 33 | QueryType=="46","RRSIG", 34 | QueryType=="47","NSEC", 35 | QueryType=="48","DNSKEY", 36 | QueryType=="49","DHCID", 37 | QueryType=="50","NSEC3", 38 | QueryType=="51","NSEC3PARAM", 39 | QueryType=="52","TLSA", 40 | QueryType=="53","SMIMEA", 41 | QueryType=="55","HIP", 42 | QueryType=="59","CDS", 43 | QueryType=="60","CDNSKEY", 44 | QueryType=="61","OPENPGPKEY", 45 | QueryType=="62","CSYNC", 46 | QueryType=="249","TKEY", 47 | QueryType=="250","TSIG", 48 | QueryType=="256","URI", 49 | QueryType=="257","CAA", 50 | QueryType=="32768","TA", 51 | QueryType=="32769","DLV" 52 | ,1==1,"Unknown") 53 | | eval QueryStatus=case(QueryStatus=="0","The operation completed successfully", 54 | QueryStatus=="9001","DNS server unable to interpret format", 55 | QueryStatus=="9002","DNS server failure", 56 | QueryStatus=="9003","DNS name does not exist", 57 | QueryStatus=="9004","DNS request not supported by name server", 58 | QueryStatus=="9005","DNS operation refused", 59 | QueryStatus=="9006","DNS name that ought not exist, does exist", 60 | QueryStatus=="9007","DNS RR set that ought not exist, does exist", 61 | QueryStatus=="9008","DNS RR set that ought to exist, does not exist", 62 | QueryStatus=="9009","DNS server not authoritative for zone", 63 | QueryStatus=="9010","DNS name in update or prereq is not in zone", 64 | QueryStatus=="9016","DNS signature failed to verify", 65 | QueryStatus=="9017","DNS bad key", 66 | QueryStatus=="9018","DNS signature validity expired", 67 | QueryStatus=="9504","DNS error, check rcode", 68 | QueryStatus=="9506","DNS query request is pending", 69 | QueryStatus=="9556","DNS name does not comply with RFC specifications", 70 | QueryStatus=="9557","DNS name is a fully-qualified DNS name", 71 | QueryStatus=="9558","DNS name is dotted (multi-label)", 72 | QueryStatus=="9559","DNS name is a single-part name", 73 | QueryStatus=="9560","DNS name contains an invalid character", 74 | QueryStatus=="9561","DNS name is entirely numeric", 75 | QueryStatus=="9601","DNS zone does not exist", 76 | QueryStatus=="9602","DNS zone information not available", 77 | QueryStatus=="9605","DNS zone has no start of authority (SOA) record", 78 | QueryStatus=="9606","DNS zone has no Name Server (NS) record", 79 | QueryStatus=="9607","DNS zone is locked", 80 | QueryStatus=="9608","DNS zone creation failed", 81 | QueryStatus=="9609","DNS zone already exists", 82 | QueryStatus=="9610","DNS automatic zone already exists", 83 | QueryStatus=="9613","DNS zone not secondary", 84 | QueryStatus=="9701","DNS record does not exist", 85 | QueryStatus=="9702","DNS record format error", 86 | QueryStatus=="9705","DNS record timed out", 87 | QueryStatus=="9711","DNS record already exists", 88 | QueryStatus=="9714","DNS name does not exist", 89 | QueryStatus=="9716","DNS domain was undeleted", 90 | QueryStatus=="9718","DNS zone already exists in the directory service", 91 | QueryStatus=="9719","DNS server not creating or reading the boot file for the directory service integrated DNS zone", 92 | QueryStatus=="9751","DNS AXFR (zone transfer) complete", 93 | QueryStatus=="9752","DNS zone transfer failed", 94 | 1==1,QueryStatus) 95 | | fields * 96 | 97 | $time_tok.earliest$ 98 | $time_tok.latest$ 99 | 100 |
101 | 102 | 103 | 104 | -24h@h 105 | now 106 | 107 | 108 | 109 | 110 | DESKTOP-0UH1C9P 111 | 112 | 113 | 114 | * 115 | 116 |
117 | 118 | 119 | 120 | Count of DNS Queries by Process 121 | 122 | | chart count by process_exec 123 | $time_tok.earliest$ 124 | $time_tok.latest$ 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | Count of DNS Queries by Status 133 | 134 | | chart count by QueryStatus 135 | $time_tok.earliest$ 136 | $time_tok.latest$ 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | Count of DNS Queries by QueryType 145 | 146 | | chart count by QueryType 147 | $time_tok.earliest$ 148 | $time_tok.latest$ 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | Count of DNS Queries by QueryResults 157 | 158 | | chart count by QueryResults 159 | $time_tok.earliest$ 160 | $time_tok.latest$ 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | Count of DNS Queries Over Time by Process 171 | 172 | | timechart count by process_exec 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | Count of Network Conversation Partners Over Time by Process, IP Type, and Protocol 186 | 187 | host="DESKTOP-0UH1C9P" source="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=3 EventID=3 "\<EventID\>3\<\/EventID\>" "$process_filter$" 188 | | eval process_exec = replace(Image,"(.*\\\)(?=.*(\.\w*)$|(\w+)$)","") 189 | | eval process_exec=lower(process_exec) 190 | | search process_exec="$process_filter$" 191 | | fields _time host Image, User, Protocol, Initiated, SourceIsIpv6, SourceIp, SourceHostname, SourcePort, SourcePortName, DestinationIsIpv6, DestinationIp, DestinationHostname, DestinationPort, DestinationPortName, process_exec 192 | | eval conversationPartnerIp = case(Initiated=="false",SourceIp,Initiated=="true",DestinationIp,1==1,"unknown") 193 | | eval conversationPartnerProtocol = Protocol 194 | | eval conversationPartnerIpType = case(Initiated=="false",SourceIsIpv6,Initiated=="true",DestinationIsIpv6,1==1,"unknown") 195 | | eval conversationPartnerIpType = if(conversationPartnerIpType=="true","ipv6","ipv4") 196 | | eval conversationPartnerPort = case(Initiated=="false",SourcePort,Initiated=="true",DestinationPort,1==1,"unknown") 197 | | eval conversationPartnerInfo = process_exec . ":" . conversationPartnerIpType . ":" . conversationPartnerProtocol 198 | | timechart dc(conversationPartnerIp) as conversationPartners by conversationPartnerInfo 199 | $time_tok.earliest$ 200 | $time_tok.latest$ 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | Record Details 214 | 215 | | table _time host QueryName, QueryStatus, QueryResults, Image, process_exec, QueryType 216 | | sort 0 _time 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 |
227 |
228 |
229 |
-------------------------------------------------------------------------------- /SysmonLoadTest.ps1: -------------------------------------------------------------------------------- 1 | 2 | <# 3 | $DebugPreference = "Continue" # Debug Mode 4 | $DebugPreference = "SilentlyContinue" # Normal Mode 5 | #> 6 | 7 | $TestDurationTotalSeconds = 120 8 | $SysinternalsSuitePath = "C:\Users\Administrator\Downloads\SysinternalsSuite" 9 | $LogFile = "C:\ProgramData\SysmonBetaTest\activity.log" 10 | $ConstrainSysmonCollection = $True 11 | 12 | # creates Sysmon config with all inputs except specified type 13 | function make-sysmon-config ($sysmonPath, $name) 14 | { 15 | 16 | Write-Debug "creating sysmon config" 17 | 18 | # Get sysmon schema into xml 19 | $sysmonSchemaPrint = & $sysmonPath -s 2> $null | Select-String -Pattern "<" 20 | $sysmonSchemaPrintXml = [xml]$sysmonSchemaPrint 21 | 22 | # spit out a new template file 23 | $events = $sysmonSchemaPrintXml.manifest.events.event | Where-Object {$_.name -notmatch "(SYSMON_ERROR|SYSMON_SERVICE_STATE_CHANGE|SYSMON_SERVICE_CONFIGURATION_CHANGE)"} 24 | 25 | $xmlConfig = @() 26 | $xmlConfig += "" 27 | if ($sysmonSchemaPrintXml.manifest.configuration.options.option.name -match "HashAlgorithms") { $xmlConfig += "`t*" } 28 | if ($sysmonSchemaPrintXml.manifest.configuration.options.option.name -match "CheckRevocation") { $xmlConfig += "`t" } 29 | # if ($sysmonSchemaPrintXml.manifest.configuration.options.option.name -match "DnsLookup") { $xmlConfig += "`tFalse" } 30 | # if ($sysmonSchemaPrintXml.manifest.configuration.options.option.name -match "ArchiveDirectory") { $xmlConfig += "`t" } 31 | $xmlConfig += "`t" 32 | 33 | 34 | foreach ($event in $events) { 35 | 36 | $printConfig = $true 37 | # print the section hearder listing ID (value), Description (template), and config file section id (rulename) 38 | $xmlConfig += "" 39 | $xmlConfig += "`t`t" 40 | 41 | # print the section hearder data elements of event 42 | $items = "" 43 | foreach ($item in $event.data | Select Name) { 44 | if ($items -eq "") { 45 | $items = "$($item.name)" 46 | } else { 47 | $items += ", $($item.name)" 48 | } 49 | } 50 | $xmlConfig += "`t`t" 51 | 52 | # 53 | if ($event.value -match "12|13|17|19|20") { $printConfig = $false} 54 | 55 | if ($name -match "SYSMON_REG_KEY|SYSMON_REG_SETVALUE" -and $event) { 56 | $name = "SYSMON_REG_NAME" 57 | } 58 | 59 | if ($name -match "SYSMON_CREATE_NAMEDPIPE|SYSMON_CONNECT_NAMEDPIPE" -and $event) { 60 | $name = "SYSMON_CONNECT_NAMEDPIPE" 61 | } 62 | 63 | if ($name -match "SYSMON_WMI_FILTER|SYSMON_WMI_CONSUMER|SYSMON_WMI_BINDING" -and $event) { 64 | $name = "SYSMON_WMI_BINDING" 65 | } 66 | 67 | $matchtype = "include" 68 | if ($event.name -ieq $name) { 69 | Write-Debug "setting $($event.name) match level to exclude" 70 | $matchtype = "exclude" 71 | } 72 | 73 | 74 | if ($printConfig -eq $true) { 75 | $xmlConfig += "" 76 | $xmlConfig += "`t`t" 77 | $xmlConfig += "`t`t`t<$($event.rulename) onmatch=`"$($matchtype)`">" 78 | $xmlConfig += "`t`t`t" 79 | $xmlConfig += "`t`t" 80 | } 81 | } 82 | $xmlConfig += "" 83 | $xmlConfig += "`t" 84 | $xmlConfig += "" 85 | $xmlConfig += "" 86 | 87 | $ConfigFile = "$($env:TEMP)\$($name).xml" 88 | if (Test-Path -Path $ConfigFile) { Remove-Item -Path $ConfigFile -Force } 89 | write-debug "writing config to file: $($configfile)" 90 | Set-Content -Path $ConfigFile -Value $xmlConfig 91 | 92 | return $ConfigFile 93 | 94 | } 95 | 96 | # merges new config, starts-sysmon 97 | function reset-sysmon ($sysmonPath, $configpath) 98 | { 99 | Write-Debug "configuring sysmon" 100 | Start-Process -FilePath $sysmonPath -ArgumentList @("-c --") -NoNewWindow 101 | Start-Sleep -Seconds 1 102 | Start-Process -FilePath $sysmonPath -ArgumentList @("-c",$configpath) -NoNewWindow 103 | Start-Sleep -Seconds 1 104 | } 105 | 106 | function format-splunktime { 107 | param ( 108 | [parameter(Mandatory=$false)][datetime]$inputDate=(Get-Date) 109 | ) 110 | 111 | $inputDateString = $inputDate.ToString('MM-dd-yyyy HH:mm:ss.fff zzzz') 112 | $inputDateParts = $inputDateString -split " " 113 | $inputDateZone = $inputDateParts[2] -replace ":","" 114 | $outputDateString = "$($inputDateParts[0]) $($inputDateParts[1]) $($inputDateZone)" 115 | return $outputDateString 116 | } 117 | 118 | # gets event logs of specified type 119 | function get-eventlog ($logname) 120 | { 121 | $events = Get-WInEvent -log $logname 122 | # Parse out the event message data 123 | ForEach ($Event in $Events) { 124 | # Convert the event to XML 125 | $eventXML = [xml]$Event.ToXml() 126 | # Iterate through each one of the XML message properties 127 | For ($i=0; $i -lt $eventXML.Event.EventData.Data.Count; $i++) { 128 | # Append these as object properties 129 | Add-Member -InputObject $Event -MemberType NoteProperty -Force -Name $eventXML.Event.EventData.Data[$i].name -Value $eventXML.Event.EventData.Data[$i].'#text' 130 | } 131 | } 132 | return $Events 133 | } 134 | 135 | # Establish path to Sysmon 136 | $sysmonPath = "$($env:windir)\sysmon.exe" 137 | if (!(Test-Path -Path $sysmonPath)) { 138 | write-host "Sysmon.exe not present in $($sysmonPath). Exiting." 139 | exit 140 | } 141 | 142 | 143 | # Ensure PowerSploit is present 144 | $ModulePath = "C:\Windows\system32\WindowsPowerShell\v1.0\Modules\PowerSploit" 145 | 146 | <# 147 | 148 | # Make sure .NET 2.0 is present 149 | if ((get-WindowsOptionalFeature -FeatureName "NetFX3" -Online).State -ne "Enabled") { 150 | Enable-WindowsOptionalFeature -Online -FeatureName "NetFX3" -All 151 | } 152 | 153 | #Disable AV 154 | if ((Get-MpPreference).DisableRealtimeMonitoring -eq $false) { 155 | Read-Host -Prompt "Please disable A/V and press ENTER to continue.." 156 | } 157 | 158 | $url = "https://github.com/PowerShellMafia/PowerSploit/archive/master.zip" 159 | $download = "$($env:temp)\master.zip" 160 | if (Test-Path -Path $download) { Remove-Item -Path $download -Force -Recurse } 161 | write-host "-downloading latest project from $($url)." 162 | $Response = Invoke-WebRequest -Uri $url -OutFile $download 163 | 164 | # extract the compressed content (if local copy older than 20 hours) 165 | write-host "-extracting archive." 166 | $extracted = "$($env:temp)\extracted" 167 | if (Test-Path -Path $extracted) { Remove-Item -Path $extracted -Force -Recurse } 168 | Expand-Archive -LiteralPath $download -DestinationPath $extracted -Force 169 | 170 | # copy the module 171 | Rename-Item -Path "$($extracted)\PowerSploit-master" -NewName "PowerSploit" 172 | Copy-Item -Path "$($extracted)\PowerSploit" -Destination $ModulePath -Recurse -Force 173 | 174 | #Disable AV 175 | if ((Get-MpPreference).DisableRealtimeMonitoring -eq $false) { 176 | Read-Host -Prompt "Please disable A/V and press ENTER to continue.." 177 | } 178 | 179 | #Set PowerShell ExecutionPolicy is top allow execution of PowerSploit 180 | Set-ExecutionPolicy -ExecutionPolicy Unrestricted -Scope CurrentUser -Force 181 | 182 | #> 183 | 184 | # remove any marks of the web/streams 185 | Get-ChildItem -path $ModulePath -Recurse | Unblock-File 186 | 187 | $x = 0 188 | do 189 | { 190 | ################################################################################ 191 | # SYSMON_CREATE_PROCESS: EventCode=1 RuleName=ProcessCreate 192 | ################################################################################ 193 | $TestName = "SYSMON_CREATE_PROCESS" 194 | 195 | if ($ConstrainSysmonCollection -eq $True) { 196 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 197 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 198 | } 199 | 200 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 201 | write-host $Message ; add-content -Path $LogFile -Value $Message 202 | 203 | $TestStart = (get-date) ; $TestCount = 0 204 | do 205 | { 206 | $TestCount++ 207 | ########################################################################### 208 | # Payload: 209 | $Process = start-process -FilePath "notepad.exe" -WindowStyle Hidden -PassThru 210 | Stop-Process -Id $Process.Id -ErrorAction stop 211 | ########################################################################### 212 | 213 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 214 | 215 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 216 | write-host $Message ; add-content -Path $LogFile -Value $Message 217 | Start-Sleep -Seconds 2 218 | 219 | 220 | ################################################################################ 221 | # SYSMON_FILE_TIME: EventCode=2 RuleName=FileCreateTime 222 | ################################################################################ 223 | $TestName = "SYSMON_FILE_TIME" 224 | 225 | if ($ConstrainSysmonCollection -eq $True) { 226 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 227 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 228 | } 229 | 230 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 231 | write-host $Message ; add-content -Path $LogFile -Value $Message 232 | 233 | $TestStart = (get-date) ; $TestCount = 0 234 | do 235 | { 236 | $TestCount++ 237 | ########################################################################### 238 | # Payload: 239 | $TemporaryFile = New-TemporaryFile 240 | (Get-Item -path $TemporaryFile.FullName).CreationTime=("08 March 2016 18:00:00") 241 | Remove-Item -Path $TemporaryFile.FullName 242 | ########################################################################### 243 | 244 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 245 | 246 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 247 | write-host $Message ; add-content -Path $LogFile -Value $Message 248 | Start-Sleep -Seconds 2 249 | 250 | 251 | ################################################################################ 252 | # SYSMON_NETWORK_CONNECT: EventCode=3 RuleName=NetworkConnect 253 | ################################################################################ 254 | $TestName = "SYSMON_NETWORK_CONNECT" 255 | 256 | if ($ConstrainSysmonCollection -eq $True) { 257 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 258 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 259 | } 260 | 261 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 262 | write-host $Message ; add-content -Path $LogFile -Value $Message 263 | 264 | $TestStart = (get-date) ; $TestCount = 0 265 | do 266 | { 267 | $TestCount++ 268 | ########################################################################### 269 | # Payload: 270 | $blah = Invoke-WebRequest -Uri "192.168.1.1" -DisableKeepAlive -UseBasicParsing 271 | ########################################################################### 272 | 273 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 274 | 275 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 276 | write-host $Message ; add-content -Path $LogFile -Value $Message 277 | Start-Sleep -Seconds 2 278 | 279 | 280 | ################################################################################ 281 | # SYSMON_PROCESS_TERMINATE: EventCode=5 RuleName=ProcessTerminate 282 | ################################################################################ 283 | $TestName = "SYSMON_PROCESS_TERMINATE" 284 | 285 | if ($ConstrainSysmonCollection -eq $True) { 286 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 287 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 288 | } 289 | 290 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 291 | write-host $Message ; add-content -Path $LogFile -Value $Message 292 | 293 | $TestStart = (get-date) ; $TestCount = 0 294 | do 295 | { 296 | $TestCount++ 297 | ########################################################################### 298 | # Payload: 299 | $Process = start-process -FilePath "notepad.exe" -WindowStyle Hidden -PassThru 300 | Stop-Process -Id $Process.Id -ErrorAction stop 301 | ########################################################################### 302 | 303 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 304 | 305 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 306 | write-host $Message ; add-content -Path $LogFile -Value $Message 307 | Start-Sleep -Seconds 2 308 | 309 | 310 | ################################################################################ 311 | # SYSMON_DRIVER_LOAD: EventCode=6 RuleName=DriverLoad 312 | ################################################################################ 313 | $TestName = "SYSMON_DRIVER_LOAD" 314 | 315 | if ($ConstrainSysmonCollection -eq $True) { 316 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 317 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 318 | } 319 | 320 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 321 | write-host $Message ; add-content -Path $LogFile -Value $Message 322 | 323 | $TestStart = (get-date) ; $TestCount = 0 324 | do 325 | { 326 | $TestCount++ 327 | ########################################################################### 328 | # Payload: 329 | $ProcessPath = "$($SysinternalsSuitePath)\notmyfault64.exe" 330 | $Process = start-process -FilePath $ProcessPath -ArgumentList @("/AcceptEula") -WindowStyle Hidden -PassThru 331 | Start-Sleep -Seconds 1 332 | Stop-Process -Id $Process.Id -ErrorAction stop -Force 333 | Get-Service myfault | Stop-Service 334 | ########################################################################### 335 | 336 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 337 | 338 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 339 | write-host $Message ; add-content -Path $LogFile -Value $Message 340 | Start-Sleep -Seconds 2 341 | 342 | 343 | ################################################################################ 344 | # SYSMON_IMAGE_LOAD: EventCode=7 RuleName=ImageLoad 345 | ################################################################################ 346 | $TestName = "SYSMON_IMAGE_LOAD" 347 | 348 | if ($ConstrainSysmonCollection -eq $True) { 349 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 350 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 351 | } 352 | 353 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 354 | write-host $Message ; add-content -Path $LogFile -Value $Message 355 | 356 | $TestStart = (get-date) ; $TestCount = 0 357 | do 358 | { 359 | $TestCount++ 360 | ########################################################################### 361 | # Payload: 362 | $ProcessPath = "c:\windows\notepad.exe" 363 | $Process = start-process -FilePath $ProcessPath -WindowStyle Hidden -PassThru 364 | Stop-Process -Id $Process.Id -ErrorAction stop 365 | ########################################################################### 366 | 367 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 368 | 369 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 370 | write-host $Message ; add-content -Path $LogFile -Value $Message 371 | Start-Sleep -Seconds 2 372 | 373 | 374 | ################################################################################ 375 | # SYSMON_CREATE_REMOTE_THREAD: EventCode=8 RuleName=CreateRemoteThread 376 | ################################################################################ 377 | # The CreateRemoteThread event detects when a process creates a thread in 378 | # another process. This technique is used by malware to inject code and hide 379 | # in other processes. The event indicates the source and target process. It 380 | # gives information on the code that will be run in the new thread: 381 | # StartAddress, StartModule and StartFunction. Note that StartModule and 382 | # StartFunction fields are inferred, they might be empty if the starting 383 | # address is outside loaded modules or known exported functions. 384 | ################################################################################ 385 | $TestName = "SYSMON_CREATE_REMOTE_THREAD" 386 | 387 | if ($ConstrainSysmonCollection -eq $True) { 388 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 389 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 390 | } 391 | 392 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 393 | write-host $Message ; add-content -Path $LogFile -Value $Message 394 | 395 | $TestStart = (get-date) ; $TestCount = 0 396 | do 397 | { 398 | $TestCount++ 399 | ########################################################################### 400 | # Payload: 401 | $ScriptPath = "$($env:temp)\$($TestName).ps1" 402 | if (Test-Path -Path $ScriptPath) { Remove-Item -Path $ScriptPath -Force } 403 | $Content = @() 404 | $Content += "param([string]`$processid,[string]`$dll)" 405 | $Content += "Import-Module PowerSploit" 406 | $Content += "if (Get-Process | ?{`$_.id -eq `$processid}) {" 407 | $Content += " write-host `"Process id `$(`$processid)`" exists" 408 | $Content += " if (Test-Path -Path `$dll) {" 409 | $Content += " write-host `"file `$(`$dll)`" exists" 410 | $Content += " Invoke-DllInjection -ProcessID `$processid -Dll `$dll" 411 | $Content += " }" 412 | $Content += "}" 413 | Set-Content -PassThru $ScriptPath -Value $Content | Out-Null 414 | 415 | $dll = "C:\Windows\System32\advapi32.dll" 416 | $ProcessPath = "c:\windows\notepad.exe" 417 | 418 | $Process = start-process -FilePath $ProcessPath -WindowStyle Hidden -PassThru 419 | Start-Process -FilePath "Powershell.exe" -ArgumentList @("-version 2.0","-file $($ScriptPath)","-processid $($process.id)","-dll $($dll)") -Wait -WindowStyle Hidden 420 | Stop-Process -Id $process.id -Force 421 | ########################################################################### 422 | 423 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 424 | 425 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 426 | write-host $Message ; add-content -Path $LogFile -Value $Message 427 | Start-Sleep -Seconds 2 428 | 429 | 430 | ################################################################################ 431 | # SYSMON_RAWACCESS_READ: EventCode=9 RuleName=RawAccessRead 432 | ################################################################################ 433 | # The RawAccessRead event detects when a process conducts reading operations 434 | # from the drive using the \\.\ denotation. This technique is often used by 435 | # malware for data exfiltration of files that are locked for reading, as well 436 | # as to avoid file access auditing tools. The event indicates the source 437 | # process and target device. 438 | # https://devblogs.microsoft.com/scripting/use-powershell-to-interact-with-the-windows-api-part-1/ 439 | ############################################################################### 440 | $TestName = "SYSMON_RAWACCESS_READ" 441 | 442 | if ($ConstrainSysmonCollection -eq $True) { 443 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 444 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 445 | } 446 | 447 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 448 | write-host $Message ; add-content -Path $LogFile -Value $Message 449 | 450 | $TestStart = (get-date) ; $TestCount = 0 451 | do 452 | { 453 | $TestCount++ 454 | ########################################################################### 455 | # Payload: 456 | 457 | # build the script to call 458 | $ScriptPath = "$($env:temp)\$($TestName).ps1" 459 | if (Test-Path -Path $ScriptPath) { Remove-Item -Path $ScriptPath -Force } 460 | $Content = @() 461 | $Content += "Import-Module PowerSploit -Force" 462 | $Content += "Invoke-NinjaCopy -Path `"$($env:windir)\system32\calc.exe`" -LocalDestination `"$($env:temp)\calc.exe`"" 463 | Set-Content -Path $ScriptPath -Value $Content 464 | $Process = Start-Process -FilePath "Powershell.exe" -ArgumentList @("-version 2.0","-file $($ScriptPath)") -Wait -WindowStyle Hidden -PassThru 465 | ########################################################################### 466 | 467 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 468 | 469 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 470 | write-host $Message ; add-content -Path $LogFile -Value $Message 471 | Start-Sleep -Seconds 2 472 | 473 | ################################################################################ 474 | # SYSMON_FILE_CREATE: EventCode=11 RuleName=FileCreate 475 | ################################################################################ 476 | # File create operations are logged when a file is created or overwritten. 477 | # This event is useful for monitoring autostart locations, like the Startup 478 | # folder, as well as temporary and download directories, which are common 479 | # places malware drops during initial infection. 480 | ############################################################################### 481 | $TestName = "SYSMON_FILE_CREATE" 482 | 483 | if ($ConstrainSysmonCollection -eq $True) { 484 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 485 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 486 | } 487 | 488 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 489 | write-host $Message ; add-content -Path $LogFile -Value $Message 490 | 491 | $TestStart = (get-date) ; $TestCount = 0 492 | do 493 | { 494 | $TestCount++ 495 | ########################################################################### 496 | # Payload: 497 | $TemporaryFile = New-TemporaryFile 498 | Remove-Item $TemporaryFile -Force 499 | ########################################################################### 500 | 501 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 502 | 503 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 504 | write-host $Message ; add-content -Path $LogFile -Value $Message 505 | Start-Sleep -Seconds 2 506 | 507 | 508 | ################################################################################ 509 | # SYSMON_REG_KEY: EventCode=12 RuleName=RegistryEvent (Object create and delete) 510 | ################################################################################ 511 | $TestName = "SYSMON_REG_KEY" 512 | 513 | if ($ConstrainSysmonCollection -eq $True) { 514 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 515 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 516 | } 517 | 518 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 519 | write-host $Message ; add-content -Path $LogFile -Value $Message 520 | 521 | $TestStart = (get-date) ; $TestCount = 0 522 | do 523 | { 524 | $TestCount++ 525 | ########################################################################### 526 | # Payload: 527 | New-Item -Path HKLM:\Software\DeleteMe | Out-Null 528 | remove-item -Path HKLM:\Software\DeleteMe | Out-Null 529 | ########################################################################### 530 | 531 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 532 | 533 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 534 | write-host $Message ; add-content -Path $LogFile -Value $Message 535 | Start-Sleep -Seconds 2 536 | 537 | 538 | ################################################################################ 539 | # SYSMON_REG_SETVALUE: EventCode=13 RuleName=RegistryEvent (Value Set) 540 | ################################################################################ 541 | $TestName = "SYSMON_REG_SETVALUE" 542 | 543 | if ($ConstrainSysmonCollection -eq $True) { 544 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 545 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 546 | } 547 | 548 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 549 | write-host $Message ; add-content -Path $LogFile -Value $Message 550 | 551 | $TestStart = (get-date) ; $TestCount = 0 552 | do 553 | { 554 | $TestCount++ 555 | ########################################################################### 556 | # Payload: 557 | New-Item -Path HKLM:\Software\DeleteMe | out-null 558 | New-ItemProperty -Path HKLM:\Software\DeleteMe -Name Test -PropertyType String -Value "Hello World!" | out-null 559 | remove-item -Path HKLM:\Software\DeleteMe | out-null 560 | ########################################################################### 561 | 562 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 563 | 564 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 565 | write-host $Message ; add-content -Path $LogFile -Value $Message 566 | Start-Sleep -Seconds 2 567 | 568 | 569 | ################################################################################ 570 | # SYSMON_FILE_CREATE_STREAM_HASH: EventCode=15 RuleName=FileCreateStreamHash 571 | ################################################################################ 572 | $TestName = "SYSMON_FILE_CREATE_STREAM_HASH" 573 | 574 | if ($ConstrainSysmonCollection -eq $True) { 575 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 576 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 577 | } 578 | 579 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 580 | write-host $Message ; add-content -Path $LogFile -Value $Message 581 | 582 | $TestStart = (get-date) ; $TestCount = 0 583 | do 584 | { 585 | $TestCount++ 586 | ########################################################################### 587 | # Payload: 588 | $TempFile = New-TemporaryFile 589 | $StreamName = "StreamMessage" 590 | $StreamText = "Hello World" 591 | Set-Content -Path $TempFile.FullName -Stream $StreamName -Value $StreamText 592 | $TempFile | Remove-Item -Force 593 | ########################################################################### 594 | 595 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 596 | 597 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 598 | write-host $Message ; add-content -Path $LogFile -Value $Message 599 | Start-Sleep -Seconds 2 600 | 601 | 602 | ################################################################################ 603 | # SYSMON_CREATE_NAMEDPIPE: EventCode=17 RuleName=PipeEvent 604 | # https://stackoverflow.com/questions/24096969/powershell-named-pipe-no-connection 605 | ################################################################################ 606 | $TestName = "SYSMON_CREATE_NAMEDPIPE" 607 | 608 | if ($ConstrainSysmonCollection -eq $True) { 609 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 610 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 611 | } 612 | 613 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 614 | write-host $Message ; add-content -Path $LogFile -Value $Message 615 | 616 | $TestStart = (get-date) ; $TestCount = 0 617 | do 618 | { 619 | $TestCount++ 620 | ########################################################################### 621 | # Payload: 622 | # create a named pipe 623 | $pipeName = "testpipe$($i)" 624 | $pipe = new-object System.IO.Pipes.NamedPipeServerStream $pipeName,'Out' 625 | $pipe.Dispose() 626 | ########################################################################### 627 | 628 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 629 | 630 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 631 | write-host $Message ; add-content -Path $LogFile -Value $Message 632 | Start-Sleep -Seconds 2 633 | 634 | 635 | ################################################################################ 636 | # SYSMON_CONNECT_NAMEDPIPE: EventCode=18 RuleName=PipeEvent 637 | ################################################################################ 638 | $TestName = "SYSMON_CONNECT_NAMEDPIPE" 639 | 640 | if ($ConstrainSysmonCollection -eq $True) { 641 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 642 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 643 | } 644 | 645 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 646 | write-host $Message ; add-content -Path $LogFile -Value $Message 647 | 648 | $TestStart = (get-date) ; $TestCount = 0 649 | do 650 | { 651 | $TestCount++ 652 | ########################################################################### 653 | # Payload: 654 | # build the script to run the pipe server 655 | $ScriptPath = "$($env:temp)\$($TestName).ps1" 656 | if (Test-Path -Path $ScriptPath) { Remove-Item -Path $ScriptPath -Force } 657 | $Content = @() 658 | $Content += "`$pipe = new-object System.IO.Pipes.NamedPipeServerStream 'testpipe','Out'" 659 | $Content += "`$pipe.WaitForConnection()" 660 | $Content += "`$sw = new-object System.IO.StreamWriter `$pipe" 661 | $Content += "`$sw.AutoFlush = `$true" 662 | $Content += "`$sw.WriteLine(`"Server pid is `$pid`")" 663 | $Content += "`$sw.Dispose()" 664 | $Content += "`$pipe.Dispose()" 665 | Set-Content -Path $ScriptPath -Value $Content 666 | $Process = Start-Process -FilePath "Powershell.exe" -ArgumentList @("-file $($ScriptPath)") -PassThru -WindowStyle Hidden 667 | 668 | # create a named pipe 669 | $pipe = new-object System.IO.Pipes.NamedPipeClientStream '.','testpipe','In' 670 | $pipe.Connect() 671 | $sr = new-object System.IO.StreamReader $pipe 672 | while (($data = $sr.ReadLine()) -ne $null) { write-debug "Received: $data"} 673 | $sr.Dispose() 674 | $pipe.Dispose() 675 | ########################################################################### 676 | 677 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 678 | 679 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 680 | write-host $Message ; add-content -Path $LogFile -Value $Message 681 | Start-Sleep -Seconds 2 682 | 683 | 684 | ################################################################################ 685 | # SYSMON_WMI_FILTER: EventCode=19 RuleName=WmiEvent 686 | # SYSMON_WMI_CONSUMER: EventCode=20 RuleName=WmiEvent 687 | # SYSMON_WMI_BINDING: EventCode=21 RuleName=WmiEvent 688 | ################################################################################ 689 | $TestName = "SYSMON_WMI_CONSUMER" 690 | 691 | if ($ConstrainSysmonCollection -eq $True) { 692 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 693 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 694 | } 695 | 696 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 697 | write-host $Message ; add-content -Path $LogFile -Value $Message 698 | 699 | $TestStart = (get-date) ; $TestCount = 0 700 | do 701 | { 702 | $TestCount++ 703 | ########################################################################### 704 | # Payload: 705 | $command = 'powershell.exe -Command {write-host "hello world!"}' 706 | 707 | $Filter = Set-WmiInstance -Namespace root/subscription -Class __EventFilter -Arguments @{ 708 | EventNamespace = 'root/cimv2' 709 | Name = "TestFilter" 710 | Query = "SELECT * FROM __InstanceCreationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Process' AND Name='calc.exe'" 711 | QueryLanguage = 'WQL' 712 | } 713 | 714 | $Consumer = Set-WmiInstance -Namespace root/subscription -Class CommandLineEventConsumer -Arguments @{ 715 | Name = "TestConsumer" 716 | CommandLineTemplate = $Command 717 | } 718 | 719 | $Binding = Set-WmiInstance -Namespace root/subscription -Class __FilterToConsumerBinding -Arguments @{ 720 | Filter = $Filter 721 | Consumer = $Consumer 722 | } 723 | 724 | #Cleanup 725 | Get-WmiObject __EventFilter -namespace root\subscription | ?{$_.Name -eq "TestFilter"} | Remove-WmiObject 726 | Get-WmiObject CommandLineEventConsumer -Namespace root\subscription | ?{$_.Name -eq "TestConsumer"} | Remove-WmiObject 727 | Get-WmiObject __FilterToConsumerBinding -Namespace root\subscription | ?{$_.filter -match "TestFilter"} | Remove-WmiObject 728 | ########################################################################### 729 | 730 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 731 | 732 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 733 | write-host $Message ; add-content -Path $LogFile -Value $Message 734 | Start-Sleep -Seconds 2 735 | 736 | 737 | ################################################################################ 738 | # SYSMON_DNS_QUERY: EventCode=22 RuleName=DnsQuery 739 | ################################################################################ 740 | $TestName = "SYSMON_DNS_QUERY" 741 | 742 | if ($ConstrainSysmonCollection -eq $True) { 743 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 744 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 745 | } 746 | 747 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 748 | write-host $Message ; add-content -Path $LogFile -Value $Message 749 | 750 | $TestStart = (get-date) ; $TestCount = 0 751 | do 752 | { 753 | $TestCount++ 754 | ########################################################################### 755 | # Payload: 756 | Resolve-DnsName -Name "www.google.com" | Out-Null 757 | ########################################################################### 758 | 759 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 760 | 761 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 762 | write-host $Message ; add-content -Path $LogFile -Value $Message 763 | Start-Sleep -Seconds 2 764 | 765 | 766 | ################################################################################ 767 | # SYSMON_FILE_DELETE: EventCode=23 RuleName=FileDelete 768 | ################################################################################ 769 | $TestName = "SYSMON_FILE_DELETE" 770 | 771 | if ($ConstrainSysmonCollection -eq $True) { 772 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 773 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 774 | } 775 | 776 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 777 | write-host $Message ; add-content -Path $LogFile -Value $Message 778 | 779 | $TestStart = (get-date) ; $TestCount = 0 780 | do 781 | { 782 | $TestCount++ 783 | ########################################################################### 784 | # Payload: 785 | $TemporaryFile = New-TemporaryFile 786 | Add-Content -Value "Hello World!" -Path $TemporaryFile.FullName 787 | Remove-Item -Path $TemporaryFile.FullName -Force 788 | ########################################################################### 789 | 790 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 791 | 792 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 793 | write-host $Message ; add-content -Path $LogFile -Value $Message 794 | Start-Sleep -Seconds 2 795 | 796 | ################################################################################ 797 | # SYSMON_CLIPBOARD: EventCode=24 RuleName=ClipboardChange 798 | ################################################################################ 799 | $TestName = "SYSMON_CLIPBOARD" 800 | 801 | if ($ConstrainSysmonCollection -eq $True) { 802 | $configpath = make-sysmon-config -name $TestName -sysmonPath $sysmonPath 803 | reset-sysmon -sysmonPath $sysmonpath -configpath $configpath 804 | } 805 | 806 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"Begin`"" 807 | write-host $Message ; add-content -Path $LogFile -Value $Message 808 | 809 | $TestStart = (get-date) ; $TestCount = 0 810 | do 811 | { 812 | $TestCount++ 813 | ########################################################################### 814 | # Payload: 815 | Set-Clipboard -Value "Hello World!" 816 | ########################################################################### 817 | 818 | } until ((New-TimeSpan -Start $TestStart).TotalSeconds -ge $TestDurationTotalSeconds) 819 | 820 | $Message = "$(format-splunktime) TestName=`"$($TestName)`" TestDurationTotalSeconds=`"$($TestDurationTotalSeconds)`" TestStatus=`"End`" TestCount=`"$($TestCount)`"" 821 | write-host $Message ; add-content -Path $LogFile -Value $Message 822 | Start-Sleep -Seconds 2 823 | 824 | 825 | } 826 | until ($x -gt 0) 827 | 828 | -------------------------------------------------------------------------------- /TS-ListSplunkAppsByCategory.ps1: -------------------------------------------------------------------------------- 1 | $SplunkHome = "C:\Program Files\SplunkUniversalForwarder" 2 | $SplunkApps = "$($SplunkHome)\etc\apps" 3 | $ServerclassXml = "$($SplunkHome)\var\run\serverclass.xml" 4 | 5 | 6 | # check for presence of splunk home 7 | if (-not (Test-Path -Path $SplunkHome)) { 8 | write-host "Path to splunk home `"$($SplunkHome)` not found. Exiting." 9 | exit 10 | } 11 | 12 | # check for presence of splunk apps 13 | if (-not (Test-Path -Path $SplunkApps)) { 14 | write-host "Path to splunk apps `"$($SplunkApps)` not found. Exiting." 15 | exit 16 | } 17 | 18 | # check for presence of Serverclass.xml 19 | if (-not (Test-Path -Path $SplunkHome)) { 20 | write-host "Path to serverclass `"$($ServerclassXml)` not found. Exiting." 21 | exit 22 | } 23 | 24 | # read xml file into object 25 | $ServerClass = [xml](Get-Content -Path $ServerclassXml) 26 | 27 | $Records = @() 28 | 29 | # iterate through items and add to records object 30 | if ($ServerClass.deployResponse) { 31 | foreach ($class in $ServerClass.deployresponse.serverClass) { 32 | foreach ($app in $class.app) { 33 | 34 | $Info = @{ 35 | "serverclass" = $class.name 36 | "app" = $app.name 37 | } 38 | 39 | $Records += New-Object -TypeName PSObject -Property $Info 40 | } 41 | } 42 | } 43 | 44 | 45 | # get list of apps present 46 | $Apps = Get-ChildItem -Path $SplunkApps -Directory 47 | $AppRecords = @() 48 | 49 | # categorize apps as built-in, deployed, or local based on factors 50 | foreach ($app in $Apps) { 51 | 52 | # assume app is local, "Local" 53 | $AppType = "Local" 54 | 55 | # if app has name of known local apps change value to "built-in" 56 | if ($app.Name -match "^(.*Splunk-FileAndDirectoryEliminator.*|introspection_generator_addon|learned|search|SplunkUniversalForwarder|splunk_httpinput|splunk_internal_metrics)$") { 57 | $AppType = "Built-In" 58 | } 59 | 60 | # if app has name present in list apps in serverlcass change value to "deployed" 61 | if ($Records.app -match $app.name) { 62 | $AppType = "Deployed" 63 | } 64 | 65 | $Info = @{ 66 | "Name" = $app.Name 67 | "Type" = $AppType 68 | } 69 | 70 | $AppRecords += New-Object -TypeName PSObject -Property $Info 71 | 72 | } 73 | 74 | # write-output records in recordset in format optimized for tanmium input 75 | foreach ($AppRecord in $AppRecords | ?{$_.type -ne "Built-in"}) { 76 | $recordString = ($AppRecord | ConvertTo-Csv -NoTypeInformation)[1] 77 | $recordString = $recordString -replace ",","|" 78 | $recordString = $recordString -replace "`"","" 79 | Write-Output $recordString 80 | } 81 | 82 | 83 | -------------------------------------------------------------------------------- /TestAppDownload.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Purpose: Powershell script to download list of apps on splunkbase, prompt for selection, and initiate download via browser that pre-authenticated to splunkbase 3 | Credit: methods exposed in Analysis Of SplunkBase Apps for Splunk (https://splunkbase.splunk.com/app/2919/) 4 | Credit: methods exposed in Splunkbase-download project (https://github.com/tfrederick74656/splunkbase-download) 5 | #> 6 | 7 | $ProjectFolder = "C:\Apps\splunkbase" 8 | $SupportFolder = "$($ProjectFolder)\support" 9 | $AppsToTrack = "$($SupportFolder)\splunkAppsInteresting.csv" 10 | $MyApps = Import-Csv -Path $AppsToTrack | ?{$_.interesting -eq "x"} | Select-Object -ExpandProperty path 11 | $MyAppsRegex = $MyApps -join "|" 12 | $MyAppsRegex = "`($($MyAppsRegex )`)" 13 | 14 | $BookmarkFile = "$($SupportFolder)\LastModifiedDate.md" 15 | 16 | # if bookmark file exists, get the date it contains 17 | if (Test-Path -Path $BookmarkFile) { 18 | [datetime]$BookmarkDate = Get-Content -Path $BookmarkFile 19 | } else { 20 | $BookmarkDate = Get-Date -Date "06/26/2020" 21 | } 22 | 23 | 24 | function Get-Splunkbase-SessionCookie { 25 | param($credFilePath) 26 | $cred = Import-Clixml $credFilePath 27 | $user = $cred.UserName 28 | $pass = [System.Net.NetworkCredential]::new("", $cred.Password).Password 29 | 30 | ## establish logon session to splunk via okta 31 | $BASE_AUTH_URL='https://account.splunk.com/api/v1/okta/auth' 32 | $Body = @{ 33 | username = $user 34 | password = $pass 35 | } 36 | $WebRequest = Invoke-WebRequest $BASE_AUTH_URL -SessionVariable 'Session' -Body $Body -Method 'POST' -UseBasicParsing 37 | if (!($WebRequest.StatusCode -eq "200")) { 38 | write-host "There was a problem authenticating to Splunk. Exit." 39 | exit 40 | } 41 | $ssoid_cookie = (($WebRequest.Content | ConvertFrom-Json).cookies).ssoid_cookie 42 | $sid = ($Session.Cookies.GetCookies("https://splunkbase.splunk.com") | ?{$_.Name -eq "sid"}).Value 43 | 44 | $Cookie = "sid=$($sid); SSOSID=$($ssoid_cookie)" 45 | 46 | return $Cookie 47 | } 48 | 49 | function Get-Splunkbase-AppInfo { 50 | param($AppID) 51 | 52 | $WebRequest = Invoke-WebRequest -Uri "https://splunkbase.splunk.com/app/$($AppID)/" 53 | 54 | # Find the DIV element of expected class having release-version info 55 | $element = $WebRequest.AllElements | ?{ 56 | $_.class -eq "u.item:1/1@*" -and 57 | $_.tagname -eq "DIV" -and 58 | $_.innerhtml -match "sb-selector=`"release-version`"" -and 59 | $_.innerhtml -match "u-for=`"download-modal`"" -and 60 | $_.innerhtml -match "checksum" -and 61 | $_.outertext -match "^Downloading"} 62 | 63 | # extract out the release version (sb-target value) 64 | $element.innerHTML -match "sb-target=`"([^\`"]+)`"" | Out-Null 65 | $version = $matches[1] 66 | 67 | # extract out the download filename 68 | $element.innerHTML -match " checksum \(([^\)]+)" | Out-Null 69 | $filename = $matches[1] 70 | 71 | # return new object with desired info 72 | [PSCustomObject]@{ 73 | filename=$filename 74 | version=$version 75 | id=$appid 76 | } 77 | 78 | } 79 | 80 | # get latest version in case tweaks were made at source 81 | Set-Location -Path $ProjectFolder 82 | & git.exe pull 83 | 84 | 85 | ## gather cred from credstore 86 | $mycredfile = "$($SupportFolder)\mycred.xml" 87 | if (!(Test-Path -Path $mycredfile)) { 88 | Get-Credential -Message "Enter credential for Splunkbase" | Export-Clixml -path $mycredfile 89 | } 90 | $cred = Import-Clixml $mycredfile 91 | 92 | # Get cookie from splunk sesssion for use with CURL later. 93 | $Cookie = Get-Splunkbase-SessionCookie -credFilePath $mycredfile 94 | 95 | 96 | # first run just to get the amount of pages to iterate over. 97 | $url = "https://splunkbase.splunk.com/api/v1/app/?order=latest&limit=1&offset=0" 98 | $response = invoke-webrequest $url 99 | $content = $response.Content | ConvertFrom-Json 100 | 101 | # gather all of the content available over pages 102 | $results = @() 103 | for ($offset = 0; $offset -le $content.total; $offset += 100) 104 | { 105 | write-host "Getting next 100 results from offset $($offset) [total=$($content.total)]" 106 | $url = "https://splunkbase.splunk.com/api/v1/app/?order=latest&limit=100&offset=$($offset)" 107 | $response = invoke-webrequest $url 108 | $content = $response.Content | ConvertFrom-Json 109 | $results += $content.results 110 | } 111 | 112 | 113 | # reduce list down to those which had an update (updated_time) recently 114 | $Selected = $results | ?{$_.path -match $MyAppsRegex} 115 | $Selected = $selected | ?{[datetime]$_.updated_time -le $BookmarkDate} | Sort-Object -Property updated_time 116 | $Selected | %{"$($_.title) - $($_.updated_time)"} 117 | 118 | 119 | $counter = 0 120 | $Failures = @() 121 | foreach ($item in $selected | ?{$_.path -match $MyAppsRegex}) { 122 | $counter++ 123 | 124 | # get latest version for app by appid 125 | $AppInfo = Get-Splunkbase-AppInfo -appid $item.uid 126 | 127 | if ($AppInfo.version -notmatch "^\d") { 128 | write-host "Download url for `"$($item.title)`" doesn't look right. Skipping!" 129 | } else { 130 | 131 | # build download url from base + appid + app version 132 | $Down_URL = "https://splunkbase.splunk.com/app/$($AppInfo.id)/release/$($AppInfo.version)/download/" 133 | 134 | write-host "Downloading `"$($item.title)`" from $($Down_URL)`"...." 135 | $filepath = "$ProjectFolder\$($appinfo.filename)" 136 | $ArgumentList = @("--cookie `"$($Cookie)`"","$($Down_URL)","--output $($filepath)","-L","-#") 137 | $process = Start-Process -FilePath "$($SupportFolder)\curl.exe" -ArgumentList $ArgumentList -WindowStyle Hidden -PassThru -Wait 138 | 139 | if (!(Test-Path -Path $filepath)) { 140 | write-host "there was a problem with download; exit." 141 | exit 142 | } 143 | 144 | # identify the file to extract 145 | $list = & "$($SupportFolder)\7z.exe" l -ba $filepath -y 146 | $list -match "\s+\S+\s+\d+\s+\d+\s+(.*)" | Out-Null 147 | $tarFilePath = "$ProjectFolder\$($matches[1])" 148 | 149 | # extract the TGZ file 150 | write-host "-extract TGZ file $($filepath)..." 151 | $ArgumentList = @("e",$filepath,"-o`"$ProjectFolder\`"","-y") 152 | $process = Start-Process -FilePath "$($SupportFolder)\7z.exe" -ArgumentList $ArgumentList -WindowStyle Hidden -PassThru -Wait 153 | 154 | if (!(Test-Path -Path $tarFilePath)) { 155 | write-host "there was a problem extracting TAR file; exiting." 156 | exit 157 | } 158 | 159 | # extract the TAR file 160 | write-host "-extract TAR file $($tarFilePath)..." 161 | $ArgumentList = @("x",$tarFilePath,"-o`"$ProjectFolder\`"","-y","-r","-aoa") 162 | $process = Start-Process -FilePath "$($SupportFolder)\7z.exe" -ArgumentList $ArgumentList -WindowStyle Hidden -PassThru -Wait 163 | 164 | # cleanup download files 165 | if (Test-Path -Path $filepath) { Remove-Item -Path $filepath -Force } 166 | if (Test-Path -Path $tarFilePath) { Remove-Item -Path $tarFilePath -Force } 167 | 168 | 169 | # remove the old app in the apps subfolder, if exists, before copying new app in. 170 | $Directories = Get-ChildItem -Path $ProjectFolder -Directory 171 | $NewDirectory = $Directories | ?{$_.name -notmatch "^(Support|Apps)$"} 172 | $TempPath = "$($ProjectFolder)\apps\$($NewDirectory.Name)" 173 | if (Test-Path -Path $TempPath) { Remove-Item -Path $TempPath -Recurse -Force } 174 | $NewDirectory | Move-Item -Destination $TempPath 175 | } 176 | 177 | } 178 | 179 | # add new assets to git 180 | & git.exe add -A 181 | 182 | # commit the content 183 | $commitDate = (get-date).ToString() 184 | & git.exe commit -m "splunkbase $($commitDate)" 185 | 186 | # push the content 187 | & git.exe push 188 | 189 | Set-Content -Path $BookmarkFile -Value $commitDate 190 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dstaulcu/SplunkTools/6beb47cf173310877985ace7ac2d10443bac93d7/demo.gif -------------------------------------------------------------------------------- /demo_csv_to_splunk.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import sys 3 | import json 4 | import requests 5 | import datetime 6 | 7 | filepath = r'C:\Users\david\Downloads\customers-100.csv' 8 | 9 | # initialize list to contain csv row dict items 10 | data = [] 11 | 12 | # get csv rows into list of dicts 13 | with open(filepath, newline='', encoding='utf-8') as csvfile: 14 | reader = csv.DictReader(csvfile) 15 | try: 16 | for row in reader: 17 | data.append(row) 18 | except csv.Error as e: 19 | sys.exit('file {}, line {}: {}'.format(filepath, reader.line_num, e)) 20 | 21 | # batch writes to Splunk http event collector (hec) in a manner 22 | # that ensures payload is lower than max content size 23 | hec_max_content_length = 1000000 24 | hec_max_content_length = 3000 # testing 25 | hec_events = [] 26 | hec_events_counter = 0 27 | hec_batch_counter = 0 28 | 29 | # construct uri and header for post 30 | splunk_uri = 'https://splunk_host:8088/services/collector/event' 31 | splunk_hec_token = 'blah' 32 | headers = {'Authorization': 'Splunk '+ splunk_hec_token} 33 | 34 | # iterate through rows to produce event and post in batches 35 | for item in data: 36 | 37 | hec_events_counter += 1 38 | 39 | # todo:// convert eventTime from string to datetime and then epoch for use in Splunk _time field 40 | #eventTime_utc = datetime.datetime.strptime(my_event['eventTime'], "%Y-%m-%dT%H:%M:%S.%fZ") 41 | #eventTime_epoch = (eventTime_utc - datetime(1970, 1, 1)).total_seconds() 42 | 43 | # todo:// append splunk metadata to item 44 | hec_event = {} 45 | hec_event['index'] = 'test-index' 46 | hec_event['source'] = 'test-source' 47 | hec_event['sourcetype'] = 'test-sourcetype' 48 | # hec_event['_time'] = eventTime_epoch 49 | hec_event['event'] = item 50 | 51 | hec_events.append(hec_event) 52 | 53 | # get size of accumulated list as json 54 | hec_events_size = len(json.dumps(hec_events)) 55 | 56 | if hec_events_size >= hec_max_content_length: 57 | hec_batch_counter += 1 58 | # commit all but most recent record to batch 59 | batch = hec_events[:-1] 60 | print('writing batch: {} to splunk hec having {} events with total size of {} bytes'.format( 61 | hec_batch_counter, 62 | len(batch), 63 | len(json.dumps(batch)) 64 | ) 65 | ) 66 | # post the event to splunk http event collector endpoint 67 | #try: 68 | # r = requests.post(splunk_uri, data=json.dumps(batch), headers=headers, verify=ca_certs) 69 | # r.raise_for_status() 70 | #except requests.exceptions.HTTPError as err: 71 | # raise SystemExit(err) 72 | 73 | 74 | # reset the events list and re-add current item 75 | hec_events = [] 76 | hec_events.append(hec_event) 77 | 78 | # handle condition where we are processing the last record but max content not exceeded 79 | if hec_events_counter == len(data): 80 | hec_batch_counter += 1 81 | batch = hec_events 82 | print('writing batch: {} to splunk hec having {} events with total size of {} bytes'.format( 83 | hec_batch_counter, 84 | len(batch), 85 | len(json.dumps(batch)) 86 | ) 87 | ) 88 | # post the event to splunk http event collector endpoint 89 | #try: 90 | # r = requests.post(splunk_uri, data=json.dumps(batch), headers=headers, verify=ca_certs) 91 | # r.raise_for_status() 92 | #except requests.exceptions.HTTPError as err: 93 | # raise SystemExit(err) 94 | 95 | print('last event: {}'.format(batch[-1])) 96 | -------------------------------------------------------------------------------- /download_latest_splunkproduct_docs.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from requests import Response 3 | from requests.exceptions import Timeout 4 | import http 5 | from bs4 import BeautifulSoup 6 | import re 7 | import concurrent.futures 8 | 9 | """ 10 | todo: 11 | • Accept command line parameters for products to download 12 | • Accept command line parameters for folder to download pdf document to 13 | """ 14 | 15 | 16 | def load_url(download): 17 | 18 | pdf_download_url, file_path = download 19 | 20 | print('-downloading url: {} to: {}'.format(pdf_download_url, file_path)) 21 | 22 | # open in binary mode 23 | with open(file_path, "wb") as file: 24 | # get request 25 | response = s.get(pdf_download_url) 26 | # write to file 27 | file.write(response.content) 28 | 29 | 30 | def get_splunkdoc_products(): 31 | print('Getting list of splunk products.') 32 | 33 | url = "https://docs.splunk.com/Documentation/Splunk" 34 | page = s.get(url) 35 | soup = BeautifulSoup(page.content, "html.parser") 36 | 37 | elements = soup.select('#product-select') 38 | pattern = 'value=\"([^\"]+)\">(.*)\' 39 | 40 | elements_dict = {} 41 | 42 | for element in elements[0].contents: 43 | element = str(element) 44 | match = re.match(pattern, element, re.IGNORECASE) 45 | if match := re.search(pattern, element, re.IGNORECASE): 46 | key = match.group(1) 47 | 48 | value = match.group(2) 49 | value = value.replace('', '') 50 | value = value.replace('', '') 51 | 52 | elements_dict[key] = value 53 | 54 | return elements_dict 55 | 56 | def get_splunkdoc_versions(product): 57 | url = "https://docs.splunk.com/Documentation/" + product 58 | page = s.get(url) 59 | soup = BeautifulSoup(page.content, "html.parser") 60 | 61 | elements = soup.select('#version-select') 62 | pattern = 'value=\"([^\"]+)\">(.*)\' 63 | 64 | elements_dict = {} 65 | 66 | for element in elements[0].contents: 67 | element = str(element) 68 | match = re.match(pattern, element, re.IGNORECASE) 69 | if match := re.search(pattern, element, re.IGNORECASE): 70 | key = match.group(1) 71 | 72 | value = match.group(2) 73 | value = value.replace('', '') 74 | value = value.replace('', '') 75 | 76 | elements_dict[key] = value 77 | 78 | return elements_dict 79 | 80 | 81 | 82 | # create session object for re-use 83 | s = requests.Session() 84 | # toggle value from 0 to 1 to enable http request debugging 85 | http.client.HTTPConnection.debuglevel = 0 86 | 87 | download_path = 'C:\Apps\splunkdocs' 88 | #my_products = get_splunkdoc_products() 89 | my_products = ['Splunk', 'Forwarder', 'DSP', 'ES', 'SOARonprem', 'UBA', 'MC', 'SSE', 'ITSI', 'DBX'] 90 | 91 | 92 | download_list = [] 93 | 94 | for product in my_products: 95 | 96 | print('working on product: {}'.format(product)) 97 | 98 | versions = (get_splunkdoc_versions(product)) 99 | 100 | for version in versions: 101 | if 'latest release' in versions[version]: 102 | 103 | print('-found latest release version: {}'.format(version)) 104 | 105 | # get page for specified product and version as soup 106 | url = 'https://docs.splunk.com/Documentation/' + product + '/' + version 107 | page = s.get(url) 108 | soup = BeautifulSoup(page.content, "html.parser") 109 | 110 | # process links listing documentation for product and version 111 | for i in soup.find_all(href=re.compile('^/Documentation/' + product + '/' + version + '/')): 112 | 113 | # get page associated with the document 114 | page = s.get('https://docs.splunk.com' + (i.attrs['href'])) 115 | soup = BeautifulSoup(page.content, "html.parser") 116 | 117 | # get the links on document page associated pdfbook (effectively excluding topic) 118 | for j in soup.find_all(href=re.compile('title=Documentation:.*&action=pdfbook&[^\&]+&product=')): 119 | 120 | # construct the download url 121 | href = j.attrs['href'] 122 | pdf_download_url = 'https://docs.splunk.com' + href 123 | 124 | # construct the download filename 125 | document = (href.split(":"))[2] 126 | file_name = product + '-' + version + '-' + document + '.pdf' 127 | file_path = download_path + '\\' + file_name 128 | 129 | print('-adding document {} to download list'.format(document)) 130 | download_list.append((pdf_download_url, file_path)) 131 | 132 | 133 | # We can use a with statement to ensure threads are cleaned up promptly 134 | with concurrent.futures.ThreadPoolExecutor(max_workers=20) as executor: 135 | # Start the load operations and mark each future with its URL 136 | future_to_url = {executor.submit(load_url, download_item): download_item for download_item in download_list} 137 | for future in concurrent.futures.as_completed(future_to_url): 138 | download_item = future_to_url[future] 139 | try: 140 | data = future.result() 141 | except Exception as exc: 142 | print('%r generated an exception: %s' % (download_item, exc)) 143 | else: 144 | pass 145 | -------------------------------------------------------------------------------- /splunk_ntfs_check_and_set.ps1: -------------------------------------------------------------------------------- 1 | <# 2 | Check for permissions issues and correct them 3 | #> 4 | 5 | <# Debug mode switches 6 | $DebugPreference = "Continue" # Debug Mode 7 | $DebugPreference = "SilentlyContinue" # Normal Mode 8 | #> 9 | 10 | #Checks if the user is in the administrator group. Warns and stops if the user is not. 11 | if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) { 12 | Write-host "You are not running this as local administrator. Run it again in an elevated prompt." 13 | exit 14 | } 15 | 16 | # define top level folder of interest 17 | $SplunkHome = "C:\Program Files\SplunkUniversalForwarder" 18 | 19 | # Define list of critical objects requiring special permissions 20 | $CriticalObjects = @("$($SplunkHome)\etc\apps" 21 | ,"$($SplunkHome)\etc\auth" 22 | ,"$($SplunkHome)\etc\apps" 23 | ,"$($SplunkHome)\etc\system" 24 | ,"$($SplunkHome)\var\log" 25 | ,"$($SplunkHome)\var\run") 26 | 27 | 28 | function Get-ChildItemUnauthorizedAccessError { 29 | param ( 30 | [parameter(Mandatory=$true)][string]$Folder 31 | ,[parameter(Mandatory=$true)][bool]$Recurse=$false 32 | ,[Parameter(Mandatory=$true)][ValidateSet("Directory", "File", "All")]$ObjectType="Directory" 33 | ) 34 | 35 | $Error.Clear() 36 | 37 | if ($ObjectType -eq "All") { 38 | $Objects = Get-ChildItem -Path $Folder -Recurse:$Recurse -ErrorAction SilentlyContinue 39 | } 40 | 41 | if ($ObjectType -eq "File") { 42 | $Objects = Get-ChildItem -Path $Folder -Recurse:$Recurse -File -ErrorAction SilentlyContinue 43 | } 44 | 45 | if ($ObjectType -eq "Directory") { 46 | $Objects = Get-ChildItem -Path $Folder -Recurse:$Recurse -Directory -ErrorAction SilentlyContinue 47 | } 48 | 49 | $Issues = $Error | ?{$_.FullyQualifiedErrorId -match "UnauthorizedAccessError"} 50 | 51 | return $Issues 52 | } 53 | 54 | 55 | # do initial permission check 56 | $Issues = Get-ChildItemUnauthorizedAccessError -Folder $SplunkHome -Recurse $true -ObjectType All 57 | 58 | if ($Issues) { 59 | 60 | # List files or folders to which access was denied 61 | foreach ($Issue in $Issues) { 62 | write-host $Issue.ToString() 63 | 64 | } 65 | 66 | # Reset permissions (and ownership) on all items to match that of reference folder 67 | $ReferenceACL = Get-Acl -Path $env:ProgramFiles 68 | Get-ChildItem -Path $SplunkHome -Recurse | %{ $ReferenceACL | Set-Acl -Path $_.FullName } 69 | 70 | # Remove user level access from critical objects 71 | foreach ($object in $CriticalObjects) { 72 | 73 | # Check to see if path exists 74 | if (Test-Path -Path $object) { 75 | 76 | # Remove inheritance 77 | $acl = Get-Acl -Path $object 78 | $acl.SetAccessRuleProtection($true,$true) 79 | Set-Acl -Path $object -AclObject $acl 80 | 81 | # Remove BUILTIN\User ACEs 82 | $acl = Get-Acl -Path $object 83 | $acesToRemove = $acl.Access | ?{ $_.IsInherited -eq $false -and $_.IdentityReference -eq 'BUILTIN\Users' } 84 | if ($acesToRemove) { 85 | foreach ($aceToRemove in $acesToRemove) { 86 | $acl.RemoveAccessRule($aceToRemove) | Out-Null 87 | } 88 | Set-Acl -AclObject $acl -Path $object 89 | } 90 | 91 | # Recurse the change from this object to it's children 92 | $ReferenceACL = Get-Acl -Path $object 93 | Get-ChildItem -Path $object -Recurse | %{ $ReferenceACL | Set-Acl -Path $_.FullName } 94 | } 95 | } 96 | 97 | } else { 98 | write-host "No persmission issues under $($SplunkHome)" 99 | } 100 | 101 | <# 102 | # Remove NT AUTHORITY\SYSTEM ACEs (to get environment to broken state again) 103 | $SplunkHome = "C:\Program Files\SplunkUniversalForwarder" 104 | $object = "$($SplunkHome)\etc\apps" 105 | $acl = Get-Acl -Path $object 106 | $acesToRemove = $acl.Access | ?{ $_.IsInherited -eq $false -and $_.IdentityReference -match '(NT AUTHORITY\\SYSTEM|BUILTIN\\Administrators)' } 107 | if ($acesToRemove) { 108 | foreach ($aceToRemove in $acesToRemove) { 109 | $acl.RemoveAccessRule($aceToRemove) | Out-Null 110 | } 111 | Set-Acl -AclObject $acl -Path $object 112 | 113 | # Recurse the change from this object to children 114 | $ReferenceACL = Get-Acl -Path $object 115 | Get-ChildItem -Path $object -Recurse | %{ $ReferenceACL | Set-Acl -Path $_.FullName } 116 | } 117 | 118 | ((Get-Acl -Path $object).access).identityReference 119 | #> 120 | -------------------------------------------------------------------------------- /tp-splunk-appreset.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | Dim objShell :: Set objShell = CreateObject("WScript.Shell") 4 | Dim objFSO :: Set objFSO = CreateObject("Scripting.FileSystemObject") 5 | Dim objFolder, colSubFolders, objSubfolder, strCurrentFolderName, intCurrentFolderAge, intYoungestFolderAge, strYoungestFolderName 6 | Dim strSplunkProgramPath :: strSplunkProgramPath = "C:\Program Files\SplunkUniversalForwarder\bin\splunk.exe" 7 | Dim strSplunkAppsPath :: strSplunkAppsPath = "C:\Program Files\SplunkUniversalForwarder\etc\apps" 8 | Dim blnSomethingDeleted :: blnSomethingDeleted = False 9 | 10 | Dim blnDebug :: blnDebug = True 11 | 12 | 13 | If NOT objFSO.FileExists(strSplunkProgramPath) Then 14 | wscript.echo "Splunk program file does not exist." 15 | wscript.quit 16 | End If 17 | 18 | If NOT objFSO.FolderExists(strSplunkAppsPath) Then 19 | wscript.echo "Splunk apps folder does not exist." 20 | wscript.quit 21 | End If 22 | 23 | Set objFolder = objFSO.GetFolder(strSplunkAppsPath) 24 | Set colSubfolders = objFolder.Subfolders 25 | 26 | For Each objSubfolder in colSubfolders 27 | strCurrentFolderName = ucase(objSubfolder.Name) 28 | if (strCurrentFolderName = ucase("introspection_generator_addon") Or _ 29 | strCurrentFolderName = ucase("learned") Or _ 30 | strCurrentFolderName = ucase("search") Or _ 31 | strCurrentFolderName = ucase("SplunkUniversalForwarder") Or _ 32 | strCurrentFolderName = ucase("splunk_httpinput")) Then 33 | ' do nothing 34 | else 35 | ' delete the folder 36 | blnSomethingDeleted = True 37 | if blnDebug = True then 38 | wscript.echo "Deleting folder with name: " & objSubfolder.Name 39 | end if 40 | objSubfolder.delete 41 | end if 42 | Next 43 | 44 | if blnSomethingDeleted = True then 45 | if blnDebug = True then 46 | wscript.echo "Restarting splunk. (no wait)" 47 | end if 48 | objShell.Run chr(34) & strSplunkProgramPath & chr(34) & " restart", 0, False 49 | end if 50 | 51 | -------------------------------------------------------------------------------- /ts-splunk-appage.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | Dim objFSO :: Set objFSO = CreateObject("Scripting.FileSystemObject") 4 | Dim objFolder, colSubFolders, objSubfolder, strCurrentFolderName, intCurrentFolderAge, intYoungestFolderAge, strYoungestFolderName 5 | 6 | Dim blnDebug :: blnDebug = False 7 | 8 | Dim strFolderOfInterest :: strFolderOfInterest = "C:\Program Files\SplunkUniversalForwarder\etc\apps" 9 | 10 | If NOT objFSO.FolderExists(strFolderOfInterest) Then 11 | wscript.echo "Splunk apps folder does not exist." 12 | wscript.quit 13 | End If 14 | 15 | Set objFolder = objFSO.GetFolder(strFolderOfInterest) 16 | Set colSubfolders = objFolder.Subfolders 17 | 18 | For Each objSubfolder in colSubfolders 19 | strCurrentFolderName = ucase(objSubfolder.Name) 20 | if (strCurrentFolderName = ucase("introspection_generator_addon") Or _ 21 | strCurrentFolderName = ucase("learned") Or _ 22 | strCurrentFolderName = ucase("search") Or _ 23 | strCurrentFolderName = ucase("SplunkUniversalForwarder") Or _ 24 | strCurrentFolderName = ucase("splunk_httpinput")) Then 25 | ' do nothing 26 | else 27 | intCurrentFolderAge = datediff("s",objSubfolder.DateLastModified,Now) 28 | if (intYoungestFolderAge = "" Or intYoungestFolderAge > intCurrentFolderAge) Then 29 | intYoungestFolderAge = intCurrentFolderAge 30 | strYoungestFolderName = strCurrentFolderName 31 | end if 32 | end if 33 | Next 34 | 35 | ' if we got this far, check to see if folders were found and report on status 36 | if intYoungestFolderAge >= 0 then 37 | ' convert age from seconds to days 38 | intYoungestFolderAge = round(intYoungestFolderAge/86400,0) 39 | if blnDebug = True Then 40 | wscript.echo "The most recent splunk app [" & strYoungestFolderName & "] is [" & intYoungestFolderAge & "] days old." 41 | end if 42 | wscript.echo intYoungestFolderAge 43 | else 44 | wscript.Echo "Splunk apps folder does not contain custom apps." 45 | end if 46 | -------------------------------------------------------------------------------- /ts-universalforwarder-health.vbs: -------------------------------------------------------------------------------- 1 | Option Explicit 2 | 3 | Dim blnDebug :: blnDebug = False 4 | 5 | Function FolderExists(strFolderName) 6 | Dim objFSO :: Set objFSO = CreateObject("Scripting.FileSystemObject") 7 | 8 | If objFSO.FolderExists(strFolderName) Then 9 | FolderExists = True 10 | else 11 | FolderExists = False 12 | End If 13 | 14 | Set objFSO = Nothing 15 | End Function 16 | 17 | Function GetSystemArchitecture 18 | Dim WshShell 19 | Dim WshProcEnv 20 | Dim system_architecture 21 | Dim process_architecture 22 | 23 | Set WshShell = CreateObject("WScript.Shell") 24 | Set WshProcEnv = WshShell.Environment("Process") 25 | 26 | process_architecture= WshProcEnv("PROCESSOR_ARCHITECTURE") 27 | 28 | If process_architecture = "x86" Then 29 | system_architecture= WshProcEnv("PROCESSOR_ARCHITEW6432") 30 | 31 | If system_architecture = "" Then 32 | system_architecture = "x86" 33 | End if 34 | Else 35 | system_architecture = process_architecture 36 | End If 37 | 38 | GetSystemArchitecture = system_architecture 39 | 40 | Set WshShell = Nothing 41 | Set WshProcEnv = Nothing 42 | End Function 43 | 44 | Function GetLastFolderAge(strFolder) 45 | Dim objFSO :: Set objFSO = CreateObject("Scripting.FileSystemObject") 46 | Dim objFolder, colSubfolders, objSubfolder, strCurrentFolderName 47 | 48 | Dim LastFolder, LastDate, LastFolderAge 49 | 50 | Set objFolder = objFSO.GetFolder(strFolder) 51 | Set colSubfolders = objFolder.Subfolders 52 | 53 | For Each objSubfolder in colSubfolders 54 | strCurrentFolderName = ucase(objSubfolder.Name) 55 | if (strCurrentFolderName = ucase("introspection_generator_addon") Or _ 56 | strCurrentFolderName = ucase("learned") Or _ 57 | strCurrentFolderName = ucase("search") Or _ 58 | strCurrentFolderName = ucase("SplunkUniversalForwarder") Or _ 59 | strCurrentFolderName = ucase("splunk_httpinput")) Then 60 | ' do nothing 61 | else 62 | ' get the age of the folder 63 | If objSubfolder.DateLastModified > LastDate Or IsEmpty(LastDate) Then 64 | LastFolder = objSubfolder.Name 65 | LastDate = objSubfolder.DateLastModified 66 | End If 67 | end if 68 | Next 69 | 70 | if LastFolder = "" Or isnull(LastFolder) Then 71 | LastFolderAge = -1 72 | Else 73 | LastFolderAge = datediff("d",LastDate,now()) 74 | End if 75 | 76 | if blnDebug = True then 77 | wscript.echo "LastFolder: " & LastFolder 78 | wscript.echo "LastDate: " & LastDate 79 | wscript.echo "LastFolderAge: " & LastFolderAge 80 | end if 81 | 82 | GetLastFolderAge = LastFolderAge 83 | 84 | Set objFSO = Nothing 85 | Set objFolder = Nothing 86 | Set colSubfolders = Nothing 87 | End Function 88 | 89 | Function GetServiceStatus(strServiceName) 90 | dim objWMIService, colListOfServices, objService 91 | 92 | Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") 93 | Set colListOfServices = objWMIService.ExecQuery _ 94 | ("Select * from Win32_Service Where Name = '" & strServiceName & "'") 95 | 96 | GetServiceStatus = False 97 | if isobject(colListOfServices) then 98 | if colListOfServices.Count > 0 then 99 | GetServiceStatus = True 100 | end if 101 | end if 102 | 103 | Set objWMIService = Nothing 104 | Set objService = Nothing 105 | End Function 106 | 107 | Private Function CheckFileFullControl(FolderName,TrusteeDomain,TrusteeName) 108 | CheckFileFullControl = False 109 | 110 | Const FullAccessMask = 2032127, ModifyAccessMask = 1245631, WriteAccessMask = 1180095 111 | Const ROAccessMask = 1179817 112 | 113 | Dim strComputer :: strComputer = "." 114 | 115 | 'Build the path to the folder because it requites 2 backslashes 116 | Dim folderpath :: folderpath = Replace(FolderName, "\", "\\") 117 | 118 | Dim objectpath, wmiFileSecSetting, wmiSecurityDescriptor, RetVal, DACL, wmiAce, Trustee, FoundAccessMask, CustomAccessMask, AccessType 119 | objectpath = "winmgmts:Win32_LogicalFileSecuritySetting.path='" & folderpath & "'" 120 | 121 | 'Get the security set for the object 122 | Set wmiFileSecSetting = GetObject(objectpath) 123 | 124 | 'verify that the get was successful 125 | RetVal = wmiFileSecSetting.GetSecurityDescriptor(wmiSecurityDescriptor) 126 | 'If Err <> 0 Then 127 | 'MsgBox ("GetSecurityDescriptor failed" & vbCrLf & Err.Number & vbCrLf & Err.Description) 128 | 'End 129 | 'End If 130 | 131 | ' Retrieve the DACL array of Win32_ACE objects. 132 | DACL = wmiSecurityDescriptor.DACL 133 | For Each wmiAce In DACL 134 | ' Get Win32_Trustee object from ACE 135 | Set Trustee = wmiAce.Trustee 136 | 137 | FoundAccessMask = False 138 | CustomAccessMask = False 139 | While Not FoundAccessMask And Not CustomAccessMask 140 | If wmiAce.AccessMask = FullAccessMask Then 141 | AccessType = "Full Control" 142 | FoundAccessMask = True 143 | End If 144 | If wmiAce.AccessMask = ModifyAccessMask Then 145 | AccessType = "Modify" 146 | FoundAccessMask = True 147 | End If 148 | If wmiAce.AccessMask = WriteAccessMask Then 149 | AccessType = "Read/Write Control" 150 | FoundAccessMask = True 151 | End If 152 | If wmiAce.AccessMask = ROAccessMask Then 153 | AccessType = "Read Only" 154 | FoundAccessMask = True 155 | Else 156 | CustomAccessMask = True 157 | End If 158 | Wend 159 | 160 | If FoundAccessMask Then 161 | 'wscript.echo AccessType 162 | Else 163 | AccessType = "Custom" 164 | End If 165 | 166 | 'wscript.echo FolderName & "," & Trustee.Domain & "\" & Trustee.Name & "," & AccessType 167 | 168 | if (ucase(TrusteeDomain) = ucase(Trustee.Domain)) and _ 169 | (ucase(TrusteeName) = ucase(Trustee.Name)) and _ 170 | AccessType = "Full Control" then 171 | CheckFileFullControl = True 172 | end if 173 | Next 174 | End Function 175 | 176 | Function RegExMatch(strString,strPattern) 177 | Dim RegEx 178 | RegExMatch=False 179 | 180 | Set RegEx = New RegExp 181 | RegEx.IgnoreCase = True 182 | RegEx.Global=True 183 | RegEx.Pattern=strPattern 184 | 185 | If RegEx.Test(strString) Then RegExMatch=True 186 | 187 | End Function 188 | 189 | Function GetMatch(strString,strPattern) 190 | Dim RegEx,arrMatches, colMatches 191 | Set RegEx = New RegExp 192 | RegEx.IgnoreCase = True 193 | RegEx.Global=True 194 | RegEx.Pattern=strPattern 195 | Set colMatches=RegEx.Execute(strString) 196 | Set GetMatch=colMatches 197 | End Function 198 | 199 | Function ReadLog(strFileName) 200 | Const ForReading = 1 201 | 202 | Dim ObjFSO, objTextFile, strText, arrLines, i, strLine, matches, match, RecordAge 203 | Set objFSO = CreateObject("Scripting.FileSystemObject") 204 | Set objTextFile = objFSO.OpenTextFile(strFileName, ForReading) 205 | strText = objTextFile.ReadAll 206 | objTextFile.Close 207 | 208 | arrLines = Split(strText, vbCrLf) 209 | 210 | ' scan the last 50 lines 211 | For i = ubound(arrLines)-50 to ubound(arrLines) 212 | strLine = arrLines(i) 213 | 214 | ' get the date from the line 215 | Set matches=GetMatch(strLine,"^\d+-\d+-\d+") 216 | For Each match In matches 217 | RecordAge = datediff("d",match.value,now()) 218 | ' show records where date is recent 219 | if RecordAge <= 1 then 220 | ' show records which would prevent phone home or tcp connection 221 | if RegExMatch(strLine,"\s+(WARN|ERROR)\s+(TcpOutput|DC:DeploymentClient|HttpPubSubConnection)") then 222 | wscript.echo strLine 223 | end if 224 | end if 225 | Next 226 | Next 227 | End Function 228 | 229 | Dim strSplunkHome :: strSplunkHome = "C:\Program Files\SplunkUniversalForwarder" 230 | Dim strSplunkApps :: strSplunkApps = strSplunkHome & "\etc\apps" 231 | Dim strSplunkdLog :: strSplunkdLog = strSplunkHome & "\var\log\splunk\splunkd.log" 232 | 233 | Wscript.echo "Splunk home folder exists: " & FolderExists(strSplunkHome) 234 | Wscript.echo "Splunk apps folder exists: " & FolderExists(strSplunkApps) 235 | wscript.echo "Operating system architecture: " & GetSystemArchitecture 236 | wscript.echo "Last Apps Folder Age: " & GetLastFolderAge(strSplunkApps) 237 | wscript.echo "ServiceStatus: " & GetServiceStatus("SplunkForwarder") 238 | wscript.echo "CheckFileFullControl (" & strSplunkHome & "): " & CheckFileFullControl(strSplunkHome,"NT AUTHORITY","SYSTEM") 239 | wscript.echo "CheckFileFullControl (" & strSplunkApps & "): " & CheckFileFullControl(strSplunkApps,"NT AUTHORITY","SYSTEM") 240 | 241 | ReadLog strSplunkdLog 242 | 243 | --------------------------------------------------------------------------------