Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vulnerability scanner integration (Nuclei) - Runtime config #16

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
87 changes: 86 additions & 1 deletion easyeasm/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package main

import (
"bufio"
"fmt"
"os"
"strings"

"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"
)

Expand All @@ -19,8 +21,11 @@ func main() {
banner := "\x1b[36m****************\n\nEASY EASM\n\n***************\x1b[0m\n"
fmt.Println(banner)

//check if flag '-i' is provided when running the tool, 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
Expand Down Expand Up @@ -59,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)
Expand Down Expand Up @@ -101,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)
Expand All @@ -114,3 +125,77 @@ 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 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 (flag -i)
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)

//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
}
}
4 changes: 2 additions & 2 deletions pkg/active/httpx/httpx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down
149 changes: 148 additions & 1 deletion pkg/configparser/configparser.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package configparser

import (
"bufio"
"fmt"
"log"
"os"
"strconv"

"github.com/g0ldencybersec/EasyEASM/pkg/utils"
"gopkg.in/yaml.v3"
)

Expand All @@ -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 {
Expand All @@ -33,5 +37,148 @@ 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)

//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)

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)
} else {
fmt.Printf("Invalid Domain format\n\n")
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.Printf("Slack Webhook added successfully\n\n")
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.Printf("Slack Webhook added successfully\n\n")
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.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)
}

//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
}
26 changes: 26 additions & 0 deletions pkg/passive/flags/flags.go
Original file line number Diff line number Diff line change
@@ -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("Interactive Mode selected")
return "interactive"
}

// //enter the periodic mode NOT IMPLEMENTED YET
// if *periodic {
// panic("periodic scan still not implemented")
// }

//std run read from config file and doesnt prompt anything to console at runtime
return "std"
}
4 changes: 2 additions & 2 deletions pkg/passive/httpx/httpx.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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")
Expand Down
Loading