Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 180 additions & 20 deletions Modules/Log/Get-LogWinEvent.ps1
Original file line number Diff line number Diff line change
@@ -1,30 +1,190 @@
<#
<#
.SYNOPSIS
Get-LogWinEvent
.PARAMETER LogName
A required parameter, that names the event log to acquire data from.
To see a list of common lognames run:
Get-WinEvent -ListLog | Select LogName

When used with Kansa.ps1, parameters must be positional. Named params
are not supported.
Get-LogWinEvent
.PARAMETER Params
A required parameter. I'll call this a pseudo parameter. This is due to the fact that you, the user, is really providing 3 parameters
worth of information that later gets parsed out in to separate parameters.

The format for this parameter follows this syntax: log name(s) [separated by a pipe if you want to specify more than one]-days ago-event IDs [separated
by a pipe if you want to specify more than one].

Note: when used with Kansa.ps1, parameters must be positional. Named params are not supported.

.EXAMPLE
Single log, no additional filtering:
Get-LogWinEvent.ps1 Security

.EXAMPLE
Get-LogWinEvent.ps1 Security
Multiple logs, over the past 7 days, with specified Event IDs
Get-LogWinEvent.ps1 Security|System-7-4625|4634|4798|267|507
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not trying to nitpick, but it looks like the event ids here are mostly from the security event log. I know they are just examples, but examples would be better if they were correct. If I'm wrong, my apologies.

I have not tested this syntax yet. Have you? How does the code determine that the pipe separated values are event logs v event ids?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...it looks like the event ids here are mostly from the security event log...

No - you're correct. Those event IDs are mostly from the security log - I was pulling IDs from memory when writing the example, and those are just happen to be ones I query for the most.

I have not tested this syntax yet. Have you?

I have tested the syntax and it works as expected. I specify in the comments that the "params" parameter follows an exacting format: "The format for this parameter follows this syntax: log name(s) [separated by a pipe if you want to specify more than one]-days ago-event IDs [separated by a pipe if you want to specify more than one]."

How does the code determine that the pipe separated values are event logs v event ids?

There are no explicit checks to ensure proper ordering of data passed to the "params" parameter. Params is explicitly cast as a string in its declaration statement and then immediately split in the appropriately type cast variables. Beyond that, there is no checking that a given log name is valid (that, I think would be rather un do-able given the number of event log collections available), or that the data passed in is entered in the correct order.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So if a user passes in multiple logs (e.g. Security, System, etc.) and a string of event IDs, does the code look in each log for all the given event IDs?

.EX
.NOTES
When passing specific modules with parameters via Kansa.ps1's
-ModulePath parameter, be sure to quote the entire string, like shown
here:
.\kansa.ps1 -Target localhost -ModulePath ".\Modules\Log\Get-LogWinEvent.ps1 Security"
When passing specific modules with parameters via Kansa.ps1's -ModulePath parameter, be sure to quote the entire string, like shown
here:
.\kansa.ps1 -Target localhost -ModulePath ".\Modules\Log\Get-LogWinEvent.ps1 Security"

Thanks to Jeff Hicks for providng the Convert-EventLogRecord function, which allows for enhanced event log collections!
Original blog post found here: https://jdhitsolutions.com/blog/powershell/7193/better-event-logs-with-powershell/

Next line is required by Kansa for proper handling of this script's
output.
OUTPUT TSV
#>

[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=0)]
[String]$LogName
)
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True,Position=0)]
[String]$Params
)

$unparsedLogSelection = ($params -split "-")[0]
$daysAgo = ($params -split "-")[1]
$unparsedEventIDs = ($params -split "-")[2]

# At a minimum, the user needs to specify at least one log name. If a valid log name is not specified,
# The script fails.
[String[]]$LogName = $unparsedLogSelection -split "\|"

