[Debconf-devel] [security] debconf: format module eval injection via debconf database configuration

Jeremy Erazo mendozayt13 at gmail.com
Fri May 8 00:28:03 BST 2026


Hello Debian Security Team,

I'd like to report an input-validation issue in debconf 1.5.92
(the current version in sid as of 2026-05-07) that I have
runtime-confirmed in a fresh `debian:sid` container.

I am sending privately first per Debian's security policy; if you
determine that no embargo is needed, I'm happy to refile the
hardening fix as a normal BTS bug.  I am not requesting a CVE
directly in this initial report; I will leave CVE handling to the
Debian security process.

============================================================
Summary
============================================================

Several Debconf::DbDriver init() functions, and Debconf::Db::makedriver(),
splice an attacker-influenced string directly into a Perl
`eval STRING`:

    Debconf/DbDriver/File.pm:75       eval "use Debconf::Format::$this->{format}";
    Debconf/DbDriver/Directory.pm:71  eval "use Debconf::Format::$this->{format}";
    Debconf/DbDriver/Pipe.pm:66       eval "use Debconf::Format::$this->{format}";
    Debconf/Db.pm:70                  eval qq{use Debconf::DbDriver::$type};

The interpolated string is the `format:` (or driver `Driver:`) value
parsed out of the debconf database configuration, which can be set
via several env-var and config-file routes (DEBCONF_DB_OVERRIDE /
DEBCONF_DB_FALLBACK / DEBCONF_DB_REPLACE; DEBCONF_SYSTEMRC; DPKG_ROOT;
${VAR} expansion in Config.pm:_hashify).  No character-class
validation is performed before the eval, so a value such as

    format:822;BEGIN{qx(touch /tmp/dconf_repro)};1

makes the eval text `use Debconf::Format::822;BEGIN{qx(touch
/tmp/dconf_repro)};1`, and the second BEGIN{} runs at compile time
in the perl process.

============================================================
Affected
============================================================

  Package : debconf
  Version : 1.5.92  (current in sid; upstream tag 1.5.92)
  Files   : Debconf/DbDriver/File.pm
            Debconf/DbDriver/Directory.pm
            Debconf/DbDriver/Pipe.pm
            Debconf/Db.pm

============================================================
Threat model
============================================================

Confirmed (runtime, debian:sid container, debconf 1.5.92):

  Arbitrary code execution in debconf-using processes through
  unvalidated format/driver module names.  The BEGIN{qx(...)}
  side-effect runs as the UID/EUID of the perl process that loads
  Debconf::Config.

Local privilege escalation is possible only when attacker-controlled
debconf environment or configuration crosses a privilege boundary
into a privileged perl process -- for example sudo with `setenv` or
`-E` granted by a sudoers entry, an env-preserving wrapper, a
maintainer script invoked with attacker-influenced env, or an
attacker-writable /etc/debconf.conf (or attacker-controlled
DEBCONF_SYSTEMRC / DPKG_ROOT path).

Explicit non-claims:

- This is not a remote vulnerability.
- This does not amount to a privilege escalation on a default
  Debian system: with sudo's default env_reset, the trigger env
  vars are dropped before the privileged perl runs.
- This is not an `apt` root exploit by default.  apt installs are
  already root by design; the issue here is the input-validation
  hole, not the privilege model of dpkg-preconfigure.

============================================================
Variants runtime-confirmed
============================================================

Nine distinct trigger combinations all reach the same primitive
and were confirmed in a debian:sid container:

  A  DEBCONF_DB_OVERRIDE                -> File.pm:75
  B  DEBCONF_DB_OVERRIDE                -> Directory.pm:71
  C  DEBCONF_DB_OVERRIDE                -> Pipe.pm:66
  D  DEBCONF_DB_FALLBACK                -> File.pm:75
  E  DEBCONF_DB_REPLACE                 -> File.pm:75
  F  DEBCONF_SYSTEMRC=/path with malicious Format:
                                        -> File.pm:75
  G  DPKG_ROOT=/path with attacker-written /etc/debconf.conf
                                        -> File.pm:75
  H  DEBCONF_SYSTEMRC + Format: ${EVIL_FORMAT} +
     EVIL_FORMAT=822;BEGIN{qx(...)};1
                                        -> Config.pm:31 -> File.pm:75
                                          (the existing ${VAR}
                                           substitution carries the
                                           payload into the parsed
                                           stanza body)
  I  DEBCONF_DB_OVERRIDE with attacker driver name + custom @INC
                                        -> Db.pm:70
                                          (driver-name eval; needs
                                           PERL5LIB-controlled @INC)

A standalone benign reproducer (creates /tmp/dconf_repro and
nothing else) is available; I am happy to attach it on request.

============================================================
Proposed fix
============================================================

