Fixing input URLs handling with non-rfc paths

This commit is contained in:
mzack 2021-08-22 19:26:28 +02:00
parent 4f7f352677
commit 57c1244a16
5 changed files with 54 additions and 30 deletions

View File

@ -25,15 +25,14 @@ import (
// HTTPX represent an instance of the library client
type HTTPX struct {
client *retryablehttp.Client
client2 *http.Client
Filters []Filter
Options *Options
htmlPolicy *bluemonday.Policy
CustomHeaders map[string]string
RequestOverride *RequestOverride
cdn *cdncheck.Client
Dialer *fastdialer.Dialer
client *retryablehttp.Client
client2 *http.Client
Filters []Filter
Options *Options
htmlPolicy *bluemonday.Policy
CustomHeaders map[string]string
cdn *cdncheck.Client
Dialer *fastdialer.Dialer
}
// New httpx instance
@ -124,7 +123,6 @@ func New(options *Options) (*HTTPX, error) {
httpx.htmlPolicy = bluemonday.NewPolicy()
httpx.CustomHeaders = httpx.Options.CustomHeaders
httpx.RequestOverride = &options.RequestOverride
if options.CdnCheck || options.ExcludeCdn {
httpx.cdn, err = cdncheck.NewWithCache()
if err != nil {
@ -136,12 +134,12 @@ func New(options *Options) (*HTTPX, error) {
}
// Do http request
func (h *HTTPX) Do(req *retryablehttp.Request) (*Response, error) {
func (h *HTTPX) Do(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (*Response, error) {
timeStart := time.Now()
var gzipRetry bool
get_response:
httpresp, err := h.getResponse(req)
httpresp, err := h.getResponse(req, unsafeOptions)
if err != nil {
return nil, err
}
@ -236,33 +234,33 @@ get_response:
}
// RequestOverride contains the URI path to override the request
type RequestOverride struct {
type UnsafeOptions struct {
URIPath string
}
// getResponse returns response from safe / unsafe request
func (h *HTTPX) getResponse(req *retryablehttp.Request) (*http.Response, error) {
func (h *HTTPX) getResponse(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (*http.Response, error) {
if h.Options.Unsafe {
return h.doUnsafe(req)
return h.doUnsafeWithOptions(req, unsafeOptions)
}
return h.client.Do(req)
}
// doUnsafe does an unsafe http request
func (h *HTTPX) doUnsafe(req *retryablehttp.Request) (*http.Response, error) {
func (h *HTTPX) doUnsafeWithOptions(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (*http.Response, error) {
method := req.Method
headers := req.Header
targetURL := req.URL.String()
body := req.Body
options := rawhttp.DefaultOptions
options.Timeout = h.Options.Timeout
return rawhttp.DoRawWithOptions(method, targetURL, h.RequestOverride.URIPath, headers, body, options)
return rawhttp.DoRawWithOptions(method, targetURL, unsafeOptions.URIPath, headers, body, options)
}
// Verify the http calls and apply-cascade all the filters, as soon as one matches it returns true
func (h *HTTPX) Verify(req *retryablehttp.Request) (bool, error) {
resp, err := h.Do(req)
func (h *HTTPX) Verify(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (bool, error) {
resp, err := h.Do(req, unsafeOptions)
if err != nil {
return false, err
}

View File

@ -8,7 +8,6 @@ import (
type Options struct {
RandomAgent bool
DefaultUserAgent string
RequestOverride RequestOverride
HTTPProxy string
SocksProxy string
Threads int
@ -36,6 +35,7 @@ type Options struct {
Deny []string
MaxResponseBodySizeToSave int64
MaxResponseBodySizeToRead int64
UnsafeURI string
}
// DefaultOptions contains the default options

View File

@ -11,8 +11,8 @@ import (
const simMultiplier = 100
// IsVirtualHost checks if the target endpoint is a virtual host
func (h *HTTPX) IsVirtualHost(req *retryablehttp.Request) (bool, error) {
httpresp1, err := h.Do(req)
func (h *HTTPX) IsVirtualHost(req *retryablehttp.Request, unsafeOptions UnsafeOptions) (bool, error) {
httpresp1, err := h.Do(req, unsafeOptions)
if err != nil {
return false, err
}
@ -20,7 +20,7 @@ func (h *HTTPX) IsVirtualHost(req *retryablehttp.Request) (bool, error) {
// request a non-existing endpoint
req.Host = fmt.Sprintf("%s.%s", xid.New().String(), req.Host)
httpresp2, err := h.Do(req)
httpresp2, err := h.Do(req, unsafeOptions)
if err != nil {
return false, err
}

View File

@ -1,6 +1,7 @@
package stringz
import (
"net/url"
"strconv"
"strings"
@ -73,3 +74,13 @@ func RemoveURLDefaultPort(rawURL string) string {
}
return u.String()
}
func GetInvalidURI(rawURL string) (bool, string) {
if _, err := url.Parse(rawURL); err != nil {
if u, err := urlutil.Parse(rawURL); err == nil {
return true, u.RequestURI
}
}
return false, ""
}

View File

@ -87,7 +87,7 @@ func New(options *Options) (*Runner, error) {
httpxOptions.MaxRedirects = options.MaxRedirects
httpxOptions.HTTPProxy = options.HTTPProxy
httpxOptions.Unsafe = options.Unsafe
httpxOptions.RequestOverride = httpx.RequestOverride{URIPath: options.RequestURI}
httpxOptions.UnsafeURI = options.RequestURI
httpxOptions.CdnCheck = options.OutputCDN
httpxOptions.ExcludeCdn = options.ExcludeCDN
httpxOptions.RandomAgent = options.RandomAgent
@ -658,19 +658,25 @@ retry:
URL.Port = ""
}
if !scanopts.Unsafe {
var reqURI string
// retry with unsafe
if scanopts.Unsafe {
reqURI = URL.RequestURI + scanopts.RequestURI
// then create a base request without it to avoid go errors
URL.RequestURI = ""
} else {
// in case of standard requests append the new path to the existing one
URL.RequestURI += scanopts.RequestURI
}
req, err := hp.NewRequest(method, URL.String())
if err != nil {
return Result{URL: URL.String(), Input: origInput, err: err}
}
if customHost != "" {
req.Host = customHost
}
reqURI := req.URL.RequestURI()
hp.SetCustomHeaders(req, hp.CustomHeaders)
// We set content-length even if zero to allow net/http to follow 307/308 redirects (it fails on unknown size)
if scanopts.RequestBody != "" {
@ -683,7 +689,11 @@ retry:
r.ratelimiter.Take()
resp, err := hp.Do(req)
// with rawhttp we should say to the server to close the connection, otherwise it will remain open
if scanopts.Unsafe {
req.Header.Add("Connection", "close")
}
resp, err := hp.Do(req, httpx.UnsafeOptions{URIPath: reqURI})
if r.options.ShowStatistics {
r.stats.IncrementCounter("requests", 1)
}
@ -719,7 +729,12 @@ retry:
// if the full url doesn't end with the custom path we pick the original input value
if !stringsutil.HasSuffixAny(fullURL, scanopts.RequestURI) {
parsedURL, _ := urlutil.Parse(fullURL)
parsedURL.RequestURI = scanopts.RequestURI
if scanopts.Unsafe {
parsedURL.RequestURI = reqURI
} else {
parsedURL.RequestURI = scanopts.RequestURI
}
fullURL = parsedURL.String()
}
builder.WriteString(stringz.RemoveURLDefaultPort(fullURL))
@ -870,7 +885,7 @@ retry:
isvhost := false
if scanopts.VHost {
r.ratelimiter.Take()
isvhost, _ = hp.IsVirtualHost(req)
isvhost, _ = hp.IsVirtualHost(req, httpx.UnsafeOptions{})
if isvhost {
builder.WriteString(" [vhost]")
}