<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    淺談 Springboot 中的文件上傳

    sugar2021-02-03 17:08:08

    引言

    在JavaWeb應用中,任意文件上傳一直是關注的重點,攻擊者通過上傳惡意jsp文件,可以獲取服務器權限。但是在Springboot框架對JSP解析存在一定的限制。

    Spring官方原文如下,大概意思是jsp對內嵌的容器的支持不太友好,推薦使用thymeleaf這類的模版引擎進行渲染。

    圖片

    那么針對Springboot應用,即使存在任意文件上傳缺陷,按照傳統的思路直接上傳jsp文件,也是無法達到理想的效果的。

    下面通過查看其具體的實現方式來看看有沒有相關的利用思路,同時在日常項目開發中應該注意些什么。

    Springboot文件上傳的實現

    首先看看在Springboot中如何實現文件上傳功能,在網上找了個教程,Controller的代碼如下,Spring會自動解析multipart/form-data請求,將multipart中的對象封裝到MultipartRequest對象中:

    @RequestMapping(value={"/uploadFile"},method={RequestMethod.POST})
    public String uploadFile(MultipartFile file,String type,HttpServletResponse
    response) throws
    Exception{
            String UPLOADED_FOLDER="/resource/upload/";
            if(!file.isEmpty()){
                    String
    path = UPLOADED_FOLDER + file.getOriginalFilename();
                    File
    targetFile = new
    File(path);
                    FileUtils.inputStreamToFile(file.getInputStream(),targetFile);
                            ......
                            ......
    }
    }

    大致是通過getOriginalFilename()方法獲取文件名,然后使用File對象創建對應的文件。接下來看看Springboot是如何解析multipart請求并封裝OriinalFilename的。

    SpringBoot在

    MultipartAutoConfiguration自動裝配了MultipartResolver來對multipart請求進行解析:

    @Configuration
    @ConditionalOnClass({ Servlet.class,StandardServletMultipartResolver.class,
            MultipartConfigElement.class})
    @ConditionalOnProperty(prefix \= "spring.http.multipart", name= "enabled", matchIfMissing =true)
    @EnableConfigurationProperties(MultipartProperties.class) public classMultipartAutoConfiguration { private final MultipartPropertiesmultipartProperties; public MultipartAutoConfiguration(MultipartPropertiesmultipartProperties) { this.multipartProperties =multipartProperties;
        }
        @Bean
        @ConditionalOnMissingBeanpublic MultipartConfigElement multipartConfigElement() { returnthis.multipartProperties.createMultipartConfig();
        }
        @Bean(name \=DispatcherServlet.MULTIPART\_RESOLVER\_BEAN\_NAME)
       @ConditionalOnMissingBean(MultipartResolver.class) publicStandardServletMultipartResolver multipartResolver() {
            StandardServletMultipartResolvermultipartResolver \= new StandardServletMultipartResolver();
           multipartResolver.setResolveLazily(this.multipartProperties.isResolveLazily());return multipartResolver;
       }
    }

    可以看到其默認裝配的解析器是org.springframework.web.multipart.support。StandardServletMultipartResolver。查看對應的實現。

    StandardServletMultipart Resolver的處理方式

    對應的spring-web組件版本為5.3.3。使用StandardServletMultipartResolver解析multipart請求的關鍵過程如下:

    關鍵multipart請求的解析方法parseRequest

    private void parseRequest(HttpServletRequestrequest)
      {
        try
        {
          Collection<Part>parts = request.getParts();
          this.multipartParameterNames = newLinkedHashSet(parts.size());
          MultiValueMap<String,MultipartFile> files = new LinkedMultiValueMap(parts.size());
          for (Part part : parts)
          {
            String headerValue =part.getHeader("Content-Disposition");
            ContentDisposition disposition =ContentDisposition.parse(headerValue);
            String filename =disposition.getFilename();
            if (filename != null)
            {
              if((filename.startsWith("=?")) && (filename.endsWith("?="))){
                filename =MimeDelegate.decode(filename);
              }
              files.add(part.getName(), newStandardMultipartFile(part, filename));
            }
            else
            {
             this.multipartParameterNames.add(part.getName());
            }
          }
          setMultipartFiles(files);
        }
        catch (Throwable ex)
        {
          handleParseFailure(ex);
        }
      }

    主要的解析方法在

    org.springframework.http.ContentDisposition的parse方法,在這里對相關的http內容進行了處理,獲取文件名的關鍵內容如下。

    如果傳入的multipart請求無法直接使用filename=解析出文件名,Spring還會使用content-disposition解析一次(使用filename*=解析文件名):

    public static ContentDisposition parse(StringcontentDisposition)
      {
        ......
        for (int i = 1; i < parts.size();i++)
        {
          String part =(String)parts.get(i);
          int eqIndex =part.indexOf('=');
          if (eqIndex!= -1)
          {
            String attribute =part.substring(0, eqIndex);
    
            String value =(part.startsWith("\"", eqIndex + 1)) &&(part.endsWith("\"")) ? part.substring(eqIndex + 2,part.length() - 1) : part.substring(eqIndex + 1);
            if(attribute.equals("name"))
            {
              name = value;
           }
            else if(attribute.equals("filename*"))
            {
              int idx1 =value.indexOf('\'');
              int idx2 = value.indexOf('\'',idx1 + 1);
              if ((idx1 != -1) &&(idx2 != -1))
              {
                charset =Charset.forName(value.substring(0, idx1).trim());
               Assert.isTrue((StandardCharsets.UTF_8.equals(charset)) ||(StandardCharsets.ISO_8859_1.equals(charset)), "Charset should be UTF-8 orISO-8859-1");
    
                filename =decodeFilename(value.substring(idx2 + 1), charset);
              }
              else
              {
                filename =decodeFilename(value, StandardCharsets.US_ASCII);
              }
            }
            else if((attribute.equals("filename")) && (filename ==null))
            {
              filename = value;
            }

    這里發現一個點,整個過程沒有對類似…/的路徑進行檢查/過濾,獲取文件名后會實例化StandardMultipartFile方便后續程序調用:

    private static classStandardMultipartFile
        implements MultipartFile,Serializable
      {
        ......
    
        public StringgetOriginalFilename()
        {
          return this.filename;
        }
        ......

    實例化方法同樣也沒有對原始上傳的filename進行檢查/過濾

    相關接口可以通過getOriginalFilename()方法獲得對應的上傳文件名,然后進行文件創建。

    未做安全處理的文件上傳

    由于獲取的fileName未進行安全處理,在使用File創建文件時,若路徑處path寫入…/…/穿越符號,是可以跨目錄新建文件的:

    File file = new File("path")

    淺談Springboot中的文件上傳

    那么也就是說即使Springboot對jsp存在一定的支持限制,在特定情況下那么可以嘗試上傳定時任務進行權限獲取。

    上傳文件名為…/…/…/…/…/…/…/…/…/var/spool/cron/root,結合前面百度到的demo成功反彈shell:

    淺談Springboot中的文件上傳

    進行了后綴安全檢查的文件上傳

    到這里針對Springboot任意文件上傳的缺陷利用已經有一些眉目了。這里發現一個有趣的點。

    針對任意文件上傳,在進行業務開發的時候,常常會對后綴進行相關的白名單檢查,如果上傳非法后綴,那么拒絕對應的業務請求,例如如下代碼:

    if(!file.isEmpty()){
            String Filename =
    file.getOriginalFilename();
            String suffix =
    originalFilename.substring(Filename.lastIndexOf("."));
            if(!".xlsx".equals(suffix)&&!".xls".equals(suffix)){
                    throw
    new Exception("非法請求,請導入excel文件");
            }
    byte[] bytes =
    file.getBytes();
            String path =ULOADED_FOLDER +
    Filename;
    }

    在進行文件上傳時進行了后綴檢查,如果不是xlsx或者xls后綴的話,拒絕請求。因為/etc/cron.d/目錄下的文件可以任意后綴命名,那么此時可以上傳文件名為“…/…/…/…/…/…/etc/cron.d/test.xls”繞過對應的安全檢查:

    淺談Springboot中的文件上傳

    淺談Springboot中的文件上傳

    上傳成功后,本地監聽端口等待定時任務執行,成功反彈 shell,獲取服務器權限:

    淺談Springboot中的文件上傳

    其他版本的spring-web組件也是大同小異,例如4版本會通過extractFilename()方法進行multipart請求的處理再封裝,同樣沒有處理目錄穿越符的問題:

    private String extractFilename(StringcontentDisposition, String key)
      {
        if (contentDisposition == null){
          return null;
        }
        int startIndex =contentDisposition.indexOf(key);
        if (startIndex == -1) {
          return null;
        }
        String filename =contentDisposition.substring(startIndex + key.length());
        if(filename.startsWith("\""))
        {
          int endIndex =filename.indexOf("\"", 1);
          if (endIndex != -1) {
            return filename.substring(1,endIndex);
          }
       }
        else
        {
          int endIndex =filename.indexOf(";");
         if (endIndex != -1) {
            return filename.substring(0,endIndex);
          }
        }
        return filename;
      }
    

    綜上,關于Springboot的文件上傳可以通過結合目錄遍歷的方式嘗試利用,當然也需要滿足一定的利用條件,例如對跨目錄具有寫權限、未重命名文件名等。

    同樣的SpringMVC也加載了默認的解析器,一般是CommonsMultipartResolver,查看其對應的處理方式進行對比。

    CommonsMultipartResolver的處理方式

    解析部分就不細看了,直接查看getOriginalFilename()

    https://github.com/spring-projects/spring-... 95-121行

    可以看到其針對linux和windows的情況對multipart請求中的原始文件名進行了截斷處理,防止了…/…/帶來的目錄穿越風險:

    淺談Springboot中的文件上傳

    結語

    上述的問題已經報告給了

    security@pivotal.io,官方回復如下:

    圖片

    因為某些原因暫不打算在

    StandardServletMultipartResolver上做更多的處理了。

    建議根據owasp提供的建議,在實現上傳業務時進行更多的安全檢查。

    https://cheatsheetseries.owasp.org/cheatsheets/File_Upload_Cheat_Sheet.html

    原創:tkswifty SecIN技術平臺
    原文鏈接:https://mp.weixin.qq.com/s/wPgdnyv57qBwkHV...

    文件上傳string
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    無意中看到ch1ng師傅的文章覺得很有趣,不得不感嘆師傅太厲害了,但我一看那長篇的函數總覺得會有更騷的東西,所幸還真的有,借此機會就發出來一探究竟,同時也不得不感慨下RFC文檔的妙處,當然本文針對的技術也僅僅只是在流量層面上waf的繞過。Pre很神奇對吧,當然這不是終點,接下來我們就來一探究竟。前置這里簡單說一下師傅的思路部署與處理上傳war的servlet是?
    這個標簽內 解析xml實體的位置也是如此0x02 lets xxe 1第一個位點就是我們剛才提到的地方我們只需要做的是在xml聲明處添加如下第一行代碼 在第二行標簽進行修改]>
    引言 在JavaWeb應用中,任意文件上傳一直是關注的重點,攻擊者通過上傳惡意jsp文件,可以獲取服務器權限。但是在Springboot框架對JSP解析存在一定的限制。 Spring官方原文如下,大概意思是jsp對內嵌的容器的支持不太...
    在Web系統中,允許用戶上傳文件作為一個基本功能是必不可少的,如論壇允許用戶上傳附件,多媒體網站允許用戶上傳圖片,視頻網站允許上傳頭像、視頻等。但如果不能正確地認識到上傳帶來的風險,不加防范,會給整個系統帶來毀滅性的災難。
    前言前幾天對自己學校進行的一次滲透測試,由于深信服過于變態,而且攔截會直接封ip,整個過程有點曲折期間進行了后綴名繞過,jspx命名空間繞過、獲取網站根目錄、base64五層編碼寫入shell等操作0x01 獲取網站接口主界面:上傳點:由于該應用是內嵌企業微信的套皮Html,所以我們首先用Burp Suite抓包獲取接口和cookie任意文件上傳:文件名強制命名為code+學號,后綴為最后一次點號出現之后的字母0x02 后綴名繞過代碼不限制后綴名,但是waf限制呀!
    記一次5000美金的文件上傳漏洞挖掘過程大家好,最有趣的功能之一是文件上傳文件上傳中的漏洞通常會導致您進入關鍵或高嚴重性,所以讓我們從我在bug bunting時遇到的這個場景開始假設我們的目標域是 target.com在尋找我們的目標時,我遇到了 edu.target.com 子域,該程序提供的服務是一個教學平臺,因為有不同類型的用戶,如學生和教師,旨在幫助學生學習與技術相關的主題,如軟件工程機器人等…
    Zoho ManageEngine Admanager Plus 任意文件上傳漏洞可GetShell。
    前言:滲透測試的時候往往會遇到盲注這類的繁雜的手工測試,所以需要編寫半自動化腳本去進行測試減少時間浪費并快速
    sugar
    暫無描述
      亚洲 欧美 自拍 唯美 另类