{"id":17227,"date":"2019-12-17T16:40:03","date_gmt":"2019-12-18T00:40:03","guid":{"rendered":"http:\/\/www.palada.net\/index.php\/2019\/12\/17\/news-10963\/"},"modified":"2019-12-17T16:40:03","modified_gmt":"2019-12-18T00:40:03","slug":"news-10963","status":"publish","type":"post","link":"https:\/\/www.palada.net\/index.php\/2019\/12\/17\/news-10963\/","title":{"rendered":"Seven Critical Vulnerabilities Discovered in Portainer"},"content":{"rendered":"<div class=\"aem-Grid aem-Grid--12 aem-Grid--default--12\">\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p><i>A FortiGuard Labs Technical Advisory Report<\/i><\/p>\n<h2>Introduction<\/h2>\n<p>Portainer is a lightweight management UI which allows you to easily manage your Docker host or Swarm cluster. Over the past few months, FortiGuard Labs has been working closely with the Portainer team to address multiple critical vulnerabilities that we discovered in their Portainer application. In this technical advisory we will provide an overview of each of these vulnerabilities.<\/p>\n<p>At the time of writing, all of the issues identified in this blog have been fixed and published by Portainer. Fortinet\u2019s FortiGuard Labs appreciates their quick responses and timely fixes.<\/p>\n<h2>FG-VD-19-118 and FG-VD-19-119: Cross-Site Scripting (XSS) Vulnerabilities<\/h2>\n<p>All versions of Portainer <u>prior<\/u> to version 1.23.0 are vulnerable to two XSS vulnerabilities. The first XSS occurs in a multiselect HTML component that allows administrators to create a new user whose username can also be a JavaScript payload.<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image.img.png\/1576259096642\/portainer-one.png\" alt=\"Figure 1: A malicious username which will alert an authentication token\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 1: A malicious username which will alert an authentication token<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>Whenever an administrator visits a page which displays a list of users, the XSS payload will be triggered, as shown in Figure 2:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_1128577968.img.png\/1576259140899\/portainer-two.png\" alt=\"Figure 2: XSS is triggered in Teams page\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 2: XSS is triggered in Teams page<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>The Endpoint access management page is also vulnerable to the username-as-JavaScript payload, as demonstrated in Figure 3:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_567478668.img.png\/1576259755259\/portainer-three.png\" alt=\"Figure 3: XSS is triggered in the Endpoint access management page\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 3: XSS is triggered in the Endpoint access management page<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>The second XSS vulnerability exists in the confirmation modal when a user performs a file deletion. Any authorized user can upload files with an arbitrary file name. When other users try to delete a file containing a JavaScript payload, XSS will be triggered, as shown in Figure 4:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_1744549805.img.png\/1576259847073\/portainer-four.png\" alt=\"Figure 4: XSS is triggered in a confirmation modal\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 4: XSS is triggered in a confirmation modal<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<h2>FG-VD-19-123 \u2013 Path Traversal Vulnerability in the Upload Feature<\/h2>\n<p>Portainer enables users to upload files with the volume browser. The volume endpoint retrieves three parameters to upload files: volume ID, file name, and file path. However, only the file path is sanitized before the fix. Therefore, it\u2019s possible to traverse the path by manipulating the volume ID or file name to upload files to an arbitrary location inside the container.<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_1283750904.img.png\/1576259930952\/portainer-five.png\" alt=\"Figure 5: filename parameter is vulnerable to path traversal attack\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 5: filename parameter is vulnerable to path traversal attack<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>As a result, the file HELLO is not uploaded into the _data directory as usual, but into its parent instead:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_1690503760.img.png\/1576259988500\/portainer-six.png\" alt=\"Figure 6: The file is successfully uploaded into unintended directory\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 6: The file is successfully uploaded into unintended directory<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>By exploiting this vulnerability, any authorized user can upload and execute an arbitrary executable in a Portainer container by chaining an improper access control vulnerability (authorization bypass) and an insufficient check issue in Extension Activation.<\/p>\n<h2>FG-VD-19-122 &amp; FG-VD-19-124: Authorization Bypass<\/h2>\n<p>In Portainer versions <u>prior<\/u> to 1.23.0, the ownership model allows users to specify who has permission to access their resources. By default, Portainer enforces access control to the resources in the docker according to the access control level defined for each user, with the exception that Administrators have full control over all resources. However, the ownership model of Portainer relied on the object ID, which is usually a random unique UUID. If users know the object ID, they can eventually gain access to that resource.<\/p>\n<p>At first glance, relying on a random unique UUID to provide Administrators with full resource control \u00a0seems like a reasonable solution because it should be nearly impossible to guess or brute force the random object ID. However, volume resources can also be accessed via volume name, which is much easier to figure out.<\/p>\n<p>Below is an example, in which the volume is owned by Administrators:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_706608468.img.png\/1576260095187\/portainer-seven.png\" alt=\"Figure 7: The volume access is granted to administrators only\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 7: The volume access is granted to administrators only<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>Other users who do not have permission will not see this volume by default:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_725935058.img.png\/1576260132624\/portainer-eight.png\" alt=\"Figure 8: Only granted users can see private volumes\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 8: Only granted users can see private volumes<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>However, they can still access these volumes if they know the volume name, simply by directly querying the URL:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_123885251.img.png\/1576260175057\/portainer-nine.png\" alt=\"Figure 9: Unauthorized access to private volume\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 9: Unauthorized access to private volume<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>As a result, any user can download, upload, or delete any data in the volume. In addition, the Portainer volume contains a file called portainer.db, which is a database that stores user credentials. By manipulating this database, users can escalate their privileges to Administrator, or disclose other sensitive information.<\/p>\n<p>The problem is due to an improper access control on Extension Activation, in which any authenticated user can access the Extension page and activate licenses. Chaining with above arbitrary file upload allows an authenticated user to overwrite the extension executable and execute it by activating the corresponding extension, which leads to remote code execution in the Portainer container.<\/p>\n<p>Another authorization bypass appears in the Host Filesystem Management feature. Portainer allows an administrator to enable the feature to manage the host filesystem from the UI. Only Administrators have access to this feature.<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_65010266.img.png\/1576260266438\/portainer-ten.png\" alt=\"Figure 10: Standard users are denied from accessing to Host Filesystem Management\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 10: Standard users are denied from accessing to Host Filesystem Management<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>However, this authorization only happens in the UI. The API endpoint still allows any authenticated user to access this feature:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_541523580.img.png\/1576260324246\/portainer-eleven.png\" alt=\"Figure 11: List of files in the host filesystem is returned for the standard users\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 11: List of files in the host filesystem is returned for the standard users<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>At this point, any authenticated user will have full permission over the host filesystem. This is a critical vulnerability, but it only works if the Host Filesystem Management feature is enabled, which is not the default setup. The next vulnerability, FG-VD-19-120, is a different story.<\/p>\n<h2>FG-VD-19-120: Unrestricted Host Filesystem Access<\/h2>\n<p>In Portainer, users have two ways to create a container: create a container alone or create a multi-container as a stack. When creating a new container alone, users have an option to mount the host filesystem into the container:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn aem-GridColumn--default--12\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_1084386917.img.png\/1576260382714\/portainer-twelve.png\" alt=\"Figure 12: Mapping a path on host into the container\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 12: Mapping a path on host into the container<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>Administrators can disable the bind mount for non-administrators due to its potential security risk:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn--default--none aem-GridColumn aem-GridColumn--default--7 aem-GridColumn--offset--default--2\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_1144267870.img.png\/1576260409249\/portainer-thirteen.png\" alt=\"Figure 13: Administrators can disable bind mounts\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 13: Administrators can disable bind mounts<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>When bind mount is disabled, users are no longer allowed to map a volume with a host file path. However, there is no security check in place when we create a stack. By instructing Docker to bind mount a host file path into the container in the Compose file, we can access the host filesystem whether or not the bind mount is allowed.\u00a0<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn--default--none aem-GridColumn--default--9 aem-GridColumn aem-GridColumn--offset--default--1\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_714711041.img.png\/1576261241534\/portainer-fourteen.png\" alt=\"Figure 14: Bind mount in the Compose file\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 14: Bind mount in the Compose file<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<p>The patch by Portainer parses the Compose file content and disallows bind mount:<\/p>\n<\/p><\/div>\n<div class=\"cmp cmp-image aem-GridColumn--default--none aem-GridColumn--default--9 aem-GridColumn aem-GridColumn--offset--default--1\">               <noscript data-cmp-image=\"{&#34;smartImages&#34;:[],&#34;smartSizes&#34;:[],&#34;lazyEnabled&#34;:true}\">             <img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image_407897532.img.png\/1576261288833\/portainer-fifteen.png\" alt=\"Figure 15: The patch for FG-VD-19-120\"\/>         <\/noscript>          <span class=\"cmp-image--title\">Figure 15: The patch for FG-VD-19-120<\/span>         <\/div>\n<div class=\"cmp cmp-text aem-GridColumn aem-GridColumn--default--12\">\n<h2>Solutions<\/h2>\n<p>FortiGuard Labs reported seven vulnerabilities to Portainer in September 2019, and we have subsequently worked closely with the Portainer team to resolve all issues. All vulnerabilities are rated critical and easy to exploit, allowing attacker to steal the session, escalate privilege or access to host filesystem.<\/p>\n<p>Fortinet released the following IPS signatures to protect our customers from the identified XSS vulnerabilities:<\/p>\n<p style=\"margin-left: 40.0px;\">Portainer.Users.XSS<br \/> Portainer.VolumeBrowse.XSS<\/p>\n<p>Other vulnerabilities are logical vulnerabilities, and the best recommendation is for administrators to upgrade to Portainer release 1.23.0 as soon as possible.<\/p>\n<h2>Consider FortiGuard Labs Security Assessment Service For Your Solution<\/h2>\n<p>If you are interested in this kind of assessment for your software or application, FortiGuard Labs provides a tailor-made vulnerability assessment and penetration testing service that can help you improve the security of your products. Check out <a href=\"https:\/\/fortiguard.com\/services\/pentesting\">https:\/\/fortiguard.com\/services\/pentesting<\/a> for more information.<\/p>\n<p><i>Learn more about\u00a0how\u00a0<a href=\"https:\/\/www.fortinet.com\/fortiguard\/threat-intelligence\/threat-research.html?utm_source=nreleaseblog&amp;utm_campaign=2018-q2-fortiguardlabs-cta\">FortiGuard Labs<\/a>\u00a0provides\u00a0unmatched security and intelligence services using integrated\u00a0<a href=\"https:\/\/twitter.com\/hashtag\/AI?src=hashtag_click\">AI<\/a>\u00a0systems.\u00a0Find out about\u00a0the FortiGuard Security Services\u00a0<a href=\"https:\/\/www.fortinet.com\/support-and-training\/support-services\/fortiguard-security-subscriptions.html?utm_source=blog&amp;utm_campaign=2018-blog-security-services\">portfolio<\/a>\u00a0and <a href=\"https:\/\/www.fortinet.com\/fortiguard\/threat-intelligence\/threat-research.html?utm_source=nreleaseblog&amp;utm_campaign=2018-q2-fortiguardlabs-cta\">sign up<\/a>\u00a0for our weekly FortiGuard Threat Brief.\u00a0<\/i><\/p>\n<p><i>Read about the FortiGuard\u00a0<a href=\"https:\/\/www.fortinet.com\/support-and-training\/support-services\/fortiguard-security-subscriptions\/security-rating.html?utm_source=blog&amp;utm_campaign=2018-blog-security-rating-service\">Security Rating Service<\/a>, which provides security audits and best practices\u00a0to guide customers in designing, implementing, and maintaining the security posture best suited for their\u00a0organization.<\/i><\/p>\n<\/p><\/div>\n<div class=\"raw-import aem-GridColumn aem-GridColumn--default--12\">\n<div class=\"text-container\">\n<div id=\"om-qxx1b0gslklfu2kjckea-holder\"><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<\/p><\/div>\n<p><a href=\"http:\/\/feedproxy.google.com\/~r\/fortinet\/blog\/threat-research\/~3\/lEt6aibW0kc\/seven-critical-vulnerabilities-portainer.html\" target=\"bwo\" >http:\/\/feeds.feedburner.com\/fortinet\/blog\/threat-research<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p><img decoding=\"async\" src=\"\/blog\/threat-research\/seven-critical-vulnerabilities-portainer\/_jcr_content\/root\/responsivegrid\/image.img.png\/1576259096642\/portainer-one.png\"\/><br \/>FortiGuard Labs uncovered several vulnerabilities in Portainer, and has been working with the Portainer team to address them. Learn more about each vulnerability in this technical advisory.&lt;img src=&#8221;http:\/\/feeds.feedburner.com\/~r\/fortinet\/blog\/threat-research\/~4\/lEt6aibW0kc&#8221; height=&#8221;1&#8243; width=&#8221;1&#8243; alt=&#8221;&#8221;\/&gt;<\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"colormag_page_container_layout":"default_layout","colormag_page_sidebar_layout":"default_layout","footnotes":""},"categories":[10424,10378],"tags":[],"class_list":["post-17227","post","type-post","status-publish","format-standard","hentry","category-fortinet","category-security"],"_links":{"self":[{"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/posts\/17227","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/comments?post=17227"}],"version-history":[{"count":0,"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/posts\/17227\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/media?parent=17227"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/categories?post=17227"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.palada.net\/index.php\/wp-json\/wp\/v2\/tags?post=17227"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}