开源内容管理系统 Cockpit 的源代码中搜索错误。以下是其官方网站上对 Cockpit 的描述:
Cockpit 是一个无头 CMS,采用 API 优先方法,将内容放在首位。它旨在通过将内容管理与客户端的内容消费分离来简化发布过程。 Cockpit 只专注于管理内容的后端工作。与其担心通过页面传递内容,它的目标是通过简单的 API 跨不同渠道提供结构化内容。
在调查 Cockpit 源代码时,我们发现了许多漏洞。攻击者可以利用它们来控制任何用户帐户并执行远程代码执行。
在本文中,我将讨论技术细节并演示如何利用这些漏洞。
提取用户帐户名称
在源代码中,我们发现了两种易受 NoSQL 注入攻击的方法,可用于提取应用程序用户名。这些方法都不需要身份验证。
NoSQL 注入/auth/check
(CVE-2020-35846)
让我们考虑负责对应用程序用户进行身份验证check
的Auth控制器的方法:
验证::检查方法
以及模块的authenticate
功能:
验证功能
如您所见,该代码不检查用户参数的类型,这允许在查询中嵌入具有任意 MongoDB 运算符的对象。
这是盲注,因此要成功利用,您需要找到返回条件结果的方法。
在分析了方法源代码之后,我们开发了一种技术。本质上,我们在密码参数中传递了一个数组(而不是字符串)。这会导致password_verify函数显示一个关于无效值类型的警告:
验证功能
现在我将演示更多利用 NoSQL 盲注入的方法:
1. 使用$eq
运算符
该
$eq
操作符匹配字段的值等于指定值的文档。
例如,您可以使用它通过字典对名称进行暴力破解。
满足条件:已找到名为admin的用户
不满足条件:未找到名为admini 的用户
2. 使用$regex
运算符
为查询中的模式匹配字符串提供正则表达式功能
您可以使用它来暴力破解所有应用程序用户的名称。
满足条件:已找到名称以字符ad开头的用户
不满足条件:未找到名称以字符ada开头的用户
我们可以通过$nin
在查询中添加运算符来加速暴力破解,这将排除任何已经找到的用户:
$nin
选择字段值不在指定数组中的文档
条件满足:已找到名字以字符j开头的用户
不满足条件:未找到名称以字符a开头的用户(具有此名称的唯一用户是admin,但该用户已从搜索中排除)
我们可以通过向正则表达式添加一个固定量词来调整它,以查找或限制字符串的长度:
满足条件:已找到名称以字符a开头并包含4个字符的用户
满足条件:已找到名称以字符ad开头且包含3个字符的用户
条件不满足:未找到名称以字符a开头并包含12个字符的用户
3.使用MongoLite库的$func操作符(默认使用)
这个非标准运算符允许调用标准函数$b
(任何带有单个参数的 PHP 函数),它接受一个等于字段的参数$a
(在本例中为用户字段):
通过传递 PHP 函数var_dump
或var_export
作为参数,我们将盲注入变成经典的带内注入。通过一次查询,我们可以获得所有应用程序用户的姓名:
NoSQL 注入 /auth/requestreset
requestreset
负责创建密码重置令牌的Auth控制器的方法:
Auth::requestreset 方法
与前一种情况一样,没有对用户参数进行类型检查。利用是类似的,但没有任何困难,例如密码或 CSRF 令牌验证:
提取密码重置令牌
与许多其他 Web 应用程序一样,Cockpit 允许重置帐户密码。 我们发现了两种容易受到 NoSQL 注入攻击并允许为任何用户获取密码重置令牌的方法。
NoSQL 注入/auth/resetpassword
(CVE-2020-35847)
resetpassword
Auth控制器的方法,它负责使用重置令牌更改用户密码:
Auth::resetpassword 方法
令牌参数没有类型检查,因此您可以使用以下查询提取现有令牌:
NoSQL 注入/auth/newpassword
(CVE-2020-35848)
newpassword
Auth控制器的方法,负责显示用户密码重置表单:
认证::新密码方法
同样,没有对令牌参数进行类型检查。该查询与上一个类似:
用户帐户泄露
现在,能够获得密码重置令牌,我们可以破坏我们感兴趣的任何用户帐户。这只需几个步骤:
1.访问/auth/requestreset
生成用于重置所选用户密码的令牌:
2. 使用刚才描述的方法之一(/auth/resetpassword
或/auth/newpassword
)提取令牌:
3. 使用/auth/newpassword
上一步获取的方法和密码重置令牌提取用户帐户数据(用户名、密码哈希、API 密钥、密码重置令牌):
提取用户帐户管理员
提取用户帐户loopa
有了这些数据,我们就可以:
- 使用带有 API 密钥的应用程序。
- 从哈希中暴力破解帐户密码。
- 使用以下
/auth/resetpassword
方法更改帐户密码:
远程代码执行
简单的RCE
在入侵了管理员帐户后,我们可以使用 Cockpit 的标准Finder组件上传一个 web shell ,以实现远程代码执行:
上传 web shell _shell.php 到 Cockpit 根目录
使用 web shell 在服务器上执行命令
UtilArrayQuery::buildCondition
MongoLite库的方法中的PHP注入
让我们考虑方法registerCriteriaFunction
的的Database
类,它创建文档的指定条件(过滤器)的条件函数:
Database::registerCriteriaFunction 方法
和相关功能buildCondition
的的UtilArrayQuery
类:
UtilArrayQuery::buildCondition 函数
记下$key
包含字段名称的变量。它的内容按原样插入到未来的字符串文字中,而不会被转义。
因此,通过控制$key
变量的内容,我们可以使用单引号从字符串文字中转义(打破它)以注入任意 PHP 代码。
为了演示该漏洞,我们将使用该/accounts/find
方法(需要身份验证)。此方法支持自定义条件(过滤器),这意味着它允许我们将任意内容放入$key
:
结论
在本文中,我展示了几种利用 NoSQL 盲注入的方法,一种未经身份验证的用户接管任何帐户的方法,以及 MongoLite 库中的远程代码执行。
每个人都应该立即更新到最新版本 (>= 0.12.0)。