Lemons, a fangless Lumma-style dropper
• Created by: Domonkos Poór

One night, surfing the pipes, I found a curious link; it had telltale signs
of being something malicious (file name ended in .rar.exe) so I went ahead and downloaded it,
as one does, and found what seems to be a broken infostealer / dropper.
This is the static analysis of that file. Nothing was executed and no operator infrastructure was contacted. The recovered IPNS name was deliberately left unresolved.
Classification
| Facet | Value |
|---|---|
| Lineage | Lumma-style (archive convention only, no code overlap confirmed) |
| Infection | phishing |
| Persistence | Windows service, disguised as "Windows Phone" (present in the payload, unreachable in this build) |
| Propagation | none observed |
| Platform | windows |
| Runtime | native, powershell |
| Purpose | loader, infostealer (suspected) |
| C2 | IPNS over public IPFS gateways, or a local IPFS daemon |
| Stage | dropper |
| Target | unknown |
How it deceives
The file hides its intent behind three layers. Each layer is a normal thing doing an abnormal job.
Layer 1: the wrapper
The outer file is a real WinRAR self-extracting archive. It is a genuine PE64 module built with MSVC in June 2026, with an archive appended to the end. This is not a fake icon or a spoofed header. It is the real WinRAR SFX program.
Two details do the work:
- The double extension
.rar.exe. It reads as an archive and runs as a program. - The SFX config sets
Setup=main.exe. When the user opens the file, the wrapper silently runs the inner file. This is documented WinRAR behavior, not a trick. See the SFX command reference.
Layer 2: the cargo
Inside the archive is one file, main.exe, protected with RAR5 AES-256
encryption. The password is 1234, and it is written in the filename itself.
The lock is not a defense. It is an invitation: the victim has to open the
archive to run it, and the filename tells them how.
The archive also carries a comment in Russian. Encryption plus a thin archive is basic anti-scan hygiene, because it hides the cargo from static scanners that cannot unlock the container.
Layer 3: the loader
main.exe is 5,179,200 bytes, and about 98 percent of that is zeros. It is a
hollow shell. This empty space serves one real purpose: it inflates the file
against scan-size heuristics.
The loader asks for Administrator rights through its manifest. It spawns
processes with a hidden window. It imports only kernel32. No network
API access or injection code observed.
Inside the loader: three message crates
The loader carries three embedded blobs, or crates. Each one decodes through the same pipeline:
raw bytes
-> subtract a fixed per-crate key (0x45 or 0x4D)
-> base64 text
-> skip one character (alignment quirk)
-> UTF-16 text
The result is a PowerShell payload.
| Crate | Content |
|---|---|
src1 and src2 |
One large PowerShell script, split across two crates |
src3 |
A second, shorter script, cut off mid-sentence |
C:\ProgramData\config |
An 879-byte base64 blob, padded with dots, holding high-entropy bytes |
The first script installs and starts Kubo, the
official Go implementation of IPFS, in
C:\ProgramData\kubo-system\. It then fetches live instructions. The
second script talks to the local IPFS daemon on 127.0.0.1:5001, resolves an
IPNS name, and downloads content by address.
The 879-byte blob is written as UTF-16 text and padded with dots that the
scripts trim away. The payload is high-entropy bytes, likely the node identity
or keys. The scripts refer to it as us_config.
What it was designed to do
The decoded scripts describe the intended mission.
- Start the engine: launch
ipfs.exe daemon --routing=auto, hidden, withIPFS_PATHset. - Ensure the engine exists: if Kubo is missing, download a zip from
$url1or$url2, unpack it, and copykubo\ipfs.exeinto place. - Ask the boss: resolve an IPNS name. This is a mutable address that the operator can repoint at any time.
- Use one of two channels: public gateways (
ipfs.io,dweb.link), or the local daemon throughname resolveandcat. - Execute the reply:
iexruns the fetched snippet together with the decodedus_configblob.
Why IPFS
IPFS is the interesting choice, and it is a good one for the attacker.
- IPFS traffic is legitimate background noise. A small node blends in.
- IPNS is a mutable pointer. The operator changes the target live, and needs no new infection to do it.
- Public gateways mean the first contact needs no local install at all.
The command channel is therefore content-addressed, mutable, and hosted by volunteers. There is no server to seize.
Persistence and stealth
A service-creation fragment appears in plain text inside the scripts:
... start= auto DisplayName= 'Windows Phone'
An auto-start service named after a dead Microsoft product. Nobody looks twice at "Windows Phone".
The rest of the stealth is ordinary but consistent: Administrator rights, hidden
windows through CREATE_NO_WINDOW, 15-second sleeps, retry loops with
cache-busting headers, and two layers of encoding across three crates to slow
down signatures.
Where it breaks
This is the finding that names the case.
The loader never starts the scripts. It builds a command line from the decoded text, which is pure base64 with no spaces, and calls:
CreateProcessW(NULL, <that text>, ...)
When the program name is NULL, Windows parses the first word of the command
line as the program to run. With no spaces, the entire 8 KB blob is one
impossible word. The call fails. There is no powershell.exe, no cmd /c, and
no interpreter prefix anywhere in the binary.
The failure is fatal, not partial:
- The first spawn fails, so the program returns immediately.
- The config file is never written.
- The second script is never decoded, and the second spawn never happens.
The second spawn would fail the same way, but it is unreachable in any case. All of it sits in the success path, which I confirmed in the disassembly.
Several oddities point to an unfinished build:
- The scripts reference variables that are never defined:
$exe,$repo,$url1. - One script has unclosed braces.
- One script ends mid-sentence.
- Part of one crate is plain text instead of encoded.
Could it still be operational
Statically, this exact file has one execution path, two failing spawn calls, no injection, and no network capability. This file alone cannot execute its payloads. There are two explanations that I think could make sense:
- A broken build of a real template. Everything except the trigger is
coherent. A sibling build with a
powershell -EncodedCommandprefix would fire, because the base64 texts are exactly that format. - One component of a larger lure. The phish may have intended to deliver other files alongside this one.
My personal opinion is that it is the result of a broken build, which is supported by some circumstantial evidence; The complete payload of a working sibling surfaced on the VirusTotal page for this sample. That sibling has the same three crates, plus the two constants this build is missing: an IPNS name and a PowerShell prefix at the two spawn sites. It also confirms the persistence wiring:
sc create 'Windows Phone' ... -EncodedCommand
The gap between the two builds is exactly two constants.
Scanners
On 2026-09-16, VirusTotal reported 35 of 58 engines as malicious, and MetaDefender reported the file as malicious.
The AV labels are generic downloader and dropper names: Wacatac, MalwareX, Paph, Yogi. No engine gave a family-specific label. VirusTotal's contacted-domain list for this sample is empty, and no files were dropped. That is consistent with a build that fires nothing.
The pass 1234 archive convention is Lumma Stealer's documented distribution
pattern; this is why I opted to call it "Lumma-style".
Facts
| Item | Value |
|---|---|
| Sample | Lemons pass 1234.rar.exe |
| Sample SHA256 | 39d0c5410d2d5de7204823ecba0a7d2fe1abbc6fa8ebec8422f7c45842c18b11 |
| Inner file | main.exe, SHA256 db7752d9de1d1b3b64377efd174d9b48df083406512061c3d8ecb2656ee29a3e |
| Wrapper | WinRAR 7.x SFX, PE64, built 2026-06-27, archive appended at 0x75600 |
| Cargo | RAR5, AES-256, password 1234, one file |
| Loader | PE64 GUI, admin manifest, 5,179,200 bytes, built 2026-07-30 |
| Payloads | Kubo bootstrap script, IPNS resolve script (truncated), 879-byte identity blob |
| C2 design | IPNS mutable name through public gateways or a local daemon |
| Method | Static analysis and public scan results. No execution. No contact with operator infrastructure. |
Timeline: SFX built 2026-06-27, loader compiled 2026-07-30, archived 2026-08-27, delivered 2026-09-16. About three months from build to delivery.
Open questions
- Monitored detonation of this exact build, to confirm that it fires nothing.
- The exact role of the 879-byte blob, which no script in this build uses.
- Whether the operator's IPNS name still resolves. I did not check, on purpose.
Glossary
Base64. A way to write binary data as letters and numbers. PowerShell's
-EncodedCommand expects exactly this format as UTF-16, which is why the
payloads look like ready-made ammunition.
IPFS. The InterPlanetary File System, a peer-to-peer file network. Kubo is its official Go implementation. A node is a small program that stores and serves content addressed by hash.
IPNS. The IPFS naming system. It is a stable name that points at changing content, and the operator can repoint it at any time. That is the command channel here.
CreateProcessW. The Windows
API
that starts a program. If the program name
is NULL, it parses the first word of the command line as the program. No
space, no word, no program, failure.
SFX. Self-extracting archive. A normal executable that unpacks an attached
archive and optionally runs a file from it through the Setup= directive.
Windows has shipped this concept for decades. The
Wikipedia article has
the history.
requireAdministrator. A manifest flag. Windows prompts for admin rights before it runs the file. The payload needs this for the service step.