漏洞背景
7月27日,Laravel发布安全通告,修复了一个Cookie伪造漏洞。这个漏洞利用难度较高,需要程序会对用户输入进行加密,并返回加密结果。由于未将cookie-value与cookie-name进行绑定,导致可以通过构造合法密文来进行cookie伪造,造成逻辑漏洞,当Session_handler为cookie时会造成远程命令执行。
漏洞分析
Laravel存在cookie加密机制,在官方的laravel项目中,默认使用config/app.php中的key字段进行加密,默认值是.env的APP_KEY字段。加密方法是cipher字段。
下面直接看cookie加密、解密的部分。首先看老版本的:
//7.6.0
//vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php
protected function encrypt(Response $response)
{
...
$response->headers->setCookie($this->duplicate(
$cookie, $this->encrypter->encrypt($cookie->getValue(), static::serialized($cookie->getName()))
));
...
}
可以发现直接调用了 $this->encrypter->encrypt() 进行加密,跟进一下:
//vendor/laravel/framework/src/Illuminate/Encryption/Encrypter.php
public function encrypt($value, $serialize = true)
{
$iv = random_bytes(openssl_cipher_iv_length($this->cipher));
$value = openssl_encrypt(
$serialize ? serialize($value) : $value,
$this->cipher, $this->key, 0, $iv
);
if ($value === false) {
throw new EncryptException('Could not encrypt the data.');
}
$mac = $this->hash($iv = base64_encode($iv), $value);
$json = json_encode(compact('iv', 'value', 'mac'), JSON_UNESCAPED_SLASHES);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new EncryptException('Could not encrypt the data.');
}
return base64_encode($json);
}
首先生成iv,然后使用之前配置的key和cipher通过openssl_encrypt()进行加密。然后对这个加密结果计算hash,最终把iv、加密结果、hash一起返回。
解密的过程同理可得,这里就不贴出来了。
回顾一下整个加解密过程不难发现,最终生成的加密的cookie-value与cookie-name是完全没有联系的。虽说用户无法得知cookie-value的明文,但是可以通过替换cookie-value为一个合法的值来进行cookie伪造,这种攻击的前提是程序会输出用户的输入的加密结果。
在官方通告中提到,当session_handler是cookie时可能造成RCE,其默认值是file,也就是session数据存在文件中。而当设置成cookie时,则会把session的序列化数据加密之后放到cookie中返回给用户,下次请求时带上这个cookie,后台在解密后会进行反序列化。当可以进行cookie伪造时,就可以通过cookie反序列化+pop链进行RCE。
补丁分析
下面分析一下7.22.4的补丁情况
//vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php:138
protected function encrypt(Response $response)
{
...
$response->headers->setCookie($this->duplicate(
$cookie,
$this->encrypter->encrypt(
CookieValuePrefix::create($cookie->getName(), $this->encrypter->getKey()).$cookie->getValue(),
static::serialized($cookie->getName())
)
));
...
}
核心补丁与老版本的对比一下:
$this->encrypter->encrypt($cookie->getValue(), static::serialized($cookie->getName())) //老版本
$this->encrypter->encrypt(
CookieValuePrefix::create($cookie->getName(), $this->encrypter->getKey()).$cookie->getValue(),
static::serialized($cookie->getName()) //补丁
其中$this->encrypter->encrypt()的底层代码并没有变,因此这里的补丁原理就很明显了,原先是直接调用$cookie->getValue()进行加密,补丁则是在value前加了个前缀:CookieValuePrefix::create($cookie->getName(), $this->encrypter->getKey())。这里获取cookie-name和加密的key,传入create(),跟进一下
//vendor/laravel/framework/src/Illuminate/Cookie/CookieValuePrefix.php:14
public static function create($cookieName, $key)
{
return hash_hmac('sha1', $cookieName.'v2', $key).'|';//为何要拼接v2?猜测是因为如果$cookieName是数组,那么计算的结果就是Null.'|',一定程度上来说相当于bypass了。但是强制拼接v2就是'Arrayv2'.'|'
}
这里就是对cookiename+key算了个hash。
回顾整个加密流程,原先是仅对cookie-value加密,而补丁则是在cookie-value前拼接了cookie-name与key的hash。由于攻击者不知道key(如果知道了key那么任何加密都是没用的),因此也就无法伪造出一个合法的cookie-value。
并且由于对cookie-name和cookie-value进行hash的算法不一样(一个是SHA,一个是AES),因此无法通过两次请求来伪造合法的cookie-value(使用第一次加密cookie-name的结果拼接到第二次加密的输入前)。
简单的看下解密部分
//vendor/laravel/framework/src/Illuminate/Cookie/Middleware/EncryptCookies.php:76
protected function decrypt(Request $request)
{
foreach ($request->cookies as $key => $cookie) {
...
$value = $this->decryptCookie($key, $cookie);
$hasValidPrefix = strpos($value, CookieValuePrefix::create($key, $this->encrypter->getKey())) === 0;//判断cookie value明文是否以cookie-name hash开头
$request->cookies->set(
$key, $hasValidPrefix ? CookieValuePrefix::remove($value) : null//如果上面的判断为true,则去掉开头的hash,否则置为null
);
...
}
return $request;
}
//vendor/laravel/framework/src/Illuminate/Cookie/CookieValuePrefix.php:25
public static function remove($cookieValue)
{
return substr($cookieValue, 41);
}
首先是判断解密后的cookie-value是否以cookie-name关联的hash开头,如果是则会调用remove()去掉开头的41个字符,不是则会将cookie-value置为null。也可以说是强制cookie-value明文必须以hash开头,保证攻击者无法伪造cookie-value。
影响版本
7.x <= 7.21.0
6.x <= 6.18.26
解决方案
-
升级到最新版本
参考信息
-
https://blog.laravel.com/laravel-cookie-security-releases
-
https://github.com/laravel/framework/compare/v7.21.0...v7.22.4
山石网科安全技术研究院简称“山石安研院”正式成立于2020年4月,是山石网科的信息安全智库部门,其前身是原安全服务部下的安全研究团队。山石安研院整体架构包括干将、莫邪两大安全实验室,以及安全预警分析、高端攻防培训两支独立的技术团队。安研院主要负责反APT跟踪和研究、出战及承办全球攻防赛事、高端攻防技术培训、全球中英文安全预警分析发布、各类软硬件漏洞挖掘和利用研究、承接国家网络安全相关课题、不定期发布年度或半年度的各类技术报告及公司整体攻防能力展现。技术方向包括移动安全、虚拟化安全、工控安全、物联安全、区块链安全、协议安全、源码安全、反APT及反窃密。
自2015年以来为多省公安厅提供技术支撑工作,为上合峰会、财富论坛、金砖五国等多次重大活动提供网络安保支撑工作。在多次攻防赛事中连获佳绩,网安中国行第一名,连续两届红帽杯冠军、网鼎杯线上第一名,在补天杯、极棒杯、全国多地的护网演习等也都获得优秀的成绩,每年获得大量的CNVD、CNNVD、CVE证书或编号。
如需帮助请咨询 [email protected]
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论