checkmarx - Как решить проблему обхода сохраненного абсолютного пути?

Checkmarx — версия 9.3.0 HF11

Я передаю значение env как путь к каталогу данных в файле докера, который используется на сервере dev/uat.

ENV DATA /app/data/

В локальном режиме, используя следующую переменную среды

ДАННЫЕ=C:\проекты\приложение\данные\

получитьКаталогДанных(ИмяМоегоКаталога); // MyDirectoryName присутствует в папке данных

public String getDataDirectory(String dirName)
{
    String path = System.getenv("DATA");
    if (path != null) {
        path = sanitizePathValue(path);
        path = encodePath(path);

        dirName = sanitizePathValue(dirName);
        if (!path.endsWith(File.separator)) {
            path = path + File.separator;
        } else if (!path.contains("data")) {
            throw new MyRuntimeException("Data Directory path is incorrect");
        }
    } else {
        return null;
    }

    File file = new File(dirName); // NOSONAR

    if (!file.isAbsolute()) {
        File tmp = new File(SecurityUtil.decodePath(path)); // NOSONAR

        if (!tmp.getAbsolutePath().endsWith(Character.toString(File.separatorChar))) {
            dirName = tmp.getAbsolutePath() + File.separatorChar + dirName;
        } else {
            dirName = tmp.getAbsolutePath() + dirName;
        }

    }

    return dirName;
}

