Skip to content
Attack Surface Management

CVE-2026-61511: Pre-Auth Remote Code Execution in vBulletin (runMaths) – Advisory + Nuclei Detection

xer0dayz · · 17 min read

On July 27, 2026, SSD Secure Disclosure published CVE-2026-61511, a pre-authentication remote code execution vulnerability in vBulletin, alongside a working proof-of-concept. The bug is an eval injection in the template engine’s math handler, and it is about as clean as pre-auth RCE gets: one unauthenticated HTTP POST, no login, no user interaction, no chained bug required. With roughly 45,000 vBulletin instances exposed on the internet, and a public exploit circulating, this is a same-day patch item for anyone still running an affected build.

This advisory is three things at once. First, a plain-English explanation of what CVE-2026-61511 is, which versions are affected, and the version-checking trap that will make a lot of forum admins believe they are patched when they are not. Second, a ready-to-run Nuclei detection template you can copy, paste, and scan with today – a non-destructive version and patch-level fingerprint we tested against every boundary case, shipped in Sn1per‘s curated set. Third, a walkthrough of how to find every vBulletin install across an attack surface, including the forgotten ones. Credit where it is due: CVE-2026-61511 was reported by Egidio Romano (“EgiX”) through SSD Secure Disclosure, and published as Karma(In)Security advisory KIS-2026-13.

CVE-2026-61511 at a glance

  • CVE: CVE-2026-61511 – eval injection (CWE-95) in vB5_Template_Runtime::runMaths().
  • Impact: unauthenticated remote code execution as the web server user, from a single HTTP POST.
  • CVSS: 9.8 Critical (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H), and 9.3 Critical under CVSS 4.0. Both scores were assigned by VulnCheck, the CNA.
  • Affected: vBulletin 5.x through 5.7.5 and 6.x through 6.2.1, per the CVE record.
  • Fixed: vBulletin 6.2.2 (released July 1, 2026), or the vendor’s Patch Level 1 backport for 6.2.1, 6.2.0 and 6.1.6 (released June 30, 2026). The 5.x branch receives no fix.
  • Status: public proof-of-concept since July 27, 2026. Not in CISA KEV, and no confirmed in-the-wild exploitation at time of writing.
  • Prerequisite: none. Unlike the 2025 vBulletin template chain, this one has no PHP version requirement, so the exploitable population is larger.
  • Fix now: upgrade to 6.2.2, or apply Patch Level 1. If you are on 5.x, migration is the only remediation.

What is CVE-2026-61511?

CVE-2026-61511 is an eval injection flaw (CWE-95) in vBulletin’s template runtime. The vulnerable method is vB5_Template_Runtime::runMaths(), which lives in /includes/vb5/template/runtime.php and implements the {vb:math} template tag. That tag exists so template authors can do inline arithmetic, and the way vBulletin implements it is to filter the expression with a regular expression and then hand the result to PHP’s eval().

The filter is the problem. It strips characters outside an allowed set, and the allowed set was drawn to permit legitimate arithmetic: digits, parentheses, and math operators. Letters are removed. On the face of it that looks safe, because you cannot write system without letters. But the character class also permits bitwise operators, including XOR, and that changes everything. In PHP, XOR-ing two strings produces a third string, so an attacker can construct arbitrary text – including function names – out of nothing but punctuation and digits. The technique is widely known as “phpfuck,” and it turns a letters-are-banned filter into no filter at all. What reaches eval() is attacker-chosen PHP.

The second half of the bug is reachability, and this is what makes it pre-auth. vBulletin renders templates over a public AJAX route, ajax/render/pagenav, which requires no session and no login. The stock pagenav template takes the visitor-supplied pagenav[pagenumber] request parameter, assigns it to the template variable pagenav.currentpage, and folds that variable into a {vb:math} tag. So untrusted input travels from an anonymous HTTP request, through the template renderer, into runMaths(), through a filter that does not stop it, and into eval(). One request, no authentication, arbitrary code execution as the web server user.

It is worth placing this in context, because vBulletin has been here before and the distinction matters for triage. In 2025, CVE-2025-48827 and CVE-2025-48828 chained an authentication bypass with a template-engine RCE. That chain only worked on PHP 8.1 and above, because its root cause was a behavioral change in ReflectionMethod::invoke(). CVE-2026-61511 is a genuinely different bug: different file, different method, different entry route, different root cause, and critically no PHP version precondition at all. If you convinced yourself last year that an older PHP build protected you, that reasoning does not carry over. This is the third distinct pre-auth code execution issue in vBulletin’s template system, and it is the broadest of them.

