鉴权分析
通过web.xml配置文件查看过滤器,所以路由都会经过SessionControl_UserLogin过滤器,
进入SessionControl_UserLogin过滤器,查看doFilter方法
其中有这么一段代码,这里的逻辑是创建一个迭代器,如果当前请求URI不在其中,会从会话中获取用户认证信息进行校验。如果当前请求URI在其中,则跳过校验。
在前面有定义默认的列表
也就是后缀为以上的url,将不会需要校验session。
漏洞点分析
那么我们可以把目标放在后缀为以上的代码中,我们可以发现getuploadimage.jsp代码其中包含image.jsp可以绕过SessionControl_UserLogin过滤器的校验。
查看其中代码,发现这是通过传入的图片URL,从文件中读取图片数据,并通过响应输出流返回给客户端的代码。
String imageURL = request.getParameter("imageURL"); System.out.println(imageURL); if (!ValidateUtil.isNull(imageURL)) { try { String fileName = StringUtil.getFileName(imageURL); String fileType = StringUtil.getFileType(fileName, "unkown"); if ("gif".equals(fileType) || "png".equals(fileType) || "bmp".equals(fileType) || "jpg".equals(fileType) || "jpeg".equals(fileType)) {
} else {
return;
}
response.setCharacterEncoding("UTF-8");
response.resetBuffer();
response.setContentType("image/jpeg");
File uploadedFile = new File(imageURL);
DataInputStream is = new DataInputStream(new FileInputStream(uploadedFile));
DataOutputStream os = new DataOutputStream(response.getOutputStream());
byte[] readBytes = new byte[128];
int buffflag = -1;
while ((buffflag = is.read(readBytes)) > -1) {
os.write(readBytes, 0, buffflag);
}
os.close();
is.close();
os = null;
response.flushBuffer();
out.clear();
out = pageContext.pushBody();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
}
}
通过分析代码,我们可以发现,String imageURL = request.getParameter("imageURL");
imageURL是用户的请求,也就是我们可以控制。然后通过String fileName = StringUtil.getFileName(imageURL);
方法成为文件名。其中getFileName
从后获取文件名,然后将文件名丢进getFileType
方法,进行文件类型的获取与校验。
public static String getFileName(String fileName) { int pos = fileName.lastIndexOf("\"); return pos > 0 ? fileName.substring(pos + 1) : fileName; }
public static String getFileType(String fileName, String defaultType) {
String fileType = "";
if (fileName != null && fileName.length() > 0) {
int i = fileName.lastIndexOf(46);
if (i > -1 && i < fileName.length() - 1) {
fileType = fileName.substring(i + 1).toLowerCase();
}
}
if (!"pdf".equals(fileType) && !"doc".equals(fileType) && !"bmp".equals(fileType) && !"xls".equals(fileType) && !"exe".equals(fileType) && !"ppt".equals(fileType) && !"gif".equals(fileType) && !"html".equals(fileType) && !"xls".equals(fileType) && !"jpg".equals(fileType) && !"png".equals(fileType) && !"jpeg".equals(fileType) && !"xml".equals(fileType) && !"wav".equals(fileType) && !"wma".equals(fileType) && !"mp3".equals(fileType) && !"rar".equals(fileType) && !"txt".equals(fileType) && !"htm".equals(fileType) && !"zip".equals(fileType) && !"rm".equals(fileType) && !"swf".equals(fileType) && !"flv".equals(fileType) && !"docx".equals(fileType) && !"pptx".equals(fileType) && !"xlsx".equals(fileType)) {
fileType = defaultType;
}
return fileType;
}
这里我们可以发现对文件类型的判断十分严格,代码中会获取最后一个.的位置,然后进行转小写(防止大写绕过),然后进行判断,只要文件类型不在规定类型中就会被设置为defaultType也就是unkown类型。在前面的getuploadimage.jsp代码中存在判断语句。
if ("gif".equals(fileType) || "png".equals(fileType) || "bmp".equals(fileType) || "jpg".equals(fileType) || "jpeg".equals(fileType)) {
} else {
return;
}
如果得到fileType不为其中的类型将无法执行后面的File uploadedFile = new File(imageURL)
代码
,进行读文件的操作。那么这里是否存在方法使我们可以绕过对文件后缀名的判断,还不会影响到我们后续读文件的操作呢。在当前安装包中的jdk版本为1.7.0(最新版中已经变为jdk8并且没有Windows版本)。
而在JDK1.7.0_40之前的版本中是没有进行对�0进行检查,所以会造成00截断绕过。(也就是%00最终被解析cha(0),而在ASCII码0对应的为空字符,当在字符串中有空字符时会导致后面的字符被丢弃。)JDK1.7.0_40后新增检查
final boolean isInvalid(){ if(status == null){ status=(this.path.indexOf('u0000')<0)?PathStatus.CHECKED:PathStatus.INVALID; } return status == PathStatus.INVALID; }
从而达到getFileType文件后缀为图片,File(imageURL)读文件时图片后缀被丢弃。
我们便可以构造poc,只需要在想要读取的文件的后面加上%00图片文件名,就可以
修复建议
更换JDK版本为JDK1.7.0_40之后版本。
来源:https://xz.aliyun.com/ 感谢【For82】
原文始发于微信公众号(船山信安):由00截断造成的文件读取(CVE-2023-47473分析)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论