kkFileViews任意文件读取漏洞原理解析
在学习的过程中,了解到kkFileView存在任意文件读取、SSRF、文件上传漏洞。
为了更深刻的理解其原理,我从git拉取了项目,由于该漏洞在最新版本已经修复,所以这只是历史版本中存在的。
写这篇文章来提醒自己代码中容易出问题的地方。
环境准备
来到kkFileView官网,发现支持Gitee,直接进去用git clone下来
git clone https://gitee.com/kekingcn/file-online-preview.git
闲话少叙,直接开始看源代码。
1.任意文件包含漏洞
根据其他文章介绍,版本<=3.6.0存在此漏洞。加上这个连接就可以读取服务器上的文件//getCorsFile?urlPath=file:///etc/passwd
git tag # 查看所有标签
git checkout v3.6.0 # 切换分支
然后用idea打开这个项目,直接搜索到controller中的getCorsFile接口,源代码如下。可以看到作者本意是为了解决跨域问题,没有想到被利用读取任意文件。
在代码中也没有任何的过滤,才会导致任意文件读取漏洞。
byte[] bytes = NetUtil.downloadBytes(url.toString());罪魁祸首在于这一行代码,没有对文件路径做出任何限制。
/**
* 根据url获取文件内容
* 当pdfjs读取存在跨域问题的文件时将通过此接口读取
*
* @param urlPath url
* @param response response
*/
@RequestMapping(value = "/getCorsFile", method = RequestMethod.GET)
public void getCorsFile(String urlPath, HttpServletResponse response) {
logger.info("下载跨域pdf文件url:{}", urlPath);
try {
URL url = WebUtils.normalizedURL(urlPath);
byte[] bytes = NetUtil.downloadBytes(url.toString());
IOUtils.write(bytes, response.getOutputStream());
} catch (IOException | GalimatiasParseException e) {
logger.error("下载跨域pdf文件异常,url:{}", urlPath, e);
}
}
本地复现一下,和预期完全一样:
修复方法
现在我切换到下一个版本,来看看作者是如何修复的。
没想到下一个版本直接v4.0.0了
git checkout v4.0.0
我发现4.0.0并没有修复,4.2.0才开始修复这个漏洞。代码如下,可以看到加了非常多的过滤。
最新版本我就不贴出来了,反正有更多的过滤。
@GetMapping("/getCorsFile")
public void getCorsFile(String urlPath, HttpServletResponse response) throws IOException {
try {
urlPath = WebUtils.decodeUrl(urlPath);
} catch (Exception ex) {
logger.error(String.format(BASE64_DECODE_ERROR_MSG, urlPath),ex);
return;
}
HttpURLConnection urlcon;
InputStream inputStream = null;
if (urlPath.toLowerCase().startsWith("file:") || urlPath.toLowerCase().startsWith("file%3")) {
logger.info("读取跨域文件异常,可能存在非法访问,urlPath:{}", urlPath);
return;
}
logger.info("下载跨域pdf文件url:{}", urlPath);
if (!urlPath.toLowerCase().startsWith("ftp:")){
try {
URL url = WebUtils.normalizedURL(urlPath);
urlcon=(HttpURLConnection)url.openConnection();
urlcon.setConnectTimeout(30000);
urlcon.setReadTimeout(30000);
urlcon.setInstanceFollowRedirects(false);
if (urlcon.getResponseCode() == 302 || urlcon.getResponseCode() == 301) {
urlcon.disconnect();
url =new URL(urlcon.getHeaderField("Location"));
urlcon=(HttpURLConnection)url.openConnection();
}
if (urlcon.getResponseCode() == 404 || urlcon.getResponseCode() == 403 || urlcon.getResponseCode() == 500 ) {
logger.error("读取跨域文件异常,url:{}", urlPath);
return ;
} else {
if(urlPath.contains( ".svg")) {
response.setContentType("image/svg+xml");
}
inputStream=(url).openStream();
IOUtils.copy(inputStream, response.getOutputStream());
urlcon.disconnect();
}
} catch (IOException | GalimatiasParseException e) {
logger.error("读取跨域文件异常,url:{}", urlPath);
return ;
} finally {
IOUtils.closeQuietly(inputStream);
}
} else {
try {
URL url = WebUtils.normalizedURL(urlPath);
if(urlPath.contains(".svg")) {
response.setContentType("image/svg+xml");
}
inputStream = (url).openStream();
IOUtils.copy(inputStream, response.getOutputStream());
} catch (IOException | GalimatiasParseException e) {
logger.error("读取跨域文件异常,url:{}", urlPath);
return ;
} finally {
IOUtils.closeQuietly(inputStream);
}
}
}
引用
https://www.cnblogs.com/xbbth/p/17446987.html