CVE-2021-2429:MySQL InnoDB Memcached 插件中的堆缓冲区溢出漏洞详解

CVE-2021-2429:MySQL InnoDB Memcached 插件中的堆缓冲区溢出漏洞详解_第1张图片 聚焦源代码安全,网罗国内外最新资讯!

编译:代码卫士

2021年4月,ZDI 收到了一名匿名者提供的关于MySQL 数据库中某漏洞的报告,结果是位于 InnoDB memcached 插件中的基于堆的缓冲区溢出漏洞。该漏洞影响MySQL 版本8.0.25及之前版本,可在未认证情况下远程触发。攻击者可利用该漏洞在 MySQL 数据库服务器上执行任意代码。Oracle 公司在7月将其修复并分配编号 CVE-2021-2429。

漏洞简介

如下分析基于 MySQL Community Server 版本8.0.25,位于 memcached GET 命令中。该命令用于从表格中检索数据。在性能方面,GET 命令支持在单个 memcached 查询中提取多键值对。如下:

mysql> SELECT * FROM test.demo_test; 
+----+--------------+------+------+------+ 
| c1 | c2           | c3   | c4   | c5   | 
+----+--------------+------+------+------+ 
| AA | HELLO, HELLO |    8 |    0 |    0 | 
+----+--------------+------+------+------+ 
1 row in set (0.01 sec)
telnet localhost 11211 
Trying 127.0.0.1... 
Connected to localhost. 
Escape character is '^]'. 
get AA AA      // query two key 'AA' within one query 
VALUE AA 8 12 
HELLO, HELLO 
VALUE AA 8 12 
HELLO, HELLO 
END

GET 命令中指定的密钥被 process_get_command() 令牌化,之后在 innodb_get() 中逐个处理。

static ENGINE_ERROR_CODE innodb_get( 
    /*=======*/ 
    ENGINE_HANDLE *handle, /*!< in: Engine Handle */ 
    const void *cookie,    /*!< in: connection cookie */ 
    item **item,           /*!< out: item to fill */ 
    const void *key,       /*!< in: search key */ 
    const int nkey,        /*!< in: key length */ 
    uint16_t next_get)     /*!< in: has more item to get */ 
{ 
... 
  if (report_table_switch) {       // (1) 
    char table_name[MAX_TABLE_NAME_LEN + MAX_DATABASE_NAME_LEN]; 
    char *name; 
    char *dbname; 


    conn_data = 
        (innodb_conn_data_t *)innodb_eng->server.cookie->get_engine_specific( 
            cookie); 
    assert(nkey > 0); 


    name = conn_data->conn_meta->col_info[CONTAINER_TABLE].col_name; 
    dbname = conn_data->conn_meta->col_info[CONTAINER_DB].col_name; 
#ifdef __WIN__ 
    sprintf(table_name, "%s\%s", dbname, name); 
#else 
    snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name); 
#endif // (5) 
... 
    memset(result, 0, sizeof(*result)); 
    assert(conn_data->row_buf_used + strlen(table_name) < REC_BUF_SLOT_SIZE);  // (2) 
    memcpy((char *)(conn_data->row_buf[conn_data->row_buf_slot]) + 
               conn_data->row_buf_used, 
           table_name, strlen(table_name));  // (3) 
... 
    result->col_value[MCI_COL_VALUE].value_len = strlen(table_name); 
    conn_data->row_buf_used += result->col_value[MCI_COL_VALUE].value_len;  // (4) 
... 
}

如GET 命令中的密钥形式为 @@containers.name,则变量 report_table_switch 将被设为 true,满足 (1)处的分支。(3)处的 memcpy 将 table_name 复制到 row_buf 缓冲区。在之心相关 memcpy 之前,(2)处的代码验证 row_buf 中仍然具有足够的空间。然而,这种验证近通过 assert() 执行。由于 assert 是build中而不是在发布build中生成代码的宏,因此它可导致缓冲区溢出漏洞,在运行发布 build 时可触发。

触发条件

InnoDB memcached 插件并非默认启用,必须通过 DWITH_INNODB_MEMCACHED=ON 从源头构建 MySQL。在默认情况下,memcached 守护进程监听 TCP 和 UDP 端口11211。该 pyload 是一个单一的 GET 命令,如下所示。

 get @@aaa @@aaa @@aaa ...

@@aaa 是位于 innodb_memcache 数据库中的默认列之一。

