The Slow Death of vBulletin, and Why It Isn't Over
I built addons for vBulletin and XenForo for years, back when a standalone forum was still where a community lived instead of a Discord server nobody can search six months later. I've watched this specific piece of software for a long time, closely enough to remember exactly which forums I administered went dark for a weekend in July 2010 while their admins scrambled to patch something that should never have shipped. I assumed, like most people who lived through that decade, that vBulletin's story ended sometime around 2012, quietly, the way most abandoned software does.<br>It didn't end. As of the week I'm writing this, vBulletin has a public proof-of-concept exploit circulating for a critical, unauthenticated vulnerability, following a critical remote code execution chain disclosed last year that attackers started exploiting within days. This is Forum Archaeology, a new recurring series where I dig into the platforms and eras I lived through firsthand, with the technical detail intact instead of flattened into nostalgia. First entry: how a piece of software with a brilliant plugin architecture managed to poison its own foundation so thoroughly that the poison is still working, sixteen years later.<br>The Architecture That Built an Empire<br>vBulletin launched in 2000, built by James Limm and John Percival, and by the time version 3 arrived in 2004 it had become the default choice for anyone running a serious community forum. If you administered a forum in the mid-2000s, you ran vBulletin, or you explained to your community why you didn't.<br>The reason it won went beyond polish. It was the plugin system, and understanding exactly how that system worked explains both why developers like me built careers around it and why the software eventually became impossible to secure cleanly. vBulletin's Product and Plugin system let you define hook points throughout the application's execution, places in the codebase the core software would pause at and check: is there any custom code registered here. Plugins were literal PHP code, stored as text in the database, and at each hook point the software would pull that stored code and execute it, effectively running database-stored PHP through the application's request lifecycle without ever touching a single core file.<br>That's an elegant answer to a real problem. Forum admins could install a hundred addons without patching core, upgrades didn't wipe out custom functionality, and a plugin developer, someone doing exactly what I did for a living, could ship a Product as a portable XML file that installed cleanly on any vBulletin site. The templating system worked on a similar principle: templates lived in the database too, with their own custom conditional syntax that got compiled into executable PHP at runtime.<br>Sit with that architecture for a second, because it's the same design decision that shows up in a vulnerability disclosed in 2025. Any system that treats stored data as executable code has drawn itself a very specific kind of target: if an attacker can get malicious content into that stored data, however indirectly, they've found a path to code execution that has nothing to do with a traditional buffer overflow or injection flaw. It's a structural property of the system, not a specific bug in it, and structural properties don't get fixed by a patch. They get fixed by a rewrite, or they get inherited by every version that follows, forever.<br>What the Hook System Looked Like From the Inside<br>Before the acquisition and the lawsuit and the exodus, I want to slow down on the architecture itself, because I've described it from the outside so far and it deserves a real walkthrough. This is reconstructed from memory of how the Product and Plugin system worked, not copied from vBulletin's source, since that's proprietary code I don't have rights to reproduce, but the shape of it is accurate to how thousands of us built against it for a decade.<br>A vBulletin hook point, conceptually, looked something like this at the moment execution reached it:<br>// Simplified reconstruction of the general hook execution pattern,<br>// not vBulletin's actual source. This is the general shape every<br>// plugin developer built against.
function vB_executeHook(string $hookName, array $context = []): void<br>$registeredPlugins = vB_Api::getPluginsForHook($hookName);
foreach ($registeredPlugins as $plugin) {<br>// Plugin code was stored as a raw PHP snippet in the database,<br>// fetched here, and executed directly in the current scope.<br>extract($context);<br>eval($plugin['phpcode']);
// Somewhere deep in core/class_core.php equivalent:<br>vB_executeHook('postbit_display_start', ['post' => $post, 'user' => $user]);<br>Look at what's happening in that loop. $plugin['phpcode'] is a column pulled straight out of a database table, and it gets handed directly to eval(), PHP's own "treat this string as executable code" function, in the same scope as the variables the core software had...