From 7f7cbc526a69ee2d3ecafa2ca302f5d87fd63ffd Mon Sep 17 00:00:00 2001 From: Giuseppe Trotta <704560+ohpe@users.noreply.github.com> Date: Wed, 2 Mar 2022 11:48:52 +0100 Subject: [PATCH] Fix #65 Due to backward compatibility to Go 1, it won't be possible to avoid net.url to sort query strings. Therefore, a custom core/url.go was introduced for this purpose. --- core/proxy/handler.go | 14 ++----- core/url.go | 95 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 core/url.go diff --git a/core/proxy/handler.go b/core/proxy/handler.go index 5a9fe9f..9e32170 100644 --- a/core/proxy/handler.go +++ b/core/proxy/handler.go @@ -13,6 +13,7 @@ import ( "github.com/evilsocket/islazy/tui" . "github.com/logrusorgru/aurora" + "github.com/muraenateam/muraena/core" "github.com/pkg/errors" "github.com/muraenateam/muraena/core/db" @@ -147,8 +148,8 @@ func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error) // // HEADERS // - // Transform query string - query, err := url.ParseQuery(request.URL.RawQuery) + // Transform query string using internal core.url instead of net.url + query, err := core.ParseQuery(request.URL.RawQuery) if err != nil { log.Error("URL: %s \n %s", request.URL, err.Error()) } @@ -159,15 +160,6 @@ func (muraena *MuraenaProxy) RequestProcessor(request *http.Request) (err error) } } - // FIX #65. If the query string ends with a key without value, such as: victim.com/a?test - // the query.Encode() automatically adds an equal sign at the end it i.e. victim.com/a?test= - realLast := request.URL.RawQuery[len(request.URL.RawQuery)-1:] - request.URL.RawQuery = query.Encode() - newLast := request.URL.RawQuery[len(request.URL.RawQuery)-1:] - if newLast == "=" && realLast != "=" { - request.URL.RawQuery = strings.TrimSuffix(request.URL.RawQuery, "=") - } - // Remove headers for _, header := range sess.Config.Remove.Request.Headers { request.Header.Del(header) diff --git a/core/url.go b/core/url.go new file mode 100644 index 0000000..d855948 --- /dev/null +++ b/core/url.go @@ -0,0 +1,95 @@ +package core + +import ( + "net/url" + "strings" +) + +// Values maps a string key to a list of values. +// It is typically used for query parameters and form values. +// Unlike in the http.Header map, the keys in a Values map +// are case-sensitive. +type Values map[string][]string + +// ParseQuery parses the URL-encoded query string and returns +// a map listing the values specified for each key. +// ParseQuery always returns a non-nil map containing all the +// valid query parameters found; err describes the first decoding error +// encountered, if any. +// +// Query is expected to be a list of key=value settings separated by ampersands. +// A setting without an equals sign is interpreted as a key set to an empty +// value. +// Settings containing a non-URL-encoded semicolon are considered invalid. +func ParseQuery(query string) (Values, error) { + m := make(Values) + err := parseQuery(m, query) + return m, err +} + +func parseQuery(m Values, query string) (err error) { + for query != "" { + key := query + if i := strings.IndexAny(key, "&"); i >= 0 { + key, query = key[:i], key[i+1:] + } else { + query = "" + } + //if strings.Contains(key, ";") { + // err = fmt.Errorf("invalid semicolon separator in query") + // continue + //} + if key == "" { + continue + } + value := "" + if i := strings.Index(key, "="); i >= 0 { + key, value = key[:i], key[i+1:] + } + key, err1 := url.QueryUnescape(key) + if err1 != nil { + if err == nil { + err = err1 + } + continue + } + value, err1 = url.QueryUnescape(value) + if err1 != nil { + if err == nil { + err = err1 + } + continue + } + m[key] = append(m[key], value) + } + return err +} + +// Encode encodes the values into ``URL encoded'' form +// ("bar=baz&foo=quux") NOT sorted by key. +func (v Values) Encode() string { + if v == nil { + return "" + } + var buf strings.Builder + keys := make([]string, 0, len(v)) + for k := range v { + keys = append(keys, k) + } + + // We don't want to be sorted + // sort.Strings(keys) + for _, k := range keys { + vs := v[k] + keyEscaped := url.QueryEscape(k) + for _, v := range vs { + if buf.Len() > 0 { + buf.WriteByte('&') + } + buf.WriteString(keyEscaped) + buf.WriteByte('=') + buf.WriteString(url.QueryEscape(v)) + } + } + return buf.String() +}