远程桌面,大家都不陌生。管理员的最爱,当然也是黑客的致爱。
A.介绍
通常,登录远程桌面的账号都有不低的权限,这对红队(攻方)来说可谓雪中送炭。通常,对于窃取登录凭据,人们总是关注LSASS,但是操控lsass.exe 不仅会受终端防护响应程序监控,还有防病毒程序也会死死盯着。所以,很自然,要绕一绕,找别的没被盯着这么紧的路子。啊,还有,搞LSASS通常都需要超级权限。
在这篇文章,咱介绍介绍,怎么写个工具,可以让咱通过API hook的方式,从远程桌面客户端提取明文密码。如果你已经在目标机器有了正常用户权限了,而且这个用户正在使用远程桌面会话,那么这个方法可以让你在没有特权的情况下也能对远程桌面提取明文密码。
B.API Hook
简单地说,API Hooking 就是拦截指定程序的某个函数调用,然后重定向到别的函数的过程。这个可以通过重写内存的代码来做到,把目标函数改写成需要重定向到另一个函数,最后还会返回到原始的函数执行(这个收尾工作目的是让用户的正常功能不受影响)。Windows里有好几种API Hooking 的方法,但这些法子很复杂,足够另写一篇文章了。
出于教学的目的,在这里咱采用微软的Detours 这个库,它是开源的,并且兼容32位和64位程序。其它一些框架例如Frida,也有类似的功能。阔是Detours 是最最最轻量级的,当针对一个中心点进行操作时,轻量自有特别的优势。为了展示这个库有多NB,咱先用它来Hook MessageBox 这个函数玩玩。
在Hook一个函数之前,我们需要明了两个事儿:
原始函数和hook函数的指针地址。
为了hook顺利,目标函数和hook函数都应该有相同数量的变量、参数类似和调用协定。
下面的例子,咱hook MessageBox 函数调用,然后修改传到原始函数的参数。
static int(WINAPI * TrueMessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) = MessageBox;
int WINAPI _MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) {
return TrueMessageBox(NULL, L"Hooked", L"Hooked", 0);
}
int main()
{
// Hook MessageBox
DetourRestoreAfterWith();
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)TrueMessageBox, _MessageBox); // Two Arguments DetourTransactionCommit();
MessageBox(NULL, L"We can't be hooked", L"Hello", 0); // Detach Hooked Function
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourDetach(&(PVOID&)TrueMessageBox, _MessageBox); DetourTransactionCommit();
}
运行这个程序,在没hook的情况下,返回的应该是“We can't be hooked”的内容。但是咱hook了它,所以message box返回的是咱hook后的内容:
C.定位要Hook的东西
在Hook之前,我们需要定位咱感兴趣的函数。这些函数,它的参数对应的数据就应该是咱感兴趣的内容。在咱的这个例子中,服务器的机器名、IP、用户名和密码就是咱感兴趣的东西。API监控器(API Monitor)在这个情境中,确实灰常灰常给力。它能让你绑定到指定的进程,记录所有的API调用,还可以查看所有相关的结果。
为了做到这一点,咱把API监控器(API Monitor)接入mstsc.exe这个程序,然后发起一个简单的链接。
现在,我们可以在API监控器(API Monitor)里面开始搜索,就搜刚刚远程桌面链接里输入的用户名吧。在这里,有好几个API调用包括这段字符串,但最有意思的一个是CredIsMarshaledCredentialW。
好,拿这个函数名到MSDN 里找找这个函数的定义。可以看到,这个函数只有一个参数,类型是一个指向unicode编码字符串的32位指针(LPCWSTR)。
为了确保hook这个函数能得到正确的数据,咱将mstsc.exe绑到Windbg中,然后在Windbg那里,CredIsMarshaledCredentialW那个地方下个断点。当用这个mstsc.exe 建立连接并尝试登录的时候,阔以发现,传递给这个函数的第一个参数就是这个unicode编码字符串的地址。
用同样的方式 ,去搜索密码字符串,可以发现CryptProtectMemory 这个调用含有指向密码字符串的指针。
通过API 监控器(API Monitor),可以看到,CryptProtectMemory 位于 Crypt32.dll 当中,但这是错的,因为在最新版本的Win10里,这个函数是由dpapi.dll导出的。为了确保也能在这个API调用里面拿到正确的数据,咱在此使用Windbg,然后将mstsc.exe绑到Windbg中,在CryptProtectMemory 函数上下个断点。
瞄了瞄对应内存里的数据,咱可以推测,这个密码参数是一个指向某数据结构的一个指针。而我们感兴趣的信息就存在这个数据结构的开头部分,所以咱不需要解析完整个数据结构。与前面的例子不同的地方是,这个例子中有好几个到这个函数的调用没有咱苦苦探寻的信息。但有办法区分,可以通过包含密码字符串长度的前4个字节看出端倪。咱可以从内存读到这个长度,然后比较比较,如果大于0x2,那就表明,这个数据结构是含有密码字符串的。
同样的手段,可以定位到SspiPrepareForCredRead 的第二个参数,就是IP地址所在的地方。
D.RdpThief 展示
现在,已经明了提取敏感的凭据信息需要hook哪些函数了,这些作为基础,拿来实现RdpThief的功能。
RdpThief作为一个单独的DLL,当注入到mstsc.exe进程里之后,就会执行API hook功能,提取出明文的账号密码并存到文件里。这个过程需要用到一个攻击性的脚本,它会记录当前状态,监视几个进程并适时地把shellcode注入到mstsc.exe中去。这个DLL可以用sRDI项目转换成shellcode。启用RdpThief的时候,它会每隔5秒读一次进程列表,搜寻mstsc.exe,并注入进去。
有人给Cobalt Strike平台做了这个攻击脚本,当这个攻击脚本加载进去之后,有3个新命令激活:
rdpthief_enable: 启用新mstsc.exe进程的心跳检查,适时注入shellcode
rdpthief_disable: 停止对新mstsc进程的心跳检查,已注入的DLL不做撤回
rdpthief_dump: 如果有,打印提取到的账号密码
下面,是一个视频展示
RdpThief源码看这里:https://github.com/0x09AL/RdpThief
原作:Rio Sherri
原文:https://www.mdsec.co.uk/2019/11/rdpthief-extracting-clear-text-credentials-from-remote-desktop-clients/
HackerHub发布 | 转载请注明出处
我在看,你呢?