缓冲区下溢如何导致 64 位远程代码执行?

信息安全 缓冲区溢出 脆弱性
2021-08-19 11:57:49

我在 ssh 服务器库中总结了以下 C89 中的伪代码,这些代码仅提供对 git-shell 之类的访问/bin/bash已替换为要运行的程序,因此无法执行其他操作)  :

struct _raw_uncapped_ssh_string { // no limit on the size of the string;
    uint32_t len;
    char non_null_terminated_string[]; // by protocol design it have a minimum length of 1
};
typedef struct _raw_uncapped_ssh_string raw_ssh_string;
union buffer {
       void * uncapped_zlib_decompressed_network_data; 
       // yes, the size is uncapped, so it’s possible to put 4Gb of
       // data in it that would be copied later into memory. zlib 
       // allow easily to turn some Mb in Gb of data, but it’s not 
       // the point of the question.

       raw_ssh_string st;
};
get_command (compressed_network_data) {
    size_t len;
    char * command;
    buffer string=uncompress_to_buffer(compressed_network_data);
    len=ntohl(string.st.len)+1;
    command=malloc(len+1);
    command[len]=0;

    // here’s the point, both the string length and content as 
    // well it’s supplied size is controlled by the attacker.
    memcpy(command,string.st.data,len); 

    return command;
}

以下是稍后执行命令的方式(字符串command在 之后未更改get_command()

const char *args[]={"/bin/bash",command,NULL}; // /bin/bash isn’t the shell, it has been replaced by git‑shell.

// redirect the program output to the network.
dup2(stdin, 0);
dup2(stdout,1);
dup2(stdout,2);
close(stdin);
close(stdout);

//if this return execution failed and print an error message
return execv(args[0],(char * const *)args); // I don’t know which is the system, so I can’t know about the libc behaviour.

我不能这样做,memcpy(command,string.st.data,0)因为第三个成员的memcpy最小大小为 1,并且在我的上下文中,size_t使用 64 位整数,我无法执行缓冲区溢出,因为存在len.

我所能做的就是设置len一个大于分配给string.st.data. 这是一个缓冲区下溢,使我能够读取未分配的内存。
我可以读取服务器内存,但是,我看不到公共 ssh 服务器可以保存哪些敏感数据(在我的情况下,可以执行 ssh 的用户列表是公开的)

那么缓冲区下溢是否memcpy允许远程代码执行?

2个回答

一般来说,不,缓冲区下溢不能用于远程代码执行。由于攻击者控制的数据永远不会离开为其分配的空间,因此它永远无法接管程序的执行流程。

缓冲区下溢确实有可能引发其他类型的攻击,例如信息泄露(如果程序指望缓冲区的原始内容被新数据擦除)。

当我最初写这个回复时,我似乎掩盖了你持有的一些误解。这些误解可能会使您无法理解我的回答。为了清楚起见,我将把这段文字放大以强调,而不是不尊重:

您使用的许多术语并不像您认为的那样。

例如:

  • memcpy“ ”的第三个成员不存在,因为memcpy它是一个函数,而不是一个structor union
  • “我不能执行缓冲区溢出,因为有len并且我不能用完汽油,因为有汽油。我在乞求这个问题吗?这对我来说似乎是一个恰当的类比,特别是因为从理论上讲,您的代码可能会引发缓冲区溢出。
  • “我所能做的就是设置len一个大于分配给的值string.st.data。这是缓冲区下溢......”不,这不是缓冲区下溢的定义。当您使用负索引或会导致指针算术环绕发生的索引访问越界数组时,会发生缓冲区下溢。后者对于某些配置可能是可能的(即使在“64 位系统”上,无论这意味着什么),但我怀疑这就是你在写这些词时的意思,因为你接着他们:
  • “......使我能够读取未分配的内存。” 我想也许你的意思是“未初始化的内存”。需要明确的是,我认为您的意思是说您已经分配了一个多余的部分并没有初始化多余的部分,也许您想对此做点什么(请参阅callocmemset)。

让我们考虑char *fubar = NULL;一下……这种指针通常具有值。解引用它被认为是一个空指针解引用,但是如果我们写类似的东西,我们会得到与(即)0["hello"]相同的东西因此,当攻击者控制方括号之间的表达式时(就像在您的情况下所做的那样),可以在攻击中使用空指针取消引用。"hello"[0]'h'

回到fubar事情上来;假设 we memset(&fubar, UCHAR_MAX, sizeof fubar);, nowfubar都是 1 位,这意味着它可能是我们的系统(理论上)可以容纳的最大地址。如果我们访问fubar[1]怎么办?如何访问最大地址之后的元素?然后地址会回绕吗?从技术上讲,所有这些都是未定义的行为,但如果我要在通用架构上命名它将是:

  1. 指针的算术溢出,导致...
  2. 空指针取消引用,和/或潜在的缓冲区下溢

缓冲区下溢是否memcpy允许远程代码执行?

缓冲区下溢可能允许攻击者覆盖位于相关数组之前的内存区域中的函数指针。如果这些函数指针指向 shellcode,那么这些 shellcode 可能会在稍后被调用时被执行。

在这段有点晦涩的代码中,当int域比 更宽时,似乎存在缓冲区下溢的风险uint32_t,因为ntohl(string.st.len)+1这会导致uint32_t值转换为int类型。例如,考虑 if INT_MINis -4294967296(小于 1 0 - UINT32_MAX)并且INT_MAXis 4294967295... 这本质上是一个 33 位int,带有填充以填充到一个字节的宽度;不常见,但可能。在这种情况下,表达式ntohl(string.st.len)+1不在uint32_t类型中;它在类型中,而不是在发生无符号整数溢出int时回绕回 0 ,它可能会在发生有符号整数溢出时回绕。-4294967296

如果您正在寻找防止缓冲区下溢的保证,请使用U整数文字后缀(即ntohl(string.st.len)+1U)。然后在这种情况下,您最终会得到表达式是 auint32_t或 an unsigned int(取决于哪个类型具有最大的域)。

如果您认为这ntohl(string.st.len)可能会返回比该无符号类型的最大值小一的值(无论它是什么),那么len=ntohl(string.st.len)+1将导致最大值,malloc(len+1)将导致无符号包装,因此您最终会调用malloc(0),然后command[len]=0会写得很好,真正超越数组的末尾。然后,当然,您也会遇到问题memcpy(command,string.st.data,len);(这是缓冲区溢出)。

缓冲区下溢和溢出并不是唯一的风险。如果您不检查 , 的返回值mallocmalloc返回,NULL则可以使用空指针取消引用来导致任意代码执行。这意味着您应该有一种方法可以将malloc故障传回给调用者。这也意味着您可以使用相同的方法来检查包装问题并将其传达给调用者。