代码之家  ›  专栏  ›  技术社区  ›  Kevin Meredith

了解sprintf的危险(…)

  •  20
  • Kevin Meredith  · 技术社区  · 14 年前

    OWASP

    “C库函数,如strcpy ()对以null结尾的字符串进行操作 并执行无边界检查。”

    sprintf int sprintf(char*str,const char*format,…);

    sprintf(str, "%s", message); // assume declaration and 
                                 // initialization of variables
    

    如果我理解OWASP的评论,那么使用sprintf的危险在于

    消息 的长度> 的长度,缓冲区溢出

    2) 如果 不以null结尾 \0 ,那么 消息 可能被复制到 超出内存地址 消息 ,导致缓冲区溢出

    请确认/否认。谢谢

    8 回复  |  直到 11 年前
        1
  •  26
  •   Pharap wonce    5 年前

    解决第一个问题的方法是使用 std::snprintf ,它接受缓冲区大小作为参数。

    第二个问题的解决方案是给 snprintf . 例如:

    char buffer[128];
    
    std::snprintf(buffer, sizeof(buffer), "This is a %.4s\n", "testGARBAGE DATA");
    
    // std::strcmp(buffer, "This is a test\n") == 0
    

    sizeof(buffer) snprintf公司 两次:

    int length = std::snprintf(nullptr, 0, "This is a %.4s\n", "testGARBAGE DATA");
    
    ++length;           // +1 for null terminator
    char *buffer = new char[length];
    
    std::snprintf(buffer, length, "This is a %.4s\n", "testGARBAGE DATA");
    

    (您可以使用 va 或可变模板。)

        2
  •  10
  •   John Dibling    14 年前

    你的两个断言都是正确的。

    char buf[1024] = {0};
    float f = 42.0f;
    sprintf(buf, "%s", f);  // `f` isn't a string.  the sun may explode here
    

    这对调试来说可能特别讨厌。

    所有这些都导致许多C++开发者得出这样的结论:你永远不应该使用。 sprintf

    #include <sstream>
    #include <string>
    
    // ...
    
    float f = 42.0f;
    
    stringstream ss;
    ss << f;
    string s = ss.str();
    

    …对于那些像我一样仍然喜欢使用 把格式数据写成串 boost Format libraries :

    #include <string>
    #include <boost\format.hpp>
    
    // ...
    
    float f = 42.0f;
    string s = (boost::format("%1%") %f).str();
    

    把格式数据写成串 可能就是这样。

        3
  •  4
  •   T.E.D.    14 年前

    是的,主要是缓冲区溢出的问题。然而,这些是当今相当严重的事务,因为缓冲区溢出是系统破解者用来绕过软件或系统安全的主要攻击向量。如果你把这样的东西暴露给用户输入,你很有可能把程序(甚至你的计算机本身)的密钥交给破解者。

    现在让我们假设有人恶意传递给我们的web浏览器一个比我们选择的缓冲区大得多的字符串。他的额外数据将覆盖附近的数据。如果他把它做得足够大,他的一些数据将通过Web服务器的指令而不是数据被复制。现在他可以让我们的网络服务器执行了 他的密码 .

        4
  •  3
  •   MSalters    14 年前

    你的两个结论是正确的,但不完整。

    char* format = 0;
    char buf[128];
    sprintf(buf, format, "hello");
    

    在这里, format 不是以NULL结尾的。 sprintf() 也不检查。

        5
  •  1
  •   bta    14 年前

    你的解释似乎是对的。然而,您的案例2并不是真正的缓冲区溢出。这更像是内存访问冲突。不过,这只是个术语,仍然是个大问题。

        6
  •  1
  •   supercat    14 年前

        7
  •  0
  •   Community Tales Farias    7 年前

    请记住,sprintf()在每个字符串的末尾添加ascii0字符作为字符串终止符,这一点非常重要。因此,目标缓冲区必须至少有n+1字节(要打印单词“HELLO”,需要6字节的缓冲区,而不是5字节的缓冲区)

    char buf[3] = {'1', '2'};
    int n = sprintf(buf, "A");
    

    还要注意,sprintf()的返回值不包括null终止字符。在上面的示例中,写入了2个字节,但函数返回“1”。

    struct S
    {
        char buf[4];
        int i;
    };
    
    
    int main()
    {
        struct S s = { };
        s.i = 12345;
    
        int num = sprintf(s.buf, "ABCD");
        // The value of s.i is NOT 12345 anymore !
    
        return 0;
    }
    
        8
  •  0
  •   Sergio Mcfly PYK    6 年前

    我已经给出了一个小示例,说明了如何为 把格式数据写成串 (当然,如果你愿意的话!)而且没有 snprintf公司 悬而未决。。。。

    here