اتفاقای جالب اغلب بدون برنامهریزی میان !
خب بازم سلام. طبق برنامه ای که داشتیم و فصل اول کتاب Evading EDR رو داشتیم حالا تو این پست قراره بریم سراغ فصل دوم.
خیلی به این فکر میکردم که چطور میشه این فصل رو ارائه داد که حوصله سربر نباشه ! چه برای خودم چه برای شمایی که میخونید !
و نتیجه ؟ امروز وقتی که داشتم با vpn به شبکه محل کارم وصل میشدم به شکل مریضی فکر کردم که چی میشه اگه این یوزرنیم پسورده رو یکی بین راه برداره؟
صادقانه بگم برای منی که درس امنیت شبکه رو ۱۳ شده بودم این سوال مناسبی نبود پس تصمیم گرفتم سوال رو عوض بکنم و قبل اینکه بره تو اون شبکه خراب شده کارو در بیارم.
سوال بعدی این بود که تو سطح سیستم عامل چطو میشه یکی یوزرنیم پسورد منو بدزده ؟!

از Dll SideLoading تاا API Hooking
خب قراره چه بکنیم ؟ کتاب تو این فصل راجع به هوک کردن توابع صحبت میکنه. پس ماهم میخوایم یک فانکشن رو هوک کنیم تا بفهمیم EDR ها چطور میکنن .
باور کردین؟ :))
بیخیال اقا. ما قراره بدونیم ملور ها چطور کار میکنن و چطور میتونن با Hook کردن توابع کارای قشنگ بکنن ! چیزی که انتخاب کردم اینه که برای نرم افزار Fortinet Client که یک vpn سازمانی هست یه چیزی بنویسیم که بتونه یوزرنیم پسورد آدمارو بخونه . ( و بفرسته به یک سرور؟! نه بابا مگه ما کاربر مخربیم؟!! )
بعد این وسط احتمالا یکی میگفت هااا. تو به فضای پراسس دسترسی داشتی منم داشتم میتونستم . ( این آدم رو مخارو دیدین؟ ) پس تصمیم گرفتم که با ایده ی ساید لودینگ ترکیبش کنم ! اگه نمیدونید DLL SideLoading چیه برید پست مرتبط بهش رو بخونید چون یک بار توضیحش دادم .
پس اول DLL sideLoad میکنیم روی فورتی و بعدش با هوک کردن فانکشن یوزرنیم پسورد رو میکشیم بیرون. آیا شدنیه؟ بخدا اگه بدونم !
الان که دارم مینویسمش قراره لایو خودمم تست کنم ببینیم چی میشه .
حالا بیشوخی هوک کردن یعنی چی ؟

هوک کردن عین فضولی کردن تو API ها میمونه . یک چیزی مثل Man In the Middle توی شبکه !
فرض کنید شما تو برنامتون CreateFile("Hello.txt", "Cabbage") رو صدا کردین . یعنی میخواستین که فایل Hello.txt رو بسازین و محتوای توش هم کبج باشه .
حالا فرض کنید یکی اومده باشه این api رو هوک کرده باشه ! یعنی چی ؟ یعنی اینکه قبل از اینکه بذاره عملیات CreateFile شروع بشه اومده داخلش نوشته jmp &HookedFunction !
پس برنامه شما قبل از اینکه بخواد کارای این api رو بکنه میره تو آدرس HookedFunction و اونجا اون فرد EDR / Malware میتونه همه چیو ببینه ! از جمله آرگمان هایی که دادید. مثلا اسم فایل یا محتوای فایل ! جدای از اون میتونه یه سری دستورات اضافه بنویسه که ران بشه و هر وقتی که دیگه کاری نداشت Return بکنه به CreateFile اصلی !
بازم این ایده پروکسی رو شما میبینید ! اگه برنگرده به api اصلی عملکرد سیستم عامل مختل میشه . این ایده رو توی COM Hijack و DLL Hijack هم دیده بودیم.
حالا EDR ها چرا ازش استفاده میکنن؟
میدونیم که EDR ها یک بخش مهمشون سنسور ها بودن که کارشون جمع آوری Telemetry ها بودش . یکی از تلمتری های مهم اینه که ما بدونیم فانکشن های حساس مثلا VirtualAlloc() یا VirtualProtect بدونیم که پراسس ها چه فضای آدرسی رو به خودشون اختصاص میدن؟ چه ورودی هایی میدن؟ چه پیج هایی از working set رو به چه چیزی تغییر میدن ؟ آیا اینکارا حساسه یا نشونه حمله ی خاصیه؟
اینکه EDR ها واقعا به چه شکلی DLL هاشون رو به پراسس ها تزریق میکنن در فصل های جلوتر کتاب بررسی میشه ولی بایدیفالت میدونید که اگه بتونید از پراسسی هندل بگیرید امکان تزریق DLL توش دارید. ( امکان کارای قشنگ تری دارید البته ) .
مرحله ی اول . پیدا کردن DLL مناسب واسه سایدلود
میدونم که قرار نبود اینجارو توضیح بدم ولی خب لایو دارم میرم اینجام مینویسم .
خب طبق چیزایی که از Side Load میدونیم اول با فیلترهای زیر میریم و پیدا میکنیم که کدوما Not Found میشن . دلیل پشتشم حتما بفهمید با همون بلاگ پست SideLoad وگرنه ادامه آموزش حرام هست.
همونطور که میبینید Secur32.dll بنظر تارگت خوبی میاد . نه؟

خب در مرحله بعدی با استفاده از همون DLL Export Viewer خروجی واسه لینکر میگیریم و میچسبونیم تنگ کدمون. بازم میگم اگه این بخش رو نمیفهمید من مفصل تو پست خودش توضیح دادم.
من صرفا یه مسیج باکس فعلا اضافه کردم تا ببینیم موفقیت آمیز بوده یا خیر . و نتیجه ... ( به بک گراند توجه بشه )

الان کجاییم و داریم چیکار میکنیم ؟
سوال خوبیه. الان ما یک DLL داریم که هر بار فورتی لود بشه در فضای آدرس فورتی بالا میاد و مهم تر اینکه کنترلش در اختیار ماست و میتونیم کارای کثیف توش انجام بدیم.
مرحله ی بعدی چیه ؟ ما میخوایم هر دفعه کاربر یوزرنیم پسورد رو میزنه بفهمیم در چه تابعی قرار میگیره تا بتونیم اون تابع رو هوک کنیم و بیگیریمش واسه خودمون.
بنظرتون برای این نیاز باید سراغ چی بریم ؟ Windbg ؟ هعیهعی نه بابا مگه عقلمون کمه .
این شما و این ستاره ای به نام API Monitor !

این نرمافزار خیلی باحالیه که میتونید باهاش api های مختلفی رو که هر نرم افزار کال میکنه ببینید و ذخیره کنید و از اون مهم تر ! تو api هاش سرچ کنید !
من فورتی رو آوردم بالا و از میون پراسس ها انتخابش کردم و بعد یک یوزرنیم پسورد سکرت میزنیم تا کاراشو بکنه و بتونیم مقادیرش ر بدزدیم !
Username : Cabbage
Password:VerySecret@123

اونجا که با قرمز نشون دادم میتونیم پراسسی که میخوایم مانیتور کنیم اضافه کردم و توی فورتی لاگین رو زدم و دنبال یوزرنیم خودم گشتم .
میبینید مثلا یوزر نیم من توی API که اسمش RtlUTF8ToUnicodeN هست از Kernel32.dll لود شده البته میدونیم این rtl ها همشون از ntdll اکسپورت میشن . به عنوان پارامتر چندم ؟ پارامتر چهارم !
برای یول هایی که Calling Convention نمیدونن میدونیم چهارتا متغیر تابع توی رجیستر های rcx, rdx, r8, r9 نگهداری میشه . پس منطقا اگه ما بریم تو windbg و یک برک پوینت روی تابع RtlUTF8ToUnicodeN بذاریم و رجیستر r9 رو ببینیم باید چی ببینیم ؟
بلند بگید چیی؟ کبججج.
حالا مهمتر ! پسورد کجایه ؟! بیاید پسورد رو سرچ کنیم تا ببینیم در کدوم تابع میتونیم پیداش کنیم .

تو چندتا جا میتونید پسورد رو ببینید ولی نکته جالب اینه که هم اسم دامین هم یوزرنیم و هم پسورد توی این تابع RtlUTF8ToUnicodeN کال میشن ! یعنی من خودمم همین الان دارم میبینم :))
پس کارمونو برامون ساده کرد ! یک فانکشن هوک میکنید و هر سه تارو میکشید بیرون :))
بریم تو winDbg
شاید بگید دلیل اینکار چیه ؟ نمیدونم باحاله .
بریم ببینیم که این راست میگه. اگه از winDbg بدتون میاد میتونید استوری رو بکشید بالا.
اینجا هم کار ساده ست میایم روی این تابع که پیدا کردیم و یه برک پوینت میندازیم روش :
bu ntdll!RtlUTF8ToUnicodeN
حالا یوزرنیم پسورد رو میزنم تا روش برک کنه و بعدش کافیه محتوای رجیستر r9 رو ببینید اونم به شکل اسکی ! ( اگه نمیدونید چرا r9 رو باید ببینید یعنی Calling Convention هارو خوب نفهمیدید متاسفانه هیچ شعبه ای از کبج در این باره پستی نداشته و باید سرچ کنید )

تابع اینقده پر برکت دیده بودید ؟ هی g بزنید r9 رو چک کنید یه سوپرایز براتون داره :))
حله ! دیگه بریم دست به کد شیم !
بریم سراغ نوشتن هوکر ! نوشتنش بله !

خب پس برنامه چیه ؟! تو DLL قبلی که Proxy کرده بودیم بیایم و api عه RtlUTF8ToUnicodeN رو هوک کنیم تا پارامتر ۴ امش رو ذخیره بکنه !
بسم الله ! بریم سراغ پروسه هوک !
مرحله ی اول لود کردن DLL ایی هست که میخوایم و پیدا کردن آدرس اون api به خصوص تا بتونیم گند بزنیم بهش ! چطور ؟
if (reason==DLL_PROCESS_ATTACH)
{
HMODULE ntdll = GetModuleHandleW(L"ntdll.dll");
FARPROC RTL_Address = GetProcAddress(ntdll, "RtlUTF8ToUnicodeN");
}مرحله ی بعدی چیه؟ ما میخوایم به اول این تابع RtlUTF8ToUnicodeN دوتا اسمبلی اضافه بکنیم تا قبل از انجام هر کاری جامپ کنه به تابع خود ما ! یعنی دوتا اسمبلی باید بشینه رو اول instruction های این تابع . اونا به ترتیب میشن چیا؟
اگه تو 32bit بودید خیلی راحت میتونستید جامپ کنید به آدرس خودتون یعنی دوتا دستور
push &hooked_address
ret
ولی چون تو ۶۴ بیتی هستیم باید اینکارو با رجیستر انجام بدیم . ( آدرسا تو ۶۴ بیت به شکل مستقیم یا باید ۳۲ بیتی باشن یا باید رجسیتری باشن )
پس اول آدرس تابعی که میخوایم هوک کننده باشه رو میذاریم تو یک رجیستر و بعد جامپ میکنیم به اون رجیستر !
یعنی
mov rax, address
jmp rax
.
اگه این دوتا اینستراکشن رو اسمبلی بکنیم میرسیم به مقدار زیر :
BYTE new_state_buffer[12] = { 0x48, 0xB8, 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0x90 , 0xFF , 0xE0 };تو دوتای اولی دارید mov رو میبینید و دوتای آخری هم جامپ رو . از ۲ تا ۱۰ هم 0x90 هست که یعنی خالیه و آماده اینه که اون آدرس تابع هوک کننده رو بذاریم جاش !
چه جوری ؟! تو C اگه با فانکشن پوینتر ها آشنا باشید میدونید که با & میتونید آدرس فانکشنه رو در بیارید که اصلا مفهوم callback هم از همین میاد . کافیه آدرسش رو بگیرید و از خونه ۲ تا ۱۰ رو آدرس این بابا قرار بدید .
void* hooked_address = &RtlUTF8ToUnicodeNHooked;
memcpy_s(new_state_buffer + 2, 8, &hooked_address, 8);خب حالا new_state_buffer ما آماده ست تا بتونه بشینه اول تابع و کاراشو انجام بده ولی قبل اینکه اونجا بنویسیمش یه نکته ای داره !
بعد اینکه این اینجکت رو انجام دادید چطور میخواید تابع رو به عملکرد نرمال خودش برگردونید؟ کد ما همینجوریم کار میکنه ولی عملکرد برنامه مختل میشه چون ما هوک کردیم ولی کارکرد تابع اصلی رو بهش برنگردوندیم ! اینجا شما دوتا راه دارید یکی بهش میگن ترامپولینگ که تو این پست نمیگم و یکی راه دوم که الان میگم.
ساده ست ! بیاید استیت فعلی(سالم) فانکشن رو ذخیره کنین . بفرستیدش تو تابع هوک خودتون هر بلایی خواستین سرش بیارید و نهایتا مقدار استیت اصلی رو جایگزین کنید و دوباره فانکشن سالم رو کال کنید ! همه این وسط خوشحالن دیگه نه؟
فهمیدید یا یول بازی در میارید؟ بیاید انجام بدیم ببینید . با api عه ReadProcessMemory میتونید استیت فعلی رو ذخیره کنید. چندتای اولشو میخواید ؟ به همون اندازه که میخواید اینجکت کنید . ( بایت های دستور mov, ret ) یعنی ۱۲ تا !
ReadProcessMemory(GetCurrentProcess(), RTL_Address, original_state_buffer, sizeof(original_state_buffer), NULL);پارامتراشو دیدید ؟ از پراسس فعلی (۱) که آدرسش از بیس اونRtlUTF8ToUnicodeN شروع میشه بخون و بریز توی original state buffer ! ایزی؟
دیگه خیالمون راحته ! میتونیم بریم و ۱۲ بایت اینجکتی خودمون رو بنویسم روی پراسس !
WriteProcessMemory(GetCurrentProcess(), RTL_Address, new_state_buffer, 12, NULL);دیگه کار تمانع ! گرگی از مازندران ...
دیگه فقط میمونه نوشتن تابع هوک شده و انجام هر کاری که دلتون میخواد ! مثلا من محتوای کانتنت هوک شده رو میریزم روی users\\public . این پوشه ی مظلوم گوناهی :))
بعدش چی؟ همونطور که گفتم ما از روش ترامپولینگ اینجا استفاده نمیکنیم. پس باید خود تابع اصلی رو هم کال کنیم تا برنامه نپوکه ! پس دوباره با WriteProcessMemory میایم تابعو به شکل اولش در میاریم و صداش میکنیم تا بره کاراشو بکنه !
بعدش ؟! اگه اینجا ولش کنیم تابع ما فقط یک بار هوک میشه قبول دارید ؟ :)) چون آخر داریم فیکسش میکنیم دیگه ! چاره چیه؟ اینکه تهش دوباره بیایم ساختار خودمون رو بذاریم اول تابع تا دوباره همین منوال اجرا بشه .
NTSTATUS NTAPI RtlUTF8ToUnicodeNHooked(
_Out_opt_ PWSTR UnicodeStringDestination,
_In_ ULONG UnicodeStringMaxByteCount,
_Out_ PULONG UnicodeStringActualByteCount,
_In_ PCCH UTF8StringSource,
_In_ ULONG UTF8StringByteCount
)
{
if (ShouldLogString(UTF8StringSource, UTF8StringByteCount)) {
FILE* myfile = nullptr;
if (_wfopen_s(&myfile, L"C:\\Users\\Public\\info.txt", L"a+") == 0 && myfile) {
fwrite(UTF8StringSource, 1, UTF8StringByteCount, myfile);
fwrite("\n", 1, 1, myfile);
fclose(myfile);
}
}
WriteProcessMemory(GetCurrentProcess(), RTL_Address, original_state_buffer, 12, NULL);
NTSTATUS result = RTL_Address(
UnicodeStringDestination,
UnicodeStringMaxByteCount,
UnicodeStringActualByteCount,
UTF8StringSource,
UTF8StringByteCount
);
WriteProcessMemory(GetCurrentProcess(), RTL_Address, new_state_buffer, 12, NULL);
return result;
}اون تابع ShouldLogString رو هم فعلا کاری نداشته باشید میگم که چرا نوشتمش ! حالا dll ر کامپایل میکنیم و میندازیم روی دایرکتوری که Side Load میشد یعنی دایرکتوری خود فورتی.
و میاریمش بالا و تو دامین Cabbage با یوزرنیم Cabbage-sec و پسورد $alam12345 لاگین میبینیم و رخ کار :

