Skip to content
Open
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# Nuclear Pond
# Nuclear Pond V.2

`We forked this project so we can continue with this great idea, I hope that together we can improve the code and make it available to everyone - @OFJAAAH`

<img src="assets/logo.png" width="400" height="300" align="right">

Nuclear Pond is used to leverage [Nuclei](https://github.com/projectdiscovery/nuclei) in the cloud with unremarkable speed, flexibility, and [perform internet wide scans for far less than a cup of coffee](https://devsecopsdocs.com/blog/nuclear-pond/).
To install Nuclear Pond, you need to configure the backend [terraform module](https://github.com/DevSecOpsDocs/terraform-nuclear-pond). You can do this by running `terraform apply` or by leveraging [terragrunt](https://terragrunt.gruntwork.io/).


It leverages [AWS Lambda](https://aws.amazon.com/lambda/) as a backend to invoke Nuclei scans in parallel, choice of storing json findings in s3 to query with [AWS Athena](https://aws.amazon.com/athena/), and is easily one of the cheapest ways you can execute scans in the cloud.

Expand All @@ -24,7 +26,8 @@ Think of Nuclear Pond as just a way for you to run Nuclei in the cloud. You can

## Setup & Installation

To install Nuclear Pond, you need to configure the backend [terraform module](https://github.com/DevSecOpsDocs/terraform-nuclear-pond). You can do this by running `terraform apply` or by leveraging [terragrunt](https://terragrunt.gruntwork.io/).
To install Nuclear Pond, you need to configure the backend [terraform module](https://github.com/KingOfBugbounty/terraform-nuclear-pond-OFJAAAH). You can do this by running `terraform apply` or by leveraging [terragrunt](https://terragrunt.gruntwork.io/).


```bash
$ go install github.com/DevSecOpsDocs/nuclearpond@latest
Expand Down
Binary file added oryxBuildBinary
Binary file not shown.
214 changes: 107 additions & 107 deletions pkg/cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
package cmd

import (
"fmt"
"log"
"os"
"fmt"
"log"
"os"

"github.com/DevSecOpsDocs/nuclearpond/pkg/core"
"github.com/DevSecOpsDocs/nuclearpond/pkg/helpers"
"github.com/DevSecOpsDocs/nuclearpond/pkg/server"
"github.com/DevSecOpsDocs/nuclearpond/pkg/core"
"github.com/DevSecOpsDocs/nuclearpond/pkg/helpers"
"github.com/DevSecOpsDocs/nuclearpond/pkg/server"

"github.com/common-nighthawk/go-figure"
"github.com/spf13/cobra"
"github.com/common-nighthawk/go-figure"
"github.com/spf13/cobra"
)

var asciiBanner = figure.NewFigure("Nuclear Pond", "", true).String()

var rootCmd = &cobra.Command{
Use: "nuclearpond",
Short: "A CLI tool for Nuclear Pond to run nuclei in parallel",
Long: "Nuclear Pond invokes nuclei in parallel through invoking lambda functions, customizes command line flags, specifies output, and batches requests.",
Example: `nuclearpond run -t devsecopsdocs.com -a $(echo -ne "-t dns" | base64) -o cmd`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(asciiBanner)
cmd.Help()
},
Use: "nuclearpond",
Short: "A CLI tool for Nuclear Pond to run nuclei in parallel",
Long: "Nuclear Pond invokes nuclei in parallel through invoking lambda functions, customizes command line flags, specifies output, and batches requests.",
Example: `nuclearpond run -t devsecopsdocs.com -a $(echo -ne "-t dns" | base64) -o cmd`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(asciiBanner)
cmd.Help()
},
}

var silent bool
Expand All @@ -37,107 +37,107 @@ var output string
var threads int

var runCmd = &cobra.Command{
Use: "run",
Short: "Execute nuclei tasks",
Long: "Executes nuclei tasks in parallel by invoking lambda asynchronously",
Run: func(cmd *cobra.Command, args []string) {
if !silent {
fmt.Println(asciiBanner)
fmt.Println(" devsecopsdocs.com")
fmt.Println()
}

// Nuclei args flag
if nucleiArgs == "" {
log.Fatal("Nuclei arguments are required")
os.Exit(1)
}

// Targets flag
if targets == "" && target == "" {
log.Fatal("Either a target or a list of targets is required")
os.Exit(1)
}

if targets != "" {
urls := helpers.ReadUrlsFromFile(targets)
urls = helpers.RemoveEmpty(urls)
log.Println("Running nuclear pond against", len(urls), "targets")
batches := helpers.SplitSlice(urls, batchSize)
log.Println("Splitting targets into", len(batches), "individual executions")
log.Println("Running with " + fmt.Sprint(threads) + " threads")
core.ExecuteScans(batches, output, functionName, nucleiArgs, threads, silent)
} else {
log.Println("Running nuclei against the target", target)
log.Println("Running with " + fmt.Sprint(threads) + " threads")
batches := [][]string{{target}}
core.ExecuteScans(batches, output, functionName, nucleiArgs, threads, silent)
}
},
Use: "run",
Short: "Execute nuclei tasks",
Long: "Executes nuclei tasks in parallel by invoking lambda asynchronously",
Run: func(cmd *cobra.Command, args []string) {
if !silent {
fmt.Println(asciiBanner)
fmt.Println(" devsecopsdocs.com")
fmt.Println()
}

if nucleiArgs == "" {
log.Fatal("Nuclei arguments are required")
os.Exit(1)
}

if targets == "" && target == "" {
log.Fatal("Either a target or a list of targets is required")
os.Exit(1)
}

if targets != "" {
urls := helpers.ReadUrlsFromFile(targets)
urls = helpers.RemoveEmpty(urls)
log.Println("Running nuclear pond against", len(urls), "targets")
batches := helpers.SplitSlice(urls, batchSize)
log.Println("Splitting targets into", len(batches), "individual executions")
log.Println("Running with " + fmt.Sprint(threads) + " threads")
core.ExecuteScans(batches, output, functionName, nucleiArgs, threads, silent, region)
} else {
log.Println("Running nuclei against the target", target)
log.Println("Running with " + fmt.Sprint(threads) + " threads")
batches := [][]string{{target}}
core.ExecuteScans(batches, output, functionName, nucleiArgs, threads, silent, region)
}
},
}

var startServer = &cobra.Command{
Use: "service",
Short: "Launch API to launch run tasks to the nuclei runner.",
Long: "Executes nuclei through an API through asynchronous lambda functions",
Run: func(cmd *cobra.Command, args []string) {
// Print banner
fmt.Println(asciiBanner)
fmt.Println(" devsecopsdocs.com")
fmt.Println()
// Start server
log.Println("Running nuclear pond http server on port 8080")
log.Println("http://localhost:8080")
server.HandleRequests()
},
Use: "service",
Short: "Launch API to launch run tasks to the nuclei runner.",
Long: "Executes nuclei through an API through asynchronous lambda functions",
Run: func(cmd *cobra.Command, args []string) {
// Print banner
fmt.Println(asciiBanner)
fmt.Println(" devsecopsdocs.com")
fmt.Println()
// Start server
log.Println("Running nuclear pond http server on port 8080")
log.Println("http://localhost:8080")
server.HandleRequests()
},
}

func init() {
// run subcommand
// Mark flags as required
runCmd.MarkFlagRequired("args")
runCmd.MarkFlagRequired("output")
// General flags
runCmd.Flags().BoolVarP(&silent, "silent", "s", false, "silent command line output")
runCmd.Flags().StringVarP(&target, "target", "t", "", "individual target to specify")
runCmd.Flags().StringVarP(&targets, "targets", "l", "", "list of targets in a file")
runCmd.Flags().StringVarP(&nucleiArgs, "args", "a", "", "nuclei arguments as base64 encoded string")
runCmd.Flags().IntVarP(&batchSize, "batch-size", "b", 1, "batch size for number of targets per execution")
runCmd.Flags().StringVarP(&output, "output", "o", "cmd", "output type to save nuclei results(s3, cmd, or json)")
runCmd.Flags().IntVarP(&threads, "threads", "c", 1, "number of threads to run lambda functions, default is 1 which will be slow")
// Region flag
runCmd.Flags().StringVarP(&region, "region", "r", "", "AWS region to run nuclei")
if region == "" {
region, ok := os.LookupEnv("AWS_REGION")
if !ok {
runCmd.MarkFlagRequired("region")
} else {
runCmd.Flags().Set("region", region)
}
}
// Function name flag
runCmd.Flags().StringVarP(&functionName, "function-name", "f", "", "AWS Lambda function name")
if functionName == "" {
functionName, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME")
if !ok {
runCmd.MarkFlagRequired("function-name")
} else {
runCmd.Flags().Set("function-name", functionName)
}
}
// run subcommand
// Mark flags as required
runCmd.MarkFlagRequired("args")
runCmd.MarkFlagRequired("output")
// General flags
runCmd.Flags().BoolVarP(&silent, "silent", "s", false, "silent command line output")
runCmd.Flags().StringVarP(&target, "target", "t", "", "individual target to specify")
runCmd.Flags().StringVarP(&targets, "targets", "l", "", "list of targets in a file")
runCmd.Flags().StringVarP(&nucleiArgs, "args", "a", "", "nuclei arguments as base64 encoded string")
runCmd.Flags().IntVarP(&batchSize, "batch-size", "b", 1, "batch size for number of targets per execution")
runCmd.Flags().StringVarP(&output, "output", "o", "cmd", "output type to save nuclei results(s3, cmd, or json)")
runCmd.Flags().IntVarP(&threads, "threads", "c", 1, "number of threads to run lambda functions, default is 1 which will be slow")
// Region flag
runCmd.Flags().StringVarP(&region, "region", "r", "", "AWS region to run nuclei")
if region == "" {
var ok bool // Declare ok here to avoid shadowing
region, ok = os.LookupEnv("AWS_REGION") // Removed := to modify the existing region variable
if !ok {
runCmd.MarkFlagRequired("region")
} else {
runCmd.Flags().Set("region", region)
}
}

// Function name flag
runCmd.Flags().StringVarP(&functionName, "function-name", "f", "", "AWS Lambda function name")
if functionName == "" {
functionName, ok := os.LookupEnv("AWS_LAMBDA_FUNCTION_NAME")
if !ok {
runCmd.MarkFlagRequired("function-name")
} else {
runCmd.Flags().Set("function-name", functionName)
}
}
}

// Execute executes the root command.
func Execute() error {
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.SetHelpCommand(&cobra.Command{
Use: "no-help",
Hidden: true,
})
rootCmd.CompletionOptions.DisableDefaultCmd = true
rootCmd.SetHelpCommand(&cobra.Command{
Use: "no-help",
Hidden: true,
})

rootCmd.HasHelpSubCommands()
rootCmd.AddCommand(runCmd)
rootCmd.AddCommand(startServer)
rootCmd.HasHelpSubCommands()
rootCmd.AddCommand(runCmd)
rootCmd.AddCommand(startServer)

return rootCmd.Execute()
return rootCmd.Execute()
}
108 changes: 54 additions & 54 deletions pkg/core/core.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
package core

import (
"encoding/base64"
"log"
"strings"
"sync"
"time"
"encoding/base64"
"log"
"strings"
"sync"
"time"

"github.com/DevSecOpsDocs/nuclearpond/pkg/lambda"
"github.com/DevSecOpsDocs/nuclearpond/pkg/lambda"
)

func ExecuteScans(batches [][]string, output string, lambdaName string, nucleiArgs string, threads int, silent bool) {
// Get start time
start := time.Now()

// Create a WaitGroup to wait for the goroutines to finish
var wg sync.WaitGroup

// Set the number of threads to use
numThreads := threads

// Create a channel to pass tasks to the goroutines
tasks := make(chan func())

// Start the goroutines
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for task := range tasks {
task()
}
}()
}

// Add tasks to the channel
for _, batch := range batches {
decodedBytes, _ := base64.StdEncoding.DecodeString(nucleiArgs)
// Convert args.NucleiArg from base64, to string, split by space, and convert to list
nucleiFlags := strings.Split(string(decodedBytes), " ")

// create lambda invoke struct
lambdaInvoke := lambda.LambdaInvoke{
Targets: batch,
Args: nucleiFlags,
Output: output,
}
tasks <- func() {
lambda.InvokeLambdas(lambdaInvoke, lambdaName, output)
}
}

close(tasks)
wg.Wait()

// Print the results if not silent mode
if !silent {
log.Println("Completed all parallel operations, best of luck! Completed in", time.Since(start))
}
func ExecuteScans(batches [][]string, output string, lambdaName string, nucleiArgs string, threads int, silent bool, region string) {
// Get start time
start := time.Now()

// Create a WaitGroup to wait for the goroutines to finish
var wg sync.WaitGroup

// Set the number of threads to use
numThreads := threads

// Create a channel to pass tasks to the goroutines
tasks := make(chan func())

// Start the goroutines
for i := 0; i < numThreads; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for task := range tasks {
task()
}
}()
}

// Add tasks to the channel
for _, batch := range batches {
decodedBytes, _ := base64.StdEncoding.DecodeString(nucleiArgs)
// Convert args.NucleiArg from base64, to string, split by space, and convert to list
nucleiFlags := strings.Split(string(decodedBytes), " ")

// create lambda invoke struct
lambdaInvoke := lambda.LambdaInvoke{
Targets: batch,
Args: nucleiFlags,
Output: output,
}
tasks <- func() {
lambda.InvokeLambdas(lambdaInvoke, lambdaName, output, region)
}
}

close(tasks)
wg.Wait()

// Print the results if not silent mode
if !silent {
log.Println("Completed all parallel operations, best of luck! Completed in", time.Since(start))
}
}
Loading