httpfuzz

package module
v0.0.0-...-f57650e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 26, 2021 License: GPL-3.0 Imports: 17 Imported by: 2

README

httpfuzz

PkgGoDev

httpfuzz is a fast HTTP fuzzer written in Go inspired by Burp Intruder. It takes a seed request and uses a wordlist to generate requests. For a wordlist with m words and a seed request with n injection points, httpfuzz will generate m * n requests. It can be used as a library, but is meant to be used with the included httpfuzz CLI. It allows fuzzing of HTTP requests with text bodies and multipart file uploads.

File Fuzzing

httpfuzz can generate files to help you quickly test a file upload endpoints for file header whitelisting using the --automatic-file-payloads flag. It generates random bytes and puts a valid file headers on them before injecting them into the request body and sending them to the web service. This lets you easily see if a dev team is only using filenames to validate image uploads ;). You can see a list of the supported file types in fileheaders.go. Feel free to add any file types you see missing.

If you want to use httpfuzz with existing payloads, simply place them in a directory and pass it to the payload-dir flag.

Using httpfuzz CLI

NAME:
   httpfuzz - fuzz endpoints based on a HTTP request file

USAGE:
   httpfuzz [global options] command [command options] [arguments...]

COMMANDS:
   help, h  Shows a list of commands or help for one command

GLOBAL OPTIONS:
   --count-only                 don't send the requests, just count how many would be sent (default: false)
   --seed-request value         the request to be fuzzed
   --delay-ms value             the delay between each HTTP request in milliseconds (default: 0)
   --wordlist value             newline separated wordlist for the fuzzer
   --target-header value        HTTP headers to fuzz
   --https                      (default: false)
   --skip-cert-verify           skip verifying SSL certificate when making requests (default: false)
   --proxy-url value            HTTP proxy to send requests through
   --proxy-ca-pem value         PEM encoded CA Certificate for TLS requests through a proxy
   --target-param value         URL Query string param to fuzz
   --target-path-arg value      URL path argument to fuzz
   --dirbuster                  brute force directory names from wordlist (default: false)
   --target-delimiter value     delimiter to mark targets in request bodies (default: "`")
   --multipart-file-name value  name of the file field to fuzz in multipart request
   --multipart-form-name value  name of the form field to fuzz in multipart request
   --fuzz-file-size value       file size of autogenerated files for fuzzing multipart request (default: 1024)
   --payload-dir value          directory with payload files to attempt to upload using the fuzzer
   --automatic-file-payloads    enable this flag to automatically generate files for fuzzing (default: false)
   --target-filename value      fuzz files but also fuzz the filename using the provided wordlist
   --post-request value         plugin binary for processing requests and responses
   --log-output                 enable to log results to stdout (default: false)
   --help, -h                   show help (default: false)

Seed requests are a text HTTP request. You can tag injection points in request bodies by surrounding them with the delimiter character specified at program startup with the --target-delimiter flag. By default, it's `. You can fuzz other parts of the request with CLI flags.

Post-Request Plugins

httpfuzz supports Go plugins so you can use the full power of Go to analyse requests and responses. An httpfuzz plugin is a regular Go plugin with a function called New that implements httpfuzz.InitializerFunc. You can use plugins to save request-response transactions to disk, log them to a database or perform multi-stage attacks. If you need to configure your plugins, use environment variables prefixed with your plugin's name to minimizes collisions.

// InitializerFunc is a go function that should be exported by a function package.
// It should be named "New".
// Your InitializerFunc should return an instance of your Listener with a reference to httpfuzz's logger for consistent logging.
type InitializerFunc func(*log.Logger) (Listener, error)

The httpfuzz.Listener interface has one method: Listen.

// Listener must be implemented by a plugin to users to hook the request - response transaction.
// The Listen method will be run in its own goroutine, so plugins cannot block the rest of the program, however panics can take down the entire process.
type Listener interface {
	Listen(results <-chan *Result)
}