# Add a check to see if $daysAgo is populated - technically, it is an optional parameter.
# If the user declined to specify, set $daysAgo to $null and search through all the logs.
if ($daysAgo -eq $null) {
$daysAgo = $null
}
else {
[int]$daysAgo = $daysAgo
}

# Add a check to see if we need to populate $EventIDs. It too is an optional patameter.
# If the user declined to specify a list of event ids, set the parameter to $null and get all of the
# event ids for the specified log(s).
if ($unparsedEventIDs -eq $null) {
$EventIDs = $null
}
else {
[String[]]$EventIDs = $unparsedEventIDs -split "\|"
}

Function Convert-EventLogRecord {

[cmdletbinding()]
[alias("clr")]

Param(
[Parameter(Position = 0, Mandatory, ValueFromPipeline)]
[ValidateNotNullorEmpty()]
[System.Diagnostics.Eventing.Reader.EventLogRecord[]]$LogRecord
)

Begin {
Write-Verbose "[BEGIN ] Starting: $($MyInvocation.Mycommand)"
} #begin

Process {
foreach ($record in $LogRecord) {
Write-Verbose "[PROCESS] Processing event id $($record.ID) from $($record.logname) log on $($record.machinename)"
Write-Verbose "[PROCESS] Creating XML data"
[xml]$r = $record.ToXml()

$h = [ordered]@{
LogName = $record.LogName
RecordType = $record.LevelDisplayName
TimeCreated = $record.TimeCreated
ID = $record.Id
}

if ($r.Event.EventData.Data.Count -gt 0) {
Write-Verbose "[PROCESS] Parsing event data"
if ($r.Event.EventData.Data -is [array]) {
<#
I only want to enumerate with the For loop if the data is an array of objects
If the data is just a single string like Foo, then when using the For loop,
the data value will be the F and not the complete string, Foo.
#>
for ($i = 0; $i -lt $r.Event.EventData.Data.count; $i++) {

$data = $r.Event.EventData.data[$i]
#test if there is structured data or just text
if ($data.name) {
$Name = $data.name
$Value = $data.'#text'
}
else {
Write-Verbose "[PROCESS] No data property name detected"
$Name = "RawProperties"
#data will likely be an array of strings
[string[]]$Value = $data
}

if ($h.Contains("RawProperties")) {
Write-Verbose "[PROCESS] Appending to RawProperties"
$h.RawProperties += $value
}
else {
Write-Verbose "[PROCESS] Adding $name"
$h.add($name, $Value)
}
} #for data
} #data is an array
else {
$data = $r.Event.EventData.data
if ($data.name) {
$Name = $data.name
$Value = $data.'#text'
}
else {
Write-Verbose "[PROCESS] No data property name detected"
$Name = "RawProperties"
#data will likely be an array of strings
[string[]]$Value = $data
}

if ($h.Contains("RawProperties")) {
Write-Verbose "[PROCESS] Appending to RawProperties"
$h.RawProperties += $value
}
else {
Write-Verbose "[PROCESS] Adding $name"
$h.add($name, $Value)
}
}
} #if data
else {
Write-Verbose "[PROCESS] No event data to process"
}

$h.Add("Message", $record.Message)
$h.Add("Keywords", $record.KeywordsDisplayNames)
$h.Add("Source", $record.ProviderName)
$h.Add("Computername", $record.MachineName)

Write-Verbose "[PROCESS] Creating custom object"
New-Object -TypeName PSObject -Property $h
} #foreach record
} #process

End {
Write-Verbose "[END ] Ending: $($MyInvocation.Mycommand)"
} #end
} #end Convert-EventLogRecord

If ($DaysAgo -eq $null) {
$StartDate = $($(Get-WinEvent -LogName Security -Oldest -MaxEvents 1).TimeCreated)
$EndDate = Get-Date
$Span = $(New-TimeSpan -Start $StartDate -End $EndDate).Days
} # end If
Else {$Span = $DaysAgo} # end else

$StartTime = (Get-Date).AddDays(-$Span)

