Skip to content

Instantly share code, notes, and snippets.

@hkva
Last active July 15, 2022 22:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hkva/d3cf8de59dfbd25499d77db0c06507e3 to your computer and use it in GitHub Desktop.
Save hkva/d3cf8de59dfbd25499d77db0c06507e3 to your computer and use it in GitHub Desktop.
Format string vulnerability finder
//
// Format string vulnerability finder
//
// $ gcc printfcheck.c -Wall -O3 -g -shared -lunwind -lunwind-x86_64 -o printfcheck.so
//
// $ gcc printfcheck.c -Wall -O3 -g -shared -m32 -lunwind -lunwind-x86 -o printfcheck.so
//
// $ PRINTFCHECK_TRACE=1 PRINTFCHECK_BACKTRACE=1 LD_PRELOAD=./printfcheck.so ./prog
//
#define _GNU_SOURCE
// Standard headers
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Linux headers
#include <dlfcn.h>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
// Libunwind
#include <libunwind.h>
// Check if a c-string is mutable
static bool is_str_mutable(const char* str) {
// gray beard unix wizardry
int devzero = open("/dev/zero", O_RDONLY);
char backup = str[0];
bool wrote = read(devzero, (void*)str, 1) == 1;
if (wrote) {
((char*)str)[0] = backup;
}
close(devzero);
return wrote;
}
// Target functions
static struct {
// stdio.h
// https://cplusplus.com/reference/cstdio/
typeof(fprintf)* fprintf;
typeof(printf)* printf;
typeof(snprintf)* snprintf;
typeof(sprintf)* sprintf;
typeof(vfprintf)* vfprintf;
typeof(vprintf)* vprintf;
typeof(vsnprintf)* vsnprintf;
typeof(vsprintf)* vsprintf;
} o = { 0 };
static void write_stdout(const char* msg) {
write(STDOUT_FILENO, msg, strlen(msg));
}
static void check_format_arg(const char* funcname, const char* format) {
// Load target functions
#define LOAD_FUNCTION(name) if (o.name == NULL) { o.name = dlsym(RTLD_NEXT, #name); }
LOAD_FUNCTION(fprintf);
LOAD_FUNCTION(printf);
LOAD_FUNCTION(snprintf);
LOAD_FUNCTION(sprintf);
LOAD_FUNCTION(vfprintf);
LOAD_FUNCTION(vprintf);
LOAD_FUNCTION(vsnprintf);
LOAD_FUNCTION(vsprintf);
#undef LOAD_FUNCTION
// Trace function calls
if (getenv("PRINTFCHECK_TRACE") != NULL) {
// use write() to prevent recursive call
write_stdout("[printf-check] ");
write_stdout(funcname);
write_stdout(" called with format arg \"");
write_stdout(format);
write_stdout("\"\n");
}
// Check for potentially vulnerable argument
if (is_str_mutable(format)) {
write_stdout("[printf-check] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
write_stdout("[printf-check] Format argument is potentially vulnerable\n");
write_stdout("[printf-check] !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
if (getenv("PRINTFCHECK_BACKTRACE") != NULL) {
// Print stack trace
struct {
unw_context_t ctx;
unw_cursor_t cur;
} unw = { 0 };
unw_getcontext(&unw.ctx);
unw_init_local(&unw.cur, &unw.ctx);
write_stdout("[printf-check] Backtrace:\n");
// Walk stack
while (unw_step(&unw.cur) > 0) {
char sym[512];
unw_word_t sym_offset;
if (unw_get_proc_name(&unw.cur, sym, sizeof(sym), &sym_offset) == 0) {
write_stdout("[printf-check] ");
write_stdout(sym);
write_stdout("\n");
}
}
}
if (getenv("PRINTFCHECK_BREAK") != NULL) {
raise(SIGTRAP);
}
}
}
int fprintf(FILE* stream, const char* format, ...) {
check_format_arg("fprintf", format);
// return original
va_list va;
va_start(va, format);
int r = o.vfprintf(stream, format, va);
va_end(va);
return r;
}
int printf(const char* format, ...) {
check_format_arg("printf", format);
// return original
va_list va;
va_start(va, format);
int r = o.vprintf(format, va);
va_end(va);
return r;
}
int snprintf(char* s, size_t n, const char* format, ...) {
check_format_arg("snprintf", format);
// return original
va_list va;
va_start(va, format);
int r = o.vsnprintf(s, n, format, va);
va_end(va);
return r;
}
int sprintf(char* s, const char* format, ...) {
check_format_arg("sprintf", format);
// return original
va_list va;
va_start(va, format);
int r = o.vsprintf(s, format, va);
va_end(va);
return r;
}
int vfprintf(FILE* stream, const char* format, va_list arg) {
check_format_arg("vfprintf", format);
// return original
return o.vfprintf(stream, format, arg);
}
int vprintf(const char* format, va_list arg) {
check_format_arg("vprintf", format);
// return original
return o.vprintf(format, arg);
}
int vsnprintf(char* s, size_t n, const char* format, va_list arg) {
check_format_arg("vsnprintf", format);
// return original
return o.vsnprintf(s, n, format, arg);
}
int vsprintf(char* s, const char* format, va_list arg) {
check_format_arg("vsprintf", format);
// return original
return o.vsprintf(s, format, arg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment