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

I ricercatori del Centro Helmholtz per la Sicurezza delle Informazioni (CISPA) e del Royal Institute of Technology (Svezia) hanno analizzato l'applicabilità della tecnica di inquinamento dei prototipi degli oggetti JavaScript per realizzare attacchi sulla piattaforma Node.js e su popolari applicazioni basate su di essa, causando l'esecuzione di codice.

Il metodo di inquinamento dei prototipi utilizza una peculiarità del linguaggio JavaScript che consente di aggiungere nuove proprietà al prototipo radice di qualsiasi oggetto. Nelle applicazioni possono comparire blocchi di codice (gadget) la cui funzionalità è influenzata dalla proprietà iniettata; ad esempio, nel codice potrebbe esserci una costruzione del tipo ‘const cmd = options.cmd || “/bin/sh”’, la cui logica di funzionamento sarà modificata se un aggressore riesce a iniettare la proprietà ‘cmd’ nel prototipo radice.

Per eseguire con successo un attacco, è necessario che i dati esterni nell'applicazione possano essere utilizzati per creare una nuova proprietà nel prototipo radice dell'oggetto, e che, durante l'esecuzione, si incontri un gadget che dipenda dalla proprietà modificata. La modifica del prototipo avviene mediante il trattamento in Node.js delle proprietà speciali «__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 compare l'assegnazione «obj[a][b] = value» e i valori vengono impostati da dati esterni, l'attaccante può impostare «a» al valore «__proto__» e ottenere l'impostazione della sua proprietà con il nome «b» e il valore «value» nel prototipo radice dell'oggetto (obj.__proto__.b = value;), rendendo la proprietà impostata visibile in tutti gli oggetti. Analogamente, se nel codice ci sono espressioni del tipo «obj[a][b][c] = value», impostando «a» al valore «constructor» e «b» a «prototype», in tutti gli oggetti esistenti è possibile definire una nuova proprietà con il nome «c» e il valore «value».

Esempio di modifica del prototipo: const o1 = {}; const o2 = new Object(); o1.__proto__.x = 42; // creiamo la proprietà "x" nel prototipo radice console.log(o2.x); // accediamo alla proprietà "x" da un altro oggetto // l'uscita 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 nel prototipo radice una proprietà con qualsiasi nome. Se si passa a arg2 il valore "toString" e a arg3 — 1, è possibile definire la proprietà "toString" (Object.prototype.toString=1) e causare il crash dell'applicazione durante la chiamata della funzione toString().

Come esempio di situazioni che possono portare all'esecuzione di codice malevolo, si possono citare la creazione delle proprietà «main», «shell», «exports» e «env». Ad esempio, un attaccante potrebbe creare la proprietà «main» nel prototipo radice dell'oggetto, assegnandole il percorso del proprio script (Object.prototype.main = './.../pwned.js') e questa proprietà verrebbe invocata al momento dell'esecuzione nel codice della costruzione require('my-package'), se il pacchetto importato non definisce esplicitamente la proprietà «main» in package.json (se la proprietà non è definita, verrà ottenuta dal prototipo radice). Analogamente, possono essere sostituite 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 con il maggior numero di dipendenze e hanno rilevato che 1958 di essi non possiedono la proprietà main in package.json, 4420 utilizzano percorsi relativi nell'espressione require, mentre 355 utilizzano direttamente l'API per la sostituzione dei comandi.

Un esempio pratico può essere un exploit per attaccare il backend di Parse Server, che sovrascrive la proprietà evalFunctions. Per facilitare l'individuazione di tali vulnerabilità, è stato sviluppato uno strumento che combina metodi di analisi statica e dinamica. Durante i test con Node.js, sono stati identificati 11 gadget che possono essere utilizzati per orchestrare attacchi che portano all'esecuzione di codice da parte 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