作者:Ricter Z
漏洞时间线:
- 2021/03/08 – 提交漏洞至 TeX 官方;
- 2021/03/27 – 漏洞修复,安全版本:TeX Live 2021;
- 2021/06/06 – 漏洞分析公开。
I. Tex 安全限制概述
TeX 提供了 \write18 原语以执行命令。为了提高安全性,TexLive 的配置文件(texmf.cnf)提供了配置项(shell_escape、shell_escape_commands)去配置 \write18能否执行命令以及允许执行的命令列表。
其中 shell_escape 有三种配置值,分别为:
- f:不允许执行任何命令
- t:允许执行任何命令
- p:支持执行白名单内的命令(默认)
白名单命令列表可以通过如下命令查询:
kpsewhich --var-value shell_escape_commands
shell_escape 配置值可以通过如下命令查询:
kpsewhich --var-value shell_escape
本文涉及的 CVE 如下:没 advisory 懒得申请了。
II. 挖掘思路
TeX 提供了一个默认的白名单命令列表,如若在调用过程中,这些命令出现安全问题,则会导致 TeX 本身在调用命令的时候出现相同的安全问题。
可以假设:在命令调用的过程中,由于开发者对于命令参数的不完全掌握,有可能存在某个命令参数最终会作为系统命令进行调用的情况。依据这个思路,挖掘白名单内的命令以及白名单内命令的内部调用,并最终得到一个调用链以及相关的参数列表,依据研究人员的经验去判断是否存在安全问题。
III. 在 *nix 下的利用方式
通过针对命令的深入挖掘,发现白名单内的 repstopdf 命令存在安全问题,通过精心构造参数可以执行任意系统命令。
repstopdf 意为 restricted epstopdf,epstopdf 是一个 Perl 开发的脚本程序,可以将 eps 文件转化为 pdf 文件。repstopdf 强制开启了 epstopdf 的 –safer 参数,同时禁用了 –gsopt/–gsopts/–gscmd 等 GhostScript 相关的危险参数,以防止在 TeX 中调用此命令出现安全问题。repstopdf 调用方式如下:
repstopdf [options] [epsfile [pdffile.pdf]]
repstopdf 会调用 GhostScript 去生成 pdf 文件(具体调用参数可以用过 strace 命令进行跟踪),其中传入的 epsfile 参数会成为 GhostScript 的 -sOutputFile= 选项的参数。
通过查阅 GhostScript 的文档可知,GhostScript 的此项参数支持管道操作。当我们传入文件名为:|id 时,GhostScript 会执行 id 命令。于是我们可以构造 repstopdf 的参数实现任意的命令执行操作,不受前文所提及的限制条件限制。利用方式如下所示:
repstopdf '|id #'
在 TeX 内的利用方式为:
\write18{repstopdf "|id #"}
IV. 在 Windows 下的利用方式
Windows 平台下,白名单内存在的是 epstopdf 而非 repstopdf,且相关参数选项与 *nix 平台下不相同,但仍旧存在 –gsopt 选项。利用此选项可以控制调用 GhostScript 时的参数。
此参数只支持设定调用 GhostScript 时的参数选项。参考 GhostScript 的文档,指定参数 -sOutputFile 及其他相关参数即可。利用方式为:
epstopdf 1.tex "--gsopt=-sOutputFile=%pipe%calc" "--gsopt=-sDEVICE=pdfwrite" "--gsopt=-"
V. LuaLaTeX 的安全问题
LuaLaTex 内置了 Lua 解释器,可以在编译时执行 Lua 代码,原语为:\directlua。LuaLaTeX 支持调用系统命令,但是同样地受到 shell_escape 的限制。如在受限(f)模式下,不允许执行任意命令;默认情况下只允许执行白名单内的命令。由于可以调用白名单内的命令,LuaLaTeX 同样可以利用 III、IV 内描述的方式进行利用,在此不做进一步赘述。
LuaLaTeX 的 Lua 解释器支持如下风险功能:
- 命令执行(io.popen 等函数)
- 文件操作(lfs 库函数)
- 环境变量设置(os.setenv)
- 内置了部分自研库(fontloader)
1. 环境变量劫持导致命令执行
通过修改 PATH 环境变量,可以达到劫持白名单内命令的效果。PATH 环境变量指定了可执行文件所在位置的目录路径,当在终端或者命令行输入命令时,系统会依次查找 PATH 变量中指定的目录路径,如果该命令存在与目录中,则执行此命令(https://en.wikipedia.org/wiki/PATH_(variable))。
将 PATH 变量修改为攻击者可控的目录,并在该目录下创建与白名单内命令同名的恶意二进制文件后,攻击者通过正常系统功能调用白名单内的命令后,可以达到任意命令执行的效果。
2. fontloader 库安全问题
fontloader 库存在典型的命令注入问题,问题代码如下:
// texk/web2c/luatexdir/luafontloader/fontforge/fontforge/splinefont.c char *Decompress(char *name, int compression) { char *dir = getenv("TMPDIR"); char buf[1500]; char *tmpfile; if ( dir==NULL ) dir = P_tmpdir; tmpfile = galloc(strlen(dir)+strlen(GFileNameTail(name))+2); strcpy(tmpfile,dir); strcat(tmpfile,"/"); strcat(tmpfile,GFileNameTail(name)); *strrchr(tmpfile,'.') = '\0'; #if defined( _NO_SNPRINTF ) || defined( __VMS ) sprintf( buf, "%s < %s > %s", compressors[compression].decomp, name, tmpfile ); #else snprintf( buf, sizeof(buf), "%s < %s > %s", compressors[compression].decomp, name, tmpfile ); #endif if ( system(buf)==0 ) return( tmpfile ); free(tmpfile); return( NULL ); }
调用链为:
ff_open -> ReadSplineFont -> _ReadSplineFont -> Decompress -> system
通过 Lua 调用 fontloader.open 函数即可触发。此方式可以在受限(f)模式下执行命令。
VI. DVI 的安全问题
DVI(Device independent file)是一种二进制文件格式,可以由 TeX 生成。在 TeX 中,可以利用 \special 原语嵌入图形。TeX 内置了 DVI 查看器,其中 *nix 平台下为 xdvi 命令,Windows 平台下通常为 YAP(Yet Another Previewer)。
1. xdvi 命令的安全问题
xdvi 在处理超链接时,调用了系统命令启动新的 xdvi,存在典型的命令注入问题。问题代码如下:
// texk/xdvik/hypertex.c void launch_xdvi(const char *filename, const char *anchor_name) { #define ARG_LEN 32 int i = 0; const char *argv[ARG_LEN]; char *shrink_arg = NULL; ASSERT(filename != NULL, "filename argument to launch_xdvi() mustn't be NULL"); argv[i++] = kpse_invocation_name; argv[i++] = "-name"; argv[i++] = "xdvi"; /* start the new instance with the same debug flags as the current instance */ if (globals.debug != 0) { argv[i++] = "-debug"; argv[i++] = resource.debug_arg; } if (anchor_name != NULL) { argv[i++] = "-anchorposition"; argv[i++] = anchor_name; } argv[i++] = "-s"; shrink_arg = XMALLOC(shrink_arg, LENGTH_OF_INT + 1); sprintf(shrink_arg, "%d", currwin.shrinkfactor); argv[i++] = shrink_arg; argv[i++] = filename; /* FIXME */ argv[i++] = NULL; ... execvp(argv[0], (char **)argv);
2. YAP 安全问题
YAP 在处理 DVI 内置的 PostScripts 脚本时调用了 GhostScript,且未开启安全模式(-dSAFER),可以直接利用内嵌的 GhostScript 进行命令执行。
VII. 漏洞利用
TeX 底层出现安全问题时,可以影响基于 TeX 的相关在线平台、TeX 编辑器以及命令行。
VIII. 参考文章
- http://scumjr.github.io/2016/11/28/pwning-coworkers-thanks-to-latex/
- https://mirrors.tuna.tsinghua.edu.cn/CTAN/support/epstopdf/epstopdf.man1.pdf
- https://www.texfaq.org/FAQ-spawnprog
- https://0day.work/hacking-with-latex/
- https://www.ghostscript.com/doc/current/Use.htm#Pipes
- https://ruxcon.org.au/assets/2017/slides/hong-ps-and-gs-ruxcon2017.pdf