需要帮助了解这个简短的C++程序及其漏洞

Need help understing this short C++ program and its vulnerability

本文关键字:C++ 程序 漏洞 帮助 了解      更新时间:2023-10-16

如果有人能准确地解释我代码的作用,我会很高兴。我知道存在缓冲区溢出和 bash 命令执行漏洞 - 但由于我是一个网络人而不是程序员,我真的可以使用一些帮助来理解整个代码。提前感谢!

int main () {
int status;
char t[1024]="ps -eo lstart,cmd | grep ";
cout << "Content-type:text/htmlrnrn"<<endl;
char *value = getenv("QUERY_STRING");
strcat(t,value);
status = system(strcat(t," | grep -v grep | head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'"));
return 0;
}

tl;dr:这就是你的代码作为shell脚本所做的:

#!/bin/bash
echo -en "Content-type:text/htmlrnrn"
ps -eo lstart,cmd | grep init | grep -v $QUERY_STRING | 
head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'

现在是更长的答案。

重写代码

首先,让我们通过一些错误处理将它变成 C++ 而不是 C(就像您的标签建议您询问的那样(,然后讨论正在发生的事情:

#include <iostream>
#include <string>
#include <string_view>
int main () {
auto query_string = getenv("QUERY_STRING");
if (query_string == nullptr) {
std::cerr << "Couldn't obtain QUERY_STRING environment variablen";
return EXIT_FAILURE;
}
if (std::string_view{query_string}.empty()) {
std::cerr << "Empty query string (QUERY_STRING environment variable)n";
return EXIT_FAILURE;
}
std::stringstream command_line;
command_line 
<< "ps -eo lstart,cmd | grep "
<< query_string 
<< " | grep -v grep | head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'";
std::cout << "Content-type:text/htmlrnrn";
return system(command_line.str()); // security vulnerability, see below
}

我们在这里做什么?

因此,我们在这里创建一个命令行,然后使用system()函数执行该命令行。它是使用一些开关调用ps命令,然后使用grepheadawk进行一些文本处理 - 使用管道机制将每个命令的输出移动到下一个命令。它们的关键部分是我们使用环境变量QUERY_STRING来过滤ps结果,即我们列出与某个短语匹配的进程。如果我们编译这个程序,设置环境变量并运行,它看起来像这样:

$ export QUERY_STRING=init
$ ./the_program
Content-type:text/html

Sun 3 Jun 2018 21:48:56

这给了我们第一个进程的开始时间,其命令行不包含短语"init"。所以现在你可以猜到我的系统从昨天开始就已经启动了......

最后,作为一个网络人,你可能会意识到"内容类型"的mumbo-jumbo和双换行符是一个MIME标头,所以这个输出可能打算用作HTTP响应。可能这是作为某种CGI脚本。

安全漏洞

  1. 在原始代码中,缓冲区大小被任意限制为 1024 - 而没有任何限制QUERY_SIZE不能超过这个长度。如果它更长,您将有内存损坏,这可能会产生安全隐患;攻击者可能能够找出您的内存布局,因此它更危险。这在C++版本中消失了。
  2. 第二个漏洞与system命令有关。我们将任意字符串注入到我们正在创建的字符串中;没有什么能阻止某人设置

    $export QUERY_STRING="dummy; rm -rf $HOME ; echo"
    

    在这种情况下,您将运行:

    ps -eo lstart,cmd | grep dummy; rm -rf $HOME ; echo | grep -v init | head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'
    

    这将删除有效用户主目录下的所有内容。或者它可以是任何命令,包括编译自定义 C/C++ 程序以在系统上运行一些任意代码。很差。

  3. 即使您将QUERY_STRING净化为仅有效的 grep 模式,如果有人以某种方式提供复杂的超长 grep 模式,仍可能存在拒绝服务攻击。因此,限制长度也是一个好主意。

这只是声明变量status

int status;

这声明了 t,带有 C 字符串"ps -eo lstart,cmd |格雷普"。t的最大容量为 1024 字节(字符串为 1023 字节 +1 字节(

char t[1024]="ps -eo lstart,cmd | grep ";

打印下面的字符串

cout << "Content-type:text/htmlrnrn"<<endl;

获取环境变量QUERY_STRING

char *value = getenv("QUERY_STRING");

将上面的值连接起来t。在这里,您可能会遇到缓冲区溢出,因为您不知道字符串的大小value并且它可能超过 1024 字节

strcat(t,value);

调用system()函数,使用所有这些 grep 等命令对前t进行另一个串联......在这里,您可能会有另一个缓冲区溢出,一旦t也可能溢出其 1024 字节。

status = system(strcat(t," | grep -v grep | head -n 1 | awk '{ print $1" "$3" "$2" "$5" "$4}'"));