Анализатор обнаружил создание команды уровня операционной системы из непроверенных данных, полученных из внешнего источника. Это может стать причиной возникновения уязвимости command injection.
В OWASP Top 10 Application Security Risks инъекции команд относятся к следующим категориям:
Рассмотрим пример:
HttpRequest _request;
string _pathToExecutor;
private void ExecuteOperation()
{
....
String operationNumber = _request.Form["operationNumber"];
Process.Start("cmd", $"/c {_pathToExecutor} {operationNumber}");
....
}
В представленном коде из запроса считывается номер операции, которую должен выполнить вызываемый процесс. Таким образом, набор операций чётко ограничен. Тем не менее, злоумышленник может передать в качестве значения параметра "operationNumber" специальную строку, которая позволит выполнить дополнительные несанкционированные действия. К примеру, в "operationNumber" может храниться следующая строка:
0 & del /q /f /s *.*
Допустим, что в '_pathToExecutor' записан путь 'executor.exe'. Тогда в результате вызова 'Process.Start' будет выполнена следующая команда:
cmd /c executor.exe 0 & del /q /f /s *.*
Символ '&' здесь будет интерпретирован как разделитель команд. Инструкция 'del' с такими аргументами приведёт к удалению всех файлов в текущей и вложенных папках (за исключением тех, для которых у приложения нет достаточных прав доступа). Таким образом, правильно подобранное значение в параметре "operationNumber" позволяет совершить вредоносные действия.
Во избежание появления уязвимости рекомендуется всегда проверять входные данные. Конкретный способ проверки зависит от ситуации. В указанном выше примере будет достаточно убедиться, что в переменной 'operationNumber' записано число:
private void ExecuteOperation()
{
String operationNumber = _request.Form["operationNumber"];
if (uint.TryParse(operationNumber, out uint number))
Process.Start("cmd", $"/c {_pathToExecutor} {number}");
}
Параметры методов, доступных из других сборок, также являются источниками небезопасных данных, хотя предупреждение для таких источников будет выдано с низким уровнем достоверности. Подробное объяснение данной позиции приведено в заметке "Почему важно проверять значения параметров общедоступных методов".
В качестве примера рассмотрим следующий код:
private string _userCreatorPath;
public void CreateUser(string userName, bool createAdmin)
{
string args = $"--name {userName}";
if (createAdmin)
args += " --roles ADMIN";
RunCreatorProcess(args); // <=
}
private void RunCreatorProcess(string arguments)
{
Process.Start(_userCreatorPath, arguments).WaitForExit();
}
В данном коде пользователь создаётся с помощью процесса, запускаемого методом 'RunCreatorProcess'. Этот пользователь должен получить права администратора только в том случае, если флаг 'createAdmin' имеет значение 'true'.
Код из библиотеки, зависимой от текущей, может вызывать метод 'CreateUser' для создания пользователя. В параметр 'userName' может быть передано, к примеру, значение какого-нибудь параметра запроса. При этом высока вероятность того, что никаких проверок в вызывающем коде не производится, так как разработчик будет рассчитывать на их наличие в методе 'CreateUser'. Таким образом, и в библиотеке, и в использующем её коде отсутствует валидация 'userName'.
В результате правильно подобранное имя позволит злоумышленнику создать пользователя с правами администратора вне зависимости от значения флага 'createAdmin', который, очевидно, будет равен 'false' в большинстве случаев. Допустим, что в параметр 'userName' записана следующая строка:
superHacker --roles ADMIN
После подстановки строка аргументов будет выглядеть так же, как если бы в 'createAdmin' было записано значение 'true':
--name superHacker --roles ADMIN
Таким образом, даже не имея прав на создание администратора, злоумышленник сможет создать пользователя с правами администратора и использовать его далее в своих целях.
В данном случае для защиты нужно проверять имя пользователя на отсутствие запрещённых символов. Например, можно разрешить использование только латинских букв и цифр:
public void CreateUser(string userName, bool createAdmin)
{
if (!Regex.IsMatch(userName, @"^[a-zA-Z0-9]+$"))
{
// error handling
return;
}
string args = $"--name {userName}";
if (createAdmin)
args += " --roles ADMIN";
RunCreatorProcess(args);
}
Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки непроверенного использования чувствительных данных (ввода пользователя, файлов, сети и пр.). |
Данная диагностика классифицируется как: