top title background image

Inside ScarfaceStealer's Sandbox-Aware Anti-Analysis System

Published on: 14.07.2026



Introduction


In this blog post, we examine a multi-stage ScarfaceStealer infection chain delivered through an Electron-based application packaged with NSIS.

The investigation began with a Joe Sandbox Cloud Basic analysis https://www.joesandbox.com/analysis/1915862/0/html that produced only limited behavioral indicators, suggesting that the sample did not fully expose its intended execution path. The filename ClaudeMythos.exe, however, stood out as a lure likely targeting users interested in AI-related tools and prompted a deeper look into the sample.

Unpacking the Electron application exposed a second-stage JavaScript-based loader that performs initial evasion checks before decrypting and executing the next stage. That third stage applies four additional decryption layers, maps an embedded PE in memory, and transfers execution to it.

The recovered final stage revealed the core anti-sandbox logic: a scoring-based mechanism used to decide whether the ScarfaceStealer payload should continue execution.

This case highlights how modern stealers increasingly distribute anti-analysis logic across multiple stages, protecting not only the initial loader but also the final payload execution path.


Electron Loader Evasion in the Second-Stage


The first evasion layer is implemented inside the Electron application's main.js entry point. Since the script was heavily obfuscated, we first removed the initial obfuscation layer using https://deobfuscate.io/, and then submitted the cleaned code to Joe Reverser for automated reverse engineering: https://www.joesandbox.com/joereverser/analysis/download/df4d795d-341f-4d9e-93fa-77fb24d92793?type=html.



Before decrypting and launching the next stage, the JavaScript loader performs a set of environment checks intended to evade sandbox environments. Execution is immediately terminated if the host exposes less than 4 GB of RAM or fewer than two CPU cores. In both cases, the loader calls app.quit() followed by process.exit(0), causing the application to exit silently before the next stage is decrypted.

The loader also verifies the graphics environment by querying Win32_VideoController through PowerShell and checking the returned GPU name against common vendor strings such as nvidia, radeon, intel, and amd. If none of these values are present, the process exits silently. Notably, this check is wrapped in an empty exception handler. If the PowerShell command fails, returns an unexpected result, or times out, the validation is skipped and execution continues. As a result, the GPU check acts as an opportunistic sandbox filter rather than a hard requirement.

To better illustrate the behavior of this first evasion layer, we converted the relevant JavaScript logic into a small PowerShell proof of concept script. The purpose of this PoC was not to reproduce the loader completely, but to isolate the environment checks and make their decision logic easier to validate:



This confirms that modern malware increasingly relies on hardware-based heuristics to distinguish real endpoints from analysis environments. To trigger the intended execution path reliably, a sandbox must therefore expose hardware characteristics that resemble a genuinely attractive target, including adequate memory, multiple CPU cores, and a realistic graphics stack.

Joe Sandbox supports dynamic analysis on bare-metal systems, executing samples directly on physical hardware rather than in a virtualized environment. By leveraging real computers for analysis, it inherently avoids hardware-based sandbox detection techniques that rely on identifying virtualized or emulated hardware characteristics.




Scoring Based Evasion in the Final Stage


After bypassing the first evasion layer inside the Electron loader, execution reached a second anti-analysis stage in the final payload. Unlike the loader, this stage does not rely on a single failed check to terminate execution. Instead, it evaluates the host through 11 indicators and combines their results into a weighted suspicion score.

Using Joe Reverser Expert Mode, we mapped how each check contributes to the final decision and how the malware determines whether the host is realistic enough to continue with payload execution.

The full analysis is available here: https://www.joesandbox.com/joereverser/analysis/download/ad36c5da-4d4b-4d0c-bbb2-ae5328ba5cfa?type=html.

The routine FUN_140006d08 combines the results of all anti-sandbox checks into a single weighted score. Each indicator contributes a different value depending on its significance.

If the final score reaches 7 or higher, the host is classified as suspicious and the stealer payload execution path is not reached. As a result the malware enters a decoy loop that continuously displays random message boxes: each time one dialog is closed, another one is shown. If the score remains below 7, execution continues toward the real payload.





To make static analysis more difficult, the sample avoids storing strings in clear text. Each entry is first converted to lowercase and then hashed as UTF-16LE using a modified FNV-1a algorithm with a custom seed (0xeb1af681) instead of the standard offset basis (0x811c9dc5). Since the resulting hashes are one-way transformations, the original strings cannot be recovered directly.

We reconstructed the hashed entries through brute force and dictionary matching. The reconstructed lists are included in the appendices:

  • "Appendix A. Check id=0 Process Name Blocklist"
  • "Appendix B. Check id=3 GPU Name Allowlist"
  • "Appendix C. Check id=4 Username Blocklist"
  • "Appendix D. Check id=10 GPU Vendor Blocklist"

Once the hashed indicators had been reconstructed, we implemented a C++ proof of concept that reproduces the anti-analysis decision logic described by Joe Reverser Expert Mode. The PoC combines all 11 checks into a single test harness, making it possible to validate the scoring logic, inspect the contribution of each condition, and determine whether the sample would proceed toward payload execution.

The execution output produced during our analysis with a score of 2 (<7) is included in Appendix E. Appendix F contains the link to the PoC code, while the full PoC analysis is available at the following link:

https://www.joesandbox.com/analysis/1940467/0/html

After incorporating the insights from the PoC we re-ran the sample in Joe Sandbox and obtained a successful execution. The updated environment was able to satisfy the malware's anti-sandbox requirements and expose the next stage of its behavior, confirming that the reconstructed checks had been correctly understood and addressed.

The successful Joe Sandbox analysis, together with the corresponding screenshot, is available at the following link:

https://www.joesandbox.com/analysis/1940471/0/html



This layered design suggests that the malware author aimed to protect the entire infection chain, not just the initial loader. Even after the first layer is bypassed, the final stage introduces additional evasion logic before the payload behavior is exposed.

Joe Reverser helped speed up the investigation by making these evasion layers easier to identify, understand, and validate.


Malware Family Attribution


At this point, we performed a broader Joe Reverser analysis of the same final stage, focusing on Threat Intelligence and IOCs extraction: https://www.joesandbox.com/joereverser/analysis/download/88df7947-5c7e-4e03-978b-18c2dd143ae2?type=html.

The sample contains an embedded C2 configuration for check.mentor-square80.click:443.

It also queries the Polygon smart contract 
0x25C73C98F0E509Bd909cd1C1456EB973Dda60344 through an Ethereum-compatible JSON-RPC eth_call, using the selector 0xd040556c. The contract likely provides an alternative or fallback source for the malware's C2 configuration. This technique is commonly associated with the EtherHiding ecosystem.

curl https://polygon.drpc.org -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_call","params":[{"to":"0x25C73C98F0E509Bd909cd1C1456EB973Dda60344","data":"0xd040556c"}],"id":1}'

The call returns the following response:

{"id":1,"jsonrpc":"2.0","result":"0x00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000060613861396530386235383138626439343539626435306563393561633163636131306638656135616634356163303635626466383662323864343734616337623265613464333561313162623032396163346165623166616465623563343462"}

The returned value is ABI-encoded (hexadecimal with left zero padding). In many similar cases, the decoded value directly contains the C2 endpoint. In this sample, however, the decoded blob appears to be encrypted:

a8a9e08b5818bd9459bd50ec95ac1cca10f8ea5af45ac065bdf86b28d474ac7b2ea4d35a11bb029ac4aeb1fadeb5c44b

Joe Reverser Expert Mode identified an AES-256-CBC decryption routine while inspecting the HTTP request handler (FUN_140008268). Decrypting the retrieved ABI-decoded value produced an endpoint matching the C2 configuration already embedded in the sample:

