#1152844 docx2txt: shell command injection via crafted input filename

Package:
docx2txt
Source:
docx2txt
Submitter:
Koyo Keiter
Date:
2026-11-02 09:37:02 UTC
Severity:
normal
Tags:
#1152844#5
Date:
2026-11-02 07:46:46 UTC
From:
To:
docx2txt builds its unzip invocations as shell command strings and runs
them with backticks, interpolating the input filename inside double
quotes:

  404: my $unzip_cmd = "'$config_unzip' $config_unzip_opts";
  409: $content = `$unzip_cmd "$ARGV[0]" word/document.xml 2>$nullDevice`;
  441: $_ = `$unzip_cmd "$ARGV[0]" word/_rels/document.xml.rels 2>$nullDevice`;
  458: $_ = `$unzip_cmd "$ARGV[0]" word/numbering.xml 2>$nullDevice`;

Double quotes do not protect against ", $(...) or backticks in the
name, so a file whose name contains them runs arbitrary commands as the
user who converts it.

Reproducer:

  $ cp any.docx 'x"; touch PWNED; ".docx'
  $ docx2txt 'x"; touch PWNED; ".docx' - >/dev/null
  $ ls PWNED
  PWNED

Impact: anyone who can choose the name of a file the victim later
converts (an archive member, a download, a shared directory, a mail
attachment) can execute commands as the victim. Callers that quote the
filename correctly are still affected, because docx2txt re-embeds it in
a shell string. The package's own mailcap entry ("docx2txt %s -") is a
possible path for this; I have not verified which mail clients preserve
attachment names there. I found this while adding .docx support to
less's lesspipe (#1152840), which works around it by passing docx2txt a
symlink with a fixed name.

The same class of problem exists for extracted-directory input:
readFileInto() and readOptionalFileInto() use 2-argument open on
"$ARGV[0]/word/...", so a directory named e.g. '|touch PWNED;:'
(containing word/document.xml and word/_rels/document.xml.rels) runs
the command when given as a relative path (verified).

The attached patch adds debian/patches/0002-run-unzip-without-a-shell.patch,
which replaces the three backtick calls with a small unzipMember()
helper using a list-form pipe open (stderr still discarded), and uses
3-argument open in the two read helpers. Tested against the unpatched
script: output is byte-identical for a real-world .docx, a synthetic
.docx with and without numbering.xml, stdin input and the default
output file; error messages for missing, non-docx and non-zip input
are unchanged; and the hostile names above (", $(...), backticks, and
the '|' directory) no longer execute anything. Upstream appears
inactive since 1.4 (2014), so the patch is not forwarded.

Not addressed here: stdin mode writes to a predictable temporary file
("$config_tempDir/dx2tTemp_<pid>_<time>.docx", opened without O_EXCL).
I can file that separately if useful.

This is similar in nature to #985594 (quoted %s in the mailcap entry),
which was handled as a public normal/security bug.

#1152844#12
Date:
2026-11-02 09:01:00 UTC
From:
To:
Thanks for the report and the patch.  I've applied it in 1.4-6, with some
additions:

 - a file name starting with "-" is passed to unzip as "./-..." so it
   can't be taken as an unzip option;
 - the output file is also opened with 3-arg open ("-" still means stdout);
 - the STDIN temporary file is now created with File::Temp (so no need to
   file that one separately);
 - docx2txt.config (perl code, run with "do") is no longer loaded from the
   current directory.  With modern perl that is blocked by '.' not being
   in @INC, but PERL_USE_UNSAFE_INC=1 re-enabled it.

Will close with the upload.

#1152844#17
Date:
2026-11-02 09:01:34 UTC
From:
To:
Thanks for the report and the patch.  I've applied it in 1.4-6, with some
additions:

 - a file name starting with "-" is passed to unzip as "./-..." so it
   can't be taken as an unzip option;
 - the output file is also opened with 3-arg open ("-" still means stdout);
 - the STDIN temporary file is now created with File::Temp (so no need to
   file that one separately);
 - docx2txt.config (perl code, run with "do") is no longer loaded from the
   current directory.  With modern perl that is blocked by '.' not being
   in @INC, but PERL_USE_UNSAFE_INC=1 re-enabled it.

Will close with the upload.

#1152844#22
Date:
2026-11-02 09:34:18 UTC
From:
To:
We believe that the bug you reported is fixed in the latest version of
docx2txt, which is due to be installed in the Debian FTP archive.

A summary of the changes between this version and the previous one is
attached.

Thank you for reporting the bug, which will now be closed.  If you
have further comments please address them to 1152844@bugs.debian.org,
and the maintainer will reopen the bug report if appropriate.

Debian distribution maintenance software
pp.
Barak A. Pearlmutter <bap@debian.org> (supplier of updated docx2txt package)