One honest note on sourcing. The vendor advisory scopes the issue to the 6.x branch, while the CVE record from VulnCheck states “vBulletin 5.x through 5.7.5 and 6.x through 6.2.1.” We follow the CVE record here, because the vulnerable code lives in the vb5 template runtime that both branches share, and because the narrower vendor framing tracks which branches are commercially supported rather than which are vulnerable. NVD has marked the record Deferred, meaning it will not receive independent NIST enrichment, so the CNA data is the authoritative source and will stay that way.

Affected and fixed versions

The version picture has an unusual wrinkle that deserves attention before you check anything, because it is the single most likely cause of a false clean bill of health.

vBulletin branch Affected Fixed
6.2.x 6.2.0 and 6.2.1 6.2.2, or Patch Level 1 on 6.2.0 / 6.2.1
6.1.x 6.1.6 and earlier Patch Level 1 on 6.1.6, or upgrade to 6.2.2
6.0.x All Upgrade to 6.2.2
5.x All, through 5.7.5 No fix. Migrate to 6.2.2.

Here is the trap. On June 30, 2026, vBulletin shipped the fix as a “Patch Level 1” security patch for 6.2.1, 6.2.0 and 6.1.6, and a patch level does not change the version number. A server running patched 6.2.1 still reports itself as 6.2.1. That means the version string alone cannot tell you whether a 6.x host is safe, and any check that flags purely on “6.2.1 or below” will report a false positive against precisely the administrators who took the vendor’s advice fastest. Conversely, a check that trusts a bare “6.2.1” as patched will miss every host that never applied the patch. You need the patch level, not just the version.

The 5.x branch has the opposite property, and it is simpler: there is no patch at any level, so the version number is decisive. Every 5.x install through 5.7.5 is affected and will stay affected. vBulletin’s own support staff have been explicit that 5.x users need to upgrade rather than wait for a backport.

How the exploit chain works

The attack is a single unauthenticated POST to the site root, routed internally to the pagenav renderer. The request is unremarkable in shape – it is the same call the forum makes to render pagination – and everything hostile lives in one parameter. With the weaponized payload deliberately redacted, the request looks like this:

POST / HTTP/1.1
Host: forum.example.com
Content-Type: application/x-www-form-urlencoded

routestring=ajax%2Frender%2Fpagenav&pagenav%5Bpagenumber%5D=[NON-ALPHANUMERIC PHP EXPRESSION - REDACTED]

The redacted value is a long string of digits, parentheses, dots and XOR operators that carries no letters at all. When runMaths() applies its filter, nothing is removed, because every character is on the allowed list. When the filtered string reaches eval(), PHP resolves the XOR operations into a function name and its arguments, and calls it. The published proof-of-concept wraps this in an interactive shell that runs arbitrary system commands.

We are deliberately not publishing a working exploit here, and the payload position above is redacted for that reason. The mechanism is described because defenders need to understand what their WAF is looking at, and the request structure is already the published detection guidance. If you need a weaponized copy for authorized validation against your own lab instance, the researcher’s proof-of-concept is linked from the Karma(In)Security advisory. One practical note if you go that route: the publicly posted script contains a one-character typo, a letter where a digit belongs, so it will not run unmodified. That is the same editorial line we took in our CVE-2026-50522 SharePoint advisory.

Am I affected? Quick manual checks

The authoritative check is local, and it takes one command. On the forum host, read the version banner out of the shipped JavaScript, which carries both the version and the patch level:

# On the server, from the vBulletin web root:
head -5 js/login.js

# Look for a line like:
#   || # vBulletin 6.2.1 Patch Level 1
# Version alone is not enough on 6.x - you need the "Patch Level" token.

The admin control panel shows the same information for anyone who prefers a UI: log in to AdminCP and read the version and patch level from the dashboard header.

Remotely, and without authentication, the same /js/login.js file is the single best signal, because it is the only unauthenticated source that exposes the patch level:

curl -s https://forum.example.com/js/login.js | head -5

Two weaker fallbacks exist if that file is blocked. vBulletin 5 and 6 emit a generator tag in the homepage HTML, <meta name="generator" content="vBulletin 5.7.5" />, and expose an inline JavaScript global, vBulletin.version = '5.7.5'. Asset URLs also encode the version as a compact suffix, so a 5.7.5 install requests js/login.js?v=575 and js/header-rollup-575.js. That asset pattern is useful because administrators who strip the generator tag rarely rewrite their asset URLs.

Be clear-eyed about what those fallbacks can and cannot tell you. None of them carries the patch level. On 5.x that does not matter, because no patch exists and the version is decisive. On 6.x it matters entirely: a generator tag reading “6.2.1” is genuinely ambiguous, and the only honest verdict from that signal alone is “needs manual confirmation.” Treat the absence of a version banner the same way – a stripped tag means unknown, not clean.

CVE-2026-61511 Nuclei detection template

Below is the full template. It is non-destructive: it performs GET requests for a static JavaScript file, reads the version and patch level from the header comment, and compares them against the affected ranges. It sends no payload, never touches ajax/render/pagenav, and cannot cause code execution. It is safe to run against production.

id: CVE-2026-61511

info:
  name: vBulletin 5.x / 6.x - Pre-Auth Eval Injection RCE (runMaths)
  author: xer0dayz
  severity: critical
  description: |
    vBulletin is affected by CVE-2026-61511, a pre-authentication remote code
    execution vulnerability caused by eval injection (CWE-95) in
    vB5_Template_Runtime::runMaths(). The regex filter preserves the XOR
    operator, so arbitrary PHP is reconstructible from non-alphabetic
    characters. The stock pagenav template folds the visitor-supplied
    pagenav[pagenumber] parameter into a {vb:math} tag, and ajax/render/pagenav
    is reachable without authentication.

    This template is NON-DESTRUCTIVE. It reads the version and patch-level
    banner from /js/login.js and compares against the affected ranges. It sends
    no payload and does not touch the vulnerable route.

    PATCH-LEVEL CAVEAT: the vendor backported the fix as "Patch Level 1" to
    6.2.1, 6.2.0 and 6.1.6 WITHOUT changing the version number, so a 6.x host
    cannot be judged on version alone. This template flags 6.x only when the
    banner carries no "Patch Level" token. 5.x has no vendor patch at any
    level, so 5.x is an unambiguous version comparison.
  remediation: |
    Upgrade to vBulletin 6.2.2 or later. On 6.2.1, 6.2.0 or 6.1.6, apply the
    vendor "Patch Level 1" security patch released 2026-06-30. The 5.x branch
    receives no fix - migrate to a supported 6.x release.
  reference:
    - https://ssd-disclosure.com/vbulletin-runtime-template-runmaths-preauth-rce/
    - https://karmainsecurity.com/KIS-2026-13
    - https://nvd.nist.gov/vuln/detail/CVE-2026-61511
    - https://www.vulncheck.com/advisories/vbulletin-eval-injection-rce-via-vb5-template-runtime-php
  classification:
    cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2026-61511
    cwe-id: CWE-95
  metadata:
    verified: true
    max-request: 5
    vendor: vbulletin
    product: vbulletin
    shodan-query: http.html:"vBulletin.version"
  tags: cve,cve2026,vbulletin,rce,eval,unauth,template-injection

http:
  - method: GET
    path:
      - "{{BaseURL}}/js/login.js"
      - "{{BaseURL}}/forum/js/login.js"
      - "{{BaseURL}}/forums/js/login.js"
      - "{{BaseURL}}/board/js/login.js"
      - "{{BaseURL}}/community/js/login.js"

    stop-at-first-match: true
    redirects: true
    max-redirects: 2

    matchers-condition: or
    matchers:
      - type: dsl
        name: vbulletin-5x-affected-no-vendor-fix
        dsl:
          - "compare_versions(version, '>= 5.0.0', '<= 5.7.5')"
          - "contains(body, 'vBulletin')"
        condition: and

      - type: dsl
        name: vbulletin-6x-affected-no-patch-level-reported
        dsl:
          - "compare_versions(version, '>= 6.0.0', '<= 6.2.1')"
          - "!contains(body, 'Patch Level')"
          - "contains(body, 'vBulletin')"
        condition: and

    extractors:
      - type: regex
        name: version
        part: body
        group: 1
        internal: true
        regex:
          - 'vBulletins+([0-9]+.[0-9]+.[0-9]+)'

      - type: regex
        name: vbulletin_build
        part: body
        group: 1
        regex:
          - 'vBulletins+([0-9]+.[0-9]+.[0-9]+(?:s+Patch Levels+[0-9]+)?)'

