# Secure Uploader
- Category: Web- Points: 150- Author: clubby789
```asciiarmorA new secure, safe and smooth uploader!```
## Exploring
We have been given the source code and Dockerfile again, meaning that we could test the challenge locally again:
```secureuploader│   Dockerfile│└───app    │   app.py    │   database.db    │   start.sh    │    └───uploads            .gitkeep```
Fantastic, let us first check the Dockerfile:
```dockerfileFROM python:3-alpineRUN  pip install --no-cache-dir flask gunicorn
RUN addgroup -S ctf && adduser -S ctf -G ctf
COPY app /appCOPY flag.txt /flagWORKDIR /app
RUN chown -R ctf:ctf /app && chmod -R 770 /appRUN chown -R root:ctf /app && \  chmod -R 770 /app
USER ctfENTRYPOINT ["/app/start.sh"]
```
Most importantly, we know where the `flag` file is located again! The `start.sh` script seems to be called:
```bash#!/bin/shgunicorn --chdir /app app:app -w 4 --threads 4 -b 0.0.0.0:1337```
Brilliant, gunicorn is used to host servers, this will yet again help with local testing. Time to check out the actual app:
```pythonfrom flask import Flask, request, redirect, gimport sqlite3import osimport uuid
app = Flask(__name__)
SCHEMA = """CREATE TABLE files (id text primary key,path text);"""
def db():    g_db = getattr(g, '_database', None)    if g_db is None:        g_db = g._database = sqlite3.connect("database.db")    return g_db
@app.before_first_requestdef setup():    os.remove("database.db")    cur = db().cursor()    cur.executescript(SCHEMA)```
Going part-by-part, we can see that an SQLite database structure is provided in the variable `SCHEMA`, where the table's name is `files` and it has two columns `id` (which is also the primary key) and `path`. The function `db()` returns an object for database interaction, and the `@app.before_first_request` is self-explanatory. On start, remove the database and create it again (essentially starting fresh). 
Moving on!
```python@app.route('/')def hello_world():    return """<html><body><form action="/upload" method="post" enctype="multipart/form-data">    Select image to upload:    <input type="file" name="file">    <input type="submit" value="Upload File" name="submit"></form>
</body></html>"""
@app.route('/upload', methods=['POST'])def upload():    if 'file' not in request.files:        return redirect('/')    file = request.files['file']    if "." in file.filename:        return "Bad filename!", 403    conn = db()    cur = conn.cursor()    uid = uuid.uuid4().hex    try:        cur.execute("insert into files (id, path) values (?, ?)", (uid, file.filename,))    except sqlite3.IntegrityError:        return "Duplicate file"    conn.commit()    file.save('uploads/' + file.filename)    return redirect('/file/' + uid)```
In the basic `/` route we only have a simple upload form, nothing fancy. The `/upload` route uses the `upload()` function, where we can see that no dots are allowed in the filenames, preventing us from the performing any `directory traversal` attacks. After the "dot check" is performed an `insert` query is performed, and our file is entered into the database, through what looks like a safe parameter substitution. The files are saved in the `uploads/` directory and then we are redirected to our file in `/file/+uid`. 
But what is actually exploitable? 
```python@app.route('/file/<id>')def file(id):    conn = db()    cur = conn.cursor()    cur.execute("select path from files where id=?", (id,))    res = cur.fetchone()    if res is None:        return "File not found", 404    with open(os.path.join("uploads/", res[0]), "r") as f:        return f.read()
if __name__ == '__main__':  app.run(host='0.0.0.0')
```
 After navigating to a `/file/<id>` part of the app, the `file()` function is executed, where most notably we can see the `os` package being used! 
```pythonos.path.join("uploads/",res[0],"r")```
Our input (being the filename) goes in as `res[0]`, but what does `os.path.join` actually do? Best to consult the [docs](https://docs.python.org/3/library/os.path.html#os.path.join):
```asciiarmorJoin one or more path components intelligently. The return value is the concatenation of path and any members of *paths with exactly one directory separator following each non-empty part except the last, meaning that the result will only end in a separator if the last part is empty. If a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.```
Time to move on to exploitation.
## Exploit
Remember the `Dockerfile`?
```dockerfileCOPY flag.txt /flag```
The flag file is copied to the root directory of the system `/` and renamed to not have any dots in the name ie. `/flag`. And if we remember a really important part of the documentation:
```asciiarmorIf a component is an absolute path, all previous components are thrown away and joining continues from the absolute path component.```
The absolute path to `/flag` is `/flag`. Time to test the exploit:
![](./page.png)
Time to upload and manipulate the filename in Burpsuite:
![](./exploit.png)
Hit `forward` and we'll see:
![](./flag.png)
There we go! Always read the documentation properly! 
```rarctf{4lw4y5_r34d_th3_d0c5_pr0p3rly!-71ed16}```
