你驻足于春色中,于那独一无二的春色之中.
Struts2的环境有时候显得有些庞大而复杂,这里借着漏洞学习的过程了解一下Tomcat+Struts2架构的一些问题,这么一看重要的学习其他而不是漏洞了~~。
之前很少接触Java环境下的Web问题,对于Jboss,Tomcat,Struts2,Weblogic等等的概念没有一下清晰的认识,通过查阅资料,有了如下的认识。
首先这些应用都是为了完成一个Java的Web服务,那么,在拥有Java环境的情况下,我们需要一个能够监听Web服务并且调度相关逻辑的服务器,这个就是Tomcat起到的作用。
和Tomcat具有同样身份作用的还有Jboss,Resin,Glassfish和Weblogic,那么其实Struts2都可以部署在这些Java容器中,这里就以Tomcat为例,一个运行成功的Tomcat应用如下图所示。
我们可以看一下默认的Tomcat目录
bin # 启动/关闭服务的脚本
|-bootstrap.jar # 引导程序
|-commons-daemon.jar # 守护程序
|-tomcat-juli.jar # java util logging 日志管理
|-daemon.sh
|-startup.sh
|-shutdown.sh
|-setclasspath.sh # 动态设置JAVA变量,内部使用
|-digest.sh # 使用特殊的算法加密
|-tool-wrapper.sh # 封装工具,内部使用
|-version.sh # 检测版本
|-configtest.sh # 测试配置文件语法
|-catalina.sh # 启动相应的java程序
|-catalina-tasks.xml # 加载包路径
conf
|-Catalina # 默认似乎是空的,可以用来配置项目路径
|-catalina.properties # catalina的安全设置、类加载设置、字符缓存设置
|-catalina.policy # 安全策略
|-jaspic-providers.xml # 身份验证模块配置
|-jaspic-providers.xsd # 似乎xsd在这里是变量定义
|-logging.properties # 日志记录配置
|-tomcat-users.xml
|-web.xml # Web基础配置
|-context.xml # 配置策略的路径
|-server.xml # 服务端信息配置,端口,日志存储方式,根路径等等
lib
|-*.jar # java封装库
logs
|-*.log # 日志存储文件
temp
webapps
|-*.war # 打包的Web程序
|-* # 自动解压的Web 页面程序
work
|-* # 对应webapps的类库
*安全策略,包括对于一些系统变量的读取限制,特殊的函数执行限制,文件的读写控制
下图是一张Tomcat的层级关系
简单认识了一下Tomcat的结构后,我们来部署一个含有S054漏洞的struts2应用,下载struts2后,拷贝apps中的structs2-blank.war到Tomcat的webapps目录下,重启Tomcat,就会自动解压war包。
访问 http://127.0.0.1:8080/struts2-blank/example/HelloWorld.action
就可以访问到一个struts自带的欢迎界面
我们尝试修改解压出来的应用文件夹名为ROOT,发现在没有任何配置的情况下,tomcat会默认访问ROOT文件夹,而其他文件夹可以通过路由访问,当然前提是策略允许访问,
同时,我在之前提到了路径还可以配置在conf中,我们尝试创建一个指定根路径为structs2-blank的配置文件于conf/Catalina/localhost中也可以实现当Web文件不在webapps中时的路由访问。
再来看一下struts默认的目录结构
docs # 文档
apps # struts2的war包,提供样例参考
|-blank
|-mailreader
|-portlet
|-rest-showcase
|-showcase
lib # jar包库
src # struts2的源码
我们使用blank来做测试用,blank基本展示了struts2应用应该有的几部分:
WEB-INF
|-web.xml # 过滤器配置文件以及基础路径配置
|-src # 源码
|-lib # jar库
|-jsp # jsp文件夹
|-classes # 类和其他配置文件文件夹
META-INF # 元信息
这里就直接使用struts2自带的欢迎页面来测试:
http://127.0.0.1:8080/struts2-blank/example/HelloWorld.action
先利用网上流传的工具来进行测试:
确实存在漏洞,这个时候我们抓包进行细节的复现。在GET包中增加Content-Type字段,使用网上流传的Payload:
1 | %{ |
对上面一段payload进行格式整理
1 | %{ |
根据大牛们的解读文章,可以了解到
首先是multipart/form-data绕过类型判断,然后调用了@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS这个方法给了#_memberAccess这个节点,然后就是利用java.lang.ProcessBuilder执行预定的cmd命令,对执行后的错误流进行重定向显示。~~当然这只能算是对于这段payload的主观感觉猜测。
详细的底层分析可以阅读这篇文章
有朋友在测试时发现,在有漏洞的站点执行命令是很容易的,反弹Shell也可以轻松完成,但上传Webshell就不见得每次都能够顺利完成,而很多时候如果能使用Webshell进行图形界面操作会更加方便。这里就常见的几种上传失败情况进行总结。
1.上传成功但解析跳转:
这种情况对应到之前说到的路由关系,我们仍然使用struts2-blank来测试。
blank默认情况下,有策略限制直接跳转到jsp。
访问URL:struts2-blank/WEB-INF/jsp/example/Login.jsp,页面会显示403。
这个时候发现访问Login.action可以访问成功。此时,如果在当前文件夹下上传test.jsp,必须使用test或者test.action来访问。
值得一提的是,这种对攻击者上传文件的成功访问是基于以下这条规则:
1 | <action name="*" class="example.ExampleSupport"> |
简单解释一下,class相当于处理逻辑(Model?),然后对action 的name进行全匹配,将匹配到的文件名进行相应的jsp(View?)跳转。
而之所以test和test.action都可以被访问到应该是一种缺省规则,在struts.xml中添加如下规则:
1 | <constant name="struts.action.extension" value="action"></constant> |
此时,test就无法被访问到。
从上面这种情况来看,跳转的原因首先是对接收的后缀进行匹配,其次是相关action动作的处理,那么我们就可以通过添加和上面相似的规则来实现Webshell的访问。
这里就不得不提一下struts的运行机制
struts.xml,这个按照我的理解就相当于是一个控制器,进行路由控制
|
|
\ /
class,当路由得到相应的action名后,以相应的method来调用class处理逻辑
|
|
\ /
jsp,根据处理逻辑的不同,跳转给用户相应的显示页面
这里从我理解的MVC的角度进行解释,根据我查阅的资料,再往下走应该是struts2的filter机制,这里我就先不去理解了。
2.上传成功,解析出错
这种情况主要分为两种,一种是解析错但菜刀依然可以执行,另一种是类文件或者库文件缺失,个人猜测通过上传相应的文件到lib或者classes文件夹来进行尝试,但因为缺乏相应的测试条件,这个问题先留在这里,后面实践了再来补写。
————————————————————2017.3.23 更新
最近测试的时候发现,解析出错还有一种情况会导致Getshell失败,那就是服务端自己写了错误显示用的jsp,这样就会导致shell上传成功后,显示状态500,一般情况下状态500并不会影响shell连接,但是服务端自己实现500后可能会写入一个跳转操作,跳转回根目录,自然shell就无法访问到。
这种时候,我们可以利用struts2的漏洞来改写服务端的错误处理代码,禁止出错跳转,是的shell可以被访问到。
3.负载均衡
测试中发现,很多Jboss的站会用到负载均衡,导致shell 404,这种情况一种是多试几次,反正有一次应该能够连上,一种是保证每台机器上都有Wenshell。
————————————————————
4.无法上传
根据朋友讲的他遇到的经历,无法上传主要有以下几种情况:
在实际测试中发现,许多struts2的应用广泛使用了许多其他中间件,比如Hiberate、HyperSQL、FirebirdSQL等等平时没有使用到的框架或者数据库,还有会使用其他Web服务器的情况,比如JBoss。
————————————————————2017.3.23 更新
这里在补充一下shell方面的学习
1.war包shell
这种需要服务器开启了热部署相关的应用才能使用,当然~~你愿意重启整个WEB服务也可以,将jsp和WEB-INF打包成一个war包上传,可以实现一个新的路径下的访问。
http://www.freebuf.com/articles/web/36455.html
这篇文章提到了具体的操作步骤。
2.jspx shell
园长在乌云上的JavaWeb系列讲解过,jspx是以xml语法来写jsp文件。个人理解是使用xml语法来规范jsp语法到html。
当然园长的文章中提到,只要修改tomcat的conf中的web.xml,可以任意后缀解析。
3.shell隐藏
同样JavaWeb系列文章中提到了诸如jar包等隐藏方法。
这里我复现的是91ri的《利用 Java Binary Webshell 对抗静态检测》,里面提到在tomcat下通过修改Catalina的配置文件,来做jsp文件更改延迟,达到欺骗引擎一直返回给浏览器字节码的目的,进而将Webshell隐藏在字节码中,实现无shell jsp文件下的理论上的权限维持
当然,说是理论上,因为Tomcat只能延迟检测,不能完全去除检测,不知道对Tomcat框架理解深入的大牛们有没有方法完全规避检测~~
————————————————————