Save it and run it against a single host or a list:

nuclei -t CVE-2026-61511.yaml -u https://forum.example.com/
nuclei -t CVE-2026-61511.yaml -l vbulletin-hosts.txt

A match looks like this, with the extracted build shown so you can triage without a second request:

[CVE-2026-61511:vbulletin-5x-affected-no-vendor-fix] [http] [critical] https://forum.example.com/js/login.js ["5.7.5 Patch Level 3"]

The matcher name tells you which case fired. A vbulletin-5x-affected-no-vendor-fix hit is unambiguous and needs migration planning. A vbulletin-6x-affected-no-patch-level-reported hit means the host is on an affected 6.x version and reported no patch level at all, which is the strongest remote signal available that the June 30 patch was never applied.

The two blind spots, stated plainly. First, this is a version-based check, so it inherits the usual limitation: a host that reports a version is telling you what it claims to be running, and a stripped or proxied banner produces silence that means “unknown, verify manually,” never “clean.” Second, and specific to this CVE: a 6.x host that does report a patch level is deliberately not flagged, because the banner says a patch level was applied but not which fix it contained. Those hosts are excluded rather than cleared, and they need confirmation against the vendor’s patch notes. We would rather under-report than hand you a scan result that quietly marks unpatched forums as safe.

One more thing worth knowing if you are evaluating other detection approaches for this CVE. A tempting idea is to probe the vulnerable route with a harmless arithmetic expression and see whether it evaluates. That does not work. The {vb:math} tag evaluates arithmetic by design, so a benign sum like 2-1 returns the same result on a patched build as on a vulnerable one. The only behavior that actually distinguishes them is whether a function call can be reconstructed from non-alphanumeric characters, which means any behavioral check has to send something much closer to a real exploit. That trade-off is exactly why we ship a fingerprint template instead.

How to detect CVE-2026-61511 across your attack surface with Sn1per

A template is only as useful as the target list you feed it, and for vBulletin the target list is the hard part. Forum software is rarely the flagship application. It is the community subdomain someone stood up years ago, the support board attached to a product that shipped in 2019, the acquired company’s user forum nobody migrated. Those are the hosts that stay unpatched, and they are invisible to a scan that only covers the assets on your inventory spreadsheet. Detection here is a discovery problem before it is a scanning problem, which is the model we cover in external attack surface management.

Sn1per handles both halves. Run reconnaissance first to enumerate subdomains and live hosts across the domain, then a web scan to fingerprint what is running and apply the curated Nuclei set, including the CVE-2026-61511 template above:

sniper -t example.com -m recon -w acme-forums
sniper -t example.com -m web   -w acme-forums

Everything lands in a workspace, so the discovery pass and the detection pass share one view of the surface. On Sn1per Professional 2026 you can pull the findings straight out over the JSON API and route them into a ticket queue or SIEM:

curl -sk -H "X-API-Key: $SN1PER_API_KEY" 
  "https://sn1per.local/pro/api.php?action=vulnerabilities&workspace=acme-forums" | jq '.'

Because the check is a passive fingerprint, it is cheap enough to run continuously rather than as a one-off sweep. That matters more than it sounds: the forums that get compromised are usually not the ones you scanned and found vulnerable, they are the ones that appeared on the surface after your last scan. Continuous attack surface testing and automated penetration testing both exist to close that gap.

One last point that is specific to this class of work. A complete map of which of your hosts are running unpatched, publicly exploitable software is one of the most sensitive documents your organization will produce. Sn1per is self-hosted across every edition, so that map stays on infrastructure you control rather than in a vendor’s multi-tenant cloud.

Which Sn1per edition fits

Capability Community Professional 2026 Enterprise
Curated Nuclei set incl. CVE-2026-61511 template Yes Yes Yes
Discovery + detection engine Yes Yes Yes
Scale Single operator Up to 150 assets, single operator Near-unlimited, multi-operator
Web UI + scheduled scans No Yes Yes
JSON API + SIEM / ticketing export No Yes Yes
Multi-workspace / multi-team No No Yes
Self-hosted / on-prem Yes Yes Yes

Sn1per Community Edition is free and open-source and ships the same detection engine, so a spot check across a handful of forum hosts costs nothing. Sn1per Professional 2026 adds the web UI, scheduled scans, and the JSON API for teams that want continuous coverage; live pricing is on the shop. Sn1per Enterprise scales to near-unlimited assets with multi-operator, multi-workspace management (custom quote). If you are weighing the two paid tiers, our Professional vs Enterprise comparison breaks it down.

