Cleaning up Google Purchases

The Google Purchase History feature has been doing rounds in the news recently. In case you missed it, go to https://myaccount.google.com/purchases right now and make sure you are logged in with your personal gmail account to see what all Google thinks you’ve bought.

For me it lists purchases going as far back as 2013, which include:

  1. All of my Amazon Purchases (including Kindle and Audible)
  2. Flipkart/Sneapdeal purchases
  3. Gifts I’ve bought for others on various platforms
  4. All my iCloud purchases
  5. Purchases on Steam
  6. BigBasket purchases
  7. Google Playstore purchases as well, of course
  8. And much, much more.

For each of the purchases, it remembers the price, the taxes, as well as the delivery address used.

While this isn’t shocking in the least, I was surprised, because as a Infosec professional, I’ve disabled all of google’s invasive tracking features:

  1. All my Activity Controls are paused.
  2. Google Location history is disabled for my account.
  3. I have Shared endorsements turned off.
  4. Ad personalization is turned off.
  5. I used to run with the Protect my Choices extension till a while back to avoid targeted advertising.

Regardless, the Google purchases page had hundreds of results, going back half a decade. Google currently does not offer a way to delete collected purchases directly, or to pause this collection in any way. The only way is to find the emails that Google scanned, and delete them.

I ended up deleting everything from the following email addresses:

auto-confirm@amazon.com
auto-confirm@amazon.in
cs@flipkart.com
digital-no-reply@amazon.com
do_not_reply@audible.com
do_not_reply@gog.comorders@services.target.com
ebay@ebay.in
googleplay-noreply@google.com
help@stickermule.com
mail@info.fabfurnish.com
no-reply@flipkart.com
no-reply@paytm.com
no_reply@email.apple.com
noreply@flipkart.com
noreply@pizzahut.co.in
noreply@snapdeals.co.in
noreply@steampowered.com
notification@wish.com
order-update@amazon.in
orders@services.target.com
payments-messages@amazon.in
return@amazon.in
ship-confirm@amazon.com
ship-confirm@amazon.in
shipment-tracking@amazon.com
shipment-tracking@amazon.in
updates@myntra.com

Note that deleting the email doesn’t seem to be sufficient either, you need to clear your Trash, and then wait for a while (almost 2 days for me) before the system refreshes. After 3 days of just deleting mails, I finally got this screen:

screenshot of google purchases dashboard showing "You don't have any purchases"

Google seems to be picking up all kinds of emails, including:

  1. Invoices
  2. Shipment Confirmations / Updates
  3. Return confirmations
  4. Payment Confirmations
  5. Order Cancellations
  6. Payment Failures (gasp!)

Warning about Deletions

I’ve already switched away from Amazon/Flipkart emails from my Gmail. But deleting invoices from your inbox isn’t always the best idea. Most websites will let you re-download invoices (Amazon/Flipkart do), but take care not to delete any necessary emails that you might need for warranty claims or any other purpose later.

Migrating DNSCrypt Server to Docker

I’ve been running a personal DNSCrypt server in Bangalore for the last 2 years. When I set it up, it was just a compiled version of dnscrypt-wrapper, which was the bare minimum setup I could do.

Since then, I’ve upgraded it to a distribution supported version, but recent changes in dnscrypt key rotation, I’ve been wanting to setup something automated as well.

The easiest way was to switch to the official DNSCrypt Docker image, which does both key generation and certificate rotation. Since my public key was already present in the DNSCrypt Server lists, I was not too keen to regenerate a new key.

The primary challenge was ensuring that the docker container picks up my existing keys without trying to generate new ones from scratch. It was basically 2 steps:

  1. Match the directory structure that the container expects.
  2. Invoke the container directly into start mode while passing existing keys.

Directory Structure

I copied my keys (public.key, secret.key) to /etc/dnscrypt-keys and ran the following:

echo 2.dnscrypt-cert.captnemo.in > provider_name
touch provider_info.txt # I couldn't figure out how to output the same info, so kept it blank
hexdump -ve '1/1 "%.2x"' < public.key > public.key.txt

Then I ensured that the file permissions are matching what the container expects:

chmod 640 secret.key
chmod 644 public.key
chown root:1002 public.key secret.key
chmod 644 provider_name

