Triage Insights

OrbitalVoyage: A Fake TikTok App Drops an Android RAT Behind a VPN Black Hole

Blog.

OrbitalVoyage: A Fake TikTok App Drops an Android RAT Behind a VPN Black Hole

A Fake TikTok App That Turns Off the Internet

Recently, we observed a TikTok-themed Android sample, TkTok_Candy.apk, in public Triage submissions. Instead of shipping its RAT directly, it uses a dropper that asks the victim to allow a VPN connection “because of the law”, quietly cuts the whole phone off from the internet, and installs the real payload while Google Play Protect cannot reach the cloud. Once the installation finishes, the VPN disappears and the payload, an Android RAT that we dubbed OrbitalVoyage RAT (named after its package name, com.digitalpearl.orbitalvoyage), starts talking to its C2.

In this blog post, we walk through the infection chain, explain the evasion tricks, and share what we found.

Key Takeaways

Technical Analysis

Targeted Sample Information

Both APKs in the chain require Android 14 or later (minSdkVersion 34).

Stage 1: TkTok_Candy Loader

The TkTok_Candy icon on the home screen

The TkTok_Candy icon on the home screen

TkTok_Candy borrows TikTok’s name, minus one letter, and its music-note icon. Its Application class, org.auction.inject.Gjunglemelody, is only a packer stub. It RC4-decrypts the asset TThhk.html (key Rnto), hidden under 84 nested directories with random names, and loads the real dropper from it.

The container is built to break analysis tools while staying installable on Android:

After the victim opens TkTok_Candy and accepts the VPN prompts, the dropper black-holes all IPv4 and IPv6 traffic through the VPN before it installs the second APK.

Stage 2: VPN Black-Hole Dropper

The dropper, running inside TkTok_Candy, is a small WebView-based app. It loads a local lure page with an adult site in the background and shows two fake dialogs in the device’s language, one of 30, with English as the fallback.

The fake VPN dialog

The fake VPN dialog

The first dialog says that “in your country, the app can only work through a VPN because of the law”. “Allow connection” opens Android’s own VPN consent dialog.

The system VPN consent dialog

The system VPN consent dialog

Once the victim taps OK, the dropper starts a VPN session named “Internet blocked”. It routes every app’s IPv4 and IPv6 connections into the dropper and throws the packets away, so no app, including Play Store, Play services, and the dropper itself, can connect anywhere. The most likely purpose is to keep Google Play Protect from getting a cloud verdict on the payload.

The dropper's VPN setup that routes all IPv4 and IPv6 traffic into the VPN

The dropper’s VPN setup that routes all IPv4 and IPv6 traffic into the VPN

The fake system update dialog

The fake system update dialog

The second dialog announces an update. Tapping “Update” starts the payload install, and Android first asks the victim to allow installs from TkTok_Candy.

Android blocking the unknown source

Android blocking the unknown source

The Install unknown apps settings page

The Install unknown apps settings page

The install does not pass an APK file to Android’s installer app. Instead, the dropper streams the payload into a PackageInstaller session it creates itself. On Android 14, Restricted settings, which by default block Accessibility and notification-listener access for sideloaded apps, apply only when the session is marked as a local or downloaded file, which the system installer does when it opens an APK file. The dropper leaves the source unspecified, so nothing stops the victim from enabling the RAT’s accessibility service.

As soon as the install succeeds, the dropper stops the VPN and launches the payload, while a failed install leaves the device offline.

The dropper also reports three events, open, update_click, and install_success, together with a random six-character device ID, to hxxps://cloudstatesinfo[.]space/event. The dropper only sends data there; it reads the response and discards it.

Stage 3: GoogleUpdateService Loader

The payload asking for notification permission

The payload asking for notification permission

The payload calls itself “GoogleUpdateService”, uses the Google Play services icon, and has no launcher icon. Its container uses the same tricks as Stage 1, and its Application class RC4-decrypts the asset rnG.jpg (key MI) and injects the RAT code into the app’s class loader.

On first launch, it asks for notification permission and opens a Google-styled page that walks the victim through enabling “Update Service” in Accessibility. Settings actually lists the service as “GoogleUpdateService”, and the last system prompt asks to give it full control of the device.

The accessibility lure page

The accessibility lure page

The accessibility settings page

The accessibility settings page

The full control dialog

The full control dialog

Stage 4: OrbitalVoyage RAT

OrbitalVoyage is an Android RAT that uses remote control and overlay phishing as its core capabilities. It requests no SMS, contacts, phone, or notification-listener permission. Almost everything comes from its accessibility service, which listens to every event in every app.

