From f6df01b31f9a235e098db7077ecbbc2c316d74f5 Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Mon, 18 Mar 2024 15:19:28 +0100 Subject: [PATCH 1/7] Added utility functions. Added nuclei install --- pkg/utils/utils.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index a6f7e90..4a3a5cf 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -1,6 +1,7 @@ package utils import ( + "bufio" "bytes" "encoding/csv" "encoding/json" @@ -9,6 +10,7 @@ import ( "net/http" "os" "os/exec" + "strings" ) func RemoveDuplicates(slice []string) []string { @@ -192,6 +194,7 @@ func InstallTools() { "httpx": "github.com/projectdiscovery/httpx/cmd/httpx@latest", "oam_subs": "github.com/owasp-amass/oam-tools/cmd/oam_subs@master", "subfinder": "github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest", + "nuclei": "go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest", } { if !checkTool(name) { installGoTool(name, path) @@ -223,3 +226,25 @@ func installGoTool(name string, path string) { log.Printf("Successfully installed the package: %s", packagePath) } + +func GetInput(prompt string, r *bufio.Reader) (string, error) { + fmt.Print(prompt) + input, err := r.ReadString('\n') + if err != nil { + panic(err) + } + + return strings.TrimSpace(input), err +} + +func CheckJq() { + //check if jq is installed, if not, abort the scan + cmd := exec.Command("jq", "--version") + err := cmd.Run() + if err != nil { + print("Jq is not installed, nuclei scan can't be run.\n\n") + panic(err) + } else { + return + } +} From e02c2896f36beedfeb29421a49ba05126da52272 Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Mon, 18 Mar 2024 15:27:17 +0100 Subject: [PATCH 2/7] Added the nuclei pkg and nuclei.go --- pkg/passive/nuclei/nuclei.go | 105 +++++++++++++++++++++++++++++++++++ pkg/utils/utils.go | 2 +- 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 pkg/passive/nuclei/nuclei.go diff --git a/pkg/passive/nuclei/nuclei.go b/pkg/passive/nuclei/nuclei.go new file mode 100644 index 0000000..f918b74 --- /dev/null +++ b/pkg/passive/nuclei/nuclei.go @@ -0,0 +1,105 @@ +package nuclei + +import ( + "bufio" + "fmt" + "os" + "os/exec" + + "github.com/g0ldencybersec/EasyEASM/pkg/utils" +) + +func RunNuclei(domains []string, flags string) { + //run the nuclei tool + writeTempFile(domains) + + //run the interactive mode if flag is provided + if flags == "interactive" { + reader := bufio.NewReader(os.Stdin) + std, _ := utils.GetInput("Press y if you want to insert template directory, or any other character to run standard\n", reader) + switch std { + case "y": + reader = bufio.NewReader(os.Stdin) + opt, _ := utils.GetInput("Insert the template directory or press enter to run standard list\n", reader) + + if _, err := os.Stat(opt); os.IsNotExist(err) { + fmt.Println("DIRECTORY DOES NOT EXISTS -> Running standard config...") + //run the nuclei std command + cmd := exec.Command("nuclei", "-l", "tempNuclei.txt", "-silent", "-o", "temp.json", "-j", "-exclude-severity", "info", "-exclude-severity", "unknown") + err := cmd.Run() + if err != nil { + panic(err) + } + } else { + //run the nuclei cmd on the selected list of templetes + fmt.Println("Running found template lists...") + cmd := exec.Command("nuclei", "-l", "tempNuclei.txt", "-silent", "-t", opt, "-o", "temp.json", "-j", "-exclude-severity", "info", "-exclude-severity", "unknown") + err := cmd.Run() + if err != nil { + panic(err) + } + } + + default: + //run the nuclei std command + cmd := exec.Command("nuclei", "-l", "tempNuclei.txt", "-silent", "-o", "temp.json", "-j", "-exclude-severity", "info", "-exclude-severity", "unknown") + err := cmd.Run() + if err != nil { + panic(err) + } + } + } else { + //run the standard scan with nuclei, on the provided targets + cmd := exec.Command("nuclei", "-l", "tempNuclei.txt", "-silent", "-o", "temp.json", "-j", "-exclude-severity", "info", "-exclude-severity", "unknown") + err := cmd.Run() + if err != nil { + panic(err) + } + } + processJson() + fmt.Printf("Nuclei scan completed!\n") + os.Remove("temp.json") + os.Remove("tempNuclei.txt") +} + +func writeTempFile(list []string) { + //create the temporary file and read the domains provided + file, err := os.OpenFile("tempNuclei.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + + if err != nil { + panic(err) + } + + datawriter := bufio.NewWriter(file) + + for _, data := range list { + _, _ = datawriter.WriteString(data + "\n") + } + + datawriter.Flush() + file.Close() +} + +func processJson() { + // Check if jq is installed + utils.CheckJq() + + //process the json output to make lines unique using jq + cmd := exec.Command("jq", "-c", "--unbuffered", "-r", "del(.timestamp) | del(.\"curl-command\") | @json", "temp.json") + + // Create a file to hold the output + outputFile, err := os.Create("EasyEASM.json") + if err != nil { + panic(err) + } + defer outputFile.Close() + + // Set the output of the command to the created file + cmd.Stdout = outputFile + + // Run the command + err = cmd.Run() + if err != nil { + panic(err) + } +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 4a3a5cf..757ce45 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -194,7 +194,7 @@ func InstallTools() { "httpx": "github.com/projectdiscovery/httpx/cmd/httpx@latest", "oam_subs": "github.com/owasp-amass/oam-tools/cmd/oam_subs@master", "subfinder": "github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest", - "nuclei": "go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest", + "nuclei": "github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest", } { if !checkTool(name) { installGoTool(name, path) From 4b9b809e5c279441cd29933765bb28021f830b4a Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Mon, 18 Mar 2024 15:36:25 +0100 Subject: [PATCH 3/7] Added runtime changes to config.yml Added the flags pkg Added flag 'i' to config parser to run interactive mode --- easyeasm/main.go | 6 +- pkg/configparser/configparser.go | 141 ++++++++++++++++++++++++++++++- pkg/passive/flags/flags.go | 26 ++++++ 3 files changed, 171 insertions(+), 2 deletions(-) create mode 100644 pkg/passive/flags/flags.go diff --git a/easyeasm/main.go b/easyeasm/main.go index 46a2afe..1a63211 100644 --- a/easyeasm/main.go +++ b/easyeasm/main.go @@ -8,6 +8,7 @@ import ( "github.com/g0ldencybersec/EasyEASM/pkg/active" "github.com/g0ldencybersec/EasyEASM/pkg/configparser" "github.com/g0ldencybersec/EasyEASM/pkg/passive" + "github.com/g0ldencybersec/EasyEASM/pkg/passive/flags" "github.com/g0ldencybersec/EasyEASM/pkg/utils" ) @@ -19,8 +20,11 @@ func main() { banner := "\x1b[36m****************\n\nEASY EASM\n\n***************\x1b[0m\n" fmt.Println(banner) + //check if flag '-i' is provided, if yes return the interactive parameter + flag := flags.ParsingFlags() + // parse the configuration file - cfg := configparser.ParseConfig() + cfg := configparser.ParseConfig(flag) // check for previous run file var prevRun bool diff --git a/pkg/configparser/configparser.go b/pkg/configparser/configparser.go index 7859acf..72b5195 100644 --- a/pkg/configparser/configparser.go +++ b/pkg/configparser/configparser.go @@ -1,9 +1,13 @@ package configparser import ( + "bufio" + "fmt" "log" "os" + "strconv" + "github.com/g0ldencybersec/EasyEASM/pkg/utils" "gopkg.in/yaml.v3" ) @@ -18,7 +22,7 @@ type Config struct { } `yaml:"runConfig"` } -func ParseConfig() Config { +func ParseConfig(flags string) Config { // Read file data data, err := os.ReadFile("config.yml") if err != nil { @@ -33,5 +37,140 @@ func ParseConfig() Config { if err != nil { log.Fatalf("error: %v", err) } + + //runtime config modification if flag -i is provided + if flags == "interactive" { + reader := bufio.NewReader(os.Stdin) + fmt.Println("Do you want to change anything in the config?") + opt, _ := utils.GetInput("Press \"y\" to make changes or any characther to keep running\n", reader) + if opt == "y" { + config = PromptConfigChange(config) + } + } + + return config +} + +func PromptConfigChange(config Config) (cfg Config) { + //runtime changes to the config file if flag -i is provided + fmt.Println("Choose an option or press any other character to run without anymore changes") + reader := bufio.NewReader(os.Stdin) + + opt, _ := utils.GetInput("1.Domains - 2.Slack - 3.Discord - 4.Run Type 5.N of Threads\n", reader) + switch opt { + + //add domains to the list + case "1": + opt, _ = utils.GetInput("Write the domain you would like to add\n", reader) + config.RunConfig.Domains = append(config.RunConfig.Domains, opt) + + yamlData, err := yaml.Marshal(config) + if err != nil { + log.Fatalf("error marshalling YAML: %v", err) + } + + // Write modified data back to YAML file + err = os.WriteFile("config.yml", yamlData, 0644) + if err != nil { + log.Fatalf("error writing YAML file: %v", err) + } + + fmt.Println("Domain added successfully") + PromptConfigChange(config) + + //add slack webhook at runtime + case "2": + opt, _ = utils.GetInput("Insert the Slack Webhook, end by pressing \"Enter\"\n", reader) + config.RunConfig.SlackWebhook = opt + + //marshal back the data to yml + yamlData, err := yaml.Marshal(config) + if err != nil { + log.Fatalf("error marshalling YAML: %v", err) + } + + // Write modified data back to YAML file + err = os.WriteFile("config.yml", yamlData, 0644) + if err != nil { + log.Fatalf("error writing YAML file: %v", err) + } + + fmt.Println("Slack Webhook added successfully") + PromptConfigChange(config) + + //add discord webhook at runtime + case "3": + opt, _ = utils.GetInput("Insert the Discord Webhook, end by pressing \"Enter\"\n", reader) + config.RunConfig.DiscordWebhook = opt + + //marshal back the data to yml + yamlData, err := yaml.Marshal(config) + if err != nil { + log.Fatalf("error marshalling YAML: %v", err) + } + + // Write modified data back to YAML file + err = os.WriteFile("config.yml", yamlData, 0644) + if err != nil { + log.Fatalf("error writing YAML file: %v", err) + } + + fmt.Println("Slack Webhook added successfully") + PromptConfigChange(config) + + //change the configuration type + case "4": + opt, _ = utils.GetInput("Insert the run type (fast or complete). End by pressing \"Enter\"\n", reader) + + //check if the type is setted correctly + if opt == "fast" || opt == "complete" { + config.RunConfig.RunType = opt + yamlData, err := yaml.Marshal(config) + if err != nil { + log.Fatalf("error marshalling YAML: %v", err) + } + + // Write modified data back to YAML file + err = os.WriteFile("config.yml", yamlData, 0644) + if err != nil { + log.Fatalf("error writing YAML file: %v", err) + } + + fmt.Println("Config type setted correctly") + PromptConfigChange(config) + } else { + //restart the config if the type was invalid + PromptConfigChange(config) + } + + //change the number of threads + case "5": + opt, _ = utils.GetInput("Insert the number of threads you want to run. End by pressing \"Enter\"\n", reader) + + //check if the value inserted is a number + num, err := strconv.Atoi(opt) + if err != nil { + log.Fatalf("error converting thread number: %v", err) + } + + //set the number back in the config file + config.RunConfig.ActiveThreads = num + yamlData, err := yaml.Marshal(config) + if err != nil { + log.Fatalf("error marshalling YAML: %v", err) + } + + // Write modified data back to YAML file + err = os.WriteFile("config.yml", yamlData, 0644) + if err != nil { + log.Fatalf("error writing YAML file: %v", err) + } + + fmt.Println("Thread number setted correctly") + PromptConfigChange(config) + + default: + return config + } return config } diff --git a/pkg/passive/flags/flags.go b/pkg/passive/flags/flags.go new file mode 100644 index 0000000..f6f1e95 --- /dev/null +++ b/pkg/passive/flags/flags.go @@ -0,0 +1,26 @@ +package flags + +import ( + "flag" + "fmt" +) + +func ParsingFlags() string { + interactive := flag.Bool("i", false, "interactive mode selected") + //periodic := flag.Bool("p", false, "periodic scan") + flag.Parse() + + //start the interactive mode with runtime config + if *interactive { + fmt.Println("flag read correctly") + return "interactive" + } + + // //enter the periodic mode, need to be implemented + // if *periodic { + // panic("periodic scan still not implemented") + // } + + //std run read from config file and doesnt prompt anything to console at runtime + return "std" +} From b0111b5159e40b2b55dc0aaaf18caa5604ccdb71 Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Mon, 18 Mar 2024 16:00:46 +0100 Subject: [PATCH 4/7] Added RunNuclei to passive resources Added the PromptNucleiFunc to main (because of pkg struct) Added interactive mode for nuclei scan --- easyeasm/main.go | 81 +++++++++++++++++++++++++++++++++++++++++- pkg/passive/passive.go | 5 +++ 2 files changed, 85 insertions(+), 1 deletion(-) diff --git a/easyeasm/main.go b/easyeasm/main.go index 1a63211..b3bdaf1 100644 --- a/easyeasm/main.go +++ b/easyeasm/main.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "fmt" "os" "strings" @@ -20,7 +21,7 @@ func main() { banner := "\x1b[36m****************\n\nEASY EASM\n\n***************\x1b[0m\n" fmt.Println(banner) - //check if flag '-i' is provided, if yes return the interactive parameter + //check if flag '-i' is provided when running the tool, if yes return the interactive parameter flag := flags.ParsingFlags() // parse the configuration file @@ -63,6 +64,9 @@ func main() { // run Httpx to check live domains Runner.RunHttpx() + //start the nuclei func + PromptOptionsNuclei(Runner, cfg, flag) + // notify about new domains if prevRun is true if prevRun && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { utils.NotifyNewDomainsSlack(Runner.Subdomains, cfg.RunConfig.SlackWebhook) @@ -105,6 +109,9 @@ func main() { fmt.Println("Checking which domains are live and generating assets csv...") ActiveRunner.RunHttpx() + //nuclei function start + PromptOptionsNuclei(PassiveRunner, cfg, flag) + // notify about new domains if prevRun is true if prevRun && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { utils.NotifyNewDomainsSlack(ActiveRunner.Subdomains, cfg.RunConfig.SlackWebhook) @@ -118,3 +125,75 @@ func main() { panic("Please pick a valid run mode and add it to your config.yml file! You can set runType to either 'fast' or 'complete'") } } + +// func is here and not in nuclei path to avoid aving to modify the current structure of the pkg (import cycle with passive) +func PromptOptionsNuclei(r passive.PassiveRunner, cfg configparser.Config, flags string) { + + //check if interactive mod is active + if flags == "interactive" { + //vuln scan starting + reader := bufio.NewReader(os.Stdin) + opt, _ := utils.GetInput("Do you want to run the vulnerability scanner? y/n\n", reader) + switch opt { + case "y": + fmt.Println("Running Nuclei") + + //var prevRunNuclei bool + if _, err := os.Stat("EasyEASM.json"); err == nil { + fmt.Println("Found data from previous Nuclei scan!") + //prevRunNuclei = true + e := os.Rename("EasyEASM.json", "old_EasyEASM.json") + if e != nil { + panic(e) + } + } else { + fmt.Println("No previous Nuclei scan data found") + //prevRunNuclei = false + } + r.RunNuclei(flags) + + //notify discord and slack if present + // if prevRunNuclei && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { + // utils.NotifyVulnSlack(cfg.RunConfig.SlackWebhook) + // os.Remove("old_EasyEASM.json") + // } else if prevRunNuclei && strings.Contains(cfg.RunConfig.DiscordWebhook, "https") { + // utils.NotifyVulnDiscord(cfg.RunConfig.DiscordWebhook) + // os.Remove("old_EasyEASM.json") + // } + + case "n": + return + + default: + //invalid option chosen at runtime + fmt.Println("Choose a valid option") + PromptOptionsNuclei(r, cfg, flags) + } + } else { + //std run without any console prompt + fmt.Println("Running Nuclei") + + //var prevRunNuclei bool + if _, err := os.Stat("EasyEASM.json"); err == nil { + fmt.Println("Found data from previous Nuclei scan!") + //prevRunNuclei = true + e := os.Rename("EasyEASM.json", "old_EasyEASM.json") + if e != nil { + panic(e) + } + } else { + fmt.Println("No previous Nuclei scan data found") + //prevRunNuclei = false + } + r.RunNuclei(flags) + + // if prevRunNuclei && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { + // utils.NotifyVulnSlack(cfg.RunConfig.SlackWebhook) + // os.Remove("old_EasyEASM.json") + // } else if prevRunNuclei && strings.Contains(cfg.RunConfig.DiscordWebhook, "https") { + // utils.NotifyVulnDiscord(cfg.RunConfig.DiscordWebhook) + // os.Remove("old_EasyEASM.json") + // } + return + } +} diff --git a/pkg/passive/passive.go b/pkg/passive/passive.go index c429602..99b01d8 100644 --- a/pkg/passive/passive.go +++ b/pkg/passive/passive.go @@ -6,6 +6,7 @@ import ( "github.com/g0ldencybersec/EasyEASM/pkg/passive/amass" "github.com/g0ldencybersec/EasyEASM/pkg/passive/httpx" + "github.com/g0ldencybersec/EasyEASM/pkg/passive/nuclei" "github.com/g0ldencybersec/EasyEASM/pkg/passive/subfinder" ) @@ -58,3 +59,7 @@ func (r *PassiveRunner) RunPassiveEnum() []string { func (r *PassiveRunner) RunHttpx() { httpx.RunHttpx(r.Subdomains) } + +func (r *PassiveRunner) RunNuclei(flags string) { + nuclei.RunNuclei(r.Subdomains, flags) +} From 6ec733a141d2656e93f19bf355aa4fab28792ffa Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Wed, 20 Mar 2024 09:28:41 +0100 Subject: [PATCH 5/7] Added notify func for Nuclei scan --- easyeasm/main.go | 43 +++++++++---------- pkg/utils/utils.go | 100 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 21 deletions(-) diff --git a/easyeasm/main.go b/easyeasm/main.go index b3bdaf1..d86631d 100644 --- a/easyeasm/main.go +++ b/easyeasm/main.go @@ -126,7 +126,7 @@ func main() { } } -// func is here and not in nuclei path to avoid aving to modify the current structure of the pkg (import cycle with passive) +// func is here and not in nuclei path to avoid having to modify the current structure of the pkg (import cycle with passive) func PromptOptionsNuclei(r passive.PassiveRunner, cfg configparser.Config, flags string) { //check if interactive mod is active @@ -138,28 +138,28 @@ func PromptOptionsNuclei(r passive.PassiveRunner, cfg configparser.Config, flags case "y": fmt.Println("Running Nuclei") - //var prevRunNuclei bool + var prevRunNuclei bool if _, err := os.Stat("EasyEASM.json"); err == nil { fmt.Println("Found data from previous Nuclei scan!") - //prevRunNuclei = true + prevRunNuclei = true e := os.Rename("EasyEASM.json", "old_EasyEASM.json") if e != nil { panic(e) } } else { fmt.Println("No previous Nuclei scan data found") - //prevRunNuclei = false + prevRunNuclei = false } r.RunNuclei(flags) //notify discord and slack if present - // if prevRunNuclei && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { - // utils.NotifyVulnSlack(cfg.RunConfig.SlackWebhook) - // os.Remove("old_EasyEASM.json") - // } else if prevRunNuclei && strings.Contains(cfg.RunConfig.DiscordWebhook, "https") { - // utils.NotifyVulnDiscord(cfg.RunConfig.DiscordWebhook) - // os.Remove("old_EasyEASM.json") - // } + if prevRunNuclei && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { + utils.NotifyVulnSlack(cfg.RunConfig.SlackWebhook) + os.Remove("old_EasyEASM.json") + } else if prevRunNuclei && strings.Contains(cfg.RunConfig.DiscordWebhook, "https") { + utils.NotifyVulnDiscord(cfg.RunConfig.DiscordWebhook) + os.Remove("old_EasyEASM.json") + } case "n": return @@ -173,27 +173,28 @@ func PromptOptionsNuclei(r passive.PassiveRunner, cfg configparser.Config, flags //std run without any console prompt fmt.Println("Running Nuclei") - //var prevRunNuclei bool + var prevRunNuclei bool if _, err := os.Stat("EasyEASM.json"); err == nil { fmt.Println("Found data from previous Nuclei scan!") - //prevRunNuclei = true + prevRunNuclei = true e := os.Rename("EasyEASM.json", "old_EasyEASM.json") if e != nil { panic(e) } } else { fmt.Println("No previous Nuclei scan data found") - //prevRunNuclei = false + prevRunNuclei = false } r.RunNuclei(flags) - // if prevRunNuclei && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { - // utils.NotifyVulnSlack(cfg.RunConfig.SlackWebhook) - // os.Remove("old_EasyEASM.json") - // } else if prevRunNuclei && strings.Contains(cfg.RunConfig.DiscordWebhook, "https") { - // utils.NotifyVulnDiscord(cfg.RunConfig.DiscordWebhook) - // os.Remove("old_EasyEASM.json") - // } + //notify discord and slack if presents + if prevRunNuclei && strings.Contains(cfg.RunConfig.SlackWebhook, "https") { + utils.NotifyVulnSlack(cfg.RunConfig.SlackWebhook) + os.Remove("old_EasyEASM.json") + } else if prevRunNuclei && strings.Contains(cfg.RunConfig.DiscordWebhook, "https") { + utils.NotifyVulnDiscord(cfg.RunConfig.DiscordWebhook) + os.Remove("old_EasyEASM.json") + } return } } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 757ce45..116cdd6 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -248,3 +248,103 @@ func CheckJq() { return } } + +func NotifyVulnDiscord(discordWebhook string) { + // Used to parse the nuclei file and notify about vuln + // notification are based on: host, name of the vulnerability and severity + + // Open the JSON file + inputFile, err := os.Open("EasyEASM.json") + if err != nil { + fmt.Println("Error opening JSON file") + panic(err) + } + defer inputFile.Close() + + //structured json of the nuclei output, used only here so declared inside + type Info struct { + Name string `json:"name"` + Severity string `json:"severity"` + } + + type Data struct { + Host string `json:"host"` + Inform Info `json:"info"` + } + + var jsonPayload []Data + var vulnerability Data + decoder := json.NewDecoder(inputFile) + + //decode the json output from nuclei + for decoder.More() { + err := decoder.Decode(&vulnerability) + if err != nil { + panic(err) + } + + //append the parametres for each line of the JSON + jsonPayload = append(jsonPayload, vulnerability) + } + + //bulking toghether the different vuln to have a single notification + var message string + message = "List of discovered vulnerabilities:\n" + for _, v := range jsonPayload { + newMessage := fmt.Sprintf("Host: %v, Name: %v, Severity: %v\n", v.Host, v.Inform.Name, v.Inform.Severity) + message += newMessage + } + + //sending the message to the provided webhook + sendToDiscord(discordWebhook, message) +} + +func NotifyVulnSlack(slackWebhook string) { + // Used to parse the nuclei file and notify about vuln + // notification are based on: host, name of the vulnerability and severity + + // Open the JSON file + inputFile, err := os.Open("EasyEASM.json") + if err != nil { + fmt.Println("Error opening JSON file") + panic(err) + } + defer inputFile.Close() + + //structured json of the nuclei output, used only here so declared inside + type Info struct { + Name string `json:"name"` + Severity string `json:"severity"` + } + + type Data struct { + Host string `json:"host"` + Inform Info `json:"info"` + } + + var jsonPayload []Data + var vulnerability Data + decoder := json.NewDecoder(inputFile) + + //decode the json output from nuclei + for decoder.More() { + err := decoder.Decode(&vulnerability) + if err != nil { + panic(err) + } + + //append the parametres for each line of the JSON + jsonPayload = append(jsonPayload, vulnerability) + } + + //bulking toghether the different vuln to have a single notification + var message string + message = "List of discovered vulnerabilities:\n" + for _, v := range jsonPayload { + newMessage := fmt.Sprintf("Host: %v, Name: %v, Severity: %v\n", v.Host, v.Inform.Name, v.Inform.Severity) + message += newMessage + } + + //sending the message to the provided webhook + sendToDiscord(slackWebhook, message) +} From 598b6844b4fb491fc1641b16900055f3034cfc9b Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Wed, 20 Mar 2024 10:19:57 +0100 Subject: [PATCH 6/7] Added comments and better formatting Added valid check for domains added at runtime --- easyeasm/main.go | 3 ++- pkg/active/httpx/httpx.go | 4 ++-- pkg/configparser/configparser.go | 31 +++++++++++++++++++------------ pkg/passive/flags/flags.go | 2 +- pkg/utils/utils.go | 14 +++++++++++++- 5 files changed, 37 insertions(+), 17 deletions(-) diff --git a/easyeasm/main.go b/easyeasm/main.go index d86631d..cd80f2f 100644 --- a/easyeasm/main.go +++ b/easyeasm/main.go @@ -127,9 +127,10 @@ func main() { } // func is here and not in nuclei path to avoid having to modify the current structure of the pkg (import cycle with passive) +// it can probably be adjusted to be make the main cleaner func PromptOptionsNuclei(r passive.PassiveRunner, cfg configparser.Config, flags string) { - //check if interactive mod is active + //check if interactive mod is active (flag -i) if flags == "interactive" { //vuln scan starting reader := bufio.NewReader(os.Stdin) diff --git a/pkg/active/httpx/httpx.go b/pkg/active/httpx/httpx.go index d99c3b5..aa8c743 100644 --- a/pkg/active/httpx/httpx.go +++ b/pkg/active/httpx/httpx.go @@ -15,7 +15,7 @@ func RunHttpx(domains []string) { if err != nil { panic(err) } - fmt.Printf("Httpx run completed") + fmt.Printf("Httpx run completed\n") processCSV() os.Remove("tempHttpx.txt") os.Remove("temp.csv") @@ -56,7 +56,7 @@ func processCSV() { } // Specify the indices of the columns to keep - columnsToKeep := []int{0,1,7,8,10,13,17,20,26,27,28,32,33,35,37} // Keeping only the first and third columns (0-indexed) + columnsToKeep := []int{0, 1, 7, 8, 10, 13, 17, 20, 26, 27, 28, 32, 33, 35, 37} // Keeping only the first and third columns (0-indexed) // Open the output CSV file outputFile, err := os.Create("EasyEASM.csv") diff --git a/pkg/configparser/configparser.go b/pkg/configparser/configparser.go index 72b5195..037e3b4 100644 --- a/pkg/configparser/configparser.go +++ b/pkg/configparser/configparser.go @@ -62,21 +62,28 @@ func PromptConfigChange(config Config) (cfg Config) { //add domains to the list case "1": opt, _ = utils.GetInput("Write the domain you would like to add\n", reader) - config.RunConfig.Domains = append(config.RunConfig.Domains, opt) - yamlData, err := yaml.Marshal(config) - if err != nil { - log.Fatalf("error marshalling YAML: %v", err) - } + //check if the domain is in a valid format (doesnt ensure that the domain exists) + if utils.ValidDomain(opt) { + config.RunConfig.Domains = append(config.RunConfig.Domains, opt) - // Write modified data back to YAML file - err = os.WriteFile("config.yml", yamlData, 0644) - if err != nil { - log.Fatalf("error writing YAML file: %v", err) - } + yamlData, err := yaml.Marshal(config) + if err != nil { + log.Fatalf("error marshalling YAML: %v", err) + } - fmt.Println("Domain added successfully") - PromptConfigChange(config) + // Write modified data back to YAML file + err = os.WriteFile("config.yml", yamlData, 0644) + if err != nil { + log.Fatalf("error writing YAML file: %v", err) + } + + fmt.Println("Domain added successfully") + PromptConfigChange(config) + } else { + fmt.Printf("Invalid Domain format\n\n") + PromptConfigChange(config) + } //add slack webhook at runtime case "2": diff --git a/pkg/passive/flags/flags.go b/pkg/passive/flags/flags.go index f6f1e95..cd96ae0 100644 --- a/pkg/passive/flags/flags.go +++ b/pkg/passive/flags/flags.go @@ -16,7 +16,7 @@ func ParsingFlags() string { return "interactive" } - // //enter the periodic mode, need to be implemented + // //enter the periodic mode NOT IMPLEMENTED YET // if *periodic { // panic("periodic scan still not implemented") // } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 116cdd6..249f8cc 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -10,6 +10,7 @@ import ( "net/http" "os" "os/exec" + "regexp" "strings" ) @@ -251,7 +252,7 @@ func CheckJq() { func NotifyVulnDiscord(discordWebhook string) { // Used to parse the nuclei file and notify about vuln - // notification are based on: host, name of the vulnerability and severity + // notification contains: host, name of the vulnerability, severity // Open the JSON file inputFile, err := os.Open("EasyEASM.json") @@ -338,6 +339,7 @@ func NotifyVulnSlack(slackWebhook string) { } //bulking toghether the different vuln to have a single notification + //notify the host, name and severity of the vulnerability var message string message = "List of discovered vulnerabilities:\n" for _, v := range jsonPayload { @@ -348,3 +350,13 @@ func NotifyVulnSlack(slackWebhook string) { //sending the message to the provided webhook sendToDiscord(slackWebhook, message) } + +func ValidDomain(domain string) bool { + //check if the string provided is a valid domain - pattern can be made modified to be more strict + pattern := `^(?:(?:[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*(?:[A-Za-z0-9\-]+\.)+[A-Za-z]{2,}$` + regex := regexp.MustCompile(pattern) + + //retrun a boolean to make the check quick in the configparser + return regex.MatchString(domain) + +} From d40e578702cdd7003e10073cbc4d5a6a800124b0 Mon Sep 17 00:00:00 2001 From: Zedel17 Date: Wed, 20 Mar 2024 11:30:35 +0100 Subject: [PATCH 7/7] Small formatting fixes --- pkg/configparser/configparser.go | 7 ++++--- pkg/passive/flags/flags.go | 2 +- pkg/passive/httpx/httpx.go | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/configparser/configparser.go b/pkg/configparser/configparser.go index 037e3b4..ef19c0d 100644 --- a/pkg/configparser/configparser.go +++ b/pkg/configparser/configparser.go @@ -102,7 +102,7 @@ func PromptConfigChange(config Config) (cfg Config) { log.Fatalf("error writing YAML file: %v", err) } - fmt.Println("Slack Webhook added successfully") + fmt.Printf("Slack Webhook added successfully\n\n") PromptConfigChange(config) //add discord webhook at runtime @@ -122,7 +122,7 @@ func PromptConfigChange(config Config) (cfg Config) { log.Fatalf("error writing YAML file: %v", err) } - fmt.Println("Slack Webhook added successfully") + fmt.Printf("Slack Webhook added successfully\n\n") PromptConfigChange(config) //change the configuration type @@ -143,10 +143,11 @@ func PromptConfigChange(config Config) (cfg Config) { log.Fatalf("error writing YAML file: %v", err) } - fmt.Println("Config type setted correctly") + fmt.Printf("Config type setted correctly\n\n") PromptConfigChange(config) } else { //restart the config if the type was invalid + fmt.Printf("Config type invalid, please choose fast or complete\n\n") PromptConfigChange(config) } diff --git a/pkg/passive/flags/flags.go b/pkg/passive/flags/flags.go index cd96ae0..2602502 100644 --- a/pkg/passive/flags/flags.go +++ b/pkg/passive/flags/flags.go @@ -12,7 +12,7 @@ func ParsingFlags() string { //start the interactive mode with runtime config if *interactive { - fmt.Println("flag read correctly") + fmt.Println("Interactive Mode selected") return "interactive" } diff --git a/pkg/passive/httpx/httpx.go b/pkg/passive/httpx/httpx.go index d99c3b5..aa8c743 100644 --- a/pkg/passive/httpx/httpx.go +++ b/pkg/passive/httpx/httpx.go @@ -15,7 +15,7 @@ func RunHttpx(domains []string) { if err != nil { panic(err) } - fmt.Printf("Httpx run completed") + fmt.Printf("Httpx run completed\n") processCSV() os.Remove("tempHttpx.txt") os.Remove("temp.csv") @@ -56,7 +56,7 @@ func processCSV() { } // Specify the indices of the columns to keep - columnsToKeep := []int{0,1,7,8,10,13,17,20,26,27,28,32,33,35,37} // Keeping only the first and third columns (0-indexed) + columnsToKeep := []int{0, 1, 7, 8, 10, 13, 17, 20, 26, 27, 28, 32, 33, 35, 37} // Keeping only the first and third columns (0-indexed) // Open the output CSV file outputFile, err := os.Create("EasyEASM.csv")