Skip to content

Commit

Permalink
Multiple bug fixes in query param fuzzing (#4925)
Browse files Browse the repository at this point in the history
* fuzz: check and handle typed slice

* do not query encode params + fuzz/allow duplicates params

* sometimes order matters ~query params

* component: fix broken iterator

* result upload add meta params
  • Loading branch information
tarunKoyalwar authored Mar 25, 2024
1 parent bc26817 commit c1bd4f8
Show file tree
Hide file tree
Showing 16 changed files with 420 additions and 129 deletions.
4 changes: 4 additions & 0 deletions internal/pdcp/writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import (
"time"

"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/retryablehttp-go"
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
errorutil "github.com/projectdiscovery/utils/errors"
updateutils "github.com/projectdiscovery/utils/update"
urlutil "github.com/projectdiscovery/utils/url"
)

Expand Down Expand Up @@ -217,6 +219,8 @@ func (u *UploadWriter) getRequest(bin []byte) (*retryablehttp.Request, error) {
if err != nil {
return nil, errorutil.NewWithErr(err).Msgf("could not create cloud upload request")
}
// add pdtm meta params
req.URL.RawQuery = updateutils.GetpdtmParams(config.Version)
req.Header.Set(pdcpauth.ApiKeyHeaderName, u.creds.APIKey)
req.Header.Set("Content-Type", "application/octet-stream")
req.Header.Set("Accept", "application/json")
Expand Down
23 changes: 13 additions & 10 deletions pkg/fuzz/component/body.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,17 @@ func (b *Body) Parse(req *retryablehttp.Request) (bool, error) {
}

b.value = NewValue(dataStr)
if b.value.Parsed() != nil {
tmp := b.value.Parsed()
if !tmp.IsNIL() {
return true, nil
}

switch {
case strings.Contains(contentType, "application/json") && b.value.Parsed() == nil:
case strings.Contains(contentType, "application/json") && tmp.IsNIL():
return b.parseBody(dataformat.JSONDataFormat, req)
case strings.Contains(contentType, "application/xml") && b.value.Parsed() == nil:
case strings.Contains(contentType, "application/xml") && tmp.IsNIL():
return b.parseBody(dataformat.XMLDataFormat, req)
case strings.Contains(contentType, "multipart/form-data") && b.value.Parsed() == nil:
case strings.Contains(contentType, "multipart/form-data") && tmp.IsNIL():
return b.parseBody(dataformat.MultiPartFormDataFormat, req)
}
parsed, err := b.parseBody(dataformat.FormDataFormat, req)
Expand Down Expand Up @@ -93,16 +94,18 @@ func (b *Body) parseBody(decoderName string, req *retryablehttp.Request) (bool,
}

// Iterate iterates through the component
func (b *Body) Iterate(callback func(key string, value interface{}) error) error {
for key, value := range b.value.Parsed() {
func (b *Body) Iterate(callback func(key string, value interface{}) error) (errx error) {
b.value.parsed.Iterate(func(key string, value any) bool {
if strings.HasPrefix(key, "#_") {
continue
return true
}
if err := callback(key, value); err != nil {
return err
errx = err
return false
}
}
return nil
return true
})
return
}

// SetValue sets a value in the component
Expand Down
34 changes: 20 additions & 14 deletions pkg/fuzz/component/cookie.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ package component

import (
"context"
"fmt"
"net/http"

"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
"github.com/projectdiscovery/retryablehttp-go"
mapsutil "github.com/projectdiscovery/utils/maps"
)

// Cookie is a component for a request cookie
Expand Down Expand Up @@ -35,29 +38,31 @@ func (c *Cookie) Parse(req *retryablehttp.Request) (bool, error) {
c.req = req
c.value = NewValue("")

parsedCookies := make(map[string]interface{})
parsedCookies := mapsutil.NewOrderedMap[string, any]()
for _, cookie := range req.Cookies() {
parsedCookies[cookie.Name] = cookie.Value
parsedCookies.Set(cookie.Name, cookie.Value)
}
if len(parsedCookies) == 0 {
if parsedCookies.Len() == 0 {
return false, nil
}
c.value.SetParsed(parsedCookies, "")
c.value.SetParsed(dataformat.KVOrderedMap(&parsedCookies), "")
return true, nil
}

// Iterate iterates through the component
func (c *Cookie) Iterate(callback func(key string, value interface{}) error) error {
for key, value := range c.value.Parsed() {
func (c *Cookie) Iterate(callback func(key string, value interface{}) error) (err error) {
c.value.parsed.Iterate(func(key string, value any) bool {
// Skip ignored cookies
if _, ok := defaultIgnoredCookieKeys[key]; ok {
continue
return ok
}
if err := callback(key, value); err != nil {
return err
if errx := callback(key, value); errx != nil {
err = errx
return false
}
}
return nil
return true
})
return
}

// SetValue sets a value in the component
Expand All @@ -83,13 +88,14 @@ func (c *Cookie) Rebuild() (*retryablehttp.Request, error) {
cloned := c.req.Clone(context.Background())

cloned.Header.Del("Cookie")
for key, value := range c.value.Parsed() {
c.value.parsed.Iterate(func(key string, value any) bool {
cookie := &http.Cookie{
Name: key,
Value: value.(string), // Assume the value is always a string for cookies
Value: fmt.Sprint(value), // Assume the value is always a string for cookies
}
cloned.AddCookie(cookie)
}
return true
})
return cloned, nil
}

Expand Down
40 changes: 22 additions & 18 deletions pkg/fuzz/component/headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"strings"

"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
"github.com/projectdiscovery/retryablehttp-go"
)

Expand Down Expand Up @@ -40,22 +41,24 @@ func (q *Header) Parse(req *retryablehttp.Request) (bool, error) {
}
parsedHeaders[key] = value
}
q.value.SetParsed(parsedHeaders, "")
q.value.SetParsed(dataformat.KVMap(parsedHeaders), "")
return true, nil
}

// Iterate iterates through the component
func (q *Header) Iterate(callback func(key string, value interface{}) error) error {
for key, value := range q.value.Parsed() {
func (q *Header) Iterate(callback func(key string, value interface{}) error) (errx error) {
q.value.parsed.Iterate(func(key string, value any) bool {
// Skip ignored headers
if _, ok := defaultIgnoredHeaderKeys[key]; ok {
continue
return ok
}
if err := callback(key, value); err != nil {
return err
errx = err
return false
}
}
return nil
return true
})
return
}

// SetValue sets a value in the component
Expand All @@ -79,22 +82,23 @@ func (q *Header) Delete(key string) error {
// component rebuilt
func (q *Header) Rebuild() (*retryablehttp.Request, error) {
cloned := q.req.Clone(context.Background())
for key, value := range q.value.parsed {
q.value.parsed.Iterate(func(key string, value any) bool {
if strings.EqualFold(key, "Host") {
cloned.Host = value.(string)
return true
}
switch v := value.(type) {
case []interface{}:
if vx, ok := IsTypedSlice(value); ok {
// convert to []interface{}
value = vx
}
if v, ok := value.([]interface{}); ok {
for _, vv := range v {
if cloned.Header[key] == nil {
cloned.Header[key] = make([]string, 0)
}
cloned.Header[key] = append(cloned.Header[key], vv.(string))
cloned.Header.Add(key, vv.(string))
}
case string:
cloned.Header[key] = []string{v}
return true
}
}
cloned.Header.Set(key, value.(string))
return true
})
return cloned, nil
}

Expand Down
14 changes: 8 additions & 6 deletions pkg/fuzz/component/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ func (q *Path) Parse(req *retryablehttp.Request) (bool, error) {
}

// Iterate iterates through the component
func (q *Path) Iterate(callback func(key string, value interface{}) error) error {
for key, value := range q.value.Parsed() {
if err := callback(key, value); err != nil {
return err
func (q *Path) Iterate(callback func(key string, value interface{}) error) (err error) {
q.value.parsed.Iterate(func(key string, value any) bool {
if errx := callback(key, value); errx != nil {
err = errx
return false
}
}
return nil
return true
})
return
}

// SetValue sets a value in the component
Expand Down
12 changes: 7 additions & 5 deletions pkg/fuzz/component/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,15 @@ func (q *Query) Parse(req *retryablehttp.Request) (bool, error) {
}

// Iterate iterates through the component
func (q *Query) Iterate(callback func(key string, value interface{}) error) error {
for key, value := range q.value.Parsed() {
func (q *Query) Iterate(callback func(key string, value interface{}) error) (errx error) {
q.value.parsed.Iterate(func(key string, value interface{}) bool {
if err := callback(key, value); err != nil {
return err
errx = err
return false
}
}
return nil
return true
})
return
}

// SetValue sets a value in the component
Expand Down
Loading

0 comments on commit c1bd4f8

Please sign in to comment.