Remediation and mitigation

1. Upgrade to 6.2.2, or apply Patch Level 1. This is the only real fix. vBulletin 6.2.2 shipped July 1, 2026 and contains the fix outright. If a full upgrade is not immediately possible on 6.2.1, 6.2.0 or 6.1.6, apply the vendor’s Patch Level 1 security patch from June 30. After either action, confirm the running state from js/login.js rather than assuming the deployment landed – on 6.x you are looking for the “Patch Level” token, not just the version.

2. If you are on 5.x, plan the migration now. There is no patch for the 5.x branch and there will not be one. A 5.x forum on the public internet is running a version with a published, working, unauthenticated RCE exploit and no vendor remediation path. If the migration cannot happen this week, take the forum off the public internet or put it behind authentication until it can.

3. Filter the vulnerable route at the edge. As a stopgap only, block or closely inspect unauthenticated POST requests carrying routestring=ajax/render/pagenav where pagenav[pagenumber] is unusually long or dense with operators. A legitimate page number is a small integer, so a value containing XOR operators, long parenthesis runs, or hundreds of characters is not a page number. This reduces risk but is not a substitute for patching, and a determined attacker will work to reshape the payload around a naive rule.

4. Hunt for prior compromise. The patch shipped June 30 and the proof-of-concept went public July 27, so there was a four-week window in which the fix was reverse-engineerable from the patch diff before most administrators had applied it. Search web server access logs for POST requests carrying routestring=ajax/render/pagenav with abnormal pagenav[pagenumber] values, and check for PHP files created or modified under the web root since late June. Because execution happens as the web server user, treat any evidence of exploitation as a full application compromise: rotate database credentials and any API keys readable from the vBulletin configuration, and audit administrator accounts for additions.

Frequently asked questions

What is CVE-2026-61511? CVE-2026-61511 is a critical, unauthenticated remote code execution vulnerability in vBulletin. It is an eval injection (CWE-95) in the vB5_Template_Runtime::runMaths() method, which backs the {vb:math} template tag. The method filters input with a regex that permits the XOR operator, then passes the result to PHP’s eval(), so an attacker can reconstruct arbitrary PHP from non-alphabetic characters and execute it through the unauthenticated ajax/render/pagenav route.

Which vBulletin versions are affected by CVE-2026-61511? vBulletin 5.x through 5.7.5 and 6.x through 6.2.1, according to the CVE record. The fix shipped in vBulletin 6.2.2 on July 1, 2026, and as a Patch Level 1 backport for 6.2.1, 6.2.0 and 6.1.6 on June 30, 2026. The 5.x branch does not receive a patch, so migrating to a supported 6.x release is the only remediation there.

Is CVE-2026-61511 being exploited in the wild? There is no confirmed in-the-wild exploitation at the time of writing, and the CVE is not in the CISA Known Exploited Vulnerabilities catalog. However, a working proof-of-concept has been public since July 27, 2026, and roughly 45,000 vBulletin instances are internet-exposed, so treat public exploitation as likely rather than hypothetical.

How do I check if my vBulletin is patched against CVE-2026-61511? Read the first few lines of js/login.js in the vBulletin web root, which reports both the version and the patch level. On 6.x the patch level is what matters, because the vendor backported the fix without changing the version number, so a host reporting “6.2.1” alone is ambiguous while “6.2.1 Patch Level 1” is patched. On 5.x the version is decisive, because no patch exists at any level.

Is the CVE-2026-61511 Nuclei template safe to run in production? Yes. The template only performs GET requests for the static /js/login.js file and compares the reported version and patch level against the affected ranges. It sends no payload, never touches the vulnerable ajax/render/pagenav route, and cannot trigger code execution. Confirm any hit against the host’s actual build before scheduling remediation.

CVE-2026-61511 is the kind of vulnerability that punishes forgotten infrastructure more than slow patching. The forums that stay exploitable will not be the ones on a patch calendar – they will be the community subdomain from an acquisition, the support board attached to a discontinued product, the 5.x install that nobody realized has no upgrade path. Patch what you know about today, then use the template above, or Sn1per across your whole surface, to find the vBulletin you forgot.

Written by

xer0dayz

Founder of XeroSecurity.

Try it free

See your attack surface like a pentester would.

Sn1per finds, ranks, and exploits real vulnerabilities autonomously — the same way attackers do.