This is how the final permissions looked for the directory (/etc/dnscrypt-keys)

-rw-r-----   1 root 1002    64 May 18 07:15 secret.key
-rw-r--r--   1 root 1002    32 May 18 07:15 public.key
-rw-r--r--   1 root root    28 May 18 07:19 provider_name
-rw-r--r--   1 root root     0 May 18 07:23 provider_info.txt
-rw-r--r--   1 root root    64 May 18 07:25 public.key.txt
drwxr-xr-x   2 root root  4096 May 18 07:26 .

Running the Container

Then, I directly ran dnscrypt-wrapper container:

docker run --detatched --restart=unless-stopped --volume /etc/dnscrypt-keys:/opt/dnscrypt-wrapper/etc/keys --publish 10.47.0.5:4434:443/tcp --publish 10.47.0.5:4434:443/udp jedisct1/dnscrypt-server start

I pass a host path mount instead of creating a Docker Volume, since they can get deleted in regular docker prune.

Here, 10.47.0.5 is the “Anchor IP”, which Digital Ocean internally maps to my Floating IP.

The container comes up, generates new short-term keys and goes live:

Starting DNSCrypt service for provider:
2.dnscrypt-cert.captnemo.in
Starting pre-service scripts in /etc/runit_init.d
setup in directory /opt/unbound/etc/unbound
generating unbound_server.key
Generating RSA private key, 3072 bit long modulus (2 primes)
.......++++
...................++++
e is 65537 (0x010001)
generating unbound_control.key
Generating RSA private key, 3072 bit long modulus (2 primes)
.........................++++
........................................++++
e is 65537 (0x010001)
create unbound_server.pem (self signed certificate)
create unbound_control.pem (signed client certificate)
Signature ok
subject=CN = unbound-control
Getting CA Private Key
Setup success. Certificates created. Enable in unbound.conf file to use
ok: run: unbound: (pid 28) 300s
ok: run: dnscrypt-wrapper: (pid 31) 300s
ok: run: unbound: (pid 28) 600s
ok: run: dnscrypt-wrapper: (pid 31) 600s

Once the server was up, I verified connectivity with dnscrypt-proxy and it worked perfectly.

Future Scope

Right now, I have a single container that does 2 things:

  1. Certificate Rotation via a service that checks it every 30 minutes.
  2. DNSCrypt Service, which is accessible over the internet.

For (1) to work, it needs access to the Private Keys that are used to sign the temporary certificates that last 24 hours. Since both things are managed within the same container, the container ends up with both network and long-term keys access. This means, any RCE on the service can result in the long-term keys being compromised.

A simple fix for this would be to separate out the Certificate Rotation part into a separate “mode” on the Docker Image, which can be called independently. This would allow someone to run certificate rotation on a second container using a scheduler, but with far more limitations (such as no network access). A common file-mount between both the containers can take care of sharing the temporary keys between the containers, and a simple unix socket on the shared-file-mount can be used to signal a certificate rotation (this triggers the dnscrypt service restart, so it picks the new cert).

Stripping Audible DRM

Self-Guide for stripping the Audible DRM, in similar vein as my Kindle Self-Guide.

  1. Download the aax file from Audible website.
  2. Run the inAudible-NG Rainbrow crack table against the AAX file.

Easiest way is via docker/podman:

cd ~/Music/Audiobooks
podman run -v $(pwd):/data ryanfb/inaudible@sha256:b66738d235be1007797e3a0a0ead115fa227e81e2ab5b7befb97d43f7712fac5
for i in "*.m4a"; do fix-audible-m4a "$i";done

The cool part about this is that the entire activation is done offline, and runs a Rainbow Table attack against the Audible DRM. To make the process faster in the future, you can save your “activation bytes” (8 hex characters) and directly use them with ffmpeg to decode instead:

ffmpeg -loglevel panic -y -activation_bytes ${AUDIBLE_ACTIVATION_BYTES} -i "$aax_file" -c:a copy -vn "$m4a_file"

A small percentage of Audible AAX files have a incorrect bit set in the “Audio Object Type Specific Config” in the ESDS atom in an M4A file, which leads to them not playing in Firefox/Android and some other players. To fix this, I have a fix-audible-m4a script called above.

References