Skip to content

A PWN Request in make-dist workflow can execute PR-controlled code with write-scoped GITHUB_TOKEN

High
jimklimov published GHSA-w6wj-3r73-fxmh Jun 10, 2026

Package

actions networkupstools/nut (GitHub Actions)

Affected versions

db633d1bdf2ab36eb28343ae91bb0775ceaa1919

Patched versions

658b24ef8410648ceca6d5a59e8690efbc8c36bc
actions networkupstools/wmnut (GitHub Actions)
4b3812ce5442bc36d03cc788cee55d2749162427
1aa31d14b649031b43ada9e04cf82882a64cba0a

Description

SUMMARY

The GitHub Actions script used to prepare NUT tarballs and update GitHub Checks statuses and PR comments about it was mis-structured in terms of mixing code running with higher privileges (single-use token generated with write permissions) and untrusted inputs (PR source branch).

This was proven insecure by research attached to this advisory: a malicious PR run from a fork could extract the GITHUB_TOKEN value. It could potentially be abused while it was valid (while the GHA job ran) to manipulate Git repository contents, commit checks/statuses, or issue/PR comments, according to permissions it was issued with.

Kudos to @avivdon and @Wang-Haimin for independent discoveries and similar analysis, and responsible reporting, and additionally to @avivdon for preparing a separate set of repositories to prove the problem and evaluate the fixes.

Remedy:

  • The script was restructured into two workflows, one dealing only with metadata writes (and permissions to do so, the set of which was revised and reduced), and another focusing only on build and upload of the tarball artifacts. Sibling repositories were revised, and this fix was also applied to WMNut recipes.
    • NUT: problem introduced between releases v2.8.4 and v2.8.5, fixed after v2.8.5 before v2.8.6.
    • WMNut: problem both introduced and fixed after the most recent official release 0.72.
  • The token involved is generated for each run, so there is nothing to revoke or treat as compromised if a token did leak earlier.
  • Actual code history seems to have never been abused by this vector (regular development and code sync across maintainer workstations and CI systems did not expose any abnormalities like force-pushes or unexpected advancement of the master branch).
  • There is nothing to fix for consumers of the NUT code base (packagers, etc.) regarding this issue. However if there are any forks including flawed code - their branches treated by GitHub as "default" or "base" (used for reference GHA implementations) should be updated with similar restructuring of .github/workflows/01-make-dist*.yml scripts.

Follow-ups on the side:

  • A GitHub backed facility for responsible reporting of suspected vulnerabilities was enabled
  • A SECURITY.md policy was published
  • Dependabot enabled to keep GHA action versions up to date

INITIAL POST

Hello Network UPS Tools maintainers,

I would like to report a potential GitHub Actions CI/CD security issue affecting networkupstools/nut.

I did not access secrets, exfiltrate tokens, push changes, modify repository state, or perform destructive testing. This report is based on static verification of the current workflow structure.

Summary

Affected workflow:

.github/workflows/01-make-dist.yml

Checked commit:

db633d1bdf2ab36eb28343ae91bb0775ceaa1919

The workflow uses pull_request_target, grants write-capable permissions, checks out the pull request head SHA, and then executes scripts and build targets from the checked-out repository. This is a classic Pwn Request pattern.

Technical details

The workflow declares write-capable permissions:

checks: write
contents: write
issues: write
pull-requests: write

For pull request events, the checkout ref resolves to the PR head SHA:

github.event.pull_request.head.sha

The workflow then executes code from that checkout, including:

bash -x ./tools/gitlog2version.sh
./autogen.sh
./configure
make dist
make distcheck
make distcheck-completeness
make dist-docs

Because these files and build targets come from the PR checkout, a fork PR author may influence code executed in the pull_request_target workflow context.

Although persist-credentials: false is useful hardening for checkout credentials, it does not prevent execution of PR-controlled code in the privileged job. The same job still has write-capable workflow permissions and later uses GITHUB_TOKEN for GitHub API operations such as PR comments and check-run updates.

Impact

If exploited, a malicious pull request may execute code in a privileged workflow context.

Potential impact includes:

  • misuse or exposure of a write-capable GITHUB_TOKEN;
  • modification of check-run status or PR/issue metadata;
  • potential repository write impact through contents: write;
  • manipulation of generated distribution artifacts;
  • misleading PR comments or build artifact links;
  • broader supply-chain impact if generated artifacts are consumed by users or downstream automation.

Suggested remediation

Recommended fixes:

  1. Do not checkout and execute PR head code in a pull_request_target workflow.

  2. For untrusted PRs, use pull_request instead of pull_request_target, with no repository secrets and read-only permissions.

  3. If pull_request_target is required, checkout only trusted base-branch code before running scripts.

  4. Add a fork-origin rejection before privileged execution:

if: github.event.pull_request.head.repo.full_name == github.repository
  1. Alternatively require a maintainer-applied trusted label or OWNER/MEMBER/COLLABORATOR actor check before running the privileged build.

  2. Split the workflow:

untrusted PR build:
  - pull_request
  - no write token
  - no secrets
  - no distribution publication

trusted distribution build:
  - same-repository branch or maintainer-approved only
  - write token only after trust is established
  1. Keep persist-credentials: false, but do not rely on it as the primary mitigation because it does not stop PR-controlled code execution.

  2. Consider rotating tokens or reviewing recent workflow runs if untrusted fork PRs may previously have reached this workflow path.

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
High
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
High
Availability
Low

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:H/PR:N/UI:R/S:C/C:H/I:H/A:L

CVE ID

CVE-2026-54160

Weaknesses

Inclusion of Functionality from Untrusted Control Sphere

The product imports, requires, or includes executable functionality (such as a library) from a source that is outside of the intended control sphere. Learn more on MITRE.

Credits