{"id":30303871,"url":"https://github.com/otsmr/profuzz","last_synced_at":"2025-08-17T06:34:31.976Z","repository":{"id":309891323,"uuid":"1037912973","full_name":"otsmr/profuzz","owner":"otsmr","description":"A generic approach to easily create a fast and easy-to use protocol fuzzer for custom targets.","archived":false,"fork":false,"pushed_at":"2025-08-14T10:17:19.000Z","size":4150,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-08-14T12:13:37.288Z","etag":null,"topics":["fuzzing","fuzzing-framework","network-security","rust"],"latest_commit_sha":null,"homepage":"https://tsmr.eu/generic-protocol-fuzzer.html","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/otsmr.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,"zenodo":null}},"created_at":"2025-08-14T10:11:45.000Z","updated_at":"2025-08-14T10:25:51.000Z","dependencies_parsed_at":"2025-08-14T12:24:05.889Z","dependency_job_id":null,"html_url":"https://github.com/otsmr/profuzz","commit_stats":null,"previous_names":["otsmr/profuzz"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/otsmr/profuzz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otsmr%2Fprofuzz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otsmr%2Fprofuzz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otsmr%2Fprofuzz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otsmr%2Fprofuzz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/otsmr","download_url":"https://codeload.github.com/otsmr/profuzz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/otsmr%2Fprofuzz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270816061,"owners_count":24650748,"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","status":"online","status_checked_at":"2025-08-17T02:00:09.016Z","response_time":129,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","fuzzing-framework","network-security","rust"],"created_at":"2025-08-17T06:34:31.256Z","updated_at":"2025-08-17T06:34:31.966Z","avatar_url":"https://github.com/otsmr.png","language":"Rust","readme":"# profuzz\n\n`profuzz` is a generic approach to easily create a fast and easy-to use protocol fuzzer for custom targets. `profuzz` aims to be used mainly in the embedded world, where most of the time it is not easy to create a running harness on a Linux-based system because of hardware dependencies, the source code is not available, or it requires hardware attacks to dump the firmware. Dumping the firmware, reverse engineering, and then fuzzing potential targets is time intensive. In these cases `profuzz` can be used to find \"low-hanging\" fruits by fuzzing either the network stack itself or custom binary protocols.\n\n![screenshot](images/screenshot.png)\n\n## The generic architecture\n![screenshot](images/structure.png)\n\nThe `profuzz_core` highlighted in **blue** contains the main fuzzing loop, a TUI, and a CLI. It is basically the \"glue\" code for the target-specific crate and the implementations of all `traits` highlighted in **yellow** required to use the `profuzz_core` crate. These traits implementing the logic to communicate with the target, mutate the corpus, resetting the target or to perform a health check. In the crate `profuzz_common` multiple implementations can be found and reused for different target-specific fuzzing setups.\n\n## Getting started\n\nThe `main` function to start `profuzz_core` must be implemented by the target-specific crate. This allows `profuzz_core` to be used either in a small setup, as shown in the [example/profuzz_network_stack](example/profuzz_network_stack/) or in a bigger project, like an automated scanning tool as one feature in many to test multiple targets.\n\nThe following code shows the basic setup required to create a network stack fuzzer. It uses a TCP server on the target to detect a crash and sends the packets directly on eth0. The TCP health check uses the [pcat](https://crates.io/crates/pcap) crate to listen for the response, which requires having `libpcap-dev` installed on your system. \n\nThe full example can be found in the [example](example) folder.\n\n```rs\n#[tokio::main]\nasync fn main() {\n\n    // Defining the `Transport` crate by using a raw linux socket provided by the profuzz_common crate.\n    let transport = RawSocketTransport::new(\"eth0\");\n\n    // Defining the `Healthcheck` using a TCP server. The implementation is also provided by the profuzz_common crate.\n    let healthcheck = TcpHealthcheck::new(\n       \"lo0\",\n       TcpPacket {\n           eth_src: MacAddr::from_str(\"13:33:33:33:33:37\").unwrap(),\n           eth_dst: MacAddr::from_str(\"13:33:33:33:33:38\").unwrap(),\n           vlan_id: None,\n           ipv4_src: Ipv4Addr::from([127, 0, 0, 7]),\n           ipv4_dst: Ipv4Addr::from([127, 0, 0, 8]),\n           sport: 1337,\n           dport: 1338,\n       },\n   )\n   .unwrap();\n\n    // Initialization the `profuzz_core` crate by providing the different implementations for the traits\n    let fuzzer = ProFuzzer::new(transport, healthcheck, DummyResetHandler());\n\n    // Starting the CLI including a TUI, and defining the `Mutable` implementation struct that\n    // implements the mutation of the corpus files also provided by the `profuzz_common` crate\n    if let Err(err) = fuzzer.start_cli::\u003cEtherMutatorOwned\u003e().await {\n        eprintln!(\"{err}\");\n    }\n}\n```\n\n## Using the CLI to start the fuzzer\n\nIn case the `start_cli` function is used to start the fuzzer the following options are available at the moment:\n\n```plain\nUsage: profuzz_network_stack [OPTIONS] \u003cCOMMAND\u003e\n\nCommands:\n  triage  Triage found crashes to identify the potential root cause\n  fuzz    \n  help    Print this message or the help of the given subcommand(s)\n\nOptions:\n      --verbose  Verbose mode\n  -h, --help     Print help\n```\n\n### Start fuzzing\n\nTo start the fuzzer the `fuzz` command can be used with the following options. When started `profuzz_core` automatically create an output directory storing all detected `crashes`.\n\n```plain\nUsage: profuzz_network_stack fuzz [OPTIONS] --in-dir \u003cIN_DIR\u003e --out-dir \u003cOUT_DIR\u003e\n\nOptions:\n  -i, --in-dir \u003cIN_DIR\u003e    input directory with test cases\n  -o, --out-dir \u003cOUT_DIR\u003e  output directory for fuzzer findings\n      --hide-ui            Displays the profuzz UI\n      --auto-resume        If output directory is not empty auto resume the session\n  -h, --help               Print help\n```\n\n### Triaging a crash\n\nWhen a crash is detected, e.g., the health check reports the target is not healthy `profuzz_core` stores all messages sent to the target since the last successful health check. The `triage` command then resends all the buffers while performing a health check after each send buffer. In case the health check reports unhealthy, the crash is detected and stored in the `\u003cout-dir\u003e/crashes/\u003csha1\u003e`.\n\n```plain\nUsage: profuzz_network_stack triage --out-dir \u003cOUT_DIR\u003e\n\nOptions:\n  -o, --out-dir \u003cOUT_DIR\u003e  output directory for fuzzer findings\n  -h, --help               Print help\n```\n## Running the profuzz_tplink_tmdp example\n\nIf you want to play around with profuzz, you can use the `target_tcp_server` as a target and the `profuzz_tplink_tmpd` as a target-specific crate. First, start the target with the `simulate.sh` script. This implements an example `Resethandler` to demonstrate the resting behavior of profuzz.\n\n```bash\ncd example/target_tcp_server\n./simulate.sh\n```\n\nThen in a second terminal you can start the fuzzer with:\n\n```bash\n# directly from the profuzz directory \ncargo run --bin profuzz_tplink_tmdp -- fuzz --in-dir example/profuzz_tplink_tmdp/corpus/ --out-dir /tmp/fuzzing --auto-resume\n```\n\nWhen the target crashes, the crash files are stored in the output dir `/tmp/fuzzing`. When the directory already exists, you can add the `--auto-resume` option to still start the fuzzer.\n\nIn case the protocol fuzzer detects a crash after around 16.4k executions, you are probably running into the TCP `TIME_WAIT` issue. You can find more details in this [blog](https://kaiwern.com/posts/2022/08/11/benchmarking-http-server-stuck-at-16k-requests/) post.\n\nIn case you are running on a Mac, you can prevent this with the following command:\n\n```bash\nsudo sysctl -w net.inet.tcp.msl=10\n\n# If you are done you should change this back to 15000 the normal behavior.\n# sudo sysctl -w net.inet.tcp.msl=15000\n```\n\nAs a reference, on my machine the fuzzer then fuzzes with around 4.5k packets/s and should find after 4 second the first crash. profuzz will automatically reset the target but will end up in a loop with crash 1. So to continue fuzzing, you first should triage this crash with the following command. You probably have to manually restart the `./simulate.sh` in case it did not automatically restart.\n\n```bash\ncargo run --bin profuzz_tplink_tmdp -- triage --out-dir /tmp/fuzzing\n```\n\nAfter running the above command the log should look like in the following: \n\n```log\nTriage 1/1 [y/n]? y\nINFO profuzz_core::triage::dynamic: Starting with detecting the cause.\nINFO profuzz_core::triage::dynamic: Starting with a healthcheck.\nINFO profuzz_core::triage::dynamic: Running full test of all buffers.\nINFO profuzz_core::triage::dynamic: TARGET crashed\n###[ Tether ]###\n version: 1\n unknown0: 0\n tether_type: 5\n unknown1: 0\n length: 8\n unknown2: 0\n unknown3: 12\n crc32: 2965152089       \u003c\u003c This should be highlighted in yellow \n options: 279\n function_id: 3          \u003c\u003c This should be highlighted in yellow\n\nINFO profuzz_core::triage::dynamic: Crash stored in /tmp/fuzzing/crashes/c2c54fb5e43ab7e401f34e921527c8315db097f1\nINFO profuzz_core::triage::dynamic: Identified the corpus that crashed the target.\n```\n\nThe triage command will go through all buffers sent between the last positive health check and the negative one. After every send buffer, a health check will be performed to check if the target crashed. In case the target crashed, profuzz will search for the most similar send buffer that did not cause a crash using the Hamming distance and highlight all differences. This can be used as an aid, not as a truth. :) \n\nAs we are able to look into the target, we can quickly verify that the crash 1 will trigger when the `function_id` is set to 3 and the `options` to `279`. As we also want to discover the other two crashes, we can now modify the `Mutator` to prevent setting the `function_id` to 3:\n\nFor this you can add the 3 to the `ignore_function_ids` hashset in the `Mutate` implementation of the `Tether` packet in the main.rs:\n\n```rs\n- let ignore_function_ids = HashSet::from([200]);\n+ let ignore_function_ids = HashSet::from([200, 3]);\n```\n\nAfter this modification, the fuzzer should find crash 2 in 3 seconds. You now know what to do to also find Crash 3. :)\n\n# License\nThis project is licensed under the [Apache-2.0](./LICENSE) license.","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fotsmr%2Fprofuzz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fotsmr%2Fprofuzz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fotsmr%2Fprofuzz/lists"}