Attacco a Node.js attraverso manipolazioni dei prototipi degli oggetti JavaScript

I ricercatori del Centro Helmholtz per la sicurezza informatica (CISPA) e del Royal Institute of Technology (Svezia) hanno analizzato l'applicabilità della tecnica di inquinamento del prototipo JavaScript («prototype pollution») per creare attacchi sulla piattaforma Node.js e su applicazioni popolari basate su di essa, portando all'esecuzione di codice.

Il metodo di inquinamento del prototipo sfrutta una caratteristica del linguaggio JavaScript che consente di aggiungere nuove proprietà al prototipo radice di qualsiasi oggetto. Nelle applicazioni possono esserci blocchi di codice (widget) il cui funzionamento è influenzato dalla proprietà sostituita; ad esempio, nel codice può esserci una costruzione del tipo ‘const cmd = options.cmd || «/bin/sh»’, la cui logica di funzionamento verrà modificata se l'attaccante riesce a sostituire la proprietà «cmd» nel prototipo radice.

Per avere successo nell'attacco è necessario che i dati provenienti dall'esterno possano essere utilizzati per creare una nuova proprietà nel prototipo radice dell'oggetto, e che durante l'esecuzione ci sia un widget che dipenda dalla proprietà modificata. La modifica del prototipo avviene grazie alla gestione in Node.js delle proprietà «__proto__» e «constructor». La proprietà «__proto__» restituisce il prototipo della classe dell'oggetto, mentre la proprietà «constructor» restituisce la funzione utilizzata per creare l'oggetto.

Se nel codice dell'applicazione è presente un'assegnazione «obj[a][b] = value» e i valori provengono da dati esterni, l'attaccante può impostare «a» su «__proto__» e ottenere l'impostazione della propria proprietà con nome «b» e valore «value» nel prototipo radice dell'oggetto (obj.__proto__.b = value;), dove la proprietà impostata nel prototipo sarà visibile in tutti gli oggetti. Analogamente, se nel codice ci sono espressioni del tipo «obj[a][b][c] = value», impostando «a» su «constructor» e «b» su «prototype» in tutti gli oggetti esistenti è possibile definire una nuova proprietà con nome «c» e valore «value».

Esempio di modifica del prototipo: const o1 = {}; const o2 = new Object(); o1.__proto__.x = 42; // creiamo nel prototipo radice la proprietà «x» console.log(o2.x); // accediamo alla proprietà «x» da un altro oggetto // il risultato sarà 42, poiché attraverso l'oggetto o1 è stato modificato il prototipo radice, utilizzato anche nell'oggetto o2.

Esempio di codice vulnerabile: function entryPoint(arg1, arg2, arg3){ const obj = {}; const p = obj[arg1]; p[arg2] = arg3; return p; }

Se gli argomenti della funzione entryPoint sono generati dai dati in ingresso, l'attaccante può passare a arg1 il valore «__proto__» e creare una proprietà con qualsiasi nome nel prototipo radice. Se si passa a arg2 il valore «toString» e a arg3 — 1, è possibile definire la proprietà «toString» (Object.prototype.toString=1) e causare un crash dell'applicazione durante la chiamata alla funzione toString().

Come esempio di situazioni che possono portare all'esecuzione del codice dell'attaccante, si cita la creazione delle proprietà «main», «shell», «exports», «contextExtensions» e «env». Ad esempio, l'attaccante può creare nel prototipo radice dell'oggetto una proprietà «main», inserendo il percorso del proprio script (Object.prototype.main = «.\/..\/..\/pwned.js») e questa proprietà verrà chiamata al momento dell'esecuzione nel codice della costruzione require(«my-package»), se il pacchetto collegato non definisce esplicitamente la proprietà «main» in package.json (se la proprietà non è definita, sarà ottenuta dal prototipo radice). Analogamente, possono essere inserite le proprietà «shell», «exports» e «env»: let rootProto = Object.prototype; rootProto[«exports»] = {«.»: «.\/changelog.js»}; rootProto[«1»] = «\/path\/to\/npm\/scripts\/»; // trigger call require («.\/target.js»); Object.prototype.main = «\/path\/to\/npm\/scripts\/changelog.js»; Object.prototype.shell = «node»; Object.prototype.env = {}; Object.prototype.env.NODE_OPTIONS = «—inspect-brk=0.0.0.0:1337»; // trigger call require («bytes»);

I ricercatori hanno analizzato 10.000 pacchetti NPM che hanno il maggior numero di dipendenze e hanno scoperto che 1958 di essi non hanno la proprietà main in package.json, 4420 utilizzano percorsi relativi nell'espressione require e 355 utilizzano direttamente l'API per la sostituzione dei comandi.

Come esempio funzionante, si può citare un exploit per attaccare il backend di Parse Server, che ridefinisce la proprietà evalFunctions. Per semplificare l'individuazione di tali vulnerabilità, è stato sviluppato un toolkit che combina metodi di analisi statica e dinamica. Durante il test di Node.js sono stati trovati 11 gadget che possono essere utilizzati per organizzare attacchi che portano all'esecuzione del codice dell'attaccante. Oltre a Parse Server, sono state identificate due vulnerabilità sfruttabili anche in NPM CLI.

Fonte: opennet.ru

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster