{
	"id": "f96d6d6a-6f1a-4a84-ae5d-b23fb2388e14",
	"created_at": "2026-04-06T00:21:12.961886Z",
	"updated_at": "2026-04-10T03:25:07.653955Z",
	"deleted_at": null,
	"sha1_hash": "1b3adbbf4c8d0487a0167c646f6887c1b456934f",
	"title": "How to catch a wild triangle",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 339543,
	"plain_text": "How to catch a wild triangle\r\nBy Leonid Bezvershenko\r\nPublished: 2023-10-26 · Archived: 2026-04-05 15:44:28 UTC\r\nUPD 23.04.2025: MITRE created a page for Operation Triangulation as part of its ATT\u0026CK framework.\r\nIn the beginning of 2023, thanks to our Kaspersky Unified Monitoring and Analysis Platform (KUMA) SIEM system, we\r\nnoticed suspicious network activity that turned out to be an ongoing attack targeting the iPhones and iPads of our colleagues.\r\nThe moment we understood that there was a clear pattern in the connections, and that the devices could have been infected,\r\nwe initiated a standard digital forensics and incident response (DFIR) protocol for such cases – moving around the office,\r\ncollecting the devices, and inspecting their contents. The ultimate goal was to locate and extract the malware, to find the\r\npoint of entry (hopefully, a 0-day) and to develop a protocol for scanning the iDevices for active infection. That process\r\nturned into a months-long journey, and in this article we would like to summarize it.\r\nFirst steps\r\nAs we mentioned in the very first article on Operation Triangulation, the infected devices that we knew about initially,\r\nbelonged to Kaspersky employees who worked in the HQ office in Moscow. All these devices were connected to the\r\ncorporate Wi-Fi network, which allowed us to record and inspect the network traffic. After spending some time investigating\r\nwith Wireshark, eventually we found the following:\r\nRight before exhibiting the suspicious behavior, the devices connected to the iMessage servers usually responsible\r\nfor receiving messages and downloading attachments;\r\nAfter downloading a few kilobytes of data that could have been an attachment, the devices established a connection\r\nto the server backuprabbit[.]com, exchanging data with it over a course of less than a minute;\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 1 of 9\n\nNext, the devices connected to one of the following servers for a longer session:\r\ncloudsponcer[.]com\r\nsnoweeanalytics[.]com\r\ntopographyupdates[.]com\r\nunlimitedteacup[.]com\r\nvirtuallaughing[.]com\r\nOnce the device rebooted, all the suspicious activity stopped.\r\nUnfortunately for us, all the communications with the servers in question happened over HTTPS, so we could not recover\r\nany additional details from the traffic.\r\nDevice imaging\r\nSince all the devices were literally within hand’s reach, the obvious next step for us was to inspect their contents.\r\nUnfortunately, it was a dead end: available forensics acquisition software at the time of research was based on checkra1n\r\nand similar exploits that did not work for modern processors running iOS 15 and 16.\r\nExamining backups\r\nWhat we decided to do next was to use iTunes backups of the devices as a substitute for complete device images. This\r\nprocedure had to be done with a fair amount of secrecy so as not to scare away the attackers. Since we did not know about\r\nthe exact capabilities of the attackers, we assumed that they were listening to the microphones of the devices, and reading e-mail messages and messenger conversations. So, we had to arrange our meetings in person, putting the phones in airplane\r\nmode, and sometimes in Faraday bags. We used the excellent tooling from libimobiledevice to acquire the backups, and\r\ninspected them by building a timeline of events with the Mobile Verification Toolkit.\r\nSuch a timeline combines file system timestamps with data records extracted from various system databases. We focused on\r\nthe iMessage directories, because we knew that in 2021 Citizen Lab researchers had found the infamous FORCEDENTRY\r\nexploit by examining files in these directories. We wanted our analysis to be as fruitful – but the attackers behind Operation\r\nTriangulation turned out to be very stealthy, and we found no signs of exploits in the backup. We also searched it for\r\nmalware executables, which we were also unable to find.\r\nWe had additional data points – the timestamps of suspicious network activity. So, we started to look for any repeating\r\nevents in the timeline that happened around the same time. As a result, we found what looked like a new indicator: data\r\nusage records mentioning a system process named “BackupAgent” that should not have been executed at all – the binary\r\nwas deprecated years before.\r\n2022-09-13 10:04:11.890351Z Datausage\r\nIMTransferAgent/com.apple.datausage.messages (Bundle ID:\r\ncom.apple.datausage.messages, ID: 127) WIFI IN: 0.0, WIFI OUT:\r\n0.0 - WWAN IN: 76281896.0, WWAN OUT: 100956502.0\r\n2022-09-13 10:04:54.000000Z Manifest\r\nLibrary/SMS/Attachments/65/05 - MediaDomain\r\n2022-09-13 10:05:14.744570Z Datausage BackupAgent (Bundle ID: ,\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 2 of 9\n\nID: 710) WIFI IN: 0.0, WIFI OUT: 0.0 - WWAN IN: 734459.0, WWAN\r\nOUT: 287912.0\r\nAnomalous activity from the BackupAgent process observed in device logs – we shared this snippet in our first post on\r\nOperation Triangulation\r\nBased on the discovery of this anomaly, we wrote a first version of our triangle_check tool. It allowed us to quickly confirm\r\nif a device’s backup contained traces of potential compromise.\r\nTrying to intercept the malicious iMessage\r\nLooking further into the event timelines, we discovered a second weak indicator: modification of an empty SMS attachment\r\ndirectory (one or several) prior to data usage by the BackupAgent process. Since the directory is modified but contains no\r\nfiles, that usually means with a high degree of confidence that the last operation was file deletion: there was an incoming\r\nattachment, it gets deleted, and seconds later a process named BackupAgent is running suspicious networking code.\r\nSince the threat actor behind Operation Triangulation seemed to be smart enough to remove the malicious attachment from\r\ninfected devices, we decided to try and capture the incoming message during the iMessage delivery process. While looking\r\nfor a way to intercept iMessages, we found this Frida script coded by the Google Project Zero team. IIt is designed to run on\r\na Mac, so we took a spare Mac mini and installed Frida on it. We then asked several of our targeted colleagues to log in to\r\nthat Mac mini with their Apple IDs. By doing that, we were able to monitor and intercept iMessages that they received. All\r\nthat we needed to do next was to wait until the attackers infected one of our colleagues’ devices once again.\r\nAt the same time, we were actively monitoring SIEM logs for the traces of suspicious activity. Soon enough, we detected\r\nfamiliar network connections that indicated a successful compromise of a phone with a “cloned” iMessage account.\r\nHowever, when we checked the iMessage interception logs on the Mac mini, we identified no traces of messages at the time\r\nof infection. Thus, our system was failing to capture the message that could have contained an exploit (we still don’t know\r\nwhy it didn’t work), so we started to seek other ways of capturing the malware.\r\nGood old MITM\r\nAfter the plan to intercept iMessages through a Mac device failed, we decided to try to decrypt HTTPS communications\r\nwith the C2 servers previously identified from traffic analysis.\r\nTo do that, we:\r\nSet up a Linux server and installed mitmproxy, an HTTPS interception tool;\r\nInstalled a root SSL certificate (that we previously generated through mitmproxy) on several iOS devices that were\r\nknown to be compromised before;\r\nInstalled a Wireguard VPN client on these devices and configured them to use our mitmproxy instance as a VPN\r\nserver.\r\nWe also developed a Telegram bot that would notify us whenever one of the monitored devices became infected:\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 3 of 9\n\nUnfortunately, this method did not allow us to intercept HTTPS traffic of Apple services (including iMessage), as iOS\r\nimplements SSL pinning for this. Thus, we were not able to decrypt iMessage traffic that came through the VPN.\r\nCatching the JavaScript validator\r\nOnce the attackers reinfected one of the targets, we looked in the mitmproxy logs, noticing that it managed to decrypt the C2\r\nserver traffic:\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 4 of 9\n\nWe expected the payload that we managed to obtain to be an exploit for iOS. However, it was the JavaScript validator,\r\nwhich simply collected information about the victim browser and sent it to the C2 server.\r\nWe observed the infected device receiving a payload in response to the validation information sent to the C2 server.\r\nHowever, while we were able to intercept HTTPS traffic, we could not decrypt it. That is because the JS validator\r\nimplements its own layer of encryption for C2 communications, using the NaCl library. The encryption algorithm used is\r\nbased on public-key cryptography. Specifically, to communicate with the C2 server, the JS validator:\r\nGenerates a random key pair (consisting of private and public key);\r\nDerives a shared key from the generated private key and the C2 server’s public key;\r\nUses this shared key to encrypt messages sent to the C2 server and decrypt the ones received from it.\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\nvar o = function (t) {\r\n          function u(t) {\r\n            this.D = nacl.box.keyPair();\r\n            this.L = nacl.box.before(t, this.D.secretKey);\r\n          }\r\n          u.prototype.encrypt = function (t, nonce) {\r\n            return nacl.box.after(t, nonce, this.L);\r\n          };\r\n          u.prototype.decrypt = function (ciphertext, nonce) {\r\n            return nacl.box.open.after(ciphertext, nonce, this.L);\r\n          };\r\n          u.prototype.N = function () {\r\n            return nacl.randomBytes(nacl.box.nonceLength);\r\n          };\r\n          u.prototype.k = function () {\r\n            return this.D.publicKey;\r\n          };\r\n          return new u(t);\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 5 of 9\n\n19         }(h.values.R)\r\nKey generation\r\nIn order to decrypt the C2 server communications, it is necessary to know the private key that was randomly generated by\r\nthe JS validator. However, this key is kept in memory and is not sent to the C2 server. And so we had to do some additional\r\nwork in order to decrypt the validator’s traffic.\r\nAs demonstrated by the screenshot above, the JS validator generates a random key pair by invoking the nacl.box.keyPair()\r\nmethod. To decrypt the traffic, we decided to compromise the process of generating this keypair. Specifically, we coded a\r\ntiny mitmproxy add-on that looked for invocations of the nacl.box.keyPair() method. It then replaced them with another\r\nmethod, nacl.box.keyPair.fromSecretKey(), that initializes a keypair from a provided private key.\r\nAs can be observed from the screenshot above, we hardcoded a private key into the arguments of this method, thus\r\nbackdooring the validator’s encryption scheme. Once the backdoored validator was executed on the infected device, it\r\nbecame possible to decrypt all communications of the JS validator.\r\nThe binary validator and the hint about the attachment\r\nOnce the attackers reinfected one of their targets, we were able to analyze the payload further executed by the validator. It\r\nturned out to contain two exploits: one for WebKit and the other for the iOS kernel. The ultimate goal of those two exploits\r\nis to launch the binary validator stage on the target device.\r\nAs we described in the article covering the stealth of Operation Triangulation, the binary validator contains a function that\r\ncleans up traces of the malicious iMessage. Specifically, we found out that this function makes the following SQL request to\r\nthe SMS.db database:\r\nSELECT guid FROM attachment WHERE uti == \"com.apple.watchface\" AND\r\nLENGTH(transfer_name) \u003e 32 AND INSTR(transfer_name, CHAR(0x2013)) == 9;\r\nThe condition ‘uti == “com.apple.watchface”‘ gave us another hint: now it became clear that the malicious attachment is a\r\n.watchface file. And so, with a little bit more information about the attachment, we planned and executed the next attempt to\r\nobtain it.\r\nExploring iMessage\r\nIn order to devise another strategy for getting the attachment, we decided to study the process of sending iMessage\r\nattachments in greater detail. It turns out that this process consists of the following steps:\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 6 of 9\n\n1. 1 The sender generates a random AES key and encrypts the attachment with it;\r\n2. 2 The encrypted attachment is uploaded to iCloud;\r\n3. 3 The iCloud link to the encrypted attachment is sent to the recipient along with the AES key, which is additionally\r\nencrypted using the device’s public RSA key.\r\nThus, to obtain the malicious attachment file, we had to retrieve two components:\r\nThe ciphertext of the attachment;\r\nThe AES key used to encrypt it.\r\nIt is quite straightforward to obtain the attachment ciphertext, as it is possible to intercept traffic to the iCloud servers\r\nthrough mitmproxy. Previously, we wrote that iOS does not allow decryption of HTTPS traffic of Apple services. However,\r\niCloud attachment links turned out to be an exception to that rule.\r\nAt the same time, the process of obtaining the AES key is quite difficult. It is sent over the iMessage protocol and so cannot\r\nbe intercepted through mitmproxy. However, we discovered a way to recover the attachment and to extract this key using\r\nphysical access to the target device. We prevented iMessage from successfully downloading the attachment using the iCloud\r\nlink, so that the exploit would not activate and then delete the attachment, but the AES encryption key would be stored in the\r\nSMS.db database.\r\nWe decided to change a few bytes in the attachment ciphertext using our mitmproxy add-on. By doing that, we disrupted the\r\nprocess of downloading the attachment ciphertext, and the decryption key was kept in the SMS.db database. We then\r\ndownloaded an iTunes backup of the infected device and extracted the key from the database inside this backup. As a result,\r\nwe obtained the malicious .watchface attachment sent by the attackers – that was the beginning of the exploit chain used to\r\ncompromise the devices.\r\nGetting the implant\r\nAfter we finished obtaining the exploits used by the attackers, all that remained was to get the implant itself. The binary\r\nvalidator is the component responsible for downloading and activating the implant payload from the C2 server, and it uses a\r\ncombination of RSA and AES for communications. Again, the use of RSA means that it is impossible to decrypt the implant\r\npayload by having only the ciphertext traffic.\r\nTo exchange data with the C2 server, the binary validator:\r\nGenerates a random AES key;\r\nEncrypts the generated AES key with the server’s RSA public key specified in the validator’s configuration;\r\nEncrypts the message to be sent to the C2 server with the generated AES key;\r\nSends the encrypted message to the C2 server and receives a response from it;\r\nDecrypts the response with the same AES key used for encrypting the sent message.\r\nThe validator encrypts all the packets with the following ARM instructions:\r\nE0 03 18 AA                           MOV           X0, X24\r\n2E 1A 00 94                           BL            serialize_plist\r\nA0 04 00 B4                           CBZ           X0, loc_100006CD4\r\nE1 03 17 AA                           MOV           X1, X23\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 7 of 9\n\n39 1A 00 94                           BL            encryptData      \r\nThis code first executes the “serialized_plist” function, which prepares data for sending. After it is executed, the register X0\r\npoints to the data ready to be sent to the server.\r\nThe next function to be invoked is “encryptData”. It has two arguments: a pointer to the data being encrypted is passed to\r\nthe X0 register, while the X1 register contains a plist with configuration data, including encryption parameters. After the\r\nexecution of this function, the X0 register contains a pointer to the ciphertext.\r\nOnce again, we needed to compromise the encryption process to intercept the data from the infected device. We decided to\r\nreplace the call to the “encryptData” function with a NOP instruction (1f 20 03 d5). This way, the value of the X0 register\r\nwill not be overwritten with the pointer to the encrypted data, and the validator will send cleartext data to our VPN server.\r\nJust as in the case with the JavaScript validator, we patched the code on the fly by extending our mitmproxy add-on:\r\nsig_pattern =\r\nre.compile(rb\"...\\xaa...\\xf9...\\x94...\\xaa...\\x94...\\xb4...\\xaa...\\x94...\\xb4...\\xb4...\\x71...\\x54...\\xaa...\\x70...\\xd5...\\xaa...\\x14\")\r\nblock_to_nop = sig_pattern.findall(ungzipped)[0]\r\nnew_block = block_to_nop[:28] + b'\\x1f\\x20\\x03\\xd5' + block_to_nop[32:]\r\nungzipped = ungzipped.replace(block_to_nop, new_block)\r\nSnippet of the mitmproxy add-on code that patches the call to the encryptData function with a NOP\r\nWhen the cleartext data reaches our VPN server, we (again, through the mitmproxy add-on) simulate a key exchange and\r\ndata encryption and control the value of the encryption key. As a result, we successfully decrypted the data sent by the C2\r\nserver and extracted the body of the implant.\r\nObtaining the modules\r\nAfter reverse engineering the TriangleDB implant, we found out that it is able to execute auxiliary modules, which made us\r\nwant to obtain the module binaries. In our analysis of the implant, we wrote that module executables are passed to the\r\nimplant through the CRXUpdateRecord and CRXUpdateRunRecord commands. Thus, in order to get these executables, it\r\nwas necessary to be able to decrypt all the commands sent to the C2 server.\r\nAgain, the encryption algorithm was based on RSA, so our actions were similar to those we took to obtain the implant\r\nbinaries.\r\nThis time, however, we decided to:\r\nGenerate our own RSA public/private key pair;\r\nReplace the RSA public key from the implant’s configuration with the previously generated one.\r\nWe did that by adding the following code to the mitmproxy add-on:\r\nmacho_second_index = find_nth(decompressed_implant, b\"\\xcf\\xfa\\xed\\xfe\", 2)\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 8 of 9\n\nmacho_second = decompressed_implant[macho_second_index:]\r\nrsa_key, xored_signature_offset, xor_key = extract_rsa_key(macho_second)\r\nmy_der_key = MY_CERT_DER\r\nself.infections[flow.client_conn.peername[0]][\"true_device_public_cert\"] = rsa_key\r\nmy_der_key_xored = xor_one_byte_key(my_der_key, xor_key)\r\ndecompressed_implant[macho_second_index + xored_signature_offset:macho_second_index +\r\nxored_signature_offset+796] = my_der_key_xored\r\nWhen the implant traffic reached our VPN server, we:\r\nDecrypted it with the RSA private key that we generated;\r\nSaved the decrypted traffic;\r\nRe-encrypted the traffic with the public key used by the attackers.\r\nThis way, we were able to eavesdrop on all communications performed by the implant, as well as obtain the module\r\nbinaries.\r\nConclusion\r\nOur journey of investigating Operation Triangulation was quite lengthy; it took several months. Despite many ups and\r\ndowns, we eventually managed to obtain all the stages used in this attack, including four zero-day exploits reported to\r\nApple, two validators, an implant and its modules.\r\nAlong the way, we conducted a lot of research on iOS internals and came up with many interesting techniques, such as the\r\none we used for extracting the iMessage attachment.\r\nThe main difficulty that we encountered during our research was to deal with the public-key cryptography that was used in\r\njust about every stage of the infection chain. To bypass the encryption, we had to develop a mitmproxy add-on that patched\r\nthe malicious stages on the fly and compromised the original algorithms. Initially, we started with just 30 lines of code.\r\nWhen we finished extracting the modules, it had about 400 lines – and we definitely did not expect the add-on’s code to\r\ngrow that much back at the start of our enduring adventure!\r\nSource: https://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nhttps://securelist.com/operation-triangulation-catching-wild-triangle/110916/\r\nPage 9 of 9\n\nDecrypts the The validator encrypts response with all the packets the same AES key with the following used for encrypting ARM instructions: the sent message.\nE0 03 18 AA MOV X0, X24\n2E 1A 00 94 BL serialize_plist \nA0 04 00 B4 CBZ X0, loc_100006CD4\nE1 03 17 AA MOV X1, X23\n   Page 7 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MISPGALAXY",
		"ETDA",
		"Malpedia"
	],
	"references": [
		"https://securelist.com/operation-triangulation-catching-wild-triangle/110916/"
	],
	"report_names": [
		"110916"
	],
	"threat_actors": [
		{
			"id": "ad08bd3d-e65c-4cfd-874a-9944380573fd",
			"created_at": "2023-06-23T02:04:34.517668Z",
			"updated_at": "2026-04-10T02:00:04.842233Z",
			"deleted_at": null,
			"main_name": "Operation Triangulation",
			"aliases": [],
			"source_name": "ETDA:Operation Triangulation",
			"tools": [
				"TriangleDB"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "113b8930-4626-4fa0-9a3a-bcf3ef86f595",
			"created_at": "2024-02-06T02:00:04.14393Z",
			"updated_at": "2026-04-10T02:00:03.578394Z",
			"deleted_at": null,
			"main_name": "Operation Triangulation",
			"aliases": [],
			"source_name": "MISPGALAXY:Operation Triangulation",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775434872,
	"ts_updated_at": 1775791507,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/1b3adbbf4c8d0487a0167c646f6887c1b456934f.pdf",
		"text": "https://archive.orkl.eu/1b3adbbf4c8d0487a0167c646f6887c1b456934f.txt",
		"img": "https://archive.orkl.eu/1b3adbbf4c8d0487a0167c646f6887c1b456934f.jpg"
	}
}