this cute little project is part of the 2025 SpookyCTF games hosted by the NJIT Information & Cybersecurity Club.
i initially wanted to make a buffer overflow exploit since that is my bread and butter as a C programmer, however, I am far from the first person to have that idea so i needed to think of something else...
the idea here is that this is a semi realistic program for some game you may one day play. running the executable as is there is no vulnerablility i know of...(prove me wrong)... the quirk here is that there is actually a debug mode which can be accessed by running ./game --debug
the debug mode allows a debug print function to run but there is something weird going on with it, it seems to print some broken text followed by only one value. this is your in as a challenger, the code running is actually something like this:
printf(name, &name, governor, &governor);
this seems like an honest mistake and at a glance maybe even not a mistakei, however, it is vulnerable to a format string exploit
when this runs, the char array in name may be treated as the format string for printf...
this means you can insert something like %p %p %p %p or %x %x %x %x in the name buffer and the compiler will treat it like: printf("%p %p %p %p", &name, governor, &governor)
this cool but not inherently vulnerable if that is all you can do...
we can do more though!
assume we are able to print a few memory addresses, we can now do some math to verify that the third positonal argument to printf is indeed the memory address of governor
if it is, then we can take advantage of another quirk of the format string to actually write the value of governor to whatever we want
https://man7.org/linux/man-pages/man3/printf.3.html <-- read this for printf spec
https://tc.gts3.org/cs6265/tut/tut05-fmtstr.html <-- awesome as well
%n can be used to write the number of bytes written by printf into the value at a specified memory address
for example, printf("A%n", &bytes) -> this will write 1 (1 char/sizeof(char)) into the value of bytes!
we can also use %hn to write 1 as a short or even %hhn to write 1 as a single byte
you can see how this exploit can get interesting...
you can now use positional specifiers to write to arbitrary addresses, ex) %3$hhn -> writes number of bytes printed into the third printf argument.
this challenge doesnt take full advantage but you can read/write as many "printf arguments" as you want(until you segfault on some bad value)"
when printf is called, it loads the stack like so:
[argument 1]
[argument 2]
[argument 3]
[format specifier]
when used correctly the format specifier tells the program to read one of those arguments and it does, but if somehow you asked for argument 0 then printf would just grab whatever data is on the stack before argument 1.
your compiler stops this but in our example we provide the format string at runtime, there is no check!
in some other challenge you could possibly modify the stack in some way to produce your desired behavior
because i cannot control myself here is an example:
suppose stack looks like this
[main]
[mains data]
[return to main]
[somefunction]
[somefunctions data] < this contains printf(some user inputted buffer)
.......
if we can understand the size of the somefunction stack entry, we can use printf positional arguments to go back as far as the return to main pointer and override it with %n!
this gives us the ability to preform arbitrary code execution, how cool is that!
anyways as far as this challenge, we already know that the address of governer is the third positional argument
we can construct a string which will write 1, as a byte, into that third positional argument
something like this does the job: A%3$hhn
i had a world of fun writing this challenge and the happy little game that goes with it. i will make this repository public after the CTF completes on haloween weekend of 2025
reach out to me at [email protected] with any questions or concerns, (it makes my whole day when i get emails)
good luck and thank you!