0x01. 前言
時(shí)隔應(yīng)該快一年了吧,具體ghost這個(gè)漏洞出來我也忘記了,由于我最近無聊,然后想起我使用的Tomcat有沒有漏洞,于是我就來試了試,順便分析一下這段已經(jīng)時(shí)隔許久的漏洞,依稀記得上次的文章是簡(jiǎn)單的復(fù)現(xiàn)~~ 反正是閑的無聊

0x02. 環(huán)境部署
idea2020.2 + tomcat7.0.99+jdk1.8
具體參考這篇文章,idea導(dǎo)入Tomcat源碼:
環(huán)境與exp打包好,在附件中~,收1金幣應(yīng)該問題不大,我想社區(qū)幣去抽華子
https://blog.csdn.net/u013268035/article/details/81349341
https://www.cnblogs.com/r00tuser/p/12343153.html
0x03.漏洞的基礎(chǔ)
Tomcat部署的時(shí)候會(huì)有兩個(gè)重要的文件


而Tomcat在 server.xml中配置了兩種連接器。其中包含的是AJP Connector和HTTP Connector
AJP Connector說明啟用了8080和8009端口,這時(shí)候我們可以看一下

而相對(duì)于,8080我們是可以訪問的

也就是說,8080是負(fù)責(zé)Http協(xié)議,8009負(fù)責(zé)接收ajp協(xié)議;
提問1:AJP協(xié)議和HTTP協(xié)議有什么區(qū)別嗎?
HTTP協(xié)議:負(fù)責(zé)接收建立HTTP數(shù)據(jù)包,成為一個(gè)web服務(wù)器,處理HTTP協(xié)議的同時(shí)還額外可處理Servlet和jsp
AJP協(xié)議: 負(fù)責(zé)和其他的HTTP服務(wù)器建立連接, 通過AJP協(xié)議和另一個(gè)web容器進(jìn)行交互
Web用戶訪問Tomcat服務(wù)器的兩種方式

而一個(gè)Tomcat就是一個(gè)server,其中包含多個(gè)service;而每個(gè)service由Connector、Container、Jsp引擎、日志等組件構(gòu)成,造成漏洞的關(guān)鍵地方是Connector、Container
Connector上面已經(jīng)說過分別是AJP Connector和HTTP Connector ,是用來接受客戶端的請(qǐng)求,請(qǐng)求中的數(shù)據(jù)包在被Connector解析后就會(huì)由Container處理。這個(gè)過程大致如下圖:

一次請(qǐng)求的處理可以劃分為Connector及Container進(jìn)行處理,經(jīng)歷的過程大致如下:
- 一個(gè)TCP/IP數(shù)據(jù)包發(fā)送到目標(biāo)服務(wù)器,被監(jiān)聽此端口的Tomcat獲取到。
- 處理這個(gè)Socket網(wǎng)絡(luò)連接,使用Processor解析及包裝成request和response對(duì)象,并傳遞給下一步處理。
- Engine來處理接下來的動(dòng)作,匹配虛擬主機(jī)Host、上下文Context、MApping Table中的servlet。
- Servlet調(diào)用相應(yīng)的方法(service/doGet/doPost…)進(jìn)行處理,并將結(jié)果逐級(jí)返回。小結(jié)
對(duì)于使用HTTP協(xié)議或AJP協(xié)議進(jìn)行訪問的請(qǐng)求來講,在解析包裝成為request和response對(duì)象之后的流程都是一樣的,主要的區(qū)別就是對(duì) socket流量的處理以及使用Processor進(jìn)行解析的過程的不同

也就是第二步的地方出現(xiàn)問題
0x04.源碼分析
通過第三步中,我們知道是Processor的解析過程不同,而提供這部分功能的接口,在
org.Apache.coyote.Processor,主要負(fù)責(zé)請(qǐng)求的預(yù)處理。

而此處AjpProcessor則是處理ajp協(xié)議的請(qǐng)求,并通過它將請(qǐng)求轉(zhuǎn)發(fā)給Adapter,針對(duì)不用的協(xié)議則具有不同的實(shí)現(xiàn)類。
我們從上面wirshark抓包可以看出

可以看出這邊是設(shè)置了三個(gè)莫名其妙的東西

通過exp源碼可以看到這邊是設(shè)置了三個(gè)域?qū)ο蟮闹?/p>

可以看出,我們這邊是執(zhí)行成功了,由于wirshark抓包沒有去截圖,又關(guān)閉了,所以就不截圖了
這邊可以假設(shè):請(qǐng)求參數(shù)時(shí)是寫死,也就是xxx.jsp文件,而jsp后綴等等原因,然后成功進(jìn)行了文件包含
那么我們的test.txt是怎么識(shí)別的?
我們繼續(xù)往下看,來解決種種疑惑

知道是這個(gè)prepareRequest()問題,我們f7跟進(jìn)去看看

進(jìn)入了該方法的內(nèi)部,我們繼續(xù)跟進(jìn)查看,這邊進(jìn)行判斷,獲取到了請(qǐng)求參數(shù)為GET

首先是一些解析數(shù)據(jù)包讀取字節(jié)的操作

while循環(huán)獲取,switch判斷


當(dāng)attributeCode=10時(shí),則進(jìn)入第一條分支,繼續(xù)f8進(jìn)去



是不是有了有種眼熟的感覺~~~ 其實(shí)就是往域?qū)ο笾写嬷?/p>
由于if都不滿足,直接進(jìn)入了最后這個(gè)比進(jìn)的else分支

最后進(jìn)入第二個(gè)步驟的最后一小步,f8繼續(xù)下一步走,在預(yù)處理完了request headers之后,在adapter里面處理request,然后調(diào)用Adapter將請(qǐng)求交給Container處理


進(jìn)入service方法,然后f8一直跟進(jìn)

直到跟進(jìn)此處,請(qǐng)求會(huì)發(fā)送到對(duì)應(yīng)的servlet,我們請(qǐng)求的是一個(gè)jsp文件,根據(jù)tomcat的默認(rèn)web.xml文件

通過上面假設(shè),tomcat默認(rèn)將jsp/jspx結(jié)尾的請(qǐng)求交給
org.apache.jasper.servlet.JspServlet處理,它的service()方法如下:

而jspUri等于null,滿足條件,則進(jìn)入該if條件分支

由于
JAVAx.servlet.include.servlet_path可控制,通過getAttribute去域?qū)ο笾蝎@取屬性名為
javax.servlet.include.servlet_path的值,得到值為 test.txt

此時(shí),jspUri的值為/test.txt

pathInfo的值此時(shí)為空

最后一直到下面,傳入serviceJspFile方法中

繼續(xù)跟進(jìn),會(huì)先判斷文件是否存在,如果存在,隨后才會(huì)初始化wrapper,最后調(diào)用JspServletWrapper的service方法來解析,從而導(dǎo)致本地文件包含

參考
https://gitee.com/wdragondragon/javasec/blob/master/tomcat%20ajp%E4%BB%BB%E6%84%8F%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90.md
https://www.cnblogs.com/r00tuser/p/12343153.html
https://xz.aliyun.com/t/7325#toc-6
https://zhishihezi.net/b/5d644b6f81cbc9e40460fe7eea3c7925#