(This message was generated automatically at their request; if you
believe that there is a problem with it please contact the archive
administrators by mailing ftpmaster@ftp-master.debian.org)
Format: 1.8
Date: Mon, 02 Nov 2026 08:46:20 +0000
Source: docx2txt
Architecture: source
Version: 1.4-6
Distribution: unstable
Urgency: medium
Maintainer: Barak A. Pearlmutter <bap@debian.org>
Changed-By: Barak A. Pearlmutter <bap@debian.org>
Closes: 907994 1152844
Changes:
 docx2txt (1.4-6) unstable; urgency=medium
 .
   * Bump to debhelper compat level 14
   * Bump standards version to 4.7.4
   * Security: run unzip without a shell, use 3-arg open, create the STDIN
     temporary file safely, and do not load perl config from the current
     directory (closes: #1152844), thanks to Koyo Keiter for the report
     and initial patch
   * Tolerate insignificant whitespace in docx XML (closes: #907994)
Checksums-Sha1:
 5ad1087c4835c46fa9824936a0398c002937fff5 2112 docx2txt_1.4-6.dsc
 10f4c3b139145eb0e9e98c8cf03e68a09487d85f 6684 docx2txt_1.4-6.debian.tar.xz
 d2fb80f964458a29cdae386c5bc1c954d9c39714 91500 docx2txt_1.4-6.git.tar.xz
 ebd52a2fa02f45d7a0a535bfd0ac3e55e7ab1cd3 17733 docx2txt_1.4-6_source.buildinfo
Checksums-Sha256:
 1310318801116000a6e53004f583f5442e564192c88ab3021a0a56e85752b561 2112 docx2txt_1.4-6.dsc
 ab2e7191e7fca33ab4424b812a1289e777738c36034b54c64808559ac7558045 6684 docx2txt_1.4-6.debian.tar.xz
 38f160da41ab48fd600e7f1f3040dc6dd0eebba6d6e9125d5dd815a603259a50 91500 docx2txt_1.4-6.git.tar.xz
 775fc1bda7d1a86af229dbe0bd30c36bc5c1589f5e6ddf3b16f0438a95e68415 17733 docx2txt_1.4-6_source.buildinfo
Files:
 5a8a3d8d7963921585568ae7584fc9ea 2112 text optional docx2txt_1.4-6.dsc
 8d42bc970a19916bdc1e6a1ddd9d42e6 6684 text optional docx2txt_1.4-6.debian.tar.xz
 ebcbebd3ab54efe7636a754c2c5bd045 91500 text optional docx2txt_1.4-6.git.tar.xz
 66e0a65fd3acc3f08ad312e7f8689c83 17733 text optional docx2txt_1.4-6_source.buildinfo
Git-Tag-Info: tag=f8e96ec6ba21ef58748e6384d3e32f804d973d16 fp=2de4bf05421076c47ad20367c6af2490d3ca72b9
Git-Tag-Tagger: Barak A. Pearlmutter <barak+git@pearlmutter.net>
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEEN02M5NuW6cvUwJcqYG0ITkaDwHkFAmroVoUACgkQYG0ITkaD
wHkDDRAA3Gp/BgDqcbUGVQp2wCWc8XHO8bhm2+5OUdrdIS4qykDz/z0h0qqv8fFb
m3ZyhJyiVybhiBKahO/RDfS5EaBgOO0rqgMxJ76JHNr4UmM+pqnx47vCLiBNxZcq
mT4foALbyXjIMl418NoWgfjnyx4CTnJhkWlgJgbCo8Nz8SeHojUdmt5TRhiYgXwK
L1/xQNMEN0cADBvW0vunt1FY2fMhfVu5botbAlQpskbN0xktOoL1Jq8FSHLuPGBN
Qu8L/i2501u8QPmm+jBDuM6ApnppjQUFsjTEVN+Dpr+3/GrJt2LDahTsXewo8Q8z
1/pAJGx/WE5jzA5uN0QFXN8r/r2Dlt7WA672PLsXufbQ8kvjlN4cL0NyeQLnZq2f
aLuqoMb8CH2YN0twHsIzmGEMxWQqn4+t/yHH9FbBmU4WJjI3xP5GqQgZ6Dqa3r8E
Rj3wggkgwTI0Z6zeD/09sulTzROCk281EWzqrJYoGGb2/DRpxCaZpkA04Dlnz4u5
vWUhYM7ymV9nq69Y5yKyxWp/klxLMyN3HMIkvm25JrgXdQmbzqiaLFY2aryTiOcZ
HbJBkOokiRvQC3CgV2v4/ACgV5MR8nOOtVwvuqn1LbsOejSworC0TiDPddGkIW6G
Oho6xUyKMEpYhNfOUtQizhuRCG6I7YWaW7G/LrkrLLtDybI3Fio=
=GMEO
-----END PGP SIGNATURE-----