-
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.
- Loading branch information
Showing
3 changed files
with
319 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
module greg | ||
|
||
go 1.19 | ||
|
||
require ( | ||
github.com/akamensky/argparse v1.4.0 // indirect | ||
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect | ||
github.com/daviddengcn/go-colortext v1.0.0 // indirect | ||
github.com/mattn/go-runewidth v0.0.9 // indirect | ||
github.com/olekukonko/tablewriter v0.0.5 // indirect | ||
golang.org/x/net v0.3.0 // indirect | ||
golang.org/x/text v0.5.0 // 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,18 @@ | ||
github.com/akamensky/argparse v1.4.0 h1:YGzvsTqCvbEZhL8zZu2AiA5nq805NZh75JNj4ajn1xc= | ||
github.com/akamensky/argparse v1.4.0/go.mod h1:S5kwC7IuDcEr5VeXtGPRVZ5o/FdhcMlQz4IZQuw64xA= | ||
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= | ||
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= | ||
github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX8ATG8oKsE= | ||
github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= | ||
github.com/golangplus/bytes v0.0.0-20160111154220-45c989fe5450/go.mod h1:Bk6SMAONeMXrxql8uvOKuAZSu8aM5RUGv+1C6IJaEho= | ||
github.com/golangplus/bytes v1.0.0/go.mod h1:AdRaCFwmc/00ZzELMWb01soso6W1R/++O1XL80yAn+A= | ||
github.com/golangplus/fmt v1.0.0/go.mod h1:zpM0OfbMCjPtd2qkTD/jX2MgiFCqklhSUFyDW44gVQE= | ||
github.com/golangplus/testing v1.0.0/go.mod h1:ZDreixUV3YzhoVraIDyOzHrr76p6NUh6k/pPg/Q3gYA= | ||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= | ||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= | ||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= | ||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= | ||
golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= | ||
golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= | ||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= | ||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= |
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,288 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/csv" | ||
"encoding/xml" | ||
"fmt" | ||
"github.com/akamensky/argparse" | ||
"github.com/common-nighthawk/go-figure" | ||
ct "github.com/daviddengcn/go-colortext" | ||
"github.com/olekukonko/tablewriter" | ||
"golang.org/x/net/html/charset" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"strconv" | ||
) | ||
|
||
var globalpath string | ||
var outputfileneeed bool = false | ||
var outputpath string | ||
var showme bool | ||
var sliceofslices [][]string | ||
|
||
// Task represents the <Task> element in the XML file | ||
type Task struct { | ||
XMLName xml.Name `xml:"Task"` | ||
Version string `xml:"version,attr"` | ||
RegistrationInfo RegistrationInfo `xml:"RegistrationInfo"` | ||
Triggers Triggers `xml:"Triggers"` | ||
Principals Principals `xml:"Principals"` | ||
Settings Settings `xml:"Settings"` | ||
Actions Actions `xml:"Actions"` | ||
} | ||
|
||
// RegistrationInfo represents the <RegistrationInfo> element in the XML file | ||
type RegistrationInfo struct { | ||
Version string `xml:"Version"` | ||
Description string `xml:"Description"` | ||
URI string `xml:"URI"` | ||
} | ||
|
||
// Triggers represents the <Triggers> element in the XML file | ||
type Triggers struct { | ||
LogonTrigger LogonTrigger `xml:"LogonTrigger"` | ||
CalendarTrigger CalendarTrigger `xml:"CalendarTrigger"` | ||
} | ||
|
||
// LogonTrigger represents the <LogonTrigger> element in the XML file | ||
type LogonTrigger struct { | ||
Enabled bool `xml:"Enabled"` | ||
} | ||
|
||
// CalendarTrigger represents the <CalendarTrigger> element in the XML file | ||
type CalendarTrigger struct { | ||
StartBoundary string `xml:"StartBoundary"` | ||
ScheduleByDay ScheduleByDay `xml:"ScheduleByDay"` | ||
} | ||
|
||
// ScheduleByDay represents the <ScheduleByDay> element in the XML file | ||
type ScheduleByDay struct { | ||
DaysInterval int `xml:"DaysInterval"` | ||
} | ||
|
||
type Principals struct { | ||
Principal Principal `xml:"Principal"` | ||
} | ||
|
||
// Principal represents the <Principal> element in the XML file | ||
type Principal struct { | ||
ID string `xml:"id,attr"` | ||
UserID string `xml:"UserId"` | ||
RunLevel string `xml:"RunLevel"` | ||
} | ||
|
||
// Settings represents the <Settings> element in the XML file | ||
type Settings struct { | ||
MultipleInstancesPolicy string `xml:"MultipleInstancesPolicy"` | ||
DisallowStartIfOnBatteries bool `xml:"DisallowStartIfOnBatteries"` | ||
StartWhenAvailable bool `xml:"StartWhenAvailable"` | ||
RunOnlyIfNetworkAvailable bool `xml:"RunOnlyIfNetworkAvailable"` | ||
Enabled bool `xml:"Enabled"` | ||
RunOnlyIfIdle bool `xml:"RunOnlyIfIdle"` | ||
WakeToRun bool `xml:"WakeToRun"` | ||
ExecutionTimeLimit string `xml:"ExecutionTimeLimit"` | ||
} | ||
|
||
type Actions struct { | ||
Context string `xml:"Context,attr"` | ||
Exec Exec `xml:"Exec"` | ||
} | ||
|
||
type Exec struct { | ||
Command string `xml:"Command"` | ||
Arguments string `xml:"Arguments"` | ||
} | ||
|
||
func getalltaskfiles(path string) { | ||
|
||
// Create a slice to hold the file paths | ||
var files []string | ||
|
||
// Recursively search for files | ||
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Check if the current path is a regular file | ||
if info.Mode().IsRegular() { | ||
files = append(files, path) | ||
parseTask(path) | ||
if err != nil { | ||
ct.Foreground(ct.Yellow, true) | ||
fmt.Println("[-] Failed to parse task:", err)c | ||
ct.ResetColor() | ||
} | ||
} | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
ct.Foreground(ct.Red, true) | ||
fmt.Println("[!] Failed to search for files:", err) | ||
ct.ResetColor() | ||
return | ||
} | ||
|
||
} | ||
|
||
func parseTask(path string) { | ||
f16, err := os.Open(path) | ||
if err != nil { | ||
return | ||
} | ||
|
||
task := Task{} | ||
err = DecodeUtf16XML(f16, &task) | ||
if err != nil { | ||
return | ||
} else { | ||
|
||
newslice := []string{task.Version, task.RegistrationInfo.Description, task.RegistrationInfo.URI, task.Triggers.CalendarTrigger.StartBoundary, strconv.Itoa(task.Triggers.CalendarTrigger.ScheduleByDay.DaysInterval), task.Principals.Principal.UserID, task.Principals.Principal.RunLevel, task.Settings.MultipleInstancesPolicy, strconv.FormatBool(task.Settings.DisallowStartIfOnBatteries), strconv.FormatBool(task.Settings.StartWhenAvailable), strconv.FormatBool(task.Settings.RunOnlyIfNetworkAvailable), strconv.FormatBool(task.Settings.Enabled), strconv.FormatBool(task.Settings.RunOnlyIfIdle), strconv.FormatBool(task.Settings.WakeToRun), task.Settings.ExecutionTimeLimit, task.Actions.Exec.Command, task.Actions.Exec.Arguments} | ||
sliceofslices = append(sliceofslices, newslice) | ||
//table := tablewriter.NewWriter(os.Stdout) | ||
//table.SetHeader([]string{"Version", "Description", "URI", "StartBoundary", "DaysInterval", "UserId", "RunLevel", "MultipleInstancesPolicy", "DisallowStartIfOnBatteries", "StartWhenAvailable", "RunOnlyIfNetworkAvailable", "Enabled", "RunOnlyIfIdle", "WakeToRun", "ExecutionTimeLimit", "Command", "Arguments"}) | ||
//table.Append([]string{task.Version, task.RegistrationInfo.Description, task.RegistrationInfo.URI, task.Triggers.CalendarTrigger.StartBoundary, strconv.Itoa(task.Triggers.CalendarTrigger.ScheduleByDay.DaysInterval), task.Principals.Principal.UserID, task.Principals.Principal.RunLevel, task.Settings.MultipleInstancesPolicy, strconv.FormatBool(task.Settings.DisallowStartIfOnBatteries), strconv.FormatBool(task.Settings.StartWhenAvailable), strconv.FormatBool(task.Settings.RunOnlyIfNetworkAvailable), strconv.FormatBool(task.Settings.Enabled), strconv.FormatBool(task.Settings.RunOnlyIfIdle), strconv.FormatBool(task.Settings.WakeToRun), task.Settings.ExecutionTimeLimit, task.Actions.Exec.Command, task.Actions.Exec.Arguments}) | ||
//table.Render() | ||
} | ||
} | ||
|
||
func findTasksDir(basepath string) string { | ||
// Start the search at the root of the file system | ||
|
||
var tasksDir string | ||
|
||
// Recursively search for directories named "Tasks" | ||
err := filepath.Walk(basepath, func(path string, info os.FileInfo, err error) error { | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Check if the current path is a directory and is named "Tasks" | ||
if info.IsDir() && info.Name() == "Tasks" { | ||
tasksDir = path | ||
return nil | ||
} | ||
|
||
return nil | ||
}) | ||
if err != nil { | ||
ct.Foreground(ct.Red, true) | ||
fmt.Println("[!] Failed to search for Tasks directories:", err) | ||
ct.ResetColor() | ||
return "" | ||
} | ||
|
||
return tasksDir | ||
} | ||
|
||
func main() { | ||
ct.Foreground(ct.Green, true) | ||
totesimportant := figure.NewFigure("Greg", "doom", true) | ||
|
||
totesimportant.Print() | ||
fmt.Println("\nThe Taskmaster - Windows Task XML parser\n") | ||
parser := argparse.NewParser("Okonma", "Go Wrapper for Forensic Tools") | ||
i := parser.String("i", "inputdir", &argparse.Options{Required: true, Help: "Input Directory"}) | ||
o := parser.String("o", "outputdir", &argparse.Options{Required: false, Help: "(optional) param. Output Directory"}) | ||
var showOnScreen *bool = parser.Flag("s", "show", &argparse.Options{Required: false, Help: "Dont Output to a file - show me the results onscreen"}) | ||
ct.ResetColor() | ||
err := parser.Parse(os.Args) | ||
if err != nil { | ||
fmt.Print(parser.Usage(err)) | ||
|
||
} | ||
|
||
var inputset bool = false | ||
|
||
if *i != "" { | ||
globalpath = *i | ||
inputset = true | ||
} | ||
|
||
if *i == "" { | ||
os.Exit(1) | ||
} | ||
|
||
if *o != "" { | ||
outputpath = *o | ||
outputfileneeed = true | ||
} | ||
|
||
if *showOnScreen { | ||
showme = true | ||
} | ||
|
||
if inputset && showme == false && showme == false { | ||
ct.Background(ct.Yellow, true) | ||
fmt.Println("[-] You need to specific some action to take") | ||
ct.ResetColor() | ||
fmt.Print(parser.Usage(err)) | ||
os.Exit(1) | ||
} | ||
|
||
if inputset && (showme == true || outputfileneeed == true) { | ||
getalltaskfiles(*i) | ||
if showme == true { | ||
table := tablewriter.NewWriter(os.Stdout) | ||
table.SetHeader([]string{"Version", "Description", "URI", "StartBoundary", "DaysInterval", "UserId", "RunLevel", "MultipleInstancesPolicy", "DisallowStartIfOnBatteries", "StartWhenAvailable", "RunOnlyIfNetworkAvailable", "Enabled", "RunOnlyIfIdle", "WakeToRun", "ExecutionTimeLimit", "Command", "Arguments"}) | ||
for _, value := range sliceofslices { | ||
table.Append(value) | ||
} | ||
table.Render() | ||
} | ||
if outputfileneeed == true { | ||
var newslice [][]string | ||
var tempslice []string | ||
tempslice = []string{"Version", "Description", "URI", "StartBoundary", "DaysInterval", "UserId", "RunLevel", "MultipleInstancesPolicy", "DisallowStartIfOnBatteries", "StartWhenAvailable", "RunOnlyIfNetworkAvailable", "Enabled", "RunOnlyIfIdle", "WakeToRun", "ExecutionTimeLimit", "Command", "Arguments"} | ||
newslice = append(newslice, tempslice) | ||
for _, value := range sliceofslices { | ||
newslice = append(newslice, value) | ||
} | ||
|
||
f, err := os.Create(outputpath) | ||
if err != nil { | ||
panic(err) | ||
} | ||
defer f.Close() | ||
|
||
// Create a new CSV writer. | ||
w := csv.NewWriter(f) | ||
|
||
// Iterate over the slice of slices and write each row to the CSV writer. | ||
for _, v := range newslice { | ||
if err := w.Write(v); err != nil { | ||
panic(err) | ||
} | ||
|
||
} | ||
ct.Foreground(ct.Green, true) | ||
fmt.Println("[+] Output written to " + outputpath) | ||
ct.ResetColor() | ||
// Flush the writer to ensure that all data is written to the file. | ||
w.Flush() | ||
} | ||
|
||
} | ||
} | ||
|
||
func DecodeUtf16XML(r io.Reader, v interface{}) (err error) { | ||
//kudos to the below for really helping me out | ||
// https://www.tipitaka.org/romn/cscd/vin01m.mul.toc.xml | ||
// The Tipiṭaka XML is encoded in UTF-16 | ||
// Google search: golang xml utf-16 | ||
// https://stackoverflow.com/questions/6002619/unmarshal-an-iso-8859-1-xml-input-in-go | ||
// https://groups.google.com/forum/#!topic/golang-nuts/tXcECEKC2rs | ||
nr, err := charset.NewReader(r, "utf-16") | ||
if err != nil { | ||
return | ||
} | ||
decoder := xml.NewDecoder(nr) | ||
decoder.CharsetReader = BypassReader | ||
err = decoder.Decode(v) | ||
return | ||
} | ||
func BypassReader(label string, input io.Reader) (io.Reader, error) { | ||
return input, nil | ||
} |