{"uuid": "21449826-50fa-490e-91e3-8bc524decc4e", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2026-63093", "type": "seen", "source": "https://gist.github.com/AfvanJaffer/8b427775455df5679cc487a0f92dc1b9", "content": "#!/bin/bash\n#\n# Checks whether this machine shows any artifact of having been targeted\n# by the disclosed Cursor IDE CVEs discussed in this investigation:\n#\n#   CVE-2025-54136  \"MCPoison\"   -- MCP config trust bypass\n#   CVE-2026-26268                -- sandbox escape via .git/hooks\n#   CVE-2026-22708                -- shell environment poisoning (prompt injection)\n#   CVE-2026-50548 / CVE-2026-50549  \"DuneSlide\" -- sandbox escape via prompt injection\n#   CVE-2026-63093                -- Windows-only git.exe binary planting (N/A on macOS,\n#                                     checked anyway as a trivial negative-confirmation)\n#\n# IMPORTANT, read before trusting the output:\n#   - CVE-2026-26268 (git hooks) and CVE-2025-54136 (MCP config history) leave\n#     STATIC, verifiable artifacts on disk -- those checks are strong evidence.\n#   - CVE-2026-22708 and DuneSlide are prompt-injection-driven behavioral\n#     exploits with no documented static signature. Those checks are\n#     BEST-EFFORT / LOW-CONFIDENCE: they look for exec-event and local-storage\n#     traces, but absence of a hit here does NOT prove the machine was never\n#     hit, only that no evidence of it was found by these particular queries.\n#   - All captured text is masked before being written to disk (see\n#     mask_secrets()).\n#   - Read-only / non-destructive.\n#\n# Usage:\n#   chmod 700 ./cursor_cve_exposure_check.sh\n#   sudo /bin/bash ./cursor_cve_exposure_check.sh [lookback_days] [trusted_org_pattern]\n#\n#   lookback_days defaults to 60. trusted_org_pattern defaults to \"fastfishio\"\n#   (case-insensitive substring match against git remote URLs) -- change it\n#   if your actual GitHub org differs; anything NOT matching is flagged for\n#   manual review, not assumed malicious.\n\nset -u\numask 077\n\nif [[ \"$EUID\" -ne 0 ]]; then\n    echo \"ERROR: Run using sudo (sudo /bin/bash ./cursor_cve_exposure_check.sh).\"\n    exit 1\nfi\n\nLOOKBACK_DAYS=\"${1:-60}\"\nTRUSTED_ORG=\"${2:-fastfishio}\"\n\nHOSTNAME_NOW=\"$(/usr/sbin/scutil --get ComputerName 2&gt;/dev/null || /bin/hostname)\"\nSAFE_HOST=\"$(printf '%s' \"$HOSTNAME_NOW\" | /usr/bin/tr ' /:' '___' | /usr/bin/tr -cd '[:alnum:]_.-')\"\nSTARTSTAMP=\"$(/bin/date '+%Y%m%d_%H%M%S')\"\n\nOUT=\"$(/bin/pwd -P)/cursor_cve_check_${SAFE_HOST}_${STARTSTAMP}\"\n\n/bin/mkdir -p \\\n    \"$OUT/00_SUMMARY\" \\\n    \"$OUT/01_VERSION_CHECK\" \\\n    \"$OUT/02_GIT_HOOKS_CVE-2026-26268\" \\\n    \"$OUT/03_MCP_HISTORY_CVE-2025-54136\" \\\n    \"$OUT/04_BEHAVIORAL_CVE-2026-22708_DuneSlide\" \\\n    \"$OUT/05_WINDOWS_ONLY_CVE-2026-63093\" \\\n    \"$OUT/06_UNTRUSTED_REPO_REMOTES\"\n\ntimestamp() { /bin/date '+%Y-%m-%d %H:%M:%S %z'; }\nlog() { echo \"[$(timestamp)] $*\"; }\n\nmask_secrets()\n{\n    /usr/bin/sed -E \\\n        -e 's/(gh[pousr]_)[A-Za-z0-9]{16,}/\\1***REDACTED***/g' \\\n        -e 's/(github_pat_)[A-Za-z0-9_]{16,}/\\1***REDACTED***/g' \\\n        -e 's#(https?://)[^/@[:space:]]+:[^/@[:space:]]+@#\\1***REDACTED-BASICAUTH***@#g' \\\n        -e 's/([Oo][Aa][Uu][Tt][Hh][_-]?[Tt][Oo][Kk][Ee][Nn][[:space:]]*[:=][[:space:]]*)[^[:space:]\"'\"'\"']+/\\1***REDACTED***/g' \\\n        -e 's/(\"?[Aa][Pp][Ii][_-]?[Kk][Ee][Yy]\"?[[:space:]]*[:=][[:space:]]*)[^[:space:],}]+/\\1***REDACTED***/g' \\\n        -e 's/([Pp]assword[[:space:]]*[:=][[:space:]]*)[^[:space:]\"'\"'\"']+/\\1***REDACTED***/g'\n}\n\ncapture_shell()\n{\n    local label=\"$1\" outfile=\"$2\" cmd=\"$3\"\n    { echo \"### $label\"; echo \"### shell: $cmd\"; echo \"### captured: $(timestamp)\"; echo; /bin/bash -c \"$cmd\" 2&gt;&amp;1; } \\\n        | mask_secrets &gt; \"$outfile\"\n}\n\n###############################################################################\n# 01 - VERSION CHECK (informational -- cross-reference against known fix table)\n###############################################################################\n\nlog \"Checking installed Cursor version(s)...\"\n\n{\n    echo \"# Known fix versions for the CVEs this script checks for:\"\n    echo \"#   CVE-2025-54136 (MCPoison)            fixed in 1.3\"\n    echo \"#   CVE-2026-26268  (git hooks escape)     fixed in 2.5\"\n    echo \"#   CVE-2026-22708  (shell env poisoning)  fixed in 2.3\"\n    echo \"#   CVE-2026-50548/-50549 (DuneSlide)      fixed in 3.0\"\n    echo \"#   CVE-2026-63093  (Windows git.exe)      Windows-only, N/A here\"\n    echo \"# A version at/above the fix version means the CURRENT binary is\"\n    echo \"# not exploitable via that bug -- it says nothing about whether\"\n    echo \"# an attack happened while running an OLDER, vulnerable version\"\n    echo \"# before an update was applied. That's what the checks below are for.\"\n    echo\n    if [[ -f \"/Applications/Cursor.app/Contents/Resources/app/product.json\" ]]; then\n        /usr/bin/python3 -c \"\nimport json\nd = json.load(open('/Applications/Cursor.app/Contents/Resources/app/product.json'))\nprint('Installed version:', d.get('version'))\n\"\n    else\n        echo \"Cursor.app not found at the standard path.\"\n    fi\n} &gt; \"$OUT/01_VERSION_CHECK/version.txt\"\n\n###############################################################################\n# 02 - CVE-2026-26268: malicious .git/hooks (strong evidence if present)\n###############################################################################\n\nlog \"Scanning every git repo for non-default, active git hooks...\"\n\nHOOKS_OUT=\"$OUT/02_GIT_HOOKS_CVE-2026-26268/findings.txt\"\n{\n    echo \"# Git ships hook files ending in .sample which are inactive by\"\n    echo \"# default. Any file here WITHOUT .sample is an ACTIVE hook that\"\n    echo \"# git will execute automatically at the relevant lifecycle point\"\n    echo \"# (pre-commit, post-checkout, post-merge, pre-push, etc).\"\n    echo \"# Custom hooks are rare in normal repos -- treat every hit below\"\n    echo \"# as worth reading in full.\"\n    echo\n} &gt; \"$HOOKS_OUT\"\n\nwhile IFS= read -r -d '' HOOKDIR; do\n    for HOOK in \"$HOOKDIR\"/*; do\n        [[ -f \"$HOOK\" ]] || continue\n        [[ \"$HOOK\" == *.sample ]] &amp;&amp; continue\n        {\n            echo \"==== ACTIVE HOOK: $HOOK ====\"\n            /usr/bin/stat -f '  modified: %Sm   accessed: %Sa   executable: %Sp' \"$HOOK\" 2&gt;&amp;1\n            REPO_DIR=\"$(/usr/bin/dirname \"$(/usr/bin/dirname \"$HOOKDIR\")\")\"\n            echo \"  repo: $REPO_DIR\"\n            echo \"  -- content --\"\n            /bin/cat \"$HOOK\" 2&gt;&amp;1\n            echo\n        } &gt;&gt; \"$HOOKS_OUT\"\n    done\ndone &lt; &lt;(/usr/bin/find /Users -maxdepth 9 -type d -path '*/.git/hooks' -not -path '*/node_modules/*' -print0 2&gt;/dev/null)\n\nif ! /usr/bin/grep -q '^==== ACTIVE HOOK' \"$HOOKS_OUT\"; then\n    echo \"No active (non-.sample) git hooks found in any repo under /Users.\" &gt;&gt; \"$HOOKS_OUT\"\nfi\n\n###############################################################################\n# 03 - CVE-2025-54136 (MCPoison): mcp.json modification history\n###############################################################################\n\nlog \"Checking mcp.json files inside git repos for post-approval tampering...\"\n\nMCP_OUT=\"$OUT/03_MCP_HISTORY_CVE-2025-54136/findings.txt\"\n{\n    echo \"# The MCPoison attack requires a repo-committed mcp.json to be\"\n    echo \"# modified AFTER the developer already approved it once in Cursor.\"\n    echo \"# More than one commit touching the same mcp.json is not proof of\"\n    echo \"# attack (legitimate config changes happen), but is the exact\"\n    echo \"# pattern to manually review, especially whitespace-only diffs.\"\n    echo\n} &gt; \"$MCP_OUT\"\n\nwhile IFS= read -r -d '' MCPFILE; do\n    REPO_DIR=\"$(/usr/bin/git -C \"$(/usr/bin/dirname \"$MCPFILE\")\" rev-parse --show-toplevel 2&gt;/dev/null)\"\n    [[ -z \"$REPO_DIR\" ]] &amp;&amp; continue\n    RELPATH=\"${MCPFILE#$REPO_DIR/}\"\n\n    {\n        echo \"==== $MCPFILE ====\"\n        echo \"  repo: $REPO_DIR\"\n        echo \"  -- commit history for this file --\"\n        /usr/bin/git -C \"$REPO_DIR\" log --follow --oneline -- \"$RELPATH\" 2&gt;&amp;1\n        COMMIT_COUNT=\"$(/usr/bin/git -C \"$REPO_DIR\" log --follow --oneline -- \"$RELPATH\" 2&gt;/dev/null | /usr/bin/wc -l | /usr/bin/tr -d ' ')\"\n        echo \"  commit count touching this file: $COMMIT_COUNT\"\n        if [[ \"${COMMIT_COUNT:-0}\" -gt 1 ]]; then\n            echo \"  -- full diff history (review for post-approval command changes) --\"\n            /usr/bin/git -C \"$REPO_DIR\" log --follow -p -- \"$RELPATH\" 2&gt;&amp;1 | /usr/bin/head -300\n        fi\n        echo\n    } &gt;&gt; \"$MCP_OUT\"\ndone &lt; &lt;(/usr/bin/find /Users -maxdepth 8 -type f -iname 'mcp.json' -not -path '*/node_modules/*' -not -path '*/.vscode/extensions/*' -not -path '*/.cursor/extensions/*' -print0 2&gt;/dev/null)\n\nif ! /usr/bin/grep -q '^====' \"$MCP_OUT\"; then\n    echo \"No git-tracked mcp.json files found under /Users.\" &gt;&gt; \"$MCP_OUT\"\nfi\n\n###############################################################################\n# 04 - CVE-2026-22708 / DuneSlide: behavioral traces (best-effort, weak signal)\n###############################################################################\n\nlog \"Best-effort scan for Cursor-spawned shell activity in unified log (last ${LOOKBACK_DAYS}d)...\"\n\ncapture_shell \"Unified log: exec-like events tied to Cursor/Cursor Helper processes\" \\\n    \"$OUT/04_BEHAVIORAL_CVE-2026-22708_DuneSlide/exec_events.txt\" \\\n    \"/usr/bin/log show --last ${LOOKBACK_DAYS}d --predicate '(process CONTAINS[c] \\\"Cursor\\\") AND (eventMessage CONTAINS[c] \\\"bash\\\" OR eventMessage CONTAINS[c] \\\"/bin/sh\\\" OR eventMessage CONTAINS[c] \\\"osascript\\\" OR eventMessage CONTAINS[c] \\\"curl\\\" OR eventMessage CONTAINS[c] \\\"child_process\\\")' --style compact 2&gt;&amp;1 | /usr/bin/head -5000\"\n\nlog \"Best-effort sweep of Cursor's local storage for suspicious command strings (noisy, low confidence)...\"\n\ncapture_shell \"Strings sweep of Cursor SQLite state stores for shell/exfil-looking patterns\" \\\n    \"$OUT/04_BEHAVIORAL_CVE-2026-22708_DuneSlide/local_storage_strings.txt\" \\\n    \"/usr/bin/find /Users/*/Library/'Application Support'/Cursor -maxdepth 6 -type f \\\\( -iname '*.vscdb' -o -iname '*.json' \\\\) -size -20M 2&gt;/dev/null | while read -r f; do /usr/bin/strings \\\"\\$f\\\" 2&gt;/dev/null | /usr/bin/grep -iE 'curl .*(-d|-F|-T)|base64 -d|/dev/tcp/|nc -e|reverse.?shell|wget .*\\\\|.*sh' | /usr/bin/sed \\\"s|^|[\\$f] |\\\"; done\"\n\n###############################################################################\n# 05 - CVE-2026-63093: Windows-only, trivial negative confirmation\n###############################################################################\n\nlog \"Confirming no Windows git.exe-style binary planting artifacts exist (N/A platform, sanity check only)...\"\n\ncapture_shell \"Search for any git.exe-named file anywhere under /Users (expected: none, this is macOS)\" \\\n    \"$OUT/05_WINDOWS_ONLY_CVE-2026-63093/check.txt\" \\\n    \"/usr/bin/find /Users -iname 'git.exe' 2&gt;/dev/null || true\"\n\n###############################################################################\n# 06 - Cross-cutting: repos cloned from remotes outside the trusted org\n###############################################################################\n\nlog \"Listing git remotes outside the trusted org pattern '${TRUSTED_ORG}' (every CVE above requires a malicious/untrusted repo as the delivery vector)...\"\n\ncapture_shell \"Git remote URLs not matching '${TRUSTED_ORG}' -- review each manually\" \\\n    \"$OUT/06_UNTRUSTED_REPO_REMOTES/findings.txt\" \\\n    \"/usr/bin/find /Users -maxdepth 8 -type d -name .git -not -path '*/node_modules/*' -print0 2&gt;/dev/null | while IFS= read -r -d '' g; do repo=\\$(/usr/bin/dirname \\\"\\$g\\\"); remotes=\\$(/usr/bin/git --git-dir=\\\"\\$g\\\" remote -v 2&gt;/dev/null); echo \\\"\\$remotes\\\" | /usr/bin/grep -viq '${TRUSTED_ORG}' &amp;&amp; [[ -n \\\"\\$remotes\\\" ]] &amp;&amp; { echo \\\"== \\$repo ==\\\"; echo \\\"\\$remotes\\\"; echo; }; done\"\n\necho\necho \"====================================================================\"\necho \"CHECK COMPLETE\"\necho \"Output: $OUT\"\necho \"====================================================================\"\necho\necho \"Confidence summary:\"\necho \" - 02 (git hooks / CVE-2026-26268):        STRONG -- static artifact,\"\necho \"   any hit here needs immediate manual review.\"\necho \" - 03 (mcp.json history / CVE-2025-54136): STRONG -- static artifact,\"\necho \"   review any file with commit count &gt; 1.\"\necho \" - 04 (CVE-2026-22708 / DuneSlide):        WEAK/BEST-EFFORT -- these\"\necho \"   are prompt-injection-driven behavioral exploits with no documented\"\necho \"   static signature. An empty result here does not clear the machine,\"\necho \"   it only means these specific queries found nothing.\"\necho \" - 05 (CVE-2026-63093):                    N/A on macOS, included for\"\necho \"   completeness only.\"\necho \" - 06 (untrusted remotes):                 every CVE above needs a\"\necho \"   malicious repo as the delivery vector -- anything flagged here is\"\necho \"   where you'd look for the actual injection point.\"\necho\necho \"Before sharing $OUT, spot-check a few files yourself to confirm\"\necho \"the redaction caught everything you'd consider sensitive.\"\n", "creation_timestamp": "2026-08-26T08:54:53.988055Z"}