mysql> SELECT * FROM innodb_memcache.containers; 
+------+-----------+-----------+-------------+---------------+-------+------------+--------------------+------------------------+ 
| name | db_schema | db_table  | key_columns | value_columns | flags | cas_column | expire_time_column | unique_idx_name_on_key | 
+------+-----------+-----------+-------------+---------------+-------+------------+--------------------+------------------------+ 
| aaa  | test      | demo_test | c1          | c2            | c3    | c4         | c5                 | PRIMARY                | 
+------+-----------+-----------+-------------+---------------+-------+------------+--------------------+------------------------+ 
1 row in set (0.00 sec)

每个 @@aaa被在innodb_get()函数内的(5)处的表格名 test/demo_test替换,结果溢出内容的形式是 test/demo_testtest/demo_testtest/demo_test....。溢出的长度由攻击者控制。发送 payload 后,mysqld 进程中触发该堆缓冲区溢出漏洞。调用栈如下所示。

#0  innodb_get 
#1  process_get_command 
#2  process_command 
#3  try_read_command 
#4  conn_parse_cmd 
#5  event_handler 
#6  event_persist_closure 
#7  event_process_active_single_queue 
#8  event_process_active 
#9  event_base_loop 
#10 worker_libevent 
#11 start_thread 
#12 clone

补丁

该漏洞已在版本8.0.26中修复,非常直接。它在复制前先检查长度。

+   if (conn_data->row_buf_used + strlen(table_name) >= REC_BUF_SLOT_SIZE) { 
+     conn_data->row_buf_slot++; 
+ 
+     /* Limit the record buffer size to 16 MB */ 
+     if (conn_data->row_buf_slot >= 1024) { 
+       err_ret = ENGINE_KEY_ENOENT; 
+       goto func_exit; 
+     } 
+ 
+     if (conn_data->row_buf[conn_data->row_buf_slot] == nullptr) { 
+       conn_data->row_buf[conn_data->row_buf_slot] = malloc(REC_BUF_SLOT_SIZE); 
+     } 
+ 
+     conn_data->row_buf_used = 0; 
+   } 


    memset(result, 0, sizeof(*result)); 
-   assert(conn_data->row_buf_used + strlen(table_name) < REC_BUF_SLOT_SIZE); 
    memcpy((char *)(conn_data->row_buf[conn_data->row_buf_slot]) +

结论

尽管 InnoDB memcached 插件并非默认启用,但尽快应用补丁无疑是明智之举。不久后出现可靠的完整 exploit 应该并不令人惊讶。


开奖啦!!!!!】

限时赠书|《软件供应链安全—源代码缺陷实例剖析》新书上市

上次的限时赠书活动中奖名单已出炉,恭喜以下同学中奖,请微信后台私信地址,本周我们将陆续发送书籍。

@whyseu @。@HFwuhome@惊蛰 @nimo @XuZ @淡然 @Marco韬 @王孟 @Wecat@nwnλ @MOBE @湘北二两西香葱@※ @搬砖小土妞@云烟过眼 @r00t@小风 @傲雪@最好走的路是套路 @Zhao.xiaojun @浅笑淡然 @X-Star @Erick2013 @小秦同学 @X @王骏 @欢寻 @nbp@Mr. Guo

大家可移步京东电子工业出版社一睹为快!

https://item.jd.com/12927539.html 或直接点击“原文链接”购买。

CVE-2021-2429:MySQL InnoDB Memcached 插件中的堆缓冲区溢出漏洞详解_第2张图片

如下是本书相关讲解:

推荐阅读

PHP源代码后门事件后续:用户数据库遭泄露或是元凶

RigUp 数据库暴露7.6万份美国能源行业文件

【漏洞预警】Squid缓冲区溢出及拒绝服务漏洞安全预警通告

谷歌漏洞披露规则增加30天补丁缓冲期;Reddit 公开漏洞奖励计划

原文链接

https://www.zerodayinitiative.com/blog/2021/9/2/cve-2021-2429-a-heap-based-buffer-overflow-bug-in-the-mysql-innodb-memcached-plugin

题图:Pixabay License

本文由奇安信编译,不代表奇安信观点。转载请注明“转自奇安信代码卫士 https://codesafe.qianxin.com”。

CVE-2021-2429:MySQL InnoDB Memcached 插件中的堆缓冲区溢出漏洞详解_第3张图片

奇安信代码卫士 (codesafe)

国内首个专注于软件开发安全的产品线。

   CVE-2021-2429:MySQL InnoDB Memcached 插件中的堆缓冲区溢出漏洞详解_第4张图片 觉得不错,就点个 “在看” 或 "赞” 吧~

你可能感兴趣的:(CVE-2021-2429:MySQL InnoDB Memcached 插件中的堆缓冲区溢出漏洞详解)