欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

v2.7.1以下版本的Git中存在漏洞 攻击者可从中实现远程代码执行

程序员文章站 2022-03-16 11:46:57
 据了解,安全研究人员在2.7.1版本之前的所有版本Git中发现了一个安全漏洞,其服务器端和客户端中均存在这一漏洞。攻击者可利用这个漏洞来引起目标系统的缓冲区溢出,从而在...

 据了解,安全研究人员在2.7.1版本之前的所有版本Git中发现了一个安全漏洞,其服务器端和客户端中均存在这一漏洞。攻击者可利用这个漏洞来引起目标系统的缓冲区溢出,从而在目标主机中实现远程代码执行。(漏洞信息目前尚未公布,CVE-2016-2324和CVE 2016‑2315)

在此之前,安全研究人员曾就这个缓冲区溢出漏洞进行过分析和描述,具体内容请点击 这里 进行查看。

2016年2月11日,Jeff King在git安全邮件中写下了下面这段内容:

“2016年2月11日,星期四,下午2点32分49秒,‘Laël Cellier’在Git安全中写到:利用一个较长的文件名,或者大量的嵌套树,就可以推送或者克隆一个Git代码库,这就是这个漏洞的工作方式。

而现在的问题就在于,受此漏洞影响的版本数量非常之多,大量版本的Git代码分支目前仍处于使用状态,而且其中还有很多为稳定的代码分支。所以我认为这个漏洞是一个非常严重的漏洞,它应该得到CVE的认证,并且我也认为应该将此漏洞的详细信息公布出来。”

没错,正如你所说的那样,我也确实认为低于v2.7.0版本的Git中存在一个堆溢出漏洞。但是我并不认为导致这一问题出现的唯一原因就是其中的path_name(),而且当前最新版本中仍然存在这一问题。

由于之前所发布的代码比较杂乱,阅读起来非常地有困难。所以我对相关代码进行了整理,并提供给大家:

char *path_name(const struct name_path *path, const char *name){         const struct name_path *p;         char *n, *m;         int nlen = strlen(name);         int len = nlen + 1;          for (p = path; p; p = p->up) {                 if (p->elem_len)                         len += p->elem_len + 1;         }         n = xmalloc(len);         m = n + len - (nlen + 1);         memcpy(m, name, nlen + 1);         for (p = path; p; p = p->up) {                 if (p->elem_len) {                         m -= p->elem_len + 1;                         memcpy(m, p->elem, p->elem_len);                         m[p->elem_len] = '/';                 }         }         return n;}

根据漏洞的描述信息,系统所分配的内存空间大小与strcpy函数需要写入数据的大小并不匹配。但是从某种程度上来说,我们可以利用memcpy()函数来解决这一问题,因为至少“len”的初始值与我们所需要写入的数据字节数是匹配的(也许相关的数值并不是真实值,但是只要我们写入的数据大小不超过内存空间的分配值就可以了)。

但是在系统对数据进行了循环计算之后,“len”的值是会进行相应改变的。如果你已经获取到了path参数的序列值(每个值均小于2^31),系统将会把这些数值进行相加,并得出一个更小的正数值。比如说,我们需要处理的数据为A/B/C,其中各个数据长度如下: A=2^31-5, B=2^31-5, C=20,,系统在计算之后最终将会得到len=10。然后,缓冲区的大小将会变小,以至于无法存储C的数据,这样一来,系统将无法存储在第二轮循环中所需要写入的数据了。

针对这一问题,我也提出了我自己的解决方案。我发现,只需要将所有的“int”整形变量转变为“size_t”就可以适当缓解这个问题了。虽然这样做并不能完美解决所有的问题,但是这也意味着在一个64位架构的操作系统中,用户将需要利用一个长度为2^64的值来触发这个漏洞,这是很不切实际的。虽然这种操作方式可以帮助64位的系统来避免这个问题,但是32位的操作系统就没办法了。

这里还有一个值得注意的地方,在tree-diff.c中的path_appendnew()函数中还存在一个类似的问题。我们需要在strbuf中构建一个完整的路径名称,并利用相应的函数参数来对内存空间溢出进行检测。但是当我们将length长度作为一个整形参数传递给函数之后,系统会分配一个FLEX_ARRAY结构体来对传入的参数进行处理。我认为,这个问题比我们之前所讨论的问题都要有意思。因为我们可以通过git-log来触发这个漏洞,而且只有当系统对代码进行重新封装的过程中才会利用到path_name()函数。所以,虽然这个问题始终会对你产生影响,但是并不会在你刚开始克隆代码库的时候就出现问题。

而我就这一问题所提出的解决方案与之前方案的类似:我们需要使用size_t。这样一来,至少可以保证在64位操作系统中,必须分配一个较大的数值才能触发这个漏洞。虽然在32位操作系统中的效果并不是那么好,但是至少也可以帮助你避免内存空间分配失败的问题。

所以这也就是我为什么要将这个漏洞缓解方案提交上来的原因。我也承认,这些解决方案的确不能完美解决这些问题。完美的解决方案应该是:始终使用size_t来存储strlen()函数的返回值,并且当我们需要对size_t的数值进行计算处理之前,必须对内存空间做溢出检测。

可能有的读者还记得我在去年所发表的一系列相关文章,感兴趣的读者可以看一看,也许大家能够从中发现一些新的东西。

除此之外,我认为其实我们可以完全弃用path_name()函数。该函数的唯一用处就是计算数据包对象的包名哈希值,实际上我们并不需要通过在内存中重构数据包来实现这一点。

据了解,本文所有讨论的问题都已经在git 2.7.1版本中得到了修复,该版本移除了path_name(),tree-diff.c文件中的size_t,以及缓冲区溢出检测机制。其实在此之前,Github已经为其企业用户修复了这一问题。目前,Bitbucket和GitLab仍然会受到这个问题的影响。虽然目前此漏洞还未得到CVE认证,但是我相信相关人员很快会对这一问题进行处理。