Az elmúlt két hétben cikksorozatot láthattunk, amely a népszerű KeePass nyílt forráskódú jelszókezelő „főjelszó-törésnek” nevezett jelenségéről szól.
A hibát elég fontosnak tartották ahhoz, hogy hivatalos amerikai kormányzati azonosítót kapjanak (úgy ismert, mint CVE-2023 32784-, ha le akarja vadászni), és mivel a jelszókezelő fő jelszava nagyjából az egész digitális kastély kulcsa, megértheti, miért váltott ki sok izgalmat a történet.
A jó hír az, hogy egy támadónak, aki ki akarta használni ezt a hibát, szinte biztos, hogy már meg kell fertőznie számítógépét rosszindulatú programokkal, és így mindenképpen kémkedhet a billentyűleütések és a futó programok után.
Vagyis a hiba könnyen kezelhető kockázatnak tekinthető mindaddig, amíg a KeePass készítője ki nem jön egy frissítéssel, aminek hamarosan (látszólag 2023 júniusának elején) meg kell jelennie.
Ahogy a hiba feltárója gondoskodik róla rámutat:
Ha teljes lemeztitkosítást használ erős jelszóval, és rendszere [mentes a rosszindulatú programoktól], akkor minden rendben van. Ezzel a megállapítással senki sem tudja távolról ellopni jelszavait az interneten keresztül.
Megmagyarázták a kockázatokat
Erősen összefoglalva, a hiba abban rejlik, hogy nehéz biztosítani, hogy a bizalmas adatok minden nyoma a memóriából törlésre kerüljön, miután végzett velük.
Itt figyelmen kívül hagyjuk azokat a problémákat, hogyan kerülhetjük el, hogy titkos adatok legyenek a memóriában, akár csak röviden is.
Ebben a cikkben csak arra szeretnénk emlékeztetni a programozókat mindenhol, hogy a kódot egy biztonságtudatos ellenőr hagyta jóvá egy megjegyzéssel, például „úgy tűnik, hogy helyesen takarítja el magát”…
…lehetséges, hogy egyáltalán nem tisztítják meg teljesen, és a lehetséges adatszivárgás nem nyilvánvaló magának a kódnak a közvetlen tanulmányozásából.
Leegyszerűsítve, a CVE-2023-32784 biztonsági rés azt jelenti, hogy a KeePass főjelszó még a KeyPass program kilépése után is helyreállítható a rendszeradatokból, mivel a jelszaváról elegendő információ (bár valójában nem maga a nyers jelszó, amelyre rövid időn belül összpontosítani fogunk) lemaradhat a rendszercsere során, vagy az alvó rendszermemória fájlok későbbi tárolására.
Egy olyan Windows rendszerű számítógépen, ahol a BitLockert nem használják a merevlemez titkosítására, amikor a rendszer ki van kapcsolva, ez esélyt adna egy szélhámosnak, aki ellopta a laptopját, hogy USB-ről vagy CD-meghajtóról induljon, és visszaszerezze a fő jelszót, még akkor is, ha maga a KeyPass program gondoskodik arról, hogy soha ne mentse azt véglegesen lemezre.
A hosszú távú jelszószivárgás a memóriában azt is jelenti, hogy a jelszót elméletileg vissza lehet állítani a KeyPass program memóriakiíratásából, még akkor is, ha a kiíratást jóval a jelszó beírása után ragadták meg, és jóval azután, hogy magának a KeePass-nak már nem kellett tartania.
Nyilvánvaló, hogy feltételezni kell, hogy a rendszerén már található rosszindulatú programok szinte bármilyen beírt jelszót képesek visszaállítani különféle valós idejű leskelődési technikákkal, feltéve, hogy a gépeléskor aktívak voltak. De joggal feltételezheti, hogy a veszélynek kitett ideje a gépelés rövid időtartamára korlátozódik, nem hosszabbodik meg több percre, órára vagy napra azután, vagy esetleg tovább, beleértve a számítógép leállítását is.
Mi marad el?
Ezért úgy gondoltuk, hogy alaposan megvizsgáljuk, hogyan maradhatnak titkos adatok a memóriában olyan módon, amely közvetlenül nem derül ki a kódból.
Ne aggódjon, ha nem programozó – mi leegyszerűsítjük, és menet közben elmagyarázzuk.
Kezdjük azzal, hogy megvizsgáljuk a memóriahasználatot és -tisztítást egy egyszerű C programban, amely szimulálja a jelszó beírását és ideiglenes tárolását a következők szerint:
- Egy dedikált memóriarész lefoglalása kifejezetten a jelszó tárolására.
- Ismert szöveges karakterlánc beszúrása így szükség esetén könnyen megtalálhatjuk a memóriában.
- 16 pszeudo-véletlenszerű 8 bites ASCII karakter hozzáfűzése az AP tartományból.
- Kinyomtatás a szimulált jelszópuffer.
- A memória felszabadítása a jelszópuffer törlésének reményében.
- Kilépés A program.
Nagymértékben leegyszerűsítve a C kód valahogy így nézhet ki, hibaellenőrzés nélkül, rossz minőségű pszeudo-véletlen számokat használva a C futásidejű függvényből rand()
, és figyelmen kívül hagyja a puffertúlcsordulási ellenőrzéseket (ezt soha ne csinálja valódi kódban!):
// Ask for memory char* buff = malloc(128); // Copy in fixed string we can recognise in RAM strcpy(buff,"unlikelytext"); // Append 16 pseudo-random ASCII characters for (int i = 1; i <= 16; i++) { // Choose a letter from A (65+0) to P (65+15) char ch = 65 + (rand() & 15); // Modify the buff string directly in memory strncat(buff,&ch,1); } // Print it out, so we're done with buff printf("Full string was: %sn",buff); // Return the unwanted buffer and hope that expunges it free(buff);
Valójában a tesztjeink során végül felhasznált kód tartalmaz néhány további, alább látható bitet és darabot, hogy az ideiglenes jelszópufferünk teljes tartalmát kiírhassuk, ahogy azt használtuk, hogy megkeressük a nem kívánt vagy megmaradt tartalmat.
Ne feledje, hogy hívás után szándékosan töröljük a puffert free()
, ami technikailag egy használat után mentes hiba, de azért csináljuk, hogy megtudjuk, nem marad-e vissza valami kritikus a puffer visszaadása után, ami a való életben veszélyes adatszivárgáshoz vezethet.
Mi is beszúrtunk kettőt Waiting for [Enter]
bekéri a kódot, hogy lehetőséget adjunk magunknak memóriakiíratások létrehozására a program kulcsfontosságú pontjain, így nyers adatokat kaphatunk későbbi kereséshez, hogy megnézzük, mi maradt hátra a program futása során.
A memória kiíratásához a Microsoftot fogjuk használni Sysinternals eszköz procdump
a ... val -ma
választási lehetőség (törölje ki az összes memóriát), így elkerülhető, hogy saját kódot írjunk a Windows használatához DbgHelp
rendszer és meglehetősen összetett MiniDumpXxxx()
funkciók.
A C kód összeállításához saját, kicsi és egyszerű összeállításunkat használtuk Fabrice Bellard ingyenes és nyílt forráskódú verziójából. Apró C fordító, elérhető 64 bites Windowshoz forrás és bináris forma közvetlenül a GitHub oldalunkról.
A cikkben szereplő összes forráskód másolható és beilleszthető szövege az oldal alján jelenik meg.
Ez történt, amikor összeállítottuk és futtattuk a tesztprogramot:
C:UsersduckKEYPASS> petcc64 -stdinc -stdlib unl1.c Tiny C Compiler - Copyright (C) 2001-2023 Fabrice Bellard Paul Ducklin leszerelte tanulási eszközként való használatra Verzió petcc64-0.9.27 [0006] - Csak c.: unluckllds. cc/petccinc/stdio.h [. . . [ 64 1ac .data 1 c64 32 .pdata ------------------------------- <- unl32.exe (32 bájt) C:UsersduckKEYPASS> unl1000.exe Dömping 200 indításkor 438 F2000F800 2 3000 00 24 1 F3584 1 00 51390 90 57 .W......P....... 5F00A00: 00 00 00 50D 01 5 00C 00 00D 00 00E 00 513 0 73E 74 65 6 33 32B 5 .exe . A 63D6: 64 2 65 78 65 00C 44 32 00 513 0 72 69 76 65 72 iversDriverData 44F61E74: 61 3 43 3 5F 57 69 6 00 513F 0 64 6 77 73 5 53 79 73 74 iversDriverData 65F6E33 32=5.FPS_ 44F72F32: 00 513 0F 69 76 65 72 73F 5 44 72 69F 76 65 72F 44 BROWSER_APP_PROF 61F74: 61 00F 513: 0 00F 45 46F 43 5 34E 33 37 32 ILE_STRING=Inter 3F31: 00E 46 50 53 5 4372 1 00C 513A 0 F42 52C AC 4B 57 53 nettó ExplzV.<.K.. A teljes karakterlánc: nem ANANAN45OM52HKJHJJJ szöveg5JDDJH41 50C 50 5B 50 52C 4 46 00 51400 49 4A 45 5B 53E valószínűtlenszövegJHKN 54F52A49: 4 47A 3A 49 6 74F 65D 72 00A 51410 6 65D 74 20A 45 78 70EJ6 7EJ56 4B3: 4 00 00 00 51390 75 6 6 69 6D 65 6A 79C 74 65 78E riverData=C:Win 74F4C48: 4 4F 00 513 0C 45 4 4d 43 50 4C 4 44 4 d System48Dr 41F4D00: 65 00 44 00 513 0C 72 69 76 65 72 44 61 74 61 3 iversDriverData 43F3E5: 57 69 6 00 513F 0 64 6 77 73 5F .EFC_53=79.FPS_ 73F74F65: 6 33 32F 5 44 72 32 00F 513 0 69 76F 65 72 73F 5 BROWSER_APP_PROF 44C 72 69F 76F65 A 72F44: A61 74 F61 00 513 0 00 45 46 43 F5 34 33 37 32 3 .g......P....... 31F00A46: 50 53A 5A 4372 1 00F 513D 0 42 52F 4D 57 53 45F 52D 5 41 50 A DJHAN.eD 50F5B50: 52 4 46 00 51400 49 4 45 5 53D 54 52A 49C 4 47 3E riverData=C:Win 49F6C74: 65 72F 00 51410 6 65 74 20 45C 78 70 dowsSystem6Dr 7F56D4: 3 4 00 00 00 51390C 0 67 5 00 00 00 00 00 50 01 iversDriverData 5F00 00 iversDriverData 00F00 00D 00 A 513 0 45 4 4E 43 50D 4 4E 44 4 48 ILE_STRING=Inter 41F4: 00E 65 00 44 00 513 0 72C 69D 76 65 72D AC 44 MK nettó pl. Várakozás az [ENTER]-re a main()-ból való kilépéshez... C:UsersduckKEYPASS>
Ebben a futásban nem vesződtünk semmilyen folyamatmemória kiíratással, mert a kimenetből rögtön láthattuk, hogy ez a kód adatokat szivárogtat ki.
Közvetlenül a Windows C futásidejű könyvtár funkciójának meghívása után malloc()
, láthatjuk, hogy a visszakapott puffer olyan környezeti változóadatokat tartalmaz, amelyek a program indítókódjából megmaradtak, és az első 16 bájt láthatóan megváltozott, és úgy néz ki, mint valami megmaradt memóriafoglalási fejléc.
(Jegyezze meg, hogy ez a 16 bájt úgy néz ki, mint két 8 bájtos memóriacím, 0xF55790
és a 0xF50150
, amelyek közvetlenül a saját memóriapufferünk után és közvetlenül előtt vannak.)
Amikor a jelszónak a memóriában kell lennie, a teljes karakterláncot tisztán láthatjuk a pufferben, ahogy azt elvárnánk.
De hívás után free()
, figyelje meg, hogyan írták át pufferünk első 16 bájtját a közeli memóriacímekkel, feltehetően azért, hogy a memóriaelosztó nyomon tudja követni azokat a blokkokat a memóriában, amelyeket újra felhasználhat…
… de a „letörölt” jelszavunk többi része (az utolsó 12 véletlenszerű karakter EJJCPOMDJHAN
) elmaradt.
Nemcsak a saját memóriafoglalásainkat és lefoglalásainkat kell kezelnünk C-ben, hanem arról is gondoskodnunk kell, hogy az adatpufferekhez a megfelelő rendszerfunkciókat válasszuk ki, ha pontosan vezérelni akarjuk azokat.
Például, ha inkább erre a kódra váltunk, akkor egy kicsit jobban szabályozhatjuk, hogy mi van a memóriában:
-ről váltva malloc()
és a free()
az alacsonyabb szintű Windows kiosztási funkciók használatához VirtualAlloc()
és a VirtualFree()
közvetlenül, jobb irányítást kapunk.
Azonban árat fizetünk a sebesség, mert minden hívás VirtualAlloc()
több munkát végez, mint egy hívás malloc()
, amely az előre lefoglalt alacsony szintű memória egy blokkjának folyamatos felosztásával és felosztásával működik.
<p></p>
VirtualAlloc()
A kis blokkok ismételt megismétlése összességében több memóriát is igénybe vesz, mivel minden blokk kiesik VirtualAlloc()
jellemzően 4 KB memória többszörösét fogyasztja (vagy 2 MB, ha ún nagy memórialapok), így a fenti 128 bájtos pufferünket felfelé kerekítjük 4096 bájtra, elpazarolva a 3968 bájtot a 4 KB-os memóriablokk végén.
De amint látja, a visszakapott memória automatikusan kiürül (nullára állítva), így nem látjuk, hogy mi volt ott korábban, és ezúttal a program összeomlik, amikor megpróbáljuk végrehajtani a használat utáni trükkünket, mert a Windows azt észleli, hogy a már nem birtokolt memóriába próbálunk belekukucskálni:
C:UsersduckKEYPASS> unl2 „Új” puffer kiíratása induláskor 0000000000EA0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0010 00 00 00 EA00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 0000000000EA0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0040 00 00 00 00 00 ................ 00EA00: 00 00 00 00 00 00 00 00 00 0000000000 0050 00 00 00 00 00 00 00EA00: 00 00 00 00 00 00 00 00 0000000000 0060 00 00 00 00 00 00 ................ 00EA00: 00 00 00 00 00 00 00 00 0000000000 0070 00 00 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0080 00 00 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 ................ A teljes karakterlánc a következő volt: unlikelytextIBIPJPPHEOPOIDLL 0000000000EA0000: 75 6E 6C 69 6B 65 6C 79 74 65 78 74 49 IBIP42 unlike 49 szöveg 50 A 0000000000 0010 4 50 50F 48 45F 4 50 4C 49C 44 4 4 00 JPPHEOPOIDLL.... 00EA00: 00 0000000000 0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0030 00 00 00 00 00 00EA00: 00 00 00 00 00 00 00 00 00 0000000000 0040 00 00 00 00 00 ................ 00EA00: 00 00 00 00 00 00 00 00 00 0000000000 0050 00 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0060 00 ................ 00EA00 00 00 00 00 00 00 00 ................ 00EA00: 00 00 00 00 0000000000 0070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0080 00 00 00 00 00 00 00 00 00 ................ Várakozás az [ENTER]-re, hogy felszabadítsa a puffert... Puffer kiíratása free() 00EA00 után: [A program itt leállt, mert a Windows elkapta az utánhasználatunkat]
Mert a felszabadított memóriát újra le kell foglalni VirtualAlloc()
mielőtt újra felhasználható lenne, feltételezhetjük, hogy az újrahasznosítás előtt nullázzuk.
Ha azonban meg akarunk győződni arról, hogy ki van zárva, hívhatjuk a speciális Windows funkciót RtlSecureZeroMemory()
közvetlenül a felszabadítás előtt, hogy a Windows először nullákat írjon a pufferünkbe.
A kapcsolódó funkció RtlZeroMemory()
, ha kíváncsi vagy, hasonlót csinál, de a tényleges működés garanciája nélkül, mert a fordítók eltávolíthatják, mint elméletileg redundáns, ha azt veszik észre, hogy utána nem használják újra a puffert.
Amint látja, nagy gondot kell fordítanunk a megfelelő Windows-funkciók használatára, ha minimalizálni akarjuk azt az időt, amely a memóriában tárolt titkok számára a későbbiekben rejlik.
Ebben a cikkben nem foglalkozunk azzal, hogyan akadályozhatja meg, hogy a titkok véletlenül a cserefájlba kerüljenek, ha azokat a fizikai RAM-ba zárja. (Célzás: VirtualLock()
valójában önmagában nem elég.) Ha többet szeretne megtudni az alacsony szintű Windows memóriabiztonságról, tudassa velünk a megjegyzésekben, és egy későbbi cikkben megvizsgáljuk.
Automatikus memóriakezelés használata
Az egyik ügyes módja annak elkerülésére, hogy magunknak kelljen lefoglalni, kezelni és felszabadítani a memóriát, ha olyan programozási nyelvet használunk, amely gondoskodik a malloc()
és a free()
vagy VirtualAlloc()
és a VirtualFree()
, automatikusan.
Szkriptnyelv, mint pl Perl, Piton, Lua, JavaScript és mások megszabadulnak a leggyakoribb memória-biztonsági hibáktól, amelyek a C és C++ kódokat sújtják, azáltal, hogy a háttérben nyomon követik a memóriahasználatot.
Ahogy korábban említettük, a fenti, rosszul megírt C mintakódunk most jól működik, de csak azért, mert ez még mindig egy rendkívül egyszerű program, fix méretű adatstruktúrákkal, ahol ellenőrzéssel ellenőrizhetjük, hogy nem írjuk felül a 128 bájtos pufferünket, és csak egy végrehajtási útvonal van, amely malloc()
és ennek megfelelővel végződik free()
.
De ha frissítjük, hogy lehetővé tegye a változó hosszúságú jelszavak generálását, vagy további funkciókat adunk a generálási folyamathoz, akkor mi (vagy bárki, aki ezt követően karbantartja a kódot) könnyen puffertúlcsorduláshoz, használat utáni mentességet okozó hibákhoz vagy olyan memóriához juthatunk, amely soha nem szabadul fel, és ezért a titkos adatok sokáig ott maradnak, miután már nincs rájuk szükség.
Egy olyan nyelven, mint a Lua, megengedhetjük a Lua futásidejű környezetet, amely a szakzsargonban ún. automatikus szemétszállítás, foglalkozik a memória lekérésével a rendszertől, és visszaküldésével, ha azt észleli, hogy abbahagytuk a használatát.
A fent felsorolt C program sokkal egyszerűbbé válik, ha a memóriafoglalásról és a lefoglalásról gondoskodunk helyettünk:
Lefoglalunk memóriát a karakterlánc megtartásához s
egyszerűen a karakterlánc hozzárendelésével 'unlikelytext'
hozzá.
Később vagy utalhatunk Luának kifejezetten arra, hogy már nem érdekel minket s
érték hozzárendelésével nil
(minden nils
lényegében ugyanaz a Lua objektum), vagy hagyja abba a használatát s
és várja meg, amíg Lua észreveszi, hogy már nincs rá szükség.
Akárhogy is, az általa használt memória s
végül automatikusan helyreáll.
És a puffer túlcsordulás vagy a méret helytelen kezelése elkerülése érdekében szöveges karakterláncokhoz való hozzáfűzéskor (a Lua operátor ..
, kiejtett koncat, lényegében két karakterláncot ad össze, például +
Pythonban), valahányszor kiterjesztünk vagy lerövidítünk egy karakterláncot, a Lua varázsütésre helyet foglal egy vadonatúj karakterlánc számára, ahelyett, hogy módosítaná vagy lecserélné az eredetit a meglévő memóriahelyén.
Ez a megközelítés lassabb, és a szövegkezelés során lefoglalt közbenső karakterláncok miatt magasabb memóriahasználati csúcsokhoz vezet, mint a C-ben, de sokkal biztonságosabb a puffertúlcsordulás tekintetében.
De ez a fajta automatikus karakterlánckezelés (a szakzsargonban úgy ismert, mint állandóság, mert a húrok soha nem kapnak mutált, vagy a helyükön módosítva, miután létrehozták őket), új kiberbiztonsági fejfájást okoz.
A fenti Lua programot futtattuk Windowson, egészen a második szünetig, közvetlenül a program kilépése előtt:
C:UsersduckKEYPASS> lua s1.lua A teljes karakterlánc: unlikelytextHLKONBOJILAGLNLN Várakozás az [ENTER]-re a karakterlánc felszabadítása előtt... Várakozás az [ENTER]-re a kilépés előtt...
Ezúttal egy folyamatmemória kiíratást vettünk, például:
C:UsersduckKEYPASS> procdump -ma lua lua-s1.dmp ProcDump v11.0 - Sysinternals folyamatkiíratási segédprogram Copyright (C) 2009-2022 Mark Russinovich és Andrew Richards Sysinternals - www.sysinternals.com [00:00:00]Puckuckslump. [1:1:00] 00. kiíratás írása: A kiíratott fájl becsült mérete 00 MB. [1:10:00] Az 00. kiíratás befejeződött: 00 MB 1 másodperc alatt íródott [10:0.1:00] A kiíratások száma elérve.
Ezután lefuttattuk ezt az egyszerű szkriptet, amely visszaolvassa a dump fájlt, és mindenhol megtalálja a memóriában, hogy az ismert karakterlánc unlikelytext
megjelent, és kinyomtatja a dumpfile-ban lévő helyével és a közvetlenül utána következő ASCII karakterekkel együtt:
Még akkor is, ha korábban szkriptnyelveket használtál, vagy bármilyen programozási ökoszisztémában dolgoztál, amely ún kezelt karakterláncok, ahol a rendszer nyomon követi a memóriafoglalásokat és -felszabadításokat, és úgy kezeli, ahogy jónak látja…
…meglepődhet, ha látja, hogy ez a memóriavizsgálat milyen kimenetet produkál:
C:UsersduckKEYPASS> lua findit.lua lua-s1.dmp 006D8AFC: unlikelytextALJBNGOAPLLBDEB 006D8B3C: unlikelytextALJBNGOA 006D8B7C: unlikelytextALJBNGO 006D8B006C: unlikelytextALJBNGO 8D006AFC: nem valószínű lytextALJBN 8D7D006C: unlikelytextALJBNGOAP 903D006C: unlikelytextALJBNGOAPL 90D006BC: unlikelytextALJBNGOAPLL 90D006FC: unlikelytextALJBNG 913D006BBALCALJBNG 91D006BALCBALJBlikely:91APDDALC:006text923text006 70D006FC: valószínűtlentextALJBNGOAPLLBD 8D006C: valószínűtlentextALJBNGOAPLLBDE 0DBXNUMXC: valószínűtlentextALJ XNUMXDBBXNUMXC: valószínűtlentextAL XNUMXDBDXNUMXC: valószínűtlentextA
Íme, annak idején, amikor megragadtuk a memórialerakásunkat, pedig már befejeztük a húrt s
(és azt mondta Luának, hogy már nincs rá szükségünk, mondván s = nil
), a kód által az út során létrehozott karakterláncok továbbra is jelen voltak a RAM-ban, még nem lettek helyreállítva vagy törölve.
Valójában, ha a fenti kimenetet maguk a karakterláncok szerint rendezzük, ahelyett, hogy a RAM-ban való megjelenés sorrendjét követnénk, el tudja képzelni, mi történt a ciklus során, amikor egy-egy karaktert fűztünk a jelszó karakterlánchoz:
C:UsersduckKEYPASS> lua findit.lua lua-s1.dmp | rendezés /+10 006DBD0C: unlikelytextA 006DBB8C: unlikelytextAL 006DB70C: unlikelytextALJ 006D91BC: unlikelytextALJB 006D8CBC: unlikelytextALJBN 006D90FC: unlikelytextALJBJBBD006textALJ8BBD7textALJ006BBD8textALJ3BBD006 8B7C: valószínűtlentextALJBNGOA 006D903D006C: valószínűtlentextALJBNGOAP 90D006C: valószínűtlen szövegALJBNGOAPL 913D006BC: valószínűtlentextALJBNGOAPLL 91D006C: valószínűtlentextAP ALJBNGOBLL923text006BDAL8AP006text8BDALXNUMXAPXNUMXtextXNUMX DXNUMXC: valószínűtlen szövegALJBNGOAPLLBDE XNUMXDXNUMXAFC: nem valószínűtextALJBNGOAPLLBDEB XNUMXDXNUMXBFC: valószínűtlen szövegALJBNGOAPLLBDEBJ
Mindazok az ideiglenes, köztes karakterláncok továbbra is megvannak, tehát még akkor is, ha sikeresen kitöröltük a végső értékét s
, akkor is mindent kiszivárogtatunk, kivéve az utolsó karakterét.
Valójában ebben az esetben még akkor is, ha a speciális Lua függvény meghívásával szándékosan rákényszerítettük a programunkat, hogy semmisítse meg az összes felesleges adatot. collectgarbage()
(a legtöbb szkriptnyelvben van valami hasonló), a bosszantó ideiglenes karakterláncokban lévő adatok nagy része amúgy is a RAM-ban ragadt, mert a Lua-t úgy fordítottuk le, hogy az automatikus memóriakezelést a jó öreg program segítségével végezze. malloc()
és a free()
.
Más szavakkal, még azután is, hogy a Lua visszanyerte ideiglenes memóriablokkjait, hogy újra felhasználhassa őket, nem tudtuk szabályozni, hogy ezek a memóriablokkok hogyan és mikor kerüljenek újra felhasználásra, és így mennyi ideig feküdjenek a folyamatban a megmaradt adataikkal, akik kiszaglásra, kiürítésre vagy más módon kiszivárogtatásra várnak.
Írja be a .NET parancsot
De mi a helyzet a KeePass-szal, innen indult ez a cikk?
A KeePass C# nyelven íródott, és a .NET futási környezetet használja, így elkerülhető a helytelen memóriakezelés, amelyet a C programok magukkal hoznak…
…de a C# saját szöveges karakterláncait kezeli, mint a Lua, ami felveti a kérdést:
Még ha a programozó elkerülte is a teljes mesterjelszó egy helyen történő tárolását, miután befejezte, a memóriakiíratáshoz hozzáférő támadók ennek ellenére találhatnak elegendő ideiglenes adatot a fő jelszó kitalálásához vagy visszaállításához, még akkor is, ha a támadók percekkel, órákkal vagy napokkal a jelszó beírása után hozzáfértek a számítógépéhez?
Egyszerűen fogalmazva: vannak-e észlelhető, kísérteties maradványai a főjelszónak, amelyek fennmaradnak a RAM-ban, még akkor is, ha azt várná, hogy törölték őket?
Bosszantó módon Github felhasználóként Vdohney felfedezte, a válasz (legalábbis a 2.54-nél régebbi KeePass verziók esetében) az: „Igen”.
Az egyértelműség kedvéért nem gondoljuk, hogy a tényleges főjelszó egyetlen szöveges karakterláncként állítható vissza a KeePass memóriakiíratásból, mert a szerző egy speciális funkciót hozott létre a fő jelszó beviteléhez, amely mindent megtesz annak érdekében, hogy elkerülje a teljes jelszó tárolását, ahol könnyen észrevehető és kiszagolható.
Ezzel megelégedtünk azzal, hogy a fő jelszavunkat a következőre állítottuk SIXTEENPASSCHARS
, írja be, majd azonnal, röviddel és jóval később memóriakiírásokat készít.
Egy egyszerű Lua szkripttel kerestük a dumpokat, amely mindenhol kereste a jelszó szövegét, mind 8 bites ASCII formátumban, mind 16 bites UTF-16 (Windows widechar) formátumban, például:
Az eredmények biztatóak voltak:
C:UsersduckKEYPASS> lua searchknown.lua kp2-post.dmp Kiíratási fájl olvasása... KÉSZ. SIXTEENPASSCHARS keresése 8 bites ASCII-ként... nem található. A SIXTEENPASSCHARS keresése UTF-16 formátumban... nem található.
Vdohney, a CVE-2023-32784 felfedezője azonban észrevette, hogy a fő jelszó beírása közben a KeePass vizuális visszajelzést ad azáltal, hogy egy Unicode „blob” karakterekből álló helyőrző karakterláncot hoz létre és jelenít meg, egészen a jelszó hosszáig:
A Windows széles karakterű szöveges karakterláncaiban (amelyek karakterenként két bájtból állnak, nem csak egy bájtból, mint az ASCII-ben) a „blob” karakter hexadecimális bájtként van kódolva a RAM-ban. 0xCF
követ 0x25
(ami véletlenül egy százalékjel az ASCII-ben).
Tehát még ha a KeePass nagy gondot fordít is a jelszó megadásakor beírt nyers karakterekre, előfordulhat, hogy megmaradt „blob” karakterláncok maradnak meg, amelyek könnyen észlelhetők a memóriában ismétlődő futtatások során, mint pl. CF25CF25
or CF25CF25CF25
...
…és ha igen, a talált blob-karakterek leghosszabb sorozata valószínűleg feladná a jelszava hosszát, ami a jelszó-információk kiszivárogtatásának szerény formája lenne, ha más nem.
A következő Lua-szkriptet használtuk a megmaradt jelszó-helyőrző karakterláncok jeleinek keresésére:
A kimenet meglepő volt (a helytakarékosság kedvéért töröltük az egymást követő sorokat, amelyekben ugyanannyi blob van, vagy kevesebb, mint az előző sornál):
C:UsersduckKEYPASS> lua findblobs.lua kp2-post.dmp 000EFF3C: * [. . .] 00BE621B: ** 00BE64C7: *** [. . .] 00BE6E8F: **** [. . .] 00BE795F: ***** [. . .] 00BE84F7: ****** [. . .] 00BE8F37: ******* [ hasonlóan folytatódik 8 blobnál, 9 blobnál stb. ] [ egészen pontosan 16 blobból álló utolsó két sorig ] 00C0503B: **************** 00C05077: ******************** 00C09337: ******************** 00C09738: 0123 maradék 058 egyezés *XNUMXb XNUMXBXNUMX: *
Az egymáshoz közel álló, de egyre növekvő memóriacímeken egy szisztematikus listát találtunk, amely 3 blobból, majd 4 blobból áll, és így tovább egészen 16 blobig (a jelszavunk hossza), amit sok véletlenszerűen elszórt egyblob-karakterlánc követ.
Tehát ezek a helyőrző „blob” karakterláncok valóban úgy tűnik, hogy beszivárognak a memóriába, és hátramaradnak a jelszó hosszának kiszivárogtatása érdekében, még jóval azután, hogy a KeePass szoftver befejezte a fő jelszót.
A következő lépés
Úgy döntöttünk, hogy tovább ásunk, akárcsak Vdohney.
Módosítottuk a mintaillesztő kódunkat, hogy észleljük a blob karakterláncokat, amelyeket egyetlen ASCII-karakter követ 16 bites formátumban (az ASCII-karaktereket az UTF-16 a szokásos 8 bites ASCII-kódként jeleníti meg, amit egy nulla bájt követ).
Ezúttal a helytakarékosság kedvéért minden olyan egyezés kimenetét letiltottuk, amely pontosan megegyezik az előzővel:
Meglepetés, meglepetés:
C:UsersduckKEYPASS> lua searchkp.lua kp2-post.dmp 00BE581B: *I 00BE621B: **X 00BE6BD3: ***T 00BE769B: ****E 00BE822B: *****E 00BE8C6B: ******N 00BE974B: *******P 00BEA25B: ********A 00BEAD33: *********S 00BEB81B: **********S 00BEC383: ***********C 00BECEEB: ************H 00BEDA5B: *************A 00BEE623: **************R 00BEF1A3: ***************S 03E97CF2: *N 0AA6F0AF: *W 0D8AF7C8: *X 0F27BAF8: *S
Nézze meg, mit hozunk ki a .NET felügyelt karakterlánc-memória régiójából!
Ideiglenes „blob karakterláncok” szorosan összeillesztett készlete, amely felfedi a jelszavunkban szereplő egymást követő karaktereket, kezdve a második karakterrel.
Ezeket a szivárgó húrokat széles körben elterjedt egykarakteres egyezések követik, amelyekről feltételezzük, hogy véletlenül keletkeztek. (A KeePass kiíratási fájl mérete körülbelül 250 MB, így bőven van hely a „blob” karakterek számára, hogy szerencsésen megjelenjenek.)
Még ha figyelembe vesszük is ezt a további négy egyezést, ahelyett, hogy elvetnénk őket valószínű eltérésként, sejthetjük, hogy a fő jelszó a következők egyike:
?IXTEENPASSCHARS ?NXTEENPASSCHARS ?WXTEENPASSCHARS ?SXTEENPASSCHARS
Nyilvánvalóan ez az egyszerű technika nem találja meg a jelszó első karakterét, mert az első „blob karakterlánc” csak az első karakter beírása után jön létre.
Ne feledje, hogy ez a lista szép és rövid, mert kiszűrtük azokat az egyezéseket, amelyek nem végződnek ASCII karakterekkel.
Ha más tartományba tartozó karaktereket keresett, például kínai vagy koreai karaktereket, akkor több véletlenszerű találatot kaphat, mert sokkal több karakterrel lehet párosítani…
…de gyanítjuk, hogy így is elég közel kerül a fő jelszavához, és úgy tűnik, hogy a jelszóhoz kapcsolódó „blob-karakterláncok” a RAM-ban vannak csoportosítva, feltehetően azért, mert a .NET futási környezet ugyanazon része körülbelül ugyanabban az időben foglalta le őket.
És itt van egy bevallottan hosszú és diszkurzív dióhéjban a lenyűgöző története CVE-2023 32784-.
Mit kell tenni?
- Ha Ön KeePass felhasználó, ne essen pánikba. Bár ez egy programhiba, és technikailag kihasználható sebezhetőség, a távoli támadóknak, akik ezzel a hibával akarták feltörni a jelszavát, először rosszindulatú programokat kell telepíteniük a számítógépére. Ez sok más módot adna számukra a jelszavak közvetlen ellopására, még akkor is, ha ez a hiba nem létezne, például a billentyűleütések naplózásával gépelés közben. Ezen a ponton egyszerűen csak figyelhet a közelgő frissítésre, és megragadhatja, amikor készen áll.
- Ha nem használ teljes lemezes titkosítást, fontolja meg annak engedélyezését. A megmaradt jelszavak swap fájlból vagy hibernált fájlból (az operációs rendszer lemezének fájljai, amelyek a memóriatartalom ideiglenes mentésére szolgálnak nagy terhelés esetén vagy amikor a számítógép „alszik”) kinyeréséhez a támadóknak közvetlen hozzáférésre van szükségük a merevlemezhez. Ha aktiválva van a BitLocker vagy annak megfelelője más operációs rendszerekhez, akkor nem férhetnek hozzá a swap-fájlhoz, a hibernált fájlhoz vagy más személyes adatokhoz, például dokumentumokhoz, táblázatokhoz, mentett e-mailekhez és így tovább.
- Ha Ön programozó, tájékozódjon a memóriakezelési problémákról. Ne feltételezze, hogy csak azért, mert minden
free()
megfelel a megfelelőjénekmalloc()
hogy adatai biztonságban és jól kezelve legyenek. Néha előfordulhat, hogy további óvintézkedéseket kell tennie, hogy elkerülje a titkos adatok heverését, és ezeket az óvintézkedéseket operációs rendszerenként kell betartani. - Ha Ön minőségbiztosítási tesztelő vagy kódellenőr, mindig gondoljon „a színfalak mögé”. Még ha a memóriakezelési kód rendezettnek és kiegyensúlyozottnak tűnik is, legyen tudatában annak, hogy mi történik a színfalak mögött (mert az eredeti programozó ezt nem tudhatta), és készüljön fel néhány penesztelési jellegű munkára, mint például a futásidejű figyelés és a memória kiíratása, hogy megbizonyosodjon arról, hogy a biztonságos kód valóban úgy viselkedik, ahogyan kell.
KÓD A CIKKBŐL: UNL1.C
#beleértve #beleértve #beleértve void hexdump(előjel nélküli char* buff, int len) { // Puffer nyomtatása 16 bájtos darabokban for (int i = 0; i < len+16; i = i+16) { printf("%016X: ",buff+i); // 16 bájt hexadecimális értéke a következőhöz: (int j = 0; j < 16; j = j+1) { printf("%02X ",buff[i+j]); } // Ismételje meg ezt a 16 bájtot karakterként a következőhöz: (int j = 0; j < 16; j = j+1) { unsigned ch = buff[i+j]; printf("%c",(ch>=32 && ch<=127)?ch:'.'); } printf("n"); } printf("n"); } int main(void) { // Memória beszerzése a jelszó tárolására, és megmutatja, hogy // mi van a pufferben, amikor az hivatalosan "új"... char* buff = malloc(128); printf("Az 'új' puffer kiürítése indításkor"); hexdump(buff,128); // Pseudorandom puffer cím használata véletlenül seedként srand((unsigned)buff); // Indítsa el a jelszót valamilyen rögzített, kereshető szöveggel strcpy(buff,"unlikelytext"); // 16 álvéletlen betű hozzáfűzése, egyenként ehhez: (int i = 1; i <= 16; i++) { // Válasszon egy betűt A (65+0) és P (65+15) között char ch = 65 + (rand() & 15); // Ezután módosítsa a buff karakterláncot a strncat(buff,&ch,1); } // A teljes jelszó már a memóriában van, ezért // írja ki karakterláncként, és mutassa meg a teljes puffert... printf("A teljes karakterlánc: %sn",buff); hexdump(buff,128); // Szünet a folyamat RAM kiírásához (próbáld meg: 'procdump -ma') puts("Várakozás az [ENTER] puffer felszabadítására..."); getchar(); // Formálisan szabadd fel a memóriát, és mutasd meg újra a puffert //, hogy megnézd, nem maradt-e hátra valami... free(buff); printf("Puffer kiürítése free()n után"); hexdump(buff,128); // Szünet a RAM ismételt kiírásához a különbségek vizsgálatához puts("Várakozás az [ENTER]-re a main() kilépéshez"); getchar(); visszatérés 0; }
KÓD A CIKKBŐL: UNL2.C
#beleértve #beleértve #beleértve #beleértve void hexdump(előjel nélküli char* buff, int len) { // Puffer nyomtatása 16 bájtos darabokban for (int i = 0; i < len+16; i = i+16) { printf("%016X: ",buff+i); // 16 bájt hexadecimális értéke a következőhöz: (int j = 0; j < 16; j = j+1) { printf("%02X ",buff[i+j]); } // Ismételje meg ezt a 16 bájtot karakterként a következőhöz: (int j = 0; j < 16; j = j+1) { unsigned ch = buff[i+j]; printf("%c",(ch>=32 && ch<=127)?ch:'.'); } printf("n"); } printf("n"); } int main(void) { // Memória beszerzése a jelszó tárolására, és megmutatja, hogy // mi van a pufferben, amikor az hivatalosan "új"... char* buff = VirtualAlloc(0,128,MEM_COMMIT,PAGE_READWRITE); printf("Az 'új' puffer kiürítése indításkor"); hexdump(buff,128); // Pseudorandom puffer cím használata véletlenül seedként srand((unsigned)buff); // Indítsa el a jelszót valamilyen rögzített, kereshető szöveggel strcpy(buff,"unlikelytext"); // 16 álvéletlen betű hozzáfűzése, egyenként ehhez: (int i = 1; i <= 16; i++) { // Válasszon egy betűt A (65+0) és P (65+15) között char ch = 65 + (rand() & 15); // Ezután módosítsa a buff karakterláncot a strncat(buff,&ch,1); } // A teljes jelszó már a memóriában van, ezért // írja ki karakterláncként, és mutassa meg a teljes puffert... printf("A teljes karakterlánc: %sn",buff); hexdump(buff,128); // Szünet a folyamat RAM kiírásához (próbáld meg: 'procdump -ma') puts("Várakozás az [ENTER] puffer felszabadítására..."); getchar(); // Formálisan szabadítsd fel() a memóriát, és mutasd meg a puffert // újra, hogy megnézd, nem maradt-e hátra valami... VirtualFree(buff,0,MEM_RELEASE); printf("Puffer kiürítése free()n után"); hexdump(buff,128); // Szünet a RAM ismételt kiírásához a különbségek vizsgálatához puts("Várakozás az [ENTER]-re a main() kilépéshez"); getchar(); visszatérés 0; }
KÓD A CIKKBŐL: S1.LUA
-- Kezdje valamilyen rögzített, kereshető szöveggel s = 'unlikelytext' -- Adjon hozzá 16 véletlenszerű karaktert 'A'-tól 'P'-ig, ha i = 1,16 do s = s .. string.char(65+math.random(0,15)) end print('A teljes karakterlánc:',s,pauseing'n'TER folyamat előtt ') io.read() -- Törölje a karakterláncot, és jelölje meg a változót unusedként s = nil -- A diffs kereséséhez ismét írja ki a RAM-ot print('Várakozás az [ENTER]-re a kilépés előtt...') io.read()
KÓD A CIKKBŐL: FINDIT.LUA
-- beolvasás a dump fájlban local f = io.open(arg[1],'rb'):read('*a') -- keressen jelölőszöveget, majd egy -- vagy több véletlenszerű ASCII karaktert local b,e,m = 0,0,nil, míg true do -- keresse meg a következő egyezést, és emlékezzen a b,e,m = f:find('(valószínűtlen) eltolásra, ha nincs egyezés, ex AZ]+) pozíció és karakterlánc található print(string.format('%1X: %s',b,m)) end
KÓD A CIKKBŐL: SEARCHKNOWN.LUA
io.write('Olvasás a kiíratási fájlban... ') local f = io.open(arg[1],'rb'):read('*a') io.write('DONE.n') io.write('SIXTEENPASSCHARS keresése 8 bites ASCII-ként... ') local p08(SCHIOPIX) és find(SCHiop) 'FOUND' vagy 'not found','.n') io.write('SIXTEENPASSCHARS keresése UTF-08-ként... ') local p16 = f:find('Sx16Ix00Xx00Tx00Ex00Ex00Nx00Px00'.. 'Ax00x00x00x00 00') io.write(p00 és 'FOUND' vagy 'not found','.n')
KÓD A CIKKBŐL: FINDBLOBS.LUA
-- beolvasás a parancssorban megadott kiíratási fájlban local f = io.open(arg[1],'rb'):read('*a') -- Keressen egy vagy több jelszóblobot, majd minden nem blobot -- Ne feledje, hogy a blob karakterek (●) Windows widechars-okba kódolnak -- litte-endian UTF-16 kódokként, amelyek hexCF-ként jönnek ki. lokális b,e,m = 25,nil, míg true do -- Egy vagy több blobot akarunk, majd bármely nem blobot. -- Leegyszerűsítjük a kódot azzal, hogy keresünk egy explicit CF0,0-öt -- ezt követi minden olyan karakterlánc, amelyben csak CF vagy 25 van, -- így megtaláljuk a CF25CFCF vagy CF25CF és a CF2525CF25 karakterláncot is. -- A "téves pozitívumot" később kiszűrjük, ha vannak. -- Az x25 helyett '%%'-ot kell írnunk, mert az x25 -- karakter (százalékjel) egy speciális keresési karakter a Lua-ban! b,e,m = f:find('(xCF%%[xCF%%]*)',e+25) -- kilépés, ha nincs több egyezés, ha nem b, akkor break end -- A CMD.EXE nem tud blobokat nyomtatni, ezért csillagokká alakítjuk őket. print(string.format('%1X: %s',b,m:gsub('xCF%%','*'))) end
KÓD A CIKKBŐL: SEARCHKP.LUA
-- beolvasás a parancssorban megadott kiíratási fájlban local f = io.open(arg[1],'rb'):read('*a') local b,e,m,p = 0,0,nil,nil, míg true do -- Most egy vagy több blobot (CF25) akarunk, majd a kódot -- for A..Z, majd egy fAC 0 bájtot a következőre konvertálva: b,tfc16(IIx) %[xCF%%]*[AZ])x00',e+1) -- kilép, ha nincs több egyezés, ha nem b, majd törje a végét -- A CMD.EXE nem tud blobokat nyomtatni, ezért csillagokká alakítjuk őket. -- A helytakarékosság érdekében elnyomjuk az egymást követő egyezéseket, ha m ~= p, majd print(string.format('%08X: %s',b,m:gsub('xCF%%','*'))) p = m end end
- SEO által támogatott tartalom és PR terjesztés. Erősödjön még ma.
- PlatoAiStream. Web3 adatintelligencia. Felerősített tudás. Hozzáférés itt.
- A jövő pénzverése – Adryenn Ashley. Hozzáférés itt.
- Részvények vásárlása és eladása PRE-IPO társaságokban a PREIPO® segítségével. Hozzáférés itt.
- Forrás: https://nakedsecurity.sophos.com/2023/05/31/serious-security-that-keepass-master-password-crack-and-what-we-can-learn-from-it/
- :van
- :is
- :nem
- :ahol
- ][p
- $ UP
- 1
- 10
- 12
- 15%
- 20
- 200
- 2023
- 24
- 250
- 27
- 31
- 3d
- 49
- 50
- 67
- 70
- 72
- 77
- 8
- 9
- a
- Képes
- Rólunk
- felett
- Abszolút
- AC
- hozzáférés
- Fiók
- szerez
- megszerzése
- aktív
- tényleges
- tulajdonképpen
- hozzáadott
- További
- cím
- címek
- Hozzáteszi
- Után
- később
- újra
- Minden termék
- elkülönített
- kiosztja
- kiosztás
- juttatások
- lehetővé
- kizárólag
- mentén
- már
- Is
- megváltozott
- Bár
- mindig
- an
- és a
- Andrew
- válasz
- bármilyen
- bármi
- bármi kritikus
- megjelenik
- megjelent
- megközelítés
- jóváhagyott
- VANNAK
- körül
- cikkben
- cikkek
- AS
- At
- szerző
- auto
- Automatikus
- automatikusan
- elérhető
- elkerülése érdekében
- kerülendő
- tudatában van
- el
- vissza
- háttér
- background-image
- BE
- mert
- válik
- óta
- előtt
- Kezdet
- mögött
- a színfalak mögött
- lent
- Jobb
- Bit
- Blokk
- Blocks
- határ
- mindkét
- Alsó
- márka
- Brand New
- szünet
- tömören
- hoz
- ütköző
- puffer túlcsordulás
- Bogár
- bogarak
- épít
- de
- by
- C + +
- hívás
- hívás
- TUD
- Kaphat
- ami
- eset
- elkapott
- CD
- Központ
- biztosan
- láncok
- esély
- megváltozott
- karakter
- karakter
- ellenőrzése
- Ellenőrzések
- kínai
- A pop-art design, négy időzóna kijelzése egyszerre és méretének arányai azok az érvek, amelyek a NeXtime Time Zones-t kiváló választássá teszik. Válassza a
- világos
- világosan
- közel
- kód
- szín
- COM
- jön
- érkező
- megjegyzés
- Hozzászólások
- Közös
- teljes
- bonyolult
- számítógép
- Fontolja
- tekintélyes
- figyelembe vett
- Összeáll
- építése
- tartalom
- tartalom
- folyamatosan
- tovább
- ellenőrzés
- megtérít
- copyright
- Megfelelő
- tudott
- terjed
- repedés
- teremt
- készítette
- Teremtő
- kritikai
- Kiberbiztonság
- VESZÉLY
- Veszélyes
- dátum
- adatszivárgás
- Nap
- üzlet
- határozott
- elszánt
- leírt
- DID
- különbségek
- különböző
- Nehézség
- DIG
- digitális
- közvetlen
- Közvetlen hozzáférés
- közvetlenül
- kijelző
- megjelenítő
- van
- do
- dokumentumok
- nem
- Nem
- Ennek
- csinált
- ne
- le-
- hajtás
- két
- kiírása
- alatt
- e
- minden
- Korábban
- könnyen
- ökoszisztéma
- bármelyik
- más
- e-mailek
- lehetővé téve
- bátorító
- titkosítás
- végén
- vége
- elég
- biztosítására
- biztosítása
- belép
- belépés
- Egész
- belépés
- Környezet
- Egyenértékű
- hiba
- lényegében
- becsült
- stb.
- Eter (ETH)
- Még
- végül is
- egyre növekvő
- Minden
- minden
- pontosan
- példa
- Kivéve
- Izgalom
- végrehajtás
- létezik
- létező
- Kilépés
- Kilépés
- vár
- Magyarázza
- Exploit
- kitett
- terjed
- külön-
- kivonat
- tény
- hamis
- elbűvölő
- Jellemzők
- Visszacsatolás
- kevesebb
- harcoló
- filé
- Fájlok
- szűrő
- utolsó
- Végül
- Találjon
- megtalálása
- leletek
- végén
- vezetéknév
- rögzített
- Összpontosít
- követ
- következő
- A
- forma
- Formálisan
- formátum
- közelgő
- talált
- négy
- Ingyenes
- ból ből
- Tele
- teljesen
- funkció
- funkciók
- további
- jövő
- generál
- generáció
- kap
- szerzés
- GitHub
- Ad
- adott
- ad
- Giving
- Go
- Goes
- megy
- jó
- Kormány
- megragad
- nagy
- garancia
- kellett
- Fogantyúk
- történt
- Esemény
- megtörténik
- Kemény
- Legyen
- tekintettel
- fejfájás
- nehéz
- magasság
- itt
- HEX
- magas szinten
- <p></p>
- Találat
- tart
- Lyuk
- remény
- NYITVATARTÁS
- lebeg
- Hogyan
- How To
- HTTPS
- vadászat
- i
- azonosító
- if
- azonnal
- fontos
- in
- magában foglalja a
- Beleértve
- információ
- tájékoztatták
- helyette
- érdekelt
- Közbülső
- Internet
- bele
- kérdések
- IT
- ITS
- maga
- zsargon
- június
- éppen
- csak egy
- Tart
- Kulcs
- Ismer
- ismert
- koreai
- nyelv
- Nyelvek
- hordozható számítógép
- keresztnév
- a későbbiekben
- vezet
- vezetékek
- szivárog
- Szivárgás
- TANUL
- tanulás
- legkevésbé
- kilépő
- balra
- Hossz
- levél
- könyvtár
- élet
- mint
- Valószínű
- Korlátozott
- vonal
- vonalak
- Lista
- Listázott
- ll
- kiszámításának
- helyi
- elhelyezkedés
- fakitermelés
- Hosszú
- hosszú lejáratú
- hosszabb
- néz
- hasonló
- nézett
- keres
- MEGJELENÉS
- Sok
- szerencse
- fenntartja
- csinál
- malware
- kezelése
- sikerült
- vezetés
- menedzser
- kezeli
- Manipuláció
- sok
- Margó
- jel
- jelző
- mester
- Mérkőzés
- egyező
- max-width
- Lehet..
- eszközök
- Memory design
- említett
- microsoft
- esetleg
- jegyzőkönyv
- szerény
- módosított
- módosítása
- pillanat
- ellenőrzés
- több
- a legtöbb
- sok
- többszörös
- Tiszta
- Szükség
- szükséges
- háló
- soha
- Mindazonáltal
- Új
- hír
- következő
- szép
- nem
- normális
- semmi
- Értesítés..
- Most
- szám
- számok
- tárgy
- Nyilvánvaló
- of
- kedvezmény
- hivatalos
- Hivatalosan
- eltolt
- Régi
- on
- egyszer
- ONE
- csak
- nyílt forráskódú
- üzemeltetési
- operációs rendszer
- operációs rendszer
- operátor
- opció
- or
- érdekében
- eredeti
- Más
- Egyéb
- másképp
- mi
- magunkat
- ki
- teljesítmény
- felett
- átfogó
- saját
- oldal
- Pánik
- rész
- Jelszó
- Password Manager
- jelszavak
- ösvény
- Mintás
- Paul
- szünet
- Fizet
- százalék
- talán
- időszak
- tartósan
- személyes
- személyes adat
- fizikai
- kép
- darabok
- Hely
- placeholder
- Pestis
- Plató
- Platón adatintelligencia
- PlatoData
- bőséges
- pont
- pont
- Népszerű
- pozíció
- lehetséges
- Hozzászólások
- potenciális
- pontosan
- be
- szép
- megakadályozása
- előző
- ár
- Plakátok
- valószínűleg
- problémák
- folyamat
- Program
- Programozó
- programozók
- Programozás
- Programok
- kiejtett
- tesz
- Piton
- Kérdések és válaszok
- kérdés
- emelés
- RAM
- véletlen
- hatótávolság
- Inkább
- Nyers
- nyers adatok
- RE
- elérte
- Olvass
- Olvasás
- kész
- igazi
- való élet
- real-time
- tényleg
- elismerik
- Meggyógyul
- visszanyerésére
- összefüggő
- megmaradó
- eszébe jut
- távoli
- eltávolítása
- ismétlés
- megismételt
- TÖBBSZÖR
- jelentést
- képviselők
- tisztelet
- illetőleg
- REST
- Eredmények
- visszatérés
- visszatérő
- mutatják
- megszabadít
- jobb
- Kockázat
- kockázatok
- Szoba
- futás
- futás
- futásidejű megfigyelés
- s
- biztonságos
- biztonságosabb
- azonos
- elégedett
- Megtakarítás
- mondás
- beolvasás
- elszórt
- jelenetek
- Keresés
- keres
- Második
- másodperc
- Titkos
- Rész
- biztonság
- biztonság
- lát
- mag
- látás
- látszik
- látott
- lát
- Series of
- súlyos
- készlet
- beállítás
- rövid
- Hamarosan
- kellene
- előadás
- mutatott
- <p></p>
- Jelek
- hasonló
- Hasonlóképpen
- Egyszerű
- egyszerűsített
- egyszerűsítése
- egyszerűen
- egyetlen
- Méret
- alvás
- kicsi
- Trükkös
- bepillantás
- So
- szoftver
- szilárd
- néhány
- valami
- Nemsokára
- forrás
- forráskód
- Hely
- speciális
- különösen
- meghatározott
- sebesség
- Csillag
- kezdet
- kezdődött
- Kezdve
- kezdődik
- indítás
- Még mindig
- stóla
- megáll
- megállt
- tárolni
- memorizált
- Történet
- Húr
- erős
- Tanulmány
- sikeresen
- ilyen
- elegendő
- feltételezett
- meglepetés
- meglepődött
- meglepő
- túlélni
- SVG
- csere
- rendszer
- Systems
- Vesz
- meghozott
- tart
- bevétel
- beszéd
- technikailag
- technikák
- ideiglenes
- teszt
- tesztek
- mint
- hogy
- A
- The Source
- azok
- Őket
- maguk
- akkor
- elmélet
- Ott.
- ebből adódóan
- ők
- dolog
- Szerintem
- ezt
- azok
- bár?
- gondoltam
- idő
- Cím
- nak nek
- együtt
- vett
- szerszám
- felső
- vágány
- Csomagkövetés
- átmenet
- átlátszó
- igaz
- megpróbál
- Fordult
- kettő
- típus
- jellemzően
- megért
- unicode
- -ig
- felhasználatlan
- felesleges
- Frissítések
- frissítve
- URL
- us
- minket kormány
- Használat
- usb
- használ
- utólagos használat
- használt
- használó
- használ
- segítségével
- hasznosság
- érték
- Értékek
- fajta
- ellenőrzése
- változat
- nagyon
- keresztül
- sebezhetőség
- W
- várjon
- Várakozás
- akar
- kívánatos
- volt
- Nézz
- Út..
- módon
- we
- Hetek
- JÓL
- voltak
- Mit
- amikor
- vajon
- ami
- míg
- WHO
- bárki
- egész
- miért
- lesz
- nyer
- ablakok
- törlés
- val vel
- nélkül
- csodálkozó
- szavak
- Munka
- dolgozott
- dolgozó
- művek
- aggódik
- lenne
- adna
- ír
- írás
- írott
- még
- te
- A te
- magad
- zephyrnet
- nulla