Skip to content
KitploitKITPLOIT
StrumentiExploitsBlog
Log in
Invia
StrumentiExploitsBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
wp2shell — CVE-2026-63030 + CVE-2026-60137 - “wp2shell”: RCE non autenticata nel core di WordPress | Kitploit
Strumenti/GitHubGitHub/0xsha/wp2shell
Password CrackingAnalisi delle VulnerabilitàExploitSfruttamento di Applicazioni WebPenetration TestingCommand and ControlApprendimento e FormazioneRed TeamingSviluppo PayloadLab e Pratica
GitHub
9630372 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi
0xsha/wp2shell

wp2shell

CVE-2026-63030 + CVE-2026-60137 - “wp2shell”: RCE non autenticata nel core di WordPress

Vedi Repository

CVE-2026-63030 + CVE-2026-60137 - “wp2shell”: RCE non autenticata nel core di WordPress

REST API confusione di route batch (CVE-2026-63030) combinata con un'iniezione SQL author__not_in in WP_Query (CVE-2026-60137) → esecuzione remota di codice pre-autenticazione su un'installazione WordPress predefinita.

Scoperta da Adam Kues (Assetnote / Searchlight Cyber), divulgata il 2026-07-17. Advisory: GHSA-ff9f-jf42-662q, GHSA-fpp7-x2x2-2mjf.

Catena (RCE non autenticata)WordPress 6.9.0 - 6.9.4 e 7.0.0 - 7.0.1
Solo SQLi (richiede un plugin/tema facilitatore)6.8.0 - 6.8.5
Non vulnerabili≤ 6.8 per la confusione di batch; 6.9.5 / 7.0.2 / 7.1-beta2 (corrette)
PrerequisitiAPI REST raggiungibile; nessuna cache di oggetti persistente (Redis/Memcached); almeno 1 articolo pubblicato
Autenticazione richiestanessuna
Impattonon autenticato → creare un nuovo amministratore → esecuzione di codice (la SQLi esfiltra anche l'hash dell'amministratore)

Demo

https://github.com/user-attachments/assets/7f9cc52c-3f31-4339-9192-e31e506684f6

Cosa aggiunge questo repository

  • Un tool originale, solo libreria standard (wp2shell.py), che unifica il meglio di sei PoC pubblici in un unico file, senza dipendenza da requests e senza funzionalità rotte.
  • La RCE completa senza crack, verificata end-to-end in laboratorio: shell senza credenziali forgia un falso WP_Post tramite la confusione UNION del singolo post, usa il customizer come ponte per creare un nuovo amministratore (POST /wp/v2/users), accede e rilascia una webshell protetta da token. Il dump dell'hash admin tramite SQLi (read --preset users) è mantenuto come secondo percorso verificato.
  • Un rilevatore di confusione indipendente dalla versione (block_cannot_read), usato come check primario e non distruttivo.
  • Trasporto production-oriented su ogni comando: TLS autofirmato, header personalizzati, User-Agent personalizzato, proxy, retry, ritardo di richiesta.
  • Un percorso SQLi facilitato per 6.8.x (sqli) verificato, che gli altri PoC non hanno.
  • Lab Docker riproducibili più una matrice di affidabilità versione × DB, con ogni risultato verificato in laboratorio.
  • La modalità hashcat per il nuovo hash password $wp$2y$ (-m 35500).
wp2shell/
├── README.md              ← you are here
├── wp2shell.py            ← the unified PoC (single file, stdlib only, by 0xsha)
└── lab/                   ← reproducible Docker labs + reliability matrix
    ├── docker-compose.yml         (default 6.9.4 lab)
    ├── docker-compose.matrix.yml  (parameterised: any version × MySQL/MariaDB)
    ├── docker-compose.sqli.yml    (6.8.3 "SQLi only" lab)
    ├── matrix.sh                  (runs the whole reliability matrix)
    └── sqli-only/facilitator.php  (mu-plugin: the 6.8.x facilitating sink)

I sei PoC pubblici da cui questo tool attinge non sono inclusi qui; sono collegati in Crediti.

Tutto ciò che segue è stato verificato nel lab Docker locale (vedi §4); le affermazioni che non sono state eseguite in laboratorio sono etichettate come tali.


1. Dettagli della vulnerabilità - analisi approfondita del codice

La catena salda due bug indipendenti. I numeri di riga provengono dal codice sorgente reale di WordPress 6.9.4 (estratto da wordpress:6.9.4-apache).

