编辑
2023-08-11
Fuzz
00
请注意,本文编写于 601 天前,最后修改于 601 天前,其中某些信息可能已经过时。

目录

Fuzz Farm
Fuzz Farm #1
环境配置
Fuzz 配置
漏洞 1 - SEGV
漏洞 2 - 资源耗尽漏洞

😃

Fuzz Farm


Fuzz Farm #1

项目1 主要是复现 GEGL 开源库的漏洞挖掘过程

项目版本: a566b738331757cf25118af5bdc65218ae5eb3b2

环境配置
bash
$ git clone https://gitlab.gnome.org/GNOME/gegl.git $ git checkout a566b $ sudo pip3 install meson $ CC=afl-gcc CXX=afl-g++ meson _build $ ASAN_OPTIONS=detect_leaks=0 AFL_USE_ASAN=1 ninja -C _build $ ninja -C _build install
Fuzz 配置

虽然GEGL 提供了一个二进制文件gegl但是如果直接对二进制文件进行 Fuzz 可能会出现效率低下的问题 所以这里采用编写harness 的方式来进行Fuzz

c
gint main(gint argc, gchar **argv) { GeglNode *gegl = NULL; gchar *script = NULL; GError *err = NULL; gchar *path_root = NULL; // [1] gegl_init(NULL, NULL); path_root = g_get_current_dir (); // [2] g_file_get_contents (argv[1], &script, NULL, &err); if (err != NULL) { return 1; } // [3] gegl = gegl_node_new_from_xml (script, path_root); if (!gegl) { return 1; } // [4] GeglNode *output = gegl_node_new_child (gegl, "operation", "gegl:save", "path", "out.png", NULL); gegl_node_connect_from (output, "input", gegl, "output"); gegl_node_process (output); // [5] g_object_unref (output); g_object_unref (gegl); g_free (script); g_clear_error (&err); g_free (path_root); gegl_exit (); return 0; }

使用afl-clang-fast进行编译即可

bash
$ afl-clang-fast -o test test.c `pkg-config --cflags --libs gegl-0.4 glib-2.0`

语料库使用的是项目内的tests/compositions内的文件

bash
$ afl-fuzz -i ./compositions -o ./out -m none -t 20000 -- ./test @@

一会就能跑出 crash (虽然 harness 也快不到哪里去就对了..)

这个时候可以编译出来一版包含 ASAN 的 harness 看看到底是哪里 crash 了

bash
$ AFL_USE_ASAN=1 afl-clang-fast -o test1 test.c `pkg-config --cflags --libs gegl-0.4 glib-2.0`

可以看到与预估漏洞一致 是 crash 在gegl_path_parse_string

漏洞 1 - SEGV

可以根据 ASAN 爆出的信息直接定位漏洞所在地 根据GDB 动态调试复现 漏洞实际触发于../gegl/property-type/gegl-path.c:817

c
void gegl_path_parse_string (GeglPath *vector, const gchar *path) { GeglPathPrivate *priv = GEGL_PATH_GET_PRIVATE (vector); const gchar *p = path; InstructionInfo *previnfo = NULL; gdouble x0, y0, x1, y1, x2, y2; while (*p) { gchar type = *p; InstructionInfo *info = lookup_instruction_info(type); if (!info && ((type>= '0' && type <= '9') || type == '-')) // [1] { if (previnfo->type == 'M') // here { info = lookup_instruction_info(type = 'L'); } else if (previnfo->type == 'm') { info = lookup_instruction_info(type = 'l'); } else if (previnfo->type == ' ') g_warning ("EEEK"); } ... if (*p) p++; } gegl_path_dirty (vector); }

漏洞成因也很简单gegl_path_parse_string主要的作用是针对 xml 中传入的路径 对传入的路径进行上色 例如<gegl:fill-path path='M100,100 L200,100 L200,200 Z' color=ff0000> 就是针对路径 "M100,100 L200,100 L200,200 Z" 定义的区域上红色

如果我们传入<gegl:fill-path path="0"> 可以发现*p = path = type 那么此时type = 0 可以顺利通过[1]处的 if 语句 然后到达 crash 处

至于为什么 crash 可以发现在函数刚开始InstructionInfo *previnfo = NULL;且后面没有对previnfo进行赋值导致访问0x0地址 导致SEGV

漏洞 2 - 资源耗尽漏洞

经过继续的Fuzz发现一个资源耗尽漏洞 可以申请0xfffffffffffd9d7c大小的堆块 很显然这是一个地址 肯定是在申请堆块之前存在问题

可以看到是g_malloc之前的问题 直接上GDB然后查栈帧

很明显问题的调用存在于datauri_parse_header -> g_strndup -> g_malloc 重点就是这个datauri_parse_header

c
static gchar ** datauri_parse_header(const gchar *uri, gchar **raw_data_out, gint *header_items_no_out) { // Determine data format const gchar * header_end = g_strstr_len (uri, -1, ","); const gint data_prefix_len = strlen("data:"); const gint header_len = header_end - uri - data_prefix_len; gchar *header = g_strndup(uri+data_prefix_len, header_len); // here gchar **header_items = g_strsplit(header, ";", 3); gint header_items_no = -1; while (header_items[++header_items_no]) { } g_free(header); if (header_items_no_out) { *header_items_no_out = header_items_no; } if (raw_data_out) { *raw_data_out = (gchar *)uri+data_prefix_len+header_len; } return header_items; }

经过动态调试很明显问题是出在header_end这个值上

其中g_strstr_len(const gchar* haystack, gssize haystack_len, const gchar* needle)的作用是从haystack字符串中搜索needle其中最大搜索长度为haystack_len

很显然传入的参数uri并没有问题 问题主要出现在g_strstr_len的调用上

因为匹配长度是-1 也就是0xffffffffffffffff 可以简单理解成无穷大 因为 uri 是一个存在栈上的参数 如果 uri 中没有匹配到,那近乎就是实现了全栈的搜索 很显然这是不合理的 返回回来的地址自然也是预料之外的 那么当后面调用str_dup时 因为长度过大 会申请一个堆块用于存放输出的字符串 很显然这时候申请的堆块长度是不合理的 自然也就造成了资源耗尽

本文作者:Du4t

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!