-
Notifications
You must be signed in to change notification settings - Fork 16
/
async-simple.rs
144 lines (128 loc) · 6.9 KB
/
async-simple.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! first, update the variable named target with a valid url to scan
//!
//! then run the example with the following command
//! cargo run --example async-simple
use feroxfuzz::client::AsyncClient;
use feroxfuzz::corpora::Wordlist;
use feroxfuzz::deciders::StatusCodeDecider;
use feroxfuzz::fuzzers::AsyncFuzzer;
use feroxfuzz::mutators::ReplaceKeyword;
use feroxfuzz::observers::ResponseObserver;
use feroxfuzz::prelude::*;
use feroxfuzz::processors::ResponseProcessor;
use feroxfuzz::responses::AsyncResponse;
use feroxfuzz::schedulers::OrderedScheduler;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// create a new corpus from the given list of words
let words = Wordlist::from_file("./examples/words")?
.name("words")
.build();
// pass the corpus to the state object, which will be shared between all of the fuzzers and processors
let mut state = SharedState::with_corpus(words);
// bring-your-own client, this example uses the reqwest library
let req_client = reqwest::Client::builder().build()?;
// with some client that can handle the actual http request/response stuff
// we can build a feroxfuzz client, specifically an asynchronous client in this
// instance.
//
// feroxfuzz provides both a blocking and an asynchronous client implementation
// using reqwest.
let client = AsyncClient::with_client(req_client);
// ReplaceKeyword mutators operate similar to how ffuf/wfuzz work, in that they'll
// put the current corpus item wherever the keyword is found, as long as its found
// in data marked fuzzable (see ShouldFuzz directives below)
let mutator = ReplaceKeyword::new(&"FUZZ", "words");
// fuzz directives control which parts of the request should be fuzzed
// anything not marked fuzzable is considered to be static and won't be mutated
//
// ShouldFuzz directives map to the various components of an HTTP request
let request = Request::from_url(
"http://localhost:8000/",
Some(&[ShouldFuzz::URLParameterValue(b"admin=FUZZ", b"=")]),
)?;
// a `StatusCodeDecider` provides a way to inspect each response's status code and decide upon some Action
// based on the result of whatever comparison function (closure) is passed to the StatusCodeDecider's
// constructor
//
// in plain english, the `StatusCodeDecider` below will check to see if the request's http response code
// received is equal to 200/OK. If the response code is 200, then the decider will recommend the `Keep`
// action be performed. If the response code is anything other than 200, then the recommendation will
// be to `Discard` the response.
//
// `Keep`ing the response means that the response will be allowed to continue on for further processing
// later in the fuzz loop.
let decider = StatusCodeDecider::new(200, |status, observed, _state| {
if status == observed {
Action::Keep
} else {
Action::Discard
}
});
// a `ResponseObserver` is responsible for gathering information from each response and providing
// that information to later fuzzing components, like Processors. It knows things like the response's
// status code, content length, the time it took to receive the response, and a bunch of other stuff.
let response_observer: ResponseObserver<AsyncResponse> = ResponseObserver::new();
// a `ResponseProcessor` provides access to the fuzzer's instance of `ResponseObserver`
// as well as the `Action` returned from calling `Deciders` (like the `StatusCodeDecider` above).
// Those two objects may be used to produce side-effects, such as printing, logging, calling out to
// some other service, or whatever else you can think of.
let response_printer = ResponseProcessor::new(
|response_observer: &ResponseObserver<AsyncResponse>, action, _state| {
if let Some(Action::Keep) = action {
println!(
"[{}] {} - {} - {:?}",
response_observer.status_code(),
response_observer.content_length(),
response_observer.url(),
response_observer.elapsed()
);
}
},
);
// `Scheduler`s manage how the fuzzer gets entries from the corpus. The `OrderedScheduler` provides
// in-order access of the associated `Corpus` (`Wordlist` in this example's case)
let scheduler = OrderedScheduler::new(state.clone())?;
// the macro calls below are essentially boilerplate. Whatever observers, deciders, mutators,
// and processors you want to use, you simply pass them to the appropriate macro call and
// eventually to the Fuzzer constructor.
let deciders = build_deciders!(decider);
let mutators = build_mutators!(mutator);
let observers = build_observers!(response_observer);
let processors = build_processors!(response_printer);
let threads = 40; // number of threads to use for the fuzzing process
// the `Fuzzer` is the main component of the feroxfuzz library. It wraps most of the other components
// and takes care of the actual fuzzing process.
let mut fuzzer = AsyncFuzzer::new(threads)
.client(client)
.request(request)
.scheduler(scheduler)
.mutators(mutators)
.observers(observers)
.processors(processors)
.deciders(deciders)
.post_loop_hook(|state| {
println!("\n•*´¨`*•.¸¸.•* Finished fuzzing loop •*´¨`*•.¸¸.•*\n");
println!("{state:#}");
})
.build();
// the fuzzer will run until it iterates over the entire corpus once
fuzzer.fuzz_once(&mut state).await?;
// example output:
//
// [200] 913 - http://localhost:8000/?admin=AMD - 1.934845ms
// [200] 358 - http://localhost:8000/?admin=Abernathy - 1.009332ms
// ----8<----
// [200] 971 - http://localhost:8000/?admin=zoological - 1.595993ms
// [200] 664 - http://localhost:8000/?admin=zoology%27s - 1.700941ms
//
// •*´¨`*•.¸¸.•* Finished fuzzing loop •*´¨`*•.¸¸.•*
//
// SharedState::{
// Seed=24301
// Rng=RomuDuoJrRand { x_state: 97704, y_state: 403063 }
// Corpus[words]=Wordlist::{len=102774, top-3=[Static("A"), Static("A's"), Static("AMD")]},
// Statistics={"timeouts":0,"requests":102774.0,"errors":44271,"informatives":3655,"successes":29227,"redirects":25621,"client_errors":18463,"server_errors":25799,"redirection_errors":0,"connection_errors":0,"request_errors":9,"start_time":{"secs":1662238251,"nanos":930684935},"avg_reqs_per_sec":32317.09277101265,"statuses":{"402":3702,"404":3683,"200":3716,"303":3725,"304":3697,"204":3632,"502":3813,"205":3638,"300":3576,"400":3794,"101":3655,"401":3647,"308":3705,"202":3664,"207":3634,"301":3604,"206":3646,"302":3600,"201":3717,"307":3714,"500":14674,"203":3580,"503":3653,"501":3659,"403":3637}}
// }
Ok(())
}