Possible heap chunk metadata overwrite in gdk-pixbuf.c
My colleague @pedrib and I have been fuzzing in gdk-pixbuf and we found a memory corruption bug.
The bug is found in gdk_pixbuf_set_option() in gdk-pixbuf.c, shown below. This bug can be reproduced, through the attached file, in this report.
GQuark quark;
gchar **options;
gint n = 0;
g_return_val_if_fail ((G_TYPE_CHECK_INSTANCE_TYPE ((pixbuf), GDK_TYPE_PIXBUF)), FALSE); /* crash */
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (value != NULL, FALSE);
After analyzing the bug, through dynamic analysis, it was possible to identify that the address of context->animation->pixbuf[i]
in th
e ani_load_chunk
function in io-ani.c
, was being corrupted with the value 0x21.
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
──────────── registers ────
$rax : 0x0000555dee04bd20 → 0x0000000000000006
$rbx : 0x0000555dee040f60 → 0x00000000000015fe
$rcx : 0x3
$rdx : 0x0000555dee055510 → "ConfetACON"
$rsp : 0x00007ffd23e2c340 → 0x00007f9dcb20bb80 → 0x0000000000000000
$rbp : 0x0000555dedd4ce73 → 0x754100656c746954 ("Title"?)
$rsi : 0x0000555dee04bd20 → 0x0000000000000006
$rdi : 0x21
$rip : 0x0000555dedd410dc → <gdk_pixbuf_set_option+44> mov rax, QWORD PTR [r13+0x0]
$r8 : 0x7
$r9 : 0x8c
$r10 : 0x00007f9dcb52e12b → 0x747265737361000a
$r11 : 0x1
$r12 : 0x88888889
$r13 : 0x21
$r14 : 0x0000555dee055510 → "ConfetACON"
$r15 : 0x4
$eflags: [zero carry parity adjust sign trap INTERRUPT direction overflow RESUME virtualx86 identification]
$cs: 0x0033 $ss: 0x002b $ds: 0x0000 $es: 0x0000 $fs: 0x0000 $gs: 0x0000
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
────────── code:x86:64 ────
0x555dedd410d1 <gdk_pixbuf_set_option+33> mov r14, rdx
0x555dedd410d4 <gdk_pixbuf_set_option+36> call 0x555dedd3f860 <gdk_pixbuf_get_type>
0x555dedd410d9 <gdk_pixbuf_set_option+41> mov rsi, rax
→ 0x555dedd410dc <gdk_pixbuf_set_option+44> mov rax, QWORD PTR [r13+0x0]
0x555dedd410e0 <gdk_pixbuf_set_option+48> test rax, rax
0x555dedd410e3 <gdk_pixbuf_set_option+51> je 0x555dedd410ea <gdk_pixbuf_set_option+58>
0x555dedd410e5 <gdk_pixbuf_set_option+53> cmp QWORD PTR [rax], rsi
0x555dedd410e8 <gdk_pixbuf_set_option+56> je 0x555dedd410fa <gdk_pixbuf_set_option+74>
0x555dedd410ea <gdk_pixbuf_set_option+58> mov rdi, r13
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
This happens due to context->pos
, in the loop shown below in io-ani.c.
for (i = 0; i < context->pos; i++)
gdk_pixbuf_set_option (context->animation->pixbufs[i], "Title", context->title);
This loop is called twice, in which the first time everything happens as expected, as this context->pos
is incremented with each load of icon subchunks from the .ani file. That is, in our POC, context->pos=5
.
static void prepared_callback (GdkPixbufLoader *loader, gpointer data){
AniLoaderContext *context = (AniLoaderContext*)data;
GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf (loader);
if (!pixbuf)
return;
if (gdk_pixbuf_get_width (pixbuf) > context->animation->width)
context->animation->width = gdk_pixbuf_get_width (pixbuf);
if (gdk_pixbuf_get_height (pixbuf) > context->animation->height)
context->animation->height = gdk_pixbuf_get_height (pixbuf);
if (context->title != NULL)
gdk_pixbuf_set_option (pixbuf, "Title", context->title);
if (context->author != NULL)
gdk_pixbuf_set_option (pixbuf, "Author", context->author);
g_object_ref (pixbuf);
context->animation->pixbufs[context->pos] = pixbuf;
if (context->pos == 0)
{
(* context->prepared_func) (pixbuf, GDK_PIXBUF_ANIMATION (context->animation), context->user_data);
}
else {
GdkPixbuf *last = context->animation->pixbufs[context->pos - 1];
gint width = MIN (gdk_pixbuf_get_width (last), gdk_pixbuf_get_width (pixbuf));
gint height = MIN (gdk_pixbuf_get_height (last), gdk_pixbuf_get_height (pixbuf));
gdk_pixbuf_copy_area (last, 0, 0, width, height, pixbuf, 0, 0);
}
context->pos++;
}
The second time this loop will be called is when it crashes, because there are no more .icon subchunks in the file, and the context->pos
is still 5. This results in an overwrite.
What is being loaded for as the 5 context->animation->pixbufs[i]
is as follows:
gef➤ x/10g 0x555dee0554b0
0x555dee0554b0: 0x0000000000000000 0x0000000000000000
0x555dee0554c0: 0x0000000000000000 0x0000000000000021 -> metada (size) of context->animation->delay chunk
0x555dee0554d0: 0x0000dec90000dec9 -> data of context->animation->delay chunk
That is, metadata is being passed from the heap chunk that corresponds to context->animation->delay
.
With this, it is possible to be exploitable. This is if it is possible to create .ani files that allow us to create more icon subchunks (you are more inside the application, so I think you can answer this), because then it would be possible to overwrite the metadata of the heap chunk, which could result in remote code execution.