public static String encodePath(String path) {
        try {
            return URLEncoder.encode(path, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.error("Exception while encoding path", e);
        }
        return "";
}

public static String validateAndNormalizePath(String path) {
        path = path.replaceAll("/../", "/");
        path = path.replaceAll("/%46%46/", "/");
        path = SecurityUtil.cleanIt(path);
        path = FilenameUtils.normalize(path); // normalize path
        return path;

    }

public static String sanitizePathValue(String filename){
    filename = validateAndNormalizePath(filename);
    String regEx = "..|\\|/";
    // compile the regex to create pattern
    // using compile() method
    Pattern pattern = Pattern.compile(regEx);
    // get a matcher object from pattern
    Matcher matcher = pattern.matcher(filename);

    // check whether Regex string is
    // found in actualString or not
    boolean matches = matcher.matches();
    if(matches){
        throw new MyAppRuntimeException("filename:'"+filename+"' is bad.");
    }
    return  filename;
}

public static String validateAndNormalizePath(String path) {
    path = path.replaceAll("/../", "/");
    path = path.replaceAll("/%46%46/", "/");
    path = SecurityUtil.cleanIt(path);
    path = FilenameUtils.normalize(path); // normalize path
    return path;

}

[Попытка] - Обновите код, который я пробовал с помощью нескольких участников, чтобы предотвратить проблему обхода пути.

Пытался дезинфицировать строку и нормализовать строку, но не повезло и возникла та же проблема.

Как решить проблему обхода сохраненного абсолютного пути?


person StackOverFlow    schedule 02.12.2020    source источник
comment
Вы можете попробовать запустить свое приложение, используя пользователя с ограниченными правами, у которого есть разрешения на доступ только к определенным каталогам. Даже если путь не очищен, пользователь вряд ли сможет что-либо сделать. Что-то вроде это   -  person Navin M    schedule 20.12.2020
comment
@Butiri Dan Не могли бы вы ответить, если вы уверены. Ценю ваш подробный ответ.   -  person StackOverFlow    schedule 23.12.2020
comment
@StackOverFlow У меня есть несколько вопросов, которые нужно задать вам, прежде чем пытаться решить эту проблему: 1. Почему вы не используете относительные пути в качестве входных данных от пользователя? (вместо того, чтобы пройти весь путь). Вы можете сгенерировать абсолютный путь, объединив соответствующий суффикс с относительным путем. › Строковое имя файла = System.getEnv(test); › › Файловый словарьФайл = новый файл (используйте здесь суффикс + имя файла);   -  person Aniketh Jain    schedule 28.12.2020
comment
2. Даже если вы ограничиваете доступ пользователя к определенному каталогу, хранит ли ваша система конфиденциальные данные для каждого пользователя в другом каталоге? Может ли пользователь угадать случайное имя файла (что рискованно, если имена файлов имеют шаблон) и при этом получить доступ к неавторизованным файлам? 3. Если у вас нет контроля над разделением файлов в разных каталогах в зависимости от пользователя, то есть ли у вас какие-либо метаданные или сопоставление файлов, к которым пользователю разрешен доступ?   -  person Aniketh Jain    schedule 28.12.2020


Ответы (6)


Ваша первая попытка не сработает, потому что побег в одиночку не предотвратит обход пути. Замена одинарных кавычек двойными кавычками также не поможет, поскольку вам нужно убедиться, что кто-то, устанавливающий переменную свойства/env с помощью ../../etc/resolv.conf, не сможет обманом заставить ваш код перезаписать/прочитать конфиденциальный файл. Я считаю, что Checkmarx не будет искать StringUtils как часть распознавания его как очищенного, поэтому простой рабочий пример ниже аналогичен без использования StringUtils.

Ваша вторая попытка не сработает, потому что это валидатор, который использует поток управления для предотвращения неправильного ввода, когда он выдает исключение. Checkmarx анализирует потоки данных. Когда filename передается в качестве параметра в sanitizePathValue и возвращается как есть в конце, анализ потока данных считает, что это не вносит изменения в исходное значение.

В вашей системе также есть некоторые настройки, которые распознают System.getProperty и System.getenv как ненадежные входные данные. По умолчанию они не распознаются таким образом, поэтому любой, кто попытается просканировать ваш код, вероятно, не получил бы никаких результатов для обхода абсолютного пути. Возможно, профиль риска вашего приложения требует, чтобы вы вызывали свойства и переменные среды как ненадежные входные данные, поэтому вы не можете просто удалить их и вернуться к настройкам OOTB.

Как упомянул Роман, логика запроса ищет значения, которые добавляются к этому ненадежному вводу, чтобы удалить эти потоки данных в качестве результатов. В приведенном ниже коде показано, как это можно сделать, используя метод Романа, чтобы обмануть сканер. (Я настоятельно рекомендую вам не выбирать маршрут, чтобы обмануть сканер... очень плохая идея.) Могут быть и другие значения строковых литералов, которые будут работать с использованием этого метода, но это потребует некоторых действий, которые контролируют то, как работает среда выполнения. выполняется (например, с помощью chroot), чтобы убедиться, что проблема действительно устранена.

Если вы просканируете приведенный ниже код, вы должны увидеть только один уязвимый путь к данным. Последний пример, вероятно, является чем-то вроде того, что вы могли бы использовать для устранения проблем. Это действительно зависит от того, что вы пытаетесь сделать с создаваемым файлом.

(Я тестировал это на 9.2; это должно работать для предыдущих версий. Если это не работает, опубликуйте свою версию, и я смогу изучить запрос этой версии.)

// Vulnerable
String fn1 = System.getProperty ("test");
File f1 = new File(fn1);


// Path prepend - still vulnerable, tricks the scanner, DO NOT USE
String fn2 = System.getProperty ("test");
File f2 = new File(Paths.get ("", fn2).toString () );

// Path prepend - still vulnerable, tricks the scanner, DO NOT USE
String fn3 = System.getProperty ("test");
File f3 = new File("" + fn3);

// Path prepend - still vulnerable, tricks the scanner, DO NOT USE
String fn4 = System.getProperty ("test");
File f4 = new File("", fn4);

// Sanitized by stripping path separator as defined in the JDK
// This would be the safest method
String fn5 = System.getProperty ("test");
File f5 = new File(fn5.replaceAll (File.separator, ""));


Итак, резюмируя (TL;DR), замените разделитель файлов в ненадежном входном значении:

String fn5 = System.getProperty ("test");
File f5 = new File(fn5.replaceAll (File.separator, ""));

Изменить Обновление для других пользователей Checkmarx, которые могут столкнуться с этим в поисках ответа.

После моего ответа OP обновил вопрос, чтобы показать, что обнаруженная проблема связана с механизмом, написанным для запуска кода в разных средах. До докера этот метод можно было бы использовать. Уязвимость все равно была бы обнаружена, но большинство вариантов действий заключалось бы в том, чтобы сказать, что наша среда развертывания имеет меры безопасности, чтобы предотвратить внедрение злоумышленником нежелательного пути в переменную среды, где мы храним наш базовый путь.

Но теперь, с Docker, это ушло в прошлое. Как правило, Docker предназначен для создания приложений, которые работают одинаково везде, где они развернуты. . Использование базового пути в среде, вероятно, означает, что OP выполняет код вне контейнера для разработки (на основе обновления, показывающего путь Windows) и внутри контейнера для развертывания. Почему бы просто не запустить код в контейнере для разработки и развертывания, как это предусмотрено Docker?

Большинство ответов, как правило, объясняют, что OP должен использовать статический путь. Это связано с тем, что они понимают, что нет способа избежать этой проблемы, потому что получение ненадежных входных данных (из среды) и добавление префикса к пути является точной проблемой абсолютного обхода пути.

OP мог бы последовать хорошему совету многих авторов здесь и добавить в код статический базовый путь, а затем использовать тома Docker. или подключение привязки Docker.

Это сложно? Неа. Если бы я был OP, я бы исправил префикс базового пути в коде до статического значения /app/data и сделал простую привязку тома во время разработки. (Если подумать, если во время развертывания в контейнере хранятся данные, то среда развертывания должна делать именно это для /app/data, если только данные не сохраняются после окончания срока службы контейнера.)

С базовым путем, фиксированным на /app/data, один из вариантов для OP запустить свою сборку для разработки:

docker run -it -v"C:\\projects\\app\\data":/app/data {container name goes here}

Все данные, записанные приложением, будут отображаться в C:\projects\app\data так же, как и при использовании переменных среды. Основное отличие состоит в том, что нет путей с префиксом переменной среды, и, следовательно, сканер статического анализа не дает результатов абсолютного обхода пути.

person NathanL    schedule 13.01.2021
comment
Спасибо за ответ . Я передаю значение env как путь в файле докера, который используется в тесте ENV сервера dev/uat /app/data/. В переменной local/dev Environment test=C:\projects\app\data - person StackOverFlow; 13.01.2021
comment
Я обновил вопрос, 1] я получаю путь к файлу из env/property.2) путь к файлу + мое переданное имя файла добавляется и передается в класс FILE - person StackOverFlow; 13.01.2021
comment
Ваш контекст выполнения (который не может быть отсканирован) потенциально делает это ложным срабатыванием. Если вы доверяете props/env, это FP. В противном случае это истинно положительно из-за недоверенных входных данных, формирующих путь. Цель использования Docker обычно заключается в том, чтобы Dockerized-приложение работало одинаково во всех средах. Если вы разработали его и запустили в Docker, у вас может быть статический путь (абсолютный или, возможно, относительный к WORKDIR). Единственный способ избежать обхода пути из ненадежного входа — превратить его во что-то, что нельзя использовать в пути, что будет сложно из-за выбора реализации. - person NathanL; 13.01.2021
comment
Также хочу добавить - обычно приложения Dockerized используют абсолютные пути, а затем выполнение сопоставляет том с этим абсолютным путем. Это позволяет разработчикам разрабатывать локально и заменять локальное хранилище, возможно, облачным хранилищем или NAS при развертывании в производственной среде. Запустите свой контейнер с путем, сопоставленным с томом/локальным каталогом, а не с переменной среды, и вы получите тот же эффект. Если вы пойдете на буткемп на DockerCon, вам на самом деле покажут эту технику как часть презентации. - person NathanL; 13.01.2021
comment
Мы используем версию Checkmarx 9.3.0 HF11. Также обновлен код для справки. - person StackOverFlow; 12.07.2021