Attached: 0001-debconf-validate-format-and-driver-names-before-eval.patch

Summary of the diff (4 files, ~14 added lines):

- Add `\A[A-Za-z0-9_]+\z` validation on $this->{format} before
  the eval in each of the three DbDriver init() functions
  (File.pm, Directory.pm, Pipe.pm).
- Add the same validation on $type in Debconf::Db::makedriver()
  before `eval qq{use Debconf::DbDriver::$type}`.
- Debconf/Config.pm is intentionally NOT modified.

The ${VAR} substitution path in Config.pm:_hashify remains
documented as a transport for the malicious value, but the patch
blocks it at the eval sinks because the substituted result cannot
pass `\A[A-Za-z0-9_]+\z`.  I can prepare a separate hardening
patch for Config.pm (restricting or removing the substitution)
if maintainers prefer.

The patch keeps the legitimate `Format: 822` driver working
(verified post-patch in the sid container), only rejects values
outside [A-Za-z0-9_]+, and adds clear error messages
("Invalid Format value: must match [A-Za-z0-9_]+" / "Invalid
DbDriver name '...': must match [A-Za-z0-9_]+").

Compatibility: a legitimate config file using ${SAFE_NAME} for a
non-Format field (e.g. `Filename: /tmp/${SAFE_NAME}.dat` with
`SAFE_NAME=mydb` in the env) loads identically before and after
the patch -- the substitution is left untouched.

============================================================
Validation
============================================================

In a fresh `debian:sid` container with debconf 1.5.92:

  BEFORE patch:
    DEBCONF_DB_OVERRIDE='File{filename:/tmp/d format:822;BEGIN{qx(...
    -> /tmp/before_v2 created (owned by perl process UID).

  AFTER patch (PERL5LIB overlay of patched tree):
    same env -> debconf: DbDriver "_ENV_OVERRIDE":
                 Invalid Format value: must match [A-Za-z0-9_]+
    -> /tmp/after_pwn NOT created.

  All 9 attacker paths (A through I) blocked, no /tmp/V*.pwn file
  created.  Legitimate `Format: 822` config still loads OK.
  Legitimate `${VAR}` substitution in Config.pm still works.

============================================================
What I'd appreciate guidance on
============================================================

- Whether you'd like an embargo or whether I can file the BTS bug
  publicly.
- Whether the Maintainer line on the package
  (debconf-devel at lists.alioth.debian.org) is still the right
  forwarding address; alioth was retired in 2018 and I'd like to
  make sure my Cc actually reaches a human.
- Whether you'd prefer BTS severity `important` (with `security`
  tag) vs `grave`; my own read is that "important" matches the
  "input-validation hole that becomes LPE only via env/config
  crossing a privilege boundary" framing, but I defer to your
  judgement.
- Whether Debian Security wants the Config.pm hardening (restrict
  or remove the ${VAR} substitution) as a follow-up patch, or
  whether sink-side validation alone is considered sufficient.

I have an offline copy of the runtime traces and the variant
matrix; happy to share on request.

Thank you for your time.

Best regards,
Jeremy Erazo
mendozayt13 at gmail.com

============================================================
Inline patch (also attached)
============================================================

[ patch body --- attached as
  0001-debconf-validate-format-and-driver-names-before-eval.patch ]
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-debconf-validate-format-and-driver-names-before-eval.patch
Type: text/x-patch
Size: 4293 bytes
Desc: not available
URL: <http://alioth-lists.debian.net/pipermail/debconf-devel/attachments/20260507/e0e83b3d/attachment-0001.bin>
-------------- next part --------------
debconf format-module-name eval injection — runtime validation summary (v2)
============================================================================

Patch under test: 0001-debconf-validate-format-and-driver-names-before-eval.patch
v2 differs from v1 by NOT modifying Debconf/Config.pm — the eval sinks alone
are validated.  This avoids any compatibility discussion around the
${VAR} substitution feature in stanza bodies.

Environment
-----------
- Image: docker.io/library/debian:sid (pulled 2026-05-07)
- Package: debconf
- Version: 1.5.92
- Maintainer (per apt-cache show): Debconf Developers
                                    <debconf-devel at lists.alioth.debian.org>
- Source: apt source debconf  ->  debconf_1.5.92.dsc / debconf_1.5.92.tar.xz
- Perl: distro default (5.x)

Sinks identified in fresh source
--------------------------------
$ grep -n 'eval "use Debconf::Format' Debconf/DbDriver/{File,Directory,Pipe}.pm
Debconf/DbDriver/File.pm:75:        eval "use Debconf::Format::$this->{format}";
Debconf/DbDriver/Directory.pm:71:   eval "use Debconf::Format::$this->{format}";
Debconf/DbDriver/Pipe.pm:66:        eval "use Debconf::Format::$this->{format}";

