{"id":13642252,"url":"https://github.com/kazet/wpgarlic","last_synced_at":"2026-01-25T02:19:50.259Z","repository":{"id":43081403,"uuid":"359851333","full_name":"kazet/wpgarlic","owner":"kazet","description":"A proof-of-concept WordPress plugin fuzzer","archived":false,"fork":false,"pushed_at":"2024-08-31T14:48:32.000Z","size":1042,"stargazers_count":189,"open_issues_count":3,"forks_count":20,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-04-20T16:38:21.833Z","etag":null,"topics":["fuzzing","security","security-tools","testing","wordpress"],"latest_commit_sha":null,"homepage":"","language":"Python","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/kazet.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":"2021-04-20T14:47:26.000Z","updated_at":"2025-04-17T02:13:10.000Z","dependencies_parsed_at":"2024-01-22T15:12:50.466Z","dependency_job_id":"5a0a398c-c31d-4fa8-a056-28744bd477ca","html_url":"https://github.com/kazet/wpgarlic","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kazet/wpgarlic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazet%2Fwpgarlic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazet%2Fwpgarlic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazet%2Fwpgarlic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazet%2Fwpgarlic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kazet","download_url":"https://codeload.github.com/kazet/wpgarlic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kazet%2Fwpgarlic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28742081,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-25T01:40:51.112Z","status":"online","status_checked_at":"2026-01-25T02:00:06.841Z","response_time":113,"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","security","security-tools","testing","wordpress"],"created_at":"2024-08-02T01:01:28.987Z","updated_at":"2026-01-25T02:19:50.244Z","avatar_url":"https://github.com/kazet.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# wpgarlic\nA proof-of-concept WordPress plugin fuzzer that led to the discovery of more than 300 vulnerabilities\nin WordPress plugins installed on almost 30 million sites.\n\nThe used technique is described in https://kazet.cc/2022/02/03/fuzzing-wordpress-plugins.html\n\nIf you want to continue the research, start with less popular plugins - if a plugin\nachieved at least 10k active installs between October 2021 and January 2024, I have\nmost probably looked at the fuzzer reports (and most focus has been put on plugins having\nat least 20k active installs). Because there is a lot of randomness in how fuzzer works,\nsome vulnerabilities in these plugins remain undiscovered - but fewer ones.\n\n**Fuzzer reports contain a lot of false positives - most of them don't indicate a vulnerability.\nAfter seeing a report, first analyze whether the behavior you're observing is indeed a vulnerability\nor a false positive. Don't spam WPScan/vendors with raw fuzzer reports - provide a PoC exploit\ninstead.**\n\n## Examples\nFor obvious reasons, the examples will contain only vulnerabilities that have already been fixed.\n\n### Arbitrary file read\nLet's assume you are fuzzing `responsive-vector-maps` in version 6.4.0:\n\n```\n./bin/fuzz_object plugin responsive-vector-maps --version 6.4.0\n```\n\n(to fuzz the latest version, just skip `--version`).\n\nAfter the fuzzing finishes (which would take 10-30 minutes for this plugin) you can call:\n\n```\n./bin/print_findings data/object_fuzz_results/\n```\n\nYou will see, among others:\n\n![responsive-vector-maps fuzzer output example](examples/responsive-vector-maps.png).\n\nThat means that the fuzzer detected executing `fopen()` on a known payload.\nMost of the payloads contain the word `GARLIC` in them to facilitate automatic\ndetection in output. You may see or configure them in `docker_image/magic_payloads.php`.\n\nThen, you may browse the source code and see that indeed the `wp_ajax_rvm_import_markers`\nendpoint uses the file content to render output, thus allowing you to read arbitrary files\non the server: [CVE-2021-24947](https://wpscan.com/vulnerability/c6bb12b1-6961-40bd-9110-edfa9ee41a18).\n\nWhat you see in white is a crash considered interesting (you may modify them or add new ones\nin `crash_detectors.py`). Green is the context. In blue you see the report file name\n(with plugin name), plugin popularity and endpoint name (here: the ajax action name).\n\nThe data in yellow are what payloads were injected into what variables.\n\n### Reflected XSS\nLet's assume you are fuzzing `page-builder-add` in version 1.4.9.4:\n\n```\n./bin/fuzz_object plugin page-builder-add --version 1.4.9.4\n```\n\nAfter printing the results, you will see known payload echoed back:\n\n![page-builder-add fuzzer output example](examples/page-builder-add.png).\n\nYou can then manually test whether indeed this place (remember: in blue you have the\nendpoint name, here: the menu page name) is vulnerable to XSS.\nIn this case, it is: [CVE-2021-25067](https://wpscan.com/vulnerability/365007f0-61ac-4e81-8a3a-3a068f2c84bc).\n\n### Option update leading to stored XSS\n```\n./bin/fuzz_object plugin duplicate-page-or-post --version 1.4.6\n```\n\nAfter printing the results, you will see `update_option` being called:\n\n![duplicate-page-or-post fuzzer output example](examples/duplicate-page-or-post.png).\n\nAnalysis of the endpoint's code will tell you that this indeed leads to a stored XSS\nvulnerability: [CVE-2021-25075](https://wpscan.com/vulnerability/db5a0431-af4d-45b7-be4e-36b6c90a601b).\n\n### False positives\nUnfortunately, for most of the plugins the fuzzer doesn't find any interesting crashes, and for the rest, **most of the reports are false positives**. For example, if you see:\n\n```\nCall: wp_mail arguments={'to': 'plugin@pluginvendor.com', 'subject': '[Plugin contact]  - http://GARLICGARLICGARLIC.example.com'}\n```\n\nThis can mean, that `wp_mail` is indeed called, but you don't control the recipient\nand most of the subject. If you want to be sure, look at the plugin source.\n\n### Additional tips for reading fuzzer reports\n\n* If you see the message; *May as well be equal: ... and ...* - that means that the mechanism\n  to pretend that a known payload is equal to any other string (described in\n  https://kazet.cc/2022/02/03/fuzzing-wordpress-plugins.html#patched-equality) was triggered.\n* If you see `__GARLIC_ACCESSED__ _FILES[files] __ENDGARLIC__` - that means that an uploaded\n  file access was detected. No further checks are currently made - to check whether this is a\n  vulnerability, look at the code.\n* Sometimes what the fuzzer injects into GET, POST etc. parameters is not reproducible in the real world:\n  for example, you can't inject anything into `$_GET['page']` if you want a particular menu page to be displayed.\n\n## Usage cheatsheet\nThe first run of fuzzing or of the tests may take about an hour, because\nwe need to build the Docker image with instrumented PHP and WordPress.\n\n### Fuzzing a plugin by name\n```\n./bin/fuzz_object plugin PLUGIN_SLUG\n```\n\n### Fuzzing a theme by name\n```\n./bin/fuzz_object theme THEME_SLUG\n```\n\n### Fuzzing a plugin from file\nYou can also install a plugin from a local zip file:\n\n```\n./bin/fuzz_object plugin PLUGIN_FILE_NAME.zip\n```\n\n### Printing findings\nTo print what the fuzzer found, use:\n```\n./bin/print_findings data/object_fuzz_results/\n```\n\n### Running tests\nTo run the tests, use:\n```\n./bin/test\n```\n\nWarning: the tests take long (more than an hour) and because they check whether the fuzzer\nwould find vulnerabilities, they fail with some probability.\n\n### Reformatting code and running linters\nwpgarlic uses `pre-commit` to run linters and format the code.\n`pre-commit` is executed on CI to verify that the code is formatted properly.\n\nTo run it locally, use:\n\n```\npre-commit run --all-files\n```\n\nTo setup `pre-commit` so that it runs before each commit, use:\n\n```\npre-commit install\n```\n\n## Manual testing environment\nYou may start a test environment with only one plugin installed using:\n\n```\n./bin/manual_testing PLUGIN_SLUG|PLUGIN_PATH.zip [version]\n```\n\nYou can install a plugin using its slug or from a local zip file.\n\nIt will listen on http://127.0.0.1:8001/\n\nThere will be two test users in the database:\n\n* username: admin, password: admin, privileges: administrator\n* username: subscriber, password: subscriber, privileges: subscriber\n\n## Extending and configuring the fuzzer\nThis tool is a proof of concept - **this section contains places where it can be improved to find\nmore vulnerabilities**.\n\nYou may want to edit `filtering.py` -- it contains the rules that deem a particular crash\nimportant or not. If you change them, you may have more false positives, but find more vulnerabilities\nas well. For example, the only header that I consider interesting when emitted is the Location\nheader, to detect Open Redirect vulnerabilities. That is just one idea and you may have others.\n\nAnother file that may be worth extending is `docker_image/patch_wordpress.sh`. It describes\ncalls to which functions will be logged as interesting.\n\nIf you want to inject other payloads (or change the probabilities with which they are injected)\nedit `docker_image/magic_payloads.php` and `docker_image/fuzz/config.py`.\n\n`crash_detector.py` contans regular expressions that find interesting crashes or interesting\ninformation (e.g. e-mails) being exposed.\n\nFuzzing REST routes as logged-in admin has been disabled as it led to false positives.\nUncomment `rest_routes_admin` in  `config.DEFAULT_ENABLED_FEATURES` to change that.\n\n## Fuzzer internals\n### Blocklists\nSome plugins need other ones (e.g. woocommerce) as a dependency. When fuzzing a plugin that has\na dependency, we want to fuzz only the chosen plugin and skip the dependency AJAX actions,\nREST routes and menu pages. We want to fuzz woocommerce actions/routes/pages only when we picked\nwoocommerce to fuzz.\n\nList of dependency actions/routes/pages are called blocklists and are listed in `docker_image/blocklists/`.\nFiles named `common` contain the WordPress core actions/routes/pages - we don't want to fuzz these\nas well.\n\nTo update these blocklists, use `./bin/update_blocklists`.\n\n## FAQ\n### Fuzzer found something, is this a vulnerability?\nMaybe. Install the plugin in a local test environment (for example you may use the one described in\nthe *Manual testing environment* section) and analyze the bug.\n\n### Fuzzer found nothing, is the plugin secure?\n**Don't assume that.** The fuzzer finds some classes of vulnerabilities, but has its limitations.\n\nFuzzer founds nothing for most of the plugins - the purpose of the tool is rather to massively\nscan a large number of Wordpress plugins, not to perform comprehensive tests of a single plugin.\n\n### Fuzzer found something in one run, but not in the next one. What happened?\nThis is possible. The fuzzer chooses payloads randomly, and introduces randomness in other places (e.g.\nhow the`==` operator should behave in cases described in\nhttps://kazet.cc/2022/02/03/fuzzing-wordpress-plugins.html#patched-equality).\n\n### Doesn't work? Have a question?\nFile a Github ticket or e-mail me: kazet@p4.team.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazet%2Fwpgarlic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkazet%2Fwpgarlic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkazet%2Fwpgarlic/lists"}