OWNCLOUD: DETAILS ABOUT CVE-2023-49103 AND CVE-2023-49105

OWNCLOUD: DETAILS ABOUT CVE-2023-49103 AND CVE-2023-49105

Introduction 介绍

On November 21th 2023, Owncloud released a new version patching two vulnerabilities (1 and 2) we reported a few weeks before. The vulnerabilities were assigned CVE-2023-49103 and CVE-2023-49105.
th 2023 年 11 月 21 日,Owncloud 发布了一个新版本,修补了我们几周前报告的两个漏洞(1 和 2)。这些漏洞的编号为 CVE-2023-49103 和 CVE-2023-49105。

Note: We performed the security assessment on dockerized Owncloud 10.12.2.
注意:我们在 dockerized Owncloud 10.12.2 上执行了安全评估。

CVE-2023-49103: A slippery PHPinfo
CVE-2023-49103:狡猾的 PHPinfo

One of the advisories mentioned a PHPinfo bug which boasted a CVSS of 10 out of 10. Indeed, on docker installs, if one could reach the PHPinfo page, they’d get access to every environment variable, and juicy secrets such as the username and password of the ownCloud administrator. As soon as it came up, it received lots of attention ~and an equal number of bad takes~.
其中一个公告提到了一个 PHPinfo 错误,该错误拥有 10 分(满分 10 分)的 CVSS。事实上,在 docker 安装中,如果可以访问 PHPinfo 页面,他们就可以访问每个环境变量,以及多汁的秘密,例如 ownCloud 管理员的用户名和密码。它一出现,就受到了很多关注~和同样数量的坏镜头~。

Although reaching the file directly yields a redirect, appending anything that looks like a resource allow access:
尽管访问文件会直接产生重定向,但附加任何看起来像资源的内容都允许访问:

http://docker.local/apps/graphapi/vendor/microsoft/microsoft-graph/tests/GetPhpInfo.php/a.css

With this horror out of the way, let’s talk about the vulnerability that should have gotten the attention instead.
撇开这种恐怖,让我们谈谈应该引起注意的漏洞。

CVE-2023-49105: Privilege escalation, remote code execution
CVE-2023-49105:权限提升、远程代码执行

This bug, which is not related to docker, got a CVSS of 9.8. It affects every single ownCloud from version 10.6.0 to version 10.13.0. Regrettably, ownCloud’s advisory is not precise enough, and only describes part of the impact for unauthenticated users.
这个与 docker 无关的 bug 的 CVSS 为 9.8。它会影响从版本 10.6.0 到版本 10.13.0 的每个 ownCloud。遗憾的是,ownCloud的公告不够精确,只描述了对未经身份验证的用户的部分影响。

The real impact is twofold:
真正的影响是双重的:

  • Attackers with no account can take control (CRUD) of every file of any account. In some cases, they may be able to RCE.
    没有帐户的攻击者可以控制 (CRUD) 任何帐户的每个文件。在某些情况下,他们可能能够进行 RCE。
  • Attackers that have credentials of a standard account can escalate to administrator and get RCE.
    拥有标准帐户凭据的攻击者可以升级为管理员并获取 RCE。

Let’s first start with the unauthenticated attack.
让我们首先从未经身份验证的攻击开始。

From anonymous to user (and RCE)
从匿名到用户(和 RCE)

When issuing requests to some parts of the website, including the WEBDAV and CALDAV, users can authenticate by providing a username and a signature. The signature is computed from a user-specific key and elements from the HTTP request, such as the GET parameters, the HTTP method, etc. Sadly, by default, users do not have a key set. Their signing key, in this case, defaults to a blank string. As a result, an unauthenticated attacker can impersonate any user if they know their username.
当向网站的某些部分(包括 WEBDAV 和 CALDAV)发出请求时,用户可以通过提供用户名和签名进行身份验证。签名是根据特定于用户的密钥和 HTTP 请求中的元素(例如 GET 参数、HTTP 方法等)计算得出的。可悲的是,默认情况下,用户没有设置密钥。在本例中,其签名密钥默认为空白字符串。因此,未经身份验证的攻击者可以冒充任何用户,前提是他们知道其用户名。

Accessing WEBDAV as an attacker has incredible potential: one could read, create, modify, or delete any file a user possesses on the server. Someone said ransomware?
以攻击者身份访问 WEBDAV 具有不可思议的潜力:可以读取、创建、修改或删除用户在服务器上拥有的任何文件。有人说勒索软件?

Even scarier: whenever you upload files of certain types (images, for instance), a preview gets generated. For specific file formats, ownCloud uses ImageMagick to generate said preview. If the library is not up to date, an attacker will get remote code execution.
更可怕的是:每当您上传某些类型的文件(例如图像)时,都会生成预览。对于特定的文件格式,ownCloud 使用 ImageMagick 生成所述预览。如果库不是最新的,攻击者将获得远程代码执行。

However, if Imagemagick is not vulnerable, and you already have an account, there is another way to get RCE: escalating your privileges to full admin.
但是,如果 Imagemagick 不容易受到攻击,并且您已经有一个帐户,则还有另一种方法可以获得 RCE:将您的权限升级为完全管理员。

From user to admin (and RCE)
从用户到管理员(和 RCE)

Sadly for attackers, user settings and the administration panel cannot be accessed using signed URLs. To understand why, let’s check the code that handles the authentication through this mean:
可悲的是,对于攻击者来说,用户设置和管理面板无法使用签名 URL 访问。为了理解原因,让我们检查一下通过以下方法处理身份验证的代码:

# /apps/dav/lib/Connector/Sabre/Auth.php

$verifier = new Verifier($request, $this->config);
if ($verifier->isSignedRequest()) {
    if (!$verifier->signedRequestIsValid()) {
        return [false, 'Invalid url signature'];
    }
    // TODO: setup session ???
    $urlCredential = $verifier->getUrlCredential();
    $user = \OC::$server->getUserManager()->get($urlCredential);
    if ($user === null) {
        $message = \OC::$server->getL10N('dav')->t('User unknown');
        throw new LoginException($message);
    }
    if (!$user->isEnabled()) {
        $message = \OC::$server->getL10N('dav')->t('User disabled');
        throw new LoginException($message);
    }
    $this->userSession->setUser($user); // <--- here
    \OC_Util::setupFS($urlCredential);
    $this->session->close();
    return [true, $this->principalPrefix . $urlCredential];
}

If the signature is valid, Session::setUser() gets called:
如果签名有效, Session::setUser() 则调用:

public function setUser($user) {
    if ($user === null) {
        $this->session->remove('user_id');
    } else {
        $this->session->set('user_id', $user->getUID());
    }
    $this->activeUser = $user;
}

user_id is the only session variable that gets set when authenticating using a signed URL. It stores the name of the logged-in user.
user_id 是使用签名 URL 进行身份验证时设置的唯一会话变量。它存储登录用户的名称。

Now, if we try to reach a “standard” page of the website, for instance user settings, the authentication is checked like so:
现在,如果我们尝试访问网站的“标准”页面,例如用户设置,则会像这样检查身份验证:

if (\OC::$server->getUserSession()) {
    $request = \OC::$server->getRequest();
    $session = \OC::$server->getUserSession();
    $davUser = \OC::$server->getUserSession()->getSession()->get('AUTHENTICATED_TO_DAV_BACKEND');
    if ($davUser === null) {
        $session->validateSession();
    } else {
        ...
    }
}

Since AUTHENTICATED_TO_DAV_BACKEND is not set in our case, we end up in validateSession(), which checks that the session has a valid token. Since no token is set, we are logged out: we can’t access “standard pages” using only this bug.
由于 AUTHENTICATED_TO_DAV_BACKEND 在我们的例子中没有设置,我们最终会得到 validateSession() ,它检查会话是否具有有效的令牌。由于没有设置令牌,我们被注销:我们不能仅使用此错误访问“标准页面”。

However, we could look to elevate our privileges by logging in as a standard user, and then spoofing our user_id to admin: we’d keep the old session variables (such as AUTHENTICATED_TO_DAV_BACKEND), and would just change our ID.
但是,我们可以通过以标准用户身份登录来提升我们的权限,然后欺骗我们的 admin :我们将保留旧的会话变量(例如 AUTHENTICATED_TO_DAV_BACKEND ),并且只会更改我们的 user_id ID。

Sadly, further down the authentication process, verifyAuthHeaders() gets called. It checks that the user is properly authenticated.
可悲的是,在身份验证过程的进一步下, verifyAuthHeaders() 被调用。它检查用户是否已正确通过身份验证。

class Session implements IUserSession, Emitter {
    ...

    public function verifyAuthHeaders($request) {
        $shallLogout = false;
        try {
            ...

            foreach ($this->getAuthModules(true) as $module) {
                $user = $module->auth($request); # [1]
                if ($user !== null) {
                    if ($this->isLoggedIn() && $this->getUser()->getUID() !== $user->getUID()) { // [2]
                        $shallLogout = true;
                        break;
                    }
                    ...
                }
            }
        } catch (Exception $ex) {
            $shallLogout = true;
        }
        if ($shallLogout) { # [3]
            // the session is bad -> kill it
            $this->logout();
            return false;
        }
        return true;
    }
}

If an authentication module is able to authenticate a user [1], ownCloud makes sure that the user returned by the module matches the one stored in the user_id session key [2]. If it does not, we’re logged out [3].
如果身份验证模块能够对用户 [1] 进行身份验证,ownCloud 会确保该模块返回的用户与 user_id 会话密钥 [2] 中存储的用户匹配。如果没有,我们将注销 [3] 。

The only way to bypass this check is to make the auth() method of the module that authenticates us, TokenAuthModule, return null, despite having a valid session.
绕过此检查的唯一方法是使对我们进行身份验证的模块 auth() 的方法, TokenAuthModule 返回 null ,尽管具有有效的会话。

class TokenAuthModule implements IAuthModule {
    ...

    public function auth(IRequest $request) {
        ...
        $dbToken = $this->getToken($request, $token); # [1]
        if ($dbToken === null) {
            return null;
        }
        ...
        $uid = $dbToken->getUID();
        return $this->manager->get($uid);
    }

    private function getToken(IRequest $request, &$token) {
        $authHeader = $request->getHeader('Authorization'); # [2]
        if ($authHeader === null || \strpos($authHeader, 'token ') === false) {
            // No auth header, let's try session id
            try {
                $token = $this->session->getId();
            } catch (SessionNotAvailableException $ex) {
                return null;
            }
        } else {
            $token = \substr($authHeader, 6);
        }

        try {
            return $this->tokenProvider->getToken($token);
        } catch (InvalidTokenException $ex) {
            $token = null;
            return null;
        }
    }
}

We therefore need to make getToken() return NULL [1]. Luckily, the latter checks whether a token is present in the Authorization header before checking if a valid session is present [2]. By providing an incorrect token through this header, we can make TokenAuthModule::auth() return NULL despite being logged in, and thus bypass the username check.
因此,我们需要使 getToken() return NULL [1] 。幸运的是,后者在检查是否存在有效会话之前检查 Authorization 标头中是否存在 [2] 令牌。通过此标头提供不正确的令牌,我们可以在 TokenAuthModule::auth() 登录后返回 NULL,从而绕过用户名检查。

The resulting privilege escalation procedure is as so:
生成的权限升级过程如下:

  • Log in as a standard user
    以标准用户身份登录
  • Use the URL-signing mechanism to spoof your user_id
    使用 URL 签名机制来欺骗您的 user_id
  • Access any page with Authorization: token thisisnotavalidtoken
    访问 Authorization: token thisisnotavalidtoken 任何页面

This allows you to get from a normal user to the admin account. From there, there are various ways to get remote code execution. These are left as an exercise to the reader.
这允许您从普通用户访问管理员帐户。从那里,有多种方法可以远程执行代码。这些留给读者作为练习。

Conclusion 结论

CVE-2023-49105 allows you to either get complete access to the files of any user (and potentially, get RCE), or if you already have an account, escalate your privileges to admin, paving the way for remote code execution. The other, CVE-2023-49103, is a PHPinfo.
CVE-2023-49105 允许您完全访问任何用户的文件(并可能获得 RCE),或者如果您已经拥有帐户,请将您的权限升级为管理员,为远程代码执行铺平道路。另一个 CVE-2023-49103 是 PHPinfo。

We’re hiring! 我们正在招聘!

Ambionics is an entity of Lexfo, and we’re hiring! To learn more about job opportunities, do not hesitate to contact us at [email protected]We’re a french-speaking company, so we expect candidates to be fluent in our beautiful language.
Ambionics 是 Lexfo 的一个实体,我们正在招聘!要了解有关工作机会的更多信息,请随时通过 [email protected] 与我们联系。我们是一家讲法语的公司,因此我们希望候选人能够流利地使用我们优美的语言。

 

原文始发于Lexfo :OWNCLOUD: DETAILS ABOUT CVE-2023-49103 AND CVE-2023-49105

版权声明:admin 发表于 2023年12月5日 下午5:56。
转载请注明:OWNCLOUD: DETAILS ABOUT CVE-2023-49103 AND CVE-2023-49105 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...