میبینید که تونستیم دیتارو از این api بکشیم بیرون با استفاده از مکانیزم هوک کردن !
حالا بیاید چندتا چیز رو بررسی کنیم ! نکته اول ! اگه شما هرآنچیزی که api آقای RtlUTF8ToUnicodeN تولید میکنه رو لاگ کنید در هر بار لاگین کاربر نزدیک به ۷۰۰ کیلوبایت لاگ تولید میشه ! حالا میون این ۷۰۰ کیلوبایت چطو بفهمید یوزرنیم پسورد چیه؟!
پس یک تجربه ی مهم ! بهتره که توی هوک کردن تابعی رو انتخاب کنید که کمترین فرکانس تکرار رو داره ! اگه تابعی مثل RtlUTF8ToUnicodeN که اینقدر عمومی هست رو انتخاب میکنید یک پروسه ی پاکسازی داده هم دارید ! بذارید مثال بزنم !
مثلا من با نوشتن یک تابع ShouldLogString که طول و محتوا رو بررسی میکنه مشخص کردم که چه داده هایی لاگ نشن ! مثلا داده بیشتر از ۴۰ کرکتر خیلی بعیده یوزرنیم یا پسورد باشه ! ( اگه بود نوش جونش هک نشه :)) با این تابع در هر بار لاگین به ۳۴ کیلو بایت رسوندم. قطعا میتونید با دیدن پترن های چیزای لاگ شده این حجم رو خیلی کمتر هم بکنید !
و به قول امیرپارسا .. و ناگهان نوبت تو میشود ( دستبند را سفت میکند )

جا داره در انتها هم بگم من یک دانشجوی علاقهمند به سیستم عامل هستم و مسئولیت هرگونه استفاده خوب بد زشت بر عهده کاربر خواننده هست. البته همه اینا بخشی از کتاب edr هست و همه میتونن بخونن.
سورس کد رو میتونید اینجا ببیینید :
READER SIGNALS
Comments 00
Useful additions, corrections, and suspicious vegetables are welcome.
No approved comments yet. You can be first in the moderation queue.
Leave a comment.
It will appear after manual review. Your email is optional, stays private, and is never shown on the site.