tomcat temp 大量 upload 文件_原创 | 浅谈URI中的任意文件下载
文件下載是比較常見的業務。常見的接口格式為/download?fileName=xxx.png,整個過程若沒過濾目錄穿越符號…/或者未對下載的路徑進行處理限制。當傳入的filename參數為../../etc/passwd即可穿越路徑達到任意文件下載的效果。
有些接口在嘗試獲取某一文件時,路徑是/file/test.jpg,通過解析URI中的內容來得到對應的文件名test.jpg,然后完成相關的下載操作。此時如果嘗試訪問/file/../../test.jpg,相關的中間件或者開發框架在解析路由時會做相關的處理。那么此時是否可以進行目錄穿越的實際利用呢?
以下是實際項目中遇到的URI中任意文件下載實例:
URI中任意文件下載實例
自定義Servlet
相關業務為用戶上傳文件的下載,主要通過Servlet進行交互,以下是相關的實現。
servlet的具體映射:
<servlet-mapping> <servlet-name>UserfilesDownloadServletservlet-name> <url-pattern>/userfiles/*url-pattern>servlet-mapping>下載業務具體實現如下:
public class UserfilesDownloadServlet extends HttpServlet { private static final long serialVersionUID = 1L; private Logger logger = LoggerFactory.getLogger(getClass()); public void fileOutputStream(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String filepath = req.getRequestURI(); int index = filepath.indexOf(Global.USERFILES_BASE_URL); if(index >= 0) { filepath = filepath.substring(index + Global.USERFILES_BASE_URL.length()); } try { filepath = UriUtils.decode(filepath, "UTF-8"); } catch (UnsupportedEncodingException e1) { logger.error(String.format("解釋文件路徑失敗,URL地址為%s", filepath), e1); } File file = new File(Global.getUserfilesBaseDir() + Global.USERFILES_BASE_URL + filepath); try { FileCopyUtils.copy(new FileInputStream(file), resp.getOutputStream()); resp.setHeader("Content-Type", "application/octet-stream"); return; } catch (FileNotFoundException e) { req.setAttribute("exception", new FileNotFoundException("請求的文件不存在")); req.getRequestDispatcher("/WEB-INF/views/error/404.jsp").forward(req, resp); } }......}這里使用了springframework的FileCopyUtils.copy()
方法,具體實現如下,主要是對File對象進行相應的處理,
將讀取的文件內容復制到response內容中:
public static int copy(File in, File out) throws IOException { Assert.notNull(in, "No input File specified"); Assert.notNull(out, "No output File specified"); return copy((InputStream)(new BufferedInputStream(new FileInputStream(in))), (OutputStream)(new BufferedOutputStream(new FileOutputStream(out)))); }最后通過設置response header返回文件內容,具體效果如下:其中下載的文件名filepath是通過
req.getRequestURI()來獲取的。
該方法是不會對URI中的../或者;等特殊字符進行規范化
處理的。同時在獲取到對應的filepath后直接進行路徑
拼接然后進行文件讀取,整個過程未限定下載的文件目錄
范圍,也并未過濾../和/等敏感關鍵字,存在任意文件下
載風險。但是這里場景比較特殊,相關參數的獲取是在URI中獲取的,能否深入利用還有待商榷。
當前項目的upload目錄位置如下:
這里嘗試訪問../WEB-INF/web.xml(回到upload上級
目錄WebContent),成功完成目錄穿越讀取到對應的
web.xml配置信息:
中間件在進行解析時,會對URI中的../進行相關處理從
而得到相關的servlet,tomcat解析時已經對../進行
處理了處理,上面的訪問方式其實跟直接訪問
/userfiles/WEB-INF/web.xml是一樣的:
若此時如果想讀取/etc/passwd,就需要寫入更多的目錄穿越符../,此時tomcat處理完../后已經不在/userfiles/*這個servlet映射范圍內了,那么此時
會拋出相關的異常,并不能進一步獲取更多的敏感信息。
嘗試擴大漏洞的危害,讀取更多的敏感文件。首先要讓
tomcat不處理../../,filepath = UriUtils.decode(filepath, "UTF-8");嘗試進行編碼請求,發現觸發400 Invalid URI錯誤,因為tomcat會對URI路徑信息進行解碼并進行檢測,當遇到斜杠的URL(即%2F)時,出于安全的考慮會返回400狀態碼:else if (metaChar == '%') { char res = (char)Integer.parseInt(str .substring(strPos + 1, strPos + 3), 16); if ((noSlash) && (res == '/')) { throw new IllegalArgumentException(sm.getString("uDecoder.noSlash")); } dec.append(res); strPos += 3; } } return dec.toString();
在URL中有一個保留字符分號;,主要作為參數分隔符進行使用,有時候是請求中傳遞的參數太多了,所以使用分號;將參數對(key=value)連接起來作為一個請求參數進行傳遞。
tomcat在對;進行處理時,同樣的也會對;進行截斷并當成參數處理,在URI編碼的基礎上加上;再次訪問,經過一系列處理獲取到的路徑為/upload/;/../../../../../../../../etc/passwd,此時返回的是404狀態碼,應該是在進行文件讀取的時候找不到名為;的目錄,觸發了FileNotFoundException異常:
try { FileCopyUtils.copy(new FileInputStream(file), resp.getOutputStream()); resp.setHeader("Content-Type", "application/octet-stream"); return; } catch (FileNotFoundException e) { req.setAttribute("exception", new FileNotFoundException("請求的文件不存在")); req.getRequestDispatcher("/WEB-INF/views/error/404.jsp").forward(req, resp); }剛好結合網站的其他業務可以進行目錄創建,在upload下創建名為;的目錄名,此時訪問/userfiles/upload/;%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc%2fpasswd即可獲取到相關的敏感文件:@PathVariable注解
@PathVariable注解是spring3.0的一個新功能,可以接收請求路徑中占位符的值并將URL中占位符參數{xxx} 綁定到控制器處理方法的入參中。該功能在SpringMVC向REST 、目標挺進發展過程中具有十分重要的意義。例如下面的例子:
- 獲取id = 1的訂單信息
除此之外,@PathVariable同樣可以用于實現文件的下載,例如下面的例子:
@RequestMapping("/file/get/{filename:.+}") public ResponseEntity<byte[]> download(HttpServletRequest request, @PathVariable(value = "filename") String fileName) throws IOException { String uploadRoot = "/var/work/download"; File file = new File(uploadRoot + URLDecoder.decode(File.separator + fileName, "UTF-8")); byte[] body = null; InputStream is = new FileInputStream(file); body = new byte[is.available()]; is.read(body); HttpHeaders headers = new HttpHeaders(); headers.add("Content-Disposition", "attchement;filename=" + file.getName()); HttpStatus statusCode = HttpStatus.OK; ResponseEntity<byte[]> entity = new ResponseEntity<>(body, headers, statusCode); return entity; }具體效果如下,例如這里嘗試獲取上傳的test.png圖片:這里將URL中占位符參數filename跟初始路徑/var/work/download進行拼接,然后下載對應的文件。這里對應的文件名用戶可控,未限定下載的文件目錄范圍,同時并未過濾../和/等敏感關鍵字,存在任意文件下載風險。
但是這里參數的位置在URI中,能否利用還需要進一步探究。
正常來說,直接使用../../../嘗試目錄穿越即可下載/etc/passwd等敏感文件,實際上中間件在進行解析時,會對URI中的../行相關處理從而得到相關的servlet,以tomcat為例,實際上解析時已經對../進行處理了,占位符參數filename并不能獲取到../來進行目錄穿越。這里因為處理后URI為/file/etc/passwd沒找到對應的映射,所以返回了404狀態碼:
同樣的,嘗試對/進行URL編碼,tomcat源碼會對URI路徑信息進行解碼并進行檢測,當遇到斜杠的URL(即%2F)時,出于安全的考慮會返回400狀態碼(拋出Invalid URI: noSlash異常):
tomcat源碼中相關處理流程如下:else if (metaChar == '%') { char res = (char)Integer.parseInt(str .substring(strPos + 1, strPos + 3), 16); if ((noSlash) && (res == '/')) { throw new IllegalArgumentException(sm.getString("uDecoder.noSlash")); } dec.append(res); strPos += 3; } } return dec.toString();
這里可以使用雙重URL編碼繞過中間件的處理,對/進行URL雙重編碼后返回404狀態碼,說明已經繞過noSlash異常了:
中間件后就是Spring的處理過程,查看Spring的解析過程,主要通過getPathWithinServletMapping方法獲取路由:
public String getPathWithinServletMapping(HttpServletRequest request) { String pathWithinApp = this.getPathWithinApplication(request); String servletPath = this.getServletPath(request); String sanitizedPathWithinApp = this.getSanitizedPath(pathWithinApp);getPathWithinApplication方法中會使用 getRequestUri 來獲取對應路由:public String getRequestUri(HttpServletRequest request) { String uri =(String)request.getAttribute("javax.servlet.include.request_uri"); if (uri == null) { uri = request.getRequestURI(); } return this.decodeAndCleanUriString(request, uri); }隨即在decodeAndCleanUriString對URI進行格式化處理,首先對分號進行處理,然后進行URI解碼,最后進行返回:
private String decodeAndCleanUriString(HttpServletRequest request, String uri){ uri = this.removeSemicolonContent(uri); uri = this.decodeRequestString(request, uri); uri = this.getSanitizedPath(uri); return uri;}也就是說,Spring本身會對URI進行一次解碼處理。例如/file/get/..%252fetc/passwd
經過tomcat+spring處理后會變成
/file/get%2fetc/passwd。
再回到案例代碼,這里應該是考慮到了中文傳輸的問題,在前端用js對URL進行編碼后再發送請求,這時候開發可能忽略了Spring自身的一次解碼操作,在對應的接口再次進行了一次解碼:File?file?=?new?File(uploadRoot?+?URLDecoder.decode(File.separator?+?fileName,?"UTF-8"));那么也就是說使用雙重URL編碼的方式處理../進行請求,即可在Spring以及接口自身的URLDecode獲得漏洞利用需要的../../,從而成功進行目錄穿越讀取到/etc/passwd內容:
拓展延伸
因為涉及到URI部分的解析,參與的有中間件、spring、人工的解碼等一系列處理。那么如果在上面案例的基礎上集成shiro框架。那相關的利用poc又是如何呢?
案例一的;可能需要編碼成%3b。這里就有點CVE-2020-13933的味道了,但是高版本shiro對編碼的;進行了處理,怎么繼續利用還是值得思考的。點分享點收藏點點贊點在看
總結
以上是生活随笔為你收集整理的tomcat temp 大量 upload 文件_原创 | 浅谈URI中的任意文件下载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python time 语句_pytho
- 下一篇: 笔记本屏幕出现横条纹_笔记本支架+拓展坞