C2 host: check.mentor-square80.click
C2 port: 443

At the time of the analysis, the URL did not receive any reports on VirusTotal:

https://www.virustotal.com/gui/url/b9d3bd64c33371dab4a93a9270df33b396182d998d13353b85493f3397188e0f

The domain relations also highlight a closely related sample:

https://www.virustotal.com/gui/file/2cdf5a3a344220ee41b7f509714f694b25bf435d838728f57845ec58d1899262

After additional pivoting in VirusTotal and by comparing similar analyses in Joe Sandbox Cloud Basic, we were able to attribute this sample to the ScarfaceStealer malware family: https://www.joesandbox.com/analysis/search?q=ScarfaceStealer.


Conclusion


This case shows how modern stealers increasingly distribute anti-analysis logic across the full infection chain. The Electron loader filters out analysis environments through hardware heuristics, while the final stage applies a weighted scoring system before allowing the ScarfaceStealer payload to execute.

By combining dynamic analysis, Joe Reverser, and bare metal analysis, we reconstructed the anti-analysis logic, identified the conditions that prevented payload execution, and adapted the analysis environment to bypass the malware's evasion checks.

Joe Reverser made these deeper evasion layers easier to understand and validate at scale, turning heavily obfuscated code into actionable insight for anti-evasion tuning and successful payload execution.


Would you like to try Joe Reverser or Joe Sandbox? Register for a free account on Joe Sandbox Cloud Basic and start using it!


Indicators of Compromise (IOCs)

SHA-256: 

  • 42b9c406d5569b9619c980b336d3236ac7f7789db57ba3b31cd79c463e6f2ca2
  • 2cdf5a3a344220ee41b7f509714f694b25bf435d838728f57845ec58d1899262

C2 domains:

  • check.mentor-square80.click

URIs: 

  • /api/v3/health

User-Agent: 

  • Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36

Appendix A. Check id=0 Process Name Blocklist

0xBA2A05BF // x32dbg.exe
0xB9454E34 // x64dbg.exe
0xFD407966 // windbg.exe
0x854E2A78 // ollydbg.exe
0xD7A05A03 // ida.exe
0xC4AA0F91 // ida64.exe
0x14143F12 // idaq.exe
0x47FB2AB0 // idaq64.exe
0xC1BBDF5F // h74xsp.exe
0x0745234D // idat64.exe
0xF797E6CC // radare2.exe
0x90ADBB1E // ghidra.exe
0xE72659B7 // ghidrarun.exe
0x40C4C43F // dnspy.exe
0xE9618405 // de4dot.exe
0x5C27A3CB // dotpeek.exe
0x40C0D839 // dotpeek64.exe
0x2DC251BC // cheatengine-x86_64.exe
0x80D108DA // cheatengine.exe
0xE76ACA8F // <not found>
0xDC30D8CF // scylla.exe
0x41CEC4CA // scylla_x64.exe
0x30032056 // scylla_x86.exe
0xDB68AC3F // die.exe
0xC81E3552 // pestudio.exe
0x51A2E803 // pe-bear.exe
0xCF43F3B4 // hiew.exe
0xE49EAD55 // hiew32.exe
0x64D4223C // apimonitor-x64.exe
0x4CD9A220 // apimonitor-x86.exe
0x3F20CF87 // regshot.exe
0x851C9BF2 // iamn0f.exe
0x45A901D5 // wireshark.exe
0x0279E8DB // fiddler.exe
0x5ABF9252 // j-obdk.exe
0x91D75626 // httpdebugger.exe
0xD48B4CF5 // <not found>
0x1278F6CC // burpsuite.exe
0x59CE92B9 // pyw.exe
0x023F59E6 // mitmproxy.exe
0xC9590D6E // 1-7oyt.exe
0x97510723 // proxifier.exe
0x415AFE92 // smartsniff.exe
0xA4EF56F1 // rawcap.exe
0x60203BFC // networkminer.exe
0xDA59FCD1 // dumpcap.exe
0x068FDF61 // fakenet.exe
0x6A7F9AC9 // xenservice.exe
0xA3C3EA38 // <not found>
0xE5A5D8A3 // <not found>
0xCB69584F // procmon64.exe
0x7A16F9CC // procexp.exe
0x3F72412E // procexp64.exe
0x2324C535 // tcpview.exe
0x30773BF2 // autoruns.exe
0x4E0861CD // filemon.exe
0x7E11166B // regmon.exe
0x0AA6028A // processhacker.exe
0x0535AC50 // sysanalyzer.exe
0x0536BF1F // 52n1oo.exe
0x343B0086 // ozu0jc.exe
0x0E6EA0C8 // immunitydebugger.exe
0x33ECDC2A // <not found>
0x1E071347 // lordpe.exe
0x8D8648DE // protection_id.exe
0x8C963DD7 // peid.exe
0x1E08DE85 // joeboxcontrol.exe
0x3B9C56E7 // joeboxserver.exe
0x2A00D3A1 // vboxservice.exe
0x292AEC72 // vboxtray.exe
0x96B59713 // vmtoolsd.exe
0xDB40F9D3 // vmwaretray.exe
0xD890E6CC // vmwareuser.exe
0x93760CA8 // vmsrvc.exe
0xBA27E6E5 // vmusrvc.exe
0x5F7A2BB5 // prl_tools.exe
0xEAA39666 // prl_cc.exe

