

Immediate actionable code for spotting the Nightmare Eclipse zero‑day that targets CrowdStrike, Nvidia and Avast products.
<a name="step-1-prerequisites"></a>
| Item | Why you need it | How to obtain |
|---|---|---|
| Python ≥ 3.9 | Runs the detection script | https://www.python.org/downloads/ |
| Node.js ≥ 16 (or TypeScript) | Runs the JS/TS detector | https://nodejs.org/ |
| Git | Clone the example repo (optional) | https://git-scm.com/ |
CrowdStrike Falcon API credentials (client_id, client_secret) | Query Falcon IOC feeds | Sign‑up at https://www.crowdstrike.com/ → API Clients |
| Nvidia GPU driver version check (no special token needed) | Detect known vulnerable driver versions | Nvidia driver download page |
| Avast Threat Intelligence API key (optional) | Pull Avast‑specific IOCs | Request via https://www.avast.com/ → Threat Intel |
| VirusTotal API key (optional, for hash verification) | Confirm file hashes against VT | <https://www.virustotal.com/gui/user/<your‑email>/api-key> |
| dotenv package (both runtimes) | Load secrets from .env without hard‑coding | Installed via pip/npm |
Tip: Keep all secrets in a
.envfile that is never committed to source control (add it to.gitignore).
<a name="step-2-installation-and-setup"></a>
git clone https://github.com/example/nightmare-eclipse-detector.git
cd nightmare-eclipse-detector
# Create a virtual environment (recommended)
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
# Install dependencies
pip install --upgrade pip
pip install crowdstrike-falconpy requests python-dotenv tqdm
# Initialise a new npm project (if you don't have one)
npm init -y
# Install core libraries
npm install axios dotenv crypto-js
# TypeScript (optional but recommended)
npm install --save-dev typescript @types/node ts-node
npx tsc --init # creates tsconfig.json
.env)Create a file named .env in the project root and add it to .gitignore.
# CrowdStrike
CROWDSTRIKE_CLIENT_ID=your_cs_client_id
CROWDSTRIKE_CLIENT_SECRET=your_cs_client_secret
# Avast Threat Intel (optional)
AVAST_API_KEY=your_avast_api_key
# VirusTotal (optional)
VT_API_KEY=your_virustotal_api_key
# Nvidia – no secret needed; we just read the driver version locally
<a name="step-3-basic-implementation"></a>
Below are complete, copy‑paste‑ready scripts that:
./scan_target) for matching file hashes, running processes, and suspicious network connections.Replace
SCAN_TARGETwith the path you want to monitor (e.g.,C:\Windows\System32or/opt).
Never run the scanner on production systems without first testing in a sandbox.
detector.py)#!/usr/bin/env python3
"""
Nightmare Eclipse Detector – Python version
-------------------------------------------
- Authenticates to CrowdStrike Falcon via OAuth2.
- Retrieves IOCs (SHA256, MD5, domains, IPs) tagged with "Nightmare Eclipse".
- Optionally pulls Avast and VirusTotal feeds.
- Walks a directory, computes SHA256 of each file, and matches against IOCs.
- Lists any matching processes (by name) and outbound connections to IOC IPs/domains.
"""
import os
import sys
import hashlib
import json
import time
from pathlib import Path
from typing import List, Set, Dict, Tuple
import requests
from dotenv import load_dotenv
from tqdm import tqdm
# -------------------------- CONFIGURATION --------------------------
load_dotenv() # loads .env into os.environ
CROWDSTRIKE_BASE = "https://api.crowdstrike.com"
AVAST_BASE = "https://api.avast.com" # placeholder – adjust if real endpoint differs
VT_BASE = "https://www.virustotal.com/api/v3"
# IOC types we care about
IOC_SHA256 = "sha256"
IOC_MD5 = "md5"
IOC_DOMAIN = "domain"
IOC_IP = "ipv4"
# Where to look for suspicious files (change as needed)
SCAN_TARGET = Path(os.getenv("SCAN_TARGET", "./scan_target"))
# How deep to walk (None = unlimited)
MAX_DEPTH = int(os.getenv("MAX_DEPTH", "3"))
# -------------------------- HELPERS --------------------------
def _get_env(var: str, default: str = None) -> str:
val = os.getenv(var, default)
if val is None:
raise EnvironmentError(f"Missing required environment variable: {var}")
return val
def crowdstrike_token() -> str:
"""Obtain an OAuth2 bearer token from CrowdStrike."""
client_id = _get_env("CROWDSTRIKE_CLIENT_ID")
client_secret = _get_env("CROWDSTRIKE_CLIENT_SECRET")
url = f"{CROWDSTRIKE_BASE}/oauth2/token"
payload = {
"client_id": client_id,
"client_secret": client_secret,
}
resp = requests.post(url, data=payload, timeout=10)
resp.raise_for_status()
return resp.json()["access_token"]
def _paginated_get(url: str, headers: Dict, params: Dict) -> List[Dict]:
"""Generic helper for paginated CrowdStrike endpoints."""
results = []
while url:
r = requests.get(url, headers=headers, params=params, timeout=15)
r.raise_for_status()
data = r.json()
results.extend(data.get("resources", []))
# CrowdStrike uses `next` token in meta.pagination
meta = data.get("meta", {}).get("pagination", {})
url = meta.get("next")
params = {} # after first request, pagination token is in URL
return results
def fetch_crowdstrike_iocs(token: str, query: str = '"Nightmare Eclipse"') -> Tuple[Set[str], Set[str], Set[str], Set[str]]:
"""
Query Falcon IOC search for the given free‑text query.
Returns four sets: (sha256, md5, domains, ipv4).
"""
headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
# The IOC endpoint expects a POST with a filter body.
# See: https://www.crowdstrike.com/api/docs/falcon/ioc/search-ioc-entities-v1
search_url = f"{CROWDSTRIKE_BASE}/ioc/entities/ioc-search/v1"
payload = {
"query": query,
"limit": 5000, # max per page; adjust if you need more
}
# First page to get total count & pagination token
resp = requests.post(search_url, headers=headers, json=payload, timeout=20)
resp.raise_for_status()
data = resp.json()
resources = data.get("resources", [])
# Extract IOCs from the first page
sha256_set, md5_set, domain_set, ip_set = set(), set(), set(), set()
for ioc in resources:
# IOC objects differ by type; we normalise them.
ioc_type = ioc.get("type", "").lower()
value = ioc.get("value", "").lower()
if ioc_type == "sha256":
sha256_set.add(value)
elif ioc_type == "md5":
md5_set.add(value)
elif ioc_type in ("domain", "hostname"):
domain_set.add(value)
elif ioc_type in ("ipv4", "ip address"):
ip_set.add(value)
# Handle pagination if needed (CrowdStrike returns a `next` token in meta)
# For brevity, we skip full pagination here – in production loop until `next` is null.
return sha256_set, md5_set, domain_set, ip_set
def fetch_avast_iocs() -> Tuple[Set[str], Set[str], Set[str], Set[str]]:
"""
Placeholder for Avast Threat Intel call.
Replace the URL/payload with the real Avast API spec.
Returns empty sets if the key is missing or the call fails.
"""
api_key = os.getenv("AVAST_API_KEY")
if not api_key:
return set(), set(), set(), set()
try:
headers = {"Authorization": f"Bearer {api_key}"}
resp = requests.get(f"{AVAST_BASE}/iocs?tag=Nightmare%20Eclipse", headers=headers, timeout=15)
resp.raise_for_status()
data = resp.json()
# Assume Avast returns a list of objects with `type` and `value`
sha256_set, md5_set, domain_set, ip_set = set(), set(), set(), set()
for item in data.get("items", []):
t = item.get("type", "").lower()
v = item.get("value", "").lower()
if t == "sha256":
sha256_set.add(value)
elif t == "md5":
md5_set.add(value)
elif t in ("domain", "hostname"):
domain_set.add(value)
elif t in ("ipv4", "ip address"):
ip_set.add(value)
return sha256_set, md5_set, domain_set, ip_set
except Exception as e:
print(f"[WARN] Avast feed failed: {e}")
return set(), set(), set(), set()
def fetch_virustotal_hashes(hash_list: Set[str]) -> Set[str]:
"""
Optional: verify a set of hashes against VirusTotal to reduce false positives.
Returns the subset that VT flags as malicious.
"""
vt_key = os.getenv("VT_API_KEY")
if not vt_key:
return set()
malicious = set()
headers = {"x-apikey": vt_key}
for h in hash_list:
url = f"{VT_BASE}/files/{h}"
try:
r = requests.get(url, headers=headers, timeout=10)
if r.status_code == 404:
continue # unknown hash – treat as clean
r.raise_for_status()
data = r.json()
stats = data.get("data", {}).get("attributes", {}).get("last_analysis_stats", {})
if stats.get("malicious", 0) > 0:
malicious.add(h)
except requests.RequestException:
# network hiccup – skip this hash; you may want to retry
continue
# VT rate‑limit: 4 requests/min for free key; sleep a bit
time.sleep(15)
return malicious
def sha256_file(path: Path) -> str:
"""Return SHA256 hex digest of a file."""
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
h.update(chunk)
return h.hexdigest()
def scan_directory(root: Path,
sha256_set: Set[str],
md5_set: Set[str],
max_depth: int = None) -> List[Tuple[Path, str]]:
"""
Walk `root` (respecting max_depth) and return list of (path, hash_type)
for any file whose hash matches the supplied sets.
"""
matches = []
for dirpath, dirnames, filenames in os.walk(root):
# Compute current depth
depth = len(Path(dirpath).relative_to(root).parts)
if max_depth is not None and depth > max_depth:
# Prune this branch
dirnames[:] = []
continue
for fname in filenames:
fpath = Path(dirpath) / fname
try:
# Quick size filter – skip zero‑byte files
if fpath.stat().st_size == 0:
continue
file_sha256 = sha256_file(fpath)
if file_sha256 in sha256_set:
matches.append((fpath, IOC_SHA256))
continue # no need to check MD5 if SHA256 matched
file_md5 = hashlib.md5(open(fpath, "rb").read()).hexdigest()
if file_md5 in md5_set:
matches.append((fpath, IOC_MD5))
except (PermissionError, OSError):
# Skip files we can't read
continue
return matches
def get_running_process_names() -> Set[str]:
"""Return a set of lower‑cased process names (cross‑platform via psutil if available)."""
try:
import psutil
return {p.name().lower() for p in psutil.process_iter(['name'])}
except Exception:
# Fallback: use platform‑specific commands (very basic)
import platform, subprocess
if platform.system() == "Windows":
out = subprocess.check_output("tasklist", shell=True, text=True)
names = {line.split()[0].lower() for line in out.splitlines()[3:] if line.strip()}
return names
else: # Linux/macOS
out = subprocess.check_output("ps -ax -o comm=", shell=True, text=True)
return {line.strip().lower() for line in out.splitlines() if line.strip()}
def get_outbound_connections() -> List[Tuple[str, str, str]]:
"""
Return list of (local_addr, remote_addr, state) for TCP connections in ESTABLISHED state.
Uses psutil if installed; otherwise returns empty list.
"""
try:
import psutil
conns = []
for c in psutil.net_connections(kind='tcp'):
if c.status == 'ESTABLISHED' and c.raddr:
conns.append((f"{c.laddr.ip}:{c.laddr.port}",
f"{c.raddr.ip}:{c.raddr.port}",
c.status))
return conns
except Exception:
return []
# -------------------------- MAIN LOGIC --------------------------
def main() -> int:
print("[+] Starting Nightmare Eclipse detector …")
try:
token = crowdstrike_token()
except Exception as e:
print(f"[!] Failed to obtain CrowdStrike token: {e}")
return 1
print("[+] Fetching CrowdStrike IOCs …")
cs_sha256, cs_md5, cs_domains, cs_ips = fetch_crowdstrike_iocs(token)
print("[+] Fetching Avast IOCs (if configured) …")
av_sha256, av_md5, av_domains, av_ips = fetch_avast_iocs()
# Merge sets
all_sha256 = cs_sha256 | av_sha256
all_md5 = cs_md5 | av_md5
all_domains= cs_domains | av_domains
all_ips = cs_ips | av_ips
print(f"[+] IOC counts – SHA256:{len(all_sha256)} MD5:{len(all_md5)} Domains:{len(all_domains)} IPs:{len(all_ips)}")
# Optional VirusTotal verification (only for hashes we already have)
if os.getenv("VT_API_KEY"):
print("[+] Verifying hashes with VirusTotal …")
vt_malicious_sha256 = fetch_virustotal_hashes(all_sha256)
vt_malicious_md5 = fetch_virustotal_hashes(all_md5)
print(f"[+] VT malicious – SHA256:{len(vt_malicious_sha256)} MD5:{len(vt_malicious_md5)}")
# Replace our sets with VT‑confirmed ones to reduce noise
all_sha256 = vt_malicious_sha256
all_md5 = vt_malicious_md5
# ---- Scan filesystem ----
print(f"[+] Scanning directory {SCAN_TARGET} (max depth={MAX_DEPTH}) …")
file_hits = scan_directory(SCAN_TARGET, all_sha256, all_md5, max_depth=MAX_DEPTH)
if file_hits:
print(f"[!!] Found {len(file_hits)} matching file(s):")
for path, htype in file_hits[:20]: # limit output
print(f" - {path} ({htype})")
if len(file_hits) > 20:
print(f" … and {len(file_hits)-20} more")
else:
print("[+] No suspicious files found.")
# ---- Scan running processes ----
print("[+] Checking running processes …")
proc_names = get_running_process_names()
# We don't have process‑hash IOCs in this simple example,
# but you could match against known malicious process names if you have them.
# For demonstration, we just list any process whose name appears in domain/IP IOCs (unlikely).
suspicious_procs = {p for p in proc_names if any(d in p for d in all_domains) or any(ip.split(":")[0] in p for ip in all_ips)}
if suspicious_procs:
print(f"[!!] Suspicious process names: {', '.join(suspicious_procs)}")
else:
print("[+] No suspicious process names detected.")
# ---- Scan network connections ----
print("[+] Checking outbound TCP connections …")
conns = get_outbound_connections()
bad_conns = []
for local, remote, state in conns:
rip, rport = remote.split(":")
if rip in all_ips or any(dom in rip for dom in all_domains):
bad_conns.append((local, remote, state))
if bad_conns:
print(f"[!!] Found {len(bad_conns)} connection(s) to IOC IPs/domains:")
for l, r, s in bad_conns[:10]:
print(f" {l} -> {r} [{s}]")
if len(bad_conns) > 10:
print(f" … and {len(bad_conns)-10} more")
else:
print("[+] No outbound connections to IOC endpoints.")
# ---- Final verdict ----
if file_hits or suspicious_procs or bad_conns:
print("\n[!!!] POTENTIAL NIGHTMARE ECLIPSE ACTIVITY DETECTED !!!")
return 1
else:
print("\n[+] No indicators of Nightmare Eclipse found.")
return 0
if __name__ == "__main__":
sys.exit(main())
How to run
# Make sure .env is present with at least CrowdStrike keys
python detector.py
detector.ts)/**
* Nightmare Eclipse Detector – Node.js / TypeScript version
* ---------------------------------------------------------
* Mirrors the Python script's functionality:
* - OAuth2 token from CrowdStrike
* - Pull IOCs (SHA256, MD5, domain, IPv4) tagged "Nightmare Eclipse"
* - Optional Avast & VirusTotal enrichment
* - Walks a directory, computes SHA256, matches IOCs
* - Lists matching processes and outbound connections to IOC IPs/domains
*
* Requires Node.js >= 16. Install dependencies:
* npm install axios dotenv crypto-js
* (Optional for TypeScript) npm install --save-dev typescript @types/node ts-node
*/
import * as dotenv from 'dotenv';
import axios from 'axios';
import * as crypto from 'crypto';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
// Load .env file
dotenv.config();
// -------------------------- CONFIG --------------------------
const CROWDSTRIKE_BASE = process.env.CROWDSTRIKE_BASE ?? 'https://api.crowdstrike.com';
const AVAST_BASE = process.env.AVAST_BASE ?? 'https://api.avast.com';
const VT_BASE = 'https://www.virustotal.com/api/v3';
const SCAN_TARGET = process.env.SCAN_TARGET ?? './scan_target';
const MAX_DEPTH = parseInt(process.env.MAX_DEPTH ?? '3', 10);
// IOC type constants
const IOC_SHA256 = 'sha256';
const IOC_MD5 = 'md5';
const IOC_DOMAIN = 'domain';
const IOC_IP = 'ipv4';
// -------------------------- HELPERS --------------------------
function requireEnv(key: string): string {
const val = process.env[key];
if (!val) throw new Error(`Missing required env var: ${key}`);
return val;
}
/**
* Get CrowdStrike OAuth2 bearer token (client_credentials flow)
*/
async function getCrowdStrikeToken(): Promise<string> {
const clientId = requireEnv('CROWDSTRIKE_CLIENT_ID');
const clientSecret = requireEnv('CROWDSTRIKE_CLIENT_SECRET');
const url = `${CROWDSTRIKE_BASE}/oauth2/token`;
const { data } = await axios.post(url, new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
}), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
return data.access_token;
}
/**
* Generic paginated GET for CrowdStrike IOC search.
* Returns a flat array of IOC objects.
*/
async function fetchCrowdStrikeIOCs(token: string, query: string = '"Nightmare Eclipse"'): Promise<any[]> {
const headers = { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' };
const searchUrl = `${CROWDSTRIKE_BASE}/ioc/entities/ioc-search/v1`;
const payload = { query, limit: 5000 };
const { data } = await axios.post(searchUrl, payload, { headers });
// For simplicity we only return the first page; in prod handle `meta.pagination.next`
return data.resources ?? [];
}
/**
* Normalise CrowdStrike IOC objects into typed sets.
*/
function normaliseIOCs(raw: any[]): {
sha256: Set<string>;
md5: Set<string>;
domain: Set<string>;
ip: Set<string>;
} {
const sha256 = new Set<string>();
const md5 = new Set<string>();
const domain = new Set<string>();
const ip = new Set<string>();
for (const ioc of raw) {
const type = (ioc.type ?? '').toLowerCase();
const value = (ioc.value ?? '').toLowerCase();
if (type === 'sha256') sha256.add(value);
else if (type === 'md5') md5.add(value);
else if (type === 'domain' || type === 'hostname') domain.add(value);
else if (type === 'ipv4' || type === 'ip address') ip.add(value);
}
return { sha256, md5, domain, ip };
}
/**
* Placeholder for Avast feed – replace with real endpoint if you have one.
*/
async function fetchAvastIOCs(): Promise<{
sha256: Set<string>;
md5: Set<string>;
domain: Set<string>;
ip: Set<string>;
}> {
const avastKey = process.env.AVAST_API_KEY;
if (!avastKey) return { sha256: new Set(), md5: new Set(), domain: new Set(), ip: new Set() };
try {
const { data } = await axios.get(`${AVAST_BASE}/iocs?tag=Nightmare%20Eclipse`, {
headers: { Authorization: `Bearer ${avastKey}` },
});
// Assume Avast returns { items: [{type, value}, ...] }
const sha256 = new Set<string>();
const md5 = new Set<string>();
const domain = new Set<string>();
const ip = new Set<string>;
for (const item of data.items ?? []) {
const t = (item.type ?? '').toLowerCase();
const v = (item.value ?? '').toLowerCase();
if (t === 'sha256') sha256.add(v);
else if (t === 'md5') md5.add(v);
else if (t === 'domain' || t === 'hostname') domain.add(v);
else if (t === 'ipv4' || t === 'ip address') ip.add(v);
}
return { sha256, md5, domain, ip };
} catch (e) {
console.warn('[WARN] Avast feed failed:', (e as Error).message);
return { sha256: new Set(), md5: new Set(), domain: new Set(), ip: new Set() };
}
}
/**
* Query VirusTotal for a hash – returns true if VT marks it malicious.
* Respects VT free‑tier rate limit (~4 req/min).
*/
async function vtIsMalicious(hash: string, apiKey: string): Promise<boolean> {
const url = `${VT_BASE}/files/${hash}`;
try {
const { data } = await axios.get(url, { headers: { 'x-apikey': apiKey } });
const stats = data.data?.attributes?.last_analysis_stats ?? {};
return (stats.malicious ?? 0) > 0;
} catch (err: any) {
if (err.response?.status === 404) return false; // unknown => treat as clean
console.warn(`VT lookup error for ${hash}:`, err.message);
return false;
}
}
/**
* Compute SHA256 of a file (streaming to avoid large memory use).
*/
function sha256File(filePath: string): Promise<string> {
return new Promise((resolve, reject) => {
const hash = crypto.createHash('sha256');
const stream = fs.createReadStream(filePath);
stream.on('error', reject);
stream.on('data', chunk => hash.update(chunk));
stream.on('end', () => resolve(hash.digest('hex')));
});
}
/**
* Walk a directory (respecting maxDepth) and return matches.
*/
async function scanDirectory(
root: string,
sha256Set: Set<string>,
md5Set: Set<string>,
maxDepth: number | null
): Promise<Array<{file: string; type: string}>> {
const matches: Array<{file: string; type: string}> = [];
async function walk(dir: string, depth: number) {
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
if (maxDepth !== null && depth + 1 > maxDepth) continue;
await walk(full, depth + 1);
} else if (entry.isFile()) {
try {
const stats = await fs.promises.stat(full);
if stats.size === 0 continue; // skip empty files
const fileSha256 = await sha256File(full);
if (sha256Set.has(fileSha256)) {
matches.push({ file: full, type: IOC_SHA256 });
continue;
}
const md5Hash = crypto.createHash('md5')
.update(await fs.promises.readFile(full))
.digest('hex');
if (md5Set.has(md5Hash)) {
matches.push({ file: full, type: IOC_MD5 });
}
} catch (err) {
// ignore permission errors etc.
}
}
}
}
await walk(root, 0);
return matches;
}
/**
* Get list of running process names (cross‑platform via psutil‑like fallback).
*/
async function getProcessNames(): Promise<Set<string>> {
try {
const { default: psutil } = await import('psutil');
const procs = await psutil.process_iter(['name']);
const names = new Set<string>();
for (const p of procs) {
const name = (await p.get('name'))?.toLowerCase() ?? '';
if (name) names.add(name);
}
return names;
} catch {
// Very basic fallback using platform commands
if (process.platform === 'win32') {
const { execSync } = require('child_process');
const out = execSync('tasklist', { encoding: 'utf8' });
const names = new Set<string>();
for (const line of out.split('\n').slice(3)) {
const parts = line.trim().split(/\s+/);
if (parts[0]) names.add(parts[0].toLowerCase());
}
return names;
} else {
const { execSync } = require('child_process');
const out = execSync('ps -ax -o comm=', { encoding: 'utf8' });
const names = new Set<string>();
for (const line of out.split('\n')) {
const name = line.trim();
if (name) names.add(name.toLowerCase());
}
return names;
}
}
}
/**
* Get outbound TCP connections (ESTABLISHED) – best effort.
*/
async function getOutboundConnections(): Promise<Array<{local: string; remote: string; state: string}>> {
try {
const { default: psutil } = await import('psutil');
const nets = await psutil.net_connections({ kind: 'tcp' });
const result: Array<{local: string; remote: string; state: string}> = [];
for (const c of nets) {
if (c.status === 'ESTABLISHED' && c.raddr) {
result.push({
local: `${c.laddr.address}:${c.laddr.port}`,
remote: `${c.raddr.address}:${c.raddr.port}`,
state: c.status,
});
}
}
return result;
} catch {
// If psutil not available, return empty list (you can implement netstat parsing if needed)
return [];
}
}
/**
* Main routine
*/
async function main(): Promise<number> {
console.log('[+] Starting Nightmare Eclipse detector (Node/TS)…');
// 1️⃣ CrowdStrike token
let token: string;
---
## Next Steps
1. **Get API Access** - Sign up at the official website
2. **Try the Examples** - Run the code snippets above
3. **Read the Docs** - Check official documentation
4. **Join Communities** - Discord, Reddit, GitHub discussions
5. **Experiment** - Build something cool!
## Further Reading
- [TechCrunch AI](https://techcrunch.com/category/artificial-intelligence/)
- [The Verge](https://www.theverge.com/technology)
- [Wired AI](https://www.wired.com/tag/artificial-intelligence/)
- [Medium AI](https://medium.com/topic/artificial-intelligence)
**Source:** [Security Week AI](https://www.securityweek.com/nightmare-eclipse-drops-crowdstrike-nvidia-avast-zero-day-exploits/)
---
*Follow ICARAX for more AI insights and tutorials.*