The RAT's accessibility service listens to all events and declares itself an accessibility tool

The RAT’s accessibility service listens to all events and declares itself an accessibility tool

The following are its main capabilities:

The cover screen, rendered from the RAT's duffy_gregor.html asset

The cover screen, rendered from the RAT’s duffy_gregor.html asset

By default, the RAT sends every request as an HTTPS POST to one hardcoded endpoint, and the C2 can push replacement gates, which the RAT reads only when it starts. Every request carries a command name and a five-character bot ID. Except for setKey, the body is XORed with a random 16-byte per-device key and Base64-encoded; setKey sends that key in clear, inside TLS. The C2’s replies are plain JSON.

The full command reference is in the appendix at the end of this post.

During our analysis of OrbitalVoyage, the C2 answered our requests but never returned a single command, so we could not obtain a target list. This may suggest that the RAT has not been widely distributed yet or that the threat actors behind it are still developing it.

Targets

We only have access to the samples submitted to Triage, so we cannot determine how TkTok_Candy is distributed or who has installed it. However, the sample provides some clues. TkTok_Candy is designed to mimic TikTok and uses adult content as a lure, showing an adult website behind its fake dialogs. The lure supports 30 languages, and the lure page’s HTML source contains Russian code comments that mention 50 countries where these languages are spoken. Most of these countries are located in Africa, South and Southeast Asia, the Middle East, and Latin America. The Russian comments may point to a Russian-speaking developer, but language artifacts like these are not enough on their own to attribute the campaign.

Russian code comments in the lure page's translation table

Russian code comments in the lure page’s translation table

The table below lists each lure language and the countries named in its comment, translated from Russian:

Language Countries listed in the comment
Russian (ru) Russia
English (en) United States, United Kingdom, Nigeria, Kenya, Uganda, Ghana, Canada, and others
German (de) Germany
French (fr) France, DR Congo, Cameroon, Madagascar, and others
Spanish (es) Mexico, Colombia, Spain, Argentina, Peru, Venezuela, and others
Portuguese (pt) Brazil, Angola, Mozambique
Turkish (tr) Turkey
Ukrainian (uk) Ukraine
Chinese (zh) China
Hindi (hi) India
Indonesian (id) Indonesia
Urdu (ur) Pakistan
Bengali (bn) Bangladesh
Japanese (ja) Japan
Filipino (fil, tl) Philippines
Arabic (ar) Egypt, Algeria, Sudan, Iraq, Morocco, Saudi Arabia, Yemen, and others
Vietnamese (vi) Vietnam
Persian (fa) Iran
Thai (th) Thailand
Swahili (sw) Tanzania, Kenya, Uganda
Korean (ko) South Korea
Polish (pl) Poland
Malay (ms) Malaysia
Nepali (ne) Nepal
Uzbek (uz) Uzbekistan
Amharic (am) Ethiopia
Burmese (my) Myanmar
Zulu (zu) South Africa
Afrikaans (af) South Africa
Malagasy (mg) Madagascar

Detection

Defenders can look for the following signals:

MITRE ATT&CK Techniques

ID Technique
T1406.002 Obfuscated Files or Information: Software Packing
T1655.001 Masquerading: Match Legitimate Name or Location
T1628.001 Hide Artifacts: Suppress Application Icon
T1629.001 Impair Defenses: Prevent Application Removal
T1629.003 Impair Defenses: Disable or Modify Tools
T1541 Foreground Persistence
T1453 Abuse Accessibility Features
T1516 Input Injection
T1417.002 Input Capture: GUI Input Capture
T1517 Access Notifications
T1513 Screen Capture
T1418 Software Discovery
T1426 System Information Discovery
T1437.001 Application Layer Protocol: Web Protocols
T1521.001 Encrypted Channel: Symmetric Cryptography
T1646 Exfiltration Over C2 Channel

Conclusion

This chain shows how much an Android dropper can do with very little. We see more and more Android malware reach victims this way, through a separate dropper that gets them past the device’s defenses. A TikTok look-alike, two fake dialogs, and a short-lived VPN are likely enough to get a fresh RAT past Play Protect’s install-time cloud check, and a few malformed ZIP structures are enough to make analysis tools fail.

For users, the two system prompts in this chain matter most: the VPN connection request and the accessibility “full control” prompt. Be very careful with both, since few legitimate apps need either.

We will keep tracking this malware as new builds show up in public Triage. Stay tuned and follow our blog posts to get the most out of our sandbox. If you haven’t signed up yet, visit tria.ge to register for a free account!

Indicators of Compromise (IOCs)

Stage 1: TkTok_Candy Loader