Appendix B. Check id=3 GPU Name Allowlist

0x5D9F8AA4 // nvidia
0xB46022F2 // radeon
0xA77EEF5B // intel
0xD31A9699 // <not found>
0x7346A4CC // qualcomm

Appendix C. Check id=4 Username Blocklist

0x7BA43F80 // dab cu (probably hash collision)
0x966EA0AA // miller
0x6D2AB6D0 // malware
0x6BA382EF // maltest
0x854D8BEC // johndoe
0xF8A8714C // sandbox
0x0EF62EBA // virus
0x767A179C // john doe
0xB9184372 // ba5wjx
(probably hash collision)
0x5A7EB0B4 // a u4l1
(probably hash collision)
0x7F1C2975 // _c9j12
(probably hash collision)
0x4490D09D // bruno
0x1C9436CC // george
0x69C2D508 // 
<not found>
0xF6C2B2AF // test
0x20AB9BF7 // sample
0xA7E472E3 // analysis
0x2410B3A7 // cuckoo
0xAC7D9203 // vmware
0x17518351 // lichao

Appendix D. Check id=10 GPU Vendor Blocklist

0x884CAC3E // 0x1234 -> QEMU / BOCHS
0x884CABA7 // 0x15AD -> VMware
0x884C3EE4 // 0x80EE -> VirtualBox
0x884CA53C // 0x1B36 -> QEMU VirtIO GPU
0x884CA4FE // 0x1AF4 -> Red Hat VirtIO
0x884CA4EA // 0x1AE0 -> Google Compute Engine

Appendix E.  Scoring-Based Evasion PoC Output

