Format String Vulnerability
Definition : bug that take advantage of an easily avoidable programmer error. If the programmer passes an attacker-controlled buffer as an argument to a printf (or any of the related functions), the attacker can perform writes to arbitrary memory addresses
Format function
Definition : Is a function which converts a primitive variable of the programming language into a human-readable string representation
Below is an example of Format Function that vulnerable to Format String Attack :
fprint
printf
sprintf
snprintf
vfprintf
vprintf
vsprintf
vsnprintf
Format Parameter
Definition : like %x %s defines the type of conversion of the format function
Below is an example of Format Parameter that can be useful for you :
%%
character
reference
%p
External representation of a pointer to void
reference
%d
decimal
value
%c
character
%u
unsigned decimal
value
%x
hexa
value
%s
string
reference
%n
Writes the number of characters into a pointer
reference
Example
Let's take the example from PicoCTF.
Challenge Name : Stonk Market
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#define FLAG_BUFFER 128
#define MAX_SYM_LEN 4
typedef struct Stonks {
int shares;
char symbol[MAX_SYM_LEN + 1];
struct Stonks *next;
} Stonk;
typedef struct Portfolios {
int money;
Stonk *head;
} Portfolio;
int view_portfolio(Portfolio *p) {
if (!p) {
return 1;
}
printf("\nPortfolio as of ");
fflush(stdout);
system("date"); // TODO: implement this in C
fflush(stdout);
printf("\n\n");
Stonk *head = p->head;
if (!head) {
printf("You don't own any stonks!\n");
}
while (head) {
printf("%d shares of %s\n", head->shares, head->symbol);
head = head->next;
}
return 0;
}
Stonk *pick_symbol_with_AI(int shares) {
if (shares < 1) {
return NULL;
}
Stonk *stonk = malloc(sizeof(Stonk));
stonk->shares = shares;
int AI_symbol_len = (rand() % MAX_SYM_LEN) + 1;
for (int i = 0; i <= MAX_SYM_LEN; i++) {
if (i < AI_symbol_len) {
stonk->symbol[i] = 'A' + (rand() % 26);
} else {
stonk->symbol[i] = '\0';
}
}
stonk->next = NULL;
return stonk;
}
int buy_stonks(Portfolio *p) {
if (!p) {
return 1;
}
/*
char api_buf[FLAG_BUFFER];
FILE *f = fopen("api","r");
if (!f) {
printf("Flag file not found\n");
exit(1);
}
fgets(api_buf, FLAG_BUFFER, f);
*/
int money = p->money;
int shares = 0;
Stonk *temp = NULL;
printf("Using patented AI algorithms to buy stonks\n");
while (money > 0) {
shares = (rand() % money) + 1;
temp = pick_symbol_with_AI(shares);
temp->next = p->head;
p->head = temp;
money -= shares;
}
printf("Stonks chosen\n");
char *user_buf = malloc(300 + 1);
printf("What is your API token?\n");
scanf("%300s", user_buf);
printf("Buying stonks with token:\n");
printf(user_buf);
// TODO: Actually use key to interact with API
view_portfolio(p);
return 0;
}
Portfolio *initialize_portfolio() {
Portfolio *p = malloc(sizeof(Portfolio));
p->money = (rand() % 2018) + 1;
p->head = NULL;
return p;
}
void free_portfolio(Portfolio *p) {
Stonk *current = p->head;
Stonk *next = NULL;
while (current) {
next = current->next;
free(current);
current = next;
}
free(p);
}
int main(int argc, char *argv[])
{
setbuf(stdout, NULL);
srand(time(NULL));
Portfolio *p = initialize_portfolio();
if (!p) {
printf("Memory failure\n");
exit(1);
}
int resp = 0;
printf("Welcome back to the trading app!\n\n");
printf("What would you like to do?\n");
printf("1) Buy some stonks!\n");
printf("2) View my portfolio\n");
scanf("%d", &resp);
if (resp == 1) {
buy_stonks(p);
} else if (resp == 2) {
view_portfolio(p);
}
free_portfolio(p);
printf("Goodbye!\n");
exit(0);
}
Have a look at the code given. Do you notice something?
Line 88-92 is vulnerable to Format String Attack
So we need to enter the Format Parameter like %x-%x-%x-%x-%x-%x into the program to leak some of the memory

Yeah that's how you use it 😎👍
Tips 📋
If you don't get the leaked memory that you need, just spam the Format Parameter a lot. Yes, a lot. It can be up to hundreds but you should get what you want in just a few.
Let say you want to display just a certain part of the memory, try using this %n$p . n is an index of the memory that you want. This certainly help you.
When you got the memory leaked (some will be in hex or depends on what parameter you put), put that in Cyberchef and let it cook. If you don't see what you want yet, just remember that binary number can change the translated data. So, don't be shy to remove the binary one by one until it reveal itself.
If i got another, i will write here 🤝
Last updated