2020-10-20 21:17:02 +03:00
|
|
|
package runner
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"os"
|
|
|
|
"regexp"
|
|
|
|
|
|
|
|
"github.com/projectdiscovery/gologger"
|
2021-02-17 18:15:37 +03:00
|
|
|
"github.com/projectdiscovery/gologger/formatter"
|
2021-02-17 01:40:01 +03:00
|
|
|
"github.com/projectdiscovery/gologger/levels"
|
2020-10-20 21:17:02 +03:00
|
|
|
"github.com/projectdiscovery/httpx/common/customheader"
|
|
|
|
customport "github.com/projectdiscovery/httpx/common/customports"
|
|
|
|
"github.com/projectdiscovery/httpx/common/fileutil"
|
|
|
|
"github.com/projectdiscovery/httpx/common/stringz"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2021-04-06 15:43:55 +03:00
|
|
|
maxFileNameLength = 255
|
2020-10-20 21:17:02 +03:00
|
|
|
two = 2
|
|
|
|
)
|
|
|
|
|
|
|
|
type scanOptions struct {
|
|
|
|
Methods []string
|
|
|
|
StoreResponseDirectory string
|
|
|
|
RequestURI string
|
|
|
|
RequestBody string
|
|
|
|
VHost bool
|
|
|
|
OutputTitle bool
|
|
|
|
OutputStatusCode bool
|
|
|
|
OutputLocation bool
|
|
|
|
OutputContentLength bool
|
|
|
|
StoreResponse bool
|
|
|
|
OutputServerHeader bool
|
|
|
|
OutputWebSocket bool
|
|
|
|
OutputWithNoColor bool
|
|
|
|
OutputMethod bool
|
|
|
|
ResponseInStdout bool
|
|
|
|
TLSProbe bool
|
|
|
|
CSPProbe bool
|
2021-04-06 15:43:55 +03:00
|
|
|
VHostInput bool
|
2020-10-20 21:17:02 +03:00
|
|
|
OutputContentType bool
|
|
|
|
Unsafe bool
|
|
|
|
Pipeline bool
|
|
|
|
HTTP2Probe bool
|
|
|
|
OutputIP bool
|
|
|
|
OutputCName bool
|
|
|
|
OutputCDN bool
|
|
|
|
OutputResponseTime bool
|
|
|
|
PreferHTTPS bool
|
|
|
|
NoFallback bool
|
2021-04-06 15:43:55 +03:00
|
|
|
TechDetect bool
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
|
2020-12-22 02:32:46 +03:00
|
|
|
func (s *scanOptions) Clone() *scanOptions {
|
|
|
|
return &scanOptions{
|
|
|
|
Methods: s.Methods,
|
|
|
|
StoreResponseDirectory: s.StoreResponseDirectory,
|
|
|
|
RequestURI: s.RequestURI,
|
|
|
|
RequestBody: s.RequestBody,
|
|
|
|
VHost: s.VHost,
|
|
|
|
OutputTitle: s.OutputTitle,
|
|
|
|
OutputStatusCode: s.OutputStatusCode,
|
|
|
|
OutputLocation: s.OutputLocation,
|
|
|
|
OutputContentLength: s.OutputContentLength,
|
|
|
|
StoreResponse: s.StoreResponse,
|
|
|
|
OutputServerHeader: s.OutputServerHeader,
|
|
|
|
OutputWebSocket: s.OutputWebSocket,
|
|
|
|
OutputWithNoColor: s.OutputWithNoColor,
|
|
|
|
OutputMethod: s.OutputMethod,
|
|
|
|
ResponseInStdout: s.ResponseInStdout,
|
|
|
|
TLSProbe: s.TLSProbe,
|
|
|
|
CSPProbe: s.CSPProbe,
|
|
|
|
OutputContentType: s.OutputContentType,
|
|
|
|
Unsafe: s.Unsafe,
|
|
|
|
Pipeline: s.Pipeline,
|
|
|
|
HTTP2Probe: s.HTTP2Probe,
|
|
|
|
OutputIP: s.OutputIP,
|
|
|
|
OutputCName: s.OutputCName,
|
|
|
|
OutputCDN: s.OutputCDN,
|
|
|
|
OutputResponseTime: s.OutputResponseTime,
|
|
|
|
PreferHTTPS: s.PreferHTTPS,
|
|
|
|
NoFallback: s.NoFallback,
|
2021-04-06 16:03:40 +03:00
|
|
|
TechDetect: s.TechDetect,
|
2020-12-22 02:32:46 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-20 21:17:02 +03:00
|
|
|
// Options contains configuration options for chaos client.
|
|
|
|
type Options struct {
|
|
|
|
CustomHeaders customheader.CustomHeaders
|
|
|
|
CustomPorts customport.CustomPorts
|
|
|
|
matchStatusCode []int
|
|
|
|
matchContentLength []int
|
|
|
|
filterStatusCode []int
|
|
|
|
filterContentLength []int
|
|
|
|
Output string
|
|
|
|
StoreResponseDir string
|
|
|
|
HTTPProxy string
|
|
|
|
SocksProxy string
|
|
|
|
InputFile string
|
|
|
|
Methods string
|
|
|
|
RequestURI string
|
2020-12-20 04:37:45 +03:00
|
|
|
RequestURIs string
|
|
|
|
requestURIs []string
|
2020-10-20 21:17:02 +03:00
|
|
|
OutputMatchStatusCode string
|
|
|
|
OutputMatchContentLength string
|
|
|
|
OutputFilterStatusCode string
|
|
|
|
OutputFilterContentLength string
|
|
|
|
InputRawRequest string
|
|
|
|
rawRequest string
|
|
|
|
RequestBody string
|
|
|
|
OutputFilterString string
|
|
|
|
OutputMatchString string
|
|
|
|
OutputFilterRegex string
|
|
|
|
OutputMatchRegex string
|
|
|
|
Retries int
|
|
|
|
Threads int
|
|
|
|
Timeout int
|
|
|
|
filterRegex *regexp.Regexp
|
|
|
|
matchRegex *regexp.Regexp
|
|
|
|
VHost bool
|
2021-04-06 15:43:55 +03:00
|
|
|
VHostInput bool
|
2020-10-20 21:17:02 +03:00
|
|
|
Smuggling bool
|
|
|
|
ExtractTitle bool
|
|
|
|
StatusCode bool
|
|
|
|
Location bool
|
|
|
|
ContentLength bool
|
|
|
|
FollowRedirects bool
|
|
|
|
StoreResponse bool
|
|
|
|
JSONOutput bool
|
|
|
|
Silent bool
|
|
|
|
Version bool
|
|
|
|
Verbose bool
|
|
|
|
NoColor bool
|
|
|
|
OutputServerHeader bool
|
|
|
|
OutputWebSocket bool
|
|
|
|
responseInStdout bool
|
|
|
|
FollowHostRedirects bool
|
|
|
|
OutputMethod bool
|
|
|
|
TLSProbe bool
|
|
|
|
CSPProbe bool
|
|
|
|
OutputContentType bool
|
|
|
|
OutputIP bool
|
|
|
|
OutputCName bool
|
|
|
|
Unsafe bool
|
|
|
|
Debug bool
|
|
|
|
Pipeline bool
|
|
|
|
HTTP2Probe bool
|
|
|
|
OutputCDN bool
|
|
|
|
OutputResponseTime bool
|
|
|
|
NoFallback bool
|
2021-04-06 15:43:55 +03:00
|
|
|
TechDetect bool
|
2020-10-20 21:17:02 +03:00
|
|
|
protocol string
|
2020-11-15 23:21:17 +03:00
|
|
|
ShowStatistics bool
|
2020-12-21 02:13:27 +03:00
|
|
|
RandomAgent bool
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ParseOptions parses the command line options for application
|
|
|
|
func ParseOptions() *Options {
|
|
|
|
options := &Options{}
|
|
|
|
|
2021-04-06 15:43:55 +03:00
|
|
|
flag.BoolVar(&options.TechDetect, "tech-detect", false, "Perform wappalyzer based technology detection")
|
2020-10-20 21:17:02 +03:00
|
|
|
flag.IntVar(&options.Threads, "threads", 50, "Number of threads")
|
|
|
|
flag.IntVar(&options.Retries, "retries", 0, "Number of retries")
|
|
|
|
flag.IntVar(&options.Timeout, "timeout", 5, "Timeout in seconds")
|
|
|
|
flag.StringVar(&options.Output, "o", "", "File to write output to (optional)")
|
|
|
|
flag.BoolVar(&options.VHost, "vhost", false, "Check for VHOSTs")
|
2021-04-06 15:43:55 +03:00
|
|
|
flag.BoolVar(&options.VHostInput, "vhost-input", false, "Get a list of vhosts as input")
|
2020-10-20 21:17:02 +03:00
|
|
|
flag.BoolVar(&options.ExtractTitle, "title", false, "Extracts title")
|
|
|
|
flag.BoolVar(&options.StatusCode, "status-code", false, "Extracts status code")
|
|
|
|
flag.BoolVar(&options.Location, "location", false, "Extracts location header")
|
|
|
|
flag.Var(&options.CustomHeaders, "H", "Custom Header")
|
|
|
|
flag.Var(&options.CustomPorts, "ports", "ports range (nmap syntax: eg 1,2-10,11)")
|
|
|
|
flag.BoolVar(&options.ContentLength, "content-length", false, "Extracts content length")
|
|
|
|
flag.BoolVar(&options.StoreResponse, "sr", false, "Save response to file (default 'output')")
|
|
|
|
flag.StringVar(&options.StoreResponseDir, "srd", "output", "Save response directory")
|
|
|
|
flag.BoolVar(&options.FollowRedirects, "follow-redirects", false, "Follow Redirects")
|
|
|
|
flag.BoolVar(&options.FollowHostRedirects, "follow-host-redirects", false, "Only follow redirects on the same host")
|
|
|
|
flag.StringVar(&options.HTTPProxy, "http-proxy", "", "HTTP Proxy, eg http://127.0.0.1:8080")
|
|
|
|
flag.BoolVar(&options.JSONOutput, "json", false, "JSON Output")
|
|
|
|
flag.StringVar(&options.InputFile, "l", "", "File containing domains")
|
|
|
|
flag.StringVar(&options.Methods, "x", "", "Request Methods, use ALL to check all verbs ()")
|
|
|
|
flag.BoolVar(&options.OutputMethod, "method", false, "Output method")
|
|
|
|
flag.BoolVar(&options.Silent, "silent", false, "Silent mode")
|
|
|
|
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, "Extracts server header")
|
|
|
|
flag.BoolVar(&options.OutputWebSocket, "websocket", false, "Prints out if the server exposes a websocket")
|
|
|
|
flag.BoolVar(&options.responseInStdout, "response-in-json", false, "Server response directly in the tool output (-json only)")
|
|
|
|
flag.BoolVar(&options.TLSProbe, "tls-probe", false, "Send HTTP probes on the extracted TLS domains")
|
|
|
|
flag.BoolVar(&options.CSPProbe, "csp-probe", false, "Send HTTP probes on the extracted CSP domains")
|
|
|
|
flag.StringVar(&options.RequestURI, "path", "", "Request path/file (example '/api')")
|
2020-12-20 04:37:45 +03:00
|
|
|
flag.StringVar(&options.RequestURIs, "paths", "", "Command separated paths or file containing one path per line (example '/api/v1,/apiv2')")
|
2020-10-20 21:17:02 +03:00
|
|
|
flag.BoolVar(&options.OutputContentType, "content-type", false, "Extracts content-type")
|
|
|
|
flag.StringVar(&options.OutputMatchStatusCode, "mc", "", "Match status code")
|
|
|
|
flag.StringVar(&options.OutputMatchStatusCode, "ml", "", "Match content length")
|
|
|
|
flag.StringVar(&options.OutputFilterStatusCode, "fc", "", "Filter status code")
|
|
|
|
flag.StringVar(&options.OutputFilterContentLength, "fl", "", "Filter content length")
|
|
|
|
flag.StringVar(&options.InputRawRequest, "request", "", "File containing raw request")
|
|
|
|
flag.BoolVar(&options.Unsafe, "unsafe", false, "Send raw requests skipping golang normalization")
|
|
|
|
flag.StringVar(&options.RequestBody, "body", "", "Request Body")
|
|
|
|
flag.BoolVar(&options.Debug, "debug", false, "Debug mode")
|
|
|
|
flag.BoolVar(&options.Pipeline, "pipeline", false, "HTTP1.1 Pipeline")
|
|
|
|
flag.BoolVar(&options.HTTP2Probe, "http2", false, "HTTP2 probe")
|
|
|
|
flag.BoolVar(&options.OutputIP, "ip", false, "Output target ip")
|
|
|
|
flag.StringVar(&options.OutputFilterString, "filter-string", "", "Filter String")
|
|
|
|
flag.StringVar(&options.OutputMatchString, "match-string", "", "Match string")
|
|
|
|
flag.StringVar(&options.OutputFilterRegex, "filter-regex", "", "Filter Regex")
|
|
|
|
flag.StringVar(&options.OutputMatchRegex, "match-regex", "", "Match Regex")
|
|
|
|
flag.BoolVar(&options.OutputCName, "cname", false, "Output first cname")
|
|
|
|
flag.BoolVar(&options.OutputCDN, "cdn", false, "Check if domain's ip belongs to known CDN (akamai, cloudflare, ..)")
|
|
|
|
flag.BoolVar(&options.OutputResponseTime, "response-time", false, "Output the response time")
|
|
|
|
flag.BoolVar(&options.NoFallback, "no-fallback", false, "If HTTPS on port 443 is successful on default configuration, probes also port 80 for HTTP")
|
2020-11-15 23:21:17 +03:00
|
|
|
flag.BoolVar(&options.ShowStatistics, "stats", false, "Enable statistic on keypress (terminal may become unresponsive till the end)")
|
2020-12-21 02:13:27 +03:00
|
|
|
flag.BoolVar(&options.RandomAgent, "random-agent", false, "Use randomly selected HTTP User-Agent header value")
|
2020-10-20 21:17:02 +03:00
|
|
|
|
|
|
|
flag.Parse()
|
|
|
|
|
|
|
|
// Read the inputs and configure the logging
|
|
|
|
options.configureOutput()
|
|
|
|
|
|
|
|
showBanner()
|
|
|
|
|
|
|
|
if options.Version {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Info().Msgf("Current Version: %s\n", Version)
|
2020-10-20 21:17:02 +03:00
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
options.validateOptions()
|
|
|
|
|
|
|
|
return options
|
|
|
|
}
|
|
|
|
|
|
|
|
func (options *Options) validateOptions() {
|
|
|
|
if options.InputFile != "" && !fileutil.FileExists(options.InputFile) {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("File %s does not exist!\n", options.InputFile)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if options.InputRawRequest != "" && !fileutil.FileExists(options.InputRawRequest) {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("File %s does not exist!\n", options.InputRawRequest)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
var err error
|
|
|
|
if options.matchStatusCode, err = stringz.StringToSliceInt(options.OutputMatchStatusCode); err != nil {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("Invalid value for match status code option: %s\n", err)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.matchContentLength, err = stringz.StringToSliceInt(options.OutputMatchContentLength); err != nil {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("Invalid value for match content length option: %s\n", err)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.filterStatusCode, err = stringz.StringToSliceInt(options.OutputFilterStatusCode); err != nil {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("Invalid value for filter status code option: %s\n", err)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.filterContentLength, err = stringz.StringToSliceInt(options.OutputFilterContentLength); err != nil {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("Invalid value for filter content length option: %s\n", err)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.OutputFilterRegex != "" {
|
|
|
|
if options.filterRegex, err = regexp.Compile(options.OutputFilterRegex); err != nil {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("Invalid value for regex filter option: %s\n", err)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if options.OutputMatchRegex != "" {
|
|
|
|
if options.matchRegex, err = regexp.Compile(options.OutputMatchRegex); err != nil {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.Fatal().Msgf("Invalid value for match regex option: %s\n", err)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// configureOutput configures the output on the screen
|
|
|
|
func (options *Options) configureOutput() {
|
|
|
|
// If the user desires verbose output, show verbose output
|
|
|
|
if options.Verbose {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.Debug {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.DefaultLogger.SetMaxLevel(levels.LevelDebug)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.NoColor {
|
2021-02-17 18:15:37 +03:00
|
|
|
gologger.DefaultLogger.SetFormatter(formatter.NewCLI(true))
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
if options.Silent {
|
2021-02-17 01:40:01 +03:00
|
|
|
gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent)
|
2020-10-20 21:17:02 +03:00
|
|
|
}
|
|
|
|
}
|