Kada potpisujete PDF, obi?no mislite o klju?u za potpisivanje kao o ne?emu ?to kontrolirate. On ?ivi u .pfx datoteci koju ste generirali, za?ti?en lozinkom koju ste odabrali. Kod koji ?ita tu datoteku ?ini se kao obi?na cijev, a ne granica. Ta je intuicija pogre?na u trenutku kada certifikat prestane biti va?. Stolni alat koji korisniku omogu?uje odabir bilo kojeg .pfx-a, poslu?itelj koji prihva?a prenesenu vjerodajnicu, skupni potpisiva? koji prima certifikate preko mre?e - svi oni predaju parseru bajtove pod utjecajem napada?a prije nego ?to se proizvede ijedan bajt potpisa. ?ita? PKCS#12 je napada?ka povr?ina, u istom smislu u kojem je to dekoder slika ili u?itava? fontova
Ovaj ?lanak prolazi kroz dva stvarna nedostatka koja su ?ivjela u tom ?ita?u, oba na putanji koja uvozi vjerodajnicu za potpisivanje. Nijedan nije egzoti?an. Oba proizlaze iz istog korijenskog uzroka koji poga?a gotovo svaki binarni parser napisan u jeziku s cijelim brojevima fiksne ?irine: duljini ili broju iz datoteke vjeruje se korak vi?e nego ?to bi trebalo. Jedan vodi do ?itanja izvan granica, a drugi do procesa koji visi dok ga ne prekinete
Kuda bajtovi putuju
Uvoz .pfx-a radi potpisivanja dokumenta nije jedna operacija, to je kratki cjevovod, a svaka faza analizira ne?to ?to je napada? mogao napisati. Spremnik je struktura PKCS#12 kako je definirano u RFC 7292, gnijezdo AuthenticatedSafe vre?ica omotanih oko ?ifriranog omota?a koji dr?i privatni klju?. Njezino ?itanje zna?i prolazak kroz ASN.1, izvo?enje klju?a iz lozinke, de?ifriranje, a zatim predavanje oporavljenog RSA klju?a kodu koji gradi potpis
U HotPDF-u se te faze mapiraju na zasebne jedinice. Logika spremnika PKCS#12 ?ivi u HPDFPFX. Svaku oznaku, duljinu i vrijednost koju dotakne dekodira ASN.1 ?ita? u HPDFASN1. Izvo?enje klju?a i de?ifriranje PBES2 nalaze se u HPDFCrypt zajedno s PBKDF2HMACSHA256. Kada se klju? oporavi, HPDFRSA i graditelj CMS SignedData u HPDFCMS pretvaraju ga u izdvojeni potpis ugra?en u PDF. Javna ulazna to?ka koja pokre?e cijeli lanac je jedan poziv
// Drives the full pipeline: load the placeholder PDF, parse the PFX,
// derive the key, build CMS SignedData, write the signed output.
if THotPDF.SignPDFWithPFX('Prepared.pdf', 'Signed.pdf',
'signer.pfx', 'p@ssw0rd') then
// signature embedded
else
// signing did not complete
;
Svaki bajt datoteke signer.pfx te?e kroz HPDFASN1 i HPDFPFX prije nego ?to se dogodi bilo kakvo ?ifriranje. Ako te dvije jedinice nisu oprezne oko onoga ?to datoteka tvrdi, kriptografija nizvodno nikada ne?e dobiti priliku da bude va?na
Nedostatak jedan: duljina ASN.1 koja se omotava preko za?tite
ASN.1 u DER-u i BER-u kodira svaki element kao oznaku, duljinu i toliko bajtova sadr?aja. Duljina je polje kojem morate vjerovati, ali ga i provjeriti, jer ono govori parseru koliko daleko treba ?itati, a napisao ga je onaj tko je proizveo datoteku. X.690 ?8.1.3 definira dva kodiranja. Kratki oblik pakira duljinu od 0 do 127 u jedan bajt. Dugi oblik, koji se koristi za sve ve?e od toga, tro?i jedan po?etni bajt ?ijih donjih sedam bitova daje broj bajtova duljine koji slijede, a zatim toliko big-endian bajtova nosi stvarnu vrijednost. ?etiri bajta duljine mogu stoga deklarirati veli?inu sadr?aja koja se pribli?ava ?etirima gigabajtima
Nakon dekodiranja takve vrijednosti, parser mora provjeriti stane li sadr?aj doista unutar spremnika prije nego ?to mu povjeruje. Prirodna provjera je potvrda da trenutna pozicija plus duljina sadr?aja ne prelaze kraj podataka. Napisana na o?it na?in, s pozicijom, duljinom sadr?aja i ukupnim zbrojem koji se ?uvaju u 32-bitnim ozna?enim cijelim brojevima, ta je za?tita slomljena
// The trap: signed 32-bit arithmetic. With ContentLen near MaxInt,
// Pos + ContentLen overflows to a NEGATIVE value, so the comparison
// is false and a forged ~2 GB length sails straight through.
if Pos + ContentLen > Total then
raise EHPDFASN1Error.Create('content overruns buffer');
Problem je u zbrajanju, a ne u usporedbi. Kada je ContentLen blizu MaxInt (2147483647), Pos + ContentLen prelijeva ozna?eni 32-bitni raspon i omotava se na negativan broj. Negativan zbroj nikada nije ve?i od Total, pa za?tita javlja da je sve u redu i dopu?ta parseru da nastavi s duljinom sadr?aja od otprilike dva gigabajta koje spremnik ne sadr?i. Ono ?to se doga?a sljede?e je ?teta: ?ita? dodjeljuje spremnik za tu tra?enu duljinu i kopira u njega, uz SetLength pra?en s Move koji ?ita iz izvora. Izvor ima samo jo? nekoliko stotina bajtova, pa kopiranje ?ita daleko izvan kraja ulaza, ?to je ?itanje izvan granica koje u najboljem slu?aju ru?i program, a u najgorem otkriva susjednu memoriju procesa u analizu
Jedina ispravna za?tita pro?iruje me?uzbroj prije usporedbe, tako da zbrajanje ne mo?e preliti tip u kojem se ra?una. Ispravak promovira oba operanda u Int64
// Correct: both operands widened to Int64 before the add, so the sum
// cannot wrap. A forged 2 GB length now fails the bounds check.
if ContentLen < 0 then
raise EHPDFASN1Error.Create('negative content length after decoding.');
if Int64(Pos) + Int64(ContentLen) > Int64(Total) then
raise EHPDFASN1Error.Create('content overruns buffer');
Int64 dr?i zbroj dviju 32-bitnih vrijednosti bez gubitka, pa usporedba vidi stvarni broj i odbacuje la?nu duljinu. Zasebna provjera nenegativnosti na ContentLen zatvara odgovaraju?i slu?aj u kojem dekodirana vrijednost sama po sebi ispadne negativna. U HotPDF-u ova za?tita ?ivi u HPDFASN1ParseNode, funkciji koja proizvodi ?vor na kojem se grade svi ostali pomo?nici. Budu?i da HPDFASN1Content odre?uje veli?inu svojih SetLength i Move izravno iz duljine sadr?aja ?vora, ?vor koji je pro?ao lo?u za?titu zatrovao bi svako ?itanje uzeto iz njega. Ispravljanje granice na to?ki dekodiranja je ono ?to ?ini pomo?nike iznad njega sigurnima
Nedostatak dva: broj ponavljanja PBKDF2 kori?ten kao oru?je
Drugi nedostatak nije memorijska pogre?ka, to je datoteka koja govori va?em procesoru koliko naporno mora raditi. PKCS#12 ?titi svoj klju?ni materijal s PBES2, shemom temeljenom na lozinki iz PKCS#5, specificiranom u RFC 8018. PBES2 pokre?e funkciju izvo?enja klju?a, ovdje PBKDF2 s HMAC-SHA-256, a zatim ?ifru, ovdje AES-256-CBC. PBKDF2 uzima broj iteracija, a taj je broj parametar koji se prenosi u datoteci. Cijela mu je svrha biti spor: vi?e iteracija zna?i da svaki poku?aj poga?anja lozinke ko?ta vi?e, ?to je dobro protiv izvanmre?nog napada?a. RFC 8018 ?4.2 izri?ito navodi da je ve?i broj bolji za sigurnost i namjerno ne postavlja gornju granicu
Ta je otvorenost u redu kada ste sami generirali datoteku. Ona je oru?je kada ju je generirao napada?. Broj iteracija je faktor rada pod kontrolom napada?a, a faktor rada pod kontrolom napada?a je uskra?ivanje usluge algoritamske slo?enosti. La?ni .pfx mo?e kodirati broj iteracija u milijardama; parser ga poslu?no ?ita i poziva PBKDF2 za toliko krugova HMAC-SHA-256, a proces nestaje u petlji koja se ne?e vratiti minutama ili satima s jednom isporu?enom datotekom. Na poslu?itelju za potpisivanje koji obra?uje jednu vjerodajnicu po zahtjevu, jedan izra?eni prijenos zaustavlja radnika
Broj ?ini omotavanje jo? gorim prije nego ?to natjera procesor da se vrti. Vrijednost iteracije ?ivi u datoteci kao ASN.1 INTEGER, koji nema fiksnu ?irinu, dok je polje koje PBKDF2 u kona?nici tro?i 32-bitni Integer. Dekodirajte INTEGER izravno u to polje i velika vrijednost se skra?uje, a vrijednost stvorena da sleti na bit predznaka vra?a se kao negativna ili kao neki nepovezani mali broj, pa ?ak ni veli?ina rada vi?e nije ono ?to se ?inilo da datoteka tra?i. Ispravak ?ita vrijednost na punoj ?irini i ograni?ava je prije su?avanja
// Read the iteration count as Int64 first, then clamp to a sane band
// BEFORE it is narrowed into the 32-bit Iterations field PBKDF2 uses.
LIter := HPDFASN1ToInteger(Data, Node); // returns Int64
if (LIter < 1) or (LIter > 100000000) then
raise EHPDFPFXError.CreateFmt(
'PBKDF2 iteration count %d is outside the accepted range 1..100000000',
[LIter]);
Iterations := Integer(LIter); // safe: already bounded
?itanje u Int64 zna?i da je dekodirana vrijednost stvarna, a ne njezin skra?eni duh. Donja granica odbacuje nulte i negativne brojeve, koji su besmisleni za izvo?enje klju?a. Gornja granica, sto milijuna, nalazi se znatno iznad bilo koje legitimne PKCS#12 datoteke, koja danas koristi desetke do niske stotine tisu?a iteracija, dok ograni?ava najgori slu?aj na ograni?enu koli?inu rada koja se mo?e pre?ivjeti. Tek nakon ?to vrijednost pro?e taj raspon, su?ava se na 32-bitno polje, tako da skra?ivanje vi?e nikoga ne mo?e iznenaditi. U HotPDF-u ova stezaljka ?ivi u ParsePBES2Params, gdje se parametri PBKDF2 dekodiraju na putu do PBKDF2HMACSHA256
Za?to su oba ispravka isti ispravak
Dva nedostatka izgledaju razli?ito, jedan je prekora?enje spremnika, a drugi obje?eni proces, ali to je ista pogre?ka. U svakom slu?aju, broj iz nepouzdane datoteke prenesen je u tip fiksne ?irine jedan korak prerano, prije nego ?to je provjeren u odnosu na stvarnost. Drugo, duljina je dodana u 32 bita prije provjere granica; broj iteracija je su?en na 32 bita prije provjere raspona. Obje se pokoravaju istoj disciplini: dekodirajte na punoj ?irini, provjerite stvarna ograni?enja i tek tada suzite. Tip Int64 nije stilski izbor, to je jedina ?irina u kojoj za?tita mo?e vidjeti vrijednost koju je napada? stvarno napisao. Granica koja se prelijeva nije granica, a broj bez gornje granice nije parametar, to je daljinski regulator na va?em vlastitom procesoru
Prakti?ne smjernice za cjevovod potpisivanja
U?a lekcija je da provjerite nepouzdani unos certifikata na isti na?in na koji biste provjerili bilo koji nepouzdani prijenos. Ograni?ite veli?inu .pfx-a koji prihva?ate, budu?i da je legitimni u kilobajtima, a ne megabajtima. Tretirajte neuspjeh analize kao uobi?ajeni odbijeni unos, a ne kao pogre?ku vrijednu pra?enja stoga korisniku. Ako potpisujete na poslu?itelju, pokrenite uvoz tamo gdje zaustavljeni radnik ne mo?e sru?iti uslugu s njim, i postavite vremensko ograni?enje oko operacije tako da neo?ekivano skupa datoteka bude ograni?ena zidnim satom kao i kapom iteracije
?ira lekcija se?e izvan certifikata. U?vr??ivanje parsera nije jednokratna revizija jedne jedinice, to je svojstvo svakog mjesta na kojem va?a knji?nica ?ita bajtove koje nije sama napisala. PDF knji?nica analizira mnogo toga iz nepouzdanih izvora: fontove ugra?ene u dokument, slike u pola tuceta kodeka, filtre tokova i, na putanji potpisivanja, certifikate. Svaki od njih je napada?ka povr?ina i svaki zaslu?uje istu sumnju za svaku duljinu i svaki broj. HotPDF gradi putanju uvoza i potpisivanja na u?vr??enim jedinicama HPDFASN1, HPDFPFX, HPDFCrypt i HPDFCMS koje su ovdje opisane, tako da se vjerodajnica koju mu predate, odakle god dolazila, analizira obrambeno prije nego ?to joj se ikada povjeruje
Tijek rada potpisivanja koji ove provjere ?tite pokriven je od po?etka do kraja u na?em vodi?u kroz PAdES digitalne potpise u Delphi-ju, a isti obrambeni stav primijenjen na ?ifriranje dokumenata, uklju?uju?i putanju klju?a AES-256 koja dijeli ovu bazu koda, opisan je u ?lanku o AES-256 ?ifriranju i sigurnosti. Sve se to isporu?uje kao dio softvera HotPDF Component za Delphi i C++Builder, zajedno s API-jima za u?itavanje, ure?ivanje, ?ifriranje i potpisivanje koji su obra?eni drugdje na ovom blogu