[CHECK-2] Total physical memory: 8191 MB
[CHECK-2] Physical memory check score: 0
[CHECK-7] GetSystemMetrics(SM_REMOTESESSION) = 0 (local session)
[CHECK-3] Enumerating display adapters with EnumDisplayDevicesW()...
[CHECK-3] Adapter index 0 found.
[CHECK-3]   DeviceString: Intel(R) UHD Graphics 3950
[CHECK-3]   StateFlags  : 0x00000005
[CHECK-3]   Checking substring len=5 start=0 substring="intel"
[CHECK-3] Whitelisted GPU adapter substring hash matched
[CHECK-3] adapter_index=0 substring="intel" full_device_string="intel(r) uhd graphics 3950"
[CHECK-3] Returning 1: whitelisted GPU adapter name detected; score unchanged.
[CHECK-4] ComputerName: DESKTOP-DF7340 (OK)
[CHECK-4] Username: Maoga (OK)
[CHECK-4] Result: 0
[CHECK-8] Samples: 330, 296, 295, 343, 348, 320, 324 cycles
[CHECK-8] Median __cpuid latency: 324 cycles
[CHECK-8] Result: 0 - likely bare metal (<=1500 cycles)
[CHECK-5] Uptime: 6413578 ms | Result: 0 | Meaning: plausible real machine
[CHECK-9] Reading CPU vendor information from __cpuid leaf 0...
[CHECK-9] Leaf 0 EBX value: 0x756E6547
[CHECK-9] Vendor leaf indicates Intel-compatible CPU vendor.
[CHECK-9] Reading CPU brand string from extended __cpuid leaves 0x80000002-0x80000004...
[CHECK-9] CPU brand string: Intel(R) Core(TM)2 CPU 6600 @ 2.40 GHz
[CHECK-9] Scanning brand string for manufacturer keywords: Intel / AMD...
[CHECK-9] Brand string contains keyword: Intel
[CHECK-9] Summary: vendor_leaf=Intel, brand_mentions_intel=true, brand_mentions_amd=false
[CHECK-9] No __cpuid vendor/brand mismatch detected.
[CHECK-9] Result: 0 - vendor and brand appear consistent.
[CHECK-1] physical core count  = 2
[CHECK-1] logical thread count = 2
[CHECK-1] returning score      = 2
[CHECK-10] Starting GPU PCI Vendor ID enumeration check.
[CHECK-10] Enumerating display adapters using EnumDisplayDevicesW().
[CHECK-10] Adapter index 0 found.
[CHECK-10]   DeviceName : \\.\DISPLAY1
[CHECK-10]   DeviceID   : PCI\VEN_8086&DEV_7D45&SUBSYS_040515AD&REV_00
[CHECK-10]   StateFlags : 0x00000005
[CHECK-10]   Adapter is attached to the desktop. Processing DeviceID.
[CHECK-10]   DeviceID length: 44 characters.
[CHECK-10]   Scanning DeviceID for VEN_ marker.
[CHECK-10]   VEN_ marker found at position 4.
[CHECK-10]   End of VEN field reached.
[CHECK-10]   Parsed PCI Vendor ID: 0x8086.
[CHECK-10]   Comparing parsed Vendor ID against VM GPU vendor blocklist.
[CHECK-10]     Blocklist entry 0: raw=0x884CAC3E decoded=0x1234.
[CHECK-10]     Blocklist entry 1: raw=0x884CABA7 decoded=0x15AD.
[CHECK-10]     Blocklist entry 2: raw=0x884C3EE4 decoded=0x80EE.
[CHECK-10]     Blocklist entry 3: raw=0x884CA53C decoded=0x1B36.
[CHECK-10]     Blocklist entry 4: raw=0x884CA4FE decoded=0x1AF4.
[CHECK-10]     Blocklist entry 5: raw=0x884CA4EA decoded=0x1AE0.
[CHECK-10]   No blocklist match for Vendor ID 0x8086.
[CHECK-10]   Continuing scan for possible additional VEN_ markers.
[CHECK-10] Moving to next display adapter.
[CHECK-10] Enumeration completed.
[CHECK-10] No blocklisted GPU PCI Vendor ID was found.
[CHECK-10] Returning 0: no VM/hypervisor GPU vendor detected by this check.
[CHECK0] total_processes=194 return=0 meaning=normal: more than 100 running processes
[CHECK-6] Sleep requested: 1000 ms
[CHECK-6] Elapsed: 1015 ms
[CHECK-6] Result: 0 (normal timing: Sleep duration looks realistic)
[FINAL] true_score=2 threshold=7
All checks passed, resume execution...
Press ENTER to continue...

Appendix F.  Scoring-Based Evasion PoC

Here’s the Pastebin link to the code: https://pastebin.com/wLNnydfX