# NetFS 1 (misc)  
Writeup by: [xlr8or](https://ctftime.org/team/235001)

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:  
```python  
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`:

```python  
# 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:  
```python  
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.  

Original writeup
(https://github.com/sbencoding/zer0pts_ctf_2023_writeups/tree/main/misc/netfs1).