漏洞分析 | Laravel Cookie伪造漏洞分析

  • A+
所属分类:安全文章

漏洞分析 | Laravel Cookie伪造漏洞分析

漏洞背景

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.phpprotected 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.phppublic 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:138protected 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:14public 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:76protected 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:25public 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]


漏洞分析 | Laravel Cookie伪造漏洞分析


发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: