渗透技巧——Pass the Hash with Remote Desktop Protocol

0x00 前言


在之前的文章《渗透技巧——Pass the Hash with Remote Desktop(Restricted Admin mode)》介绍了特定条件下(Server需要开启Restricted Admin mode,Client需要支持Restricted Admin mode)Pass the Hash with Remote Desktop的方法,本文将要介绍更为通用的方法(通过NTLM hash登录RDP),分析原理,开源代码,记录细节。

0x01 简介


本文将要介绍以下内容:

  • 渗透测试中的需求
  • 验证口令或者NTLM hash的实现方法
  • C实现代码
  • Python实现代码
  • C sharp实现代码

0x02 渗透测试中的需求


如果是开发远程桌面服务的客户端,可供选择的方法有很多,例如通过C#调用ActiveX组件AxMSTSCLib

详细方法可参考:https://www.codeproject.com/Articles/43705/Remote-Desktop-using-C-NET

但是在渗透测试中,我们有以下两个需求:

1.验证口令或者hash

需要满足以下条件:

  • 可以在Windows和Linux系统上运行
  • 登录方式支持明文口令和NTLM hash
  • 命令行下使用

2.远程登录

  • 可以在Windows和Linux系统上运行
  • 登录方式支持明文口令和NTLM hash

0x03 验证口令或者hash的实现方法


为了支持NTLM hash登录,需要对协议和加密方式有所了解

关于RDP协议的参考资料:https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation

1.使用Python实现(rdp_check.py)

代码地址:https://github.com/SecureAuthCorp/impacket/blob/master/examples/rdp_check.py

  • 可以在Windows和Linux系统上运行
  • 登录方式支持明文口令和NTLM hash
  • 命令行下使用

脚本运行前需要安装Impacket

安装方法:pip install Impacket

如果是在Windows系统下使用,可以将Python脚本转换成exe,编译好的exe文件下载地址:https://github.com/maaaaz/impacket-examples-windows

使用示例:明文口令验证

rdp_check.py /administrator:test123@192.168.1.1

使用示例:NTLM hash验证

rdp_check.py /administrator@192.168.1.1 -hashes :c5a237b7e9d8e708d8436b6148a25fa1

注:

hash的格式为LMHASH:NTHASH,如果只有NTLM hash,格式为 :NTHASH,需要注意:前面存在一个空格字符

2.使用C实现(FreeRDP)

代码地址:https://github.com/FreeRDP

  • 可以在Windows和Linux系统上运行
  • 登录方式支持明文口令
  • 命令行下使用

如果为了支持NTLM hash登录,需要使用旧版本的FreeRDP,下载地址:https://labs.portcullis.co.uk/download/FreeRDP-pth.tar.gz

如果仅仅为了验证口令或者hash,需要修改代码,去除后续登录的功能

关于FreeRDP的使用可参考:https://github.com/FreeRDP/FreeRDP/wiki/CommandLineInterface

3.使用C sharp实现(SharpRDPCheck)

  • 可以在Windows系统上运行
  • 登录方式支持明文口令和NTLM hash
  • 命令行下使用

开发过程记录:

这里无法通过调用ActiveX组件AxMSTSCLib实现,原因如下:

  • 使用ActiveX组件AxMSTSCLib无法通过NTLM hash登录
  • 对命令行的支持不够友好

所以只能参照rdp_check.py的方式,手动构造通信数据

经过搜索,我找到了一个可供参考的代码:https://github.com/RDPUploader/RDPUploader

RDPUploader包括三个子项目,编译成功后生成两个dll和一个exe文件,不支持命令行操作,也不支持NTLM hash登录,但其中的通信过程写的很清晰

接下来以RDPUploader为模板进行开发

这里具体介绍一下支持NTLM hash登录的方法:

Remote Desktop Protocol在进行验证时,先将明文口令转换成NTLM hash,再进行验证

我们首先寻找RDPUploader中从明文口令转换为NTLM hash的代码,具体内容如下:

private static byte[] nTOWFv1(string password)
{
    if (password == null)
	{
        throw new Exception("Password parameter is required");
    }

    return MD4.ComputeHash(Encoding.Unicode.GetBytes(password));
}

