Archive for April, 2013

Hacking the Withings WiFi Body Scale

Sunday, April 14th, 2013

Keeping track of weight development just got a lot easier, now that scales like the “Wifi Body Scale” by Withings (available at e.g. Amazon) can post their measurements via Wifi.

Unfortunately, the scales post their results to the webpage of the manufacturer, which some people are not comfortable with. Fortunately, the communication is unencrypted and easy understandable (JSON based).

Other folks have paved the way by analyzing the protocol. The general approach stays the same

  1. Pair the scales via the instructions (need an account at my.withings.com) with the wifi network
  2. Redirect the DNS requests from the scales to your own server
  3. Setup some script which simulates a Withings server
  4. Communicate!
  5. Do something with the data

1. Pairing
There are different models that have different pairing approaches, consult the FAQ for additional information. The document also provides information on error codes and how to reset the device (or perform firmware updates).

UPDATE: Withings now uses Zendesk for support information.

2. DNS Spoofing
In order to redirect the requests from the device, many ways lead to Rome. Depending on your network configuration, you could use dnsmasq on your router and configure it to respond with a modified ip via:

address=/withings.net/10.0.0.5
address=/withings.com/10.0.0.5

where 10.0.0.5 is the ip of your own server. While the WebGUI is hosted at withings.com, the scales try to communicate with scalews.withings.net. Both resolve to ‘89.30.121.150’. If you have your own bind service running, configure a zone for the withings domains.

3. Server
Now that the scales talk to your webserver at 10.0.0.5, the conversation has to be performed. “Prox” wrote a python script, while “sd” (german) used PHP.

The configuration of the webserver might have to be changed in order to redirect the requests from

  • /cgi-bin/once
  • /cgi-bin/session
  • /cgi-bin/maint
  • /cgi-bin/measure

to your own script. Running nginx, add the following to your server configuration

# allow POST requests to static content
error_page 405 = $uri;

# redirect withings scale requests
location /cgi-bin {
rewrite ^/cgi-bin/(.*)$ /withings/index.php?s=$1;
}

and the requests will be forwared to the PHP script “index.php” at /withings on your webserver.

4. The communication
The communication consists of 5 Steps and is handled via POST requests containing JSON data

  1. Request to /cgi-bin/once:

    POST: Array
    (
    [action] => get
    )

    Response:
    {
    "status": 0,
    "body": {
    "once": "87a6549c-93f7bce8"
    }
    }

    Remarks:
    – status 0 means no error (query the withings service to get some error codes like 2554)
    – the once UID can be random 8 chars, dash, 8 chars

  2. Request to /cgi-bin/session:

    POST: Array
    (
    [action] => new
    [auth] => 00:00:be:ef:00:07
    [hash] => e26b6c7dcb05ac46bde912d2bc951d83
    [mfgid] => 114700
    [currentfw] => 79
    [batterylvl] => 100
    [duration] => 30
    [zreboot] => 0
    )

    Response:
    {
    "status": 0,
    "body": {
    "sessionid": "7bf5-382a9b74-183dcfe5",
    "sp": {
    "users": [
    {
    "id": 5555,
    "sn": "BEN",
    "wt": 80,
    "ht": 1.75,
    "agt": 23.4,
    "sx": 0,
    "fm": 131,
    "cr": 1365963066,
    "att": 0
    },
    {
    "id": 6666,
    "sn": "SEN",
    "wt": 80,
    "ht": 1.75,
    "agt": 42,
    "sx": 1,
    "fm": 131,
    "cr": 1365963066,
    "att": 0
    }
    ]
    },
    "ind": {
    "lg": "en_US",
    "imt": 1,
    "stp": 1,
    "f": 2,
    "g": 98124
    },
    "syp": {
    "utc": 1365974415
    },
    "ctp": {
    "goff": 7200,
    "dst": 0,
    "ngoff": 0
    }
    }
    }

    Remarks:
    – auth in request: MAC address of scales (can be found in the battery compartment)
    – response: list of user profiles, scale settings and time information

    “id”: numeric id for user
    “sn”:” three chars displayed in the scales (usernick)
    “wt”: weight in kilograms, used for identification
    “ht”: height of user in meters
    “agt”: age in years
    “sx”: sex of user (0: male, 1: female)
    “fm”: unknown
    “cr”: timestamp of last measurement or account creation
    “att”: unkown
    lg”: language, like “en_US” or “en_GB”
    “imt”: perform induction measurement (0: off, 1: on)
    “stp”: 1 (unknown)
    “f”: autocalibrate mode (0: measure directly, 2: calibrate via tap before measuring)
    “g”: value for gravity (98124). This highly influences the weight result!
    “utc”: current UNIX timestamp}
    goff”: unkown
    “dst”:0 (DST switch?)
    “ngoff”: unkown

  3. Request to /cgi-bin/maint:

    POST: Array
    (
    [action] => store
    [sessionid] => 7bf5-382a9b74-183dcfe5
    [source] => 1
    [type] => 2
    [data] => [WDEBUG] dac: 1419 1508 1638 1791
    [WDEBUG] adc: 21800 21500 21900 21800
    )

    Response:
    {
    "status": 0
    }

    Remarks:
    – Maintenance values (seem like raw values from ADC and DAC)
    – Response from real server is much longer, but since its not needed for now, status: 0 should be enough

  4. Request to /cgi-bin/measure:

    POST: Array
    (
    [action] => store
    [sessionid] => 7bf5-382a9b74-183dcfe5
    [macaddress] => 00:00:be:ef:00:07
    [userid] => 5555
    [meastime] => 1365974385
    [devtype] => 1
    [attribstatus] => 0
    [measures] => {“measures”:[{“value”:80050,”type”:1,”unit”:-3},{“value”:239,”type”:16,”unit”:0}]}
    )

    Response:
    {
    "status": 0
    }

    Remarks:
    – Measurement from scales, type: 1 seems like weight
    – Response from server is just the usual “ACK”

  5. Another request to /cgi-bin/maint, ignored for now.
  6. Request to /cgi-bin/session:

    POST: Array
    (
    [action] => delete
    [sessionid] => 7bf5-382a9b74-183dcfe5
    )

    Response:
    {
    "status": 0
    }

    Remarks:
    – Logoff from service
    – Response from server is just the usual “ACK”

5. The data
Since the device can buffer some measurements, it can happen that multiple results are transfered at once.

Once the data is saved, its up to you how to analyze it. More to come (like my index.php).