{
	"id": "0f8ff13b-4af3-42c7-bc5d-4f199d95d61f",
	"created_at": "2026-04-06T00:09:09.219517Z",
	"updated_at": "2026-04-10T13:11:34.872869Z",
	"deleted_at": null,
	"sha1_hash": "0ef01d1394c5a22835e6ed1b224d3a1bba985b91",
	"title": "PHA Family Highlights: Triada",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 458595,
	"plain_text": "PHA Family Highlights: Triada\r\nBy Posted by Lukasz Siewierski, Android Security \u0026 Privacy Team\r\nPublished: 2019-06-06 · Archived: 2026-04-02 10:36:37 UTC\r\nWe continue our PHA family highlights series with the Triada family, which was first discovered early in 2016.\r\nThe main purpose of Triada apps was to install spam apps on a device that displays ads. The creators of Triada\r\ncollected revenue from the ads displayed by the spam apps. The methods Triada used were complex and unusual\r\nfor these types of apps. Triada apps started as rooting trojans, but as Google Play Protect strengthened defenses\r\nagainst rooting exploits, Triada apps were forced to adapt, progressing to a system image backdoor. However,\r\nthanks to OEM cooperation and our outreach efforts, OEMs prepared system images with security updates that\r\nremoved the Triada infection.\r\nHistory of Triada\r\nTriada was first described in a blog post on the Kaspersky Lab website in March 2016 and in a follow-up blog\r\npost in June 2016. Back then, it was a rooting trojan that tried to exploit the device and after getting elevated\r\nprivileges, it performed a host of different actions. To hide these actions from analysts, Triada used a combination\r\nof dynamic code loading and additional app installs. The Kaspersky posts detail the code injection technique used\r\nby Triada and provide some statistics on infected devices at the time. In this post, we’ll focus on the peculiar\r\nencryption routine and the unusual binary files used by Triada. Triada’s first action was to install a type of\r\nsuperuser (su) binary file. This su binary allowed other apps on the device to use root permissions. The su binary\r\nused by Triada required a password, so was unique compared to regular su binary files common with other Linux\r\nsystems. The binary accepted two passwords, od2gf04pd9 and ac32dorbdq . This is illustrated in the IDA\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 1 of 10\n\nscreenshot below. Depending on which one was provided, the binary either 1) ran the command given as an\r\nargument as root or 2) concatenated all of the arguments, ran that concatenation preceded by sh , then ran them\r\nas root. Either way, the app had to know the correct password to run the command as root. This Triada rooting\r\ntrojan was mainly used to install apps and display ads. This trojan targeted older devices because the rooting\r\nexploits didn’t work on newer ones. Therefore, the trojan implemented a weight watching feature to decide if old\r\napps needed to be deleted to make space for new installs. Weight watching included several steps and attempted to\r\nfree up space on the device’s user partition and system partition. Using a blacklist and whitelist of apps it first\r\nremoved all the apps on its blacklist. If more free space was required it would remove all other apps leaving only\r\nthe apps on the whitelist. This process freed space while ensuring the apps needed for the phone to function\r\nproperly were not removed. Every app on the system partition had a number, or weight, associated with it. The\r\nweight was a sum of the number of apps installed on the same date as the app in question and the number of apps\r\nsigned with the same certificate. The apps with the lowest weight were installed in isolation (that is, not on a day\r\nthat the device system image was created) and weren’t signed by the OEM or weren’t part of a developer bundle.\r\nIn the weight watching process, these apps were removed first, until enough space was made for the new app.\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 2 of 10\n\nsu binary accepts two passwords\r\nIn addition to installing apps that display ads, Triada injected code into four web browsers: AOSP\r\n( com.android.browser ), 360 Secure ( com.qihoo.browser ), Cheetah (com.ijinshan.browser_fast), and Oupeng\r\n( com.oupeng.browser ). The code was injected using the same technique described in our blog post about the Zen\r\nPHA family and in previously mentioned Kaspersky blog posts. The web browser injection was done to overwrite\r\nthe URLs and substitute ad banners on websites with ads benefiting the Triada authors. Triada also used a peculiar\r\nand complex communication encryption routine. Whenever it had to send a request to the Command and Control\r\n(C\u0026C) server, it encrypted the request using two XOR loops with different passwords. Because of XOR rules, if\r\nthe passwords had the same character in the same position, those characters weren’t encrypted. The encrypted\r\nrequest was saved to a file, which had the same name as its size. Finally, the file was zipped and sent to the C\u0026C\r\nserver in the POST request body. The example below illustrates one such request. The yellow bytes are the zip\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 3 of 10\n\nfile’s signature of the central directory file header. The red bytes show the uncompressed file size of 0x0952. The\r\nblue bytes show the file name length (4) and the name itself (2386, a decimal version of 0x0952).\r\n09 00 00 50 4B 01 02 14 00 14 00 08 00 08 00 4F ...PK..........O\r\n91 F3 48 AE CF 91 D5 B1 04 00 00 52 09 00 00 04 ..H........R....\r\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................\r\n00 32 33 38 36 50 4B 05 06 00 00 00 00 01 00 01 .2386PK.........\r\n00 32 00 00 00 E3 04 00 00 00 00 .2.........\r\nThe underlying data protocol changed periodically. It was either a simple JSON, a list of key-value pairs similar to\r\nthe properties file, or a proprietary format as shown below.\r\n[collect_Head]device=Nexus 5X\r\n[collect_Space]xadevicekey=xxxxx\r\n…\r\n[collect_Space]collentmod=opappresultmode\r\n[collect_Space]registerUser=true\r\n[collect_End]\r\nWhen Triada was discovered, we implemented detection that removed Triada samples from all devices with\r\nGoogle Play Protect. This implementation, combined with the increased security on newer Android devices, made\r\nit significantly harder for Triada to infect devices.\r\nWhen rooting doesn’t work…\r\nDuring the summer of 2017 we noticed a change in new Triada samples. Instead of rooting the device to obtain\r\nelevating privileges, Triada evolved to become a pre-installed Android framework backdoor. The changes to\r\nTriada included an additional call in the Android framework log function, demonstrated below with a highlighted\r\nconfiguration string.\r\nLABEL+13:\r\n V18 = -1;\r\nLABEL_18:\r\n j___config_log_println(v7, v6, v10, v11, \"cf89450001\");\r\n if ( v10 )\r\nThis backdoored log function version of Triada was first described by Dr.Web in July 2017. The blog post\r\nincludes a description of Triada code injection methods. By backdooring the log function, the additional code\r\nexecutes every time the log method is called (that is, every time any app on the phone tries to log something).\r\nThese log attempts happen many times per second, so the additional code is running non-stop. The additional code\r\nalso executes in the context of the app logging a message, so Triada can execute code in any app context. The\r\ncode injection framework in early versions of Triada worked on Android releases prior to Marshmallow. The main\r\npurpose of the backdoor function was to execute code in another app’s context. The backdoor attempts to execute\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 4 of 10\n\nadditional code every time the app needs to log something. Triada developers created a new file format, which we\r\ncalled MMD, based on the file header. The MMD format was an encrypted version of a DEX file, which was then\r\nexecuted in the app context. The encryption algorithm was a double XOR loop with two different passwords. The\r\nformat is illustrated below.\r\nEach MMD file had a specific file name of the format \u003cMD5 of the process name\u003e36.jmd . By using the MD5 of\r\nthe process name, the Triada authors tried to obscure the injection target. However, the pool of all available\r\nprocess names is fairly small, so this hash was easily reversible. We identified two code injection targets:\r\ncom.android.systemui (the System UI app) and com.android.vending (the Google Play app). The first target\r\nwas injected to get the GET_REAL_TASKS permission. This is a signature-level permission, which means that it\r\ncan’t be held by ordinary Android apps. Starting with Android Lollipop, the getRecentTasks() method is\r\ndeprecated to protect users' privacy. However, apps holding the GET_REAL_TASKS permission can get the result of\r\nthis method call. To hold the GET_REAL_TASKS permission, an app has to be signed with a specific certificate, the\r\ndevice’s platform cert, which is held by the OEM. Triada didn’t have access to this cert. Instead it executed\r\nadditional code in the System UI app, which has the GET_REAL_TASKS permission. The injected code returned the\r\napp running on top (the activity running in the foreground and being actively used by the device user) to other\r\napps on the device. This app was exposed using two methods: an intent or a socket created for this purpose. When\r\nan app on the device sent the intent or wrote to a socket created by Triada’s code injection, it received the package\r\nname of the app running on top. Triada used the package name to determine if an ad was displayed. The\r\nassumption was that if the app running on top was a browser, the user would expect to see some ads, so Triada\r\ndisplayed ads from the background. The second injection target was the Google Play app. This injection supported\r\nfive commands and responses to them. The supported commands are shown below in Chinese, a language that\r\nwas used throughout the Triada app and injection. English translations are given on the right.\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 5 of 10\n\n1. 下载请求\r\n2. 下载结果\r\n3. 安装请求\r\n4. 安装结果\r\n5. 激活请求\r\n6. 激活结果\r\n7. 拉活请求\r\n8. 拉活结果\r\n9. 卸载请求\r\n10. 卸载结果\r\n1. download request\r\n2. download result\r\n3. install request\r\n4. installation result\r\n5. activation request\r\n6. activation result\r\n7. pull request\r\n8. pull the results\r\n9. uninstall request\r\n10. uninstall result\r\nThe commands trigger the heartbeat (pull request), download, installation, uninstallation (in the Google Play app\r\ncontext), and activation (the first execution) of the apps. In the Google Play app context, installation meant that\r\nTriada didn’t have to turn on installation from unknown sources and all app installs looked like they were from\r\nGoogle Play. The apps were downloaded from the C\u0026C server and the communication with the C\u0026C was\r\nencrypted using the same custom encryption routine using double XOR and zip. The downloaded and installed\r\napps used the package names of unpopular apps available on Google Play. They didn’t have any relation to the\r\napps on Google Play apart from the same package name. The last piece of the puzzle was the way the backdoor in\r\nthe log function communicated with the installed apps. This communication prompted the investigation: the\r\nchange in Triada behavior mentioned at the beginning of this section made it appear that there was another\r\ncomponent on the system image. The apps could communicate with the Triada backdoor by logging a line with a\r\nspecific predefined tag and message. The reverse communication was more complicated. The backdoor used Java\r\nproperties to relay a message to the app. These properties were key-value pairs similar to Android system\r\nproperties, but they were scoped to a specific process. Setting one of these properties in one app context ensures\r\nthat other apps won’t see this property. Despite that, some versions of Triada indiscriminately created the\r\nproperties in every single app process. The diagram below illustrates the communication mechanisms of the\r\nTriada backdoor.\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 6 of 10\n\nCommunication mechanisms of Triada\r\nReverse engineering countermeasures and development\r\nThe Triada backdoor was hidden to make the analysis harder. The strings in the Android framework library that\r\nrelated to Triada activities were encrypted, as shown below.\r\nAndroid framework strings\r\nThe strings were encrypted using the algorithm of two XOR loops. However, the first highlighted string, 36.jmd ,\r\nwasn’t encrypted. This is the MMD file name string mentioned before. Another anti-analysis measure\r\nimplemented by the Triada authors was function padding, including additional exported functions that don't serve\r\nany purpose apart from making the file size bigger and the function layout more random with every compilation.\r\nFour types of these functions are shown in the screenshots below.\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 7 of 10\n\nExample of function padding\r\nOne final interesting feature of Triada worth mentioning is the development cycle. By analyzing subsequent\r\nversions of the Triada backdoor (up to 1.5.1) we saw the changes in the code. In the newest version, they\r\nsubstituted MD5 with SHA1. This is used to hash the filenames, which come from a restricted pool of values. The\r\nnewest version also encrypted the 36.jmd string and introduced changes to the code for compatibility with\r\nAndroid Nougat. There are also code stubs pointing at the modification of the SystemUI and WebView Android\r\nframework elements. We couldn’t find the code that was executed by these modifications, just code stubs\r\nsuggesting more development in the future.\r\nOEM outreach\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 8 of 10\n\nTriada infects device system images through a third-party during the production process. Sometimes OEMs want\r\nto include features that aren’t part of the Android Open Source Project, such as face unlock. The OEM might\r\npartner with a third-party that can develop the desired feature and send the whole system image to that vendor for\r\ndevelopment. Based on analysis, we believe that a vendor using the name Yehuo or Blazefire infected the returned\r\nsystem image with Triada.\r\nProduction process with malicious party\r\nWe coordinated with the affected OEMs to provide system updates and remove traces of Triada. We also scan for\r\nTriada and similar threats on all Android devices. OEMs should ensure that all third-party code is reviewed and\r\ncan be tracked to its source. Additionally, any functionality added to the system image should only support\r\nrequested features. It’s a good practice to perform a security review of a system image after adding third-party\r\ncode.\r\nSummary\r\nTriada was inconspicuously included in the system image as third-party code for additional features requested by\r\nthe OEMs. This highlights the need for thorough ongoing security reviews of system images before the device is\r\nsold to the users as well as any time they get updated over-the-air (OTA). By working with the OEMs and\r\nsupplying them with instructions for removing the threat from devices, we reduced the spread of preinstalled\r\nTriada variants and removed infections from the devices through the OTA updates. The Triada case is a good\r\nexample of how Android malware authors are becoming more adept. This case also shows that it’s harder to infect\r\nAndroid devices, especially if the malware author requires privilege elevation. We are also performing a security\r\nreview of system images through the Build Test Suite. You can read more about this program in the Android\r\nSecurity 2018 Year in Review report. Triada indicators of compromise are one of many signatures included in the\r\nsystem image scan. Additionally, Google Play Protect continues to track and remove any known versions of Triada\r\nand Triada-related apps it detects from user devices.\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 9 of 10\n\nSource: https://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nhttps://security.googleblog.com/2019/06/pha-family-highlights-triada.html\r\nPage 10 of 10",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE",
		"Malpedia"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://security.googleblog.com/2019/06/pha-family-highlights-triada.html"
	],
	"report_names": [
		"pha-family-highlights-triada.html"
	],
	"threat_actors": [
		{
			"id": "75108fc1-7f6a-450e-b024-10284f3f62bb",
			"created_at": "2024-11-01T02:00:52.756877Z",
			"updated_at": "2026-04-10T02:00:05.273746Z",
			"deleted_at": null,
			"main_name": "Play",
			"aliases": null,
			"source_name": "MITRE:Play",
			"tools": [
				"Nltest",
				"AdFind",
				"PsExec",
				"Wevtutil",
				"Cobalt Strike",
				"Playcrypt",
				"Mimikatz"
			],
			"source_id": "MITRE",
			"reports": null
		}
	],
	"ts_created_at": 1775434149,
	"ts_updated_at": 1775826694,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/0ef01d1394c5a22835e6ed1b224d3a1bba985b91.pdf",
		"text": "https://archive.orkl.eu/0ef01d1394c5a22835e6ed1b224d3a1bba985b91.txt",
		"img": "https://archive.orkl.eu/0ef01d1394c5a22835e6ed1b224d3a1bba985b91.jpg"
	}
}