Это зависит от того, как Checkmarx придет к этому. Скорее всего, потому что значение, переданное File, все еще испорчено. Поэтому убедитесь, что /../ и /%46%46/ заменены на /.

checkedInput = userInput.replaceAll("/../", "/");

Во-вторых, дайте File родительский каталог для начала, а затем сравните путь к файлу, который вы хотите обработать. Некоторые общие примеры кода приведены ниже. Если файл не начинается с полного родительского каталога, это означает, что у вас есть обход пути.

File file = new File(BASE_DIRECTORY, userInput);
if (file.getCanonicalPath().startsWith(BASE_DIRECTORY)) {
    // process file
}

Checkmarx может только проверить, содержат ли переменные искаженное значение, и в некоторых случаях верна ли логика. Пожалуйста, подумайте также о запущенном процессе и правах доступа к файловой системе. Многие приложения имеют возможность перезаписывать собственные исполняемые файлы.

person hspaans    schedule 21.12.2020
comment
Спасибо. Позвольте мне попробовать - заменить /../ и /%46%46/ заменить на / - person StackOverFlow; 22.12.2020
comment
Я обновил свой вопрос версией checkmex. Не могли бы вы просмотреть ответ. - person StackOverFlow; 12.07.2021

Если есть что вспомнить, так это

использовать списки разрешений, а не списки запрещенных

(традиционно известные как белые списки и черные списки).

Например, рассмотрите возможность замены /../ на /, предложенную в другом ответе. Мой ответ должен содержать последовательность /../../. Вы можете продолжить это итеративно, и у меня могут закончиться состязательные примеры, но это не значит, что они есть.

Другая проблема заключается в знании всех специальных символов. \0 используется для усечения имени файла. Что происходит с не-ASCII-символами - не помню. В будущем может быть изменен другой код, чтобы путь заканчивался в командной строке с другими специальными символами - что еще хуже, зависит от ОС/командной строки.

У канонизации тоже есть свои проблемы. Его можно использовать для некоторой проверки файловой системы (и, возможно, за пределами машины).

Итак, выберите то, что вы позволяете. Сказать

if (filename.matches("[a-zA-Z0-9_]+")) {
    return filename;
} else {
    throw new MyException(...);
}

(В этой ситуации нет необходимости повторять всю Pattern/Matcher болтовню.)

