-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add code structure to parse Sigma and YARA rules
Added functionality to parse Sigma and YARA rules and extract rule names and tags from them.
- Loading branch information
1 parent
14d9c30
commit 31c8197
Showing
16 changed files
with
1,152 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module github.com/mtnmunuklu/analyze-tags | ||
|
||
go 1.20 | ||
|
||
require ( | ||
github.com/VirusTotal/gyp v0.9.0 | ||
github.com/stretchr/testify v1.9.0 | ||
gopkg.in/yaml.v3 v3.0.1 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/golang/protobuf v1.5.0 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
google.golang.org/protobuf v1.27.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
github.com/VirusTotal/gyp v0.9.0 h1:jhOBl93jfStmAcKLa/EcTmdPng5bn5kvJJZqQqJ5R4g= | ||
github.com/VirusTotal/gyp v0.9.0/go.mod h1:nmcW15dQ1657PmMcG9X/EZmp6rTQsyo9g8r6Cz1/AHc= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= | ||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= | ||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= | ||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | ||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= | ||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= | ||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | ||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"flag" | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/mtnmunuklu/analyze-tags/sigma" | ||
"github.com/mtnmunuklu/analyze-tags/yara" | ||
) | ||
|
||
var ( | ||
filePath string | ||
fileContent string | ||
showHelp bool | ||
outputJSON bool | ||
outputPath string | ||
version bool | ||
useSigma bool | ||
useYara bool | ||
) | ||
|
||
// Set up the command-line flags | ||
func init() { | ||
flag.StringVar(&filePath, "filepath", "", "Name or path of the file or directory to read") | ||
flag.StringVar(&fileContent, "filecontent", "", "Base64-encoded content of the file or directory to read") | ||
flag.BoolVar(&showHelp, "help", false, "Show usage") | ||
flag.BoolVar(&outputJSON, "json", false, "Output results in JSON format") | ||
flag.StringVar(&outputPath, "output", "", "Output directory for writing files") | ||
flag.BoolVar(&version, "version", false, "Show version information") | ||
flag.BoolVar(&useSigma, "sigma", false, "Use Sigma rules") | ||
flag.BoolVar(&useYara, "yara", false, "Use Yara rules") | ||
flag.Parse() | ||
|
||
// If the version flag is provided, print version information and exit | ||
if version { | ||
fmt.Println("Analyze Tags version 1.0.0") | ||
os.Exit(1) | ||
} | ||
|
||
// If the help flag is provided, print usage information and exit | ||
if showHelp { | ||
printUsage() | ||
os.Exit(1) | ||
} | ||
|
||
// Check if both filecontent and configcontent are provided | ||
if filePath == "" && fileContent == "" { | ||
fmt.Println("Please provide either file paths or file contents.") | ||
printUsage() | ||
os.Exit(1) | ||
} | ||
} | ||
|
||
func printUsage() { | ||
fmt.Println("Usage: analyze-tags -sigma/-yara -filepath <path> [flags]") | ||
fmt.Println("Flags:") | ||
flag.PrintDefaults() | ||
fmt.Println("Example:") | ||
fmt.Println(" analyze-tags -sigma/-yara -filepath /path/to/file") | ||
} | ||
|
||
func formatJSONResult(identifier string, tags []string) []byte { | ||
// Define a struct type named JSONResult to represent the JSON output fields. | ||
type JSONResult struct { | ||
Name string `json:"Name"` | ||
Tags []string `json:"Tags"` | ||
} | ||
|
||
// Create an instance of the JSONResult struct. | ||
jsonResult := JSONResult{ | ||
Name: identifier, | ||
Tags: tags, | ||
} | ||
|
||
// Marshal the JSONResult struct into JSON data. | ||
jsonData, err := json.MarshalIndent(jsonResult, "", " ") | ||
if err != nil { | ||
fmt.Println("Error encoding JSON:", err) | ||
return nil | ||
} | ||
|
||
return jsonData | ||
} | ||
|
||
func main() { | ||
// Ensure either Sigma or Yara flag is provided | ||
if !useSigma && !useYara { | ||
fmt.Println("Please provide either --sigma or --yara flag to specify the type of rules.") | ||
printUsage() | ||
os.Exit(1) | ||
} | ||
|
||
// Read the contents of the file(s) specified by the filepath flag or filecontent flag | ||
fileContents := make(map[string][]byte) | ||
|
||
// Check if file paths are provided | ||
if filePath != "" { | ||
// Check if the filepath is a directory | ||
fileInfo, err := os.Stat(filePath) | ||
if err != nil { | ||
fmt.Println("Error getting file/directory info:", err) | ||
return | ||
} | ||
|
||
if fileInfo.IsDir() { | ||
// filePath is a directory, so walk the directory to read all the files inside it | ||
filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
fmt.Println("Error accessing file:", err) | ||
return nil | ||
} | ||
if !info.IsDir() { | ||
// read file content | ||
content, err := os.ReadFile(path) | ||
if err != nil { | ||
fmt.Println("Error reading file:", err) | ||
return nil | ||
} | ||
fileContents[path] = content | ||
} | ||
return nil | ||
}) | ||
} else { | ||
// filePath is a file, so read its contents | ||
fileContents[filePath], err = os.ReadFile(filePath) | ||
if err != nil { | ||
fmt.Println("Error reading file:", err) | ||
return | ||
} | ||
} | ||
} else if fileContent != "" { | ||
// Check if the filecontent is a directory | ||
lines := strings.Split(fileContent, "\n") | ||
if len(lines) > 1 { | ||
// fileContent is a directory, so read all lines as separate files | ||
for _, line := range lines { | ||
// decode base64 content | ||
decodedContent, err := base64.StdEncoding.DecodeString(line) | ||
if err != nil { | ||
fmt.Println("Error decoding base64 content:", err) | ||
return | ||
} | ||
fileContents[line] = decodedContent | ||
} | ||
} else { | ||
// fileContent is a file, so read its content | ||
// decode base64 content | ||
decodedContent, err := base64.StdEncoding.DecodeString(fileContent) | ||
if err != nil { | ||
fmt.Println("Error decoding base64 content:", err) | ||
return | ||
} | ||
fileContents["filecontent"] = decodedContent | ||
} | ||
} | ||
|
||
// Loop over each file and parse its contents as a Sigma rule | ||
for _, fileContent := range fileContents { | ||
if useSigma { | ||
sigmaRule, err := sigma.ParseRule(fileContent) | ||
if err != nil { | ||
fmt.Println("Error parsing rule:", err) | ||
continue | ||
} | ||
|
||
var output string | ||
|
||
// Print the results of the query | ||
if outputJSON { | ||
jsonResult := formatJSONResult(sigmaRule.Title, sigmaRule.Tags) | ||
output = string(jsonResult) | ||
} else { | ||
output = "Name: " + sigmaRule.Title + " Tags: " + strings.Join(sigmaRule.Tags, " ") | ||
} | ||
|
||
// Check if outputPath is provided | ||
if outputPath != "" { | ||
// Create the output file path using the Name field from the rule | ||
outputFilePath := filepath.Join(outputPath, fmt.Sprintf("%s.json", sigmaRule.Title)) | ||
|
||
// Write the output string to the output file | ||
err := os.WriteFile(outputFilePath, []byte(output), 0644) | ||
if err != nil { | ||
fmt.Println("Error writing output to file:", err) | ||
continue | ||
} | ||
|
||
fmt.Printf("Output for rule '%s' written to file: %s\n", sigmaRule.Title, outputFilePath) | ||
} else { | ||
fmt.Printf("%s", output) | ||
} | ||
} else if useYara { | ||
yaraRuleSet, err := yara.ParseByte(fileContent) | ||
if err != nil { | ||
fmt.Println("Error parsing rule:", err) | ||
continue | ||
} | ||
|
||
for _, yaraRule := range yaraRuleSet.Rules { | ||
|
||
var output string | ||
|
||
// Print the results of the query | ||
if outputJSON { | ||
jsonResult := formatJSONResult(yaraRule.Identifier, yaraRule.Tags) | ||
output = string(jsonResult) | ||
} else { | ||
output = "Name: " + yaraRule.Identifier + " Tags: " + strings.Join(yaraRule.Tags, " ") | ||
} | ||
|
||
// Check if outputPath is provided | ||
if outputPath != "" { | ||
// Create the output file path using the Name field from the rule | ||
outputFilePath := filepath.Join(outputPath, fmt.Sprintf("%s.json", yaraRule.Identifier)) | ||
|
||
// Write the output string to the output file | ||
err := os.WriteFile(outputFilePath, []byte(output), 0644) | ||
if err != nil { | ||
fmt.Println("Error writing output to file:", err) | ||
continue | ||
} | ||
|
||
fmt.Printf("Output for rule '%s' written to file: %s\n", yaraRule.Identifier, outputFilePath) | ||
} else { | ||
fmt.Printf("%s", output) | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"Name": "Chafer Activity", | ||
"Tags": [ | ||
"attack.persistence", | ||
"attack.g0049", | ||
"attack.t1053.005", | ||
"attack.s0111", | ||
"attack.t1543.003", | ||
"attack.defense_evasion", | ||
"attack.t1112", | ||
"attack.command_and_control", | ||
"attack.t1071.004" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
title: Chafer Activity | ||
id: ce6e34ca-966d-41c9-8d93-5b06c8b97a06 | ||
#related: | ||
# - id: 53ba33fd-3a50-4468-a5ef-c583635cfa92 | ||
# type: derived | ||
description: Detects Chafer activity attributed to OilRig as reported in Nyotron report in March 2018 | ||
status: test | ||
references: | ||
- https://nyotron.com/nyotron-discovers-next-generation-oilrig-attacks/ | ||
tags: | ||
- attack.persistence | ||
- attack.g0049 | ||
- attack.t1053.005 | ||
- attack.s0111 | ||
- attack.t1543.003 | ||
- attack.defense_evasion | ||
- attack.t1112 | ||
- attack.command_and_control | ||
- attack.t1071.004 | ||
date: 2018/03/23 | ||
modified: 2021/09/19 | ||
author: Florian Roth, Markus Neis, Jonhnathan Ribeiro, Daniil Yugoslavskiy, oscd.community | ||
logsource: | ||
category: process_creation | ||
product: windows | ||
detection: | ||
selection_process0: | ||
CommandLine|contains: '\Service.exe' | ||
CommandLine|endswith: | ||
- 'i' | ||
- 'u' | ||
selection_process1: | ||
- CommandLine|endswith: '\microsoft\Taskbar\autoit3.exe' | ||
- CommandLine|startswith: 'C:\wsc.exe' | ||
selection_process2: | ||
Image|contains: '\Windows\Temp\DB\' | ||
Image|endswith: '.exe' | ||
selection_process3: | ||
CommandLine|contains|all: | ||
- '\nslookup.exe' | ||
- '-q=TXT' | ||
ParentImage|contains: '\Autoit' | ||
condition: 1 of selection* | ||
falsepositives: | ||
- Unknown | ||
level: high |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Copied from https://github.com/Neo23x0/sigma/blob/0603264a09fc61b0eaeac7620aab566da61ca9b8/rules/proxy/proxy_apt40.yml | ||
# under license https://github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md | ||
title: APT40 Dropbox Tool User Agent | ||
id: 5ba715b6-71b7-44fd-8245-f66893e81b3d | ||
status: experimental | ||
description: Detects suspicious user agent string of APT40 Dropbox tool | ||
references: | ||
- Internal research from Florian Roth | ||
author: Thomas Patzke | ||
date: 2019/11/12 | ||
modified: 2020/09/02 | ||
tags: | ||
- attack.command_and_control | ||
- attack.t1071.001 | ||
- attack.t1043 # an old one | ||
- attack.exfiltration | ||
- attack.t1567.002 | ||
- attack.t1048 # an old one | ||
logsource: | ||
category: proxy | ||
detection: | ||
selection: | ||
c-useragent: 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36' | ||
r-dns: 'api.dropbox.com' | ||
condition: selection | ||
fields: | ||
- c-ip | ||
- c-uri | ||
falsepositives: | ||
- Old browsers | ||
level: high |
Oops, something went wrong.