简单理解成http的二进制优化版。
在Container部分,一个Host代表一个虚拟主机,一个Context代码一套web程序,一个Wrapper代表一个serlvlet。一个tomcat可以有多个Host,一个Host可以有多个Context,一个Context往往有多个Wrapper。
在Conector部分Endpoint的Acceptor监听连接,Handler用于处理接收到的Socket,在内部调用Processor进行处理。processor把信息读取出来并设置进request中对象最后交给Adaptor,Adapter将请求适配到Servlet容器进行具体的处理。
tomcat 7.0.96
<?xml version="1.0" encoding="ISO-8859-1"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <display-name>Welcome to Tomcat</display-name> <description> Welcome to Tomcat </description> </web-app>
代码跟踪从handler调用processor的process开始。
process里面有两部很重要一个是prepareRequest,另一个是this.adapter.service。先来看prepareRequest。顾名思义,这个函数的作用是把ajp的二进制版的http数据读取(请求方法,请求路径,http头等信息,不每处都截图了)并设置进request对象,然后设置一些对header和cookies长度或数量做一些限制。
接下来会读取一个叫attributeCode的东西,payload有意把它设置成了10。这样他会把我们在payload中构造的attributes设置进request对象的attribute,后面任意读文件或者包含祸起就是从这里开始的。
file_path = "/WEB-INF/web.xml" attributes = [ {"name": "req_attribute", "value": ("javax.servlet.include.request_uri", "/",)}, {"name": "req_attribute", "value": ("javax.servlet.include.path_info", file_path,)}, {"name": "req_attribute", "value": ("javax.servlet.include.servlet_path", "/",)}, ]
在adapter.service中会在postParseRequest中对url进行处理及合法性检查(这里面可能涉及到对url权限校验的问题以后有机会再聊)。如果合法的会进入下面的invoke。
跟进invoke后会发现是一连串疯狂的invoke,这里取几个有代表性的截图
StandardEngineValve invoke
StandardHostValve invoke
StandardContextValve invoke
最后进入StandardWrapperValve的invoke
wrapper.allocate会返回这次请求对应的servlet,因为我们访问路径是/,所以默认对应index.jsp,所以由jspServlet来处理。
接下来进入过滤器链
doFilter内部调用internalDoFilter
在internalDoFilter中会遍历所有注册过的filter然后进行过滤。遍历完成后会调用对应servlet的services方法。
这里会把payload中的 javax.servlet.include.request_uri javax.servlet.include.path_info提取并拼接到一起形成jspUri。
这里jspUri会被传入,正常情况下这里传入的是jsp,tomcat会把jsp转为servlet,但是因为我们传入的路径是是非jsp文件不符合语法所以这部分原样输出。因此另一种使用方法也呼之欲出,如果有一个上传洞可以把文件上传到webapp目录下(不管什么后缀)那么我们可以包含这个文件达到rce效果(和php很像)。
补充,只能包含webapp下面的东西。在读取之前会对传入的路径进行格式化,并在格式化后强制加/,无法../跳出。