防止命令行注入攻击

发布于 2024-07-04 23:20:57 字数 659 浏览 7 评论 0原文

我们目前正在构建一个执行许多外部工具的应用程序。 我们经常必须将用户输入系统的信息传递给这些工具。

显然,这是一场巨大的安全噩梦即将发生。

不幸的是,我们尚未在 .NET Framework 中发现任何类可以执行命令行程序,同时提供与 IDbCommand 对象针对数据库所做的相同类型的注入攻击防护。

现在,我们正在使用非常原始的字符串替换,我怀疑这还不够:

受保护的虚拟字符串转义(字符串值) 
  { 
        返回值 
          。代替(@”\”, @”\\”) 
          .替换(@"$", @"\$") 
          。代替(@””””, @”\”””) 
          .替换(“`”,“'”) 
        ; 
  } 
  

你们如何防止命令行注入攻击? 我们计划实现一个非常严格的正则表达式,只允许非常小的字符子集通过,但我想知道是否有更好的方法。

一些说明:

  • 其中一些工具没有我们可以编程的 API。 如果他们这样做了,我们就不会遇到这个问题了。
  • 用户不选择要执行的工具,而是输入我们选择的工具使用的元数据(例如,将版权声明等元数据注入目标文件)。

We're currently building an application that executes a number of external tools. We often have to pass information entered into our system by users to these tools.

Obviously, this is a big security nightmare waiting to happen.

Unfortunately, we've not yet found any classes in the .NET Framework that execute command line programs while providing the same kind of guards against injection attacks as the IDbCommand objects do for databases.

Right now, we're using a very primitive string substitution which I suspect is rather insufficient:

protected virtual string Escape(string value)
{
      return value
        .Replace(@"\", @"\\")
        .Replace(@"$", @"\$")
        .Replace(@"""", @"\""")
        .Replace("`", "'")
      ;
}

What do you guys do to prevent command-line injection attacks? We're planning to implement a regex that is very strict and only allows a very small subset of characters through, but I was wondering if there was a better way.

Some clarifications:

  • Some of these tools do not have APIs we can program against. If they did, we wouldn't be having this problem.
  • The users don't pick tools to execute, they enter meta-data which the tools we've chosen use (for example, injecting meta data such as copyright notices into target files).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

德意的啸 2024-07-11 23:20:57

您是直接执行程序还是通过 shell 执行程序? 如果您始终通过为可执行文件提供完整路径名并将 shell 排除在外来启动外部程序,那么您实际上不会受到任何类型的命令行注入的影响。

编辑:Floyd 博士,shell 负责评估诸如反引号之类的东西。 无壳,不评价壳。 显然,您仍然必须了解您所调用的程序中任何潜在的安全问题 - 但我认为这个问题与此无关。

Are you executing the programs directly or going through the shell? If you always launch an external program by giving the full path name to the executable and leaving the shell out of the equation, then you aren't really susceptible to any kind of command line injection.

EDIT: DrFloyd, the shell is responsible for evaluating things like the backtick. No shell, no shell evaluation. Obviously, you've still got to be aware of any potential security gotchas in the programs that you're calling -- but I don't think this question is about that.

似梦非梦 2024-07-11 23:20:57

当您 Process.Start 一个新进程,在其参数参数中提供参数,而不是自己构建整个命令行。

没有时间进行适当的测试,但我认为这应该有助于在某种程度上保护它。

明天将对此进行测试。

编辑:啊,有人又抢先了我。 但还有一点:尝试使用 Console.InputStream (不记得确切的名称)来提供数据而不是传递参数,这是一个可能的解决方案吗? 例如修复命令,以便它从 CON 设备读取,然后您通过输入流提供数据。

When you Process.Start a new process, supply the parameters in its Parameters argument instead of building the whole command line yourself.

Haven't got time for a proper test, but I think that should help guard it to some level.

Will test this out tomorrow.

EDIT: Ah, someone beat me to it again. But here's another point: Try using the Console.InputStream (can't remember exact name) to supply data instead of passing parameters, is that a possible solution? like fix the command so it reads from the CON device and you then supply the data via input stream instead.

黯然#的苍凉 2024-07-11 23:20:57

Windows 上的 C++ 中,您只需在需要时转义 \ 和 ",引用参数并 ShellExecute 它。然后,引号内的所有内容都应被视为文本。

这应该说明:


#include <iostream>
#include <string>
#include <windows.h>
#include <cstdlib>
using namespace std;

// Escape and quote string for use as Windows command line argument
string qEscape(const string& s) {
    string result("\"");
    for (string::const_iterator i = s.begin(); i != s.end(); ++i) {
        const char c = *i;
        const string::const_iterator next = i + 1;
        if (c == '"' || (c == '\\' && (next == s.end() || *next == '"'))) {
            result += '\\';
        }
        result += c;
    }
    result += '"';
    return result;
}

int main() {
    // Argument value to pass: c:\program files\test\test.exe
    const string safe_program = qEscape("c:\\program files\\test\\test.exe");
    cout << safe_program << " ";

    // Argument value to pass: You're the "best" around.
    const string safe_arg0 = qEscape("You're the \"best\" around.");

    // Argument value to pass: "Nothing's" gonna ever keep you down.
    const string safe_arg1 = qEscape("\"Nothing's\" gonna ever keep you down.");

    const string safe_args = safe_arg0 + " " + safe_arg1;
    cout << safe_args << "\n\n";

    // c:\program files\test\  to pass.
    const string bs_at_end_example = qEscape("c:\\program files\\test\\");
    cout << bs_at_end_example << "\n\n";

    const int result = reinterpret_cast<int>(ShellExecute(NULL, "open", safe_program.c_str(), safe_args.c_str(), NULL, SW_SHOWNORMAL));
    if (result < 33) {
        cout << "ShellExecute failed with Error code " << result << "\n";
        return EXIT_FAILURE;
    }
}

