Operation Triangulation – iOS Zero-click APT Exploit Info

Quick Summary: Operation Triangulation is an iOS zero-click exploit that will self destruct, looks to have been used since at least 2019, works on iOS 15.7, unsure if it works on iOS 16. Can collect location, mic recordings, photos, and manipulate iMessages. First point of entry is from an iMessage message, that compromises the device, after compromise, the message gets deleted.




Links for checking for infection.



The following is a list of C&C domains from the securelist.com article. Did a quick DNS lookup for each domain and they currently have the following records & IP addresses. Note that these can change at any time.

addatamarket.net - sandy.ns.cloudflare.com, doug.ns.cloudflare.com - No A records, or TXT
backuprabbit.com - nelci.ns.cloudflare.com, morgan.ns.cloudflare.com - No A records, or TXT
businessvideonews.com - ns2.dnsowl.com, ns3.dnsowl.com, ns1.dnsowl.com -,,
cloudsponcer.com - Cloudflare, kipp.ns.cloudflare.com, joyce.ns.cloudflare.com
datamarketplace.net - ns78.domaincontrol.com, ns77.domaincontrol.com,
mobilegamerstats.com - ns1.bitdomain.biz, No A records, TXT=v=spf1 redirect=_spf.mailhostbox.com
snoweeanalytics.com - cody.ns.cloudflare.com, arlee.ns.cloudflare.com -,
tagclick-cdn.com - ns4.bitdomain.biz, ns3.bitdomain.biz, ns2.bitdomain.biz, ns1.bitdomain.biz - No A records, TXT=v=spf1 redirect=_spf.mailhostbox.com"
topographyupdates.com - nero.ns.cloudflare.com, dalary.ns.cloudflare.com -,
unlimitedteacup.com - nelci.ns.cloudflare.com, javon.ns.cloudflare.com -,
virtuallaughing.com - elaine.ns.cloudflare.com, braden.ns.cloudflare.com -,
web-trackers.com - dns1.registrar-servers.com, dns2.registrar-servers.com -
growthtransport.com - ns3.dnsowl.com, ns2.dnsowl.com, ns1.dnsowl.com -,,
anstv.net - ns64.domaincontrol.com, ns63.domaincontrol.com. -
ans7tv.net - ns37.domaincontrol.com,ns37.domaincontrol.com -

List of domains


List of IPv4 addresses used

Bash command to get an updated IP address list. bad.txt contains all the above domain names.

for i in `cat bad.txt` ; do dig $i a +short >> badips.lst; done

Mikrotik packet sniffer settings to capture traffic coming or going to the above IP addresses.

/tool sniffer
set file-limit=32000KiB file-name=Triangulation filter-ip-address="\

You can then start the sniffer by running Tools -> Packet Sniffer Settings -> Start

or run


Wireshark – Please turn off promiscuous mode for this device

Recently received the following error while trying to do a packet capture on windows.

There are two solutions to this problem

  1. Disable promiscuous mode for the adapter
  2. Update Npcap

Disable Promiscuous mode

“Please turn off promiscuous mode for this device”

You can turn on promiscuous mode by going to Capture -> Options

Uncheck promiscuous

And click Start

Update Npcap

If you need promiscuous mode on, then look at installing a newer version of Npcap


Restart Wireshark, and Start a capture.


JavaScript Basic Spread and Rest (…) usage

The Spread and Rest operators i.e. the three dots (…) can be used to make code cleaner and more concise.

Difference between Spread and Rest

Spread: Works on elements on the right side of the = operator, and breaks them out into individual elements.

Rest: Works on the left hand side of the = operator, and compresses them into an array.

Using Spread to Iterate over Arrays

Spread works on iterables like strings, arrays, maps and sets.

The spread operator operates similar to taking all the elements out of an array and operating on them or writing them to a new array. Say for instance we have an array of computers and we want to log each element to the console.

const computersA = ['Acer', 'Apple', 'ASUS']

We can log each element by running

console.log(computersA[0], computersA[1], computersA[2])

Or we can use the spread operator


The output is the same.

Joining Arrays

We can also use the spread operator to join two arrays together. Say we have two arrays

const computersA = ['Acer', 'Apple', 'ASUS']
const computersB = ['HP', 'Dell', 'Lenovo']

And we want to concatenate them together. We can do that simply by

const computerAll = [...computersA, ...computersB]

Rest Example

Rest is simply the opposite of spread. Spread take an item like an array and expands it out into elements we can use. Rest takes elements and packs them into an array. This can be extremely helpful if we want to pass in an unknown amount of elements into a function for processing.

const computersA = ['Acer', 'Apple', 'ASUS']
function writeToLog (...arr) {
  for (const element of arr) {

Now we can call the function with as many elements in the array and they will all get logged to the console.

writeToLog('Razer', 'Alienware', 'Legion')

We could also use both the Spread and Rest functions

const gamingLaptops = ['Razer', 'Alienware', 'Legion']

Now as we add more laptops to the gamingLaptops array, the function will automatically process the line and write to console.


Notes on Setting up a Cambium 850C PTP

These are set up a bit weird compared to normal WISP radio equipment. The default IP address is for the radio. So you need to set a with a subnet of Looks like it is usually only accessible via the management port and you need to make or buy a special patch cable.


The User Guide is available here


You can download the Installation Guide from here


Both those documents show how to install and get into the device.

JavaScript Delete Object if it Collides or Overlaps with another object that has CSS Class X

Imagine we have a page that has anywhere from 1 to 100 floating blocks. Now what if I want the block(s) to disappear if it runs into another type of block or a boarder? How would we do that?

First lets get a list of all elements with X and Y class. We could swap one of these classes out for an ID if we wanted to.

const elements1 = document.querySelectorAll('.boxes')
const elements2 = document.querySelectorAll('.borders')

The variable elements1 and 2 are both arrays of all the elements that have class .boxes and boarders.

Now lets create a function to detect a collision.

function detectCollision(class1, class2) {
  for (let i = 0; i < class1.length; i++) {
    const e1Rectangle = class1[i].getBoundingClientRect()
    for (let i2 = 0; i2 < e2.length; i2++) {
      const e2Rectangle = class2[i2].getBoundingClientRect()
      if (
        e1Rectangle.left < e2Rectangle.right &&
        e1Rectangle.right > e2Rectangle.left &&
        e1Rectangle.top < e2Rectangle.bottom &&
        e1Rectangle.bottom > e2Rectangle.top
      ) {

This function takes two arrays as inputs, loops over each array object and compares to see if any are overlapping. If they are overlapping, run the destroyElement() function for those two specific elements.

Create the destroyElement function with

 function destroyElement(element) {

Here is our full code

const elements1 = document.querySelectorAll('.boxes')
const elements2 = document.querySelectorAll('.borders')

function detectCollision (class1, class2) {
  for (let i = 0; i < class1.length; i++) {
    const e1Rectangle = class1[i].getBoundingClientRect()
    for (let i2 = 0; i2 < e2.length; i2++) {
      const e2Rectangle = class2[i2].getBoundingClientRect()
      if (
        e1Rectangle.left < e2Rectangle.right &&
        e1Rectangle.right > e2Rectangle.left &&
        e1Rectangle.top < e2Rectangle.bottom &&
        e1Rectangle.bottom > e2Rectangle.top
      ) {

// Element is element[arraynumber].etc
function destroyElement(element) {

// Every second, let's run our collision function to check for collisions
setInterval(function () {
   detectCollision(elements1, elements2)
  }, 1000)

Ansible Error – An unhandled exception occurred while templating

}}'. Error was a <class 'ansible.errors.AnsibleError'>, original message: An unhandled exception occurred while templating '{{ ntfy_visitor_request_limit_exempt_hosts_container_networks_inspect_commands_string | split('###') }}'. Error was a <class 'ansible.errors.AnsibleError'>, original message: template error while templating string: no filter named 'split'. String: {{ ntfy_visitor_request_limit_exempt_hosts_container_networks_inspect_commands_string | split('###') }

To solve the issue, update Ansible. If you are already on the “latest” version of ansible available to for your distro, uninstall, and then install it again following the directions on Ansible’s website

For Ubuntu we can simply do that with

sudo apt remove ansible
sudo apt-add-repository ppa:ansible/ansible
sudo apt install ansible

Notes on Buttons with JavaScript

Here are some very basic notes on using buttons to change elements on a web page

We have a very simple page with three buttons that change the background color when the button is clicked.

Clicking a button changes the text and the background color.

Create a button in our html file.

    <button type="button" class="button green">Green</button>

We have two classes assigned to this button, button which is used for styling, and the green, which JavaScript will use to know which button is clicked.

In our JavaScript, we will set up an event listener for when the button is clicked.

document.querySelector('.green').addEventListener('click', function () {
  document.querySelector('body').style.backgroundColor = 'Green'

We use the document.querySelector to interact with HTML objects. So we setup the event listener to listen to the button that is in Class green, and we wait for the click event. Once that happens, we run the code in the function(){ }.

Line 2 is what changes our background color. We query the body tag, and set the backgroundColor to Green. Notice that the background color name is slightly different between JavaScript and HTML.
HTML and CSS it has a – in it: background-color
JavaScript is it Camel Case: backgroundColor

Here is the full code for the above screenshots. There are three files. index.html, style.css, and index.js. Should be able to copy them to the same folder and run the index.html file.

Index.html code

<!DOCTYPE html>
<html lang="en">
    <link rel="stylesheet" href="style.css" />
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JavaScript DOM Practice</title>
  <br />
  <section class="mainwindow">
    <div class="output">Click a button to change the background color</div>
    <br />
    <br />
    <br />
    <button type="button" class="button green">Green</button>
    <button type="button" class="button blue">Blue</button>
    <button type="button" class="button yellow">Yellow</button>

    <script src="index.js"></script>

JavaScript index.js

'use strict'

document.querySelector('.green').addEventListener('click', function () {

document.querySelector('.blue').addEventListener('click', function () {

document.querySelector('.yellow').addEventListener('click', function () {

function ChangeBackgrounColor (color) {
  document.querySelector('body').style.backgroundColor = color

function LogBackgroundColor (color) {
  document.querySelector('.output').innerHTML = `Background is ${color}`
  console.log(`Background color is ${color}`)

CSS File style.css

.mainwindow {
  padding: 1rem;
body {
  background-color: white;
button {
  border: none;
  font-size: 24px;
  cursor: pointer;
  padding: 1rem 1.5rem;
  margin: 1rem;
.output {
  font-size: 2rem;

Javascript Ternary Operator

The ternary operator is a conditional operator that takes three (ternary) options. We can almost think of it as a concise if then statement.

The basic syntax is as follows.

Condition ? Value-If-True : Value-If-False

For an example

const isOver21 = age >= 21 ? "Is over 21" : "Is Under 21"


Making RDP Faster (60FPS)


On RDP Server, Open up Registry Editor as administrator

Go to

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations

Add a new DWORD


Set the Value to 15, and base Decimal.

Apply and reboot.

You may also want to look at enabling the following options in Group Policy Editor

Computer Configuration > Policies > Administrative Template > Windows Components > Remote Desktop Session Host > Connections > Select RDP transport Protocols > Use both TCP and UDP

Computer Configuration > Policies > Administrative Template > Windows Components > Remote Desktop Session Host > Connections > Remote Session Environment > Use hardware graphics adapters for all Rmote Desktop Services sessions


Running Node App as systemd Service

In this post we will be using systemd to run a node application. This is helpful as it will automatically start the app when the server starts so we don’t have to manually. These steps can easily be modified to run a bash script, or any other application.

  • Create systemd file
  • Customize systemd file
  • Enable systemd file

We’ll be creating a service for the Simple Whisper Web Interface as an example. Chang things as needed.

Create systemd file

This is super simple. We create a .service file in /lib/systemd/system. When we enable the service, it will create a symlink to this file.

sudo vim /lib/systemd/system/whisperweb.service

Customize systemd file

Change the settings as appropriate. It would be a good idea to run any service as a limited user that only has the rights needed to get the job done. Do note that you will need to have any prerequisites installed and available for that user to use. I.e. libraries installed with npm etc.

Description=Simple Whisper Web Interface Service File

ExecStart=/usr/bin/node mainssl.js


Enable systemd file

Enabling the service will create a symlink that will then run this service file on system boot.

sudo systemctl enable whisperweb.service

And now we can start the service.

sudo systemctl start whisperweb.service

We can verify that the service is running by running

sudo systemctl status whisperweb.service

The following article has some great explanations on what different options in the unit file mean and do.