person Tom Hawtin - tackline    schedule 17.01.2021
comment
Tom Hawtin - tackline Я обновил свой вопрос версией checkmex. Не могли бы вы просмотреть и изменить ответ. - person StackOverFlow; 12.07.2021

как насчет использования пути Java для проверки (../test1.txt - это ввод от пользователя):

File base=new File("/your/base");
Path basePath=base.toPath();
Path resolve = basePath.resolve("../test1.txt");
Path relativize = basePath.relativize(resolve);
if(relativize.startsWith("..")){
    throw new Exception("invalid path");
}
person Asaf Ben-Natan    schedule 15.01.2021
comment
Я обновил свой вопрос версией checkmex. Не могли бы вы просмотреть и изменить ответ - person StackOverFlow; 12.07.2021

Для этой проблемы я бы посоветовал вам жестко закодировать абсолютный путь к каталогу, в котором вы разрешаете работать своей программе; как это:

String separator = FileSystems.getDefault().getSeparator();
// should resolve to /app/workdir in linux
String WORKING_DIR = separator + "app"+separator +"workdir"+separator ;

затем, когда вы принимаете параметр, рассматривайте его как относительный путь следующим образом:

String filename = System.getProperty("test");
sanitize(filename);
filename = WORKING_DIR+filename;
File dictionaryFile = new File(filename);

Чтобы очистить ввод пользователя, убедитесь, что он не включает .., а также не включает \ и /.

private static void sanitize(filename){
if(Pattern.compile("\\.\\.|\\|/").matcher(filename).find()){
  throw new RuntimeException("filename:'"+filename+"' is bad.");
  }
}

Изменить

Если вы запускаете процесс в Linux, вы можете изменить корень процесса, используя chroot, возможно, вы погуглите, чтобы узнать, как его реализовать.

person achabahe    schedule 28.12.2020
comment
спасибо, позвольте мне попробовать ваш ответ .. - person StackOverFlow; 29.12.2020
comment
красивое имя кстати это жемчужина - person achabahe; 30.12.2020
comment
Я попробовал ваш ответ, но не повезло. Checkmarx по-прежнему сообщает о проблеме с абсолютным обходом пути — cwe.mitre.org/data/definitions/36. html - person StackOverFlow; 11.01.2021
comment
Я обновил вопрос, 1] я получаю путь к файлу из env/property.2) путь к файлу + мое переданное имя файла добавляется и передается в класс FILE - person StackOverFlow; 13.01.2021
comment
зачем вам делать это startWith check i вы создаете имя файла ??? - person achabahe; 15.01.2021
comment
Да, например, filepath = /app/config + filename.ext, поэтому проверка filepath.startsWith(//app//) - person StackOverFlow; 16.01.2021
comment
Вам не хватает точки filepath = /app/ + filename ; означает, что вы уверены, что файл будет записан в каталог приложения, потому что вы на 100% уверены, что имя файла не содержит .. поэтому вы уверены, что je не перейдет в родительский каталог - person achabahe; 16.01.2021
comment
Предлагаемый здесь метод очистки не работает должным образом для Mac. 2 точки должны быть экранированы. Вот как должна выглядеть санация: Pattern.compile(\\.\\.|\\|/).matcher(fileName).find() - person jkasper; 21.04.2021
comment
Я подтверждаю, что забыл убрать две точки - person achabahe; 21.04.2021
comment
achabahe - я обновил свой вопрос версией checkmex. Не могли бы вы просмотреть и изменить ответ. - person StackOverFlow; 12.07.2021

Основываясь на прочтении запроса Checkmarx на предмет уязвимости, связанной с абсолютным обходом пути (и я считаю, что в целом это один из подходов к смягчению последствий), нужно добавить жестко запрограммированный путь, чтобы злоумышленники не проходили через файловую систему:

У файла есть конструктор, который принимает второй параметр, который позволит вам выполнить некоторые предварительные действия.

String filename = System.getEnv("test");
File dictionaryFile = new File("/home/", filename);

ОБНОВЛЕНИЕ: validateAndNormalizePath было бы технически достаточно, но я полагаю, что Checkmarx не может распознать это как дезинфицирующее средство (является пользовательской написанной функцией). Я бы посоветовал вам поработать с вашей командой по безопасности приложений, чтобы они использовали CxAudit и перезаписали базовый запрос Stored Path Traversal Checkmarx, чтобы распознать validateAndNormalizePath как действительное дезинфицирующее средство.

person securecodeninja    schedule 03.12.2020
comment
Мы не можем указать имя жесткого диска, предоставленный код предназначен для Windows. мы используем линукс - person StackOverFlow; 14.12.2020
comment
Я обновил свой вопрос версией checkmex. Не могли бы вы просмотреть и изменить свой ответ - person StackOverFlow; 12.07.2021