跳转至

CVE-2020-35511

Abstract

  • CVE ID: CVE-2020-35511
  • CWE ID: CWE-126 (Buffer Over-read)
  • Description: A global buffer overflow was discovered in pngcheck function in pngcheck-2.4.0(5 patches applied) via a crafted png file.
  • CVSS: 7.8
  • Published: 2022-08-23
  • Affected: pngcheck 2.4.0

漏洞概要

pngcheck 是一个用于验证 PNG / JNG / MNG 文件格式的命令行工具。

官方消息中没有说明漏洞的确切位置,但根据漏洞发现者的博客的描述,它很可能位于 printf_buffer() 函数。该函数对参数 size 的大小校验不足,当 size 小于 1 时,循环不会终止,从而导致全局缓冲区的越界读取。

漏洞原理

源码地址:http://www.libpng.org/pub/png/src/pngcheck-2.4.0.zip

pngcheck.c
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
void print_buffer(printbuf_state *prbuf, uch *buf, int size, int indent)
{
  if (indent)
    printf("    ");
  while (size--) {
    uch c;

    c = *buf++;

    if ((c < ' ' && c != '\t' && c != '\n') ||
        (sevenbit? c > 127 : (c >= 127 && c < 160)))
      printf("\\%02X", c);
/*
    else if (c == '\\')
      printf("\\\\");
 */
    else
      putchar(c);

    if (c < 32 || (c >= 127 && c < 160)) {
      if (c == '\n') {
        prbuf->lf = 1;
        if (indent && size > 0)
          printf("    ");
      } else if (c == '\r')
        prbuf->cr = 1;
      else if (c == '\0')
        prbuf->nul = 1;
      else
        prbuf->control = 1;
      if (c == 27)
        prbuf->esc = 1;
    }
  }
}

2.4.0 版本中,print_buffer 函数在处理七个不同 PNG 块时使用:

iCCP Chunk

print_buffer(&prbuf_state, buffer, name_len, 0);

name_len 来自 check_keyword,不可为负值。

iTXt Chunk

print_buffer(&prbuf_state, buffer, keylen, 0);
print_buffer(&prbuf_state, buffer+keylen+3, taglen, 0);

keylentaglen 都来自 keywordlen,不可为负值。

pCAL Chunk

print_buffer(&prbuf_state, buffer, name_len, 0);

name_len 来自 check_keyowrd,不可为负值。

sPLT Chunk

print_buffer(&prbuf_state, buffer, name_len, 0);

name_len 来自 check_keyword,不可为负值。

tEXt / zTXt Chunks

print_buffer(&prbuf_state, buffer, keylen, 0);
print_buffer(&prbuf_state, buffer + keylen + 1, toread - keylen - 1, 1);
  • keylen 来自 check_keyword,不可为负值
  • toread - keylen - 1 最小值为 0

SEEK Chunk

print_buffer(&prbuf_state, buffer, sz, 1);

sz 在每次函数调用前都进行了 if (sz > 0) 检查。

结论

所以从设计上看 print_buffer 函数可能有漏洞,但实现和使用上是安全的,因为:

  1. 所有 size 参数都在传入 print_buffer 前经过验证
  2. size == 0 时,while (size--) 循环立即终止

漏洞修复

官方在 v3.0.0 版本中在循环前添加 size 大小校验来修复这个问题:

@@ -887,6 +886,8 @@
 /* GRR EBCDIC WARNING */
 void print_buffer(printbuf_state *prbuf, uch *buf, int size, int indent)
 {
+  if (size < 1)
+    return;
   if (indent)
     printf("    ");
   while (size--) {

参考资料