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 :

Format Function

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 :

Parameter
Output
Passed as

%%

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

vuln.c
#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

See where the memory got leaked?

Yeah that's how you use it 😎👍

Tips 📋

  1. 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.

  2. 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.

  3. 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.

  4. If i got another, i will write here 🤝

Last updated