Skip to content

Instantly share code, notes, and snippets.

@zaneGittins
Last active May 26, 2023 15:37
Show Gist options
  • Save zaneGittins/65b029e1c77903f9f913b3368bd4dbe6 to your computer and use it in GitHub Desktop.
Save zaneGittins/65b029e1c77903f9f913b3368bd4dbe6 to your computer and use it in GitHub Desktop.
chainsaw2timesketch.go
package main
import (
"bufio"
"encoding/json"
"fmt"
"log"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
)
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
func ToSnakeCase(str string) string {
snake := matchFirstCap.ReplaceAllString(str, "${1}_${2}")
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
return strings.ToLower(snake)
}
type EventLog struct {
Event struct {
EventData map[string]interface{} `json:"EventData"`
System struct {
Channel string `json:"Channel"`
Computer string `json:"Computer"`
Correlation any `json:"Correlation"`
EventID int `json:"EventID"`
EventRecordID int `json:"EventRecordID"`
Level int `json:"Level"`
TimeCreatedAttributes struct {
SystemTime time.Time `json:"SystemTime"`
} `json:"TimeCreated_attributes"`
} `json:"System"`
} `json:"Event"`
}
func (b EventLog) MarshalJSON() ([]byte, error) {
result := make(map[string]string)
result["message"] = b.Event.System.Channel
result["timestamp_desc"] = strconv.Itoa(b.Event.System.EventRecordID)
result["datetime"] = b.Event.System.TimeCreatedAttributes.SystemTime.Format(time.RFC3339Nano)
result["computer_name"] = b.Event.System.Computer
result["event_id"] = strconv.Itoa(b.Event.System.EventID)
result["data_type"] = "windows:evtx:chainsaw"
for k, v := range b.Event.EventData {
key := ToSnakeCase(fmt.Sprintf("%v", k))
val := fmt.Sprintf("%v", v)
result[key] = val
}
done, err := json.Marshal(result)
return done, err
}
type ChainsawHandler struct{}
func (c *ChainsawHandler) isFile(filename string) bool {
info, err := os.Stat(filename)
return err == nil && !info.IsDir()
}
func (c *ChainsawHandler) processEvtxFiles() {
files, err := filepath.Glob("*.evtx")
if err != nil {
log.Fatalf("Failed to get evtx files: %s", err)
}
for _, file := range files {
if !c.isFile(file) {
continue
}
c.runChainsawDump(file)
}
}
func (c *ChainsawHandler) runChainsawDump(filename string) error {
cmd := exec.Command("chainsaw", "dump", filename, "--jsonl")
output, err := cmd.StdoutPipe()
if err != nil {
log.Panicf("%s\n", err)
}
done := make(chan struct{})
scanner := bufio.NewScanner(output)
outputFile := filename + ".jsonl"
go func() {
// Read line by line and process it
for scanner.Scan() {
line := scanner.Text()
c.processLine(outputFile, line)
}
// We're all done, unblock the channel
done <- struct{}{}
}()
// Start the command and check for errors
err = cmd.Start()
if err != nil {
fmt.Println(err)
}
// Wait for all output to be processed
<-done
// Wait for the command to finish
err = cmd.Wait()
if err != nil {
fmt.Println(err)
}
return nil
}
func (c *ChainsawHandler) processLine(filename string, line string) error {
winlog := EventLog{}
err := json.Unmarshal([]byte(line), &winlog)
if err != nil {
fmt.Println(err)
}
a, _ := winlog.MarshalJSON()
c.writeToFile(filename, string(a))
return nil
}
func (c *ChainsawHandler) writeToFile(filename string, data string) error {
f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
if err != nil {
panic(err)
}
defer f.Close()
if _, err = f.WriteString(data + "\n"); err != nil {
panic(err)
}
return nil
}
func main() {
chainsawHandler := ChainsawHandler{}
chainsawHandler.processEvtxFiles()
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment