<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rokkhe</title>
    <description>The latest articles on DEV Community by Rokkhe (@rokkhe).</description>
    <link>https://dev.to/rokkhe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4242669%2F79a4efb2-d27e-4b89-baf2-f406de360906.png</url>
      <title>DEV Community: Rokkhe</title>
      <link>https://dev.to/rokkhe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rokkhe"/>
    <language>en</language>
    <item>
      <title>Pin your GitHub Actions by commit SHA and lock workflow permissions</title>
      <dc:creator>Rokkhe</dc:creator>
      <pubDate>Sat, 14 Nov 2026 03:27:19 +0000</pubDate>
      <link>https://dev.to/rokkhe/pin-your-github-actions-by-commit-sha-and-lock-workflow-permissions-1kp2</link>
      <guid>https://dev.to/rokkhe/pin-your-github-actions-by-commit-sha-and-lock-workflow-permissions-1kp2</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was first published on the &lt;a href="https://rokkhe.hashnode.dev/pin-github-actions-sha-lock-workflow-permissions" rel="noopener noreferrer"&gt;Rokkhe blog on Hashnode&lt;/a&gt;. It was drafted with AI assistance and reviewed and edited by the Rokkhe team; all commands and sources were checked against GitHub's documentation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A hands-on hardening guide for teams that already ship on GitHub Actions. 14 November 2026, Asia/Dhaka. By the Rokkhe research team.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A tag like &lt;code&gt;@v6&lt;/code&gt; is convenient, and it is also movable. Anyone who can push a new commit to that tag can change the code your workflow runs the next time the tag is resolved. The same workflow often also hands the job a &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; that can write to the repository. Those two defaults — a movable pointer and a write-capable token — are what this post closes.&lt;/p&gt;

&lt;p&gt;You will leave with three things: every &lt;code&gt;uses:&lt;/code&gt; line pinned to a full 40-character commit SHA, a workflow that starts from read-only permissions, and a short list of &lt;code&gt;pull_request_target&lt;/code&gt; mistakes to avoid. All of the commands below are read-first. None of them changes a GitHub setting until you choose to apply the YAML.&lt;/p&gt;

&lt;p&gt;This is the first post in a ten-part how-to series. It is the only CI/CD-focused guide in the set. Later posts cover the OWASP Top 10, secrets, CVE triage, cloud and Kubernetes hardening, API and LLM-app security, an AI review harness, and Sigma rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Pin third-party Actions, and your own reusable workflows, to a &lt;strong&gt;full commit SHA&lt;/strong&gt;, not a tag or branch. GitHub’s own documentation tells you to do this.&lt;/li&gt;
&lt;li&gt;Put a &lt;code&gt;permissions:&lt;/code&gt; block at the top of every workflow so the default &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is read-only, then grant extra scopes on the job that needs them.&lt;/li&gt;
&lt;li&gt;Treat &lt;code&gt;pull_request_target&lt;/code&gt; as a privileged event: it runs in the base repository’s context and can see secrets. Do not check out and run untrusted pull-request code in that workflow.&lt;/li&gt;
&lt;li&gt;Audit what you already have before you change production deploy workflows. The commands below only read.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why a moving tag is not a pin
&lt;/h2&gt;

&lt;p&gt;An Action is just a Git repository. When a workflow says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub resolves &lt;code&gt;v6&lt;/code&gt; to whatever commit that tag currently points at. Tags are not immutable. A tag can be deleted and recreated, or force-moved, so the same &lt;code&gt;@v6&lt;/code&gt; on Monday is not guaranteed to be the same code on Tuesday.&lt;/p&gt;

&lt;p&gt;A full Git commit SHA names one object. This is the form GitHub documents for pinning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span class="c1"&gt;# v6.1.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SHA above is an &lt;strong&gt;example of the form&lt;/strong&gt;: it is what the &lt;code&gt;v6.1.0&lt;/code&gt; tag of &lt;code&gt;actions/checkout&lt;/code&gt; resolved to when we checked on 14 November 2026. Before you paste any SHA into a production workflow, resolve it yourself from the Action’s official repository, as shown in the next section. Do not copy SHAs from a blog post and assume they are still the latest release.&lt;/p&gt;

&lt;p&gt;Pinning does not mean “never update.” It means updates become a deliberate pull request: a new SHA, a comment with the version, and a review. That is the point. You want the change to be visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pin every &lt;code&gt;uses:&lt;/code&gt; line to a full commit SHA
&lt;/h2&gt;

&lt;p&gt;Work in a branch. For each Action, resolve the tag you intended to use to a 40-character SHA from the &lt;strong&gt;official&lt;/strong&gt; repository, not a fork.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example only: resolve a tag on the official actions/checkout repo.&lt;/span&gt;
&lt;span class="c"&gt;# Replace the URL and tag with the Action you actually use.&lt;/span&gt;
git ls-remote https://github.com/actions/checkout.git refs/tags/v6.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You want the peel to a commit, not an annotated-tag object. If the command prints both a tag object and a peeled commit (&lt;code&gt;^{}&lt;/code&gt;), take the commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git ls-remote --tags https://github.com/actions/checkout.git &lt;span class="s1"&gt;'v6.1.0*'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm the commit is on the official repo in the browser: &lt;code&gt;https://github.com/OWNER/ACTION/commit/THE_SHA&lt;/code&gt;. Then write the pin with the human-readable version in a comment, which GitHub’s documentation encourages so reviewers can see what you meant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out the repository&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span class="c1"&gt;# v6.1.0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply the same rule to reusable workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;call-shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octo-org/common/.github/workflows/build.yml@FULL_40_CHAR_SHA&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A branch name (&lt;code&gt;@main&lt;/code&gt;) or a moving major tag (&lt;code&gt;@v2&lt;/code&gt;) on a &lt;code&gt;uses:&lt;/code&gt; line is not a pin. Abbreviated SHAs are also the wrong form; GitHub asks for the full length.&lt;/p&gt;

&lt;p&gt;If you use Dependabot or a similar updater, configure it to open pull requests that bump the SHA and the comment together. The pin is only useful if the update path is boring.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set default workflow permissions to read-only, then grant per job
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; is created automatically for each job. GitHub’s guidance is to grant it the minimum permissions the job needs, and to set the default for repository contents to read-only. The clean way to do that in the workflow file is an explicit top-level block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span class="c1"&gt;# v6.1.0
&lt;/span&gt;      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;make test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a job must write, raise the scope on &lt;strong&gt;that job only&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
      &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span class="c1"&gt;# v6.1.0
&lt;/span&gt;      &lt;span class="c1"&gt;# publish steps go here
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;id-token: write&lt;/code&gt; is not a repository-write grant. It only lets the job request an OIDC token for a cloud exchange. That is expected if you later move off long-lived cloud keys, but it should still be limited to the job that talks to the cloud.&lt;/p&gt;

&lt;p&gt;Also set the repository (or organization) default in the GitHub UI so a workflow file that forgets &lt;code&gt;permissions:&lt;/code&gt; does not silently get a write token: &lt;strong&gt;Settings → Actions → General → Workflow permissions → Read repository contents and packages permissions&lt;/strong&gt;. The YAML block is still required, because it is reviewed with the code.&lt;/p&gt;

&lt;p&gt;Do not use &lt;code&gt;permissions: write-all&lt;/code&gt;. If you see it in a copied example, delete it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat &lt;code&gt;pull_request_target&lt;/code&gt; as a privileged event
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pull_request&lt;/code&gt; runs in the context of the merge commit and, for a fork, with a restricted token and no secrets. &lt;code&gt;pull_request_target&lt;/code&gt; is different. GitHub’s documentation is explicit: the workflow runs in the &lt;strong&gt;base&lt;/strong&gt; repository’s context, with the base repository’s &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; and access to secrets. That is what makes it useful for labelling or commenting on pull requests from forks. It is also what makes it dangerous.&lt;/p&gt;

&lt;p&gt;The safe pattern is: the workflow file, and any &lt;code&gt;actions/checkout&lt;/code&gt; that does not pass an explicit &lt;code&gt;ref&lt;/code&gt;, come from the default branch of the base repository. Untrusted code from the pull request is not executed.&lt;/p&gt;

&lt;p&gt;The unsafe pattern, which GitHub and the GitHub Security Lab have both warned about, is to check out the pull request’s untrusted ref and then run its install or test scripts in that same privileged job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Unsafe pattern — do not use this.
# It is shown only so you can recognise it in a review.
&lt;/span&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request_target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span class="c1"&gt;# v6.1.0
&lt;/span&gt;        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.event.pull_request.head.sha }}&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install &amp;amp;&amp;amp; npm test&lt;/span&gt;   &lt;span class="c1"&gt;# this runs untrusted code with base secrets
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need to comment on a fork pull request, keep the privileged job small: no checkout of the head SHA, no &lt;code&gt;npm install&lt;/code&gt; of the head branch, no running of the contributor’s Makefile. Use a &lt;code&gt;pull_request&lt;/code&gt; workflow for tests, and a tightly scoped &lt;code&gt;pull_request_target&lt;/code&gt; workflow only for authentication-needed comments, with &lt;code&gt;permissions&lt;/code&gt; limited to &lt;code&gt;pull-requests: write&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Two newer guardrails are worth knowing. GitHub now applies a default Actions event policy that blocks &lt;code&gt;pull_request_target&lt;/code&gt; in public repositories that have no event policy of their own. It currently runs in evaluate mode, and GitHub says it will enforce it on 2 December 2026, so check your policy insights now if you rely on this event. Recent versions of &lt;code&gt;actions/checkout&lt;/code&gt; also refuse to check out a fork's pull-request head in this context unless you set &lt;code&gt;allow-unsafe-pr-checkout: true&lt;/code&gt;. Treat that flag as a review red flag: only set it when the checked-out code is read as data and never executed.&lt;/p&gt;

&lt;p&gt;GitHub also documents cache behaviour for this event: &lt;code&gt;pull_request_target&lt;/code&gt; workflows get read-only access to the default branch’s cache, unless you opt into a writeable cache mode, which reintroduces cache-poisoning risk. Leave the default alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  A copy-paste starter workflow
&lt;/h2&gt;

&lt;p&gt;Use this as a starting point for a private or public repository that runs tests on pull requests. Resolve the checkout SHA yourself before you merge it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check out&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803&lt;/span&gt; &lt;span class="c1"&gt;# v6.1.0
&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;set -euo pipefail&lt;/span&gt;
          &lt;span class="s"&gt;if [ -f Makefile ]; then&lt;/span&gt;
            &lt;span class="s"&gt;make test&lt;/span&gt;
          &lt;span class="s"&gt;elif [ -f package.json ]; then&lt;/span&gt;
            &lt;span class="s"&gt;npm ci&lt;/span&gt;
            &lt;span class="s"&gt;npm test&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;echo "Add the test command for this repository."&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no &lt;code&gt;pull_request_target&lt;/code&gt; here, no &lt;code&gt;write-all&lt;/code&gt;, and no secrets. Add deploy jobs in a separate workflow, on a trusted event such as &lt;code&gt;push&lt;/code&gt; to &lt;code&gt;main&lt;/code&gt; or a release, with job-level &lt;code&gt;permissions&lt;/code&gt; and an environment that requires a reviewer if the job can touch production.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to audit the workflows you already have
&lt;/h2&gt;

&lt;p&gt;These commands only read the files on disk. Run them in a clone of the repository.&lt;/p&gt;

&lt;p&gt;List every workflow and every &lt;code&gt;uses:&lt;/code&gt; line that is not pinned to a 40-character SHA:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# From the repository root.&lt;/span&gt;
find .github/workflows -type f \&lt;span class="o"&gt;(&lt;/span&gt; -name &lt;span class="s1"&gt;'*.yml'&lt;/span&gt; -o -name &lt;span class="s1"&gt;'*.yaml'&lt;/span&gt; \&lt;span class="o"&gt;)&lt;/span&gt; -print

