原创记一次任意文件下载绕过过程

中科助力健康中国 http://m.39.net/disease/a_6169820.html

挖掘过程

一般来说,在文件下载/查看功能处,当文件名参数可控,且系统未对参数进行过滤或者过滤不全时,就可以实现下载服务器上的任何文件,产生任意文件下载漏洞。一般可以通过相关的业务关键字(例如download、filename等)或者相关的操作类(例如InputStam、File等)快速定位相关的模块进行审计挖掘。前段时间审计某项目时发现一处任意文件下载的绕过。系统基于Springboot进行开发,首先是发现任意文件下载接口:

Value("{CERTIFICATE.PATH}")privateStringuploadPath;

GetMapping("/downloadFile.do")publicvoiduploadFileDownload(HttpServletResponsesponse,StringfileName)throwsIOException{ServletOutputStamoutputStam=sponse.getOutputStam();StringcontentType="application/octet-stam";sponse.setContentType(contentType);StringfilePath=uploadPath+fileName;System.out.println(filePath);outputStam.write(FileUtils.adFileToByteArray(filePath));outputStam.flush();outputStam.close();}

相关的接口是下载用户上传的凭证材料的,整个下载过程从配置文件中获取uploadPath,然后与用户输入的fileName进行拼接,最后调用工具类FileUtils的adFileToByteArray()方法进行下载。查看工具类adFileToByteArray()方法的具体实现:

publicstaticbyte[]adFileToByteArray(Stringpath){StringBufferbuf=newStringBuffer();try{Filefile=newFile(path);BuffedReaderbr=newBuffedReader(newInputStamReader(newFileInputStam(file),"utf-8"));Stringrow;while((row=br.adLine())!=null){buf.append(row);}}catch(IOExceptione){e.printStackTrace();}if(buf.length()==0){buf.append("");}turnbuf.toString().getBytes();}

可以看到相关的工具类方法直接将传入的地址进行文件内容读取,封装后进行返回,整个文件下载的过程是没有过滤掉"./"、"…/"、"/"等特殊字符,防止用户进行目录回溯的,同时也没有限制下载目录。初步判断是存在任意文件下载风险的。

这里尝试去测试环境访问对应的下载接口进行复现,发现失败了:

回去查看源代码,发现有一个安全过滤器SecurityFilter,里面针对相关的敏感字符进行了过滤处理。上述任意文件下载接口无法利用很大原因是过滤器Filter的防护导致的,下面来看看能不能绕过过滤器进行利用。

绕过分析

一般来说审计过滤器缺陷主要分以下几个点:

获取数据的方式是否覆盖全面

过滤的规则内容

过滤器的顺序

首先是定位过滤器了,这里是通过注解的方式进行过滤器注册的:

WebFilter(filterName="SecurityFilter",urlPatterns="/*")publicclassSecurityFilterimplementsFilter{FilterConfigfilterConfig=null;

Overridepublicvoiddestroy(){this.filterConfig=null;}

OverridepublicvoiddoFilter(ServletRequestquest,ServletResponsesponse,FilterChainchain)throwsIOException,ServletException{chain.doFilter(newXssHttpServletRequestWrapper((HttpServletRequest)quest),sponse);}

Overridepublicvoidinit(FilterConfigfilterConfig)throwsServletException{this.filterConfig=filterConfig;}}

具体实现是通过HttpServletRequestWrapper来获取quest的内容,然后通过重写getParameterValues()、getParameter()方法进行处理的。

publicclassXssHttpServletRequestWrapperextendsHttpServletRequestWrapper{publicXssHttpServletRequestWrapper(HttpServletRequestservletRequest){super(servletRequest);}

OverridepublicString[]getParameterValues(Stringparameter){String[]values=super.getParameterValues(parameter);if(values==null){turnnull;}intcount=values.length;String[]encodedValues=newString[count];for(inti=0;icount;i++){System.out.println("befo:"+values[i]);encodedValues[i]=JsoupUtil.clean(cleanAnyFileRead((String)values[i]));System.out.println("after:"+encodedValues[i]);}turnencodedValues;}

这里存在第一种绕过方式,因为对于multipart/form-data这种数据类型上述过滤器明显也是无法获取quest提交内容的,那么可以尝试转换multipart的方式进行提交,尝试绕过相关的过滤器:

比较可惜的是,这里对任意文件下载接口并不适用,因为其是用

GetMapping("/downloadFile.do")进行注解的,限制了请求方法为GET。获取数据的方式虽然不全面,但是仅针对/downloadFile.do接口来说的话是足够的了,那么继续往下看相关的过滤规则是否存在缺陷。

这里存在两层过滤,一层是通过cleanAnyFileRead()过滤任意文件上传的敏感输入的,第二层是调用工具类JsoupUtil.clean()方法进行XSS输入过滤。

OverridepublicStringgetParameter(Stringparameter){Stringvalue=super.getParameter(parameter);if(value==null){turnnull;}turnJsoupUtil.clean(cleanAnyFileRead((String)value));}

首先看任意文件下载的过滤,这里是简单的使用正则进行过滤,出现一次或者多次的.且以/结尾的内容都进行过滤,例如…/、./等,这也就解释了为什么前面测试时输入…/…/…/etc/passwd失败了,过滤后返回的内容为/etc/passwd,拼接uploadPath后不存在相关的文件,路径穿越失败:

privatestaticStringcleanAnyFileRead(Stringvalue){value=value.placeAll("\\.+/","");turnvalue;}

继续往下看xss的过滤,JsoupUtil.clean()方法的具体实现如下:

publicclassJsoupUtil{/***配置白名单为基本使用的标签再加上img标签*/privatestaticfinalWhitelistWHITELIST=Whitelist.basicWithImages();/***配置过滤的参数,不对代码格式化*/privatestaticfinalDocument.OutputSettingsOUTPUT_SETTINGS=newDocument.OutputSettings().pttyPrint(false);static{//富文本编辑时一些样式是使用style来进行实现的比如红色字体style="color:d;"所以需要给所有标签添加style属性WHITELIST.addAttributes(":all","style");}publicstaticStringclean(Stringcontent){turnJsoup.clean(content,"",WHITELIST,OUTPUT_SETTINGS);}}

比较简单粗暴,直接使用jsoup组件进行了过滤。这里简单介绍下jsoup的api:使用Jsoup的clean方法进行清除HTML标签操作,该方法会清除在你所指定的白名单whitelist中的所有HTML标签。默认的Jsoup提供了5种Whitelist的API

none()该API会清除所有HTML标签,仅保留文本节点。impleTest()该API仅会保留b,em,i,strong,u标签,除此之外的所有HTML标签都会被清除。basic()该API会保留a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol,p,p,q,small,span,strike,strong,sub,sup,u,ul和其适当的属性标签,除此之外的所有HTML标签都会被清除,且该API不允许出现图片(imgtag)。另外该API中允许出现的超链接中可以允许其指定


转载请注明:http://www.aierlanlan.com/rzdk/3363.html