1
1
Fork 0
mirror of https://github.com/OJ/gobuster.git synced 2024-06-01 18:26:03 +02:00
gobuster/gobustervhost/gobustervhost.go

233 lines
5.9 KiB
Go
Raw Normal View History

2018-12-11 21:41:42 +01:00
package gobustervhost
import (
"bufio"
"bytes"
"context"
"fmt"
"net/url"
"strings"
"text/tabwriter"
2019-01-09 22:53:56 +01:00
"github.com/OJ/gobuster/v3/libgobuster"
2019-03-28 00:04:48 +01:00
"github.com/google/uuid"
2018-12-11 21:41:42 +01:00
)
// GobusterVhost is the main type to implement the interface
type GobusterVhost struct {
2019-03-28 00:04:48 +01:00
options *OptionsVhost
globalopts *libgobuster.Options
http *libgobuster.HTTPClient
domain string
2019-04-13 12:48:26 +02:00
baseline1 []byte
baseline2 []byte
2018-12-11 21:41:42 +01:00
}
// NewGobusterVhost creates a new initialized GobusterDir
func NewGobusterVhost(cont context.Context, globalopts *libgobuster.Options, opts *OptionsVhost) (*GobusterVhost, error) {
if globalopts == nil {
return nil, fmt.Errorf("please provide valid global options")
}
if opts == nil {
return nil, fmt.Errorf("please provide valid plugin options")
}
g := GobusterVhost{
options: opts,
globalopts: globalopts,
}
2020-05-08 10:59:45 +02:00
basicOptions := libgobuster.BasicHTTPOptions{
Proxy: opts.Proxy,
Timeout: opts.Timeout,
UserAgent: opts.UserAgent,
}
2018-12-11 21:41:42 +01:00
httpOpts := libgobuster.HTTPOptions{
2020-05-08 10:59:45 +02:00
BasicHTTPOptions: basicOptions,
FollowRedirect: opts.FollowRedirect,
2020-06-20 14:58:27 +02:00
NoTLSValidation: opts.NoTLSValidation,
2020-05-08 10:59:45 +02:00
Username: opts.Username,
Password: opts.Password,
Headers: opts.Headers,
Cookies: opts.Cookies,
Method: opts.Method,
2018-12-11 21:41:42 +01:00
}
h, err := libgobuster.NewHTTPClient(cont, &httpOpts)
if err != nil {
return nil, err
}
g.http = h
return &g, nil
}
2020-05-08 10:59:45 +02:00
// Name should return the name of the plugin
func (v *GobusterVhost) Name() string {
return "VHOST enumeration"
}
// RequestsPerRun returns the number of requests this plugin makes per single wordlist item
func (v *GobusterVhost) RequestsPerRun() int {
return 1
}
2018-12-11 21:41:42 +01:00
// PreRun is the pre run implementation of gobusterdir
func (v *GobusterVhost) PreRun() error {
// add trailing slash
if !strings.HasSuffix(v.options.URL, "/") {
v.options.URL = fmt.Sprintf("%s/", v.options.URL)
}
url, err := url.Parse(v.options.URL)
if err != nil {
2020-05-08 10:59:45 +02:00
return fmt.Errorf("invalid url %s: %w", v.options.URL, err)
2018-12-11 21:41:42 +01:00
}
v.domain = url.Host
2019-03-28 00:04:48 +01:00
// request default vhost for baseline1
2020-05-08 10:59:45 +02:00
_, _, tmp, err := v.http.Request(v.options.URL, libgobuster.RequestOptions{ReturnBody: true})
2018-12-11 21:41:42 +01:00
if err != nil {
2020-05-08 10:59:45 +02:00
return fmt.Errorf("unable to connect to %s: %w", v.options.URL, err)
2018-12-11 21:41:42 +01:00
}
2020-05-08 10:59:45 +02:00
v.baseline1 = tmp
2019-03-28 00:04:48 +01:00
// request non existent vhost for baseline2
subdomain := fmt.Sprintf("%s.%s", uuid.New(), v.domain)
2020-05-08 10:59:45 +02:00
_, _, tmp, err = v.http.Request(v.options.URL, libgobuster.RequestOptions{Host: subdomain, ReturnBody: true})
2019-03-28 00:04:48 +01:00
if err != nil {
2020-05-08 10:59:45 +02:00
return fmt.Errorf("unable to connect to %s: %w", v.options.URL, err)
2019-03-28 00:04:48 +01:00
}
2020-05-08 10:59:45 +02:00
v.baseline2 = tmp
2018-12-11 21:41:42 +01:00
return nil
}
// Run is the process implementation of gobusterdir
func (v *GobusterVhost) Run(word string) ([]libgobuster.Result, error) {
subdomain := fmt.Sprintf("%s.%s", word, v.domain)
2020-05-08 10:59:45 +02:00
status, size, body, err := v.http.Request(v.options.URL, libgobuster.RequestOptions{Host: subdomain, ReturnBody: true})
2018-12-11 21:41:42 +01:00
var ret []libgobuster.Result
if err != nil {
return ret, err
}
2019-03-28 00:04:48 +01:00
// subdomain must not match default vhost and non existent vhost
2019-03-29 17:23:12 +01:00
// or verbose mode is enabled
2020-05-08 10:59:45 +02:00
found := !bytes.Equal(body, v.baseline1) && !bytes.Equal(body, v.baseline2)
2019-03-29 17:23:12 +01:00
if found || v.globalopts.Verbose {
resultStatus := libgobuster.StatusMissed
if found {
resultStatus = libgobuster.StatusFound
2019-03-29 07:54:01 +01:00
}
result := libgobuster.Result{
2019-03-29 17:23:12 +01:00
Entity: subdomain,
StatusCode: *status,
Size: &size,
Status: resultStatus,
2018-12-11 21:41:42 +01:00
}
ret = append(ret, result)
}
return ret, nil
}
// ResultToString is the to string implementation of gobusterdir
func (v *GobusterVhost) ResultToString(r *libgobuster.Result) (*string, error) {
buf := &bytes.Buffer{}
2019-03-29 07:54:01 +01:00
statusText := "Found"
2019-03-29 17:23:12 +01:00
if r.Status == libgobuster.StatusMissed {
2019-03-29 07:54:01 +01:00
statusText = "Missed"
}
2019-03-29 17:23:12 +01:00
if _, err := fmt.Fprintf(buf, "%s: %s (Status: %d) [Size: %d]\n", statusText, r.Entity, r.StatusCode, *r.Size); err != nil {
2018-12-11 21:41:42 +01:00
return nil, err
}
s := buf.String()
return &s, nil
}
// GetConfigString returns the string representation of the current config
func (v *GobusterVhost) GetConfigString() (string, error) {
var buffer bytes.Buffer
bw := bufio.NewWriter(&buffer)
tw := tabwriter.NewWriter(bw, 0, 5, 3, ' ', 0)
o := v.options
if _, err := fmt.Fprintf(tw, "[+] Url:\t%s\n", o.URL); err != nil {
return "", err
}
2020-05-08 10:59:45 +02:00
if _, err := fmt.Fprintf(tw, "[+] Method:\t%s\n", o.Method); err != nil {
return "", err
}
2018-12-11 21:41:42 +01:00
if _, err := fmt.Fprintf(tw, "[+] Threads:\t%d\n", v.globalopts.Threads); err != nil {
return "", err
}
if v.globalopts.Delay > 0 {
if _, err := fmt.Fprintf(tw, "[+] Delay:\t%s\n", v.globalopts.Delay); err != nil {
return "", err
}
}
2018-12-11 21:41:42 +01:00
wordlist := "stdin (pipe)"
if v.globalopts.Wordlist != "-" {
wordlist = v.globalopts.Wordlist
}
if _, err := fmt.Fprintf(tw, "[+] Wordlist:\t%s\n", wordlist); err != nil {
return "", err
}
2020-05-08 10:59:45 +02:00
if v.globalopts.PatternFile != "" {
if _, err := fmt.Fprintf(tw, "[+] Patterns:\t%s (%d entries)\n", v.globalopts.PatternFile, len(v.globalopts.Patterns)); err != nil {
return "", err
}
}
2018-12-11 21:41:42 +01:00
if o.Proxy != "" {
if _, err := fmt.Fprintf(tw, "[+] Proxy:\t%s\n", o.Proxy); err != nil {
return "", err
}
}
if o.Cookies != "" {
if _, err := fmt.Fprintf(tw, "[+] Cookies:\t%s\n", o.Cookies); err != nil {
return "", err
}
}
if o.UserAgent != "" {
if _, err := fmt.Fprintf(tw, "[+] User Agent:\t%s\n", o.UserAgent); err != nil {
return "", err
}
}
if o.Username != "" {
if _, err := fmt.Fprintf(tw, "[+] Auth User:\t%s\n", o.Username); err != nil {
return "", err
}
}
if v.globalopts.Verbose {
if _, err := fmt.Fprintf(tw, "[+] Verbose:\ttrue\n"); err != nil {
return "", err
}
}
if _, err := fmt.Fprintf(tw, "[+] Timeout:\t%s\n", o.Timeout.String()); err != nil {
return "", err
}
if err := tw.Flush(); err != nil {
2020-05-08 10:59:45 +02:00
return "", fmt.Errorf("error on tostring: %w", err)
2018-12-11 21:41:42 +01:00
}
if err := bw.Flush(); err != nil {
2020-05-08 10:59:45 +02:00
return "", fmt.Errorf("error on tostring: %w", err)
2018-12-11 21:41:42 +01:00
}
return strings.TrimSpace(buffer.String()), nil
}