در طول دو هفته گذشته، ما مجموعهای از مقالات را دیدهایم که در مورد آنچه در مدیریت رمز عبور منبع باز محبوب KeePass به عنوان «شک رمز اصلی» توصیف شده است، صحبت کردهایم.
این اشکال به اندازهای مهم در نظر گرفته شد که بتوان یک شناسه رسمی دولت ایالات متحده را دریافت کرد (به نام CVE-2023-32784، اگر می خواهید آن را شکار کنید)، و با توجه به اینکه رمز عبور اصلی مدیریت رمز عبور شما تقریباً کلید کل قلعه دیجیتال شما است، می توانید درک کنید که چرا داستان هیجان زیادی را برانگیخت.
خبر خوب این است که مهاجمی که میخواهد از این باگ سوء استفاده کند، تقریباً به طور قطع باید رایانه شما را با بدافزار آلوده کرده باشد، و بنابراین میتواند به هر حال از ضربههای کلید و برنامههای در حال اجرا شما جاسوسی کند.
به عبارت دیگر، تا زمانی که خالق KeePass با یک بهروزرسانی که باید به زودی ظاهر شود (ظاهراً در ابتدای ژوئن 2023) ارائه شود، این باگ را میتوان یک ریسک قابل مدیریت در نظر گرفت.
همانطور که افشا کننده اشکال مراقبت می کند اشاره کن:
اگر از رمزگذاری کامل دیسک با رمز عبور قوی استفاده میکنید و سیستم شما [عاری از بدافزار] است، باید خوب باشید. هیچ کس نمی تواند رمزهای عبور شما را از راه دور از طریق اینترنت به تنهایی با این یافته بدزدد.
خطرات توضیح داده شده است
به طور خلاصه، این اشکال به دشواری حصول اطمینان از پاک شدن تمام ردپای داده های محرمانه از حافظه پس از پایان کار با آنها خلاصه می شود.
ما در اینجا مشکلات مربوط به نحوه جلوگیری از داشتن اطلاعات مخفی در حافظه را، حتی به طور خلاصه، نادیده خواهیم گرفت.
در این مقاله، ما فقط میخواهیم به برنامهنویسان در همه جا یادآوری کنیم که کد تأیید شده توسط یک بازبین امنیتی با نظری مانند "به نظر میرسد که پس از خود به درستی پاک میشود"…
در واقع ممکن است اصلاً به طور کامل پاک نشود و نشت داده بالقوه ممکن است با مطالعه مستقیم خود کد مشخص نباشد.
به بیان ساده، آسیبپذیری CVE-2023-32784 به این معنی است که رمز عبور اصلی KeePass ممکن است حتی پس از خروج از برنامه KeyPass از دادههای سیستم قابل بازیابی باشد، زیرا اطلاعات کافی در مورد رمز عبور شما (البته نه در واقع رمز عبور خام است، که ما به آن تمرکز خواهیم کرد. در یک لحظه روشن می شود) ممکن است فایل های تعویض سیستم یا خواب را پشت سر بگذارند، جایی که حافظه سیستم تخصیص یافته ممکن است در نهایت برای بعد ذخیره شود.
در رایانههای ویندوزی که در آن از BitLocker برای رمزگذاری هارد دیسک در هنگام خاموش شدن سیستم استفاده نمیشود، این به کلاهبرداری که لپتاپ شما را دزدیده است، فرصتی برای راهاندازی از درایو USB یا CD و بازیابی رمز اصلی شما میدهد. اگرچه خود برنامه KeyPass مراقب است که هرگز آن را به طور دائم روی دیسک ذخیره نکند.
نشت رمز عبور طولانی مدت در حافظه همچنین به این معنی است که از نظر تئوری، رمز عبور می تواند از حافظه خالی برنامه KeyPass بازیابی شود، حتی اگر این dump مدت ها پس از تایپ رمز عبور و مدت طولانی پس از KeePass گرفته شده باشد. خودش دیگر نیازی به نگه داشتن آن نداشت.
واضح است که باید فرض کنید که بدافزارهای موجود در سیستم شما میتوانند تقریباً هر رمز عبور تایپشدهای را از طریق انواع تکنیکهای جاسوسی بلادرنگ بازیابی کنند، البته تا زمانی که در زمان تایپ کردن شما فعال بودند. اما ممکن است به طور منطقی انتظار داشته باشید که زمان قرار گرفتن در معرض خطر به مدت کوتاهی از تایپ کردن محدود شود، نه به چند دقیقه، ساعت یا روز بعد، یا شاید بیشتر، از جمله پس از خاموش کردن رایانه خود.
چه چیزی پشت سر می گذارد؟
بنابراین ما فکر کردیم که نگاهی سطح بالا به این بیندازیم که چگونه داده های مخفی می توانند به روش هایی که مستقیماً از کد مشخص نیستند در حافظه باقی بمانند.
اگر برنامه نویس نیستید نگران نباشید - ما آن را ساده نگه می داریم و در حین حرکت توضیح می دهیم.
ما با نگاه کردن به استفاده از حافظه و پاکسازی در یک برنامه ساده C شروع می کنیم که وارد کردن و ذخیره موقت رمز عبور را با انجام کارهای زیر شبیه سازی می کند:
- اختصاص یک تکه حافظه اختصاصی مخصوص ذخیره رمز عبور
- درج یک رشته متن شناخته شده بنابراین در صورت نیاز می توانیم به راحتی آن را در حافظه پیدا کنیم.
- اضافه کردن 16 کاراکتر ASCII 8 بیتی شبه تصادفی از محدوده AP
- چاپ کردن بافر رمز عبور شبیه سازی شده
- آزاد کردن حافظه به امید حذف بافر رمز عبور.
- خارج شدن برنامه.
بسیار ساده شده، کد C ممکن است چیزی شبیه به این باشد، بدون بررسی خطا، با استفاده از اعداد شبه تصادفی با کیفیت پایین از تابع زمان اجرا C rand()
و نادیده گرفتن هرگونه بررسی سرریز بافر (هرگز هیچ یک از این کارها را در کد واقعی انجام ندهید!):
// 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);
در واقع، کدی که ما در نهایت در آزمایشهای خود استفاده کردیم، شامل چند بیت و تکههای اضافی است که در زیر نشان داده شده است، به طوری که میتوانیم محتویات کامل بافر رمز عبور موقت خود را همانطور که از آن استفاده میکردیم، خالی کنیم تا به دنبال محتوای ناخواسته یا باقیمانده بگردیم.
توجه داشته باشید که ما عمداً پس از تماس بافر را تخلیه می کنیم free()
، که از نظر فنی یک باگ بدون استفاده است، اما ما آن را در اینجا به عنوان روشی زیرکانه انجام می دهیم تا ببینیم آیا پس از بازگرداندن بافر ما، چیز مهمی باقی می ماند، که می تواند منجر به حفره نشت داده خطرناک در زندگی واقعی شود.
ما همچنین دو را درج کرده ایم Waiting for [Enter]
کد را درخواست می کند تا به خودمان فرصتی بدهیم تا در نقاط کلیدی برنامه حافظه خالی ایجاد کنیم، و داده های خام را برای جستجوی بعداً به ما می دهد تا ببینیم چه چیزی در حین اجرای برنامه باقی مانده است.
برای حذف حافظه، ما از مایکروسافت استفاده خواهیم کرد ابزار Sysinternals procdump
با -ma
گزینه (تمام حافظه را خالی کنید) که از نیاز به نوشتن کد خود برای استفاده از ویندوز جلوگیری می کند DbgHelp
سیستم و نسبتاً پیچیده آن MiniDumpXxxx()
توابع.
برای کامپایل کد C، از ساخت ساده و کوچک خودمان از فایل متن باز و رایگان Fabrice Bellard استفاده کردیم. کامپایلر Tiny C، برای ویندوز 64 بیتی موجود است منبع و فرم باینری مستقیماً از صفحه GitHub ما.
متن کپی و قابل چسباندن همه کد منبع تصویر شده در مقاله در پایین صفحه ظاهر می شود.
زمانی که برنامه آزمایشی را کامپایل و اجرا کردیم، این اتفاق افتاد:
C:UsersduckKEYPASS> petcc64 -stdinc -stdlib unl1.c کامپایلر کوچک C - حق چاپ (C) 2001-2023 Fabrice Bellard توسط Paul Ducklin برای استفاده به عنوان ابزار یادگیری حذف شد نسخه petcc64-0.9.27 [0006] - Generates فقط PE -> unl64.c -> c:/users/duck/tcc/petccinc/stdio.h [. . . .] -> c:/users/duck/tcc/petcclib/libpetcc1_1.a -> C:/Windows/system64/msvcrt.dll -> C:/Windows/system32/kernel32.dll -------- ----------------------- بخش اندازه فایل virt 32 1000 200 .text 438 2000 800ac .data 2 c3000 00 .pdata -------- ----------------------- <- unl24.exe (1 بایت) C:UsersduckKEYPASS> unl3584.exe تخلیه بافر "جدید" در شروع 1F00: 51390 90 F57 5 00 00 00 00 00 50 F01 5 00 00 00 00 .W......P....... 00F00A513: 0 73 74 65D 6 33 32C 5 63D 6 64E 2 65 78 . exe.D 65F00B44: 32 00 513 0 72 69 76 65 72 44D 61 74A 61C 3 43 3E riverData=C:Win 5F57C69: 6 00F 513 0 64 C 6 77 dowsSystem73Dr 5F53D79: 73 74 65 6 33 32C 5 44 72 32 00 513 0 69 76 65 iversDriverData 72F73E5: 44 72 69 76 65F 72 44 61 74 61D 00 513 0F 00 45 46 FC 43 5 34 FC 33 37 32 3 F31F00: 46 50 53F 5 4372 1 00 513F 0 42 52 4F 57 53 45F 52 BROWSER_APP_PROF 5F41: 50 50C 5 50F 52 4 46 00 51400E 49 4D 45 5E 53 54 52 ILE_49F 4 ILE_47 3 49 = ILE_6 74 65 72 00 51410 = 6 65 74C 20A 45 F78 70C AC 6B 7 56 net ExplzV.< .K.. رشته کامل بود: unlikelytextJHKNEJJCPOMDJHAN 4F3: 4 00E 00C 00 51390B 75 6C 6 69 6 65 6 79A 74 65B 78E unlikelytextJHKNEJJCPOMDJHAN 74F4 48D 4 4A 00 513 0E 45 4 4 43 EJJCPOMDJHAN.eD 50F4B4 : 44 4 48 41 4 00 65 00 44 00D 513 0A 72C 69 76 65E riverData=C:Win 72F44C61: 74 61F 3 43 3C 5 57 69 6C 00 513 0 64D6 77Dr 73F5D53: 79 73 74 65 6 33C 32 5 44 72 32 00 513 0 69 76 iversDriverData 65F72E73: 5 44 72 69 76F 65 72 44 61 74D 61 00 513 0 00 45F 46 43 5 34F .EFC33F . 37 32F 3 31 00 46 50F 53 5 4372 1F 00 513 0F 42 BROWSER_APP_PROF 52F4: 57 53C 45 52F 5 41 50 50 5E 50 52D 4 46E 00 51400 49 ILE_STRING=Inter 4F45:5 53 F54 52C AC 49B 4 47 net ExplzV.<.K.. در انتظار [ENTER] برای آزادسازی بافر... تخلیه بافر پس از free() 3F49: A6 74 F65 72 00 51410 6 65 74 20 F45 78 70 6 7 56 .g......P...... . 4 3 4E riverData=C:Win 00F00C00: 51390 0F 67 5 00C 00 00 00 00 50 01D 5 00 00C 00 00 dowsSystem00Dr 00F513D0: 45 4 4 43 50 4C 4 44 4 48 41 4 : 00 65 00 44 00F 513 0 72 69 76D 65 72 44 61 74 61F .EFC_3=43.FPS_ 3F5F57: 69 6 00F 513 0 64 6 77F 73 5 53 79F 73 74 65F 6 BROWSER_APP_PROF 33F32 5F44 72E 32 00D 513 0E 69 76 65 ILE_STRING=Inter 72F73: 5E 44 72 69 76 65 72 44C 61D 74 61 00D AC 513B 0 00 net ExplM..MK. در انتظار [ENTER] برای خروج از main()... C:UsersduckKEYPASS>
در این اجرا، ما به خود زحمت ندادیم که حافظههای پردازشی را جمعآوری کنیم، زیرا میتوانستیم بلافاصله از خروجی ببینیم که این کد دادهها را نشت میکند.
درست پس از فراخوانی تابع کتابخانه زمان اجرا ویندوز C malloc()
، میتوانیم ببینیم که بافری که برمیگردانیم شامل دادههای متغیر محیطی است که از کد راهاندازی برنامه باقی مانده است، با 16 بایت اول ظاهراً تغییر داده شده تا شبیه به نوعی سربرگ تخصیص حافظه باقیمانده باشد.
(توجه داشته باشید که چگونه آن 16 بایت شبیه دو آدرس حافظه 8 بایتی است، 0xF55790
و 0xF50150
، که به ترتیب درست بعد و درست قبل از بافر حافظه ما هستند.)
وقتی قرار است رمز عبور در حافظه باشد، همانطور که انتظار داریم میتوانیم کل رشته را به وضوح در بافر ببینیم.
اما بعد از تماس free()
، توجه داشته باشید که چگونه 16 بایت اول بافر ما یک بار دیگر با چیزی شبیه به آدرس های حافظه مجاور بازنویسی شده است، احتمالاً تا تخصیص دهنده حافظه بتواند بلوک های موجود در حافظه را که می تواند دوباره استفاده کند را ردیابی کند…
... اما بقیه متن رمز عبور "حذف شده" ما (12 کاراکتر تصادفی آخر EJJCPOMDJHAN
) عقب مانده است.
ما نه تنها باید تخصیص و عدم تخصیص حافظه خود را در C مدیریت کنیم، بلکه باید اطمینان حاصل کنیم که اگر میخواهیم آنها را دقیقاً کنترل کنیم، عملکردهای سیستم مناسب را برای بافرهای داده انتخاب میکنیم.
به عنوان مثال، با جابجایی به این کد به جای آن، کنترل بیشتری روی آنچه در حافظه وجود دارد دریافت می کنیم:
با تعویض از malloc()
و free()
برای استفاده از توابع تخصیص سطح پایین ویندوز VirtualAlloc()
و VirtualFree()
به طور مستقیم، ما کنترل بهتری خواهیم داشت.
با این حال، ما پرداخت قیمت در سرعت، چرا که هر تماس به VirtualAlloc()
کارهای بیشتری را انجام می دهد malloc()
، که با تقسیم و تقسیم مداوم یک بلوک از حافظه سطح پایین از پیش تخصیص داده شده کار می کند.
با استفاده از VirtualAlloc()
به طور مکرر برای بلوک های کوچک همچنین حافظه بیشتری را به طور کلی مصرف می کند، زیرا هر بلوک توسط آن تخلیه می شود VirtualAlloc()
معمولاً مضربی از 4 کیلوبایت حافظه (یا 2 مگابایت، اگر از اصطلاحاً استفاده می کنید) مصرف می کند. صفحات حافظه بزرگ، به طوری که بافر 128 بایتی ما در بالا به 4096 بایت گرد می شود و 3968 بایت در انتهای بلوک حافظه 4 کیلوبایتی هدر می رود.
اما، همانطور که می بینید، حافظه ای که برمی گردیم به طور خودکار خالی می شود (بر روی صفر تنظیم می شود)، بنابراین ما نمی توانیم آنچه قبلاً در آنجا بود را ببینیم، و این بار زمانی که ما سعی می کنیم استفاده خود را پس از استفاده رایگان انجام دهیم، برنامه از کار می افتد. ترفند، زیرا ویندوز تشخیص می دهد که ما سعی می کنیم به حافظه ای که دیگر مالک آن نیستیم نگاه کنیم:
C:UsersduckKEYPASS> unl2 تخلیه بافر "جدید" در شروع 0000000000EA0000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0010 00 00 00:00 00 00 00 00 00 00 00 00 00 00 00 00 0000000000 0020 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 00 .............. 00EA00: 0000000000 0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00EA0000000000: 0040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00EA0000000000: 0050 00 00 00 00 00 00 00 00 00 00 ................ 00EA00: 00 00 00 00 0000000000 0060 00 00 00 00 00 00 00 00 00 00 00 ................ 00EA00: 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 ................ رشته کامل: unlikelytextIBIPJPPHEOPOIDLL 00EA00: 00 00E 00C 00 00B 00 00C 00 00 0000000000 0000 75 6 6 69 6 IBIPJPPHEOPOIDLL 65EA6: IBA79ext 74 65 78 74F 49 42F 49 50 0000000000C 0010C 4 50 50 48 JPPHEOPOIDLL.... 45EA4: 50 4 49 44 4 4 00 00 00 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 00 00 00 ................ 00EA00: 00 00 00 00 00 00 0000000000 0040 00 00 00 ................ 00EA00: 00 00 00 00 00 00 00 00 00 00 00 0000000000 0050 00 00 00 00 ............... 00EA00: 00 00 00 00 00 00 00 00 00 00 0000000000 0060 00 00 00 00 ................ 00 00 00 00 00 ................ 00EA00: 00 00 00 00 00 0000000000 0070 00 00 00 00 00 00 00 00 00 00 ............. ... در انتظار [ENTER] برای آزادسازی بافر... تخلیه بافر پس از free() 00EA00: [برنامه در اینجا خاتمه یافت زیرا ویندوز استفاده ما را پس از آزاد شدن گرفت]
زیرا حافظه ای که آزاد کردیم نیاز به تخصیص مجدد با آن دارد VirtualAlloc()
قبل از اینکه بتوان دوباره از آن استفاده کرد، می توانیم فرض کنیم که قبل از بازیافت آن صفر می شود.
با این حال، اگر میخواهیم مطمئن شویم که خالی است، میتوانیم عملکرد ویژه ویندوز را فراخوانی کنیم RtlSecureZeroMemory()
درست قبل از آزاد کردن آن، برای تضمین اینکه ویندوز ابتدا صفرها را در بافر ما مینویسد.
تابع مربوطه RtlZeroMemory()
اگر تعجب میکنید، کار مشابهی را انجام میدهد، اما بدون ضمانت کارکرد واقعی، زیرا کامپایلرها مجازند در صورت عدم استفاده مجدد از بافر، آن را از نظر تئوری حذف کنند.
همانطور که می بینید، اگر بخواهیم زمان باقی ماندن اسرار ذخیره شده در حافظه را برای بعداً به حداقل برسانیم، باید برای استفاده از توابع مناسب ویندوز دقت زیادی داشته باشیم.
در این مقاله، ما قصد نداریم به این موضوع بپردازیم که چگونه با قفل کردن اسرار در رم فیزیکی، از ذخیره تصادفی اسرار در فایل swap خود جلوگیری کنید. (نکته: VirtualLock()
در واقع به خودی خود کافی نیست.) اگر مایلید در مورد امنیت حافظه سطح پایین ویندوز اطلاعات بیشتری کسب کنید، در نظرات به ما اطلاع دهید و در مقاله آینده به بررسی آن خواهیم پرداخت.
استفاده از مدیریت خودکار حافظه
یکی از راه های ساده برای جلوگیری از تخصیص، مدیریت و تخصیص حافظه توسط خودمان، استفاده از یک زبان برنامه نویسی است که مراقب malloc()
و free()
، یا VirtualAlloc()
و VirtualFree()
، به طور خودکار
زبان اسکریپت مانند پرل, پــایتــون, لوا, جاوا اسکریپت و سایرین با ردیابی میزان مصرف حافظه برای شما در پسزمینه، از شر رایجترین باگهای ایمنی حافظه خلاص میشوند.
همانطور که قبلاً ذکر کردیم، نمونه کد C نمونه غلط ما در بالا اکنون به خوبی کار می کند، اما فقط به این دلیل که هنوز یک برنامه فوق العاده ساده است، با ساختارهای داده با اندازه ثابت، که در آن می توانیم با بازرسی تأیید کنیم که 128- خود را بازنویسی نمی کنیم. بافر بایت، و اینکه فقط یک مسیر اجرا وجود دارد که با آن شروع می شود malloc()
و با یک متناظر به پایان می رسد free()
.
اما اگر آن را بهروزرسانی کنیم تا امکان تولید رمز عبور با طول متغیر را فراهم کنیم، یا ویژگیهای اضافی را به فرآیند تولید اضافه کنیم، ما (یا هر کسی که کد بعدی را حفظ میکند) به راحتی میتوانیم با سرریز بافر، باگهای بدون استفاده یا حافظه مواجه شویم. هرگز آزاد نمی شود و بنابراین داده های مخفی را مدت ها پس از اینکه دیگر مورد نیاز نیست، در اطراف خود باقی می گذارد.
در زبانی مانند Lua، ما میتوانیم به محیط Lua اجازه اجرا زمان اجرا را بدهیم، که کاری را انجام میدهد که در اصطلاح به عنوان شناخته شده است. جمع آوری خودکار زباله، با به دست آوردن حافظه از سیستم و بازگرداندن آن هنگامی که تشخیص داد ما استفاده از آن را متوقف کرده ایم، سر و کار دارد.
برنامه C که در بالا لیست کردیم بسیار ساده تر می شود وقتی که تخصیص و عدم تخصیص حافظه برای ما مراقبت شود:
ما حافظه را برای نگه داشتن رشته اختصاص می دهیم s
به سادگی با اختصاص رشته 'unlikelytext'
به آن است.
بعداً میتوانیم به لوا اشاره کنیم که دیگر به آن علاقه نداریم s
با تخصیص مقدار به آن nil
(همه nils
اساساً همان شی Lua هستند)، یا استفاده از آن را متوقف کنید s
و منتظر باشید تا Lua تشخیص دهد که دیگر به آن نیازی نیست.
در هر صورت، حافظه استفاده شده توسط s
در نهایت به طور خودکار بازیابی می شود.
و برای جلوگیری از سرریز شدن بافر یا سوء مدیریت اندازه هنگام الحاق به رشته های متنی (عملگر Lua ..
، تلفظ شده concat، اساساً دو رشته را به هم اضافه می کند +
در پایتون)، هر بار که یک رشته را گسترش یا کوتاه میکنیم، Lua بهجای تغییر یا جایگزینی رشته اصلی در محل حافظه موجود، فضا را برای یک رشته کاملاً جدید اختصاص میدهد.
این رویکرد کندتر است، و منجر به پیکهای استفاده از حافظه میشود که به دلیل رشتههای میانی تخصیص داده شده در طول دستکاری متن، بیشتر از آنچه در C دریافت میکنید، میشود، اما در مورد سرریز بافر بسیار ایمنتر است.
اما این نوع مدیریت خودکار رشته ها (در اصطلاح به عنوان تغییرناپذیری، زیرا رشته ها هرگز به دست نمی آیند جهش یافته، یا در جای خود اصلاح شوند، پس از ایجاد آنها، خود دردسرهای امنیت سایبری جدیدی را به همراه دارد.
ما برنامه Lua بالا را در ویندوز، تا مکث دوم، درست قبل از خروج برنامه اجرا کردیم:
C:UsersduckKEYPASS> lua s1.lua رشته کامل: unlikelytextHLKONBOJILAGLNLN در انتظار [ENTER] قبل از آزاد کردن رشته... انتظار برای [ENTER] قبل از خروج...
این بار، ما یک Dump حافظه پردازش کردیم، مانند این:
C:UsersduckKEYPASS> procdump -ma lua lua-s1.dmp ProcDump v11.0 - ابزار تخلیه فرآیند Sysinternals حق چاپ (C) 2009-2022 Mark Russinovich و Andrew Richards Sysinternals - www.sysinternals.com [00:00:00 Dump] شروع شد: C:UsersduckKEYPASSlua-s1.dmp [1:00:00] نوشتن Dump 00: اندازه فایل dump تخمینی 1 مگابایت است. [10:00:00] Dump 00 کامل شد: 1 مگابایت در 10 ثانیه نوشته شد [0.1:00:00] به تعداد Dump رسید.
سپس این اسکریپت ساده را اجرا کردیم، که فایل dump را دوباره به داخل می خواند، در همه جای حافظه می یابد که رشته شناخته شده unlikelytext
ظاهر شد و آن را همراه با مکان آن در dumpfile و کاراکترهای ASCII که بلافاصله بعد از آن منتشر شد چاپ می کند:
حتی اگر قبلا از زبان های اسکریپت استفاده کرده باشید یا در هر اکوسیستم برنامه نویسی کار کرده باشید که به اصطلاح رشته های مدیریت شده، جایی که سیستم تخصیص و تخصیص حافظه را برای شما پیگیری می کند و آنها را به دلخواه مدیریت می کند…
... ممکن است از دیدن خروجی ای که این اسکن حافظه تولید می کند شگفت زده شوید:
C:UsersduckKEYPASS> lua findit.lua lua-s1.dmp 006D8AFC: unlikelytextALJBNGOAPLLBDEB 006D8B3C: unlikelytextALJBNGOA 006D8B7C: unlikelytextALJBJBNGO 006D8BD006 غیرمحتملALJBJBNGO 8D006BDE 8CBC: متن غیر محتملALJBN 7D006D903C: متن غیرمحتملALJBNGOAP 006D90C: متن غیر محتملALJBNGOAPL 006D90BC: متن غیر محتملALJBN 006D913FC: بعید متنALJBNGOAP 006D91C: بعید متنALJBNGOAPL 006BC: unlikelytextALJB 91D006FC: unlikelytextALJBNGOAPLLBD 923D006C : unlikelytextALJBNGOAPLLBDE 70DB006C: unlikelytextALJ 8DBB006C: unlikelytextAL 0DBDXNUMXC: unlikelytextA
ببینید، در آن زمان ما حافظه خود را به دست گرفتیم، حتی اگر با رشته تمام شده بودیم s
(و با گفتن به لوا گفت که ما دیگر به آن نیاز نداریم s = nil
)، تمام رشته هایی که کد در طول مسیر ایجاد کرده بود هنوز در RAM وجود داشتند، هنوز بازیابی یا حذف نشده بودند.
در واقع، اگر خروجیهای بالا را بر اساس خود رشتهها مرتب کنیم، به جای پیروی از ترتیب ظاهر شدن آنها در RAM، میتوانید آنچه را در طول حلقهای که در آن یک کاراکتر در یک زمان به رشته رمز عبور خود متصل میکنیم، به تصویر بکشید:
C:UsersduckKEYPASS> lua findit.lua lua-s1.dmp | مرتبسازی /+10 006DBD0C: بعید متنA 006DBB8C: متن غیر محتملAL 006DB70C: متن غیر محتملALJ 006D91BC: متن غیرمحتملALJB 006D8CBC: متن غیرمحتملALJBN 006Btext90Text extALJBNGO 006D8B7C: متن غیر محتملALJBNGOA 006D8D3C: متن غیر محتملALJBNGOAP 006D8C: متن غیر محتملALJBNGOAPL 7D006BC: متن غیرمحتملALJBNGOAPL 903D006BC: متن غیر محتملALJBNGOAPLL90CLLBD006: متن غیر محتملALJBNGOAPLL913CLLBD006: متن غیر محتملALJBNGOAPLLBD 91D006C: متن غیر محتملALJBNGOAPLLBDE 923D006AFC: متن غیرمحتملALJBNGOAPLLBDEB 8D006BFC : unlikelytextALJBNGOAPLLBDEBJ
همه آن رشته های موقت و میانی هنوز وجود دارند، بنابراین حتی اگر با موفقیت مقدار نهایی را پاک کرده باشیم s
، ما هنوز همه چیز را به جز آخرین شخصیت آن لو می دهیم.
در واقع، در این مورد، حتی زمانی که ما عمداً برنامه خود را مجبور کردیم تا با فراخوانی تابع ویژه Lua، تمام داده های غیر ضروری را از بین ببرد. collectgarbage()
(بیشتر زبان های اسکریپت نویسی چیزی مشابه دارند)، بیشتر داده ها در آن رشته های موقت مزاحم به هر حال در RAM گیر می کنند، زیرا ما Lua را برای انجام مدیریت خودکار حافظه با استفاده از قدیمی خوب کامپایل کرده بودیم. malloc()
و free()
.
به عبارت دیگر، حتی پس از اینکه Lua خود بلوکهای حافظه موقت خود را بازیابی کرد تا دوباره از آنها استفاده کند، ما نمیتوانستیم نحوه یا زمان استفاده مجدد از آن بلوکهای حافظه را کنترل کنیم، و بنابراین مدت زمانی که آنها در داخل فرآیند با سمت چپ خود باقی بمانند. بیش از دادههایی که در انتظار شناسایی، ریخته شدن یا به بیرون درز کردن هستند.
وارد دات نت شوید
اما در مورد KeePass، جایی که این مقاله شروع شد، چطور؟
KeePass به زبان سی شارپ نوشته شده است و از زمان اجرا دات نت استفاده می کند، بنابراین از مشکلات سوء مدیریت حافظه که برنامه های C با خود به ارمغان می آورند جلوگیری می کند.
اما سی شارپ رشته های متنی خود را مدیریت می کند، به جای اینکه Lua انجام می دهد، که این سوال را ایجاد می کند:
حتی اگر برنامه نویس پس از اتمام کار از ذخیره کل رمز عبور اصلی در یک مکان اجتناب کند، مهاجمان با دسترسی به حافظه خالی می توانند به اندازه کافی داده های موقت باقی مانده را برای حدس زدن یا بازیابی رمز عبور اصلی پیدا کنند، حتی اگر مهاجمان چند دقیقه، ساعت یا چند روز پس از اینکه رمز عبور را وارد کردید به رایانه شما دسترسی پیدا کردند؟
به عبارت ساده، آیا بقایای شبحآلود و قابل شناسایی از رمز عبور اصلی شما وجود دارد که حتی پس از حذف آنها در RAM باقی میماند؟
آزاردهنده، به عنوان کاربر Github ودوهنی کشف کرد، پاسخ (حداقل برای نسخه های KeePass زودتر از 2.54) "بله" است.
برای روشن بودن، ما فکر نمی کنیم که رمز عبور اصلی واقعی شما را بتوان به عنوان یک رشته متنی از حافظه KeePass بازیابی کرد، زیرا نویسنده یک تابع ویژه برای ورود رمز اصلی ایجاد کرده است که برای جلوگیری از ذخیره کامل رمز عبور، از راه خود خارج می شود. رمز عبوری که به راحتی قابل تشخیص و استشمام بود.
ما با تنظیم رمز عبور اصلی خود را از این راضی کردیم SIXTEENPASSCHARS
، آن را تایپ کنید، و سپس بلافاصله، مدت کوتاهی و مدت زیادی پس از آن حافظه را حذف کنید.
ما با یک اسکریپت ساده Lua که در همه جا متن رمز عبور را جستجو می کرد، هم در قالب ASCII 8 بیتی و هم در قالب UTF-16 16 بیتی (Windows widechar) به دنبال آن بودیم:
نتایج دلگرم کننده بود:
C:UsersduckKEYPASS> lua searchknown.lua kp2-post.dmp خواندن در فایل dump... انجام شد. جستجوی SIXTEENPASSCHARS به عنوان ASCII 8 بیتی... یافت نشد. جستجوی SIXTEENPASSCHARS به عنوان UTF-16... یافت نشد.
اما Vdohney، کاشف CVE-2023-32784، متوجه شد که وقتی رمز عبور اصلی خود را تایپ می کنید، KeePass با ساخت و نمایش یک رشته محل نگهدار متشکل از کاراکترهای یونیکد «blob»، تا و شامل طول شما، بازخورد بصری به شما می دهد. کلمه عبور:
در رشته های متنی Widechar در ویندوز (که از دو بایت در هر کاراکتر تشکیل شده است، نه فقط یک بایت مانند ASCII)، کاراکتر "blob" در RAM به عنوان بایت هگز کدگذاری می شود. 0xCF
و پس از آن 0x25
(که اتفاقاً یک علامت درصد در ASCII است).
بنابراین، حتی اگر KeePass مراقب کاراکترهای خامی باشد که هنگام وارد کردن رمز عبور وارد میکنید، ممکن است با رشتههای باقیمانده از کاراکترهای «blob» مواجه شوید که بهراحتی در حافظه بهصورت تکراری قابل شناسایی هستند. CF25CF25
or CF25CF25CF25
...
...و اگر چنین است، طولانیترین کاراکتر حبابهایی که پیدا کردید احتمالاً طول رمز عبور شما را نشان میدهد، که اگر هیچ چیز دیگری نباشد، شکلی ساده از نشت اطلاعات رمز عبور خواهد بود.
ما از اسکریپت Lua زیر برای جستجوی نشانههایی از رشتههای جایبان رمز عبور باقیمانده استفاده کردیم:
خروجی شگفتانگیز بود (برای صرفهجویی در فضا، خطوط متوالی را با همان تعداد حباب یا با حبابهای کمتر از خط قبلی حذف کردهایم):
C:UsersduckKEYPASS> lua findblobs.lua kp2-post.dmp 000EFF3C: * [. . .] 00BE621B: ** 00BE64C7: *** [. . .] 00BE6E8F: **** [. . .] 00BE795F: ***** [. . .] 00BE84F7: ****** [. . .] 00BE8F37: ******* [به طور مشابه برای 8 حباب، 9 حباب، و غیره ادامه مییابد] [تا دو خط نهایی هر کدام دقیقاً 16 حباب] 00C0503B: ************* *** 00C05077: **************** 00C09337: * 00C09738: * [همه مسابقات باقیمانده یک لکه هستند] 0123B058: *
در آدرسهای حافظه نزدیک به هم، اما همیشه در حال افزایش، فهرستی سیستماتیک از 3 حباب، سپس 4 حباب، و به همین ترتیب تا 16 حباب (طول رمز عبور ما) پیدا کردیم، به دنبال آن تعداد زیادی نمونههای پراکنده تصادفی از رشتههای تک لکه .
بنابراین، به نظر میرسد که رشتههای «blob» مکاننماها در حافظه نشت میکنند و مدتها پس از اینکه نرمافزار KeePass با رمز اصلی شما به پایان رسید، طول رمز عبور را فاش میکنند.
گام بعدی
تصمیم گرفتیم بیشتر حفاری کنیم، درست مثل ودوهنی.
ما کد تطبیق الگوی خود را تغییر دادیم تا زنجیرهای از کاراکترهای لکهای را به دنبال هر نویسه ASCII در قالب ۱۶ بیتی شناسایی کنیم (نویسههای ASCII در UTF-16 بهعنوان کد اسکی ۸ بیتی معمولشان نشان داده میشوند و پس از آن یک بایت صفر نمایش داده میشود).
این بار، برای صرفه جویی در فضا، خروجی را برای هر مسابقه ای که دقیقاً با قبلی مطابقت دارد، حذف کرده ایم:
سورپرایز، تعجب:
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
ببینید چه چیزی از منطقه حافظه رشته مدیریت شده دات نت بدست می آوریم!
مجموعه ای نزدیک از "رشته های حباب" موقت که کاراکترهای متوالی رمز عبور ما را نشان می دهد و با کاراکتر دوم شروع می شود.
این رشتههای نشتکننده با منطبقهای تکنویسهای بهطور گسترده دنبال میشوند که تصور میکنیم بهطور تصادفی ایجاد شدهاند. (اندازه یک فایل dump KeePass حدود 250 مگابایت است، بنابراین فضای زیادی برای کاراکترهای "blob" وجود دارد که گویی از روی شانس ظاهر شوند.)
حتی اگر آن چهار تطابق اضافی را به جای نادیده گرفتن آنها به عنوان ناهماهنگی احتمالی در نظر بگیریم، می توانیم حدس بزنیم که رمز عبور اصلی یکی از موارد زیر است:
?IXTEENPASSCHARS ?NXTEENPASSCHARS ?WXTEENPASSCHARS ?SXTEENPASSCHARS
بدیهی است که این تکنیک ساده اولین کاراکتر را در رمز عبور پیدا نمی کند، زیرا اولین "رشته حباب" تنها پس از تایپ اولین کاراکتر ساخته می شود.
توجه داشته باشید که این لیست خوب و کوتاه است زیرا ما مواردی را که به نویسههای ASCII ختم نمیشدند فیلتر کردیم.
اگر به دنبال کاراکترهایی در محدوده متفاوتی مانند حروف چینی یا کره ای بودید، ممکن است با ضربات تصادفی بیشتری روبرو شوید، زیرا کاراکترهای احتمالی بسیار بیشتری برای مطابقت با آنها وجود دارد.
... اما ما گمان می کنیم که به هر حال به رمز عبور اصلی خود نزدیک خواهید شد و به نظر می رسد "رشته های لکه ای" که به رمز عبور مربوط می شوند در RAM با هم گروه بندی شده اند، احتمالاً به این دلیل که تقریباً در همان زمان توسط همان قسمت از رمز عبور اختصاص داده شده اند. زمان اجرا دات نت
و به طور خلاصه، در یک کلام طولانی و گفتمانی، داستان جذابی وجود دارد CVE-2023-32784.
چه کاری انجام دهید؟
- اگر کاربر KeePass هستید، نترسید. اگرچه این یک باگ است و از نظر فنی یک آسیبپذیری قابل بهرهبرداری است، مهاجمان راه دوری که میخواهند رمز عبور شما را با استفاده از این باگ بشکنند، ابتدا باید بدافزار را در رایانه شما نصب کنند. این به آنها راههای زیادی برای سرقت مستقیم رمزهای عبور شما میدهد، حتی اگر این باگ وجود نداشته باشد، برای مثال با ثبت ضربههای کلید شما هنگام تایپ. در این مرحله، میتوانید به سادگی مراقب بهروزرسانی آینده باشید و زمانی که آماده شد آن را بگیرید.
- اگر از رمزگذاری فول دیسک استفاده نمی کنید، آن را فعال کنید. برای استخراج گذرواژههای باقیمانده از فایل swap یا فایل خواب زمستانی (فایلهای دیسک سیستمعامل که برای ذخیره موقت محتویات حافظه در هنگام بارگذاری سنگین یا زمانی که رایانه شما در حالت خواب است) استفاده میشود)، مهاجمان نیاز به دسترسی مستقیم به هارد دیسک شما دارند. اگر BitLocker یا معادل آن را برای سایر سیستمعاملها فعال کرده باشید، آنها نمیتوانند به فایل مبادله، فایل خواب زمستانی یا هر داده شخصی دیگری مانند اسناد، صفحات گسترده، ایمیلهای ذخیرهشده و غیره دسترسی داشته باشند.
- اگر برنامه نویس هستید، خود را در مورد مسائل مربوط به مدیریت حافظه مطلع کنید. این را فقط به این دلیل فرض نکنید که هر
free()
مطابق با آن استmalloc()
که داده های شما ایمن و به خوبی مدیریت می شوند. گاهی اوقات، ممکن است لازم باشد اقدامات احتیاطی بیشتری برای جلوگیری از مخفی ماندن دادههای مخفی انجام دهید، و این اقدامات احتیاطی از سیستمعاملی به سیستمعامل دیگر انجام میشود. - اگر آزمایش کننده کیفیت کیفیت یا بازبینی کد هستید، همیشه به "پشت صحنه" فکر کنید. حتی اگر کد مدیریت حافظه مرتب و متعادل به نظر میرسد، از آنچه در پشت صحنه اتفاق میافتد آگاه باشید (زیرا برنامهنویس اصلی ممکن است این کار را نمیدانست)، و برای انجام کارهایی به سبک pentesting مانند نظارت بر زمان اجرا و حافظه آماده شوید. تخلیه برای تأیید اینکه کد ایمن واقعاً همانطور که قرار است رفتار می کند.
کد از مقاله: UNL1.C
#عبارتند از #عبارتند از #عبارتند از void hexdump(char* unsigned buff, int len) { // چاپ بافر در تکه های 16 بایتی برای (int i = 0; i < len+16; i = i+16) { printf("%016X: ",buff +i)؛ // نمایش 16 بایت به عنوان مقادیر هگز برای (int j = 0; j < 16; j = j+1) { printf("%02X ",buff[i+j]); } // آن 16 بایت را به عنوان کاراکتر برای (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) { // حافظه را برای ذخیره رمز عبور بدست آورید و نشان دهید که // چه چیزی در بافر زمانی که به طور رسمی "جدید" است... char* buff = malloc(128); printf("پاکسازی بافر "جدید" در شروع"); hexdump(buff,128); // از آدرس بافر شبه تصادفی به عنوان srand((unsigned)buff) استفاده کنید. // رمز عبور را با مقداری متن ثابت و قابل جستجو شروع کنید strcpy(buff,"unlikelytext"); // اضافه کردن 16 حرف شبه تصادفی، هر بار برای (int i = 1; i <= 16; i++) { // انتخاب یک حرف از A (65+0) به P (65+15) char ch = 65 + (rand() & 15); // سپس رشته buff را در جای خود تغییر دهید strncat(buff,&ch,1); } // رمز عبور کامل اکنون در حافظه است، پس // آن را به عنوان یک رشته چاپ کنید و کل بافر را نشان دهید... printf("رشته کامل بود: %sn",buff); hexdump(buff,128); // Pause to dump process RAM now (try: 'procdump -ma') puts("Waiting for [ENTER] to free buffer..."); getchar(); // به طور رسمی حافظه را آزاد کنید و بافر // را دوباره نشان دهید تا ببینید آیا چیزی پشت سر گذاشته شده است... free(buff); printf("Damping buffer after free()n"); hexdump(buff,128); // مکث کنید تا RAM دوباره تخلیه شود تا تفاوتها بررسی شود ("Waiting for [ENTER] to exit main(..."); getchar(); بازگشت 0; }
کد از مقاله: UNL2.C
#عبارتند از #عبارتند از #عبارتند از #عبارتند از void hexdump(char* unsigned buff, int len) { // چاپ بافر در تکه های 16 بایتی برای (int i = 0; i < len+16; i = i+16) { printf("%016X: ",buff +i)؛ // نمایش 16 بایت به عنوان مقادیر هگز برای (int j = 0; j < 16; j = j+1) { printf("%02X ",buff[i+j]); } // آن 16 بایت را به عنوان کاراکتر برای (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) { // حافظه برای ذخیره رمز عبور بدست آورید و نشان دهید که // چه چیزی در بافر است وقتی رسماً "جدید" است... char* buff = VirtualAlloc(0,128,MEM_COMMIT,PAGE_READWRITE); printf("پاکسازی بافر "جدید" در شروع"); hexdump(buff,128); // از آدرس بافر شبه تصادفی به عنوان srand((unsigned)buff) استفاده کنید. // رمز عبور را با مقداری متن ثابت و قابل جستجو شروع کنید strcpy(buff,"unlikelytext"); // اضافه کردن 16 حرف شبه تصادفی، هر بار برای (int i = 1; i <= 16; i++) { // انتخاب یک حرف از A (65+0) به P (65+15) char ch = 65 + (rand() & 15); // سپس رشته buff را در جای خود تغییر دهید strncat(buff,&ch,1); } // رمز عبور کامل اکنون در حافظه است، پس // آن را به عنوان یک رشته چاپ کنید و کل بافر را نشان دهید... printf("رشته کامل بود: %sn",buff); hexdump(buff,128); // Pause to dump process RAM now (try: 'procdump -ma') puts("Waiting for [ENTER] to free buffer..."); getchar(); // رسماً حافظه را آزاد کنید و بافر // را دوباره نشان دهید تا ببینید آیا چیزی پشت سر گذاشته شده است... VirtualFree(buff,0,MEM_RELEASE); printf("Damping buffer after free()n"); hexdump(buff,128); // مکث کنید تا RAM دوباره تخلیه شود تا تفاوتها بررسی شود ("Waiting for [ENTER] to exit main(..."); getchar(); بازگشت 0; }
کد از مقاله: S1.LUA
-- با چند متن ثابت و قابل جستجو شروع کنید s = 'unlikelytext' -- اضافه کردن 16 کاراکتر تصادفی از 'A' به 'P' برای i = 1,16 do s = s .. string.char(65+math.random( 0,15)) end print('Full string is:',s,'n') -- مکث برای تخلیه پردازش RAM print('در انتظار [ENTER] قبل از آزاد کردن رشته...') io.read() - - رشته را پاک کنید و متغیر unused s = nil را علامت گذاری کنید - RAM را دوباره تخلیه کنید تا به دنبال diffs print بگردید ('در انتظار [ENTER] قبل از خروج...') io.read()
کد از مقاله: FINDIT.LUA
-- خواندن در فایل dump local f = io.open(arg[1],'rb'):read('*a') -- به دنبال متن نشانگر به دنبال یک -- یا چند کاراکتر تصادفی ASCII محلی b,e باشید ,m = 0,0,nil در حالی که true do -- به دنبال تطابق بعدی بگردید و offset b,e,m = f:find('(unlikelytext[AZ]+)',e+1) را به خاطر بسپارید - زمانی که دیگر خبری از آن نیست خارج شوید اگر b نباشد، پایان شکسته منطبق است - موقعیت گزارش و رشته یافت شده چاپ (string.format('%08X: %s',b,m)) پایان
کد از مقاله: SEARCHKNOWN.LUA
io.write('Reading in dump file...') local f = io.open(arg[1],'rb'):read('*a') io.write('DONE.n') io. write('جستجوی SIXTEENPASSCHARS به عنوان ASCII 8 بیتی...') local p08 = f:find('SIXTEENPASSCHARS') io.write(p08 and 'FOUND' یا 'نیافتم'،'.n') io.write ('جستجوی SIXTEENPASSCHARS به عنوان UTF-16...') محلی p16 = f:find('Sx00Ix00Xx00Tx00Ex00Ex00Nx00Px00'.. 'Ax00Sx00Sx00Cx00Hx00Ax00 و 'Ax00Sx00Sx16CxXNUMXHxXNUMXAxXNUMX' یا "یافت نشد"،'.n')
کد از مقاله: FINDBLOBS.LUA
-- خواندن در فایل dump مشخص شده در خط فرمان local f = io.open(arg[1],'rb'):read('*a') -- به دنبال یک یا چند حباب رمز عبور بگردید و به دنبال آن هر غیر blob بگردید -- توجه داشته باشید که کاراکترهای حباب (●) در نویسه های عریض ویندوز -- به عنوان کدهای UTF-16 ساده که به صورت هگز CF 25 منتشر می شوند، رمزگذاری می شوند. محلی b,e,m = 0,0,nil در حالی که true do -- ما یک یا چند حباب و به دنبال آن هر غیر لکه می خواهیم. -- ما کد را با جستجوی یک CF25 صریح ساده می کنیم -- به دنبال هر رشته ای که فقط CF یا 25 در آن وجود دارد -- بنابراین CF25CFCF یا CF2525CF و همچنین CF25CF25 را خواهیم یافت. -- ما "اثبات نادرست" را بعداً در صورت وجود فیلتر خواهیم کرد. -- ما باید به جای x25 '%%' بنویسیم زیرا کاراکتر x25 -- (علامت درصد) یک کاراکتر جستجوی ویژه در Lua است! b,e,m = f:find('(xCF%%[xCF%%]*)',e+1) -- زمانی که دیگر منطبق نباشد از آن خارج شوید اگر b نبود پس پایان شکست -- CMD.EXE نمی تواند چاپ کند حباب ها، بنابراین آنها را به ستاره تبدیل می کنیم. print(string.format('%08X: %s',b,m:gsub('xCF%%','*'))) پایان
کد از مقاله: SEARCHKP.LUA
-- خواندن در فایل dump مشخص شده در خط فرمان local f = io.open(arg[1],'rb'): read('*a') محلی b,e,m,p = 0,0,nil,nil در حالی که true do -- اکنون، یک یا چند حباب (CF25) به دنبال کد را می خواهیم -- برای A..Z و سپس یک بایت 0 برای تبدیل ACSCII به UTF-16 b,e,m = f:find(' (xCF%%[xCF%%]*[AZ])x00',e+1) -- زمانی که دیگر منطبق نباشد از آن خارج شوید اگر b نبود پس پایان شکست -- CMD.EXE نمی تواند حباب ها را چاپ کند، بنابراین آنها را به ستاره ها. -- برای صرفه جویی در فضا، اگر m ~= p تطبیق های متوالی را متوقف کنیم، سپس print(string.format('%08X: %s',b,m:gsub('xCF%%','*'))) p = m پایان پایان
- محتوای مبتنی بر SEO و توزیع روابط عمومی. امروز تقویت شوید.
- PlatoAiStream. Web3 Data Intelligence دانش تقویت شده دسترسی به اینجا.
- ضرب کردن آینده با آدرین اشلی. دسترسی به اینجا.
- خرید و فروش سهام در شرکت های PRE-IPO با PREIPO®. دسترسی به اینجا.
- منبع: https://nakedsecurity.sophos.com/2023/05/31/serious-security-that-keepass-master-password-crack-and-what-we-can-learn-from-it/
- : دارد
- :است
- :نه
- :جایی که
- ][پ
- $UP
- 1
- 10
- 12
- ٪۱۰۰
- 20
- 200
- 2023
- 24
- 250
- 27
- 31
- 3d
- 49
- 50
- 67
- 70
- 72
- 77
- 8
- 9
- a
- قادر
- درباره ما
- بالاتر
- مطلق
- AC
- دسترسی
- حساب
- به دست آوردن
- کسب
- فعال
- واقعی
- واقعا
- اضافه
- اضافی
- نشانی
- آدرس
- می افزاید:
- پس از
- بعد از آن
- از نو
- معرفی
- اختصاص داده شده است
- تخصیص می دهد
- تخصیص
- تخصیص ها
- اجازه دادن
- تنها
- در امتداد
- قبلا
- همچنین
- تغییر
- هر چند
- همیشه
- an
- و
- اندرو
- پاسخ
- هر
- هر چیزی
- هر چیزی حیاتی
- ظاهر شدن
- به نظر می رسد
- روش
- تایید کرد
- هستند
- دور و بر
- مقاله
- مقالات
- AS
- At
- نویسنده
- خودکار
- اتوماتیک
- بطور خودکار
- در دسترس
- اجتناب از
- اجتناب کنید
- مطلع
- دور
- به عقب
- زمینه
- تصویر پس زمینه
- BE
- زیرا
- شود
- بوده
- قبل از
- شروع
- پشت سر
- پشت صحنه
- در زیر
- بهتر
- بیت
- مسدود کردن
- بلاک ها
- مرز
- هر دو
- پایین
- نام تجاری
- نام تجاری جدید
- شکستن
- بطور خلاصه
- به ارمغان بیاورد
- بافر
- سرریز بافر
- اشکال
- اشکالات
- ساختن
- اما
- by
- ++C
- صدا
- فراخوانی
- CAN
- می توانید دریافت کنید
- اهميت دادن
- مورد
- گرفتار
- CD
- مرکز
- قطعا
- زنجیر
- شانس
- تغییر
- شخصیت
- کاراکتر
- بررسی
- چک
- چینی
- را انتخاب کنید
- واضح
- به وضوح
- نزدیک
- رمز
- رنگ
- COM
- می آید
- آینده
- توضیح
- نظرات
- مشترک
- کامل
- پیچیده
- کامپیوتر
- در نظر بگیرید
- قابل توجه
- در نظر گرفته
- شامل
- ساخت
- محتوا
- محتویات
- به طور مستمر
- ادامه
- کنترل
- تبدیل
- حق چاپ
- متناظر
- میتوانست
- پوشش
- ترک
- ایجاد
- ایجاد شده
- خالق
- بحرانی
- امنیت سایبری
- خطر
- خطرناک
- داده ها
- نشت اطلاعات
- روز
- مقدار
- مصمم
- اختصاصی
- شرح داده شده
- DID
- تفاوت
- مختلف
- مشکل
- DIG
- دیجیتال
- مستقیم
- دسترسی مستقیم
- مستقیما
- نمایش دادن
- نمایش
- است
- do
- اسناد و مدارک
- میکند
- نمی کند
- عمل
- انجام شده
- آیا
- پایین
- راندن
- دو
- موادی که موقتا برای استعمال انبار میشود
- در طی
- e
- هر
- پیش از آن
- به آسانی
- اکوسیستم
- هر دو
- دیگر
- ایمیل
- را قادر می سازد
- دلگرم کننده
- رمزگذاری
- پایان
- به پایان می رسد
- کافی
- اطمینان حاصل شود
- حصول اطمینان از
- وارد
- وارد شدن
- تمام
- ورود
- محیط
- معادل
- خطا
- اساسا
- برآورد
- و غیره
- اتر (ETH)
- حتی
- در نهایت
- روزافزون
- هر
- همه چیز
- کاملا
- مثال
- جز
- هیجان
- اعدام
- وجود داشته باشد
- موجود
- خروج
- خارج شدن
- انتظار
- توضیح دهید
- بهره برداری
- قرار گرفتن در معرض
- گسترش
- اضافی
- عصاره
- واقعیت
- غلط
- شگفت انگیز
- امکانات
- باز خورد
- کمتر
- مبارزه با
- پرونده
- فایل ها
- فیلتر
- نهایی
- سرانجام
- پیدا کردن
- پیدا کردن
- پیدا می کند
- پایان
- نام خانوادگی
- ثابت
- تمرکز
- به دنبال
- پیروی
- برای
- فرم
- رسما
- قالب
- آینده
- یافت
- چهار
- رایگان
- از جانب
- کامل
- کاملا
- تابع
- توابع
- بیشتر
- آینده
- تولید می کند
- نسل
- دریافت کنید
- گرفتن
- GitHub
- دادن
- داده
- می دهد
- دادن
- Go
- می رود
- رفتن
- خوب
- دولت
- گرفتن
- بزرگ
- ضمانت
- بود
- دستگیره
- اتفاق افتاده است
- اتفاق می افتد
- اتفاق می افتد
- سخت
- آیا
- داشتن
- سردرد
- سنگین
- ارتفاع
- اینجا کلیک نمایید
- HEX
- در سطح بالا
- بالاتر
- بازدید
- نگه داشتن
- سوراخ
- امید
- ساعت ها
- در تردید بودن
- چگونه
- چگونه
- HTTPS
- شکار
- i
- شناسه
- if
- بلافاصله
- مهم
- in
- شامل
- از جمله
- اطلاعات
- اطلاع
- در عوض
- علاقه مند
- حد واسط
- اینترنت
- به
- مسائل
- IT
- ITS
- خود
- اصطلاحات مخصوص یک صنف
- ژوئن
- تنها
- فقط یکی
- نگاه داشتن
- کلید
- دانستن
- شناخته شده
- کره ای
- زبان
- زبان ها
- لپ تاپ
- نام
- بعد
- رهبری
- منجر می شود
- نشت
- نشت
- یاد گرفتن
- یادگیری
- کمترین
- ترک
- ترک کرد
- طول
- نامه
- کتابخانه
- زندگی
- پسندیدن
- احتمالا
- محدود شده
- لاین
- خطوط
- فهرست
- ذکر شده
- ll
- بار
- محلی
- محل
- ورود به سیستم
- طولانی
- دراز مدت
- دیگر
- نگاه کنيد
- شبیه
- نگاه
- به دنبال
- مطالب
- خیلی
- شانس
- حفظ
- ساخت
- نرم افزارهای مخرب
- مدیریت
- اداره می شود
- مدیریت
- مدیر
- مدیریت می کند
- دست کاری
- بسیاری
- حاشیه
- علامت
- نشانگر
- استاد
- مسابقه
- مطابق
- حداکثر عرض
- ممکن است..
- به معنی
- حافظه
- ذکر شده
- مایکروسافت
- قدرت
- دقیقه
- فروتن
- اصلاح شده
- تغییر
- لحظه
- نظارت بر
- بیش
- اکثر
- بسیار
- چندگانه
- مرتب
- نیاز
- ضروری
- خالص
- هرگز
- با این اوصاف
- جدید
- اخبار
- بعد
- خوب
- نه
- طبیعی
- هیچ چی
- اطلاع..
- اکنون
- عدد
- تعداد
- هدف
- واضح
- of
- خاموش
- رسمی
- رسما
- چاپ افست
- قدیمی
- on
- یک بار
- ONE
- فقط
- منبع باز
- عملیاتی
- سیستم عامل
- سیستم های عامل
- اپراتور
- گزینه
- or
- سفارش
- اصلی
- دیگر
- دیگران
- در غیر این صورت
- ما
- خودمان
- خارج
- تولید
- روی
- به طور کلی
- خود
- با ما
- هراس
- بخش
- کلمه عبور
- مدیر رمز عبور
- کلمه عبور
- مسیر
- الگو
- پل
- توقف
- پرداخت
- در صد
- شاید
- دوره
- به طور دائم
- شخصی
- اطلاعات شخصی
- فیزیکی
- تصویر
- قطعات
- محل
- حفره یا سوراخ
- طاعون
- افلاطون
- هوش داده افلاطون
- PlatoData
- بسیاری
- نقطه
- نقطه
- محبوب
- موقعیت
- ممکن
- پست ها
- پتانسیل
- دقیقا
- در حال حاضر
- زیبا
- جلوگیری از
- قبلی
- قیمت
- چاپ
- چاپ
- شاید
- مشکلات
- روند
- برنامه
- برنامهنویس
- برنامه نویسان
- برنامه نويسي
- برنامه ها
- تلفظ شده
- قرار دادن
- پــایتــون
- پرسش و پاسخ
- سوال
- افزایش
- رم
- تصادفی
- محدوده
- نسبتا
- خام
- داده های خام
- RE
- رسیده
- خواندن
- مطالعه
- اماده
- واقعی
- زندگی واقعی
- زمان واقعی
- واقعا
- تشخیص
- بهبود یافتن
- بازیابی
- مربوط
- باقی مانده
- به یاد داشته باشید
- دور
- برداشتن
- تکرار
- مکرر
- به طور مکرر
- گزارش
- نمایندگی
- احترام
- به ترتیب
- REST
- نتایج
- برگشت
- عودت
- فاش کردن
- خلاص شدن از شر
- راست
- خطر
- خطرات
- اتاق
- دویدن
- در حال اجرا
- نظارت بر زمان اجرا
- s
- امن
- امن تر
- همان
- راضی
- ذخیره
- گفته
- اسکن
- پراکنده
- صحنه های
- جستجو
- جستجو
- دوم
- ثانیه
- راز
- بخش
- امن
- تیم امنیت لاتاری
- دیدن
- دانه
- مشاهده
- به نظر می رسد
- مشاهده گردید
- می بیند
- سلسله
- جدی
- تنظیم
- محیط
- کوتاه
- به زودی
- باید
- نشان
- نشان داده شده
- امضاء
- نشانه ها
- مشابه
- به طور مشابه
- ساده
- ساده شده
- ساده کردن
- به سادگی
- تنها
- اندازه
- خواب
- کوچک
- اب زیر کاه
- snooping را
- So
- نرم افزار
- جامد
- برخی از
- چیزی
- بزودی
- منبع
- کد منبع
- فضا
- ویژه
- مخصوصاً
- مشخص شده
- سرعت
- ستاره
- شروع
- آغاز شده
- راه افتادن
- شروع می شود
- شروع
- هنوز
- دزدیده شد
- توقف
- متوقف شد
- opbevare
- ذخیره شده
- داستان
- رشته
- قوی
- مهاجرت تحصیلی
- موفقیت
- چنین
- کافی
- مفروض
- تعجب
- غافلگیر شدن
- تعجب آور
- زنده ماندن
- SVG
- مبادله
- سیستم
- سیستم های
- گرفتن
- صورت گرفته
- طول می کشد
- مصرف
- سخنگو
- techniquement
- تکنیک
- موقت
- آزمون
- تست
- نسبت به
- که
- La
- منبع
- شان
- آنها
- خودشان
- سپس
- نظریه
- آنجا.
- از این رو
- آنها
- چیز
- فکر می کنم
- این
- کسانی که
- اگر چه؟
- فکر
- زمان
- عنوان
- به
- با هم
- در زمان
- ابزار
- بالا
- مسیر
- پیگردی
- انتقال
- شفاف
- درست
- امتحان
- تبدیل
- دو
- نوع
- به طور معمول
- فهمیدن
- یونیکد
- تا
- استفاده نشده
- ناخواسته
- بروزرسانی
- به روز شده
- URL
- us
- دولت ایالات متحده
- استفاده
- USB
- استفاده کنید
- پس از استفاده رایگان
- استفاده
- کاربر
- استفاده
- با استفاده از
- سودمندی
- ارزش
- ارزشها
- تنوع
- بررسی
- نسخه
- بسیار
- از طريق
- آسیب پذیری
- W
- صبر کنيد
- منتظر
- می خواهم
- خواسته
- بود
- تماشا کردن
- مسیر..
- راه
- we
- هفته
- خوب
- بود
- چی
- چه زمانی
- چه
- که
- در حین
- WHO
- هرکس
- تمام
- چرا
- اراده
- پیروزی
- پنجره
- پاک کردن
- با
- بدون
- تعجب کردم
- کلمات
- مهاجرت کاری
- مشغول به کار
- کارگر
- با این نسخهها کار
- نگرانی
- خواهد بود
- می داد
- نوشتن
- نوشته
- کتبی
- هنوز
- شما
- شما
- خودت
- زفیرنت
- صفر