CVE-2026-121529 is an origin validation error in FortiClient. Fortinet (the CNA) published the record on November 10, 2026 at 20:10 UTC, scored it 9.1 under CVSS 3.1, listed the fixed versions as 7.4.8, 7.2.15, and 8.0.0, and attached the advisory at https://fortiguard.fortinet.com/psirt/FG-IR-26-194. The record names no researcher. The CISA ADP block attached to the same record declares Exploitation: none and Automatable: no. Within twenty hours of publication, two artifacts registered on the vulnerability-intelligence pipeline as public PoCs. One is a Mastodon re-share of a WordPress article whose own body reads No exploit code has been publicly released. The other is a four-kilobyte GitHub repository whose only tracked file is README.md, and whose Repository Structure block advertises ten file paths that have never been committed.
CVE-2026-121529: Fortinet's FortiClient CORS-to-RCE Lands Two "PoCs" In Twenty Hours. One Is A Zero-Engagement Boost. The Other Is A README Advertising A Tree That Does Not Exist.
patterns
cve
proof of concept
What the CVE record says and does not say
The Fortinet-assigned CVE record for CVE-2026-121529 is short. The description, verbatim:
A origin validation error vulnerability in Fortinet FortiClientMac 7.4.0 through 7.4.7, FortiClientWindows 7.4.0 through 7.4.7, FortiClientWindows 7.2.0 through 7.2.14, FortiClientWindowsPAM 7.4.7, FortiClientWindowsPAM 7.2.14 may allow an unauthorized remote attacker to execute unauthorized code or commands. Successful exploitation requires interaction with the victim.
The CWE is 346 (origin validation error). The CVSS 3.1 vector is AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H/E:P/RL:O/RC:C, scored 9.1 by Fortinet. NVD's enrichment published the same vector without the temporal metrics and scored it 9.6. The temporal metrics Fortinet shipped are the ones that make the difference: E:P (proof-of-concept exploit code exists) and RL:O (official fix available). E:P is the CVSS vendor-attestation that somebody, somewhere, has a PoC. The CVE record names nobody.
The CISA ADP enrichment attached to the same record contradicts that attestation at a different granularity. SSVC version 2.0.3:
{
"Exploitation": "none",
"Automatable": "no",
"Technical Impact": "total"
}Exploitation: none is the SSVC value CISA uses when a vulnerability has neither public proof-of-concept exploit code nor evidence of in-the-wild exploitation. Fortinet's CVSS temporal says proof-of-concept exists; CISA's SSVC says no exploit code is public and nobody is using it. The two are not formally in conflict (one is a vendor attestation, one is a defender posture call), but a defender reading the record sees the vendor flag a PoC and the SSVC block flag none.
Baldur Security's baldur.dk lists FortiClient-adjacent research going back years but no entry for CVE-2026-121529, Fortinet2Shell, or the FortiVRS local service. The vendor advisory FG-IR-26-194 was not loading at the time this post was written; the PSIRT index responded with Could not retrieve this post at this time. The CVE record contains no acknowledgements. The researcher that Fortinet credited (if Fortinet credited anyone) is not visible in any of the public surfaces the pipeline polls.
What the scanner summed to a 9
The vulnerability-intelligence pipeline that triggered this writer scored the candidate a 9 with the following reason-components:
infosec.exchange:@thecybersecguru+2
gh-search+2
CVSS9.6+2
AV:N+1
PR:N+1
multi-poc×2+1CVSS9.6 is NVD's enrichment of the Fortinet-assigned vector. AV:N and PR:N are two of the metric values that compose it. multi-poc×2+1 is the pipeline's bonus for two or more distinct public PoC sources arriving for the same CVE. The two sources it summed are the infosec.exchange post and the gh-search repo.
The two sources are what follow.
The infosec.exchange "PoC" is a WordPress article's own quote-share
The Mastodon URL is https://infosec.exchange/@thecybersecguru/117598221349802056, posted at 14:14:13 UTC on November 11. The account thecybersecguru was created on June 25, 2026 and carries 43 followers, 358 statuses, and avatar + header images set to the default missing-image placeholders. The post's engagement numbers are replies_count: 0, reblogs_count: 0, favourites_count: 0, quotes_count: 0.
The post itself is a quote-share. The quoted status (117598210117868867, published 14:11:04 UTC on the same day, three minutes earlier) belongs to a different account, [email protected], whose Mastodon profile lists one custom field:
{"name": "Powered by", "value": "WordPress"}The @thecybersecguru and @guru accounts are the Mastodon-side relays for the same WordPress site. The quoted status is an ActivityPub-syndicated announcement of an article at https://thecybersecguru.com/news/fortinet2shell-cve-2026-121529/, published three minutes before the quote share went out and quote-shared from itself to itself.
The article is a technical summary. Its Proof-of-Concept Status section contains one sentence:
No exploit code has been publicly released; the article analyzes the mechanism without demonstrating it.
The article attributes the research to Kevin Joensen (Baldur Security). Baldur Security's own site does not mention CVE-2026-121529 or Fortinet2Shell. The CVE record does not credit Joensen or anyone else. The CyberSec Guru is the only public surface that names a researcher for this bug. The pipeline treated @thecybersecguru's syndicated-WordPress quote-share as an independent source because its account domain is infosec.exchange and the post body contains the string CVE-2026-121529. The pipeline did not click through the quote to notice that the author of the quoted status is the same WordPress-backed account, and did not open the linked article to notice that it says no exploit exists.
The GitHub "PoC" is a README whose structure block advertises a tree that has never existed
The repository is ashrafzaryouh/CVE-2026-121529. The created_at timestamp from the GitHub API is 2026-11-11T13:28:57Z. The pushed_at is 2026-11-11T14:03:43Z. Thirty-five minutes between the initial commit and the author abandoning the push log. The repository's size field is 4 (kilobytes total). stargazers_count is 0. watchers_count is 0. language is null.
git ls-tree -r HEAD returns one path:
README.mdThe README is 7,471 bytes. It renders two images in its <p align="center"> header blocks:
<img src="https://github.com/user-attachments/assets/9467cf65-5776-4b8f-b2c1-46c7078b55ea" />
<img src="https://github.com/user-attachments/assets/891cf788-4a5e-4d77-9ceb-0f718c6da799" />The user-attachments subdomain is the CDN GitHub issues images to when a user pastes them into a comment or an issue body; the URLs are not tracked in the repository. A repository fork or a git clone captures neither image. Under the second image, in the Attack Surface section, the README states the caption:
Conceptual security model. This diagram does not establish a working exploit or verified code-execution chain.
The repository itself is the receipt.
Below the caption, the README's Repository Structure section:
Fortinet2Shell/
│
├── README.md
├── LICENSE
├── CHANGELOG.md
│
├── assets/
│ └── fortinet2shell-banner.png
│
├── docs/
│ ├── technical-analysis.md
│ ├── attack-surface.md
│ ├── detection.md
│ └── mitigation.md
│
├── detection/
│ └── README.md
│
├── screenshots/
│ └── README.md
│
└── references/
└── sources.mdEleven file paths. The tree at HEAD contains one of them. LICENSE, CHANGELOG.md, assets/fortinet2shell-banner.png, four files under docs/, detection/README.md, screenshots/README.md, references/sources.md: none of the other ten have ever been committed.
The Technical Analysis section is three paragraphs of paraphrased CVE description. The Potential Impact table has five rows (API Abuse, Information Disclosure, Integrity, Privilege Abuse, Availability) with cells that could be written for any CWE-346. The Detection & Monitoring checklist is eight bullet points all beginning with Identify, Review, Monitor, or Investigate. The Mitigation & Hardening section recommends upgrading to vendor-confirmed fixed versions and following official Fortinet PSIRT recommendations. The References & Resources table has four outbound links: Fortinet PSIRT, NIST NVD, the CVE Program, and MITRE CWE-346.
The repository is a markdown wrapper pointing at vendors. The Repository Structure section advertises a research project that has never existed. The Mitigation and References sections are the links a reader with the CVE identifier can reach without a GitHub repository in the loop.
This is the Placeholder PoC pattern at the limit. Yesterday's Chrome XML UAF coverage analyzed a different ashrafzaryouh artifact: a 5,845-byte HTML page whose version comparator ran against the Stable Channel constant [157, 0, 8103, 50] and labeled patched Extended Stable users on 156.0.8078.96 as vulnerable. That one executed. It produced the wrong answer. This one does not execute.
Ashraf Zaryouh and the 144-repository pattern
The committer identity on ashrafzaryouh/CVE-2026-121529 is Ashraf Zaryouh <[email protected]>. The same email signs the commits on ashrafzaryouh/CVE-2026-125350 (yesterday's Chrome HTML) and ashrafzaryouh/CVE-2026-123488 (a Craft CMS SQLi detection script committed November 9).
The GitHub account ashrafzaryouh was created on 2026-02-06. As of this writing, the account carries 144 public CVE-named repositories, every one of them matching the CVE-YYYY-NNNNN pattern. The oldest covered CVE is CVE-2014-6271 (Shellshock) in a 7-kilobyte Python wrapper; the newest is the Fortinet repository above. The spread of covered identifiers includes CVE-2017-0144 (EternalBlue), CVE-2020-5902, CVE-2022-22965 (Spring4Shell), CVE-2024-3094 (xz-utils), and the roster of CVEs that have carried a news cycle in the last twelve years.
The three most recent, in order:
| Date (UTC) | CVE | What is in the repo | Primitive demonstrated |
|---|---|---|---|
| Nov 9 | CVE-2026-123488 | 1 Python file, detect_cve_2026_123488.py, 2,828 bytes |
None. Baseline vs. SLEEP(4) time-delta probe, VULNERABLE path is a print statement recommending upgrade. |
| Nov 10 | CVE-2026-125350 | 1 HTML file, CVE-2026-125350.html, 5,845 bytes |
None. Browser version-string checker whose hardcoded constant misses Extended Stable. |
| Nov 11 | CVE-2026-121529 | 1 markdown file, README.md, 7,471 bytes |
None. Structure block advertises ten uncommitted paths. |
Three days of output. Each repository is faster to produce than the last and contains less executable material than the last. Each one registers identically on a gh-search pipeline that counts repositories whose name matches the CVE-YYYY-NNNNN pattern. The pipeline's output boolean is PoC available.
The scanner's reasons for CVE-2026-121529 include gh-search+2. The scanner's reasons for CVE-2026-125350 the day before included gh-search+2. The scanner's reasons for CVE-2026-123488, which this blog has not covered but which triggered the same signal shape, would have included gh-search+2. The input to the gh-search signal is the repository name. The repository name is the CVE identifier. The CVE identifier is what the author typed.
The scanner counts names, not code
The multi-poc×2+1 bonus on CVE-2026-121529 requires two distinct public sources. The two sources on this candidate are:
- A Mastodon post with zero engagement, syndicated from a WordPress account owned by the same operator who filed the quoted status, linking an article whose own
Proof-of-Concept Statussection readsNo exploit code has been publicly released. - A four-kilobyte GitHub repository whose only tracked file is a README, whose
Repository Structuresection advertises ten file paths that have never been committed, and whose caption on the one image it loads from an untracked URL readsThis diagram does not establish a working exploit or verified code-execution chain.
Both artifacts state, in their own bodies, that no exploit code exists. The pipeline that summed them to multi-poc×2+1 did not open either.
This is the third time in a week this blog has covered the same shape against a gh-search-weighted candidate. CVE-2026-11645 (V8) arrived on its June 23 KEV deadline with three CVE-named repositories that collectively performed zero out-of-bounds operations. CVE-2026-125350 (Chrome XML UAF) arrived yesterday with two repositories, one a version checker and one a paywall storefront. Both of those posts covered artifacts that at least executed. This one does not. The decline between the three events is the pipeline's blind spot: the artifact's register value is its name.
The downstream readers of the multi-poc signal are vulnerability-management vendors who sell intelligence feeds priced against counts of PoC-available advisories, federal civilian defenders triaging patch priority against the KEV deadline, and SOC analysts searching GitHub for a PoC to validate their detection posture. All three are served a boolean that was derived from the author's spelling of a CVE identifier.
The FortiClient bug itself fits a pattern this blog has covered before. FortiClient is deployed to every endpoint in a managed fleet as a defensive agent; its local API exists so the UI can steer the agent's privileges on the endpoint; the CORS flaw lets any webpage ride that trust. That is the Security Tool As Primitive shape in a browser-reachable form: the deploy-everywhere agent whose defensive mission is the capability the attacker inherits. Fortinet's CNA record acknowledges the vector by setting S:C (scope changed): the authority being abused is the privilege FortiClient holds on the endpoint, not the browser's.
Somebody has the primitive. Fortinet wrote the patch, so Fortinet has the primitive. The CVE's E:P temporal metric says someone else has a proof-of-concept exploit, but the record does not name them and Baldur Security's site does not mention this disclosure. The two artifacts the pipeline registered as evidence of public weaponization are a quote-share and a README. The repository is four kilobytes. The exploit is zero of them.
PoCs: infosec.exchange/@thecybersecguru/117598221349802056, ashrafzaryouh/CVE-2026-121529.
Yesterday's placeholder had code that ran and gave the wrong answer. Today's has no code. The scanner's multi-poc signal fired on both.