From 4ab7aa2c79fc7d736898b40c491b6548aebf597b Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Fri, 5 Jun 2020 20:45:48 +0200 Subject: [PATCH] adding server basic fingerprint based on server header --- cmd/httpx/httpx.go | 20 +++++++++++----- cmd/httpx/options.go | 50 +++++++++++++++++++++------------------- common/httpx/httpx.go | 2 ++ common/httpx/response.go | 14 +++++++++++ go.mod | 2 +- go.sum | 4 ++-- 6 files changed, 59 insertions(+), 33 deletions(-) diff --git a/cmd/httpx/httpx.go b/cmd/httpx/httpx.go index d442b9e..5e2b9f1 100644 --- a/cmd/httpx/httpx.go +++ b/cmd/httpx/httpx.go @@ -53,6 +53,7 @@ func main() { scanopts.StoreResponse = options.StoreResponse scanopts.StoreResponseDirectory = options.StoreResponseDir scanopts.Method = options.Method + scanopts.OutputServerHeader = options.OutputServerHeader // Try to create output folder if it doesnt exist if options.StoreResponse && options.StoreResponseDir != "" && options.StoreResponseDir != "." { @@ -78,7 +79,7 @@ func main() { defer f.Close() } for r := range output { - if r.Error != nil { + if r.err != nil { continue } row := r.str @@ -152,6 +153,7 @@ type scanOptions struct { OutputContentLength bool StoreResponse bool StoreResponseDirectory string + OutputServerHeader bool } func analyze(hp *httpx.HTTPX, protocol string, domain string, port int, scanopts *scanOptions, output chan Result) { @@ -164,7 +166,7 @@ retry: req, err := hp.NewRequest(scanopts.Method, URL) if err != nil { - output <- Result{URL: URL, Error: err} + output <- Result{URL: URL, err: err} return } @@ -172,7 +174,7 @@ retry: resp, err := hp.Do(req) if err != nil { - output <- Result{URL: URL, Error: err} + output <- Result{URL: URL, err: err} if !retried { if protocol == "https" { protocol = "http" @@ -212,6 +214,11 @@ retry: builder.WriteString(fmt.Sprintf(" [%s]", title)) } + serverHeader := resp.GetHeader("Server") + if scanopts.OutputServerHeader { + builder.WriteString(fmt.Sprintf(" [%s]", serverHeader)) + } + // check for virtual host isvhost := false if scanopts.VHost { @@ -227,7 +234,7 @@ retry: ioutil.WriteFile(responsePath, []byte(resp.Raw), 0644) } - output <- Result{URL: fullURL, ContentLength: resp.ContentLength, StatusCode: resp.StatusCode, Title: title, str: builder.String(), VHost: isvhost} + output <- Result{URL: fullURL, ContentLength: resp.ContentLength, StatusCode: resp.StatusCode, Title: title, str: builder.String(), VHost: isvhost, WebServer: serverHeader} } // Result of a scan @@ -237,8 +244,9 @@ type Result struct { StatusCode int `json:"status-code"` Title string `json:"title"` str string - Error error `json:"error"` - VHost bool `json:"vhost"` + err error + VHost bool `json:"vhost"` + WebServer string `json:"webserver"` } // JSON the result diff --git a/cmd/httpx/options.go b/cmd/httpx/options.go index f14a67d..481bf58 100644 --- a/cmd/httpx/options.go +++ b/cmd/httpx/options.go @@ -12,30 +12,31 @@ import ( // Options contains configuration options for chaos client. type Options struct { - RawRequestFile string - VHost bool - Smuggling bool - ExtractTitle bool - StatusCode bool - ContentLength bool - Retries int - Threads int - Timeout int - CustomHeaders customheader.CustomHeaders - CustomPorts customport.CustomPorts - Output string - FollowRedirects bool - StoreResponse bool - StoreResponseDir string - HttpProxy string - SocksProxy string - JSONOutput bool - InputFile string - Method string - Silent bool - Version bool - Verbose bool - NoColor bool + RawRequestFile string + VHost bool + Smuggling bool + ExtractTitle bool + StatusCode bool + ContentLength bool + Retries int + Threads int + Timeout int + CustomHeaders customheader.CustomHeaders + CustomPorts customport.CustomPorts + Output string + FollowRedirects bool + StoreResponse bool + StoreResponseDir string + HttpProxy string + SocksProxy string + JSONOutput bool + InputFile string + Method string + Silent bool + Version bool + Verbose bool + NoColor bool + OutputServerHeader bool } // ParseOptions parses the command line options for application @@ -63,6 +64,7 @@ func ParseOptions() *Options { flag.BoolVar(&options.Version, "version", false, "Show version of httpx") flag.BoolVar(&options.Verbose, "verbose", false, "Verbose Mode") flag.BoolVar(&options.NoColor, "no-color", false, "No Color") + flag.BoolVar(&options.OutputServerHeader, "web-server", false, "Prints out the Server header content") flag.Parse() // Read the inputs and configure the logging diff --git a/common/httpx/httpx.go b/common/httpx/httpx.go index c059bee..8767189 100644 --- a/common/httpx/httpx.go +++ b/common/httpx/httpx.go @@ -85,6 +85,8 @@ func (h *HTTPX) Do(req *retryablehttp.Request) (*Response, error) { return nil, err } + resp.Headers = httpresp.Header.Clone() + rawresp, err := httputil.DumpResponse(httpresp, true) if err != nil { return nil, err diff --git a/common/httpx/response.go b/common/httpx/response.go index 183aadb..819542b 100644 --- a/common/httpx/response.go +++ b/common/httpx/response.go @@ -1,5 +1,9 @@ package httpx +import ( + "strings" +) + // Response contains the response to a server type Response struct { StatusCode int @@ -10,3 +14,13 @@ type Response struct { Words int Lines int } + +// GetHeader value +func (r *Response) GetHeader(name string) string { + v, ok := r.Headers[name] + if ok { + return strings.Join(v, " ") + } + + return "" +} diff --git a/go.mod b/go.mod index d829f9b..83ec27c 100644 --- a/go.mod +++ b/go.mod @@ -12,5 +12,5 @@ require ( github.com/projectdiscovery/retryablehttp-go v1.0.1 github.com/remeh/sizedwaitgroup v1.0.0 github.com/rs/xid v1.2.1 - golang.org/x/net v0.0.0-20200528225125-3c3fba18258b + golang.org/x/net v0.0.0-20200602114024-627f9648deb9 ) diff --git a/go.sum b/go.sum index a02a06d..947cc64 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200528225125-3c3fba18258b h1:IYiJPiJfzktmDAO1HQiwjMjwjlYKHAL7KzeD544RJPs= -golang.org/x/net v0.0.0-20200528225125-3c3fba18258b/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9 h1:pNX+40auqi2JqRfOP1akLGtYcn15TUbkhwuCO3foqqM= +golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=