$ grep -n 'eval qq{use Debconf::DbDriver' Debconf/Db.pm
Debconf/Db.pm:70:                   eval qq{use Debconf::DbDriver::$type};

Patch dry-run + apply (clean, 4 files)
--------------------------------------
$ patch -p1 --dry-run < 0001-debconf-validate-format-and-driver-names-before-eval.patch
checking file Debconf/DbDriver/Directory.pm
checking file Debconf/DbDriver/File.pm
checking file Debconf/DbDriver/Pipe.pm
checking file Debconf/Db.pm

$ patch -p1 < 0001-debconf-validate-format-and-driver-names-before-eval.patch
patching file Debconf/DbDriver/Directory.pm
patching file Debconf/DbDriver/File.pm
patching file Debconf/DbDriver/Pipe.pm
patching file Debconf/Db.pm

Files NOT modified by v2:
  Debconf/Config.pm   (intentionally untouched; the ${VAR} expansion
                       is left in place to preserve compatibility for
                       legitimate uses such as ${HOME}, ${USER}, etc.)

perl -c on modified files
-------------------------
Debconf/DbDriver/File.pm        syntax OK
Debconf/DbDriver/Directory.pm   syntax OK
Debconf/DbDriver/Pipe.pm        syntax OK
Debconf/Db.pm                   syntax OK
Debconf/Config.pm               syntax OK   (untouched, sanity)

BEFORE patch — bug confirmed reachable in fresh sid
---------------------------------------------------
$ DEBCONF_DB_OVERRIDE='File{filename:/tmp/d format:822;BEGIN{qx(touch\x20/tmp/before_v2)};1}' \
      perl -MDebconf::Config -e 'Debconf::Config->load'
Can't locate object method "new" via package
"Debconf::Format::822;BEGIN{qx(touch\\x20/tmp/before_v2)};1"
... at /usr/share/perl5/Debconf/DbDriver/File.pm line 36.

$ ls -la /tmp/before_v2
-rw-r--r-- 1 root root 0 May  7 16:38 /tmp/before_v2
[BEFORE] PoC SUCCEEDED — bug confirmed in 1.5.92.

AFTER patch — re-run all 9 attacker paths with PERL5LIB=/tmp/patchroot
----------------------------------------------------------------------
A: DEBCONF_DB_OVERRIDE  + File       -> blocked: "Invalid Format value: must match [A-Za-z0-9_]+"
B: DEBCONF_DB_OVERRIDE  + Directory  -> blocked
C: DEBCONF_DB_OVERRIDE  + Pipe       -> blocked
D: DEBCONF_DB_FALLBACK  + File       -> blocked
E: DEBCONF_DB_REPLACE   + File       -> blocked
F: DEBCONF_SYSTEMRC=/tmp/sysrc_F     -> blocked
   (config-file with malicious Format:)
G: DPKG_ROOT=/tmp/myroot             -> blocked
   (attacker-written /tmp/myroot/etc/debconf.conf)
H: DEBCONF_SYSTEMRC + Format:${EVIL_FORMAT}
   + EVIL_FORMAT=822;BEGIN{qx(...)};1
                                      -> blocked at sink validation
   (the ${VAR} substitution in Config.pm:31 still happens, but the
    substituted value cannot pass the per-driver
    \A[A-Za-z0-9_]+\z gate)
I: Db.pm driver-name eval chain      -> blocked: "Invalid DbDriver name
                                                  '...': must match
                                                  [A-Za-z0-9_]+"

For all of A-I: NO file under /tmp/V*.pwn was created.

Legitimate Format: 822 still loads
----------------------------------
$ PERL5LIB=/tmp/patchroot perl -MDebconf::Config -e '
      $ENV{DEBCONF_DB_OVERRIDE} =
          "File{filename:/tmp/legit/db format:822}";
      Debconf::Config->load;
      print "[legit File+822] loaded OK\n";'
[legit File+822] loaded OK

Compatibility — Config.pm ${VAR} substitution still functional
--------------------------------------------------------------
A legitimate config file using ${SAFE_NAME} for a non-Format field:

    Name: configdb
    Driver: File
    Filename: /tmp/${SAFE_NAME}.dat
    Format: 822

with `SAFE_NAME=mydb` in the env loads correctly:

$ SAFE_NAME=mydb DEBCONF_SYSTEMRC=/tmp/sysrc_compat PERL5LIB=/tmp/patchroot \
      perl -MDebconf::Db -MDebconf::Config -e '...'
[compat] config driver: Debconf::DbDriver::File filename=/tmp/mydb.dat
[compat] OK substitution preserved

This proves v2 does NOT break legitimate ${VAR} usage.

Result
------
- Bug runtime-confirmed in debconf 1.5.92 (fresh sid) before patch.
- All 9 attacker paths neutralised after patch (4 files modified).
- Legitimate "822" Format still loads.
- Legitimate ${VAR} substitution in Config.pm still works.
- No PoC artefact larger than `touch /tmp/<file>` was used.
-------------- next part --------------
debconf format-module-name eval injection — runtime variant matrix (v2)
========================================================================

Eight original transport routes plus the Db.pm driver-name eval all
reach the same primitive (eval STRING with attacker-influenced module
name).  All were observed creating a file as the perl process UID via
the BEGIN{qx(...)} side-effect of the malicious module-name string.

Source bug — the eval that interpolates an attacker string:

  Debconf/DbDriver/File.pm:75       eval "use Debconf::Format::$this->{format}";
  Debconf/DbDriver/Directory.pm:71  eval "use Debconf::Format::$this->{format}";
  Debconf/DbDriver/Pipe.pm:66       eval "use Debconf::Format::$this->{format}";
  Debconf/Db.pm:70                  eval qq{use Debconf::DbDriver::$type};

How the attacker string reaches each sink:

  Env vars routed via Debconf::Config::_env_to_driver (Config.pm:62-79):
    DEBCONF_DB_OVERRIDE   -> finalstack[0]    (drives the *first* driver)
    DEBCONF_DB_FALLBACK   -> finalstack[-1]   (drives the fallback)
    DEBCONF_DB_REPLACE    -> $config->{config}

  Config-file path routes:
    DEBCONF_SYSTEMRC      -> @config_files[0] (Config.pm:20-21);
                              attacker-controlled file with a Database
                              stanza whose Format: is malicious.
    DPKG_ROOT             -> path prefix (Config.pm:80);
                              prepends to the system config-file lookup
                              so /tmp/myroot/etc/debconf.conf is read
                              instead of /etc/debconf.conf.

  Body-of-stanza route:
    ${VAR} substitution   -> Config.pm:31; the parser substitutes any
                              ${X} in the stanza body with $ENV{X}, so
                              one env var named in the file becomes the
                              malicious Format: value.
                              v2 does NOT remove this substitution; the
                              substituted value is rejected at the eval
                              sink because it cannot pass
                              \A[A-Za-z0-9_]+\z.

  Driver-name eval:
    Db.pm:70              -> attacker-controlled DbDriver type name
                              feeds eval qq{use Debconf::DbDriver::$type}.

Variants (all confirmed reachable in Debian sid container, debconf 1.5.92,
and all blocked by v2 patch):

  Variant   Trigger                          Sink                 Result
  ---------------------------------------------------------------------------
  A         DEBCONF_DB_OVERRIDE              File.pm:75           blocked
  B         DEBCONF_DB_OVERRIDE              Directory.pm:71      blocked
  C         DEBCONF_DB_OVERRIDE              Pipe.pm:66           blocked
  D         DEBCONF_DB_FALLBACK              File.pm:75           blocked
  E         DEBCONF_DB_REPLACE               File.pm:75           blocked
  F         DEBCONF_SYSTEMRC=/tmp/evil.conf
              with malicious Format:         File.pm:75           blocked
  G         DPKG_ROOT=/tmp/myroot +
              /tmp/myroot/etc/debconf.conf
              with malicious Format:         File.pm:75           blocked
  H         DEBCONF_SYSTEMRC + Format:
              ${EVIL_FORMAT} +
              EVIL_FORMAT=822;BEGIN{...}     Config.pm:31->75     blocked
                                             (substitution carries
                                              the payload, but the
                                              sink-side validation
                                              rejects it)
  I         DEBCONF_DB_OVERRIDE with
              attacker driver name
              + custom @INC                  Db.pm:70             blocked

PoC payload (kept benign):
  format:822;BEGIN{qx(touch\x20/tmp/<name>)};1

Patch behaviour against the matrix:

  Variants A, B, C, D, E, F, G, H:
      caught at the per-driver `\A[A-Za-z0-9_]+\z` validation that
      precedes the eval at File.pm:75 / Directory.pm:71 / Pipe.pm:66.
      Variant H still has the substitution active in Config.pm but the
      substituted result fails the sink-side regex.

  Variant I:
      caught at the same regex on $type in Db.pm before
      `eval qq{use Debconf::DbDriver::$type}`.

Threat model — single-line statement:
  Arbitrary code execution in debconf-using processes through unvalidated
  format/driver module names.  Local privilege escalation is possible
  when attacker-controlled debconf environment/configuration is
  preserved across a privilege boundary (e.g. sudo with `setenv` or `-E`,
  maintainer scripts running with attacker-influenced env, or any
  privileged wrapper that does not reset the environment).


More information about the Debconf-devel mailing list