Get-WinEvent -LogName $LogName
if ($EventIDs.Count -eq 0) {
ForEach ($log in $LogName) {
Get-WinEvent -FilterHashtable @{ Logname=$LogName; StartTime=$StartTime} -EA SilentlyContinue | Convert-EventLogRecord
} # end ForEach ($log in LogName)
} # end If
else {
ForEach ($log in $LogName) {
ForEach ($id in $EventIDs) {
Get-WinEvent -FilterHashtable @{ Logname=$LogName; StartTime=$StartTime; ID=$id} -EA SilentlyContinue | Convert-EventLogRecord
} # end inner ForEach ($id in $EventIDs)
} # end outter ForEach ($log in LogName)
} # end else
77 changes: 36 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,82 +1,77 @@
Kansa
=====

A modular incident response framework in Powershell. It's been tested in PSv2 / .NET 2 and
later and works mostly without issue.
A modular incident response framework in Powershell. It's been tested in PSv2 / .NET 2 and later and works mostly without issue.

But really, upgrade to PSv3 or later. Be happy.

More info:
http://trustedsignal.blogspot.com/search/label/Kansa
http://www.powershellmagazine.com/2014/07/18/kansa-a-powershell-based-incident-response-framework/
http://www.powershellmagazine.com/2014/07/18/kansa-a-powershell-based-incident-response-framework/

## What does it do?
It uses Powershell Remoting to run user contributed, ahem, user contri-
buted modules across hosts in an enterprise to collect data for use
during incident response, breach hunts, or for building an environmental
baseline.
It uses Powershell Remoting to run user contributed, ahem, user contributed modules across hosts in an enterprise to collect data for use during incident response, breach hunts, or for building an environmental baseline.

## How do you use it?
Here's a very simple command line example you can run on your own local
host.
Here's a very simple command line example you can run on your own local host.

