美文网首页
Tomcat CVE-2017-12615 补丁bypass

Tomcat CVE-2017-12615 补丁bypass

作者: 白里个白 | 来源:发表于2017-10-14 08:06 被阅读0次

    分析

    补丁原理

    当补丁打完之后,我们传入文件的时候,程序做了这样的处理

    protected File file(String name, boolean mustExist) {
        File file = new File(base, name);
        return validate(file, mustExist, absoluteBase);
    }
    

    调用了validate函数

    protected File validate(File file, boolean mustExist, String absoluteBase) {
    
        if (!mustExist || file.exists() && file.canRead()) { // !mustExist = true,进入 if
            ...
            try {
                canPath = file.getCanonicalPath(); 
            } catch (IOException e) {
    
            }
            ...
            if ((absoluteBase.length() < absPath.length())
                && (absoluteBase.length() < canPath.length())) {
                ...
                // 判断处理后的路径和传入的路径参数是否相等,由于 canPath 把出入的路径参数的空格去掉了,导致canPath和之前的路径不同,return null
                if (!canPath.equals(absPath))
                    return null;
            }
        } else {
            return null;
        }
    

    也就是,如果我们传入jsp[空格] 这样的文件,就会使用file.getCanonicalPath()来进行格式规整,下面的条件就无法通过,所以无法创建文件。

    突破

    我们进入file.getCanonicalPath()函数

    public String getCanonicalPath() throws IOException {
        if (isInvalid()) {
            throw new IOException("Invalid file path");
        }
        return fs.canonicalize(fs.resolve(this));
    }
    

    这里调用了fs的canonicalize函数,fs是对WinNTFileSystem对象的实例,我们进WinNTFileSystem看看,发现是Win32FileSystem的子类。我们主要看canonicalize函数,进入Win32FileSystem

    public String canonicalize(String path) throws IOException {
            // If path is a drive letter only then skip canonicalization
            //用处不大
            int len = path.length();
            if ((len == 2) &&
                (isLetter(path.charAt(0))) &&
                (path.charAt(1) == ':')) {
                char c = path.charAt(0);
                if ((c >= 'A') && (c <= 'Z'))
                    return path;
                return "" + ((char) (c-32)) + ':';
            } else if ((len == 3) &&
                       (isLetter(path.charAt(0))) &&
                       (path.charAt(1) == ':') &&
                       (path.charAt(2) == '\\')) {
                char c = path.charAt(0);
                if ((c >= 'A') && (c <= 'Z'))
                    return path;
                return "" + ((char) (c-32)) + ':' + '\\';
            }
            //用处不大
            if (!useCanonCaches) {
                return canonicalize0(path);
            } else {
                String res = cache.get(path);
                if (res == null) {
                    String dir = null;
                    String resDir = null;
                    if (useCanonPrefixCache) {
                        dir = parentOrNull(path);
                        if (dir != null) {
                            resDir = prefixCache.get(dir);
                            if (resDir != null) {
                                // Hit only in prefix cache; full path is canonical,
                                // but we need to get the canonical name of the file
                                // in this directory to get the appropriate capitalization
                                String filename = path.substring(1 + dir.length());
                                res = canonicalizeWithPrefix(resDir, filename);
                                cache.put(dir + File.separatorChar + filename, res);
                            }
                        }
                    }
                    if (res == null) {
                        res = canonicalize0(path);
                        cache.put(path, res);
                        if (useCanonPrefixCache && dir != null) {
                            resDir = parentOrNull(res);
                            if (resDir != null) {
                                File f = new File(res);
                                if (f.exists() && !f.isDirectory()) {
                                    prefixCache.put(dir, resDir);
                                }
                            }
                        }
                    }
                }
                return res;
            }
        }
    

    我们主要看核心代码,我进行简单梳理:
    首先,两个变量useCanonCachesuseCanonPrefixCache默认是等于ture的,这是CachesPrefixCache两个hashmap是否使用的配置变量,首先我们如果传入jsp[空格],那么cachehashmap里是不存在这个key对应的键值的。我们就进入了if里,那么parentOrNull函数在取得文件的路径之后,我们进入这一句resDir = prefixCache.get(dir);明显,prefixCache这个hashmap里是不存在我们的路径的,所以直接向下跳转调用res = canonicalize0(path);这个函数功能就是规范res,去掉了res中的空格。所以就回到上节说的为什么通不过了.
    那么突破从何入手呢?首先我们传入正常的123.txt文件,这里都是十分正常通过,和上面一样,到了这里

     if (res == null) {
                        res = canonicalize0(path);
                        cache.put(path, res);
                        ......
    if (f.exists() && !f.isDirectory()) {
                                    prefixCache.put(dir, resDir);
                                }
    

    程序将res作为path的键值存入了cache(重要),程序判断了文件是否存在,以及文件是否是路径格式,这里我们不满足文件存在的条件,也就没有任何动作。
    第二遍我们传入123.txt[空格],这里为什么要加空格呢,因为要过这个条件:

    String res = cache.get(path);
                if (res == null) {
    

    如果path相同,cache中已经存在了这个path对应的键值,就不会进入if了。我们使用123.txt[空格]便过了这一点,之后,就正常进入程序运行,但是在后面,因为canonicalize0处理掉了res的空格,所以当实例化file时,file.exists()的返回值变为ture了,那么进入了prefixCache.put(dir, resDir);,那么prefixCache里便存在了dir。
    第三次再次传入123.jsp[空格]时,正常走程序,当到了resDir = prefixCache.get(dir);的时候,这里由于第二次我们在prefixCache里传入了值,resDir就有值了,就会进入:

    if (resDir != null) {
                                String filename = path.substring(1 + dir.length());
                                res = canonicalizeWithPrefix(resDir, filename);
                                cache.put(dir + File.separatorChar + filename, res);
                            }
    

    这里是不会对文件整理的,就这样,我们绕过了之前的absoluteBase.length() < canPath.length(),文件就可以创建成功了

    相关文章

      网友评论

          本文标题:Tomcat CVE-2017-12615 补丁bypass

          本文链接:https://www.haomeiwen.com/subject/znqxuxtx.html