Listen implementations will receive a stream of httpfuzz.Result. These contain the httpfuzz.Request, the payload and the httpfuzz.Response, along with some other metadata.

// Result is the request, response and associated metadata to be processed by plugins.
type Result struct {
	Request     *Request
	Response    *Response
	Payload     string
	Location    string
	FieldName   string
	TimeElapsed time.Duration
}

After you've created a plugin, build it using go build -buildmode=plugin yourplugin.go and load it to httpfuzz with the --post-request

You can see example plugins in exampleplugins/

Examples

Fuzzing POST requests
POST /api/devices HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.26.3
Accept: */*
Cache-Control: no-cache
Postman-Token: c5bcc2bc-90b4-4d06-b851-1cc670cd9afa
Host: localhost:8000
Accept-Encoding: gzip, deflate
Connection: close
Content-Length: 35

{
	"name": "`S9`",
	"os": "Android"
}

The backticks (`) indicate a spot in the request body to inject payloads from the wordlist.

httpfuzz \
   --wordlist testdata/useragents.txt \
   --seed-request testdata/validPOST.request \
   --target-header User-Agent \
   --target-header Host \
   --delay-ms 50 \
   --target-header Pragma \
   --skip-cert-verify \
   --proxy-url http://localhost:8080 \
   --target-param fuzz \
   --dirbuster

In the above example, httpfuzz will insert values from the wordlist into the name field, the Pragma, User-Agent and Host headers, the end of the URL (like dirbuster) and the URL parameter fuzz.

Fuzzing multipart file uploads

httpfuzz can fuzz multipart files, both with automatically generated files and custom payloads supplied in a directory. It can also fuzz filenames using the main wordlist to check for common file upload vulnerabilities like XSS and path injection.

POST /uploadFile HTTP/1.1
Host: localhost:8000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:79.0) Gecko/20100101 Firefox/79.0
Content-Length: 1309
Accept: application/json
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.5
Cache-Control: no-cache
Connection: close
Content-Type: multipart/form-data; boundary=---------------------------416891666813988703772682177556
X-Requested-With: XMLHttpRequest

-----------------------------416891666813988703772682177556
Content-Disposition: form-data; name="file"; filename="image.png"
Content-Type: image/png

*image data here. real request is in validuploadPOST.request*
-----------------------------416891666813988703772682177556--

-----------------------------416891666813988703772682177556--

That request uploads a PNG file. You can fuzz it with the following command:

httpfuzz \
 --wordlist testdata/useragents.txt \
 --seed-request testdata/validuploadPOST.request \
 --target-header User-Agent \
 --target-header Host \
 --delay-ms 50 \
 --target-header Pragma \
 --proxy-url http://localhost:8080 \
 --target-param fuzz \
 --dirbuster \
 --fuzz-file-size 4096 \
 --multipart-form-name field \
 --multipart-file-name file \
 --automatic-file-payloads \
 --target-filename file \
 --payload-dir ./testpayloads

This command will fuzz a multipart form field called field and the file field file with randomly generated 4KB (4096 bytes) files and any payloads in the ./testpayloads directory and filenames from the wordlist testdata/useragents.txt. You can still fuzz the other injection points, but delimiter injection will not work, since binary files can contain any character they want.

Building httpfuzz

To build httpfuzz, simply run go build -o httpfuzz cmd/httpfuzz.go. You can run the tests with go test -v.

Documentation

Overview

Package httpfuzz is a fast fuzzer that allows you to easily fuzz HTTP endpoints. It works in a similar way to Burp Intruder, but it doesn't read the entire wordlist into memory. Instead, it calculates how many requests it's going to send ahead of time and streams through the wordlist line-by-line, using go's sync.WaitGroup to wait until the last request finishes.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func NativeSupportedFileTypes

func NativeSupportedFileTypes() []string

NativeSupportedFileTypes returns a list of file types httpfuzz can generate by default.

Types

type Client

type Client struct {
	*http.Client
}

Client is a modified net/http Client that can natively handle our request and response types

func (*Client) Do

func (c *Client) Do(req *Request) (*Response, error)

Do wraps Go's net/http client with our Request and Response types.

type Config

type Config struct {
	TargetHeaders             []string
	TargetParams              []string
	TargetPathArgs            []string
	TargetFileKeys            []string
	TargetMultipartFieldNames []string
	FilesystemPayloads        []string
	TargetFilenames           []string
	LogSuccess                bool
	EnableGeneratedPayloads   bool
	FuzzFileSize              int64
	FuzzDirectory             bool
	Wordlist                  *Wordlist
	Seed                      *Request
	Client                    *Client
	RequestDelay              time.Duration
	Plugins                   *PluginBroker
	Logger                    *log.Logger
	URLScheme                 string
	TargetDelimiter           byte
	// contains filtered or unexported fields
}

Config holds all fuzzer configuration.

type DelimiterArray

type DelimiterArray struct {
	Contents []byte
}

A DelimiterArray finds the positions of a delimiter within a byte slice. It is faster than SuffixArray for our use case since we only need the position of a single byte instead of a group of bytes.

func (*DelimiterArray) Get

func (d *DelimiterArray) Get(position int, delimiter byte) (int, int, error)

Get returns the offsets for a delimiter position.

func (*DelimiterArray) Lookup

func (d *DelimiterArray) Lookup(delimiter byte) []int

Lookup returns the offsets within a byte slice a particular delimiter is at in O(n) time.

type File

type File struct {
	Name     string
	FileType string
	Header   []byte
	Size     int64
	Payload  []byte
}

File is a generated file of a given type with associated metadata.

func FileFrom

func FileFrom(path string, extraExtension string) (*File, error)

FileFrom loads a file from the filesystem and wraps it in our native File type.

func GenerateFile

func GenerateFile(fileType string, size int64, extraExtension string) (*File, error)

GenerateFile creates valid files of a given type with zeroes in the body. It is meant to fuzz files to test file upload.

type Fuzzer

type Fuzzer struct {
	*Config
}

Fuzzer creates HTTP requests from a seed request using the combination of inputs specified in the config. It uses the producer-consumer pattern to efficiently handle large wordlists.

func (*Fuzzer) GenerateRequests

func (f *Fuzzer) GenerateRequests() (<-chan *Job, <-chan error)

GenerateRequests begins generating HTTP requests based on the seed request and sends them into the returned channel. It streams the wordlist from the filesystem line-by-line so it can handle wordlists in constant time. The trade-off is that callers cannot know ahead of time how many requests will be sent.

func (*Fuzzer) ProcessRequests

func (f *Fuzzer) ProcessRequests(jobs <-chan *Job)

ProcessRequests executes HTTP requests in as they're received over the channel.

func (*Fuzzer) RequestCount

func (f *Fuzzer) RequestCount() (int, error)

RequestCount calculates the total number of requests that will be sent given a set of input and the fields to be fuzzed using combinatorials. This will be slower the larger the input file. It is imperative that this count matches the number of requests created by GenerateRequest, otherwise httpfuzz will wait forever on requests that aren't coming or exit before all requests are processed.

func (*Fuzzer) WaitFor

func (f *Fuzzer) WaitFor(requests int)

WaitFor adds the requests the fuzzer will send to our internal sync.WaitGroup. This keeps the fuzzer running until all requests have been completed.

type InitializerFunc

type InitializerFunc func(*log.Logger) (Listener, error)

InitializerFunc is a go function that should be exported by a function package. It should be named "New". Your InitializerFunc should return an instance of your Listener with a reference to httpfuzz's logger for consistent logging.

type Job

type Job struct {
	Request   *Request
	FieldName string
	Location  string
	Payload   string
}

Job represents a request to send with a payload from the fuzzer.

type Listener

type Listener interface {
	Listen(results <-chan *Result)
}

Listener must be implemented by a plugin to users to hook the request - response transaction. The Listen method will be run in its own goroutine, so plugins cannot block the rest of the program, however panics can take down the entire process.

type PluginBroker

type PluginBroker struct {
	// contains filtered or unexported fields
}

PluginBroker handles sending messages to plugins.

func LoadPlugins

func LoadPlugins(logger *log.Logger, paths []string) (*PluginBroker, error)

LoadPlugins loads Plugins from binaries on the filesytem.

func (*PluginBroker) SendResult

func (p *PluginBroker) SendResult(result *Result) error

SendResult sends a *Result to all loaded plugins for further processing.

func (*PluginBroker) SignalDone

func (p *PluginBroker) SignalDone()

SignalDone closes all plugin chans that are waiting on results. Call only after all results have been sent.

func (*PluginBroker) Wait

func (p *PluginBroker) Wait()

Wait blocks the goroutine until all plugins have finished executing.

type Request

type Request struct {
	*http.Request
}

Request is a more fuzzable *http.Request. It supports deep-cloning its body and has several convenience methods for modifying request attributes.

func RequestFromFile

func RequestFromFile(filename string) (*Request, error)

RequestFromFile parses an HTTP request from a file.

func (*Request) BodyTargetCount

func (r *Request) BodyTargetCount(delimiter byte) (int, error)

BodyTargetCount calculates the number of targets in a request body.

func (*Request) CloneBody

func (r *Request) CloneBody(ctx context.Context) (*Request, error)

CloneBody makes a copy of a request, including its body, while leaving the original body intact.

func (*Request) HasPathArgument

func (r *Request) HasPathArgument(pathArg string) bool

HasPathArgument returns true if a request URL has a given path argument.

func (*Request) IsMultipartForm

func (r *Request) IsMultipartForm() bool

IsMultipartForm returns true if this is a multipart request.

func (*Request) RemoveDelimiters

func (r *Request) RemoveDelimiters(delimiter byte) error

RemoveDelimiters removes all target delimiters from a request so it can be sent to the server and interpreted properly.

func (*Request) ReplaceMultipartField

func (r *Request) ReplaceMultipartField(fieldName, payload string) error

ReplaceMultipartField replaces a regular form field in a multipart request with a payload. We do this because delimiters don't work with binary files.

func (*Request) ReplaceMultipartFileData

func (r *Request) ReplaceMultipartFileData(fieldName string, file *File) error

ReplaceMultipartFileData replaces a file in the request body with a generated payload.

func (*Request) SetBodyPayloadAt

func (r *Request) SetBodyPayloadAt(position int, delimiter byte, payload string) error

SetBodyPayloadAt injects a payload at a given position.

func (*Request) SetDirectoryRoot

func (r *Request) SetDirectoryRoot(value string)

SetDirectoryRoot inserts a string after the final "/" in a URL to

func (*Request) SetQueryParam

func (r *Request) SetQueryParam(param, value string)

SetQueryParam sets a URL query param to a given value.

func (*Request) SetURLPathArgument

func (r *Request) SetURLPathArgument(arg, value string)

SetURLPathArgument sets a URL path argument to a given value.

type Response

type Response struct {
	*http.Response
}

Response is a *http.Response that allows cloning its body.

func (*Response) CloneBody

func (r *Response) CloneBody() (*Response, error)

CloneBody makes a copy of a response, including its body, while leaving the original body intact.

type Result

type Result struct {
	Request     *Request
	Response    *Response
	Payload     string
	Location    string
	FieldName   string
	TimeElapsed time.Duration
}

Result is the request, response and associated metadata to be processed by plugins.

type Wordlist

type Wordlist struct {
	File *os.File
	// contains filtered or unexported fields
}

Wordlist is a stream of the words in httpfuzz's wordlist.

func (*Wordlist) Count

func (w *Wordlist) Count() (int, error)

Count returns the number of words in a wordlist.

func (*Wordlist) Stream

func (w *Wordlist) Stream() <-chan string

Stream returns a <- chan string that receives lines as they come from the wordlist file. It does not rewind the file after using it.

Directories

Path Synopsis
exampleplugins
bruteforce Module
fileuploaded Module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL