Loading HuntDB...

\OCA\DAV\CardDAV\ImageExportPlugin allows serving arbitrary data with user-defined or empty mimetype

Medium
N
Nextcloud
Submitted None
Reported by lukasreschke

Vulnerability Details

Technical details and impact analysis

Cross-site Scripting (XSS) - Generic
The SabreDAV plugin `\OCA\DAV\CardDAV\ImageExportPlugin` is used for displaying pictures of a VCF. It registers on a GET request on a CardDAV element and acts when the query parameter `photo` is sent. The logic can be seen below: ``` /** * Intercepts GET requests on addressbook urls ending with ?photo. * * @param RequestInterface $request * @param ResponseInterface $response * @return bool|void */ function httpGet(RequestInterface $request, ResponseInterface $response) { $queryParams = $request->getQueryParameters(); // TODO: in addition to photo we should also add logo some point in time if (!array_key_exists('photo', $queryParams)) { return true; } $path = $request->getPath(); $node = $this->server->tree->getNodeForPath($path); if (!($node instanceof Card)) { return true; } $this->server->transactionType = 'carddav-image-export'; // Checking ACL, if available. if ($aclPlugin = $this->server->getPlugin('acl')) { /** @var \Sabre\DAVACL\Plugin $aclPlugin */ $aclPlugin->checkPrivileges($path, '{DAV:}read'); } if ($result = $this->getPhoto($node)) { $response->setHeader('Content-Type', $result['Content-Type']); $response->setStatus(200); $response->setBody($result['body']); // Returning false to break the event chain return false; } return true; } ``` As can be seen the the content-type is read from `$this->getPhoto($node)` as well as the body, looking at it's implementation shows that the data is directly read from the vCard: ``` function getPhoto(Card $node) { // TODO: this is kind of expensive - load carddav data from database and parse it // we might want to build up a cache one day try { $vObject = $this->readCard($node->get()); if (!$vObject->PHOTO) { return false; } $photo = $vObject->PHOTO; $type = $this->getType($photo); $val = $photo->getValue(); if ($photo->getValueType() === 'URI') { $parsed = \Sabre\URI\parse($val); //only allow blocked:// if ($parsed['scheme'] !== 'data') { return false; } if (substr_count($parsed['path'], ';') === 1) { list($type,) = explode(';', $parsed['path']); } $val = file_get_contents($val); } return [ 'Content-Type' => $type, 'body' => $val ]; } catch(\Exception $ex) { $this->logger->logException($ex); } return false; } ``` This means if somebody uploads a VCF with the following content this will deliver the content `<html><font color="red">test</font></html>` with an empty Content-Type. The photo is a base64 encoding of before mentioned string. ``` BEGIN:VCARD VERSION:3.0 FN:test UID:5cf6e5e2-ec37-4798-abb7-3c261eda92c9 PHOTO;ENCODING=b:PGh0bWw+PGZvbnQgY29sb3I9InJlZCI+dGVzdDwvZm9udD48L2h0bWw+ END:VCARD ``` Then it's sufficient to just access http://10.211.55.7/stable9/remote.php/dav/addressbooks/users/admin/contacts/5cf6e5e2-ec37-4798-abb7-3c261eda92c9.vcf?photo, the easiest to reproduce this is by enabling `debug` mode and using Internet Explorer since we employ CSP which largely mitigates the issue. As another remark, we should replace the `file_get_contents` with another implementation. This seems currently like a too risky implementation for me. {F114833}

Report Details

Additional information and metadata

State

Closed

Substate

Resolved

Submitted

Weakness

Cross-site Scripting (XSS) - Generic