How an off-by-one calculation in Glibc's syslog logging routine allowed local users to overwrite heap buffers via su or sudo, achieving root privileges.
Imagine a mail room clerk who measures packages before putting them in boxes. If a package is 10 inches, he grabs a 10-inch box. But he forgot that the company always slaps a 3-inch shipping label on top! When he shoves the package into the box, it tears through the sides and crushes the fragile master safe sitting on the shelf next to it.
The attacker invokes a setuid binary like su with a 1024-byte argv[0] string.
The attacker provides invalid credentials, prompting syslog() to format an error message.
__vsyslog_internal() calculates the buffer size without properly accounting for the long program name.
The formatted message overflows into neighboring heap metadata, executing attacker shellcode as root.
采用清晰易读的高级编程语言展示(不含晦涩汇编或二进制机器码)。
// VULNERABLE: sysdeps/posix/syslog.c before patch
int __vsyslog_internal(int pri, const char *fmt, va_list ap, unsigned int mode_flags) {
size_t l = strlen(program_invocation_short_name);
// ROOT CAUSE:
// When reallocating the log buffer, the code adds 'l' to the length,
// but under multi-threaded logging or long format strings, 'l' is truncated!
char *buf = malloc(l + 1024);
if (!buf) return -1;
// Writes formatted string into undersized buffer!
int len = vsnprintf(buf, l + 1024, fmt, ap);
return len;
}
// SECURE: sysdeps/posix/syslog.c patch
int __vsyslog_internal(int pri, const char *fmt, va_list ap, unsigned int mode_flags) {
size_t l = strlen(program_invocation_short_name);
// 1. Explicitly verify integer overflow bounds before allocating
size_t total_size;
if (__builtin_add_overflow(l, 1024, &total_size)) {
return -1; // Reject dangerous oversized buffer
}
char *buf = malloc(total_size);
if (!buf) return -1;
// 2. Pass strictly validated total_size to vsnprintf
int len = vsnprintf(buf, total_size, fmt, ap);
return len;
}