Bug A - Iniezione SQL author__not_in (CVE-2026-60137)

wp-includes/class-wp-query.php, WP_Query::get_posts():

2403  if ( ! empty( $query_vars['author__not_in'] ) ) {
2404      if ( is_array( $query_vars['author__not_in'] ) ) {                 // ← guard only fires for ARRAYS
2405          $query_vars['author__not_in'] = array_unique( array_map( 'absint', $query_vars['author__not_in'] ) );
2406          sort( $query_vars['author__not_in'] );
2407      }
2408      $author__not_in = implode( ',', (array) $query_vars['author__not_in'] );   // ← string passes straight through
2409      $where         .= " AND {$wpdb->posts}.post_author NOT IN ($author__not_in) ";  // ← raw interpolation
2410  } elseif ( ! empty( $query_vars['author__in'] ) ) {
...
2415      $author__in = implode( ',', array_map( 'absint', array_unique( (array) $query_vars['author__in'] ) ) );  // ← absint INSIDE implode

Un author__not_in di tipo stringa salta il controllo is_array() (2404); implode(',', (array)"…") lo restituisce invariato (2408) e viene concatenato grezzo nella SQL (2409). Il gemello author__in (2415) riapplica array_map('absint', …) dentro la implode ed è sicuro: quel array_map mancante è il bug. Il valore finisce come ... post_author NOT IN (<valore>) ..., quindi 0) <sql>-- - chiude la lista e aggiunge SQL.

Ottenere una stringa lì è la parte difficile: l'endpoint REST dei post mappa author_exclude → author__not_in (class-wp-rest-posts-controller.php:247) ma lo dichiara 'type' => 'array' di interi, quindi il core converte/rifiuta una stringa:

GET /wp-json/wp/v2/posts?author_exclude=1) OR SLEEP(3)-- -
→ 400 "author_exclude[0] is not of type integer."      (verified on 6.8.3)

Ecco perché il Bug A da solo è solo “facilitato”. Il Bug B fa passare la stringa oltre la validazione su 6.9+.

Bug B - Confusione di route del batch REST (CVE-2026-63030)

wp-includes/rest-api/class-wp-rest-server.php, serve_batch_request_v1():

1720  if ( false === $parsed_url ) {
1721      $requests[] = new WP_Error( 'parse_path_failed', … );   // a bad path becomes a WP_Error IN $requests

1749  foreach ( $requests as $single_request ) {
1750      if ( is_wp_error( $single_request ) ) {
1752          $validation[] = $single_request;     // ← pushed to $validation …
1753          continue;                            // ← … but $matches is SKIPPED
1754      }
1757      $matches[] = $match;                     // ← $matches only grows for VALID requests

1825  foreach ( $requests as $i => $single_request ) {   // indexed by position in $requests
1841      $match = $matches[ $i ];                        // ← $matches is SHORTER → +1 shift
1861      $result = $this->respond_to_request( $single_request, $route, $handler, $error );

Una sub-request WP_Error viene inserita in $validation[] (1752) ma non in $matches[] (il continue a 1753 salta 1757), quindi $matches è più corto e $matches[$i] (1841) contiene l'handler della successiva request. La request i viene inviata con l'handler della request i+1, portando i propri parametri e il proprio verdetto di validazione (superato).

Origine della regressione (verificata con diff 6.8.3 → 6.9.4): in 6.8.3 il ciclo inserisce $matches[] = $match per ogni request e i percorsi errati vengono scartati nel primo ciclo: gli array restano allineati, nessun desync. Il refactoring di 6.9.0 ha introdotto lo scarto. È esattamente il motivo per cui 6.8.x è “solo SQLi” e la catena RCE inizia dalla 6.9.0.

Il fix documentato (6.9.5 / 7.0.2)

La patch aggiunge $matches[] anche per le voci di errore, indurisce la rientranza (re-entrancy) e analizza author__not_in con un helper per liste di ID. (6.9.5 non era su Docker Hub al momento del test, quindi questa informazione proviene dagli advisory, non da un diff di laboratorio.)


2. Metodo di sfruttamento

2.1 La doppia confusione di route

Lo schema batch consente solo sub-request POST/PUT/PATCH/DELETE, ma get_items dei post (il sink author_exclude) è solo GET, quindi la confusione viene annidata due volte:

Scarica lo strumento