{"id":16935342,"url":"https://github.com/epi052/feroxfuzz","last_synced_at":"2025-04-05T02:10:11.297Z","repository":{"id":59391428,"uuid":"525609556","full_name":"epi052/feroxfuzz","owner":"epi052","description":"A structure-aware HTTP fuzzing library","archived":false,"fork":false,"pushed_at":"2023-10-15T02:30:01.000Z","size":1863,"stargazers_count":202,"open_issues_count":0,"forks_count":15,"subscribers_count":5,"default_branch":"main","last_synced_at":"2024-10-14T20:54:17.419Z","etag":null,"topics":["fuzzing","hacktoberfest","http","testing"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/epi052.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-08-17T02:20:11.000Z","updated_at":"2024-10-03T05:16:31.000Z","dependencies_parsed_at":"2024-10-25T18:45:27.040Z","dependency_job_id":"8100edd7-cfb7-4eb7-9cc2-b686082cd3e4","html_url":"https://github.com/epi052/feroxfuzz","commit_stats":{"total_commits":148,"total_committers":5,"mean_commits":29.6,"dds":"0.28378378378378377","last_synced_commit":"d62df2c66696530de0a114ff83e685dfe492655c"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epi052%2Fferoxfuzz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epi052%2Fferoxfuzz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epi052%2Fferoxfuzz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/epi052%2Fferoxfuzz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/epi052","download_url":"https://codeload.github.com/epi052/feroxfuzz/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247276189,"owners_count":20912288,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fuzzing","hacktoberfest","http","testing"],"created_at":"2024-10-13T20:54:17.597Z","updated_at":"2025-04-05T02:10:11.281Z","avatar_url":"https://github.com/epi052.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003c!-- \u003ca href=\"https://github.com/epi052/feroxfuzz\"\u003e\u003cimg src=\"img/logo/default-cropped.png\" alt=\"feroxfuzz\"\u003e\u003c/a\u003e --\u003e\n  🚀 FeroxFuzz 🚀\n  \u003cbr\u003e\n\u003c/h1\u003e\n\n\u003ch4 align=\"center\"\u003eA structure-aware HTTP fuzzing library\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/epi052/feroxfuzz/actions?query=workflow%3A%22CI+Pipeline%22\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/epi052/feroxfuzz/.github/workflows/check.yml?branch=main\u0026logo=github\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://github.com/epi052/feroxfuzz/commits/master\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/last-commit/epi052/feroxfuzz?logo=github\"\u003e\n  \u003c/a\u003e\n  \n  \u003ca href=\"https://codecov.io/gh/epi052/feroxfuzz\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/epi052/feroxfuzz/branch/main/graph/badge.svg\" /\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://crates.io/crates/feroxfuzz\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/feroxfuzz?color=blue\u0026label=version\u0026logo=rust\"\u003e\n  \u003c/a\u003e\n\n  \u003ca href=\"https://crates.io/crates/feroxfuzz\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/d/feroxfuzz?label=downloads\u0026logo=rust\u0026color=inactive\"\u003e\n  \u003c/a\u003e\n  \n  \u003c!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section \n    [![All Contributors](https://img.shields.io/badge/all_contributors-15-orange.svg?style=flat-square)](#contributors-)\n  \u003c!-- ALL-CONTRIBUTORS-BADGE:END --\u003e\n\n  \u003ca href=\"https://github.com/epi052/feroxfuzz/graphs/contributors\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/all_contributors-2-orange.svg\" /\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## 🤔 Another ferox? why? 🤔\n\nChill, it's not another command-line tool, this one's a library! 😁\n\nMore specifically, FeroxFuzz is a structure-aware HTTP fuzzing library.\n\nThe primary goal in writing FeroxFuzz was to move some core pieces out of [feroxbuster](https://github.com/epi052/feroxbuster) and into a place where they could be generally useful for other folks. In so doing, my hope is that anyone who wants to write web tooling and/or one-off web fuzzers in Rust, can do so with minimal effort.  \n\n## Design \n\nFeroxFuzz's overall design is derived from [LibAFL](https://github.com/AFLplusplus/LibAFL). FeroxFuzz implements most of the components listed in [LibAFL: A Framework to Build Modular and Reusable Fuzzers (pre-print)](https://www.s3.eurecom.fr/docs/ccs22_fioraldi.pdf). When FeroxFuzz deviates, it's typically due to supporting async code.\n\nSimilar to LibAFL, FeroxFuzz is a composable fuzzing library. However, unlike LibAFL, FeroxFuzz is solely focused on **black box HTTP fuzzing**.\n\n## Fuzz-loop execution flow\n\nBelow is a visual depiction of the different components, hooks, and control flow employed by FeroxFuzz.\n\n![fuzz-flow](img/fuzz-flow.png)\n\n## 🚧 Warning: Under Construction 🚧\n\nFeroxFuzz is very capable, and was made to suit all of my planned needs for a new `feroxbuster`. However, I still expect FeroxFuzz's API to change, at least slightly, as work on the new version of `feroxbuster` begins.\n\nUntil the API solidifies, breaking changes ~~may~~ will occur.\n\n## Getting Started\n\nThe easiest way to get started is to include FeroxFuzz in your project's `Cargo.toml`. \n\n```toml\n[dependencies]\nferoxfuzz = { version = \"1.0.0-rc.12\" }\n```\n\n## Docs\n\nIn addition to the `examples/` folder, the API docs have extensive documentation of components along with examples of their use.\n\n- [FeroxFuzz API Docs](https://docs.rs/feroxfuzz/latest/feroxfuzz/): FeroxFuzz's API docs, which are automatically generated from the doc comments in this repo.\n- [Official Examples](https://github.com/epi052/feroxfuzz/tree/main/examples): FeroxFuzz's dedicated, runnable examples, which are great for digging into specific concepts and are heavily commented.\n\n## Example\n\nThe example below ([examples/async-simple.rs](https://github.com/epi052/feroxfuzz/blob/main/examples/async-simple.rs)) shows the bare minimum to write a fuzzer using FeroxFuzz.\n\nIf using the source, the example can be run from the `feroxfuzz/` directory using the following command:\n\n\u003e note: unless you have a webserver running on your machine @ port 8000, you'll need to change the target passed in `Request::from_url`\n\n```\ncargo run --example async-simple\n```\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    // create a new corpus from the given list of words\n    let words = Wordlist::from_file(\"./examples/words\")?\n        .name(\"words\")\n        .build();\n\n    // pass the corpus to the state object, which will be shared between all of the fuzzers and processors\n    let mut state = SharedState::with_corpus(words);\n\n    // bring-your-own client, this example uses the reqwest library\n    let req_client = reqwest::Client::builder().build()?;\n\n    // with some client that can handle the actual http request/response stuff\n    // we can build a feroxfuzz client, specifically an asynchronous client in this\n    // instance.\n    //\n    // feroxfuzz provides both a blocking and an asynchronous client implementation\n    // using reqwest. \n    let client = AsyncClient::with_client(req_client);\n\n    // ReplaceKeyword mutators operate similar to how ffuf/wfuzz work, in that they'll\n    // put the current corpus item wherever the keyword is found, as long as its found\n    // in data marked fuzzable (see ShouldFuzz directives below)\n    let mutator = ReplaceKeyword::new(\u0026\"FUZZ\", \"words\");\n\n    // fuzz directives control which parts of the request should be fuzzed\n    // anything not marked fuzzable is considered to be static and won't be mutated\n    //\n    // ShouldFuzz directives map to the various components of an HTTP request\n    let request = Request::from_url(\n        \"http://localhost:8000/?admin=FUZZ\",\n        Some(\u0026[ShouldFuzz::URLParameterValues]),\n    )?;\n\n    // a `StatusCodeDecider` provides a way to inspect each response's status code and decide upon some Action\n    // based on the result of whatever comparison function (closure) is passed to the StatusCodeDecider's\n    // constructor\n    //\n    // in plain english, the `StatusCodeDecider` below will check to see if the request's http response code\n    // received is equal to 200/OK. If the response code is 200, then the decider will recommend the `Keep`\n    // action be performed. If the response code is anything other than 200, then the recommendation will\n    // be to `Discard` the response.\n    //\n    // `Keep`ing the response means that the response will be allowed to continue on for further processing\n    // later in the fuzz loop.\n    let decider = StatusCodeDecider::new(200, |status, observed, _state| {\n        if status == observed {\n            Action::Keep\n        } else {\n            Action::Discard\n        }\n    });\n\n    // a `ResponseObserver` is responsible for gathering information from each response and providing\n    // that information to later fuzzing components, like Processors. It knows things like the response's\n    // status code, content length, the time it took to receive the response, and a bunch of other stuff.\n    let response_observer: ResponseObserver\u003cAsyncResponse\u003e = ResponseObserver::new();\n\n    // a `ResponseProcessor` provides access to the fuzzer's instance of `ResponseObserver`\n    // as well as the `Action` returned from calling `Deciders` (like the `StatusCodeDecider` above).\n    // Those two objects may be used to produce side-effects, such as printing, logging, calling out to\n    // some other service, or whatever else you can think of.\n    let response_printer = ResponseProcessor::new(\n        |response_observer: \u0026ResponseObserver\u003cAsyncResponse\u003e, action, _state| {\n            if let Some(Action::Keep) = action {\n                println!(\n                    \"[{}] {} - {} - {:?}\",\n                    response_observer.status_code(),\n                    response_observer.content_length(),\n                    response_observer.url(),\n                    response_observer.elapsed()\n                );\n            }\n        },\n    );\n\n    // `Scheduler`s manage how the fuzzer gets entries from the corpus. The `OrderedScheduler` provides\n    // in-order access of the associated `Corpus` (`Wordlist` in this example's case)\n    let scheduler = OrderedScheduler::new(state.clone())?;\n\n    // the macro calls below are essentially boilerplate. Whatever observers, deciders, mutators,\n    // and processors you want to use, you simply pass them to the appropriate macro call and\n    // eventually to the Fuzzer constructor.\n    let deciders = build_deciders!(decider);\n    let mutators = build_mutators!(mutator);\n    let observers = build_observers!(response_observer);\n    let processors = build_processors!(response_printer);\n\n    let threads = 40;  // number of threads to use for the fuzzing process\n\n    // the `Fuzzer` is the main component of the feroxfuzz library. It wraps most of the other components \n    // and takes care of the actual fuzzing process.\n    let mut fuzzer = AsyncFuzzer::new(threads)\n        .client(client)\n        .request(request)\n        .scheduler(scheduler)\n        .mutators(mutators)\n        .observers(observers)\n        .processors(processors)\n        .deciders(deciders)\n        .post_loop_hook(|state| {\n            // this closure is called after each fuzzing loop iteration completes.\n            // it's a good place to do things like print out stats\n            // or do other things that you want to happen after each\n            // full iteration over the corpus\n            println!(\"\\n•*´¨`*•.¸¸.•* Finished fuzzing loop •*´¨`*•.¸¸.•*\\n\");\n            println!(\"{state:#}\");\n        })\n        .build();\n\n    // the fuzzer will run until it iterates over the entire corpus once\n    fuzzer.fuzz_once(\u0026mut state).await?;\n\n    println!(\"{state:#}\");\n\n    Ok(())\n}\n```\n\nThe fuzzer above would produce something similar to what's shown below.\n\n```\n[200] 815 - http://localhost:8000/?admin=Ajax - 840.985µs\n[200] 206 - http://localhost:8000/?admin=Al - 4.092037ms\n----8\u003c----\nSharedState::{\n  Seed=24301\n  Rng=RomuDuoJrRand { x_state: 97704, y_state: 403063 }\n  Corpus[words]=Wordlist::{len=102774, top-3=[Static(\"A\"), Static(\"A's\"), Static(\"AMD\")]},\n  Statistics={\"timeouts\":0,\"requests\":102774.0,\"errors\":44208,\"informatives\":3626,\"successes\":29231,\"redirects\":25709,\"client_errors\":18195,\"server_errors\":26013,\"redirection_errors\":0,\"connection_errors\":0,\"request_errors\":0,\"start_time\":{\"secs\":1662124648,\"nanos\":810398280},\"avg_reqs_per_sec\":5946.646301595066,\"statuses\":{\"500\":14890,\"201\":3641,\"307\":3656,\"203\":3562,\"101\":3626,\"401\":3625,\"207\":3711,\"308\":3578,\"300\":3724,\"404\":3705,\"301\":3707,\"302\":3651,\"304\":3706,\"502\":3682,\"402\":3636,\"200\":3718,\"503\":3762,\"400\":3585,\"501\":3679,\"202\":3659,\"205\":3680,\"206\":3676,\"204\":3584,\"403\":3644,\"303\":3687}}\n}\n```\n\n## 🤓 Projects using FeroxFuzz 🤓\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\u003ca href=\"https://github.com/iustin24/chameleon\"\u003e\u003cimg src=\"img/chameleon.png\" width=\"400px\" height=\"225px\" alt=\"\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003echameleon\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/iustin24\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/62765470?v=4?s=100\" width=\"100px;\" alt=\"iustin24\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eiustin24\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/epi052/feroxfuzz/commits?author=iustin24\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/andreademurtas\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/56048157?v=4?s=100\" width=\"100px;\" alt=\"andreademurtas\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eandreademurtas\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/epi052/feroxfuzz/commits?author=andreademurtas\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/mgr0dzicki\"\u003e\u003cimg src=\"https://avatars.githubusercontent.com/u/29335183?v=4?s=100\" width=\"100px;\" alt=\"Mieszko Grodzicki\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eMieszko Grodzicki\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/epi052/feroxfuzz/commits?author=mgr0dzicki\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"#infra-mgr0dzicki\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepi052%2Fferoxfuzz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fepi052%2Fferoxfuzz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fepi052%2Fferoxfuzz/lists"}