&lt;span class="c"&gt;# uses: lines that do not end in a 40-character hex SHA (tags, branches, short SHAs).&lt;/span&gt;
grep -nE &lt;span class="s1"&gt;'uses:'&lt;/span&gt; .github/workflows/*.&lt;span class="o"&gt;{&lt;/span&gt;yml,yaml&lt;span class="o"&gt;}&lt;/span&gt; 2&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null \
  &lt;span class="o"&gt;|&lt;/span&gt; grep -vE &lt;span class="s1"&gt;'uses:[[:space:]]*[^@]+@[0-9a-f]{40}([[:space:]]|#|$)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List workflows that never set &lt;code&gt;permissions:&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; f &lt;span class="k"&gt;in&lt;/span&gt; .github/workflows/*.&lt;span class="o"&gt;{&lt;/span&gt;yml,yaml&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; -f &lt;span class="s2"&gt;"$f"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;continue&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; ! grep -qE &lt;span class="s1"&gt;'^[[:space:]]*permissions:'&lt;/span&gt; &lt;span class="s2"&gt;"$f"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'No permissions block: %s\n'&lt;/span&gt; &lt;span class="s2"&gt;"$f"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List &lt;code&gt;pull_request_target&lt;/code&gt; workflows so a person can review them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;grep -n &lt;span class="s1"&gt;'pull_request_target'&lt;/span&gt; .github/workflows/*.&lt;span class="o"&gt;{&lt;/span&gt;yml,yaml&lt;span class="o"&gt;}&lt;/span&gt; 2&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On GitHub, also open &lt;strong&gt;Actions → workflow run logs&lt;/strong&gt; after a test change and confirm secrets are redacted, as GitHub recommends in its secure-use reference. If a secret ever appears in a log, delete that log and rotate the secret. Finding a leak is the subject of post 3 in this series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Every &lt;code&gt;uses:&lt;/code&gt; line, including reusable workflows, pins a full 40-character SHA from the official repository.&lt;/li&gt;
&lt;li&gt;[ ] A version comment sits next to each SHA so reviewers can see the intended release.&lt;/li&gt;
&lt;li&gt;[ ] Each workflow file has a top-level &lt;code&gt;permissions:&lt;/code&gt; block; the default is &lt;code&gt;contents: read&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] Write scopes are granted only on the job that needs them, never with &lt;code&gt;write-all&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;[ ] The repository (or organization) default workflow permission is also read-only.&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;pull_request_target&lt;/code&gt; is used only when a base-repository token is required, and that job does not check out and execute untrusted head code.&lt;/li&gt;
&lt;li&gt;[ ] Deploy jobs use a GitHub Environment with required reviewers when they can reach production.&lt;/li&gt;
&lt;li&gt;[ ] An updater (Dependabot or equivalent) can open PRs that bump the SHA and the comment together.&lt;/li&gt;
&lt;li&gt;[ ] Someone has grepped the existing workflows with the audit commands above.&lt;/li&gt;
&lt;li&gt;[ ] If a secret was ever printed in a log, that log was deleted and the secret was rotated.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is pinning to &lt;code&gt;@v6.1.0&lt;/code&gt; (an exact tag) good enough?&lt;/strong&gt;&lt;br&gt;
No. An exact tag is more stable than &lt;code&gt;@v6&lt;/code&gt;, but the tag can still be moved. GitHub’s documented pin is the full commit SHA, with the tag in a comment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does a SHA pin mean we are stuck on an old Action?&lt;/strong&gt;&lt;br&gt;
No. You update by changing the SHA in a pull request, the same way you update any other dependency. The difference is that the update is reviewed, not silent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can we pin to a short SHA?&lt;/strong&gt;&lt;br&gt;
Do not. Use the full 40-character object name.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We need to comment on pull requests from forks. Must we use &lt;code&gt;pull_request_target&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
Often yes, because a privileged token is required to comment. Keep that workflow small, grant only &lt;code&gt;pull-requests: write&lt;/code&gt;, and do not run the contributor’s build in that job.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Will this break Dependabot?&lt;/strong&gt;&lt;br&gt;
Only if Dependabot is not configured to update Actions. Once it is, it should open PRs that retarget the SHA. Review those PRs the way you review application bumps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Docs, &lt;a href="https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/find-and-customize-actions" rel="noopener noreferrer"&gt;Using pre-written building blocks in your workflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Docs, &lt;a href="https://docs.github.com/en/actions/reference/security/secure-use" rel="noopener noreferrer"&gt;Secure use reference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Docs, &lt;a href="https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target" rel="noopener noreferrer"&gt;Securely using pull_request_target&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Docs, &lt;a href="https://docs.github.com/en/actions/tutorials/authenticate-with-github_token" rel="noopener noreferrer"&gt;Use GITHUB_TOKEN for authentication in workflows&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next in the series
&lt;/h2&gt;

&lt;p&gt;Next up: &lt;em&gt;Test and fix the OWASP Top 10 2025 in your web app this week.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Rokkhe is an AI pentesting and security-operations workspace. Learn more at &lt;a href="https://rokkhe.com/" rel="noopener noreferrer"&gt;rokkhe.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>github</category>
      <category>security</category>
      <category>devops</category>
      <category>cicd</category>
    </item>
    <item>
      <title>CVE-2026-123039: How PHP2Shell Became Root on Check Point Gateways, and How to Close It</title>
      <dc:creator>Rokkhe</dc:creator>
      <pubDate>Fri, 13 Nov 2026 10:03:06 +0000</pubDate>
      <link>https://dev.to/rokkhe/cve-2026-123039-how-php2shell-became-root-on-check-point-gateways-and-how-to-close-it-1mef</link>
      <guid>https://dev.to/rokkhe/cve-2026-123039-how-php2shell-became-root-on-check-point-gateways-and-how-to-close-it-1mef</guid>
      <description>&lt;p&gt;&lt;em&gt;Vulnerability briefing · 13 November 2026 · By the Rokkhe research team&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CVE-2026-123039 is a high-severity (CVSS 7.8) privilege escalation in the DynamicCLI service of Check Point Gaia OS. On 12 November 2026, Check Point confirmed that attackers chained it with PHP2Shell in the Mobile Access portal to gain root on unpatched gateways. Install the November Jumbo Hotfix, then hunt for the published indicators.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What:&lt;/strong&gt; a root process on Gaia (&lt;code&gt;dynamic_server&lt;/code&gt;) accepts commands from a world-writable local socket without checking who is connecting. Anyone already running code on the box can become root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How it was used:&lt;/strong&gt; PHP2Shell (CVE-2026-109618 / CVE-2026-109619) gave the attacker code execution through the Mobile Access portal. CVE-2026-123039 turned that into root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who:&lt;/strong&gt; Check Point Force Firewalls, Management and Log Servers, Multi-Domain servers and Maestro Orchestrator on R81 to R82.20. Spark Firewalls and Smart-1 Cloud are not affected by CVE-2026-123039.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploited?&lt;/strong&gt; Yes. Check Point saw the chain used against "a couple of dozen" unpatched systems between 30 October and 4 November 2026.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fix:&lt;/strong&gt; November Jumbo Hotfix: R82.20 Take 14, R82.10 Take 52, R82 Take 132 or R81.20 Take 174 or later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then:&lt;/strong&gt; patching closes the door, but it does not remove an attacker who is already inside. Hunt before you call it done.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is CVE-2026-123039?
&lt;/h2&gt;

&lt;p&gt;Gaia's dynamic (extended) CLI is served by a root process called &lt;code&gt;dynamic_server&lt;/code&gt;. It listens on a local socket at &lt;code&gt;/tmp/dynamic_socket&lt;/code&gt;. According to Check Point's advisory (&lt;a href="https://support.checkpoint.com/results/sk/sk1000362" rel="noopener noreferrer"&gt;sk1000362&lt;/a&gt;), on affected versions that socket is created world-writable (0777), the connecting process is not authenticated, and the daemon runs whatever command string it receives as root.&lt;/p&gt;

&lt;p&gt;On its own, this is a local bug: CVSS 3.1 vector &lt;code&gt;AV:L/AC:L/PR:L/UI:N&lt;/code&gt;, score 7.8. An attacker first needs to run code on the appliance. That is exactly what PHP2Shell provides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this one matters most this week
&lt;/h2&gt;

&lt;p&gt;We weighed everything disclosed or newly exploited from 11 to 13 November, including a critical Apache HTTP Server mod_http2 bug (CVE-2026-119618) and a large Citrix NetScaler patch set. Neither had confirmed in-the-wild exploitation when we checked. This chain does. It hits the security gateway itself, the device that sits at the edge of the network and holds VPN access and the firewall policy. In some of the incidents the attackers also reached a hypervisor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who is affected?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CVE-2026-123039 (local privilege escalation, &lt;a href="https://support.checkpoint.com/results/sk/sk1000362" rel="noopener noreferrer"&gt;sk1000362&lt;/a&gt;):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Products: Check Point Force Firewalls; Management and Log Servers; Multi-Domain Security Management and Multi-Domain Log Server; Maestro Orchestrator.&lt;/li&gt;
&lt;li&gt;Affected versions: R82.20, R82.10, R82, R81.20, plus R81.10 and R81 (both end of support).&lt;/li&gt;
&lt;li&gt;Not affected: Check Point Spark Firewalls and Smart-1 Cloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;PHP2Shell on Check Point (remote code execution, &lt;a href="https://support.checkpoint.com/results/sk/sk1000302" rel="noopener noreferrer"&gt;sk1000302&lt;/a&gt;).&lt;/strong&gt; This applies to firewalls that use any of these features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mobile Access Portal&lt;/li&gt;
&lt;li&gt;Remote Access VPN with SSL Network Extender&lt;/li&gt;
&lt;li&gt;Remote Access VPN with SAML realm authentication&lt;/li&gt;
&lt;li&gt;Identity Awareness Captive Portal&lt;/li&gt;
&lt;li&gt;UserCheck Portal&lt;/li&gt;
&lt;li&gt;Zero Phishing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Affected versions are R81.20 to R82.20, older end-of-support releases from R80, and Spark R81.10.x and R82.00.x. Security Management and Log Servers are not affected by PHP2Shell.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixed versions (both issues):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;R82.20: Jumbo Hotfix Take 14 or later&lt;/li&gt;
&lt;li&gt;R82.10: Jumbo Hotfix Take 52 or later&lt;/li&gt;
&lt;li&gt;R82: Jumbo Hotfix Take 132 or later&lt;/li&gt;
&lt;li&gt;R81.20: Jumbo Hotfix Take 174 or later&lt;/li&gt;
&lt;li&gt;Spark R82.00.11: build B998002843 or later&lt;/li&gt;
&lt;li&gt;Spark R81.10.17: build B996005005 or later&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How the attack works
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fryg8uu2obndzb0i98i0w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fryg8uu2obndzb0i98i0w.png" alt="Portal to root in four steps" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how Check Point Research described the intrusions, in plain terms:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Entry.&lt;/strong&gt; The attacker reached an internet-facing Mobile Access portal on a gateway that did not have the 23 October PHP2Shell hotfix.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code execution.&lt;/strong&gt; PHP2Shell, a use-after-free class of flaws in PHP request handling, gave them code execution as the portal's service account. Check Point found PHP session files containing the marker &lt;code&gt;upload_progress_pwn&lt;/code&gt;, and hundreds of crashes in the portal's web workers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Root.&lt;/strong&gt; The attacker wrote a command to the DynamicCLI socket (CVE-2026-123039), and it ran as root.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence and pivot.&lt;/strong&gt; They planted PHP webshells as &lt;code&gt;zz*.tpl&lt;/code&gt; templates in the portal, and a backdoor disguised as &lt;code&gt;CPconfigdaemon&lt;/code&gt; that later runs as &lt;code&gt;slot_CPconfigdaemon&lt;/code&gt; and drops &lt;code&gt;/lib/libcpfwcommon.so&lt;/code&gt;. They scanned the internal network, moved over SSH to a VMware ESXi host using the &lt;code&gt;vpxuser&lt;/code&gt; account, and in a limited number of cases ran a wiper.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Check Point says a first wave on 30 October focused on organisations in one region (Israel). A second wave on 2 November scanned organisations in several countries, with no evidence of destructive activity. Check Point also says the evidence does not support a high-confidence attribution.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check if you're affected
&lt;/h2&gt;

&lt;p&gt;Run these on every gateway, cluster member and management server. Use Expert mode unless noted. All of them only read; none changes anything.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version and Jumbo take:&lt;/strong&gt; run &lt;code&gt;show version all&lt;/code&gt; in clish, or &lt;code&gt;cpinfo -y all&lt;/code&gt; in Expert mode and read the &lt;code&gt;JUMBO_HF_MAIN&lt;/code&gt; take. Compare it with the fixed takes above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exposure to PHP2Shell:&lt;/strong&gt; &lt;code&gt;enabled_blades&lt;/code&gt; lists the active blades. Look for Mobile Access and Identity Awareness, then check in SmartConsole which portals face the internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The vulnerable socket:&lt;/strong&gt; &lt;code&gt;ls -l /tmp/dynamic_socket&lt;/code&gt;. A world-writable socket (srwxrwxrwx) on an unpatched take matches the advisory's description.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;After patching:&lt;/strong&gt; &lt;code&gt;grep "Rejected connection from unauthorized uid" /var/log/dynamicCli.log&lt;/code&gt;. The fix blocks and logs unauthenticated calls, so hits here are worth a closer look.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to fix it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the November Jumbo Hotfix&lt;/strong&gt; on all Management Servers and Security Gateways, starting with gateways that expose Mobile Access. The takes are listed above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you cannot patch PHP2Shell today,&lt;/strong&gt; apply the &lt;code&gt;cp-httpd.conf&lt;/code&gt; mitigation in &lt;a href="https://support.checkpoint.com/results/sk/sk1000302" rel="noopener noreferrer"&gt;sk1000302&lt;/a&gt; and restart the portals. Check Point also offers a standalone PHP2Shell hotfix, which needs a reboot. R82.20 gateways are protected by the IPS signature "PHP Remote Code Execution", even when IPS is not enabled.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you cannot patch CVE-2026-123039 today,&lt;/strong&gt; follow the &lt;a href="https://support.checkpoint.com/results/sk/sk1000362" rel="noopener noreferrer"&gt;sk1000362&lt;/a&gt; mitigation, which stops the &lt;code&gt;dynamic_server&lt;/code&gt; process and keeps it from restarting. This breaks the Dynamic CLI commands listed in sk144112.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retest from the outside.&lt;/strong&gt; Confirm the portals respond as expected and the take is correct on every cluster member, not just the active one.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Detection and hunting
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvrler4mtuw5cqg2uapf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvrler4mtuw5cqg2uapf.png" alt="Check, patch, hunt, retest" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check Point notes that no match does not rule out compromise. On the other hand, any match calls for immediate investigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On the appliance (read-only):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;grep -rl upload_progress_pwn /opt/CPcvpn-*/tmp/ 2&amp;gt;/dev/null&lt;/code&gt; finds PHP session artefacts from the exploit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;find / -xdev \( -name 'zz*.tpl' -o -name '.up.tpl' \) 2&amp;gt;/dev/null&lt;/code&gt; finds the webshell templates.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ls -la /tmp/CPconfigdaemon /tmp/.bd.log /tmp/.p_sock /lib/libcpfwcommon.so 2&amp;gt;/dev/null&lt;/code&gt; checks for backdoor files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ps -ef | grep -i '[C]Pconfigdaemon'&lt;/code&gt; and &lt;code&gt;netstat -tlnp | grep ':11072'&lt;/code&gt; check for the backdoor process and its listener.&lt;/li&gt;
&lt;li&gt;Check SHA-256 hashes of anything suspicious against the IOC table in &lt;a href="https://research.checkpoint.com/2026/php2shell-exploitation-on-check-point-gateway/" rel="noopener noreferrer"&gt;Check Point Research's report&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In your SIEM:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Splunk: &lt;code&gt;index=* ("130.94.19.204" OR "130.94.19.168")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Microsoft Sentinel: &lt;code&gt;CommonSecurityLog | where SourceIP in ("130.94.19.204","130.94.19.168") or DestinationIP in ("130.94.19.204","130.94.19.168")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;SSH logins to ESXi hosts as &lt;code&gt;vpxuser&lt;/code&gt; that come from a gateway address.&lt;/li&gt;
&lt;li&gt;Gaps in firewall traffic logs, especially after the logging daemon crashes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you find a match, treat the gateway as compromised. Bring in Check Point support and your incident-response process, preserve evidence, and rotate the credentials that pass through or are stored on the gateway.&lt;/p&gt;

