mirror of
https://github.com/MichaelCade/90DaysOfDevOps.git
synced 2025-01-22 02:08:03 +07:00
Day 17: Fuzzing Advanced
This commit is contained in:
parent
7a06067393
commit
cb40219778
242
2023/day17.md
242
2023/day17.md
@ -0,0 +1,242 @@
|
||||
# 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)
|
Loading…
Reference in New Issue
Block a user