TL;DR Link to heading
After discovering two zero-day vulnerabilities in Neverness to Everness (NTE), I wanted to see if those vulnerabilities were isolated to that game or could be found elsewhere. Since Tower of Fantasy is another relatively well-known game developed by Hotta Studio, I dug into this game and found the exact same issues: unencrypted authentication and no replay protection.
By combining these vulnerabilities, an attacker could capture another player’s authentication data, replay it from any system, and ultimately play Tower of Fantasy as a compromised user at any time.
This post walks through the exploit chain from initial discovery to full account takeover. The exploit code is located here on my Github.
These findings were responsibly disclosed to Perfect World / Hotta Studio. At the time of publication, both vulnerabilities remain unpatched and CVE assignments are pending:
- CVE Pending: Cleartext Transmission of Sensitive Information (CWE-319)
- CVE Pending: Insufficient Session Expiration (CWE-613)
Introduction Link to heading
Tower of Fantasy is an open-world MMO game developed by Hotta Studio and published under Perfect World Games. It initially released mid 2022, and I remember it was a notable competitor against Genshin Impact for a bit.

I’ve never played it before, but after discovering two zero-day vulnerabilities in Neverness to Everness (NTE), Hotta Studio’s newest game at the time of writing, I was curious if Tower of Fantasy had similar issues. When a publisher ships multiple titles built by the same studio, it’s reasonable to assume that they may share underlying patterns of implementation and, as a result, might share vulnerabilities!
Unencrypted Authentication (CWE-319) Link to heading
I downloaded Tower of Fantasy, created an account, and played with Wireshark running in the background. After a few minutes, I closed the game and analyzed the network capture.
From my initial NTE research, authentication took place over port 30031 so that’s the first thing I looked at. Following the TCP stream, I saw some very similar plaintext pieces floating around! Some things looked like a UUID, an authentication token, various keys, my public IP address, and more, all of which was unencrypted.

Like NTE, Tower of Fantasy uses Google FlatBuffers, a lightweight, zero-copy binary serialization format commonly used in game development for its performance in latency-sensitive applications. Conveniently, I was able to reuse my existing FlatBuffer tool to decode the structure:
python3 poc.py decode 140000000000000000000a000c...

Here are the fields I found to be relevant:
- User ID (
0x009c): A unique user identifier used specifically for authentication, separate from the “friend code” used for adding people in-game. - User Secret (
0x00ac): A UUID-format credential transmitted every time a client logs in. Not rotated between sessions unless the user fully logs out and back in. - Hardware ID (
0x010c): An identifier assigned at installation and appears to be for other Hotta/Perfect World games, but doesn’t seem relevant for authentication.
With the data structure decoded and plaintext authentication verified, I moved on to abusing the authentication process, specifically targeting the User ID and User Secret.
To obtain a victim’s authentication data, I performed a man-in-the-middle attack via ARP spoofing. By poisoning the ARP cache of machines on my local network, I can sit between victims and their gateway and passively capture authentication data as they log in. For this proof of concept, I focused on a single target host:
python3 poc.py mitm -i eth0 -t 10.0.1.17 -g 10.0.1.1
After logging into Tower of Fantasy on my victim machine, the authentication data is successfully captured and decoded:


Replay Attack (CWE-613) Link to heading
Capturing a victim’s authentication data is only useful if it can actually be replayed! Tower of Fantasy’s replay flow is nearly identical to NTE’s - the client sends a fixed preamble (50010000), the server responds with a FlatBuffers challenge, and then I can send the captured credentials:
python3 poc.py auth 140000000000000000000a000c...

With that, I want to see if I can play as a compromised account now!
Session Injection Link to heading
Network Injection Link to heading
My first attempt was the same network-level approach that worked for NTE, which involved intercepting the connection and swapping the authentication fields in transit.
python3 poc.py proxy -i eth0 -t 10.0.1.17 -g 10.0.1.1 0x009c=COMPROMISED-USER-ID 0x00ac=COMPROMISED-USER-SECRET
The server accepted the injected credentials and the victim got signed out, but after a few seconds, the session dropped and I was kicked back to the login screen.

After digging into this, I found that Tower of Fantasy’s game client makes a second outbound HTTPS connection alongside the one we captured over port 30031. Since the second connection is TLS encrypted, I cannot tamper with it, so the mismatch between the two channels kicks me out despite authentication working and the victim being signed out.
Local Credential Injection Link to heading
Since I cannot tamper with TLS-encrypted connections from the outside, I needed to find a way to make my attacker’s launcher authenticate as the victim through both connections.
The Tower of Fantasy launcher stores saved login credentials locally in HottaLaunch/UserData/cache/player_v3_0. In this file, each saved account is one line of hex. The launcher also stores usernames/emails per account UID in HottaLaunch/UserData/Config/Config.ini under the [User] section.

When I was previously digging into NTE’s local files, I already knew that Hotta Studio games use DPAPI with a hardcoded entropy value for local credential storage. The entropy key gfsa97538uoxdrwp is baked into the game executable and shared between NTE and Tower of Fantasy. On top of DPAPI, each byte is XOR’d with 0xAB and shifted by one.
# deobfuscation (file -> DPAPI blob)
decrypted = bytes(((b ^ 0xAB) - 1) & 0xFF for b in data)
# obfuscation (DPAPI blob -> file)
encrypted = bytes(((b + 1) ^ 0xAB) & 0xFF for b in data)
After decrypting everything, the plaintext is a hex-encoded string of various fields:
- Secret: The user’s UUID-format authentication secret
- UID: The account’s user ID, repeated across three fields
- Flags: 4 bytes of metadata that I didn’t dig into
- Timestamp: The date and time the session was last saved

With this in mind, I can basically generate my own launcher session entries using the authentication data I captured from network poisoning!
inject.ps1 140000000000000000000a000c... <TowerOfFantasy_Global_location>

The script parses the captured FlatBuffers blob to extract the secret and UID, builds the hex-encoded plaintext, encrypts it with DPAPI using the known entropy key, XORs it, and appends the result to player_v3_0. Since we didn’t capture an email, I just had it write a dummy email entry to Config.ini so the account shows up in the launcher’s account switcher.

Simply clicking on the account successfully authenticates as the victim!

With that, we now have a working exploit chain for hijacking Tower of Fantasy accounts! In abusing two vulnerabilities at the network layer, we can use that information to insert compromised accounts right into the game launcher. :)
Disclosure Timeline Link to heading
- June 25th, 2026 - Reached out to [email protected] and disclosed vulnerabilities along with proof-of-concept material
- June 26th, 2026 - ToF acknowledges, thanks for the disclosure, and confirms the team is already working on a fix, closed ticket
- June 30th, 2026 - Self-submitted two CVE requests to MITRE
- October 4th, 2026 - Followed up with details that vulnerabilities are still present, asked for avenues for pursuing CVEs
- October 5th, 2026 - ToF acknowledges and expresses thanks for the disclosure, closed ticket
- October 13th, 2026 - Notified vendor of intent to publicly disclose findings on November 6th, 2026
- October 14th, 2026 - ToF acknowledges, says referred to team for further verification, no update available, closed ticket
- November 6th, 2026 - Published POC and blogpost
Conclusion Link to heading
This was a fun experiment and lesson related to shared infrastructure. If one game has a certain vulnerability, the likelihood that it’s shared across games developed by the same group is decently high. With that, here are the CVEs from Tower of Fantasy pending assignment:
- CVE Pending: Cleartext Transmission of Sensitive Information (CWE-319)
- CVE Pending: Insufficient Session Expiration (CWE-613)
The proof of concept tooling has been pushed to my GitHub. Additionally, make sure to read my NTE blogpost, as that research is what led to this discovery. :)