Chapter 4 : Executing Native Codes in Local Process (Part3)
4.3 Chunking CobaltStrike Payloads + Jump Method
بنام خدا |
||
دراین بخش 3 از فصل چهارم تکنیکی را بررسی می کنیم برای Chunk کردن Payload ها یا جدا کردن Payload ها را بررسی می کنیم. | ||
با کمک این روش جداکردن Payload در Memory شما مشاهده خواهید کرد که چگونه Kaspersky دورزده می شود . | ||
در این تکنیک برای جدا کردن CobaltStrike در Memory من از Jump Method کمک گرفتم تا Payload خود را در | ||
Memory بصورت جداگانه اجرا کنیم یعنی بخش اول Payload اجرا می شود در Memory در Address A سپس | ||
کد Jump می کند به بخش دوم Payload در Memory و آن را اجرا می کند اما چرا ما این کار را می کنیم؟ | ||
برای اینکه با این کار برخی آنتی ویروسها در زمان چک کردن Memory بدلیل اینکه Payload ما به دو بخش یا چند بخش | ||
تقسیم شده در Memory آن را نمی توانند Detect کنند و کد ما اجرا می شود بدون آنکه شناسایی شود . | ||
در اینجا من CobaltStrike Payload را به دو بخش تقسیم کرده ام اما اگر بخواهید آن را به بخشهای بیشتری تقسیم کنید باید | ||
کمی با Shell کد موجود در Payload آشنا باشید و بدانید که چگونه می تواند آن را به چندین بخش تقسیم کرد و نمی توان از هرجای | ||
Payload که خواستید آن را تقسیم کنید و اینکار معمولا جواب نمی دهد و به مشکل می خورید برای همین من آن را به 2 بخش تقسیم کرده ام . | ||
Chunking Payloads in-memory + Jump Method | ||
در اینجا باید کمی در مورد اینکه چگونه یک Payload را می توانید به 2 بخش تقسیم کرد صحبت کنیم . | ||
برای Chunk کردن یک Payload بکمک Jump Method شما می توانید مراحل زیر را انجام دهید . | ||
اول دقیق مشخص کنید از کدام بخش Payload می خواهید آن را تقسیم کنید سپس هر بخش از Payload | ||
را در Memory بصورت جداگانه Write کنید که در این کد من از VirtualAllocExNuma برای اختصاص دادن | ||
فضا در Memory استفاده کرده ام و از RtlMoveMemory برای Write کردن بر روی Memory استفاده کرده ام | ||
درنتیجه در کد من از API هایی استفاده کرده ام که ممکن می باشد موجب Detect شدن کد شود اما در ادامه | ||
مشاهده می کنید با اینکه از این توانبع استفاده شده کد من توسط Kaspersky v21 با آخرین Update شناسایی نمی شود | ||
در مرحله بعد بعد از Write کردن هر دو بخش Payload در Memory در آدرسهای جداگانه شما نیاز دارید که بخش اول | ||
Payload شما بتواند بعد از اجرا شدن رجوع کند به بخش دوم یا Section 2 of Payload برای اجرا درنتیجه شما | ||
نیاز دارید در بخش یک Payload در آخر آن یکسری Byte Array اضافه کنید که کار Jump کردن از بخش 1 به بخش 2 را | ||
برای شما در Memory انجام دهد یعنی بخش یک زمانی که در Memory اجرا شد در آخر کد آن بصورت خودکار کد Jump می کند | ||
به اول بخش 2 از Payload که در آدرسی متفاوت در Memory نوشته شده در نتیجه هر 2بخش به ترتیب اجرا شده در Memory | ||
و اگر همه چیز درست انجام شود شما باید Session را در طرف CobaltStrike داشته باشید . | ||
در اینجا یک مثال مشاهده می کنید تا کمی بهتر متوجه Background کار شوید : | ||
Cobaltstrike payload: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF AA BB CC DD EE FF 11 22 33 44 55 66 77 |
||
Chunking Payloads step1 | ||
In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF + JUMP Bytes to address Section 2 [0x1CE0080] |
||
In-memory Section 2 Address [0x1CE0080]: AA BB CC DD EE FF 11 22 33 44 55 66 77 |
||
همانطور که مشاهده می کنید ما یک CobbaltStrike Payload داریم که به دو بخش تقسیم شده با دو رنگ حالا | ||
دقیقا می دانیم که اول و آخر هر بخش کجا می باشد در نتیجه این دو بخش را در Memory باید در آدرسهای متفاوت با | ||
توابعی که داریم مانند VirtualAlloc یا VirtualAllocExNuma و ... یک فضایی در حافظه برای آنها در نظر بگیریم | ||
سپس انها را توسط RtlMoveMemory یا WriteProcessMemory بر روی Memory نوشته تا اینجا مشکلی ما نداریم و همانند این کار | ||
را در فصلهای قبلی انجام داده ایم. | ||
Chunking Payloads step2 | ||
کاری که در این تکنیک ما باید اضافه کنیم به این کد این می باشد که در آخر بخش یک از Payload ما | ||
باید یک Jump کد را به Payload اضافه کنیم تا درزمان اجرا کد بلافاصله پرش کند به بخش 2 .یعنی در بخش یک Payload بعد از Byte AF در آخر آن | ||
باید Byte های Jump اضافه شود به Payload در Section 1 ما . که این Jump کد ما دارای آدرس بخش 2 از Payload در Memory می باشد . | ||
برای این کار من از Jump Method B8 00 00 00 00 FF E0 استفاده کرده ام در نتیجه ما چیزی مانند کدهای زیر را داریم برای این مثال | ||
In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF B8 80 00 CE 01 FF E0 |
||
In-memory Section 2 Address [0x1CE0080]: AA BB CC DD EE FF 11 22 33 44 55 66 77 |
||
همانند مثال بالا کد Section1 در زمان اجرا وقتی به قسمت آبی رنگ B8 برسد پرش می کند به آدرسی که در چهار بایت بعدی آن مشخص شده | ||
که همان بخش دوم یا Section2 ما می باشد از Payload و در آخر با اجرای این تکنیک شما می توانید Session را در CobaltStrike داشته باشید | ||
و این روش همانند این می باشد که همه Payload را در یک Section در Memory نوشته باشیم اما در حقیقت در 2 آدرس جدا در Memory | ||
ما این Payload ها را نوشته ایم با این کار شما دارای Payload های تقسیم شده در Memory می باشید در نتیجه Signature های Payload های شما | ||
با Signature یک Payload کامل که برای CobaltStrike می باشد یکسان نمی باشد در Memory و این کار می تواند به شما کمک کند برخی | ||
آنتی ویروسها و Memory Scanner های آنها را Bypass کنید در زمانی که این بخشهای Payload شما را در آدرسهای متفاوت Scan می کنند . | ||
در این روش ما 2 Payload داریم که به ترتیب اجرا می شوند در Memory و در 2 آدرس جداگانه 0x1CC0080 و 0x1CE0080 نوشته شده اند . | ||
در نتیجه برخی Memory Scanner ها توسط این روش دور زده می شوند اما اگر کل Payload در یک Section می بود مطمنا Detect می شد | ||
توسط Memory Scanner ها بدلیل اینکه Signature کدهای ما با DataBase آنها یکسان می بود و در نتیجه کد شناسایی میشد اما در این حالت | ||
امکان خطای Memory Scanner ها بالا می باشد و معمولا Bypass می شوند اما نمی توان با اطمینان این را برای همه آنتی ویروس ها گفت | ||
برخی ممکن می باشد Payload را در Memory شناسایی نکنند اما کد شما را بر روی Harddisk شناسایی کند که اجرا شده به عنوان کدی مخرب | ||
و برخی ممکن می باشد که این تکنیک را کلا شناسایی نکنند اما زمانی که در شبکه Network connection بین Process شما با Cobaltstrike | ||
برقرار می شود آن ترافیک را بعنوان ترافیک آلوده یا مخرب شناسایی کنند و الی آخر درنتیجه بر روی تک تک آنتی ویروسها باید این نوع کدها | ||
تست شود تا روشهای برخود آنها را بررسی کنید جداگانه . | ||
برای اجرای کد در Section1 ما نیاز داریم که کد آن را توسط CreateThread اجرا کنیم یا روش بهتر این می باشد که از یک | ||
Jump دیگه استفاده کنیم برای اجرا Section1 . من در کد مربوط به این بخش هر دو کار را با هم انجام داده ام یعنی یک | ||
Byte Array از کد Jump به آدرس Section 1 را بر روی Memory نوشته ام سپس آن را با CreateThread اجرا کرده ام | ||
در نتیجه مشاهده می کنید که کد من بسیار ریسکی می باشد و احتمال Detect شدن آن بالا می باشد توسط آنتی ویروسها | ||
برای اینکه از توابعی همانند CreateThread و ... استفاده کرده ام اما مشاهده خواهید کرد با این حال کد من کار می کند و Kaspersky دور زده می شود. | ||
و به این دلیل می باشد که در زمان Scan کردن Memory آنتی ویروس Kaspersky متوجه نمی شود که Payload تقسیم شده و | ||
دارای Signature یکسان با DB خود نمی بیند Payload های ما را در Memory در نتیجه Bypass می شود. | ||
|
||
|
||
Picture 1 |
||
در شکل 1 مشاهده می کنید در بخشی که به رنگ آبی اشاره شده در آدرس 0x01cb0080 ما یک Byte array داریم با | ||
با این بایتها F9 F9 F9 B8 80 00 CC 01 FF E0 زمانی که این بایتها توسط CreateThread اجرا شوند کد شما پرش می کند | ||
به آدرس قرمز رنگ در این بایتها که آدرس بصورت برعکس در این آرایه نوشته شده یعنی آدرس ما می باشد 0x1CC0080 | ||
در نتیجه این آدرسی می باشد که بعد از اجرا این کد توسط CreateThread به آن Jump می شود برای ادامه اجرای کد . | ||
و این آدرس همان آدرس Section 1 ما می باشد که در شکل 1 مشاهده می کنید که با رنگ قرمز مشخص شده در شکل 1 . | ||
در آدرس خانه 0x1CC0080 در خانه 80 این آدرس مشاهده می کنید که بایتهای FC 48 83 E4 و الی آخر که همان Section1 ما می باشد. | ||
In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF + JUMP Bytes to address Section 2 [0x1CE0080] |
||
در مرحله بعد Section1 ما اجرا می شود یعنی FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF در Memory اجرا می شود تا | ||
اجرای کد ما برسد به آخرین بایت ما که در این مثال AF می باشد البته AF یک مثال می باشد و در واقعیت Payload شما بسیار بیشتر از این | ||
چند بایت می باشد و این فقط برای مثال می باشد و ممکن می باشد بایت آخر شما در مثال واقعی بایت AF نباشد این نکته باید گفته می شد که | ||
موجب اشتباه نشود. سپس همانطور که در مرحله Chunking Payloads step2 گفته شد زمانی که اجرای کد به بایت آخر بخش اول رسید کد یک Jump دیگر | ||
دارد به بخش دوم یا Section2 برای Payload تا کامل CobaltStrike Payload در حافظه اجرا شود. | ||
همانند شکل 1 کد Section1 که در تصویر مشخص می باشد و با FC 48 83 E4 شروع شده ادامه دارد و دراخر آن Jump bytes ما | ||
به ادرس بخش دوم اضافه شده یعنی B8 80 00 CE 01 FF E0 به آن اضافه شده البته در شکل 1 مشخص نمی باشد | ||
In-memory Section 1 Address [0x1CC0080]: FC 48 83 E4 F0 E8 C8 00 00 00 41 41 41 50 52 AF B8 80 00 CE 01 FF E0 |
||
In-memory Section 2 Address [0x1CE0080]: AA BB CC DD EE FF 11 22 33 44 55 66 77 |
||
در این مرحله بعد از Jump به ادرس 0x1CE0080 کد AA BB CC DD EE FF 11 22 33 44 55 66 77 اجرا می شود و CobaltStrike Payload بصورت کامل | ||
در Memory اجرا می شود با اینکه در 2 بخش جداگانه نوشته شده بود و اگر همه مراحل به درستی انجام شود شما باید در CobaltStrike یک New Session | ||
داشته باشید. | ||
درشکل 2 شما می توانید مشاهده کنید اجرای کد و مراحل اجرا آن را در شکل مشاهده می کنید | ||
که Kaspersky v21 توسط این روش Bypass شده. | ||
Picture 2 |
||
در شکل 2 مشاهده می کنید که کد کار کرده و Kaspersky v21 با آخرین Update دور زده شده | ||
در شکل 3 ما اجرای کد را با Detail بیشتر داریم در اینجا شما می توانید مشاهده کنید | ||
مراحل اجرای کد را Step by step : | ||
Picture 3 |
||
مراحل در شکل 3 کاملا مشخص می باشد | ||
نکته : قبل از شرح دادن مراحل شکل 3 در زیر این نکته باید گفته شود که Memory Section هایی که در زیر در مورد آنها | ||
صحبت می شود باید در Memory دارای Protection Mode Execute + Read حداقل باشند اما من در کد آنها | ||
را در حالت RWX دارم که ReadWrite + Excute می باشند. | ||
1. در این مرحله یک CreateThread داریم که در آن Byte Array F9 F9 F9 B8 00 00 4C 01 FF E0 اجرا می شود. | ||
2. سپس بعد از اجرای مرحله 1 کد ما Jump می کند به آدرس 0x14C0000 و در خانه 80 آن یک کد Jump می باشد | ||
3. در این مرحله کد در آدرس 0x14C0000 اجرا می شود که یک Jump دیگر می باشد به آدرس Section1 of Cobaltstrike Payload | ||
آدرس Section1 ما در اینجا 0x14D0080 می باشد یعنی در خانه 80 این آدرس Payload ما شروع شده که همان بخش اول | ||
Payload ما می باشد برای اجرا در نتیجه بعد Jump شدن به آن آدرس کد آن Payload شروع به اجرا می کند و بخش اول یا Section1 ما اجرا می شود | ||
4. در آخر مرحله 3 ما یک کد Jump دیگر به Section1 اضافه کرده ایم که به آدرس Section2 ما Jump کند همانطور که در شکل 3 مشاهده می کنید . | ||
و این آدرس را با اضافه کردن Byte array F9 F9 F9 B8 80 00 4E 01 FF E0 به آخر Section1 ایجاد کرده ایم . | ||
در این مرحله در آخر Section1 کد ما پرش می کند به Section2 و در آدرس 0x14E0080 که در خانه 80 آن Section2 ما برای Cobaltstrike Payload | ||
وجود دارد و ادامه اجرای کد ازاین بخش ادامه پیدا می کند تا به آخر Section2 برسد که همان آخر Payload ما می باشد. | ||
در نتیجه در آخر مشاهده می کنید با اینکه ما CobaltStrike Payload را به دو بخش Section1 & Section2 تقسیم کرده ایم با این | ||
تکنیک توانستیم آنها را در Memory به ترتیب اجراکنیم و در آخر New Session در CobaltStrike بگیریم و Kaspersky v21 این | ||
روش را شناسایی نکرد و Bypass شد. | ||
در این کد ما چیزی مانند این مراحل در Memory برای Jump کردن بین Memory Address ها داشتیم : | ||
Jump to Memory Addresses: 1.Init code to run by CreateThread [0x1D390000] jump to [0x14C0080] jump to [0x14D0080] jump to [0x14E0080]
Payload Section1 [0x14D0080] Payload Section2 [0x14E0080] |
||
نکته: خود این مورد که شما دو jump قبل از پرش به Section1 دارید می تواند کمک کننده باشد برای | ||
گیج کردن برخی آنتی ویروسها . | ||
در شکل 4 شما می توانید مشاهده کنید که کد اجرا شده و Kaspersky v21 + update 2023/08/26 نتوانسته این | ||
تکنیک را شناسایی کند. | ||
Picture 4 |
||
در شکل 4 مشاهده می کنید که یک Thread در حالت Suspend می باشد دلیل آن در کد می باشد. | ||
این نکته باید شرح داده شود در کد مربوط به این بخش 3 از فصل 4 من در آن یک روشی را تست کرده ام | ||
در کد زیر می توانید مشاهده کنید که من Encrypted_Payload را بدون اینکه decrypt کنم توسط xor Method آنرا در | ||
Memory توسط RtlMoveMemory در حافظه نوشته ام و یک CreateThread از آن ساخته ام در حالت Suspend | ||
چرا حالت Suspend ایجاد شده در تصور4 بدلیل اینکه من در CreateThread از 0x00000004 بجای 0x0 استفاده کرده ام | ||
string Payload_Encrypted = "236 88 147 244 224 248 220 16 16 16 81 65 81 64 66 88 33 194 ...";
string[] Payload_Encrypted_Without_delimiterChar =
Payload_Encrypted.Split(' '); + (AddressOfPayload_In_Mem
+ ((uint)_X_to_Bytes.Length / 2)).ToString("X8") + "]"); + (AddressOfPayload_In_Mem).ToString("X8")
+ "]"); +
AddressOfPayload_In_Mem.ToString("X8") + "]"); +
AddressOfPayload_In_Mem.ToString("X8") + "]"); |
||
دلیل اینکه من در حالت Suspend یک Payload کامل از CobaltStrike را بصورت Encrypt شده در Thread قرار داده ام | ||
این می باشد که Anti-virus مد نظر خودم را تست کنم آیا وقتی یک New Thread در حالت Suspend ایجاد می شود | ||
آن را چک می کند اگر بله چه واکنشی انجام می دهد وقتی مقدار درون New Thread بصورت Encrypt باشد و نه Decrypt | ||
و آیا زمانی که یک New Thread در حالت Suspend مشاهده کرد و چیزی تشخیص نداد دیگر به موارد دیگری که ممکن | ||
می باشد مخرب باشد در Memory توجه می کند یا خیر بدلیل اینکه تستهای ما بر روی AV ها بصورت Black Box می باشد | ||
و ما Source های آنتی ویروس ها را نداریم تمامی مواردی که به ذهن شما می رسد باید در حالتهای مختلف بر روی تک تک | ||
آنتی ویروس ها تست شود تا شما دیدی از آنها بدست آورید و همچنین ببینید آیا ایده شما جواب می دهد یا خیر . من می توانم همین کد | ||
که در Code 4.3 مشاهده می کنید را بدون انجام این تکه کد بالا نیز انجام دهم و ببینم آیا تفاوتی در Detection می کند یا باز هم | ||
Kaspersky v21 دور زده می شود اگر نتیجه یکسان بود متوجه می شوم تا حدودی که این ایده من خیلی کار مهمی در ایجاد واکنش درست یا | ||
اشتباه آنتی ویروس نداشته اما اگر مشاهده کردم که نبودن این کد موجب می شود آنتی ویروس روش Chunking Payload من را شناسایی کند | ||
آنوقت متوجه می شوم که این کد تاثیر دارد و بیشتر بر روی آن تمرکز می کنم که دقیقا این ایده چرا کار کرده و الی آخر و شما باید این | ||
مورد را در نظر داشته باشید که برنامه نویسان شرکتهای آنتی ویروس افرادی می باشند همانند من و شما و آنها نیز مشکلاتی | ||
در کدهایشان وجود دارد و حتما Bug دارند همانند کد من و شما در نتیجه چون ما این موارد را نمی دانیم باید آنها را بصورت Black box تست | ||
کنیم و ایده های خود را در کدهای مختلف تست کنیم تا چیزی از درون آنها پیدا کنیم در نتیجه من این تست را انجام دادم تا ببینم واکنش | ||
آنتی ویروس مد نظر خود را در مورد این New Thread در حالت Suspend و به دلیل اینکه ما می دانیم معمولا Suspend Thread | ||
برای روشهایی از حمله استفاده می شود در نتیجه استفاده از آن ریسک بالا دارد بر روی برخی از Anti-virus ها حتی اگر Payload شما Encrypt باشد | ||
و شما New Thread را در کد Resume نکنید. | ||
Picture 5 |
||
در شکل 5 مشاهده می کنید Section ها ما در Memory که دارای Protection Mode RWX می باشند و کد بخوبی کار کرده | ||
و توسط Kaspersky v21 این مورد شناسایی نشده. | ||
مواردی در این کد می باشد که من باید شرح بدم اما کل کد را خط به خط توضیح نمیدم به این دلیل که | ||
من فکر می کنم شما باید اول تمامی فصلهای گذشته را بخوبی یاد گرفته باشید تا این کد را متوجه شوید و اگر شما فصلهای | ||
قبلی را متوجه نشده باشید کدهای زیر را نمی توانید متوجه شوید در نتیجه هدف من این می باشد که دانشجو کمی خودش تلاش کند کد را | ||
بخواند و متوجه آن شود با توجه به مواردی که من در این کتاب در فصلهای قبلی آموزش داده ام شما می توانید این کار را بکنید خودتان | ||
و اگر این کار را توانستید انجام دهید این یعنی که شما خوب مطالب را متوجه شده اید . | ||
یکی از مواردی که من در کد نوشته ام و کمی جدید می باشد استفاده از F9 می باشد در Byte ها که اگر ساده بگم هیچ کاری انجام نمی دهد و فقط از آن برای | ||
پرکردن Memory استفاده کرده ام تا Payload های من یا Byte array های من که می خواهم در Memory اجرا شوند در خانه آدرسهای متفاوتی ایجاد شود | ||
و دقیقا Byte های کد های Jump من در اول StartAddress نباشند برای مثال اگر 0x1CE0000 آدرس شروع من باشد اول آن را با این آرایه OPSX پر می کنم و بعد | ||
بایتهای Jump خود را می نویسم در نتیجه ممکن می باشد از خانه 80 آدرس 0x1CE0080 کد واقعی من شروع شود همانند شکل 5. | ||
byte[] opsx = new byte[] {0xf9, 0xf9, 0xf9, 0xf9, 0xf9, 0xf9,...}
RtlMoveMemory(jmpaddr_uintaddress[0], opsx,(uint) opsx.Length); |
||
در واقع اول با این کد خانه های اول آن آدرس را پر می کنم با بایتهای F9 : | ||
RtlMoveMemory(jmpaddr_uintaddress[0], opsx,(uint) opsx.Length); |
||
سپس توسط این کد از آن آدرس پر شده به بعد شروع به نوشتن Byte های مربوط به Jump خود می کنم و .... | ||
RtlMoveMemory(jmpaddr_uintaddress[0] + (uint)opsx.Length, jmpaddr_execution_jmp_code[1], (uint)chunk_Final_Payload2.Length); |
||
نکته: البته این دو خط کد دقیقا مربوط به آن تصور 5 نمی باشد اما کدها یک شکل می باشند و نتیجه دقیقا یکسان همانند شکل 5 فقط ممکن می باشد | ||
کد اضافه شده بعد از F9 ها متفاوت باشد. | ||
موارد دیگری در کد نمی باشد که جدید باشد و من امیدوارم با توضیحاتی که در فصلهای قبلی دادم بتوانید این کد را متوجه شوید خط به خط | ||
و بعد از متوجه شدن آن خود آن را بنویسید و ایده های خود را به آن اضافه کنید و این کار سخت نمی باشد فقط کمی تلاش و تمرین می خواهد. | ||
و یادتان باشد اگر شما درگیر کد نشوید شما نمی توانید کد را متوجه شوید و هدف من این می باشد که شما واقعا کد را بفهمید و کپی و پیست کار نباشید. | ||
توصیه من به شما استفاده از Breakpoint در VS.NET 2019 می باشد برای خط به خط این کد و نگاه کردن به مقدار متغیر ها | ||
تا بهتر متوجه شوید کد دقیقا چگونه کار می کند. | ||
|
||
و در آخر باید گفت که این کد توسط WindowsDefender شناسایی می شود اما Kaspersky نتوانست این کد و تکنیک را شناسایی کند | ||
در نتیجه بهتر می باشد که این کد را با ایده های خود که به کد اضافه کرده اید بر روی تک تک آنتی ویروس ها تست کنید. | ||
video about this code : https://www.youtube.com/watch?v=yNbUner6yZg | ||
eBook: Bypassing Anti viruses by C# Programming v2.0 |
||
Last Updated: 2023 Jul 14 |