mirror of
https://github.com/MichaelCade/90DaysOfDevOps.git
synced 2024-12-22 21:03:14 +07:00
245 lines
8.9 KiB
Markdown
245 lines
8.9 KiB
Markdown
# Fuzzing Advanced
|
|
|
|
Yesterday we learned what fuzzing is and how to write fuzz tests (unit tests with fuzzy inputs).
|
|
However, fuzz testing goes beyond just unit testing.
|
|
We can use this methodology to test our web application by fuzzing the requests sent to our server.
|
|
|
|
Today, we will take a practical approach to fuzzy testing a web server.
|
|
|
|
Different tools can help us do this.
|
|
|
|
Such tools are [Burp Intruder](https://portswigger.net/burp/documentation/desktop/tools/intruder) and [SmartBear](https://smartbear.com/).
|
|
However, there are proprietary tools that require a paid license to use them.
|
|
|
|
That is why for our demonstration today we are going to use a simple open-source CLI written in Go that was inspired by Burp Intruder and provides similar functionality.
|
|
It is called [httpfuzz](https://github.com/JonCooperWorks/httpfuzz).
|
|
|
|
|
|
## Getting started
|
|
|
|
This tool is quite simple.
|
|
We provide it a template for our requests (in which we have defined placeholders for the fuzzy data), a wordlist (the fuzzy data) and `httpfuzz` will render the requests and send them to our server.
|
|
|
|
First, we need to define a template for our requests.
|
|
Create a file named `request.txt` with the following content:
|
|
|
|
```text
|
|
POST / HTTP/1.1
|
|
Content-Type: application/json
|
|
User-Agent: PostmanRuntime/7.26.3
|
|
Accept: */*
|
|
Cache-Control: no-cache
|
|
Host: localhost:8000
|
|
Accept-Encoding: gzip, deflate
|
|
Connection: close
|
|
Content-Length: 35
|
|
|
|
{
|
|
"name": "`S9`",
|
|
}
|
|
```
|
|
|
|
This is a valid HTTP `POST` request to the `/` route with JSON body.
|
|
The "\`" symbol in the body defines a placeholder that will be substituted with the data we provide.
|
|
|
|
`httpfuzz` can also fuzz the headers, path, and URL params.
|
|
|
|
Next, we need to provide a wordlist of inputs that will be placed in the request.
|
|
Create a file named `data.txt` with the following content:
|
|
|
|
```text
|
|
SOME_NAME
|
|
Mozilla/5.0 (Linux; Android 7.0; SM-G930VC Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36
|
|
```
|
|
|
|
In this file, we defined two inputs that will be substituted inside the body.
|
|
In a real-world scenario, you should put much more data here for proper fuzz testing.
|
|
|
|
Now that we have our template and our inputs, let's run the tool.
|
|
Unfortunately, this tool is not distributed as a binary, so we will have to build it from source.
|
|
Clone the repo and run:
|
|
|
|
```shell
|
|
go build -o httpfuzz cmd/httpfuzz.go
|
|
```
|
|
|
|
(requires to have a recent version of Go installed on your machine).
|
|
|
|
Now that we have the binary let's run it:
|
|
|
|
```shell
|
|
./httpfuzz \
|
|
--wordlist data.txt \
|
|
--seed-request request.txt \
|
|
--target-header User-Agent \
|
|
--target-param fuzz \
|
|
--delay-ms 50 \
|
|
--skip-cert-verify \
|
|
--proxy-url http://localhost:8080 \
|
|
```
|
|
|
|
- `httpfuzz` is the binary we are invoking.
|
|
- `--wordlist data.txt` is the file with inputs we provided.
|
|
- `--seed-request requests.txt` is the request template.
|
|
- `--target-header User-Agent` tells `httpfuzz` to use the provided inputs in the place of the `User-Agent` header.
|
|
- `--target-param fuzz` tells `httpfuzz` to use the provided inputs as values for the `fuzz` URL parameter.
|
|
- `--delay-ms 50` tells `httpfuzz` to wait 50 ms between the requests.
|
|
- `--skip-cert-verify` tells `httpfuzz` to not do any TLS verification.
|
|
- `--proxy-url http://localhost:8080` tells `httpfuzz` where our HTTP server is.
|
|
|
|
We have 2 inputs and 3 places to place them (in the body, the `User-Agent` header, and the `fuzz` parameter).
|
|
This means that `httpfuzz` will generate 6 requests and send them to our server.
|
|
|
|
Let's run it and see what happens.
|
|
I wrote a simple web server that logs all requests so that we can see what is coming into our server:
|
|
|
|
```shell
|
|
$ ./httpfuzz \
|
|
--wordlist data.txt \
|
|
--seed-request request.txt \
|
|
--target-header User-Agent \
|
|
--target-param fuzz \
|
|
--delay-ms 50 \
|
|
--skip-cert-verify \
|
|
--proxy-url http://localhost:8080 \
|
|
|
|
httpfuzz: httpfuzz.go:164: Sending 6 requests
|
|
```
|
|
|
|
and the server logs:
|
|
|
|
```text
|
|
-----
|
|
Got request to http://localhost:8000/
|
|
User-Agent header = [SOME_NAME]
|
|
Name = S9
|
|
-----
|
|
Got request to http://localhost:8000/?fuzz=SOME_NAME
|
|
User-Agent header = [PostmanRuntime/7.26.3]
|
|
Name = S9
|
|
-----
|
|
Got request to http://localhost:8000/
|
|
User-Agent header = [PostmanRuntime/7.26.3]
|
|
Name = SOME_NAME
|
|
-----
|
|
Got request to http://localhost:8000/
|
|
User-Agent header = [Mozilla/5.0 (Linux; Android 7.0; SM-G930VC Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36]
|
|
Name = S9
|
|
-----
|
|
Got request to http://localhost:8000/?fuzz=Mozilla%2F5.0+%28Linux%3B+Android+7.0%3B+SM-G930VC+Build%2FNRD90M%3B+wv%29+AppleWebKit%2F537.36+%28KHTML%2C+like+Gecko%29+Version%2F4.083+Mobile+Safari%2F537.36
|
|
User-Agent header = [PostmanRuntime/7.26.3]
|
|
Name = S9
|
|
-----
|
|
Got request to http://localhost:8000/
|
|
User-Agent header = [PostmanRuntime/7.26.3]
|
|
Name = Mozilla/5.0 (Linux; Android 7.0; SM-G930VC Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/58.0.3029.83 Mobile Safari/537.36
|
|
```
|
|
|
|
We see that we have received 6 HTTP requests.
|
|
|
|
Two of them have a value from our values file for the `User-Agent` header, and 4 have the default header from the template.
|
|
Two of them have a value from our values file for the `fuzz` query parameter, and 4 have the default header from the template.
|
|
Two of them have a value from our values file for the `Name` body property, and 4 have the default header from the template.
|
|
|
|
A slight improvement of the tool could be to make different permutations of these requests (for example, a request that has both `?fuzz=` and `User-Agent` as values from the values file).
|
|
|
|
Notice how `httpfuzz` does not give us any information about the outcome of the requests.
|
|
To figure that out, we need to either set up some sort of monitoring for our server or write a `httpfuzz` plugin that will process the results in a meaningful for us way.
|
|
Let's do that.
|
|
|
|
To write a custom plugin, we need to implement the [`Listener`](https://github.com/JonCooperWorks/httpfuzz/blob/master/plugin.go#L13) interface:
|
|
|
|
```go
|
|
// 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)
|
|
}
|
|
```
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"log"
|
|
|
|
"github.com/joncooperworks/httpfuzz"
|
|
)
|
|
|
|
type logResponseCodePlugin struct {
|
|
logger *log.Logger
|
|
}
|
|
|
|
func (b *logResponseCodePlugin) Listen(results <-chan *httpfuzz.Result) {
|
|
for result := range results {
|
|
b.logger.Printf("Got %d response from the server\n", result.Response.StatusCode)
|
|
}
|
|
}
|
|
|
|
// New returns a logResponseCodePlugin plugin that simple logs the response code of the response.
|
|
func New(logger *log.Logger) (httpfuzz.Listener, error) {
|
|
return &logResponseCodePlugin{logger: logger}, nil
|
|
}
|
|
```
|
|
|
|
Now we need to build our plugin first:
|
|
|
|
```shell
|
|
go build -buildmode=plugin -o log exampleplugins/log/log.go
|
|
```
|
|
|
|
and then we can plug it into `httpfuzz` via the `--post-request` flag:
|
|
|
|
```shell
|
|
$ ./httpfuzz \
|
|
--wordlist data.txt \
|
|
--seed-request request.txt \
|
|
--target-header User-Agent \
|
|
--target-param fuzz \
|
|
--delay-ms 50 \
|
|
--skip-cert-verify \
|
|
--proxy-url http://localhost:8080 \
|
|
--post-request log
|
|
|
|
httpfuzz: httpfuzz.go:164: Sending 6 requests
|
|
httpfuzz: log.go:15: Got 200 response from the server
|
|
httpfuzz: log.go:15: Got 200 response from the server
|
|
httpfuzz: log.go:15: Got 200 response from the server
|
|
httpfuzz: log.go:15: Got 200 response from the server
|
|
httpfuzz: log.go:15: Got 200 response from the server
|
|
httpfuzz: log.go:15: Got 200 response from the server
|
|
```
|
|
|
|
Voila!
|
|
Now we can at least see what the response code from the server was.
|
|
|
|
Of course, we can write much more sophisticated plugins that output much more data, but for the purpose of this exercise, that is enough.
|
|
|
|
## Summary
|
|
|
|
Fuzzing is a really powerful testing technique that goes way beyond unit testing.
|
|
|
|
Fuzzing can be extremely useful for testing HTTP servers by substituting parts of valid HTTP requests with data that could potentially expose vulnerabilities or deficiencies in our server.
|
|
|
|
There are many tools that can help us in fuzzy testing our web applications, both free and paid ones.
|
|
|
|
## Resources
|
|
|
|
[OWASP: Fuzzing](https://owasp.org/www-community/Fuzzing)
|
|
|
|
[OWASP: Fuzz Vectors](https://owasp.org/www-project-web-security-testing-guide/v41/6-Appendix/C-Fuzz_Vectors)
|
|
|
|
[Hacking HTTP with HTTPfuzz](https://medium.com/swlh/hacking-http-with-httpfuzz-67cfd061b616)
|
|
|
|
[Fuzzing the Stack for Fun and Profit at DefCamp 2019](https://www.youtube.com/watch?v=qCMfrbpuCBk&list=PLnwq8gv9MEKiUOgrM7wble1YRsrqRzHKq&index=33)
|
|
|
|
[HTTP Fuzzing Scan with SmartBear](https://support.smartbear.com/readyapi/docs/security/scans/types/fuzzing-http.html)
|
|
|
|
[Fuzzing Session: Finding Bugs and Vulnerabilities Automatically](https://youtu.be/DSJePjhBN5E)
|
|
|
|
[Fuzzing the CNCF Landscape](https://youtu.be/zIyIZxAZLzo)
|
|
|
|
See you on [Day 18](day18.md).
|