WinLol: A Multi-Stage Rust Dropper Delivering a WebRTC Remote Desktop RAT
We discovered a new, publicly unreported malware toolkit that we call WinLol, after the Go module path its author
compiled into the final payload: win-lol/cmd/agent. It is a four-stage chain. A Rust dropper fetches in-memory
shellcode, the shellcode downloads a Go remote desktop agent, and the agent is launched through a DLL sideload of a
legitimate, signed OneDrive executable. The agent then streams the victim’s screen to the operator over WebRTC.
It started in July 2026, when a researcher shared a sample on X (source) that had no family attribution at the time.
That sample (260721-xzvc3sgr6s) turned out to be the Rust dropper at the start of the chain.
Key Takeaways
- A four-stage chain: Rust dropper → in-memory downloader →
version.dllsideload through a copied OneDrive.exe → Go remote desktop agent. - The dropper checks a server-side kill switch and runs anti-sandbox checks before downloading anything.
- The agent streams the screen over WebRTC, with the operator running its own STUN/TURN relay on the C2 host.
- The lure impersonates the Riot Games client
(
RiotClientServices.exe,RiotAgentRun key).
Technical Analysis
Targeted Sample Information
- SHA256:
5fff61dff1fe18f59ebabe729c5ff9c42de64911248fb424815755fc7e76d5a1 - Filename:
dropper-msvc-x64-no-bait-noselfdel.exe - Sample: 260722-l5b8xay1hv
In the first sample, every stage that uses the network talks to workinghardo[.]link (103.193.173[.]199) over plain HTTP on port 80.
Stage 1: Rust Dropper
The first stage is a 5.9 MB 64-bit Rust executable. Before downloading anything, it checks that it is not in a sandbox and that the operator still wants new infections:
- Timing check: it sleeps for 500 ms and exits if less than 400 ms have actually passed.
- Username check: it exits if
%USERNAME%or%USERPROFILE%containsabby. - Server-clock delay: it sleeps over several rounds and compares the C2’s HTTP
Dateheader before and after, so a sandbox that fast-forwards sleeps is detected.
Kill switch. For each configured server, the dropper requests /payload.txt and only continues if the response
starts with ok (the C2 answered ok\r\n). By changing one file, the operator can stop every dropper before it reaches
stage 2. The path itself is stored encrypted and decrypted with two XOR keys embedded in the code:
cipher @0x14047F5FC : F7 A9 82 EA 58 74 AC 97 | 6F AB A5 31
key : D8 D9 E3 93 34 1B CD F3 | 41 DF DD 45
└─ 0xF3CD1B3493E3D9D8 ─┘ └ 0x45DDDF41 ┘
plain : / p a y l o a d | . t x t
Configuration. The C2 list is stored in resource 101 as JSON, XOR-encrypted with a 2-byte key (AB CD in every build
we found):
24 00 00 00 | d0 ef d8 a8 d9 bb ce bf ... -> {"servers":["workinghardo[.]link:80"]}
Decoy. Resource 100 holds a decoy document, opened only in builds with the decoy option enabled. The July builds carry a three-page PDF that uses Chinese fonts.
Preparing the sideload. The dropper does not ship OneDrive. It looks for the OneDrive.exe already installed on the
victim’s machine and copies it to %TEMP%, where it will act as a trusted host for the malicious DLL.
Next to it, the dropper writes the malicious version.dll. The DLL is stored inside the dropper in a small embedded
SQLite database that keeps files by name, each one XOR-encrypted with its own 32-byte key. The dropper looks up the row
named version.dll, decrypts it and writes it to %TEMP%\version.dll. In every build we found, this is the only file
in the database. When OneDrive.exe is later started from %TEMP%, Windows loads this version.dll instead of the real
one.
Stage 2: [payload-c] Downloader
- SHA256:
e7e640540ef158cf77b8d84e1b153cf0133d07cb70133f501ba306ae1e00586d - Sample: 260723-kapp3sgl6w
The dropper downloads /payload.bin and runs it inside its own process, so this stage never touches disk. It is a
small C downloader that logs with a [payload-c] prefix. The downloader:
- fetches the agent from
http://<server>/download-agentand saves it asRiotClientServices.exe - adds a
RiotAgentRun key that points back to the dropper, so the whole chain runs again at every logon - adds an inbound allow-all firewall rule named
RiotClientServices_appfor the agent - starts the copied OneDrive.exe, which triggers the sideload
The downloader also tries to start the agent as administrator without a UAC prompt. Instead of asking the user, it asks Windows for an elevated copy of a built-in component (CMSTPLUA) and lets that component launch the agent, so the agent runs with full admin rights. If this fails, the agent is started as a normal user. Just before launching, the downloader also tries to remove the “downloaded from the internet” mark (Mark-of-the-Web) from the agent, so Windows is less likely to warn about it.
Stage 3: version.dll Sideload Proxy
- SHA256:
593d05c817b5f41bd0cbdcfbd9729c6a875173303f700c4dd953046f9b2f6de7 - Filename:
version.dll(internal nameVersionProxy.dll) - Sample: dropped in 260722-3ldq3asxbw
This small DLL (19 KB) is the bridge between the legitimate OneDrive.exe and the RAT. It is not a program of its own:
when OneDrive.exe starts from %TEMP%, Windows looks for version.dll in OneDrive’s own folder first, finds this one,
and loads it into OneDrive’s memory. From then on, the DLL’s code runs as part of a signed Microsoft process.
The DLL does two things:
- Keeps OneDrive working. It exports the same 17 functions as the real
version.dlland copies Microsoft’s version resource. When OneDrive.exe calls one of them, the DLL loads the realC:\Windows\System32\version.dlland passes the call through, so OneDrive behaves normally and nothing looks broken.
- Starts the RAT. As soon as it is loaded, a constructor starts a thread that waits two seconds, builds the path
RiotClientServices.exein its own folder (the name is assembled from pieces at runtime), and launches it hidden withShellExecuteA. The DLL contains no RAT functionality itself; it is only the launcher.
The result looks like this:
OneDrive.exe (legitimate, Microsoft-signed, copied to %TEMP% by stage 1)
│
├─ version.dll (stage 3, loaded inside OneDrive.exe)
│ ├─ forwards OneDrive's calls to the real System32\version.dll
│ └─ ShellExecuteA("RiotClientServices.exe")
│
└─ RiotClientServices.exe (stage 4, the RAT, a separate child process of OneDrive.exe)
This is what makes the stage important: the RAT is started by a signed Microsoft process. In a process tree it would appear as a child of OneDrive.exe instead of the dropper, which makes the chain much harder to spot.
Stage 4: Go WebRTC Remote Desktop Agent
- SHA256:
C1DFD3712D6F927ACF98A78F1B77367D0B57AEFBCD6EA8A7120D61A2B8A76525 - Filename:
RiotClientServices.exe - Sample: dropped in 260722-3ldq3asxbw
The final payload is a 12.4 MB Go executable. Its build information names the project and shows it was built from uncommitted code:
path win-lol/cmd/agent
mod win-lol v0.0.0-20260721235613-6c4109e75a17+dirty
Key dependencies are pion/webrtc v3.3.6 (with pion ICE, DTLS, SCTP and SRTP), gorilla/websocket,
kbinani/screenshot, and lxn/win. Function names in the main package show what this Remote Access Trojan can do.
It captures the screen with DXGI, encodes it as H.264 (x264 or NVIDIA NVENC) and streams it over WebRTC. It can also launch itself into the user’s session from session 0, hide its process and inject a DLL.
The process hiding is aimed at Task Manager. main.startProcessHider drops an embedded DLL to the temp folder and
shares the agent’s PID with it through a named file mapping. A background loop then looks for taskmgr.exe every
150 ms and injects the DLL into each new instance. Each Task Manager process is injected only once, and the agent logs
every attempt ([hider] found taskmgr.exe pid=%d, injecting...).
Its C2 and an agent key are compiled in. The key 55yf5Lit5ZCI5qyi5aSp5LiL56ys5LiA is base64 for the Chinese
phrase 真中合欢天下第一 (“True Chinese Acacia is the best in the world”). An agent_config.json next to the executable can override the servers, but not
the key.
The key is a shared secret for each build, not a per-victim ID. On first start, the agent enrolls with
POST /api/auto-register, sending the key in an X-Agent-Key header and a label in the JSON body. The server replies
with a code that identifies this victim from then on. The agent then opens a websocket to /ws/agent and announces
itself with that code. The operator sends commands over this websocket, such as redirecting the agent to another
server or starting a stream.
When the operator starts a stream, the agent fetches STUN/TURN servers and credentials from /api/rtc/ice-config,
again with the X-Agent-Key header. It then exchanges the WebRTC offer, answer and ICE candidates over the websocket,
and sends the screen as a video track. The stream is view-only: there is no data channel and no keyboard or mouse
input.
The video itself is encrypted with DTLS-SRTP and often relayed through TURN on port 3478, so it reveals little. The
control traffic is plain HTTP and websocket, without TLS. The /api/auto-register and /api/rtc/ice-config
requests, the X-Agent-Key header and the /ws/agent upgrade are all visible on the wire, which makes them the best
network indicators.
Campaign Evolution
Using the Recorded Future Malware Analysis and Threat Intelligence platform to hunt for related samples, we found ten dropper builds, three standalone downloader builds and three agent builds submitted between 2 July and 26 August 2026. Across these builds, the toolkit changed as follows:
- C2:
workinghardo[.]link→havefunnn[.]win→103.193.173[.]83→47.108.192[.]193. Both domains resolved to103.193.173[.]199, which also hosted the payloads and the TURN relay. - Dropper: the earliest and latest builds no longer keep their config in resource 101, and the August builds replace the real decoy with an empty placeholder.
- Downloader: later builds add the UAC bypass and Mark-of-the-Web stripping, then switch to WinINet with the user
agent
OneDrive/1.0. - Agent: three builds from three different commits on 20, 21 and 23 July, differing only in their C2.
Attribution
We have not linked WinLol to any known actor. Two details point weakly to a Chinese-speaking developer or audience: the decoy document uses Chinese fonts, and the agent key decodes to a Chinese phrase. Neither is strong evidence on its own. The Riot Games branding of the lures suggests the campaign is aimed at gamers.
Detection
Triage detects the dropper, the downloader (on disk and in memory) and the agent as family winlol, and
extracts their C2 servers.
MITRE ATT&CK Techniques
| ID | Technique |
|---|---|
| T1497.001 / T1497.003 | Virtualization/Sandbox Evasion: System Checks, Time Based Evasion |
| T1105 | Ingress Tool Transfer |
| T1620 | Reflective Code Loading |
| T1574.002 | DLL Side-Loading |
| T1548.002 | Bypass User Account Control |
| T1553.005 | Mark-of-the-Web Bypass |
| T1547.001 | Registry Run Keys |
| T1562.004 | Disable or Modify System Firewall |
| T1113 | Screen Capture |
Conclusion
WinLol pairs a carefully built delivery chain with a modern remote desktop implant: a Rust dropper with a server-side kill switch, an in-memory downloader, DLL sideloading through a signed Microsoft binary, and a Go agent that streams the desktop over encrypted WebRTC. Almost every configuration value can be recovered statically, which makes new builds straightforward to track.
We will continue to monitor potential threats, including recent trends and emerging families. Stay tuned and follow our blog posts to get the most out of our sandbox. If you haven’t signed up yet, visit tria.ge to register for a free account!
Indicators of Compromise (IOCs)
Stage 1: Rust Dropper
| SHA256 | Triage submit |
|---|---|
1e82f3fa779038d208279eca1a420f168bb4b9be95ba521362e5658cd33840e7 |
261008-r5gfhs1t2n |
5fff61dff1fe18f59ebabe729c5ff9c42de64911248fb424815755fc7e76d5a1 |
261002-q19e3a1tev |
18868595393c5345e99ebe6dbfe60780aef388836b125696a3201f1a88e67c47 |
261008-r5f5raywhy |
7de267b3b415386424cefb3cb935436462ec0490a624e886615513e80606b9b2 |
261008-r7thvshz4n |
b967ebdcb56423fd2021ce01248769ebc073f7fd00eb14870e0f45cb345f9aee |
261008-r5hzcaywh1 |
d3740b8d4744dea17e58e918485273980380c3595c31cb4b938c06bd4d1222a3 |
261008-r5jwmshz2r |
f2e81dff1c3bb139f8865327180c4d45fce68cf44e4ec445c4df8b49158bed9d |
261008-r5kg6shz3w |
a1a91d9cd396186c4fde28e050f88aa68062b05195aaea091e7b3a1ca7539893 |
261008-r5hnksywhz |
82400db6e1195da7dd6db5526f795362ca9f13ef7ed5d2dba8b730fba7663f78 |
261008-r5hctahz2l |
e7ccdf128df08e131c910e904a0be711273a2f33a531e208ec531ca19b0d091e |
261008-r83g6ahz7x |
Stage 2: [payload-c] Downloader
| SHA256 | Triage submit |
|---|---|
e7e640540ef158cf77b8d84e1b153cf0133d07cb70133f501ba306ae1e00586d |
261002-q192lawdrf |
3690c237facca095803acfd0b5e702643fcf7a22f3b16a7ac00d1bc2337d370b |
261008-r5graa1t2p |
713ec0de0e018e22642e280d02ca1516e05235fb99d72355abb1d7bca5737dd2 |
261008-r5graahz3v |
Stage 3: version.dll Sideload Proxy
| SHA256 | Triage submit |
|---|---|
593d05c817b5f41bd0cbdcfbd9729c6a875173303f700c4dd953046f9b2f6de7 |
260722-3ldq3asxbw (dropped file) |
668c0851af5a3a1f685f3e632e4cce3f71aed73808deefe4c5b59bf40088d4fa |
260728-y4zzssgq8s |
Stage 4: Go Agent
| SHA256 | Triage submit |
|---|---|
4eae9858ccc07ad60b3e5deb45d30e0439d0f30309ddc75097d1878e784b3aaf |
260721-xzvc3sgr6s (dropped file) |
c1dfd3712d6f927acf98a78f1b77367d0b57aefbcd6ea8a7120d61a2b8a76525 |
261005-n1awds1hqd |
baa36514329766db2993e8435d92209f167b27725e54ca166239158bd100794f |
261008-r5jkwahz2p |
Network
| Indicator | Type |
|---|---|
workinghardo[.]link |
Domain |
havefunnn[.]win |
Domain |
103.193.173[.]199 |
IP |
103.193.173[.]83 |
IP |
47.108.192[.]193 |
IP |