&lt;h2&gt;
  
  
  Severity, KEV and EPSS
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CVE-2026-123039:&lt;/strong&gt; CVSS 3.1 7.8 (High), assigned by Check Point as the CNA. NVD analysis is pending. Check Point and the Canadian Centre for Cyber Security (AV26-1154) report it as exploited in the wild. It was not yet in CISA's KEV catalog when we checked on 13 November (catalog version 2026.11.10), and FIRST has not yet published an EPSS score for it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP2Shell (CVE-2026-109618 / CVE-2026-109619):&lt;/strong&gt; CVSS 9.2 (Critical). It has been in CISA KEV since 26 October 2026, with a federal due date of 29 October. Its EPSS score on 12 November was 0.99999, the top of the scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What is CVE-2026-123039?&lt;/strong&gt;&lt;br&gt;
It is a local privilege escalation in Check Point Gaia OS. The root &lt;code&gt;dynamic_server&lt;/code&gt; process accepts commands from a world-writable socket without authentication, so anyone already running code on the appliance can become root. CVSS 7.8.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Is CVE-2026-123039 being exploited?&lt;/strong&gt;&lt;br&gt;
Yes. Check Point confirmed exploitation, chained with PHP2Shell, against a couple of dozen unpatched systems between 30 October and 4 November 2026.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which Check Point versions fix it?&lt;/strong&gt;&lt;br&gt;
The November Jumbo Hotfix fixes it: R82.20 Take 14, R82.10 Take 52, R82 Take 132 and R81.20 Take 174 or later. R81 and R81.10 are end of support, so plan an upgrade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My gateway doesn't use Mobile Access. Am I safe?&lt;/strong&gt;&lt;br&gt;
You are less exposed, but not safe. PHP2Shell also reaches gateways through SSL Network Extender, SAML VPN, Captive Portal, UserCheck and Zero Phishing. CVE-2026-123039 also affects management servers. Patch everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I patched. Am I done?&lt;/strong&gt;&lt;br&gt;
Not yet. Patching stops new break-ins. Hunting for the indicators above shows whether someone got in before you patched.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.checkpoint.com/security/security-advisory-active-exploitation-of-php2shell-and-local-privilege-escalation/" rel="noopener noreferrer"&gt;Check Point Blog: Security Advisory: Active Exploitation of PHP2Shell and Local Privilege Escalation (12 Nov 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://research.checkpoint.com/2026/php2shell-exploitation-on-check-point-gateway/" rel="noopener noreferrer"&gt;Check Point Research: PHP2Shell Exploitation on Check Point Gateway (12 Nov 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.checkpoint.com/results/sk/sk1000362" rel="noopener noreferrer"&gt;Check Point sk1000362: CVE-2026-123039&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://support.checkpoint.com/results/sk/sk1000302" rel="noopener noreferrer"&gt;Check Point sk1000302: PHP2Shell response&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2026-123039" rel="noopener noreferrer"&gt;NVD: CVE-2026-123039&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cyber.gc.ca/en/alerts-advisories/check-point-security-advisory-av26-1154" rel="noopener noreferrer"&gt;Canadian Centre for Cyber Security AV26-1154&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.cisa.gov/known-exploited-vulnerabilities-catalog" rel="noopener noreferrer"&gt;CISA Known Exploited Vulnerabilities catalog&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp" rel="noopener noreferrer"&gt;PHP security advisory GHSA-6wmf-4rv5-p3rp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://api.first.org/data/v1/epss?cve=CVE-2026-109619" rel="noopener noreferrer"&gt;FIRST EPSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cve.now/CVE-2026-123039" rel="noopener noreferrer"&gt;cve.now&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  From "patched" to "proven"
&lt;/h2&gt;

&lt;p&gt;Attackers struck about a week after the fix shipped, and they chained two bugs: one at the edge, one on the box. Most teams found out about the second one only this week. That gap is the job.&lt;/p&gt;

&lt;p&gt;This briefing was written by the Rokkhe research team. Rokkhe is an AI pentesting and security-operations workspace. Learn more at &lt;a href="https://rokkhe.com/" rel="noopener noreferrer"&gt;rokkhe.com&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>infosec</category>
      <category>security</category>
      <category>cybersecurity</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
