Four Short Stories on Security and Privacy

A couple of things have been on my mind.

Chapter 1. Aruba and ARP

Those unfamiliar with Aruba networks can learn now that the company services many enterprise Wireless AP control systems, providing standard isolation in a work environment for connected IP addresses from direct communication.

By the same line of thought, if there is a sensitive data transfer, it would do well to go to an IP the AP knows about, otherwise how else may we be sure that company secrets are not filtering or being downloaded off of a high-privlege network machine to a lower one, especially if those same secrets are top secret, or very, very special.

In the tedium of a desire to avoid the established, quite easy method of a local wireless channel over P2P or Bluetooth, I took to communicating between two machines on the same subnet isolated by an Aruba AP, just to see if it was possible. I booted tcpdump right up to see what was talking to what, and to no suprise, IP level packets were filtered right now, as were most ARP packets, but one packet type was not: the standard ARP "who is" packet, "asking for machine XYZ".

The punchline then is that ARP on Aruba networks can act as a sort of odd and useless information side channel which for some reason I find very appealing, but I am not sure why. Maybe someone else in a government grade, physically wired setup will find this useful, or perhaps elsewhere. If an important cryptographic key is only 4096 bits, the transfer doesn't take long.

In most cases, though, machines can reach the outside internet anyways, so a LAN sidechannel is not always super interesting. The standard watermark principle is any shared resource between an anonymous machine and a privleged machine wherein the latter can augment a single bit of the resource can also be used to create a malicious flow of information.

As an aside on P2P here and WiFi direct, I'll note as maybe it will be useful, you should set your WiFi card so that it is not "managed" by nmcli prior to attempting to establish a P2P connection between two machines directly, otherwise the wpa_cli p2p_connect command with mostly work then fail obscurely. Note that once you disable managed mode, you will need to kick up a bare-bones wpa_supplicant process for the wpa_cli system to talk P2P to your device. If you are having an issue where your P2P connection fails shortly after succeeding p2p_connect pbc this may be why: check dmesg But you might as well just go buy an ethernet to USB cable; though this is useful if you don't have one on-hand.

Chapter 2: WACk Smart Fans, The AP password for which is "intelligence".

I often think about capital's tendency to hide the sharing of information collected from immediate view via services embedded into code that is not disclosed to the UI/UX, other than through the vague language of a privacy policy (words like "we may collect personally identifying information").

In some cases the means of observation are spoken of and acknowledged by those with power, in full disclosure of what they are collecting, and any perceived attempt at obsfucation is a misinterpretation of practical realities of the mechanism of collection. More interesting to me are the cases where the theoretical operator of a Betham's Panopticon obsfucates the fact of their observation, as these demonstrate a certain curious hesitation and fear of chastisement or subversion.

My landlord installed a WAC smart fan into my living room which does not work without an app installed on your phone. The fan itself connects to the app and phone via a broadcast wireless AP, though it may as well be Bluetooth or any other network channel.

Unfortunately, it also contains the following ad service libraries which must be absolutely, positively necessary to send a simple "on/off" message from your phone to the fan:

- com::facebook::FacebookSdk::setAdvertiserIDCollectionEnabled and
  com.google.android.gms.ads.identifier.AdvertisingIdClient (see
https://javadoc.io/doc/com.facebook.android/facebook-core/5.12.1/constant-values.html
for the API)

With the following additional code present:

On the Facebook side:

pSVar6 = this.getNullableString$facebook_core_release(p1,"name");
pSVar7 = this.getNullableString$facebook_core_release(p1,"given_name");
pSVar8 = this.getNullableString$facebook_core_release(p1,"middle_name");
pSVar9 = this.getNullableString$facebook_core_release(p1,"family_name");
pSVar10 = this.getNullableString$facebook_core_release(p1,"email");
pSVar11 = this.getNullableString$facebook_core_release(p1,"picture");
pJVar12 = p1.optJSONArray("user_friends");
pSVar13 = this.getNullableString$facebook_core_release(p1,"user_birthday");
pJVar14 = p1.optJSONObject("user_age_range");
pJVar15 = p1.optJSONObject("user_hometown");
pJVar16 = p1.optJSONObject("user_location");
pSVar17 = this.getNullableString$facebook_core_release(p1,"user_gender");
pSVar18 = this.getNullableString$facebook_core_release(p1,"user_link");

On the Google side, we have most of the above, but also

