Skip to content

fix: avoid shell interpolation of SLURM_NODELIST (CVE-2024-27763) - #765

Open
christopher5106 wants to merge 1 commit into
XPixelGroup:masterfrom
christopher5106:fix/cve-2024-27763-shell-injection
Open

fix: avoid shell interpolation of SLURM_NODELIST (CVE-2024-27763)#765
christopher5106 wants to merge 1 commit into
XPixelGroup:masterfrom
christopher5106:fix/cve-2024-27763-shell-injection

Conversation

@christopher5106

Copy link
Copy Markdown

Fixes CVE-2024-27763 / GHSA-86w8-vhw6-q9qq, reported in #729.

Problem

_init_dist_slurm interpolates the SLURM_NODELIST environment variable into a
shell command string:

addr = subprocess.getoutput(f'scontrol show hostname {node_list} | head -n1')

subprocess.getoutput runs its argument through /bin/sh -c, so any shell
metacharacter in SLURM_NODELIST is interpreted rather than passed to
scontrol. This is the only getoutput call in the codebase.

Fix

Pass the node list as an argv element so no shell is involved, and take the
first line in Python instead of piping through head -n1.

Verification

With a stub scontrol on PATH that echoes its arguments:

SLURM_NODELIST before after
node[1-4] 'node-show' 'node-show' (unchanged)
x; touch /tmp/PWNED; echo done /tmp/PWNED created, output 'node-show\nnode-hostname\nnode-x\ndone' /tmp/PWNED not created, output 'node-show'

Note the injected command also defeated the head -n1 truncation before the fix.

Compatibility

Output is unchanged for well-formed node lists, and getoutput's combined
stdout/stderr and non-raising behaviour are preserved. One difference worth
calling out: a missing scontrol binary now raises FileNotFoundError rather
than silently assigning a shell error string to MASTER_ADDR. That only affects
the launcher='slurm' path, where scontrol is expected to exist, and failing
loudly seemed preferable to proceeding with a bogus master address — happy to
restore the previous silent behaviour if you would rather keep the diff purely
mechanical.

_init_dist_slurm passed the SLURM_NODELIST environment variable into a shell
command string via subprocess.getoutput, so a crafted value could execute
arbitrary commands:

    SLURM_NODELIST='x; touch /tmp/PWNED; echo done'

Pass the node list as an argv element instead, and take the first line in
Python rather than piping through `head -n1`.

Output is unchanged for well-formed node lists. Combined stdout/stderr and the
non-raising behaviour of getoutput are preserved, with one difference worth
noting: a missing `scontrol` binary now raises FileNotFoundError instead of
silently assigning a shell error message to MASTER_ADDR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant