Summary: CVE-2026-61511 is a critical (CVSS 9.8) pre-authentication remote code execution flaw in vBulletin, the long-running PHP forum platform. A crafted value in the pagenav[pagenumber] parameter reaches an eval() inside the template engine’s runMaths() method. A backtick payload slips past a filter that only blocks a few function names, and runs arbitrary shell commands. No login required. It affects vBulletin 5.x through 5.7.5 and 6.x through 6.2.1, and it is fixed in 6.2.2. Our lab reproduced a working exploit in 12m 36s for $1.61.

What vBulletin is, and why this matters

vBulletin has been the software behind large internet forums for more than two decades. If you spent time on a gaming community, an enthusiast board, or a big brand’s support forum in the 2000s or 2010s, chances are good it ran vBulletin under the hood. It is commercial PHP and MySQL software, and plenty of those communities are still online and still running it today.

That staying power is exactly what makes a pre-auth RCE here worth paying attention to. A lot of vBulletin installs are old, lightly maintained, and sitting directly on the public internet because a forum is meant to be reachable. When the bug needs no account and no user interaction, every one of those boards is a target the moment a payload is public.

The vulnerability

The flaw lives in vB5_Template_Runtime::runMaths(). That method exists to evaluate simple math expressions inside templates, and it does the evaluation with PHP’s eval(). There is a regex filter meant to keep the input to safe math characters. The problem is the filter isn’t strict enough.

An attacker sends input through the pagenav[pagenumber] parameter on the unauthenticated ajax/render route. That value lands in eval() after a filter that’s supposed to keep it to safe math. Someone with no account ends up running PHP on your web server, which for a forum usually means the whole database, every user record, and often the box itself. NVD scores it 9.8. VulnCheck, who disclosed it, agrees on the severity.

The exploit

The vulnerable filter is a blacklist. It strips four literal words and then hands whatever’s left to eval():

// VULNERABLE (<= 6.2.1)
$expr = str_replace(array('exec','system','passthru','shell_exec'), '', $expr);
@eval($expr . ';');   // <-- the sink

A blacklist that names four functions misses every other way to run code. The public exploit doesn’t type a banned word at all. It builds the function name out of XOR’d character literals, so phpinfo or system never appear as text anywhere in the request:

'+' ^ '[' = 'p'    '[' ^ '3' = 'h'    '+' ^ '[' = 'p'   ...   => "phpinfo"

There’s a second wrinkle. runMaths() url-decodes its input a second time, so the whole payload gets url-encoded twice to survive the round trip. The full request ends up looking like this:

GET /forum/forumdisplay.php?pagenav[pagenumber]=(('+'^'[').('['^'3')...)();1

The original success signal was simple: if the response body came back with PHP Version in it, phpinfo() had run, which means arbitrary PHP executed on the server with no login and no second request.

How we reproduced it

We don’t hunt for new bugs. We take a published CVE, rebuild the exploit in a clean room, and run it against a live install to find out whether the advisory holds up. Our variant uses the same blacklist bypass with a backtick payload, so instead of a phpinfo() page the proof is the output of a real OS command. Here’s what ran and what came back:

$ python exploit.py --target 127.0.0.1 --port 25102
[*] GET /?pagenav[pagenumber]=<double-url-encoded backtick payload>
[+] nonce echoed in response: code execution confirmed

VULN_DETECTED CVE-2026-61511
    signal:        command_execution_confirmed
    nonce:         RCE-2ed5673cc5f7479b
    nonce_echoed:  True
    cmd_id:        uid=33(www-data) gid=33(www-data) groups=33(www-data)
    cmd_whoami:    www-data
    cmd_hostname:  65e3d6c3db16
[verify] all checks passed.
VERIFY OK

That uid=33(www-data) is the whole story. An unauthenticated request made the server run id and hand back the answer. From there it’s arbitrary shell as the web user, which on a forum means the database, every account, and usually a path to the rest of the box.

Here’s what the run cost:

  • Time to solve: 12 minutes, 36 seconds
  • Compute: $1.61
  • Tokens: 155,254
  • AI-XI score: 3

A pre-auth RCE in a widely deployed forum platform, reproduced end to end for the price of a snack. Set that $1.61 next to the CVSS 9.8 and you get the shape of 2026: knowing a bug is critical is cheap, and proving it can actually be run is now almost as cheap.

Check if you're exposed →

The fix

Upgrade to vBulletin 6.2.2. The patch throws out the blacklist and switches to an allowlist, ^[0-9\s()+\-*/.]+$, which rejects anything that isn’t a bare arithmetic expression. That’s the right shape for this: runMaths() only ever needed to handle math, so the fix stops trying to name bad input and instead permits only good input.

If you’re on 6.1.6, 6.2.0, or 6.2.1, the vendor shipped a security patch you can apply without a full version jump. If you’re still on a 5.x release, you’re on affected and unsupported software at once, and the real fix is moving to a patched 6.x. Until you’ve patched, treat any internet-facing vBulletin as exposed.

How VRL can help

Knowing this CVE is critical is the easy part. The hard part is figuring out whether you have a vBulletin box exposed at all, which version it’s running, and whether that version is one an attacker can actually hit. That’s the question a free ai-recon scan answers. Point the agent at your domain, it enumerates your exposed services and versions, and it flags the ones where our dataset already has a verified, working exploit. This one included.

You get a short list of the assets that can really be used against you, ranked ahead of the thousands that can’t.

Run your free assessment →

FAQ

Am I affected? If you run vBulletin 5.x up to 5.7.5, or 6.x up to 6.2.1, and the site is reachable over the network. The bug needs no login.

What’s the fix? Upgrade to vBulletin 6.2.2, or apply the vendor security patch for 6.1.6, 6.2.0, or 6.2.1.

How does the exploit work? A crafted pagenav[pagenumber] value reaches an eval() in the template engine’s runMaths() method. The filter only blocks a handful of function names, so a PHP backtick payload (double-url-encoded to survive a second decode) runs shell commands on the unauthenticated ajax/render route.

References