iVar8 = pOVar24.getInteger("sdk-version");
pIVar9 = Integer_valueOf(iVar8);
pAVar7 = pAVar7.setSdkVersion(pIVar9);
pSVar4 = pOVar24.get("model");
pAVar7 = pAVar7.setModel(pSVar4);
pSVar4 = pOVar24.get("hardware");
pAVar7 = pAVar7.setHardware(pSVar4);
pSVar4 = pOVar24.get("device");
pAVar7 = pAVar7.setDevice(pSVar4);
pSVar4 = pOVar24.get("product");
pAVar7 = pAVar7.setProduct(pSVar4);
pSVar4 = pOVar24.get("os-uild");
pAVar7 = pAVar7.setOsBuild(pSVar4);
pSVar4 = pOVar24.get("manufacturer");
pAVar7 = pAVar7.setManufacturer(pSVar4);
pSVar4 = pOVar24.get("fingerprint");
pAVar7 = pAVar7.setFingerprint(pSVar4);
pSVar4 = pOVar24.get("country");
pAVar7 = pAVar7.setCountry(pSVar4);
pSVar4 = pOVar24.get("locale");
pAVar7 = pAVar7.setLocale(pSVar4);
pSVar4 = pOVar24.get("mcc_mnc");
pAVar7 = pAVar7.setMccMnc(pSVar4);
pSVar4 = pOVar24.get("application_build");
pAVar7 = pAVar7.setApplicationBuild(pSVar4);
...
pSVar3 = local_0.getString("google_api_key");
pSVar4 = local_0.getString("firebase_database_url");
pSVar5 = local_0.getString("ga_trackingId");
pSVar6 = local_0.getString("gcm_defaultSenderId");
pSVar7 = local_0.getString("google_storage_bucket");
pSVar8 = local_0.getString("project_id");

The above are only ever used for crash reporting and logging into the fan app and nothing else, of course. That's why they need a tracking ID, of course. And, of course, then, they should be called from a generic com/google/android/datatransport/runtime send utility method.

This is all sort of expected, of course. What app doesn't have the ability to know all your social media details at this point? The issues here are well defined, and outlined in books like "Psychopolitics" by Byung-chul Han.

Returning to the point, at first, it may seem like nothing here is hidden to any extent, only out-in-the-open annoying tracking and adware. But, looking at those Google utilities a bit closer, you will find:

pSVar1 = StringMerger_mergeStrings
("hts/frbslgiggolai.o/0clgbthfra=snpoo","tp:/ieaeogn.ogepscmvc/o/ac?omtjo_rt3")
;
CCTDestination_DEFAULT_END_POINT = pSVar1;
pSVar2 = StringMerger_mergeStrings
("hts/frbslgigp.ogepscmv/ieo/eaybtho","tp:/ieaeogn-agolai.o/1frlglgc/aclg");
CCTDestination_LEGACY_END_POINT = pSVar2;
pSVar3 = StringMerger_mergeStrings("AzSCki82AwsLzKd5O8zo","IayckHiZRO1EFl1aGoK");

These are Firebase Crashlytics / Analytics / Datatransport endpoints, which Google intentionally scrambles to (slightly) prevent static analysis tools from easily extracting them.

"hts/frbslgiggolai.o/0clgbthfra=snpoo"
"tp:/ieaeogn.ogepscmvc/o/ac?omtjo_rt3"

If you interleave them:

    hts + tp:/ → https://
    frbslgig + ieaeogn → firebaselog
    golai.o + ogepscmv → ging.googleapis

https://firebaselogging.googleapis.com/...
etc.

There's identical sorts of setups for the crash reporting:

pSVar1 = DataTransportCrashlyticsReportSender_mergeStrings
("hts/cahyiseot-agolai.o/1frlglgc/aclg","tp:/rsltcrprsp.ogepscmv/ieo/eaybtho");
DataTransportCrashlyticsReportSender_CRASHLYTICS_ENDPOINT = pSVar1;
pSVar1 = DataTransportCrashlyticsReportSender_mergeStrings
("AzSBpY4F0rHiHFdinTvM","IayrSTFL9eJ69YeSUO2")

Here's a standalone version of the function from the other parts of the file

public class StringMerger {

    public static String mergeStrings(String p0, String p1) {
        int len0 = p0.length();
        int len1 = p1.length();

        if (!(-1 < len0 - len1 && len0 - len1 < 2)) {
            throw new IllegalArgumentException("Invalid input received");
        }

        StringBuilder sb = new StringBuilder(len0 + len1);

        for (int i = 0; i < p0.length(); i++) {
            sb.append(p0.charAt(i));
            if (i < p1.length()) {
                sb.append(p1.charAt(i));
            }
        }

        return sb.toString();
    }

    public static void main(String[] args) {
        if (args.length != 2) {
            System.out.println("Usage: java StringMerger  ");
            return;
        }

        String merged = mergeStrings(args[0], args[1]);
        System.out.println("Merged output:");
        System.out.println(merged);
    }
}

The two strings: The two strings: "AzSCki82AwsLzKd5O8zo" "IayckHiZRO1EFl1aGoK"

When merged, produce the Google API key for the data logging service, so that you can inject your own false logs, e.g. AIzaSyCckkiH8i2ZARwOs1LEzFKld15aOG8ozKo . This noted, you could also determine all of this via Wireshark, so no real reverse engineering is needed. I am not sure why they even bothered with such a weak obsfucation.

Finally, the simplest thing to look at, which is the plaintext permissions requested by the application:

"android.permission.INTERNET",
"android.permission.ACCESS_WIFI_STATE",
"android.permission.ACCESS_NETWORK_STATE",
"android.permission.CHANGE_WIFI_MULTICAST_STATE",
"android.permission.CHANGE_WIFI_STATE",
"android.permission.WAKE_LOCK",
"android.permission.ACCESS_COARSE_LOCATION",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.BLUETOOTH",
"android.permission.BLUETOOTH_ADMIN",
"android.permission.BLUETOOTH_SCAN",
"android.permission.BLUETOOTH_CONNECT",
"android.permission.USE_FINGERPRINT",
"android.permission.USE_BIOMETRIC",
"com.WAC.PlayStore.WACSmartFans.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION",
"com.google.android.finsky.permission.BIND_GET_INSTALL_REFERRER_SERVICE"

Nevertheless, the ability to call getGeoURI for latitude and longitude was enough to make me swear off wanting to run this app on my phone, even if everything was "by the book", I simply do not think they need to know or access any of the above permissions (barring bluetooth), or even have "dead" code to access this information within an app that is supposed to turn a light on and off and a fan to a specific speed.

Maybe you agree, or maybe you would say it saved you 10% of the sticker cost on the device to include these advertising services in the app. I would provide the counter-argument that the option to engage in this collection should at least be clearly provided to consumers at purchase time, with precise numbers on the amount of profit the associated companies stand to make from such broad data collection.

Note the fan does come with a Bluetooth remote, but the circuit is designed poorly and it eats through one fresh watch battery a day. I still need to mention, as well, the fact one of the JSON command structures the app can make to the fan is decommission which will nuke the fan's operation whenever WAC(k) wants.

The hard-coded AP password makes it possible to drive around neighborhoods looking for these WAC smartfan APs, connect to them via the default [non-modifiable?] password of "intelligence", and send this decommission command yourself, early and unprompted.

Chapter 3: (un)zipping it up, APT attacks, and the madness of sharing your AI prompt data.

Let us start in a popular domain: pick up almost any of the common providers for local agentic programming, e.g. Ollama, opencode, and you will quickly find either:

There do at the present time exist systems that do not require any networking beyond the model download to localhost themselves, e.g. llama.cpp and goose (though the latter still contains questionable support for telemetry), which should not be there for a Github project: my gut tells me any honest developer would provide anonymous/private-data-free crash report functionality that requests users to open an issue, and then enforce that bug tickets attach these logs.

The concept of offering telemetry, in the first place, feels uselessly invasive if not attached to a full product offering, i.e. github repositories do not cut it. The counterargument being "oh, but how will I know how my code performs across my userbase?", to which I'd respond "how has the Linux kernel worked for the past 20 years"? This is the whole point of open source: audience participation and benchmarking.

The sort of silly, funny reality is big tech companies are hoping people do not realize is decent models are entirely capable of running on localhost without any networking at all for relatively cheap, and are just as useful for programming tasks as the cloud models.

Many of new "popular" open source projects, including many of these AI-related ones, ship through APT repositories (as in, those PPA bits fetched via apt update have associated priorities. These may be observed via apt-cache policy It should be the default behavior to add any non-distribution repositories at a lower priority than the main security updates repositories for the distribution, since a repository with an equal or higher priority can override packages in the baseline (e.g. trixie baseline), but it is *not*.

Let us take a look at the project LibreWolf: a private replacement for Firefox. Well, they support adding a PPA through extrepo However, this repository can get added with equal priority (500) to the baseline debian security repositories, meaning that packages offered by LibreWolf that are not LibreWolf may begin to be offered which override the system packages with malware.

Hence, it is highly questionable to me when project developers ask you to add a PPA to your system. There are many, many such projects. There's much profit to be made in, say, posting stackoverflow comments to key issues that suggest the installation or addition of specific PPAs. For more details, see wiki.debian.org/AptConfiguration. I highly suggest doing this if you like to add PPAs and do not like being hacked.

Wrapping up, let's talk about something that should be done but has not been done, and is mostly unrelated to security and privacy. Zip files contain a central directory structure, which it is possible to identify programmatically (supposing a correct program that identifies attempts to spoof the central directory), and use to list the contents of a Zip file without downloading it.

HTTP range requests support this pretty well with some intelligence, and moreover, can be subsequently used to download just the desired file out of a Zip file hosted remotely. How many hours of compute and human time have been wasted by the fact this operation is not a default in web browsers? Open source implementations of this have existed for more than 8 years, but people don't generally think of this operation as possible and thus this practive has not yet been generally adopted.

Maxwell Bland 02-18-2026.

Return to homepage.

Opinions subject to change, and do not necessarily reflect those of my employers or coworkers, past or present.

Hosted by the The SDF Public Access UNIX System