SHA256 Artifact
114b24cbaabdc7c31109a9cbb86e2ddfa440e0f579f27c7d4b5406823c69bbbd TkTok_Candy.apk, 260906-vnas7aydma
9840f04b56247571ec504b5fc2a29134d491b339607de076e79a1e86b2d15db4 TThhk.html, RC4-encrypted dropper

Stage 2: VPN Black-Hole Dropper

SHA256 Artifact
12d984ffea337d8ecb56aa0ef8f8f00a37737523f6c1ee77444e409b85c75d5f Decrypted dropper DEX

Stage 3: GoogleUpdateService Loader

SHA256 Artifact
bc2ceabf28a2c601999649e2516005bd2bac301d66c467311d0f958c0e1f66fe GoogleUpdateService APK
6f70d27286c69da3bc1873adaf8f232d30225f1488a6a0598ca2c2e1b70fbb2a rnG.jpg, RC4-encrypted RAT

Stage 4: OrbitalVoyage RAT

SHA256 Artifact
983abb1b88850ab8008e21c1cf4f0fb694b9988c4a57f87f7d37bfd905ac9e3e Decrypted classes.dex

Network

Indicator Type
hxxps://cloudmadrid-nginxservergate[.]com/api OrbitalVoyage C2
hxxps://cloudmadrid-nginxservergate[.]com/api/injects/ Overlay phishing pages
37.143.130[.]206 C2 host
hxxps://cloudstatesinfo[.]space/event Dropper telemetry

Appendix: OrbitalVoyage C2 Commands

Bot to C2

Command Payload Description
setKey key: 16 random bytes, Base64 Registers the per-device XOR key
botAddInfo botTag (hohl3), deviceName, androidVersion, language, appPackages Registers the device and its launcher apps
ping batteryLevel, charging, screen, logList (warnings and errors) Heartbeat; as the only periodic request, it is the bot’s main way to receive commands
setPermissions permissions: ["accessibility"] Reports that accessibility is granted
interceptPush appName, package, title, body Steals notification content
injectData package, data (form JSON) Sends phished input; once the upload goes through, the app leaves the target list

Because ping starts only after accessibility is enabled, the C2 can send commands before that point only in its replies to setKey and botAddInfo.

C2 to Bot

Command Data Description
inject List of package names Adds apps to the persistent phishing target list. When one of them comes to the foreground, the RAT opens <gate>/api/injects/<package> in a WebView.
forceInject Package name Immediately shows <gate>/api/injects/<package> in a full-screen accessibility overlay.
show_overlay value (1 shows, anything else hides); type is ignored Shows or hides the “Installing system update” cover screen. The state is saved, so a visible cover screen comes back after the accessibility service restarts.
show_push title, body, package; icon, icon_body, url, and webview are parsed but unused Posts a fake notification that opens the given app, or the RAT if that app cannot be launched.
open_url url, webview; hidden and count have no effect Opens the URL in an in-app WebView with the send_log_injects bridge (webview = 1), or otherwise hands it to the system with ACTION_VIEW.
sound_off None Sets the media volume to zero.
avnc address Opens the remote-control WebSocket at address.
getGates List of URLs Saves replacement C2 gates; the RAT reads them only when it starts.

Remote-Control Commands

The operator sends these commands over the WebSocket opened by avnc, using the fields start_x, start_y, stop_x, stop_y, to, text, package, and value. Every command also turns the screen on and keeps it on for up to 60 seconds.

# Command Description
1 click Taps at (start_x, start_y) for 100 ms
2 long_click Presses at (start_x, start_y) for 800 ms
3 swipe Swipes from the start to the stop coordinates in 300 ms
4 swipe2 Swipes up, down, left, or right, as given by to, along the middle of the screen from 15% to 85% of its height or width, in 400 ms
5 text Fills the focused input field with text
6 clipboard Writes text to the clipboard
7 back Presses Back
8 home Presses Home
9 recent Opens Recents
10 open Launches the app named in package
11 drawGraph Draws one multi-point gesture from the points in value, such as an unlock pattern
12 sound_off Mutes the media volume
13 overlay_show Shows the cover screen
14 overlay_hide Hides the cover screen
15 vnc Asks the victim for screen capture permission; once granted, screenshots join the stream
16 stop_vnc Stops screen capture
17 toggle_accessibility Turns element-tree streaming on or off
18 toggle_sms Turns blocking of the default SMS app on or off
19 gpSettings Opens Google Play Protect settings
20 optimize Opens the RAT’s own App info page
21 stop Ends the session: closes the socket, stops streaming and screen capture, and hides the cover screen

You may also like: