BuddyX

13 min read · 2,598 words

How To Use SQLmap For WordPress

How To Use SQLmap For WordPress

SQL injection has been near the top of the OWASP list for two decades, and WordPress sites are not exempt just because the platform itself is heavily audited. Core WordPress escapes and prepares its database queries carefully, that part of the codebase has been battle-tested by millions of installs and a long history of security researchers picking at it. The actual risk almost always lives one layer up: in a custom plugin, an old theme nobody has touched since 2019, or a bit of bespoke code a developer wrote in an afternoon and never revisited. SQLmap is the tool most penetration testers reach for when they need to confirm whether one of those weak points is actually exploitable, rather than just theoretically risky.

Before going further, the obligatory but genuinely important point: only run SQLmap against a site you own or have explicit written authorization to test. Scanning someone else’s WordPress install without permission is not a gray area, it’s covered under computer misuse law in most countries (the CFAA in the US, the Computer Misuse Act in the UK, and equivalents elsewhere), and “I was just curious” is not a defense that has ever worked in court. Every example below assumes a staging copy of your own site or a target you’ve been contracted to test, ideally with the scope and testing window documented in writing before you run a single command.

What SQLmap Actually Does

SQLmap automates a process that used to be entirely manual: sending crafted payloads to a parameter, watching how the application responds, and inferring from that response whether the underlying SQL query is vulnerable to manipulation. It supports several detection techniques, and understanding which one applies to your target matters more than memorizing flags.

Boolean-based blind injection works by sending a payload that should evaluate to true, then one that should evaluate to false, and comparing the two responses byte for byte. If the page content differs in a consistent way, SQLmap has found a lever it can pull. Time-based blind injection is the fallback when the response looks identical either way: SQLmap injects a payload that forces the database to sleep for a set number of seconds if the condition is true, and measures the delay. It’s slower, sometimes painfully so on a site with any kind of caching in front of it, but it catches vulnerabilities that boolean-based testing misses. UNION-based injection is the most direct: if the application echoes query results back into the page, SQLmap can often extract data directly by appending a UNION SELECT statement. Error-based injection relies on verbose database error messages leaking information, which is increasingly rare on production WordPress sites since PHP error display should be off, but shows up constantly on staging or development environments where WP_DEBUG_DISPLAY is left on by mistake.

Installing SQLmap

SQLmap is a Python tool, and it runs the same way on macOS, Linux, and Windows (via WSL, which is genuinely the least frustrating way to run most security tooling on Windows at this point).

git clone --depth 1 https://github.com/sqlmapproject/sqlmap.git
cd sqlmap
python3 sqlmap.py --version

Cloning from GitHub directly rather than using a package manager keeps you on the current development branch, which matters because tamper scripts and detection signatures get updated frequently as WAFs and CMSs change their defenses. If you’d rather not manage Python dependencies on your main machine, Kali Linux ships SQLmap preinstalled, and running it inside a disposable VM or container is a reasonable way to keep your testing environment separate from everything else.

Finding a Real Target on a WordPress Site

This is the step people skip, and it’s the one that actually matters. Pointing SQLmap at your homepage URL will almost never find anything, because the homepage typically doesn’t pass user input into a database query. You need a parameter that the application actually uses to build a query: a search box, a custom plugin’s AJAX endpoint, a filter dropdown on a WooCommerce shop page, a comment form, a custom REST API route a developer built without using $wpdb->prepare().

Open your browser’s developer tools, go to the Network tab, and interact with the site’s search, filters, and any custom forms while watching what requests go out. Burp Suite’s proxy (the free Community Edition is enough for this) gives you a cleaner view if you want to intercept and replay requests rather than just observe them. What you’re looking for is any URL with a query string parameter, or a POST request with form data, that feeds into something the plugin then uses to query the database.

Running SQLmap Against WordPress

Once you’ve identified a candidate parameter, the basic command structure looks like this:

sqlmap -u "https://staging.example.com/?s=test&post_type=product" -p s --batch

The -p s flag tells SQLmap which specific parameter to test (in this case, the search parameter), which is worth specifying explicitly rather than letting SQLmap guess when a URL has several parameters, it saves time and avoids noise in the results. The --batch flag accepts SQLmap’s default answer to every interactive prompt, useful for unattended runs but worth dropping if you want to review each decision manually the first time through.

If SQLmap confirms a vulnerability, the next steps build on each other:

sqlmap -u "https://staging.example.com/?s=test" -p s --dbs
sqlmap -u "https://staging.example.com/?s=test" -p s -D wordpress_db --tables
sqlmap -u "https://staging.example.com/?s=test" -p s -D wordpress_db -T wp_users --columns
sqlmap -u "https://staging.example.com/?s=test" -p s -D wordpress_db -T wp_users -C user_login,user_pass --dump

Each command narrows the scope: list databases, then tables within the target database, then columns within a specific table, then dump the actual data. On a real WordPress install, getting to wp_users and pulling user_pass hashes is the kind of finding that goes straight into a critical-severity report, since even a bcrypt or phpass hash is crackable given enough time and a weak enough password.

Tuning the Scan: Risk, Level, and WAF Bypass

Two flags control how aggressive and how thorough SQLmap is: --level (1-5) determines how many injection points and payload variations it tries, including cookies and HTTP headers at higher levels, and --risk (1-3) determines how potentially destructive the test payloads are, with risk 3 including payloads that can trigger heavier or slower queries. Running everything at level 5, risk 3 by default is a good way to get rate-limited, IP-banned, or simply generate so much noise that a WAF flags and blocks the whole session. Start conservative:

sqlmap -u "https://staging.example.com/?s=test" -p s --level=2 --risk=1

If a site is sitting behind Cloudflare, Sucuri, or Wordfence’s firewall (all common on WordPress), you’ll likely need tamper scripts to get payloads past basic pattern matching:

sqlmap -u "https://staging.example.com/?s=test" -p s --tamper=space2comment,randomcase --random-agent

space2comment replaces spaces in payloads with inline SQL comments, which defeats simple space-based signature matching. randomcase randomizes payload capitalization for the same reason. --random-agent rotates the User-Agent header so requests don’t all look identical to logging middleware. None of this is about “hacking harder”, it’s about confirming whether a vulnerability is exploitable in the real conditions the site runs under, since a WAF that blocks unsophisticated payloads but not a slightly obfuscated one is a finding worth reporting on its own.

Reading the Results Honestly

SQLmap will sometimes report a false positive, particularly on time-based blind tests against a slow or inconsistent server, where natural latency variance gets misread as an injection signal. Before treating any finding as confirmed, rerun the specific test a second time, ideally with --technique=T isolated to just the time-based method, and check whether the delay is consistent and proportional to the payload’s sleep value. A single anomalous result is not proof; a repeatable, proportional delay is.

Testing POST Requests and Authenticated Areas

A lot of the interesting attack surface on a WordPress site sits behind a login, in admin-ajax.php calls that a plugin only exposes to logged-in users, or in a custom REST route that checks for a valid nonce or an Application Password. SQLmap can test these too, but it needs the request context, not just a bare URL. The cleanest way to hand that over is to capture the request in Burp Suite (or your browser’s network panel, saved as a raw HTTP request) and feed it to SQLmap directly:

sqlmap -r captured_request.txt -p filter_id --batch --level=3

The request file needs to include whatever cookies or headers establish the authenticated session, since SQLmap replays the request as-is rather than logging in on its own. For sites using nonce verification on the endpoint (check_ajax_referer() in the plugin code), you’ll need a currently-valid nonce baked into the captured request, and nonces expire, WordPress defaults to a 24-hour window but many plugins tighten that. If your scan starts returning 403s or “Invalid security token” errors partway through, that’s very likely a stale nonce rather than a WAF block, and the fix is just to recapture the request.

This matters because a vulnerability that only exists for authenticated users, say, a shop manager role rather than a subscriber, is still a real finding. Privilege escalation through SQL injection (a subscriber-level account reaching admin-only data because a plugin didn’t check capabilities before running a database query) is one of the more common serious findings in WordPress plugin audits, and it only shows up if you test the endpoints that actually require a session.

What Historically Goes Wrong in WordPress Plugins

Looking back through WordPress plugin vulnerability disclosures over the years, a handful of patterns repeat constantly. Custom search or filter functionality is a frequent offender, a developer builds a “filter products by custom taxonomy” feature, passes the taxonomy value straight into a $wpdb->query() call without preparing it, and ships it. Export and reporting features are another common source, plugins that let an admin export data as CSV often build the underlying SQL dynamically based on selected columns or date ranges, and that dynamic construction is exactly where unescaped input tends to slip in. Anything involving ORDER BY clauses is worth specific attention too, because $wpdb->prepare() placeholders don’t work cleanly for column or table names (only for values), so developers sometimes skip preparation entirely on sort parameters and just trust that the dropdown menu on the frontend will only ever send expected values. It won’t, once someone starts editing requests directly.

Working With a WAF Rather Than Fighting It

If your production site runs Wordfence, Sucuri, or sits behind Cloudflare’s WAF, and a scan against staging (which typically doesn’t have the same protection) finds something, don’t assume the WAF makes it a non-issue. WAF rules are pattern-based and get bypassed regularly, the tamper scripts mentioned above exist specifically because WAF evasion is a well-understood, ongoing arms race, not a solved problem. Treat the WAF as one layer of defense, useful and worth keeping enabled, but not a substitute for fixing the underlying code. A reasonable test methodology is to run the full assessment against a staging copy with the WAF disabled first (to find every real code-level issue without noise), fix what you find, and then do a lighter pass against production with the WAF active to confirm it’s catching the obvious attack patterns as a second layer.

Why This Usually Isn’t Core WordPress

It’s worth saying directly: if SQLmap does find something exploitable on a WordPress site, the vulnerable code is almost never WordPress core itself. Core has used $wpdb->prepare() for parameterized queries for a very long time, and the handful of historical core SQL injection CVEs have been patched fast and are ancient at this point. The actual exposure sits in plugins and themes that build raw SQL strings by concatenating user input directly, a mistake that’s depressingly common in free plugins with a small install base and minimal code review. If you find something, check the plugin changelog first, there’s a real chance it’s already been fixed in a version you haven’t updated to.

Fixing What You Find

The fix is almost always the same regardless of where the vulnerable code lives: replace raw string-concatenated SQL with $wpdb->prepare() and placeholders, sanitize input on the way in with the appropriate WordPress function for the data type (sanitize_text_field(), absint(), sanitize_email(), and so on), and escape output on the way back out. Beyond the code-level fix, a few structural precautions reduce blast radius even if a vulnerability slips through: run the WordPress database user with the minimum privileges it actually needs rather than a full admin-equivalent MySQL account, keep a WAF (Wordfence, Sucuri, or a Cloudflare-level firewall) in front of production, and update plugins and themes on a schedule rather than waiting for something to break.

Pacing a Scan So You Don’t Take the Site Down

Time-based blind testing, in particular, generates a lot of database load if left to run unattended at a high thread count. SQLmap defaults to a single thread, and that default exists for a reason, cranking --threads up on a shared-hosting WordPress install can genuinely knock the database over, especially if the site is already running close to its resource limits. If you’re testing against production (which you generally shouldn’t be doing without a maintenance window and the site owner’s sign-off), keep threads low, add --time-sec=2 if the default 5-second sleep window feels excessive for the connection, and watch server response times in a second terminal tab so you notice degradation before a client does. Testing against a staging clone removes this concern almost entirely and is worth the extra setup time on anything beyond a quick sanity check.

Writing Up What You Find

A raw SQLmap output dump is not a report. If you’re doing this as part of a client engagement or even just documenting findings for your own team, translate what SQLmap found into something a developer without security background can act on: which URL and parameter, which injection technique confirmed it, what data was actually exposed (without including full extracted credentials in a report that might itself get forwarded around insecurely), and the specific code change needed, usually pointing at the exact line where raw SQL gets concatenated instead of prepared. Screenshot or log the proof-of-concept payload and response difference, since “trust me, it’s vulnerable” doesn’t hold up nearly as well as a reproducible example a developer can run themselves after applying a fix, to confirm it’s actually closed, and to have on hand if the same issue resurfaces in a later plugin update that accidentally reverts the fix.

Where SQLmap Fits Alongside Other Tools

SQLmap is purpose-built for one vulnerability class. It won’t catch XSS, broken authentication, insecure file uploads, or the dozens of other issues that show up in a real WordPress security audit. WPScan, which maintains a WordPress-specific vulnerability database covering core, plugins, and themes, is the natural companion tool, it tells you what’s known to be vulnerable in your installed software before you spend time manually hunting for zero-days. Used together, and used ethically against systems you’re authorized to test, they cover most of the ground a WordPress security review actually needs.

None of this replaces basic hygiene, either. A site running the current version of every plugin and theme, with unused plugins actually deleted rather than just deactivated, closes off a large share of the SQL injection reports that show up in the wild, since most real-world exploitation targets known, already-disclosed vulnerabilities in outdated software rather than novel zero-days. If maintaining that update cadence isn’t realistic in-house, a managed WordPress care plan that handles updates, backups, and monitoring on a schedule is often the more practical fix than any single scanning tool, SQLmap included.


Interesting Reads:

Is WordPress Canceled?

How To Embed a YouTube Video Into A WordPress Webpage

How To Add A New WordPress Page With Design

Reading
13 min · 2,598 words
Published
Sep 2, 2024
Shashank Dubey
BuddyX contributor

Writing about WordPress communities, BuddyPress, BuddyBoss, LMS plugins, and the business of paid communities.

Keep reading

More from the BuddyX blog

Browse all posts on community, WordPress, BuddyPress and the studio of plugins behind BuddyX.