1. After downloading the project and unzipping it, you'll likely need
to "unblock" the ps1 files. The easiest way to do this if you're using
Powershell v3 or later is to cd to the directory where Kansa resides
and do:
1. After downloading the project and unzipping it, you'll likely need to "unblock" the ps1 files. The easiest way to do this if you're using Powershell v3 or later is to cd to the directory where Kansa resides and do:
```Powershell
ls -r *.ps1 | Unblock-File
```
1. Ensure that you check your execution policies with PowerShell. Check [Using the Set-ExecutionPolicy Cmdlet](https://technet.microsoft.com/en-us/library/ee176961.aspx) for information on how to do so within your environment.
```
Set-ExecutionPolicy AllSigned | RemoteSigned | Unrestricted
```
1. If you're not running PS v3 or later, [Sysinternal's Streams utility](https://technet.microsoft.com/en-us/sysinternals/streams.aspx) can
be used to remove the alternate data streams that Powershell uses to
determine if files came from the Internet. Once you've removed those
ADSes, you'll be able to run the scripts without issue.
1. If you're not running PS v3 or later, [Sysinternal's Streams utility](https://technet.microsoft.com/en-us/sysinternals/streams.aspx) can be used to remove the alternate data streams that Powershell uses to determine if files came from the Internet. Once you've removed those ADSes, you'll be able to run the scripts without issue.
```
c:\ streams -sd <Kansa directory>
```

I've not run into any issues running the downloaded scripts via Windows
Remote Management / Powershell Remoting through Kansa, so you shouldn't
have to do anything if you want to run the scripts via remoting.
I've not run into any issues running the downloaded scripts via Windows Remote Management / Powershell Remoting through Kansa, so you shouldn't have to do anything if you want to run the scripts via remoting.

2. Open an elevated Powershell Prompt (Right-click Run As Administrator)
2. Open an elevated Powershell Prompt (Right-click Run As Administrator)

3. At the command prompt, enter:
3. At the command prompt, enter:
```Powershell
.\kansa.ps1 -Target $env:COMPUTERNAME -ModulePath .\Modules -Verbose
```
The script should start collecting data or you may see an error about
not having Windows Remote Management enabled. If so, do a little
searching online, it's easy to turn on. Turn it on and try again. When
it finishes running, you'll have a new Output_timestamp subdirectory,
with subdirectories for data collected by each module. You can cd into
those subdirectories and checkout the data. There are some analysis
scripts in the Analysis directory, but many of those won't make sense
on a collection of data from a single host. Kansa was written for
collection and analysis of data from dozens, hundreds, thousands, tens
of thousands of systems.
The script should start collecting data or you may see an error about not having Windows Remote Management enabled. If so, do a little searching online, it's easy to turn on. Turn it on and try again. When it finishes running, you'll have a new Output_timestamp subdirectory, with subdirectories for data collected by each module. You can cd into those subdirectories and checkout the data. There are some analysis scripts in the Analysis directory, but many of those won't make sense on a collection of data from a single host. Kansa was written for collection and analysis of data from dozens, hundreds, thousands, tens of thousands of systems.

## Running Modules Standalone
Kansa modules can be run as standalone utilities outside of the Kansa
framework. Why might you want to do this? Consider netstat -naob, the
output of the command line utility is ugly and doesn't easily lend
itself to analysis. Running
Kansa modules can be run as standalone utilities outside of the Kansa framework. Why might you want to do this? Consider netstat -naob, the output of the command line utility is ugly and doesn't easily lend itself to analysis. Running
```Powershell
Modules\Net\Get-Netstat.ps1
```
as a standalone script will call netstat -naob, but it will return
Powershell objects in an easy to read, easy to analyze format. You can
easily convert its output to CSV, TSV or XML using normal Powershell
as a standalone script will call netstat -naob, but it will return Powershell objects in an easy to read, easy to analyze format. You can easily convert its output to CSV, TSV or XML using normal Powershell
cmdlets. Here's an example:
```Powershell
.\Get-Netstat.ps1 | ConvertTo-CSV -Delimiter "`t" -NoTypeInformation | % { $_ -replace "`"" } | Set-Content netstat.tsv
```
the result of the above will be a file called netstat.tsv containing
unquoted, tab separate values for netstat -naob's ouput.
the result of the above will be a file called netstat.tsv containing unquoted, tab separate values for netstat -naob's ouput.

## Expanded use cases
Over the past few years, additional output types have been added to Kansa, and several modules have been added or expanded.

### Sending output to a log aggregator
It is now possible to configure Kansa to send collected data to Splunk or GrayLog for analysis and retention. In order to do so, configure the proper parameters in ```logging.conf```.
```Powershell
.\kansa.ps1 -Target $env:COMPUTERNAME -Authentication Default -OutputFormat SPLUNK
```
A quick tutorial on setting up Splunk to receive data from Kansa is available here: https://powerhunt.org/enterprise-dfir-on-a-budget/

### Enhanced Windows Event Log retrieval
```.\Modules\Log\Get-LogWinEvent``` has been modified to allow the user to specify multiple parameters to allow for greater filtering, as well as enhanced parsing of the message body, which is handy if the destination is a log aggregator.

Due to the way in which Kansa parses module names and accompanying parameters, the parameter passed to ```Get-LogWinEvent``` encodes 3 data points in to one pseudo parameter - the log name(s), how many days back the user wishes to search, and the event IDs the user wishes to filter on. **Only the log name(s) are mandatory***

In Modules.conf, you can specify the parameter(s) for Get-LogWinEvent as such:
```Log\Get-LogWinEvent.ps1 Security|Application|System-7-4624|1003|1014```

Or, if you wish to call ```Get-LogWinEvent``` as a stand alone module:
```Powershell
.\kansa.ps1 -Target $env:COMPUTERNAME -Authentication Default -ModulePath ".\Modules\Log\Get-LogWinEvent.ps1 Security-7-4624"
```

## Caveats:
Powershell relies on the Windows API. Your adversary may use subterfuge.*
Expand Down