mirror of https://github.com/daffainfo/nuclei.git
begin of work on fuzzing - only working for raw requests with payload (TODO code cleanup)
parent
443140a52f
commit
44821e6b77
1
go.mod
1
go.mod
|
@ -5,6 +5,7 @@ go 1.14
|
||||||
require (
|
require (
|
||||||
github.com/Knetic/govaluate v3.0.0+incompatible
|
github.com/Knetic/govaluate v3.0.0+incompatible
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
|
||||||
|
github.com/karrick/godirwalk v1.15.6
|
||||||
github.com/miekg/dns v1.1.29
|
github.com/miekg/dns v1.1.29
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
github.com/projectdiscovery/gologger v1.0.0
|
github.com/projectdiscovery/gologger v1.0.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -4,6 +4,8 @@ github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8L
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 h1:4daAzAu0S6Vi7/lbWECcX0j45yZReDZ56BQsrVBOEEY=
|
||||||
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/karrick/godirwalk v1.15.6 h1:Yf2mmR8TJy+8Fa0SuQVto5SYap6IF7lNVX4Jdl8G1qA=
|
||||||
|
github.com/karrick/godirwalk v1.15.6/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
|
||||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||||
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
github.com/miekg/dns v1.1.29 h1:xHBEhR+t5RzcFJjBLJlax2daXOrTYtr9z4WdKEfWFzg=
|
||||||
|
|
|
@ -78,7 +78,7 @@ func (e *HTTPExecutor) ExecuteHTTP(URL string) error {
|
||||||
|
|
||||||
// Send the request to the target servers
|
// Send the request to the target servers
|
||||||
mainLoop:
|
mainLoop:
|
||||||
for _, req := range compiledRequest {
|
for req := range compiledRequest {
|
||||||
resp, err := e.httpClient.Do(req)
|
resp, err := e.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if resp != nil {
|
if resp != nil {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
package generators
|
||||||
|
|
||||||
|
// Type is type of attack
|
||||||
|
type Type int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Sniper attack - each variable replaced with values at a time
|
||||||
|
Sniper Type = iota + 1
|
||||||
|
// PitchFork attack - Each variable replaced with positional value in multiple wordlists
|
||||||
|
PitchFork
|
||||||
|
// ClusterBomb attack - Generate all possible combinations of values
|
||||||
|
ClusterBomb
|
||||||
|
)
|
||||||
|
|
||||||
|
// AttackTypes is an table for conversion of attack type from string.
|
||||||
|
var AttackTypes = map[string]Type{
|
||||||
|
"sniper": Sniper,
|
||||||
|
"pitchfork": PitchFork,
|
||||||
|
"clusterbomb": ClusterBomb,
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package generators
|
||||||
|
|
||||||
|
// ClusterbombGenerator Attack - Generate all possible combinations from an input map with all values listed
|
||||||
|
// as slices of the same size
|
||||||
|
func ClusterbombGenerator(payloads map[string][]string) (out chan map[string]interface{}) {
|
||||||
|
out = make(chan map[string]interface{})
|
||||||
|
|
||||||
|
// generator
|
||||||
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
var order []string
|
||||||
|
var parts [][]string
|
||||||
|
for name, wordlist := range payloads {
|
||||||
|
order = append(order, name)
|
||||||
|
parts = append(parts, wordlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
var n = 1
|
||||||
|
for _, ar := range parts {
|
||||||
|
n *= len(ar)
|
||||||
|
}
|
||||||
|
|
||||||
|
var at = make([]int, len(parts))
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
// increment position counters
|
||||||
|
for i := len(parts) - 1; i >= 0; i-- {
|
||||||
|
if at[i] > 0 && at[i] >= len(parts[i]) {
|
||||||
|
if i == 0 || (i == 1 && at[i-1] == len(parts[0])-1) {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
at[i] = 0
|
||||||
|
at[i-1]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// construct permutation
|
||||||
|
item := make(map[string]interface{})
|
||||||
|
for i, ar := range parts {
|
||||||
|
var p = at[i]
|
||||||
|
if p >= 0 && p < len(ar) {
|
||||||
|
item[order[i]] = ar[p]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out <- item
|
||||||
|
at[len(parts)-1]++
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
package generators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"html"
|
||||||
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Knetic/govaluate"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HelperFunctions contains the dsl functions
|
||||||
|
func HelperFunctions() (functions map[string]govaluate.ExpressionFunction) {
|
||||||
|
functions = make(map[string]govaluate.ExpressionFunction)
|
||||||
|
// strings
|
||||||
|
functions["len"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
length := len(args[0].(string))
|
||||||
|
return (float64)(length), nil
|
||||||
|
}
|
||||||
|
functions["toupper"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.ToUpper(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
functions["tolower"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.ToLower(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
functions["replace"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.Replace(args[0].(string), args[1].(string), args[2].(string), -1), nil
|
||||||
|
}
|
||||||
|
functions["trim"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.Trim(args[0].(string), args[2].(string)), nil
|
||||||
|
}
|
||||||
|
functions["trimleft"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.TrimLeft(args[0].(string), args[1].(string)), nil
|
||||||
|
}
|
||||||
|
functions["trimright"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.TrimRight(args[0].(string), args[1].(string)), nil
|
||||||
|
}
|
||||||
|
functions["trimspace"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.TrimSpace(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
functions["trimprefix"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.TrimPrefix(args[0].(string), args[1].(string)), nil
|
||||||
|
}
|
||||||
|
functions["trimsuffix"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.TrimSuffix(args[0].(string), args[1].(string)), nil
|
||||||
|
}
|
||||||
|
functions["reverse"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return reverseString(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
// encoding
|
||||||
|
functions["base64"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string)))
|
||||||
|
return sEnc, nil
|
||||||
|
}
|
||||||
|
functions["base64_decode"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string)))
|
||||||
|
return sEnc, nil
|
||||||
|
}
|
||||||
|
functions["url_encode"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return url.PathEscape(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
functions["url_decode"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return url.PathUnescape(args[0].(string))
|
||||||
|
}
|
||||||
|
functions["hex_encode"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return hex.EncodeToString([]byte(args[0].(string))), nil
|
||||||
|
}
|
||||||
|
functions["hex_decode"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
hx, _ := hex.DecodeString(args[0].(string))
|
||||||
|
return string(hx), nil
|
||||||
|
}
|
||||||
|
functions["html_escape"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return html.EscapeString(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
functions["html_unescape"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return html.UnescapeString(args[0].(string)), nil
|
||||||
|
}
|
||||||
|
// hashing
|
||||||
|
functions["md5"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
hash := md5.Sum([]byte(args[0].(string)))
|
||||||
|
return hex.EncodeToString(hash[:]), nil
|
||||||
|
}
|
||||||
|
functions["sha256"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return sha256.Sum256([]byte(args[0].(string))), nil
|
||||||
|
}
|
||||||
|
// search
|
||||||
|
functions["contains"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
return strings.Contains(args[0].(string), args[1].(string)), nil
|
||||||
|
}
|
||||||
|
functions["regex"] = func(args ...interface{}) (interface{}, error) {
|
||||||
|
compiled, err := regexp.Compile(args[0].(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return compiled.MatchString(args[1].(string)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package generators
|
||||||
|
|
||||||
|
// PitchforkGenerator Attack - Generate positional combinations from an input map with all values listed
|
||||||
|
// as slices of the same size
|
||||||
|
func PitchforkGenerator(payloads map[string][]string) (out chan map[string]interface{}) {
|
||||||
|
out = make(chan map[string]interface{})
|
||||||
|
|
||||||
|
size := 0
|
||||||
|
|
||||||
|
// check if all wordlists have the same size
|
||||||
|
for _, wordlist := range payloads {
|
||||||
|
if size == 0 {
|
||||||
|
size = len(wordlist)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(wordlist) != size {
|
||||||
|
//set size = 0 and exit the cycle
|
||||||
|
size = 0
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generator
|
||||||
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
element := make(map[string]interface{})
|
||||||
|
for name, wordlist := range payloads {
|
||||||
|
element[name] = wordlist[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
out <- element
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package generators
|
||||||
|
|
||||||
|
// SniperGenerator Attack - Generate sequential combinations
|
||||||
|
func SniperGenerator(payloads map[string][]string) (out chan map[string]interface{}) {
|
||||||
|
out = make(chan map[string]interface{})
|
||||||
|
|
||||||
|
// generator
|
||||||
|
go func() {
|
||||||
|
defer close(out)
|
||||||
|
|
||||||
|
for name, wordlist := range payloads {
|
||||||
|
for _, value := range wordlist {
|
||||||
|
element := CopyMapWithDefaultValue(payloads, "")
|
||||||
|
element[name] = value
|
||||||
|
out <- element
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package generators
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadWordlists creating proper data structure
|
||||||
|
func LoadWordlists(payloads map[string]string) map[string][]string {
|
||||||
|
wordlists := make(map[string][]string)
|
||||||
|
// load all wordlists
|
||||||
|
for name, filepath := range payloads {
|
||||||
|
wordlists[name] = LoadFile(filepath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wordlists
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadFile into slice of strings
|
||||||
|
func LoadFile(filepath string) (lines []string) {
|
||||||
|
for line := range StreamFile(filepath) {
|
||||||
|
lines = append(lines, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// StreamFile content to a chan
|
||||||
|
func StreamFile(filepath string) (content chan string) {
|
||||||
|
content = make(chan string)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(content)
|
||||||
|
file, err := os.Open(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// yql filter applied
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
content <- scanner.Text()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeMaps into a new one
|
||||||
|
func MergeMaps(m1, m2 map[string]interface{}) (m map[string]interface{}) {
|
||||||
|
m = make(map[string]interface{})
|
||||||
|
for k, v := range m1 {
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
for k, v := range m2 {
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func reverseString(s string) string {
|
||||||
|
runes := []rune(s)
|
||||||
|
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
runes[i], runes[j] = runes[j], runes[i]
|
||||||
|
}
|
||||||
|
return string(runes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyMap creates a new copy of an existing map
|
||||||
|
func CopyMap(originalMap map[string]interface{}) map[string]interface{} {
|
||||||
|
newMap := make(map[string]interface{})
|
||||||
|
for key, value := range originalMap {
|
||||||
|
newMap[key] = value
|
||||||
|
}
|
||||||
|
return newMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// CopyMapWithDefaultValue creates a new copy of an existing map and set a default value
|
||||||
|
func CopyMapWithDefaultValue(originalMap map[string][]string, defaultValue interface{}) map[string]interface{} {
|
||||||
|
newMap := make(map[string]interface{})
|
||||||
|
for key := range originalMap {
|
||||||
|
newMap[key] = defaultValue
|
||||||
|
}
|
||||||
|
return newMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// StringContainsAnyMapItem verifies is a string contains any value of a map
|
||||||
|
func StringContainsAnyMapItem(m map[string]interface{}, s string) bool {
|
||||||
|
for key := range m {
|
||||||
|
if strings.Contains(s, key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimDelimiters removes trailing brackets
|
||||||
|
func TrimDelimiters(s string) string {
|
||||||
|
return strings.TrimSuffix(strings.TrimPrefix(s, "{{"), "}}")
|
||||||
|
}
|
|
@ -1,15 +1,11 @@
|
||||||
package matchers
|
package matchers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/md5"
|
|
||||||
"crypto/sha256"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/hex"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Knetic/govaluate"
|
"github.com/Knetic/govaluate"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/generators"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompileMatchers performs the initial setup operation on a matcher
|
// CompileMatchers performs the initial setup operation on a matcher
|
||||||
|
@ -34,7 +30,7 @@ func (m *Matcher) CompileMatchers() error {
|
||||||
|
|
||||||
// Compile the dsl expressions
|
// Compile the dsl expressions
|
||||||
for _, dsl := range m.DSL {
|
for _, dsl := range m.DSL {
|
||||||
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(dsl, helperFunctions())
|
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(dsl, generators.HelperFunctions())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not compile dsl: %s", dsl)
|
return fmt.Errorf("could not compile dsl: %s", dsl)
|
||||||
}
|
}
|
||||||
|
@ -63,69 +59,3 @@ func (m *Matcher) CompileMatchers() error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func helperFunctions() (functions map[string]govaluate.ExpressionFunction) {
|
|
||||||
functions = make(map[string]govaluate.ExpressionFunction)
|
|
||||||
// strings
|
|
||||||
functions["len"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
length := len(args[0].(string))
|
|
||||||
return (float64)(length), nil
|
|
||||||
}
|
|
||||||
functions["toupper"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.ToUpper(args[0].(string)), nil
|
|
||||||
}
|
|
||||||
functions["tolower"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.ToLower(args[0].(string)), nil
|
|
||||||
}
|
|
||||||
functions["replace"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.Replace(args[0].(string), args[1].(string), args[2].(string), -1), nil
|
|
||||||
}
|
|
||||||
functions["trim"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.Trim(args[0].(string), args[2].(string)), nil
|
|
||||||
}
|
|
||||||
functions["trimleft"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.TrimLeft(args[0].(string), args[1].(string)), nil
|
|
||||||
}
|
|
||||||
functions["trimright"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.TrimRight(args[0].(string), args[1].(string)), nil
|
|
||||||
}
|
|
||||||
functions["trimspace"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.TrimSpace(args[0].(string)), nil
|
|
||||||
}
|
|
||||||
functions["trimprefix"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.TrimPrefix(args[0].(string), args[1].(string)), nil
|
|
||||||
}
|
|
||||||
functions["trimsuffix"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.TrimSuffix(args[0].(string), args[1].(string)), nil
|
|
||||||
}
|
|
||||||
// encoding
|
|
||||||
functions["base64"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string)))
|
|
||||||
return sEnc, nil
|
|
||||||
}
|
|
||||||
functions["base64_decode"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
sEnc := base64.StdEncoding.EncodeToString([]byte(args[0].(string)))
|
|
||||||
return sEnc, nil
|
|
||||||
}
|
|
||||||
// hashing
|
|
||||||
functions["md5"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
hash := md5.Sum([]byte(args[0].(string)))
|
|
||||||
return hex.EncodeToString(hash[:]), nil
|
|
||||||
}
|
|
||||||
functions["sha256"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return sha256.Sum256([]byte(args[0].(string))), nil
|
|
||||||
}
|
|
||||||
// search
|
|
||||||
functions["contains"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
return strings.Contains(args[0].(string), args[1].(string)), nil
|
|
||||||
}
|
|
||||||
functions["regex"] = func(args ...interface{}) (interface{}, error) {
|
|
||||||
compiled, err := regexp.Compile(args[0].(string))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return compiled.MatchString(args[1].(string)), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,17 +4,28 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Knetic/govaluate"
|
||||||
"github.com/projectdiscovery/nuclei/pkg/extractors"
|
"github.com/projectdiscovery/nuclei/pkg/extractors"
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/generators"
|
||||||
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
||||||
retryablehttp "github.com/projectdiscovery/retryablehttp-go"
|
retryablehttp "github.com/projectdiscovery/retryablehttp-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HTTPRequest contains a request to be made from a template
|
// HTTPRequest contains a request to be made from a template
|
||||||
type HTTPRequest struct {
|
type HTTPRequest struct {
|
||||||
|
// AttackType is the attack type
|
||||||
|
// Sniper, PitchFork and ClusterBomb. Default is Sniper
|
||||||
|
AttackType string `yaml:"attack,omitempty"`
|
||||||
|
// attackType is internal attack type
|
||||||
|
attackType generators.Type
|
||||||
|
// Path contains the path/s for the request variables
|
||||||
|
Payloads map[string]string `yaml:"payloads,omitempty"`
|
||||||
// Method is the request method, whether GET, POST, PUT, etc
|
// Method is the request method, whether GET, POST, PUT, etc
|
||||||
Method string `yaml:"method"`
|
Method string `yaml:"method"`
|
||||||
// Path contains the path/s for the request
|
// Path contains the path/s for the request
|
||||||
|
@ -52,8 +63,18 @@ func (r *HTTPRequest) SetMatchersCondition(condition matchers.ConditionType) {
|
||||||
r.matchersCondition = condition
|
r.matchersCondition = condition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAttackType returns the attack
|
||||||
|
func (r *HTTPRequest) GetAttackType() generators.Type {
|
||||||
|
return r.attackType
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAttackType sets the attack
|
||||||
|
func (r *HTTPRequest) SetAttackType(attack generators.Type) {
|
||||||
|
r.attackType = attack
|
||||||
|
}
|
||||||
|
|
||||||
// MakeHTTPRequest creates a *http.Request from a request configuration
|
// MakeHTTPRequest creates a *http.Request from a request configuration
|
||||||
func (r *HTTPRequest) MakeHTTPRequest(baseURL string) ([]*retryablehttp.Request, error) {
|
func (r *HTTPRequest) MakeHTTPRequest(baseURL string) (chan *retryablehttp.Request, error) {
|
||||||
parsed, err := url.Parse(baseURL)
|
parsed, err := url.Parse(baseURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -73,43 +94,100 @@ func (r *HTTPRequest) MakeHTTPRequest(baseURL string) ([]*retryablehttp.Request,
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeHTTPRequestFromModel creates a *http.Request from a request template
|
// MakeHTTPRequestFromModel creates a *http.Request from a request template
|
||||||
func (r *HTTPRequest) makeHTTPRequestFromModel(baseURL string, values map[string]interface{}) (requests []*retryablehttp.Request, err error) {
|
func (r *HTTPRequest) makeHTTPRequestFromModel(baseURL string, values map[string]interface{}) (requests chan *retryablehttp.Request, err error) {
|
||||||
replacer := newReplacer(values)
|
requests = make(chan *retryablehttp.Request)
|
||||||
|
|
||||||
|
// request generator
|
||||||
|
go func() {
|
||||||
|
defer close(requests)
|
||||||
for _, path := range r.Path {
|
for _, path := range r.Path {
|
||||||
|
// process base request
|
||||||
|
replacer := newReplacer(values)
|
||||||
|
|
||||||
// Replace the dynamic variables in the URL if any
|
// Replace the dynamic variables in the URL if any
|
||||||
URL := replacer.Replace(path)
|
URL := replacer.Replace(path)
|
||||||
|
|
||||||
// Build a request on the specified URL
|
// Build a request on the specified URL
|
||||||
req, err := http.NewRequest(r.Method, URL, nil)
|
req, err := http.NewRequest(r.Method, URL, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// find a way to pass the error
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
request, err := r.fillRequest(req, values)
|
request, err := r.fillRequest(req, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
// find a way to pass the error
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requests = append(requests, request)
|
requests <- request
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeHTTPRequestFromRaw creates a *http.Request from a raw request
|
// makeHTTPRequestFromRaw creates a *http.Request from a raw request
|
||||||
func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string, values map[string]interface{}) (requests []*retryablehttp.Request, err error) {
|
func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string, values map[string]interface{}) (requests chan *retryablehttp.Request, err error) {
|
||||||
replacer := newReplacer(values)
|
requests = make(chan *retryablehttp.Request)
|
||||||
|
// request generator
|
||||||
|
go func() {
|
||||||
|
defer close(requests)
|
||||||
|
|
||||||
for _, raw := range r.Raw {
|
for _, raw := range r.Raw {
|
||||||
// Add trailing line
|
// Add trailing line
|
||||||
raw += "\n"
|
raw += "\n"
|
||||||
|
|
||||||
|
if len(r.Payloads) > 0 {
|
||||||
|
basePayloads := generators.LoadWordlists(r.Payloads)
|
||||||
|
generatorFunc := generators.SniperGenerator
|
||||||
|
switch r.attackType {
|
||||||
|
case generators.PitchFork:
|
||||||
|
generatorFunc = generators.PitchforkGenerator
|
||||||
|
case generators.ClusterBomb:
|
||||||
|
generatorFunc = generators.ClusterbombGenerator
|
||||||
|
}
|
||||||
|
|
||||||
|
for genValues := range generatorFunc(basePayloads) {
|
||||||
|
baseValues := generators.CopyMap(values)
|
||||||
|
finValues := generators.MergeMaps(baseValues, genValues)
|
||||||
|
|
||||||
|
replacer := newReplacer(finValues)
|
||||||
|
|
||||||
// Replace the dynamic variables in the URL if any
|
// Replace the dynamic variables in the URL if any
|
||||||
raw = replacer.Replace(raw)
|
raw := replacer.Replace(raw)
|
||||||
|
|
||||||
|
dynamicValues := make(map[string]interface{})
|
||||||
|
// find all potentials tokens between {{}}
|
||||||
|
var re = regexp.MustCompile(`(?m)\{\{.+}}`)
|
||||||
|
for _, match := range re.FindAllString(raw, -1) {
|
||||||
|
// check if the match contains a dynamic variable
|
||||||
|
if generators.StringContainsAnyMapItem(finValues, match) {
|
||||||
|
expr := generators.TrimDelimiters(match)
|
||||||
|
compiled, err := govaluate.NewEvaluableExpressionWithFunctions(expr, generators.HelperFunctions())
|
||||||
|
if err != nil {
|
||||||
|
// Debug only - Remove
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
result, err := compiled.Evaluate(finValues)
|
||||||
|
if err != nil {
|
||||||
|
// Debug only - Remove
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
dynamicValues[expr] = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// replace dynamic values
|
||||||
|
dynamicReplacer := newReplacer(dynamicValues)
|
||||||
|
raw = dynamicReplacer.Replace(raw)
|
||||||
|
|
||||||
|
// log.Println(raw)
|
||||||
|
|
||||||
// Build a parsed request from raw
|
// Build a parsed request from raw
|
||||||
parsedReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw)))
|
parsedReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// requests generated from http.ReadRequest have incorrect RequestURI, so they
|
// requests generated from http.ReadRequest have incorrect RequestURI, so they
|
||||||
|
@ -118,7 +196,7 @@ func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string, values map[string]i
|
||||||
finalURL := fmt.Sprintf("%s%s", baseURL, parsedReq.URL)
|
finalURL := fmt.Sprintf("%s%s", baseURL, parsedReq.URL)
|
||||||
req, err := http.NewRequest(r.Method, finalURL, parsedReq.Body)
|
req, err := http.NewRequest(r.Method, finalURL, parsedReq.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy headers
|
// copy headers
|
||||||
|
@ -126,11 +204,49 @@ func (r *HTTPRequest) makeHTTPRequestFromRaw(baseURL string, values map[string]i
|
||||||
|
|
||||||
request, err := r.fillRequest(req, values)
|
request, err := r.fillRequest(req, values)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requests = append(requests, request)
|
requests <- request
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// otherwise continue with normal flow
|
||||||
|
|
||||||
|
// base request
|
||||||
|
replacer := newReplacer(values)
|
||||||
|
// Replace the dynamic variables in the request if any
|
||||||
|
raw = replacer.Replace(raw)
|
||||||
|
|
||||||
|
// Build a parsed request from raw
|
||||||
|
parsedReq, err := http.ReadRequest(bufio.NewReader(strings.NewReader(raw)))
|
||||||
|
if err != nil {
|
||||||
|
// find a way to pass the error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// requests generated from http.ReadRequest have incorrect RequestURI, so they
|
||||||
|
// cannot be used to perform another request directly, we need to generate a new one
|
||||||
|
// with the new target url
|
||||||
|
finalURL := fmt.Sprintf("%s%s", baseURL, parsedReq.URL)
|
||||||
|
req, err := http.NewRequest(r.Method, finalURL, parsedReq.Body)
|
||||||
|
if err != nil {
|
||||||
|
// find a way to pass the error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy headers
|
||||||
|
req.Header = parsedReq.Header.Clone()
|
||||||
|
|
||||||
|
request, err := r.fillRequest(req, values)
|
||||||
|
if err != nil {
|
||||||
|
// find a way to pass the error
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
requests <- request
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
return requests, nil
|
return requests, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package templates
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/projectdiscovery/nuclei/pkg/generators"
|
||||||
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
"github.com/projectdiscovery/nuclei/pkg/matchers"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
)
|
)
|
||||||
|
@ -33,6 +34,13 @@ func ParseTemplate(file string) (*Template, error) {
|
||||||
request.SetMatchersCondition(condition)
|
request.SetMatchersCondition(condition)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attack, ok := generators.AttackTypes[request.AttackType]
|
||||||
|
if !ok {
|
||||||
|
request.SetAttackType(generators.Sniper)
|
||||||
|
} else {
|
||||||
|
request.SetAttackType(attack)
|
||||||
|
}
|
||||||
|
|
||||||
for _, matcher := range request.Matchers {
|
for _, matcher := range request.Matchers {
|
||||||
if err = matcher.CompileMatchers(); err != nil {
|
if err = matcher.CompileMatchers(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
Loading…
Reference in New Issue