Skip to content

Latest commit

 

History

History

netfs1

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

NetFS 1 (misc)

Writeup by: xlr8or

As part of this challenge we get access to a server (and its source code) written in Python. The purpose of this server is for the user to be able to read files on the system. Our goal is to read the file in secret/flag.txt, however before we can read files we must authenticate.

The system contains 2 users:

assert os.path.isfile("secret/password.txt"), "Password file not found."

MAX_SIZE = 0x1000
LOGIN_USERS = {
    b'guest': b'guest',
    b'admin': open("secret/password.txt", "rb").read().strip()
}
PROTECTED = [b"server.py", b"secret"]

assert re.fullmatch(b"[0-9a-f]+", LOGIN_USERS[b'admin'])

The password of the guest user is known, however the password of admin is unknown to us, other than the fact that it only consists of digits and letters a thorough f. Our goal is to authenticate with admin, since authenticating with guest, makes the user unable to access any files path, that contains any element of PROTECTED:

# Check filepath
if not self.is_admin and \
   any(map(lambda name: name in filepath, PROTECTED)):
    self.response(b"Permission denied.\n")
    continue

So let's take a closer look at the authentication method:

    def authenticate(self):
        """Login prompt"""
        username = password = b''
        with Timeout(30):
            # Receive username
            self.response(b"Username: ")
            username = self.recvline()
            if username is None: return

            if username in LOGIN_USERS:
                password = LOGIN_USERS[username]
            else:
                self.response(b"No such a user exists.\n")
                return

        with Timeout(30):
            # Receive password
            self.response(b"Password: ")
            i = 0
            while i < len(password):
                c = self._conn.recv(1)
                if c == b'':
                    return
                elif c != password[i:i+1]:
                    self.response(b"Incorrect password.\n")
                    return
                i += 1

            if self._conn.recv(1) != b'\n':
                self.response(b"Incorrect password.\n")
                return

        self.response(b"Logged in.\n")
        self._auth = True
        self._user = username

First, the system reads our username, and ensures, that it is known in the system. Afterwards we continue with checking the password, however there's a flaw here, that allows us to guess the password one character at a time. The password is received by the server one character at a time, and if that given character doesn't match the password, then we get notified that the password is incorrect. However if the character we sent is correct, then the server will wait for the next character.

This means that we can try all 16 possibilities for a given position, and the attempt, which doesn't generate an Incorrect password. response gets appended to the overall password we have so far.

Using solve.py we can bruteforce the password, and use it to authenticate as admin and read the flag file.