Apache Tomcat任意文件讀取漏洞和命令執行漏洞源碼分析(CVE-2020-1938)

Apache Tomcat任意文件讀取漏洞和命令執行漏洞源碼分析(CVE-2020-1938)

環境準備

在分析Apache Tomcat源碼漏洞之前,我們先需要使用Idea配置Tomcat源碼,請參考:https://blog.csdn.net/SouthWind0/article/details/105147406

漏洞分析

Tomcat在默認的conf/server.xml中配置了2個Connector,一個默認監聽8080端口處理HTTP請求,另外一個默認監聽8009端口處理AJP請求。

Tomcat在接收ajp請求的時候調用org.apache.coyote.ajp.AjpProcessor來處理ajp消息,prepareRequest()方法將ajp裏的內容取出,並設置成request對象的屬性。既然如此,我們先在調用prepareRequest()方法的地方下一個斷點。

跟進到Decode extra attributes這個位置,也就是獲取解析屬性和設置屬性的地方。可以看到它循環獲取數據後,又設置成request對象下面的三個Attribute屬性,這意味着我們可以控制這三個屬性。

javax.servlet.include.request_uri

javax.servlet.include.path_info

javax.servlet.include.servlet_path

繼續跟進,可以看到封裝成了對應的request之後,它將要接着走servlet的映射。

通過走不同的映射,產生了兩種類型的漏洞,如下所示。關於映射請參考,Tomcat的DefaultServlet和JspServlet一文,地址:

https://blog.csdn.net/SouthWind0/article/details/105147262。

(1)任意文件讀取

AJP請求:

forwardrequest 2 "HTTP/1.1" "/123.png" 127.0.0.1 127.0.0.1 porto 8009 false "Cookie:AAAA=BBBB","Accept-Encoding:identity" "javax.servlet.include.request_uri:/","javax.servlet.include.path_info:log/test.jsp","javax.servlet.include.servlet_path:/"

我們發送上面的AJP請求,因爲/123.png將走DefaultServlet,關鍵請求參數如下:

RequestUri:/123.png

javax.servlet.include.request_uri: /

javax.servlet.include.path_info: log/test.jsp

javax.servlet.include.servlet_path: /

Debug源碼如下:

接着調用容器來處理

在HttpServlet中調用doGet()方法

跟進,來到了org.apache.catalina.servlets.DefaultServlet的doGet()方法中,doGet()方法又調用serveResource()方法進行資源讀取操作。

跟進到serveResource()方法中,可以看到它使用getRelativePath()方法來獲取資源的相對路徑。

既然到了門口,我們去看看getRelativePath()方法,可以看到,由於我們的AJP請求設置javax.servlet.include.request_uri屬性值爲/不爲null,那麼資源的相對路徑構造如下:

= javax.servlet.include.path_info + javax.servlet.include.path_info

= / + log/test.jsp

= /log/test.jsp

返回來,我們已經通過getRelativePath()方法獲取資源的相對路徑,那麼接着就需要讀取資源了。接着往下走,可以看到通過getResources()方法就可以獲取到對應路徑的資源了。

值得一提的是,跟進這個方法,可以發現有趣的事情,如果路徑存在./或../則會返回null,這樣就解釋了爲什麼我們無法跳出webapps目錄來讀取文件了。

繼續往下走,最後資源對象的內容隨着resourceBody被寫入了ostream流對象中返回給客戶端。

成功讀取文件內容,我們請求的是/123.png,返回的是/log/test.jsp的內容。

(2)任意文件包含(代碼執行)

AJP請求:

forwardrequest 2 "HTTP/1.1" "/123.jsp" 127.0.0.1 127.0.0.1 porto 8009 false "Cookie:AAAA=BBBB","Accept-Encoding:identity" "javax.servlet.include.request_uri:/","javax.servlet.include.path_info:log/test.txt","javax.servlet.include.servlet_path:/"

我們發送上面的AJP請求,因爲/123.jsp將走JspServlet,關鍵請求參數如下:

RequestUri:/123.jsp

javax.servlet.include.request_uri: /

javax.servlet.include.path_info: log/test.txt

javax.servlet.include.servlet_path: /

Debug源碼如下:

接着調用容器來處理

在HttpServlet中調用service()方法

跟進,接着來到了org.apache.jasper.servlet.JspServlet的service()方法中,jspUri爲jsp文件的相對路徑,之後jspUri被傳入serviceJspFile()方法。

跟進serviceJspFile()方法,可以看到我們可以控制的jspUri被封裝成了一個JspServletWrapper,並添加到了Jsp運行上下文JspRuntimeContext中,最後wrapper.service()會編譯執行test.txt。這樣導致了test.txt被當作jsp文件編譯執行,代碼執行漏洞產生。

成功執行惡意代碼,我們訪問的是/123.jsp,返回的是把/log/test.txt當作jsp文件執行後的內容。

test.txt的文件內容:

注意

需要注意的是RequestUri:/,此時會走JspServlet;而RequestUri:/123,此時會走DefaultServlet。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章