private static byte[] nTOWFv2(string domain, string username, string password)
{
    HMACT64 hmact = new HMACT64(nTOWFv1(password));
    hmact.update(Encoding.Unicode.GetBytes(username.ToUpper()));
    hmact.update(Encoding.Unicode.GetBytes(domain));
	return hmact.digest();
}

补充:NTLM hash的生成方法

  1. 将明文口令转换成十六进制的格式
  2. 转换成Unicode格式,即在每个字节之后添加0x00
  3. 对Unicode字符串作MD4加密,生成32位的十六进制数字串

这里以明文口令test123为例:

转换成十六进制的格式为74657374313233

转换成Unicode格式为7400650073007400310032003300

对字符串7400650073007400310032003300作MD4加密,结果为c5a237b7e9d8e708d8436b6148a25fa1

更多细节可参考之前的文章《Windows下的密码hash——NTLM hash和Net-NTLM hash介绍》

这里我们可以将nTOWFv1(password)的结果输出,验证NTLM hash的生成方法是否准确,修改后的代码如下:

private static byte[] nTOWFv2(string domain, string username, string password)
{

    byte[] a = nTOWFv1(password);
    string text = "";
    for (int i = 0; i < a.Length; i++)
    {
        text += a[i].ToString("X2");
    }
    Console.WriteLine(text);
    HMACT64 hmact = new HMACT64(a);
    hmact.update(Encoding.Unicode.GetBytes(username.ToUpper()));
    hmact.update(Encoding.Unicode.GetBytes(domain));
	return hmact.digest();
}

输出的字符串为C5A237B7E9D8E708D8436B6148A25FA1,验证成功

接下来,直接传入字符串C5A237B7E9D8E708D8436B6148A25FA1进行验证

这里需要将hex格式的字符串转换为byte[],代码如下:

public static byte[] ConvertHexStringToBytes(string hexString)
{
    hexString = hexString.Replace(" ", "");
    if (hexString.Length % 2 != 0)
    {
        throw new ArgumentException("wrong length of ntlm hash");
    }
	byte[] returnBytes = new byte[hexString.Length / 2];
    for (int i = 0; i < returnBytes.Length; i++)
    {
        returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
    }
	return returnBytes;
 }

private static byte[] nTOWFv2(string domain, string username, string password)
{
    string text = "C5A237B7E9D8E708D8436B6148A25FA1";
	byte[] byteArray = ConvertHexStringToBytes(text);
    HMACT64 hmact = new HMACT64(byteArray);
    hmact.update(Encoding.Unicode.GetBytes(username.ToUpper()));
    hmact.update(Encoding.Unicode.GetBytes(domain));
	return hmact.digest();
}

验证成功,最后将固定字符串修改为变量传递即可实现对NTLM hash登录的支持

完整代码已开源,地址如下:https://github.com/3gstudent/SharpRDPCheck/

可通过Visual Studio进行编译,编译的目标框架推荐使用.NET Framework 4.6,这是为了使用TLSv1.2

否则会使用TLSv1.0,在进行登录时会失败,提示如下:

[!] Authentication Error (The decryption operation failed, see inner exception.)

InnerException: System.ComponentModel.Win32Exception (0x80004005): The Local Sec
urity Authority cannot be contacted

参考资料:https://docs.microsoft.com/en-us/dotnet/api/system.net.security.sslstream.authenticateasclient?redirectedfrom=MSDN&view=netframework-4.8

编译成功后在安装.NET Framework 4的Windows系统下都可以正常使用

0x04 防御思路


对于RDP的爆破攻击,可以选择提高用户口令的强度

对于爆破行为的检测,可以通过cmd查看其他用户通过RDP登录当前系统的日志:

wevtutil gli "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational"
wevtutil qe /f:text "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Operational"

每当Listener RDP-Tcp收到一个连接请求,将产生EventID为261的日志

也就是说,只要收到了RDP的连接请求,无论连接是否成功,均会产生EventID为261的日志

通过Powershell查看其他用户通过RDP登录当前系统的日志:

https://gallery.technet.microsoft.com/Remote-Desktop-Connection-3fe225cd

0x05 小结


本文介绍了针对Remote Desktop Protocol,验证口令或者NTLM hash的实现方法,开源代码SharpRDPCheck,分享程序实现NTLM hash登录的细节,最后介绍通过日志检测RDP爆破行为的方法。


LEAVE A REPLY

Written on May 8, 2020