{"id":13542002,"url":"https://github.com/ameenmaali/qsfuzz","last_synced_at":"2025-04-02T09:33:14.118Z","repository":{"id":37352462,"uuid":"248427567","full_name":"ameenmaali/qsfuzz","owner":"ameenmaali","description":"qsfuzz (Query String Fuzz) allows you to build your own rules to fuzz query strings and easily identify vulnerabilities.","archived":false,"fork":false,"pushed_at":"2023-02-12T01:21:41.000Z","size":84,"stargazers_count":296,"open_issues_count":4,"forks_count":36,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-03T07:33:05.504Z","etag":null,"topics":["bugbounty","fuzz","infosec","security","vulnerability-detection"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ameenmaali.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}},"created_at":"2020-03-19T06:28:33.000Z","updated_at":"2024-10-15T22:46:54.000Z","dependencies_parsed_at":"2024-01-14T08:55:41.495Z","dependency_job_id":"5b1b24e1-aef0-4baf-bfa8-a4af0dcb33c1","html_url":"https://github.com/ameenmaali/qsfuzz","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameenmaali%2Fqsfuzz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameenmaali%2Fqsfuzz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameenmaali%2Fqsfuzz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ameenmaali%2Fqsfuzz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ameenmaali","download_url":"https://codeload.github.com/ameenmaali/qsfuzz/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246789076,"owners_count":20834227,"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":["bugbounty","fuzz","infosec","security","vulnerability-detection"],"created_at":"2024-08-01T10:00:59.971Z","updated_at":"2025-04-02T09:33:13.816Z","avatar_url":"https://github.com/ameenmaali.png","language":"Go","funding_links":[],"categories":["Recon","Go (531)","Go"],"sub_categories":["Fuzzing"],"readme":"# qsfuzz\n\nqsfuzz (Query String Fuzz) is a tool that allows you to write simple rules in YAML that define what value you want to\ninject, and what is the outcome you expect if that injection is successful. Pass in a list of URLs, with query strings,\nand qsfuzz will replace the query string values with your injections to determine if it's vulnerable.\n\nqsfuzz injections are done one-at-a-time for URLs with multiple query strings to ensure requests aren't broken if certain\nparameters are relied on. URLs that don't have query strings will be ignored.\n\n## Installation\n```\ngo get github.com/ameenmaali/qsfuzz\n```\n\n## Usage\nqsfuzz takes URLs (with query strings) from stdin, of which you will most likely want in a file such as:\n```\n$ cat file.txt\nhttps://google.com/home/?q=2\u0026d=asd\nhttps://my.site/profile?param1=1\u0026param2=2\nhttps://my.site/profile?param3=3\n```\n\nqsfuzz also requires a config file (see `config-example.yaml` for an example) which contains the relevant rules to\nevaluate against. This should be a YAML file and formatted such as:\n\n```\n$ cat config.yaml\nrules:\n  ruleName:\n    description: This is my rule description\n  injections:\n    - injectionValue1\n    - injectionValue2\n  expectation:\n    responseContents:\n      - injectionValue1\n    responseCodes:\n      - 200\n  rule2Name:\n    description: This is my 2nd rule description\n    injections:\n      - '\"\u003e\u003ch2\u003easd\u003c/h2\u003e'\n    expectation:\n      responseContents:\n        - \u003ch2\u003easd\u003c/h2\u003e\n      responseHeaders:\n        Content-Type: html\nslack:\n  channel: \"#channel-name\"\n  botToken: \"MY-BOT-TOKEN\"\n```\n\n#### Important Notes for Config files\n\nYou can have as many rules as you'd like (of course this will slow down evaluations). These are the currently supported fields,\nannotated with comments above the field:\n\n```yaml\n# This should never change, and indicates the start of the rules list\nrules:\n  # This should be set to the rule's name you are defining\n  ruleName:\n    # This should be a short description of what the rule's purpose is\n    description: \n    # This is a list (1 or more) of additional query strings to add to requests (that aren't already included in the URLs provided)\n    # This will also keep all URLs that don't normally have query strings, and inject these params as the only ones.\n    extraParams:\n      - \n    # This is a list (1 or more) of injection values to inject within query strings\n    injections:\n      -\n      -\n    # There are several fields within expectation that will be defined below. At least 1 of the below categories must be present to be evaluated\n    expectation:\n      # This is a list (1 or more) of which include a value within a response body that should be present to indicate it is vulnerable.\n      responseContents:\n        -\n      # This is a list (1 or more) of which include a response code that should be present to indicate it is vulnerable.\n      responseCodes:\n        -\n      # This is a list (1 or more) of which include a response header that should be present to indicate it is vulnerable.\n      responseHeaders:\n        -\n      # This is a list (1 or more) of which include a response length that should be within a 10% variance to indicate it is vulnerable.\n      responseLengths:\n        -\n    # Including this heuristics key (optional) will do a couple things. It will send a request to a baseline URL with no parameter injections,\n    # then match the baselineMatches expectations against the heuristic injection. \n    # (i.e. does injecting ' give a 500, but injecting '' in a query string match the baseline request with a 200 code)\n    heuristics:\n      # This is an injection that if successful matches the original baseline request baselineMatches\n      injection: \"''\"\n      # This is a list (1 or more) which check if the defined category matches the baseline request\n      # (i.e. heuristic test match matches the baseline request)\n      baselineMatches:\n        - \"responseCode\"\n\n# Optional key, to be used if -to-slack command line flag is enabled. Sends positive results to Slack\nslack:\n  # The Slack channel you wish to send results to\n  channel: \"#channel-name\"\n  # The bot token for your Slack app to use for authentication\n  botToken: \"MY-BOT-TOKEN\"\n```\n\nFor the `expectation` section, 3 types of matching are supported: `responseContents`, `responseCodes`, and `responseHeaders`\n  - `responseContents` searches the response body for the contents within it\n  - `responseCodes` matches against the response code of the request (redirects are followed automatically, however)\n  - `responseHeaders` does a \"contains\" match against the response header. If `responseHeaders` is set to `html`, then a header value of `text/html` will successfully match\n  - If you have more than 1 `expectation`, each of the evaluation categories must be matched for the evaluation to be successful, however only 1 of each category (i.e. `responseCodes`) needs to match\n\nTake the following example:\n\n```yaml\nrules:\n  XssDetection:\n    description: This rule checks for reflected parameters\n    injections:\n      - '\"\u003e\u003ch2\u003easd\u003c/h2\u003e'\n      - \u003casd\u003etest\u003c/asd\u003e\n    expectation:\n      responseContents:\n        - \u003ch2\u003easd\u003c/h2\u003e\n        - \u003casd\u003etest\u003c/asd\u003e\n      responseHeaders:\n        Content-Type: html\n```\n\nThe above rule will inject `\"\u003e\u003ch2\u003easd\u003c/h2\u003e` and `\u003casd\u003etest\u003c/asd\u003e` in query string values, and check for `\u003ch2\u003easd\u003c/h2\u003e` OR `\u003casd\u003etest\u003c/asd\u003e` in the response contents.\nIn order to be successful, one of the 2 `responseContents` must be matched, as well as the `Content-Type` response header including `html` within it.\n\n### Templating\nThere is rudimentary templating functionality within the rule's injection points, which can be done by inserting the supported variable in square brackets `[[var]]`. \nThis is to allow for some dynamic payloads where you need them. Here are the following fields supported within the templating (these are all related to the URL that is \nbeing assessed at that point in time):\n- `fullurl` (This is the full URL, including query strings, of the URL being targeted in a given request)\n- `domain` (This is the domain of the URL being targeted in a given request)\n- `path` (This is the path, not including query strings, of the URL being targeted in a given request)\n- `originalvalue` (This is the query strings original value before being altered with the injection. i.e. `qs=asd` where `asd` is the original value)\n\nAn example on using these are:\n\n```yaml\nrules:\n  CallbackFuzz:\n    description: Test for open redirects and potential SSRFs by checking for certain responses or callbacks to your server\n    extraParams:\n      - url\n      - redirect_url\n    injections:\n      - \"http://[[domain]].example.net/\"\n      - \"//example.net?targetUrl=[[fullurl]]\"\n      - \"https://example.net?target=[[domain]][[path]]\"\n      - \"@example.net\"\n    expectation:\n      responseContents:\n        - Example Domain\n```\n\n### Heuristics Based Testing\nIncluding a `heuristics` key in your config file is optional. It will do a couple things:\n1) It will send a request to a baseline URL with no parameter injections, and store that response\n2) It will then match whatever categories are defined in the `baselineMatches` against the baseline request\n3) Then, if both the rule injection are matched and the heuristics test, the rule will be a positive match.\n\nTake the following example:\n\n```yaml\nSqlInjectionCheck:\n  description: Test for potential SQL injections by injecting characters to break SQL statements\n  injections:\n    - \"[[originalvalue]]'\"\n  expectation:\n    responseCodes:\n      - 500\n  heuristics:\n    injection: \"[[originalvalue]]''\"\n    baselineMatches:\n      - \"responseCode\"\n```\n\nThis rule will first check for injecting `'` in query strings, appended to the query string's original value. If a response back is `500`, it will then do a heuristics based test.\nA baseline request will be sent to understand what the endpoint normally returns without any injected query strings. Then, based\non the defined categories in `baselineMatches` (which in this case is `responseCode`), a check will be done to see if the response\ncode for the heuristic request matches the baseline request. So does `'` give a `500` response, but `''` gives a `200` response.\n\nIncluding this key will result in more requests being sent, but there is some basic caching logic to ensure the same URLs aren't hit\nmore than necessary.\n\nCurrently, the supported `baselineMatches` are:\n- `responseCode` (Matches the response code against the baseline request's response code)\n- `responseLength` (Matches the response code against the baseline request's response length, within a 10% variance)\n- `responseHeader` (Matches the response code against the baseline request's response headers. This is probably not very useful or worth using)\n- `responseContent` (Matches the response code against the baseline request's response code. This is probably not very useful or worth using)\n\n### Slack Integration\nqsfuzz also supports sending positive matches to Slack. This can be done by adding in the following Slack Config in your config.yaml file.\nThis should be done as a separate key from `rules` (see above example), which is the `slack` key:\n\n```yaml\nslack:\n  channel: \"#channel-name\"\n  botToken: \"MY-BOT-TOKEN\"\n```\n\nThis is particularly valuable in blind attacks, such as blind SSRF, where `qsfuzz` won't necessarily know whether it's successful, but your callback server receives a hit. \nYou can add some data, such as the above supported parameters, within the injection to also send the vulnerable, injected URL within the request.\n\n## Help\n```\n$ qsfuzz -h\nUsage of qsfuzz:\n  -H string\n    \tHeaders to add in all requests. Multiple should be separated by semi-colon\n  -c string\n    \tFile path to config file, which contains fuzz rules\n  -config string\n    \tFile path to config file, which contains fuzz rules\n  -cookies string\n    \tCookies to add in all requests\n  -d\t\n        Send requests with decoded query strings/parameters (this could cause many errors/bad requests)\n  -debug\n    \tDebug/verbose mode to print more info for failed/malformed URLs or requests\n  -decode\n    \tSend requests with decoded query strings/parameters (this could cause many errors/bad requests)\n  -headers string\n    \tHeaders to add in all requests. Multiple should be separated by semi-colon\n  -no-redirects\n    \tDo not follow redirects for HTTP requests (default is true, redirects are followed)\n  -nr\n    \tDo not follow redirects for HTTP requests (default is true, redirects are followed)\n  -s\t\n        Only print successful evaluations (i.e. mute status updates). Note these updates print to stderr, and won't be saved if saving stdout to files\n  -silent\n    \tOnly print successful evaluations (i.e. mute status updates). Note these updates print to stderr, and won't be saved if saving stdout to files\n  -t int\n    \tSet the timeout length (in seconds) for each HTTP request (default 15)\n  -timeout int\n    \tSet the timeout length (in seconds) for each HTTP request (default 15)\n  -to-slack\n    \tSend positive matches to Slack (must have Slack key properly setup in config file)\n  -ts\n    \tSend positive matches to Slack (must have Slack key properly setup in config file)\n  -version\n    \tGet the current version of qsfuzz\n  -w int\n    \tSet the concurrency/worker count (default 25)\n  -workers int\n    \tSet the concurrency/worker count (default 25)\n```\n\n## Examples\n\nqsfuzz is best used when combining with other tools, such as hakcrawler or waybackurls\n\nGet URLs from Wayback Machine with `waybackurls` and fuzz the parameters with `qsfuzz`\n\n`cat hosts.txt | waybackurls | qsfuzz -c config.yaml`\n\nUse cookies and headers for fuzzing:\n\n`cat urls.txt | qsfuzz -c config.yaml -cookies \"cookie1=value; cookie2=value2\" -H \"Authorization: Basic qosakdq==`\n\nCrawl with hakrawler, assess with qsfuzz, and send results to Slack:\n\n`cat hosts.txt | hakrawler | qsfuzz -c config.yaml -to-slack`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fameenmaali%2Fqsfuzz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fameenmaali%2Fqsfuzz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fameenmaali%2Fqsfuzz/lists"}