但是,使用任何方法使用时,您应该对其进行测试,看看它确实可以防止注入。

In C++ on Windows, you just escape \ and " where needed, quote the argument and ShellExecute it. Then, everything inside the quotes should be treated as text.

This should illustrate:


#include <iostream>
#include <string>
#include <windows.h>
#include <cstdlib>
using namespace std;

// Escape and quote string for use as Windows command line argument
string qEscape(const string& s) {
    string result("\"");
    for (string::const_iterator i = s.begin(); i != s.end(); ++i) {
        const char c = *i;
        const string::const_iterator next = i + 1;
        if (c == '"' || (c == '\\' && (next == s.end() || *next == '"'))) {
            result += '\\';
        }
        result += c;
    }
    result += '"';
    return result;
}

int main() {
    // Argument value to pass: c:\program files\test\test.exe
    const string safe_program = qEscape("c:\\program files\\test\\test.exe");
    cout << safe_program << " ";

    // Argument value to pass: You're the "best" around.
    const string safe_arg0 = qEscape("You're the \"best\" around.");

    // Argument value to pass: "Nothing's" gonna ever keep you down.
    const string safe_arg1 = qEscape("\"Nothing's\" gonna ever keep you down.");

    const string safe_args = safe_arg0 + " " + safe_arg1;
    cout << safe_args << "\n\n";

    // c:\program files\test\  to pass.
    const string bs_at_end_example = qEscape("c:\\program files\\test\\");
    cout << bs_at_end_example << "\n\n";

    const int result = reinterpret_cast<int>(ShellExecute(NULL, "open", safe_program.c_str(), safe_args.c_str(), NULL, SW_SHOWNORMAL));
    if (result < 33) {
        cout << "ShellExecute failed with Error code " << result << "\n";
        return EXIT_FAILURE;
    }
}

But, with any method you use, you should test the hell out of it to see that it does really prevent injection.

暗喜 2024-07-11 23:20:57

你是直接执行程序还是通过 shell 执行程序? 如果您始终通过为可执行文件提供完整路径名并将 shell 排除在外来启动外部程序,那么您实际上不会受到任何类型的命令行注入的影响。

@Curt Hagenlocher 反引号会杀了你。 如果 Windows 系统设置“错误”,或者 UNIX 系统允许,则 dir &bt;del *&bt; 将首先执行 del * 命令,然后使用输出代替 del *,在这种情况下,这并不重要,因为没有任何内容可以 dir (或 ls)

Are you executing the programs directly or going through the shell? If you always launch an external program by giving the full path name to the executable and leaving the shell out of the equation, then you aren't really susceptible to any kind of command line injection.

@Curt Hagenlocher The backtick can kill you. If the Windows System is setup "wrong", or the unix system allows it, a dir &bt;del *&bt; will first execute the del * command then use the output in place of the del *, which in this case, won't matter because there is nothing to dir (or ls)

绿光 2024-07-11 23:20:57

嗯...

听起来您有一个用户可以执行的有效命令列表。 但你不希望他们全部执行。

您可以尝试使用实际的命令行并验证该文件至少存在于“安全”位置。

您还可以使用更多界面来解决问题,提供他们可以使用的命令和参数的下拉列表。 这对你来说是更多的工作,但它最终对用户有帮助。

Hmmm...

It sounds like you have a list of valid commands that the users are able to execute. But you don't want them to execute them all.

You could try to take the actual command line and verify the file exists in the "safe" location at least.

You could also solve the problem with more interface, provide a drop down of commands and parameters they could use. It's more work on your end, but it ultimately helps the users.

遥远的她 2024-07-11 23:20:57

那么,如果您可以在不使用命令行的情况下以编程方式调用这些工具,那么这可能是您的最佳选择。 否则,您可能会通过绝对无权执行任何操作的用户执行命令行工具(除了可能无法对其造成任何损害的单个目录)...尽管这可能最终会破坏该工具,具体取决于关于该工具的作用。

请注意,我从未遇到过这个问题,因为我实际上从未需要从面向外部的应用程序调用命令行工具,而该工具需要用户的输入。

Well, if you can invoke the tools programmatically without the command line, that would probably be your best option. Otherwise, you could potentially execute the command line tool via a user that has absolutely no access to do anything (except perhaps a single directory that they can't do any harm with)... though that may end up breaking the tool, depending on what the tool does.

Just note, I have never had to face this problem, because I have never actually had to invoke a command line tool from an externally facing application where the tool requires input from the user.

终止放荡 2024-07-11 23:20:57

不要使用黑名单来防止注入。 如果有n种方式注入代码,你会想到n - m,其中m > 0

使用可接受的参数(或模式)的白名单。 它本质上限制性更大,但这就是安全的本质。

Don't use a blacklist for preventing injections. If there are n ways to inject code, you'll think of n - m where m > 0.

Use a whitelist of accepted parameters (or patterns). It is much more restrictive by nature, but that's the nature of security.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文