GCVE-1988-2026-0487

Vulnerability from gna-1988 – Published: 2026-11-02 07:30 – Updated: 2026-11-02 07:30
VLAI
Title
[0day-rubbish] Maian Guardian 2.1 license generator projid command injection to www-data uid 33 code execution (7.2, PR:H)
Summary
0day Rubbish Research Team is publicly disclosing a vulnerability in Maian Guardian 2.1, the self-hosted licensing system for PHP applications encoded with SourceGuardian, distributed as a closed-source commercial PHP script by Maian Script World (United Kingdom) and verified end to end against a real installation of that version. Type: OS command injection (CWE-78 primary; CWE-116, CWE-20 and CWE-561 contributing; CWE-250 and CWE-732 as environmental amplifiers of the same event). The license-generation routine builds an operating-system command line by string interpolation and passes it to shell_exec() at `control/classes/class.licensing.php:261` inside `guardianLicense::create()`, in order to invoke the external SourceGuardian generator binary. Several of the interpolated fields arrive unvalidated from the body of the administrator AJAX operation `POST /admin/index.php?ajax-ops=generate` - projid, projkey, the license property key and value pairs, and the encoder string - and are placed inside double quotes rather than argument-quoted, so POSIX command substitution still applies: shell_exec(mswSGLicGenPath('"', $this->settings) . " $string $output"); $lock[] = '--projid "' . $this->projid . '"'; // line 112 $lock[] = '--projkey "' . $this->projkey . '"'; // line 113 The rendered command line is therefore `"<licpath>" --projid "<PROJID>" --projkey "<PROJKEY>" --const "<KEY>=<VALUE>" ... <output>`, where PROJID is whatever an authenticated administrator posted. PHP hands that text to `/bin/sh -c`, and inside a double-quoted word the shell does not treat `$(`, a backtick or a backslash as literal. A projid of `$(id)` spawns `id` in a subshell and substitutes its output into the argument, and it does so during expansion, before the command word is resolved - which is why a deployment with a missing or misconfigured generator binary is still fully exploitable. The decisive detail is the sanitizer arrangement, and it is the whole root cause. Three sanitizer-shaped helpers ship with the product; two of them return their input unchanged and sit in series on the path to the shell, and the one that correctly wraps escapeshellarg has no call site anywhere in the product: private function escape($data) { return $data; } // lines 68-70 private function escapeArg($data) { return escapeshellarg($data); } // lines 72-74 `escape()` is the function actually called on the way to the shell, and it does nothing. `mswCD()` at lines 152-154 is a second `return $data;`, applied to the property key and value immediately before `escape()`, so two no-ops are stacked in a row on the properties path. `escapeArg()` wraps exactly the right primitive and is dead code: a sweep of all 308 PHP files in the shipped tree found no call site for it, which reads as a completed remediation that never landed. A fourth helper, `escapeCmd()` around `escapeshellcmd()`, is real and is invoked, but only from `head()`, `time_servers()` and `add_lic_info()` - never from `lock()` or `properties()` - so it guards the fields with the least attacker control and never sees the two that carry project identifiers. Upstream of the class the only request-wide filter is `mswMDAM('trim', $_POST)` at `control/functions.php:342`, which applies trim() and nothing else; there is no character allow-list, no length cap and no metacharacter rejection anywhere between the POST body and the shell_exec() argument. Commands execute as the web server service account, observed as `uid=33(www-data) gid=33(www-data) groups=33(www-data)`. No setuid transition, wrapper, privilege drop or sandbox sits between the web worker and the child process. Root was not reached, was not attempted, and is not claimed. How confirmation works, and what was deliberately not reproduced. The generate operation does not echo command output into its HTTP response: it answered HTTP 200 with its ordinary `{"status":"rdr"}` body, the response shape it produces when the generator creates no valid license file. Proof of execution is therefore out-of-band by construction. The injected command writes a marker file and the same installation serves it back over HTTP; one authenticated request executed six distinct commands into that single file, including a pipeline, `cat /etc/passwd | head -3`, which is what separates arbitrary command execution from one hard-coded effect. An earlier iteration of the same chain wrote its marker outside the document root and was confirmed there first, so write capability is stated as observed both inside and outside the web tree. The published advisory reproduces the identity string but elides the container hostname and the kernel build-timestamp field of the `uname -a` line, since both are artefacts of one execution on one host rather than evidence about the product. Scoring. One score is published: 7.2 High, CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H. That vector computes from first principles to ISS = 1 - (0.44 x 0.44 x 0.44) = 0.914816, Impact(S:U) = 6.42 x 0.914816 = 5.87311872, Exploitability = 8.22 x 0.85 x 0.77 x 0.27 x 0.85 = 1.234707705, base 7.107826425, taken to 7.2 by the CVSS ceiling-to-tenth Roundup - a calculator reporting 7.1 is disagreeing with the rounding rule and with nothing else. AV:N describes the product's exposure and not our lab's: the chain is three HTTP requests plus one read-back against the product's single listener, and the loopback binding in our testbed was our own safety choice, not a product property. AC:L because nothing outside the attacker's or a normal deployment's control is needed - ENC_DEBUG is at its shipped value 0 (`control/options.php:37`), which selects the shell_exec() branch, and the license-generator path is a setting a functioning installation already has. PR:H is the metric carrying the weight, and the readings on either side of it are printed with their own vectors so the arithmetic is inspectable. Published reason: only an installer-provisioned administrator session was demonstrated reaching the sink. The product does keep a `guard_staff` table, so a multi-user deployment is possible, but our review found no role or permission column that separates license generation from any other administrator operation and no documented staff role with a narrower capability set; every path we traced to `guardianLicense::create()` sat behind the administrator gate with the same operation-level CSRF check. Three alternative readings are therefore labelled and rejected rather than hidden: - Unauthenticated, AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8. Rejected: no unauthenticated path reaches a sink. The remote license-issuing API at `control/api/api-remote.php` is gated by a key randomly generated at install time, which was not bypassed, and the product ships no factory credential because the installer sets the administrator account. - Lower-privileged operator, AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8. Rejected: no role-separated principal was demonstrated reaching the generate operation, and the guard_staff role model was not established. Publishing this would rest on an unstudied possibility rather than on a demonstrated principal. - Scope Changed, AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H = 9.1. Rejected: the injected command runs as the same identity, on the same host, inside the same authority the product was granted at install time. No second authorization boundary was crossed and no cross-machine or cross-account outcome was demonstrated, so S:C would rate an outcome the evidence does not contain. Two things are deliberately priced at zero in this vector. The remote API key comparison `md5($apiKey) == md5($SETTINGS->apikey)` is reported as a separate hardening weakness - CWE-328 with a CWE-208 aspect, since hashing both sides with an unsalted MD5 adds nothing over comparing the raw key and `==` on two digests is not a constant-time comparison - and it is folded into nothing. It was not demonstrated to be exploitable, the strength of that gate rests on the entropy of the install-time random key, and no leak path for it was found. Likewise the six additional tainted fields on the same sink and the second entry point widen the patch surface a vendor must cover without changing how hard the demonstrated injection is to perform, so they contribute nothing to the score. No correction notice accompanies this figure: our own notes carry no numeric CVSS value for this finding, so 7.2 is derived from the vector above and nothing else. Authentication: post-authentication administrator, and the gate held. Every administrator page and every administrator AJAX operation passes through one init-level gate at `admin/control/init.php:122`, which is a hard include-time gate rather than a per-request filter that can be forgotten on one route, and the generate case carries a second operation-level check that the submitted `cs_rf` body field or `csrf` header matches the session-bound `csrf_token`. Eight candidate bypasses were examined and all eight recorded as blocked: forging the `guardian_access` session value (it embeds the per-install random SECRET_KEY), omitting or replaying the CSRF token (session-bound, validated per operation, rotated per page render), injecting the internal `$loginBypass` variable (no external input reaches it), SQL injection in the login path (the staff lookup is parameterised and the constant path compares salted SHA-1 digests), a timing attack on that comparison (equal-length hex digests, no exploitable divergence identified), a default-credential fallback (the installer forces the operator to set it), hard-coded LG_USER or LG_PASS (written at install time from operator input, not compiled in), and guessing the remote API key (randomly generated at install). The unauthenticated surface was enumerated interface by interface and parameter by parameter: roughly sixty-two interfaces were inventoried by three independent methods that agreed, roughly nine answer without credentials (four static asset trees, the buyer portal front page and its license-lookup operation, the remote API endpoint refusing a keyless request, the post-installation installer route, and the login operation itself), and zero of those nine reach a command-execution, deserialization or file-write sink. Every parameter of every unauthenticated interface was tested for traversal, injection, type confusion, over-long input and encoding bypass. Unauthenticated remote code execution was not reached and none is claimed. One honesty point about the negative control, because the published exploit carries a control mode that was never recorded. The script ships a --control mode that repeats the identical authenticated sequence - same login, same fresh CSRF token, same generate operation, same field set including a non-empty licpath and a key/value pair - with a benign project identifier and project key, points its read-back at a marker name it never injects, and asserts that the marker URL does not answer. A passing control would rule out that the proof file pre-existed, that the generate operation writes arbitrary attacker-named files into the document root as a normal side effect, and that the read-back URL is served by something other than the written file. What there is not, and what this post does not pretend otherwise, is an archived control transcript: the recorded verification output is the injection leg, the control leg is shipped so that any reader can execute it against their own instance, and inventing a transcript would be worse than saying so. The structural half of the control does come from source: the only thing on this path that creates a file inside the document root is the shell command assembled at `class.licensing.php:261`, and no branch of the generate operation writes to the content tree on its own. Impact: arbitrary OS
Severity
No CVSS data available.
Assigner
VULNARCHIVE GNA GNA-1988
GNA scorecard E 38/100 over 576 records in the last 180 days details
Impacted products
Vendor Product Version CPE status
Maian Guardian Affected: unknown
guessed Create a notification for this product.

{
  "containers": {
    "cna": {
      "affected": [
        {
          "product": "Guardian",
          "vendor": "Maian",
          "versions": [
            {
              "status": "affected",
              "version": "unknown"
            }
          ]
        }
      ],
      "credits": [
        {
          "lang": "en",
          "type": "finder",
          "value": "disclosure via Fulldisclosure"
        }
      ],
      "descriptions": [
        {
          "lang": "en",
          "value": "0day Rubbish Research Team is publicly disclosing a vulnerability in Maian\nGuardian 2.1, the self-hosted licensing system for PHP applications encoded\nwith SourceGuardian, distributed as a closed-source commercial PHP script by\nMaian Script World (United Kingdom) and verified end to end against a real\ninstallation of that version.\n\nType: OS command injection (CWE-78 primary; CWE-116, CWE-20 and CWE-561\ncontributing; CWE-250 and CWE-732 as environmental amplifiers of the same\nevent). The license-generation routine builds an operating-system command line\nby string interpolation and passes it to shell_exec() at\n`control/classes/class.licensing.php:261` inside `guardianLicense::create()`, in\norder to invoke the external SourceGuardian generator binary. Several of the\ninterpolated fields arrive unvalidated from the body of the administrator AJAX\noperation `POST /admin/index.php?ajax-ops=generate` - projid, projkey, the\nlicense property key and value pairs, and the encoder string - and are placed\ninside double quotes rather than argument-quoted, so POSIX command substitution\nstill applies:\n\n    shell_exec(mswSGLicGenPath(\u0027\"\u0027, $this-\u003esettings) . \" $string $output\");\n    $lock[] = \u0027--projid \"\u0027  . $this-\u003eprojid  . \u0027\"\u0027;   // line 112\n    $lock[] = \u0027--projkey \"\u0027 . $this-\u003eprojkey . \u0027\"\u0027;   // line 113\n\nThe rendered command line is therefore\n`\"\u003clicpath\u003e\" --projid \"\u003cPROJID\u003e\" --projkey \"\u003cPROJKEY\u003e\" --const \"\u003cKEY\u003e=\u003cVALUE\u003e\"\n... \u003coutput\u003e`, where PROJID is whatever an authenticated administrator posted.\nPHP hands that text to `/bin/sh -c`, and inside a double-quoted word the shell\ndoes not treat `$(`, a backtick or a backslash as literal. A projid of `$(id)`\nspawns `id` in a subshell and substitutes its output into the argument, and it\ndoes so during expansion, before the command word is resolved - which is why a\ndeployment with a missing or misconfigured generator binary is still fully\nexploitable.\n\nThe decisive detail is the sanitizer arrangement, and it is the whole root\ncause. Three sanitizer-shaped helpers ship with the product; two of them return\ntheir input unchanged and sit in series on the path to the shell, and the one\nthat correctly wraps escapeshellarg has no call site anywhere in the product:\n\n    private function escape($data) {     return $data; }   // lines 68-70\n    private function escapeArg($data) { return escapeshellarg($data); }\n                                                           // lines 72-74\n\n`escape()` is the function actually called on the way to the shell, and it does\nnothing. `mswCD()` at lines 152-154 is a second `return $data;`, applied to the\nproperty key and value immediately before `escape()`, so two no-ops are stacked\nin a row on the properties path. `escapeArg()` wraps exactly the right primitive\nand is dead code: a sweep of all 308 PHP files in the shipped tree found no call\nsite for it, which reads as a completed remediation that never landed. A fourth\nhelper, `escapeCmd()` around `escapeshellcmd()`, is real and is invoked, but only\nfrom `head()`, `time_servers()` and `add_lic_info()` - never from `lock()` or\n`properties()` - so it guards the fields with the least attacker control and\nnever sees the two that carry project identifiers. Upstream of the class the\nonly request-wide filter is `mswMDAM(\u0027trim\u0027, $_POST)` at\n`control/functions.php:342`, which applies trim() and nothing else; there is no\ncharacter allow-list, no length cap and no metacharacter rejection anywhere\nbetween the POST body and the shell_exec() argument.\n\nCommands execute as the web server service account, observed as\n`uid=33(www-data) gid=33(www-data) groups=33(www-data)`. No setuid transition,\nwrapper, privilege drop or sandbox sits between the web worker and the child\nprocess. Root was not reached, was not attempted, and is not claimed.\n\nHow confirmation works, and what was deliberately not reproduced. The generate\noperation does not echo command output into its HTTP response: it answered HTTP\n200 with its ordinary `{\"status\":\"rdr\"}` body, the response shape it produces\nwhen the generator creates no valid license file. Proof of execution is\ntherefore out-of-band by construction. The injected command writes a marker file\nand the same installation serves it back over HTTP; one authenticated request\nexecuted six distinct commands into that single file, including a pipeline,\n`cat /etc/passwd | head -3`, which is what separates arbitrary command execution\nfrom one hard-coded effect. An earlier iteration of the same chain wrote its\nmarker outside the document root and was confirmed there first, so write\ncapability is stated as observed both inside and outside the web tree. The\npublished advisory reproduces the identity string but elides the container\nhostname and the kernel build-timestamp field of the `uname -a` line, since both\nare artefacts of one execution on one host rather than evidence about the\nproduct.\n\nScoring. One score is published: 7.2 High,\nCVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H. That vector computes from first\nprinciples to ISS = 1 - (0.44 x 0.44 x 0.44) = 0.914816, Impact(S:U) =\n6.42 x 0.914816 = 5.87311872, Exploitability = 8.22 x 0.85 x 0.77 x 0.27 x 0.85\n= 1.234707705, base 7.107826425, taken to 7.2 by the CVSS ceiling-to-tenth\nRoundup - a calculator reporting 7.1 is disagreeing with the rounding rule and\nwith nothing else. AV:N describes the product\u0027s exposure and not our lab\u0027s: the\nchain is three HTTP requests plus one read-back against the product\u0027s single\nlistener, and the loopback binding in our testbed was our own safety choice, not\na product property. AC:L because nothing outside the attacker\u0027s or a normal\ndeployment\u0027s control is needed - ENC_DEBUG is at its shipped value 0\n(`control/options.php:37`), which selects the shell_exec() branch, and the\nlicense-generator path is a setting a functioning installation already has.\n\nPR:H is the metric carrying the weight, and the readings on either side of it\nare printed with their own vectors so the arithmetic is inspectable. Published\nreason: only an installer-provisioned administrator session was demonstrated\nreaching the sink. The product does keep a `guard_staff` table, so a multi-user\ndeployment is possible, but our review found no role or permission column that\nseparates license generation from any other administrator operation and no\ndocumented staff role with a narrower capability set; every path we traced to\n`guardianLicense::create()` sat behind the administrator gate with the same\noperation-level CSRF check. Three alternative readings are therefore labelled and\nrejected rather than hidden:\n\n  - Unauthenticated, AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H = 9.8. Rejected: no\n      unauthenticated path reaches a sink. The remote license-issuing API at\n      `control/api/api-remote.php` is gated by a key randomly generated at\n      install time, which was not bypassed, and the product ships no factory\n      credential because the installer sets the administrator account.\n  - Lower-privileged operator, AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H = 8.8.\n      Rejected: no role-separated principal was demonstrated reaching the\n      generate operation, and the guard_staff role model was not established.\n      Publishing this would rest on an unstudied possibility rather than on a\n      demonstrated principal.\n  - Scope Changed, AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H = 9.1. Rejected: the\n      injected command runs as the same identity, on the same host, inside the\n      same authority the product was granted at install time. No second\n      authorization boundary was crossed and no cross-machine or cross-account\n      outcome was demonstrated, so S:C would rate an outcome the evidence does\n      not contain.\n\nTwo things are deliberately priced at zero in this vector. The remote API key\ncomparison `md5($apiKey) == md5($SETTINGS-\u003eapikey)` is reported as a separate\nhardening weakness - CWE-328 with a CWE-208 aspect, since hashing both sides with\nan unsalted MD5 adds nothing over comparing the raw key and `==` on two digests\nis not a constant-time comparison - and it is folded into nothing. It was not\ndemonstrated to be exploitable, the strength of that gate rests on the entropy\nof the install-time random key, and no leak path for it was found. Likewise the\nsix additional tainted fields on the same sink and the second entry point widen\nthe patch surface a vendor must cover without changing how hard the demonstrated\ninjection is to perform, so they contribute nothing to the score. No correction\nnotice accompanies this figure: our own notes carry no numeric CVSS value for\nthis finding, so 7.2 is derived from the vector above and nothing else.\n\nAuthentication: post-authentication administrator, and the gate held. Every\nadministrator page and every administrator AJAX operation passes through one\ninit-level gate at `admin/control/init.php:122`, which is a hard include-time\ngate rather than a per-request filter that can be forgotten on one route, and the\ngenerate case carries a second operation-level check that the submitted `cs_rf`\nbody field or `csrf` header matches the session-bound `csrf_token`. Eight\ncandidate bypasses were examined and all eight recorded as blocked: forging the\n`guardian_access` session value (it embeds the per-install random SECRET_KEY),\nomitting or replaying the CSRF token (session-bound, validated per operation,\nrotated per page render), injecting the internal `$loginBypass` variable (no\nexternal input reaches it), SQL injection in the login path (the staff lookup is\nparameterised and the constant path compares salted SHA-1 digests), a timing\nattack on that comparison (equal-length hex digests, no exploitable divergence\nidentified), a default-credential fallback (the installer forces the operator to\nset it), hard-coded LG_USER or LG_PASS (written at install time from operator\ninput, not compiled in), and guessing the remote API key (randomly generated at\ninstall). The unauthenticated surface was enumerated interface by interface and\nparameter by parameter: roughly sixty-two interfaces were inventoried by three\nindependent methods that agreed, roughly nine answer without credentials (four\nstatic asset trees, the buyer portal front page and its license-lookup\noperation, the remote API endpoint refusing a keyless request, the\npost-installation installer route, and the login operation itself), and zero of\nthose nine reach a command-execution, deserialization or file-write sink.\nEvery parameter of every unauthenticated interface was tested for traversal,\ninjection, type confusion, over-long input and encoding bypass. Unauthenticated\nremote code execution was not reached and none is claimed.\n\nOne honesty point about the negative control, because the published exploit\ncarries a control mode that was never recorded. The script ships a --control\nmode that repeats the identical authenticated sequence - same login, same fresh\nCSRF token, same generate operation, same field set including a non-empty\nlicpath and a key/value pair - with a benign project identifier and project key,\npoints its read-back at a marker name it never injects, and asserts that the\nmarker URL does not answer. A passing control would rule out that the proof file\npre-existed, that the generate operation writes arbitrary attacker-named files\ninto the document root as a normal side effect, and that the read-back URL is\nserved by something other than the written file. What there is not, and what\nthis post does not pretend otherwise, is an archived control transcript: the\nrecorded verification output is the injection leg, the control leg is shipped so\nthat any reader can execute it against their own instance, and inventing a\ntranscript would be worse than saying so. The structural half of the control\ndoes come from source: the only thing on this path that creates a file inside\nthe document root is the shell command assembled at `class.licensing.php:261`,\nand no branch of the generate operation writes to the content tree on its own.\n\nImpact: arbitrary OS "
        }
      ],
      "problemTypes": [
        {
          "descriptions": [
            {
              "cweId": "CWE-116",
              "description": "CWE-116",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-20",
              "description": "CWE-20",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-208",
              "description": "CWE-208",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-250",
              "description": "CWE-250",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-328",
              "description": "CWE-328",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-561",
              "description": "CWE-561",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-732",
              "description": "CWE-732",
              "lang": "en",
              "type": "CWE"
            },
            {
              "cweId": "CWE-78",
              "description": "CWE-78",
              "lang": "en",
              "type": "CWE"
            }
          ]
        }
      ],
      "providerMetadata": {
        "dateUpdated": "2026-11-02T07:30:02Z",
        "orgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
        "shortName": "VULNARCHIVE"
      },
      "references": [
        {
          "tags": [
            "technical-description",
            "exploit"
          ],
          "url": "https://vuln.freearchive.org/archive/full-disclosure/2026/Oct/16"
        },
        {
          "tags": [
            "technical-description"
          ],
          "url": "https://seclists.org/fulldisclosure/2026/Oct/16"
        },
        {
          "url": "https://0day-rubbish.com"
        },
        {
          "url": "https://0day-rubbish.com/blog/maian-guardian-license-generator-command-injection"
        },
        {
          "url": "https://github.com/Exploit-Garbage/0day-Rubbish"
        },
        {
          "url": "https://nmap.org/mailman/listinfo/fulldisclosure"
        },
        {
          "url": "https://seclists.org/fulldisclosure/"
        }
      ],
      "source": {
        "defect": [
          "https://seclists.org/fulldisclosure/2026/Oct/16"
        ],
        "discovery": "EXTERNAL"
      },
      "title": "[0day-rubbish] Maian Guardian 2.1 license generator projid command injection to www-data uid 33 code execution (7.2, PR:H)",
      "x_gcve": [
        {
          "recordType": "advisory",
          "relationships": [],
          "vulnId": "GCVE-1988-2026-0487",
          "x_vulnarchive": {
            "archiveUrl": "https://vuln.freearchive.org/archive/full-disclosure/2026/Oct/16",
            "automated": true,
            "contentSha256": "c6f42931cdf5a35451312d596be78de698bb1c4dd482ac4faf3c5de81c35119f",
            "evidenceScore": 11,
            "messageId": "",
            "originalUrl": "https://seclists.org/fulldisclosure/2026/Oct/16",
            "policy": "vulnarchive-1",
            "sourceFormat": "text/html",
            "sourcePublishedAt": "2026-10-13T02:21:47Z"
          }
        }
      ]
    }
  },
  "cveMetadata": {
    "assignerOrgId": "4e2abfbf-4a2a-4b76-a4e0-d77c18ba156c",
    "assignerShortName": "VULNARCHIVE",
    "datePublished": "2026-11-02T07:30:02Z",
    "dateUpdated": "2026-11-02T07:30:02Z",
    "state": "PUBLISHED",
    "vulnId": "GCVE-1988-2026-0487"
  },
  "dataType": "CVE_RECORD",
  "dataVersion": "5.2"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline

Sightings and recorded dates, grouped by day · UTC

JSON
How durations are calculated

Following the VTEM framework (Black Hat USA 2015), time to patch starts when the vendor or a coordinator was informed, total time to patch at discovery, and disclosure to patch at public disclosure; vendor response time runs from the report to its acknowledgement. Time to exploit and time unpatched start at the earliest discovery or report, falling back to public disclosure. Unpatched time ends at the first recorded patch, or the calculation time when no patch is recorded. Exploitation to patch shows the interval between those two events; a published proof of concept is a public exploit, not exploitation.

Each result shows its source dates. A negative interval means the end event came first; when date-only inputs leave the order open, the result says so. Date-only inputs are approximate. The earliest evidence wins: KEV report dates and sightings are labelled as such. Records describing another vulnerability are listed but never calculated from, and a PoC alone does not establish exploitation. Missing patch evidence does not prove that no patch exists. Filters below change the event list only.

Events included in the timeline

Loading timeline…


    Loading…

    Loading…

    Loading…

    Related by attack behaviour

    Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.