..

ImageMagick 漏洞凑热闹手札

今天日常刷 twitter 看到 Tavis 点赞了这条推

经历过2016年魔图漏洞洗礼的人看到 ImageMagick 就很敏感,fu*k 这是又报漏洞了,ImageMagick 运用很广泛,赶紧应急一波。

不过比较坑的是就没有其他信息,受影响的主机受影响版本啥都没有,甚至组件官方也没有发布安全公告

按照建议的临时解决方案(https://www.kb.cert.org/vuls/id/332928 )针对装有ImageMagick的机器改一下 /etc/ImageMagick/policy.xml 配置文件,禁用以下编码器。

<policy domain="coder" rights="none" pattern="PS" />
<policy domain="coder" rights="none" pattern="EPS" />
<policy domain="coder" rights="none" pattern="PDF" />
<policy domain="coder" rights="none" pattern="XPS" />

其实在2016年的爆ImageMagick RCE(CVE-2016-3714)的时候,当时官方的解决方案就是修改配置文件的方式(https://imagetragick.com/ )p.s 虽然使用黑名单的方式不太符合漏洞修复原则..

更改完配置文件后要怎样验证是否修复了呢?

两种方法

1、 找到各个网站的上传接口,把 poc 图片打一遍.. 反正是执行命令嘛,用 dnslog 或者反弹 SHELL 的 方式都可以 2、 在主机上执行 convert poc.jpeg poc.gif

但是这两种方式都不是最优

因为短时间不可能找全所有的上传接口,而且打 poc 的方式存在一定的不稳定性,在不确定是否会对主机造成影响的情况下尽量不要尝试,当然如果是测试环境的话那就随便造了。

执行convert 方式最简单的,排查起来也会更快速更准确,对主机影响也最小最可控,不过呢,有些主机安装的 ImageMagick-libs 可能不会有 convert 命令 (不确定)

然后在我习惯性 less poc.jpeg (less 命令很好用)发现竟然也执行了。

这就很有意思了。

虽然知道这个漏洞是因为 Ghostscript 沙盒逃逸(https://cert.360.cn/warning/detail?id=154bd8345f9cd560ea1c0e5bf453a41d )ImageMagick 只是众多受影响的软件之一,但是 less 也受影响就有些匪夷所思了..

本着知其然知其所以然的态度,我决定找找原因,当然 c 我是审计不来的,逆向我也是不懂的 😭

但是还可以用 strace 跟踪进程中的系统调用的方式一窥究竟

注意,因为是用的 docker 的 centos 环境,所以启动时要加上 –security-opt seccomp:unconfined

然后就可以使用 strace 进行调试

strace -f -o /tmp/less-devul.txt less poc.jpeg

调试日志是这样的,跟踪到的进程中的系统调用,包括 fork 调用所产生的子进程

execve 就是执行的具体命令,然后我注意到这一条

基本就确定就是这个原因

这是个什么鬼脚本,看一下 less /usr/bin/lesspipe.sh

#!/bin/sh
#
# To use this filter with less, define LESSOPEN:
# export LESSOPEN="|/usr/bin/lesspipe.sh %s"
#
# The script should return zero if the output was valid and non-zero
# otherwise, so less could detect even a valid empty output
# (for example while uncompressing gzipped empty file).
# For backward-compatibility, this is not required by default. To turn
# this functionality there should be another vertical bar (|) straight
# after the first one in the LESSOPEN environment variable:
# export LESSOPEN="||/usr/bin/lesspipe.sh %s"


# This function makes all return values 1 when 0 and otherwise 0
# Usually, app returns 0 when succeded and we need to return 1
# This behavior is forced because backward compatiblity
# (tcsh print_exit_value bug)
function handle_exit_status() {
  if [ $1 -eq 0 ]; then
    exit 1
  fi
  exit 0
}

if [ ! -e "$1" ] ; then
        handle_exit_status 1 $1
fi

if [ -d "$1" ] ; then
        ls -alF -- "$1"
        handle_exit_status $?
fi

exec 2>/dev/null

case "$1" in
*.[1-9n].bz2|*.[1-9]x.bz2|*.man.bz2|*.[1-9n].[gx]z|*.[1-9]x.[gx]z|*.man.[gx]z|*.[1-9n].lzma|*.[1-9]x.lzma|*.man.lzma)
        case "$1" in
        *.gz)           DECOMPRESSOR="gzip -dc" ;;
        *.bz2)          DECOMPRESSOR="bzip2 -dc" ;;
        *.xz|*.lzma)    DECOMPRESSOR="xz -dc" ;;
        esac
        if [ -n "$DECOMPRESSOR" ] && $DECOMPRESSOR -- "$1" | file - | grep -q troff; then
                $DECOMPRESSOR -- "$1" | groff -Tascii -mandoc -
                handle_exit_status $?
        fi ;;&
*.[1-9n]|*.[1-9]x|*.man)
        if file "$1" | grep -q troff; then
                groff -Tascii -mandoc "$1" | cat -s
                handle_exit_status $?
        fi ;;&
*.tar) tar tvvf "$1"; handle_exit_status $? ;;
*.tgz|*.tar.gz|*.tar.[zZ]) tar tzvvf "$1"; handle_exit_status $? ;;
*.tar.xz) tar Jtvvf "$1"; handle_exit_status $? ;;
*.xz|*.lzma) xz -dc -- "$1"; handle_exit_status $? ;;
*.tar.bz2|*.tbz2) bzip2 -dc -- "$1" | tar tvvf -; handle_exit_status $? ;;
*.[zZ]|*.gz) gzip -dc -- "$1"; handle_exit_status $? ;;
*.bz2) bzip2 -dc -- "$1"; handle_exit_status $? ;;
*.zip|*.jar|*.nbm) zipinfo -- "$1"; handle_exit_status $? ;;
*.rpm) rpm -qpivl --changelog -- "$1"; handle_exit_status $? ;;
*.cpi|*.cpio) cpio -itv < "$1"; handle_exit_status $? ;;
*.gif|*.jpeg|*.jpg|*.pcd|*.png|*.tga|*.tiff|*.tif)
        if [ -x /usr/bin/identify ]; then
                identify "$1"
                handle_exit_status $?
        elif [ -x /usr/bin/gm ]; then
                gm identify "$1"
                handle_exit_status $?
        else
                echo "No identify available"
                echo "Install ImageMagick or GraphicsMagick to browse images"
                handle_exit_status 1
        fi ;;
*)
        if [ -x /usr/bin/file -a -x /usr/bin/iconv -a -x /usr/bin/cut ]; then
                case `file -b "$1"` in
                *UTF-16*) conv='UTF-16' ;;
                *UTF-32*) conv='UTF-32' ;;
                esac
                env=`echo $LANG | cut -d. -f2`
                if [ -n  "$conv" -a -n "$env" -a "$conv" != "$env" ]; then
                        iconv -f $conv -t $env "$1"
                        handle_exit_status $?
                fi
        fi
        handle_exit_status 1
esac

Soga,调用了 ImageMagick 的 identify 命令,less 图片格式文件的话,会输出这张图的各项信息,也是用到的 ImageMagick 或者 GraphicsMagick 这两个库

继续跟进也的确证实了这一点

而且执行过程中也有去读 policy.xml,修改完配置文件后是立即生效的。

所以说基础组件出问题影响的范围是十分广泛的。默认源搜一下就有这么多