Web 7(FILE STORER):

The file storage website was a login on signup page . The goal was to find the admins password. we had no clue while accessing the page. But later on it went so interesting.

so simply typed a admin as username and a dummy value as password for understanding the workflow. It returned bad password. And then signed up as a new user.

The app was expecting a URL and the password to download the file. Fired up burp and check the request and response. Check for /flag.txt in the url and the app returned “file already exists”

So the app might be checking for the same file names already present in the server and the filename is assigned from the name we give on the url path. So tried a random file to check how it responds.

So it actually writes the files to the files directory. Tried to guess the folder name and tried various SSRF techniques and failed.

After that we gave up a opened the hint. And the hint was “Can’t solve it? Git gud.” So there is a local git repo on the server. We actually tried the /.git path on the home path not on /files/ path.

Learning: Enumerate Everywhere !

So we finally found that there’s a .git directory under files .

But its not a open listed directory. So actually need to pick up each files by its exact path. So at first we checked the commit logs.

And found that there’s one initial commit. So we downloaded that object from the object directory. Generally, in git structure the hash will be path for the objects with first two characters as the parent directory and the rest will be file name. The git objects are compressed data formats which can be decompressed with the simple python program using zlib package.

We ended up in a gpg signature. And had no clue what need to do with that.

Hours went..

We checked the index file (shit! we should’ve checked this one first)

Here we can see the files on the server. Index.py is the main application program. So tried to get directly, But..

So downloaded that partially readable index file. And googled for how to read the git index file . And found a utility called parse_git_index.c on gist .

Now we have the hashes again, So downloaded the decompressed the index.py file using previous technique

The whole code was

from flask import Flask, request, render_template, abort

import os, requests



app = Flask(__name__)



class user:

def __init__(self, username, password):

self.username = username

self.__password = password

self.files = []

def getPass(self):

return self.__password



users = {}



users["admin"] = user("admin", os.environ["FLAG"])





def custom500(error):

return str(error), 500



@app.route("/", methods=["GET", "POST"])

def mainpage():

if request.method == "POST":

if request.form["action"] == "Login":

if request.form["username"] in users:

if request.form["password"] == users[request.form["username"]].getPass():

return render_template("index.html", user=users[request.form["username"]])

return "wrong password"

return "user does not exist"

elif request.form["action"] == "Signup":

if request.form["username"] not in users:

users[request.form["username"]] = user(request.form["username"], request.form["password"])

return render_template("index.html", user=users[request.form["username"]])

else:

return "user already exists"

elif request.form["action"] == "Add File":

return addfile()

return render_template("loggedout.html")



#beta feature for viewing info about other users - still testing

@app.route("/user/<username>", methods=['POST'])

def getInfo(username):

val = getattr(users[username], request.form['field'], None)

if val != None: return val

else: return "error"



@app.route("/files/<path:file>", methods=["GET"])

def getFile(file):

if "index.py" in file:

return "no! bad user! bad!"

return open(file, "rb").read()



def addfile():

if users[request.form["username"]].getPass() == request.form["password"]:

if request.form['url'][-1] == "/": downloadurl = request.form['url'][:-1]

else: downloadurl = request.form['url']

if downloadurl.split("/")[-1] in os.listdir("."):

return "file already exists"

file = requests.get(downloadurl, stream=True)

f = open(downloadurl.split("/")[-1], "wb")

first = True

for chunk in file.iter_content(chunk_size=1024*512):

if not first: break

f.write(chunk)

first = False

f.close()

users[request.form["username"]].files.append(downloadurl.split("/")[-1])

return render_template("index.html", user=users[request.form["username"]])

return "bad password"



if __name__ == "__main__": app.run(host="0.0.0.0")

On reviewing the code, a admin user object was created with flag value as password.

users["admin"] = user("admin", os.environ["FLAG"])

And then found this interesting hidden feature to view all user info

#beta feature for viewing info about other users - still testing

@app.route("/user/<username>", methods=['POST'])

def getInfo(username):

val = getattr(users[username], request.form['field'], None)

if val != None: return val

else: return "error"

So this is the thing!

After checking the user Class, there are three attributes — username,__pasword and files.

So we need to replace the <username> with admin __password for the field parameter on the request. But failed.

The username attribute worked but not the __password

Until this point I didn’t knew that the double underscore has a special meaning and not sure how to deal with that. So I created the same user class and created a new object and checked all the attributes of the object using __dict__

Ohh .. After googling the same concept.

` __` means `_classname__attributename`.