Labels: bug, security
⚠ Security report — arbitrary OS command execution as the running user, proven end-to-end on a
deployed 512-rank cesm.exe. Please triage privately; happy to move to a security contact.
What happened?
The share-code file utilities build shell command lines by string-concatenating file/stream-derived
paths into /bin/cp, msrcp, and rm, and run them through a shell
(src/share/util/shr_sys_mod.F90:83 call execute_command_line(str) → /bin/sh -c). The operands come
from a data-model stream descriptor, so any shell metacharacter in a <filePath>/<fileNames>
entry is executed as a command.
src/share/util/shr_file_mod.F90 — shr_file_get (get, ~line 274/280), shr_file_put (put, ~line 164/167):
rfn = rem_fn ; if (rem_fn(1:3)=="cp:") rfn = rem_fn(4:)
cmd = '/bin/cp -f '//trim(rfn)//' '//trim(loc_fn) ! :274 (put mirror + optional ' && /bin/rm -f '//loc_fn)
call shr_sys_system(trim(cmd),rcode) ! :280 (mss:/msrcp branch same: :179 / :288)
src/share/streams/shr_dmodel_mod.F90 (~line 711):
localCopy = (shr_file_queryPrefix(path) /= shr_file_noPrefix) ! stream path has cp:/mss:
if (localCopy .and. rmOldFile .and. fileExists) &
call shr_sys_system(" rm "//trim(fn_prev),rCode) ! fn_prev = a stream <fileNames> entry
The data models (DOCN/DATM/DICE/DLND/DROF) read a stream text descriptor at init and, for any stream
whose <filePath> carries a cp:/mss: prefix, call shr_file_get with the stream's own path/filename
strings — unconditionally on the master task:
src/share/streams/shr_strdata_mod.F90:308-311 call shr_stream_getDomainInfo(...); call shr_stream_getFile(filePath,fileName)
src/share/streams/shr_stream_mod.F90:2217 if (shr_file_queryPrefix(filePath)/=shr_file_noPrefix) call shr_file_get(rCode,fileName,trim(filePath)//adjustl(fileName))
<filePath>/<fileNames> are read a whole line at a time (shr_stream_mod.F90:638 read(nUnit,'(a)') str,
then only leftalign_and_convert_tabs), so internal spaces are preserved — a plain name.nc; <cmd> ; #
payload works. Impact: arbitrary command execution as the run user, at model startup, on every run
that uses a data model with a cp:/mss:-prefixed stream path. Data-only (no memory-safety dependency).
What did you expect to happen?
Filenames and paths read from a stream descriptor should be treated as data, never as shell input. The
copy/remove operations should not pass user-controlled strings through /bin/sh; a filename containing
;, `, $(…), &, |, or > must not cause command execution.
Environment
- CIME as shipped with CESM 2.1.5 (files:
src/share/util/shr_file_mod.F90,
src/share/streams/{shr_dmodel_mod,shr_stream_mod,shr_strdata_mod}.F90,
src/share/util/shr_sys_mod.F90).
- Machine: NCAR Derecho (HPE Cray EX, SUSE Linux); 512 MPI ranks; MCT driver.
- Compiler: Intel oneAPI (
ifx/icx). The issue is language-level / data-driven and
compiler-independent.
- Compset used for the in-situ reproduction:
2000_CAM60_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_CISM2%NOEVOLVE_SWAV
(DOCN%DOM active).
Describe steps to recreate issue
A. Self-contained (no model run, ~5 s). Reproduce the exact chain (shr_stream_getFile gate →
shr_file_get cp-branch → execute_command_line) with filePath='cp:/…/' and
fileName='sst.nc; touch PWNED; id>PWNED_ID.txt #', compiled with ifx -O2.
B. In-situ, deployed run. In the active docn.streams.txt.prescribed, edit the <domainInfo> block:
<filePath> cp:/glade/campaign/cesm/cesmdata/inputdata/share/domains </filePath>
<fileNames> domain.ocn.fv0.9x1.25_gx1v7.151020.nc; touch /tmp/PWNED; id>/tmp/PWNED_ID.txt # </fileNames>
Start the case. The injected commands run at DOCN strdata init; the model then aborts on the now-invalid
domain filename.
Relevant logs
Standalone (A) and in-situ (B) both execute the injected commands as the run user:
[shr_file_get] cmd = /bin/cp -f /…/sst.nc; touch PWNED; id>PWNED_ID.txt # …
/bin/cp: missing destination file operand # cp truncated by ';'
-> PWNED created
-> PWNED_ID.txt : uid=...
In-situ on the deployed 512-rank cesm.exe: run START → MODEL EXIT ~49 s later; /tmp/PWN_DOCN_INSITU
created and /tmp/PWN_DOCN_ID.txt = uid=…(run user).
Anything else we need to know?
- Same root cause, multiple sites — please fix as one unit:
shr_file_get (:280), shr_file_put
(:167), their mss:/msrcp branches (:179,:288), and shr_dmodel_mod.F90:711.
- Sibling issue reported to ESCOMP/CAM:
tracer_data.F90 and metdata.F90 build 'rm -f '//filename
from a filenames-list text file and call the same shr_sys_system. Same class, same fix.
- Suggested fix: never build a shell command from a file/stream-derived name — use an argv-exec (no
/bin/sh) or POSIX unlink()/copy; reject cp:/mss: targets containing shell metacharacters;
validate stream <filePath>/<fileNames> against a strict whitelist ([A-Za-z0-9._/:+-]).
- PoC and full in-situ evidence available on request (faithful harness + deployed-run reproduction).
- Authorized audit on the reporter's own deployment; no third-party systems involved.
Labels: bug, security
What happened?
The share-code file utilities build shell command lines by string-concatenating file/stream-derived
paths into
/bin/cp,msrcp, andrm, and run them through a shell(
src/share/util/shr_sys_mod.F90:83 call execute_command_line(str)→/bin/sh -c). The operands comefrom a data-model stream descriptor, so any shell metacharacter in a
<filePath>/<fileNames>entry is executed as a command.
src/share/util/shr_file_mod.F90—shr_file_get(get, ~line 274/280),shr_file_put(put, ~line 164/167):src/share/streams/shr_dmodel_mod.F90(~line 711):The data models (DOCN/DATM/DICE/DLND/DROF) read a stream text descriptor at init and, for any stream
whose
<filePath>carries acp:/mss:prefix, callshr_file_getwith the stream's own path/filenamestrings — unconditionally on the master task:
<filePath>/<fileNames>are read a whole line at a time (shr_stream_mod.F90:638 read(nUnit,'(a)') str,then only
leftalign_and_convert_tabs), so internal spaces are preserved — a plainname.nc; <cmd> ; #payload works. Impact: arbitrary command execution as the run user, at model startup, on every run
that uses a data model with a
cp:/mss:-prefixed stream path. Data-only (no memory-safety dependency).What did you expect to happen?
Filenames and paths read from a stream descriptor should be treated as data, never as shell input. The
copy/remove operations should not pass user-controlled strings through
/bin/sh; a filename containing;,`,$(…),&,|, or>must not cause command execution.Environment
src/share/util/shr_file_mod.F90,src/share/streams/{shr_dmodel_mod,shr_stream_mod,shr_strdata_mod}.F90,src/share/util/shr_sys_mod.F90).ifx/icx). The issue is language-level / data-driven andcompiler-independent.
2000_CAM60_CLM50%SP_CICE%PRES_DOCN%DOM_MOSART_CISM2%NOEVOLVE_SWAV(DOCN%DOM active).
Describe steps to recreate issue
A. Self-contained (no model run, ~5 s). Reproduce the exact chain (
shr_stream_getFilegate →shr_file_getcp-branch →execute_command_line) withfilePath='cp:/…/'andfileName='sst.nc; touch PWNED; id>PWNED_ID.txt #', compiled withifx -O2.B. In-situ, deployed run. In the active
docn.streams.txt.prescribed, edit the<domainInfo>block:Start the case. The injected commands run at DOCN strdata init; the model then aborts on the now-invalid
domain filename.
Relevant logs
Standalone (A) and in-situ (B) both execute the injected commands as the run user:
In-situ on the deployed 512-rank
cesm.exe: run START → MODEL EXIT ~49 s later;/tmp/PWN_DOCN_INSITUcreated and
/tmp/PWN_DOCN_ID.txt = uid=…(run user).Anything else we need to know?
shr_file_get(:280),shr_file_put(
:167), theirmss:/msrcpbranches (:179,:288), andshr_dmodel_mod.F90:711.tracer_data.F90andmetdata.F90build'rm -f '//filenamefrom a filenames-list text file and call the same
shr_sys_system. Same class, same fix./bin/sh) or POSIXunlink()/copy; rejectcp:/mss:targets containing shell metacharacters;validate stream
<filePath>/<fileNames>against a strict whitelist ([A-Za-z0-9._/:+-]).