Skip to content

Malicious package: npm/fmt-date-lite (date-utility download-execute dropper)#1345

Open
akyroslabs wants to merge 2 commits into
ossf:mainfrom
codelake-research:report-fmt-date-lite
Open

Malicious package: npm/fmt-date-lite (date-utility download-execute dropper)#1345
akyroslabs wants to merge 2 commits into
ossf:mainfrom
codelake-research:report-fmt-date-lite

Conversation

@akyroslabs

Copy link
Copy Markdown
Contributor

Adds an OSV record for the npm package fmt-date-lite, a download-and-execute supply-chain dropper disguised as a date-formatting utility ("Lightweight date formatting utility with locale support").

A postinstall hook runs node postinstall.js on install, which fetches a payload from the hardcoded endpoint 155.190.124.243:6788 and executes it via a Node child process — arbitrary code execution on any machine or CI runner that installs the package (version 1.0.0).

Part of a coordinated campaign of near-identical date-utility droppers: siblings datefmt-helper (C2 115.190.124.243) and date-fns-lite (C2 115.190.124.243, OSV MAL-2026-6722).

Detected by codelake Research from the live npm feed; not present in OSV or GHSA at the time of reporting.

IOCs:

  • Remote endpoint: 155.190.124.243:6788
  • sha256 (fmt-date-lite-1.0.0.tgz): 987872707c668af0739f7d0193c1db906eb87e0749a5801a8a166a0aa2735136

Full write-up: https://research.codelake.dev/advisories/clr-2026-2991-fmt-date-lite

"155.190.124.243"
],
"hashes": [
"sha256:987872707c668af0739f7d0193c1db906eb87e0749a5801a8a166a0aa2735136"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec currently doesn't include hashes.

I am interested in ideas about how to add support for this.

For example, what does this hash apply to? Is it the archive, an artifact in the archive, or a file dropped by the malicious package?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question — happy to clarify, and glad you're thinking about formalizing this.

In these entries the hash is the SHA-256 of the published npm package tarball — the .tgz artifact exactly as served by the registry (the same bytes npm pack produces / the registry dist points at). So it applies to the archive, not to a file inside it and not to the dropped second-stage payload. It can be verified against the registry tarball directly.

On adding first-class support, the three cases you list are genuinely distinct, and I think it's worth letting an entry carry more than one, discriminated by subject. Using this package as a concrete example (real hashes):

"hashes": [
  { "alg": "sha256", "value": "987872707c668af0739f7d0193c1db906eb87e0749a5801a8a166a0aa2735136",
    "target": "archive" },
  { "alg": "sha256", "value": "3268a973540ef67255c402867f3b189b6a5ac912952d49cd408a09fe622f518d",
    "target": "file", "path": "postinstall.js" },
  { "alg": "sha256", "value": "<unavailable>", "target": "dropped-payload",
    "note": "second-stage fetched at install time from 155.190.124.243:6788; C2 already offline, not retrievable" }
]

From working these supply-chain droppers, the practical trade-offs between the three:

  • archive — the most reliable/verifiable IOC: it pins the exact malicious release, and once the package is unpublished the hash still lets anyone confirm a cached/mirrored copy is the bad one. Downside: any repack (even a version bump with identical behaviour) changes it.
  • file-in-archive — useful when only one file in an otherwise-plausible package is malicious (here postinstall.js; index.js is a benign date-format facade), and it survives repackaging of unrelated files — good for clustering a campaign that reuses the same install shell across sibling packages.
  • dropped-payload — the highest-value IOC for correlating across ecosystems, but frequently unavailable: these droppers live ~40–60h before takedown and the C2 is often already gone by the time we catch them, so the second stage can't be retrieved and hashed. Worth supporting, but it can't be required.

Happy to send a small PR proposing the target field in docs/schema_additions.md, or to expand the current entries with the per-file hashes as a worked example — whichever is more useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants