
by Robin Dost
EDIT: I have YARA rules available for this one, if you need them, contact me at contact@robin-dost.de
Also, checkout my project KRAKEN if you’re interested in continuous threat actor tracking.
Lately I’ve been spending more time looking at malware targeting Ukraine and Europe. And yeah, a lot of it is neither new nor particularly creative. But it works. And that’s exactly why it’s worth digging into.
The sample we’re looking at here is fresh (from today, 09.04.2026), part of a UAC-0226 campaign and turns out to be a variant of the well-known GIFTEDCROOK stealer.
Initial access? Surprise: CVE-2025-6218 & CVE-2025-8088.
Maybe you already know this one from one of my previous articles.
A prepared archive, some basic social engineering, an LNK and the user still clicks it. End of story.
From there it’s the usual flow:
LNK launches payload
Payload decodes another binary
Binary initially looks like absolute garbage
Constants everywhere, useless function calls, pseudo-random noise. The classic “maybe the analyst just gives up” approach.
If you ignore all that noise, what’s actually happening becomes pretty obvious:
- RC4-based encryption
- Chunked data exfiltration
- A simple but working exfil client
- Runtime reconstructed C2
Nothing high-end. No fancy exploit chain fireworks.
Just cleanly glued together building blocks doing exactly what they’re supposed to do: grab data and ship it home.
And that’s exactly what makes this sample interesting.
So let’s take a look.
The sample has the hash7200a9f1e1ea51b66ab9c9274e9d8f805633179634e8ff4dcb8ef82bc02518df
It’s a RAR archive and once extracted it’s immediately obvious the actor is abusing the WinRAR CVE to deliver the payload.
We’ve seen this before, for example with Gamaredon, but the execution here is slightly different.
We get a decoy PDF named:
“Відомості з реєстру військовозобов’язаних про працівників №20260409-7496423-1.pdf”
Roughly translated:
“Information from the Military Service Registry regarding employees No. 20260409-7496423-1.pdf”
So yeah, clearly targeting Ukraine.
Opening the PDF confirms the theme.

(Personal information has been redacted)
Alongside the PDF there are additional files:

- ojMP31J28ohEDxT.lnk
- Arj
- 05fE
Naturally, we start with the LNK using lnkparse since that’s usually the entry point

We can see a PowerShell command that compiles the file “05fE”.
Easy to verify by just looking at the file:

The code is obfuscated, but nothing crazy.
Quick summary:
- Random function names
- Useless variables
- Thousands of Write-Host calls -> pure noise
- Sleep calls -> sandbox evasion
The key function is:
function table_pincenez_bruise()
This acts as a simple XOR/byte decoder.

We also see a lot of [Type]::(...).(...) usage, which is used for:
- .NET reflection
- Dynamically resolving WinAPI
Execution flow looks like this:
- Prepare shellcode
- Decrypt using (($value – $key) % 256)
- Allocate memory (VirtualAlloc)
- Write shellcode
- Start thread
At this point we have everything we need to reconstruct the decoded binary.
I threw together a quick script for that:
from pathlib import Path
import re
import hashlib
import subprocess
input_path = Path(r"Arj")
output_path = Path("decoded_arj.bin")
raw = input_path.read_bytes()
decoded = bytes(((b - 117) % 256) for b in raw)
output_path.write_bytes(decoded)

2a8ea9f1ad8936fb302243faa64b91c5767df411923715cbdb1a869e3bfd7e6d decoded_arj.bin
Nice. Now we have the decoded payload.
Let’s throw it into Ghidra.

FUN_18008e2a0 -> Seed / Cookie Generation
FUN_18008e06c -> Dispatcher

This looks like a main dispatcher. Function FUN_1800189c0 stands out immediately.

The function is full of junk and noise with only a few real control paths hidden inside.
After going through it, a couple of functions are actually relevant. FUN_180001180
FUN_180001180

First it builds a 256-byte table:
local_128[0..255] = 0..255
Then

bVar24 = bVar24 + cVar4 + key[i % keylen]
swap(...)
That’s classic RC4 Key Scheduling (KSA)
It sets up a 256-byte state array (S-box) using the provided key. The algorithm starts with a simple identity permutation (0–255) and then shuffles it based on the key through a series of swaps. The process mixes the key material into the internal state, effectively “seeding” the cipher.
Once KSA is complete, the resulting permutation is used by the PRGA (Pseudo-Random Generation Algorithm) to produce the keystream that will later be XORed with the data
After that:
cVar4 = S[i]
j += cVar4
swap(...)
output = S[(cVar4 + S[i])]
-> That’s RC4 PRGA.
So part of the payload is RC4 encrypted, no surprises here
FUN_1800128a0
The key comes from here:

JtyIQxPND8G
his function also handles setting up and executing the HTTP communication
FUN_180014580
This is the file exfiltration function.
It loads data into memory and sends it to the C2
Chunk-size:
0x21400 = 136192 Bytes (~133 KB)
So data is sent in chunks.
Simple, works.
Sending to C2:

The network layer is most likely HTTP using WinHTTP or something similar.

Options are set, payload prepared, callback configured, request sent
Behavior
Behavior is straightforward:
Initial beacon Environment check If OK -> data exfiltration
To extract the C2 URL, we need to rebuild the RC4 decoding process.
Overall Architecture

Extracting the C2 URL
In the end we obviously want to extract the C2 URL.
For that we basically just need to rebuild the RC4 logic.
For this I jump to the memory region of DAT_1800b2ff2 and use those values to reconstruct the RC4 decoding.


I’ll just drop my script here in case anyone cares, I basically just reconstructed the logic in Python.
Source Code (Click to view)
def rc4_crypt(data: bytes, key: bytes) -> bytes:
# KSA
s = list(range(256))
j = 0
for i in range(256):
j = (j + s[i] + key[i % len(key)]) & 0xFF
s[i], s[j] = s[j], s[i]
# PRGA
i = 0
j = 0
out = bytearray()
for b in data:
i = (i + 1) & 0xFF
j = (j + s[i]) & 0xFF
s[i], s[j] = s[j], s[i]
k = s[(s[i] + s[j]) & 0xFF]
out.append(b ^ k)
return bytes(out)
def run():
encrypted = bytes([
0x8A, 0xE5, 0xBA, 0x1E, 0x2C, 0x19, 0x48, 0xF0,
0x8E, 0x3B, 0xED, 0x22, 0xED, 0xBE, 0xBE, 0x0D,
0xCE, 0x6F, 0x72, 0x81, 0xD2, 0xD7, 0x52, 0xE9,
0xD6, 0x8B, 0x14, 0x0A, 0x9A, 0x33, 0xF9
])
key = b"JtyIQxPND8G"
decrypted = rc4_crypt(encrypted, key)
print("Raw bytes:", decrypted)
print("ASCII:", decrypted.decode("utf-8", errors="replace"))
if __name__ == "__main__":
run()


https://136.0.141[.]138:8406/rcv/
And if I open the URL in the browser:

There’s our candidate.
Certificate looks funny too:

The malware is a GIFTEDCROOK stealer variant used by UAC-0226.
Who is UAC-0226?
UAC-0226 is basically a designation used by Ukrainian CERT for a Russian-aligned threat actor group primarily targeting Ukraine.
Some of the artifacts and the tradecraft found currently and in the past make me also believe that this is a russian (speaking) threat actor, but that’s just me.
Their tradecraft is pretty straightforward “it works, so we use it” operations: a lot of phishing, archives, LNKs, then multi-stage payload chains.
Classic flow is: user clicks something > loader > next stage > eventually you end up with a stealer like GIFTEDCROOK.
What makes this interesting:
They don’t build ultra complex frameworks.
They build simple chains that are just obfuscated enough (RC4, some string garbage, staged decoding) to make analysis annoying, even if this one wasn’t exactly a masterpiece.
It’s not elegant, but effective enough and that’s exactly what makes them relevant for us 🙂
References:
https://cert.gov.ua/article/6282946
https://arcticwolf.com/resources/blog/giftedcrook-strategic-pivot-from-browser-stealer-to-data-exfiltration-platform/
https://malpedia.caad.fkie.fraunhofer.de/actor/uac-0226
https://socprime.com/de/blog/detect-uac-0226-attacks-against-ukraine/
https://netzpalaver.de/2025/06/27/cyberspionage-gruppe-uac-0226-hat-giftedcrook-zu-einem-umfassenden-exfiltrations-tool-ausgebaut/