Deobfuscating the Edimax SP-2101W cloud protocol

As i mentioned in another post, the Edimax SP-2101W sends out packets to the cloud, that are obfuscated somewhat, but not really encrypted.

I don’t really understand why the edimax engieers did that; In my opinion, they chose the worst path they could go. If you’re sending data to the cloud:

  • either, send plain text, and tell everyone what you’re sending and when you’re sending it. That way, nothing is hidden, and anyone can check your claim that nothing harmful is sent.
  • or, use strong encryption for your data, and tell everyone how you’re encrypting it, so people can have confidence that noone will be able to read what you’re sending.

By obfuscating the data in a really primitive way, you don’t protect customer’s data, but you deprive people of checking what’s actually sent. This just builds distrust, and will give you a “XXX’s cloud encryption hacked” headline some day in the – not too far – future.

Anyway, here’s how it works:

  • Every 10 seconds, the device sends a UDP packet to www.myedimax.com. This packet doesn’t contain any “real” data, it’s just 4 bytes payload, and it seems to serve only to keep the udp connection within your firewall/router open.
  • Whenever the cloud and the device communicate data, they use obfuscated XML data. The first byte is always a '<' (hex 3c), just as XML data would assume, plus a random value between 1 and 7. The receiver can retrieve this value by subtracting 0x3c from the 1st byte it receives.
  • Every byte from the 2nd one is bit-rotated by the number retrieved in the first step.

So, for example, the device might send the hex string

3d38b039b0b61f1eb1b732b2103bb036bab29e11991818181110971f1e9738b039b0b61f

The first byte is 0x3d, 0x3c+1, which means the other bytes are rotated to the right by 1 bit.

The second byte, 0x38, must be rotated to the left by one bit, which yields 0x70, or 'p'.

The third byte, rotated to the left, is 0x41, or 'a'.

Continuing with this, the whole message reads

<param><code value="3000" /></param>

A longer example has all bytes rotated by 5 bits:

41830b930b6bf150e11b7b232b01b30b63ab2be91189818981110179f150
e16b7b232b6301b30b63ab2be9119a826991898189ba110179f150e14b23
01b30b63ab2be911c18189328191320ab1a10aa9110179f150e1a3cb832b
01b30b63ab2be9119a6b0b93a38263ab3b110179f150e10b634b0b9b01b3
0b63ab2be911a2937b1b5b732b93110179f150e1630b734b8301b30b63ab
2be91189c9917189b1c1718171c181110179f150e1630b73837b93a301b3
0b63ab2be911a1c98999b1110179f150e19b7301b30b63ab2be9115a5a5a
5a5a5a5a5a110179f150e12b731b93cb83a34b7b7301b30b63ab2be91181
110179f150e1730ba3a3cb832b01b30b63ab2be911b9110179f150e1232b
b333bbb32b9301b30b63ab2be9118971819919818981818189110179f150
e183937b23ab1ba34b2301b30b63ab2be9112a224a6a0ac2199a82699189
8189ba198971811989718199110179f150e179830b930b6bf1

which decodes to

<param>
<code value="1010" />
<model value="SP-2101W" />
<id value="801F02FA64A5" />
<type value="SmartPlug" />
<alias value="Trockner" />
<lanip value="192.168.0.80" />
<lanport value="49136" />
<sn value="KKKKKKKK" />
<encryption value="0" />
<nattype value="7" />
<devfwver value="1.03#010001" />
<productid value="EDIMAX#SP-2101W#1.0#1.03" />
</param>

I wrote a C program to help in decoding, if anyone is interested. Pass the hex string as argument on the command line; if no argument is present, the test string will be decoded.

char *teststr="41830b930b6bf150e11b7b232b01b30b63ab2be91189818981110179f150"
        "e16b7b232b6301b30b63ab2be9119a826991898189ba110179f150e14b23"
        "01b30b63ab2be911c18189328191320ab1a10aa9110179f150e1a3cb832b"
        "01b30b63ab2be9119a6b0b93a38263ab3b110179f150e10b634b0b9b01b3"
        "0b63ab2be911a2937b1b5b732b93110179f150e1630b734b8301b30b63ab"
        "2be91189c9917189b1c1718171c181110179f150e1630b73837b93a301b3"
        "0b63ab2be911a1c98999b1110179f150e19b7301b30b63ab2be9115a5a5a"
        "5a5a5a5a5a110179f150e12b731b93cb83a34b7b7301b30b63ab2be91181"
        "110179f150e1730ba3a3cb832b01b30b63ab2be911b9110179f150e1232b"
        "b333bbb32b9301b30b63ab2be9118971819919818981818189110179f150"
        "e183937b23ab1ba34b2301b30b63ab2be9112a224a6a0ac2199a82699189"
        "8189ba198971811989718199110179f150e179830b930b6bf1";

int main(int argc, char **argv) {
  char *bytes=teststr;
  if (argc>1)
    bytes=*++argv;
  int rotate=0;
  while (*bytes && bytes[1]) {
    int val;
    sscanf(bytes, "%02x", &val);
    if (rotate==0) {
      rotate=val-'<';
      val='<';
    } else {
      val<<=rotate;
      val=(val&0xff) | ((val&0xff00)>>8);
    }
    putchar(val);
    bytes+=2;
  }
  putchar('\n');
}

When your tablet communicates with the edimax, it uses the LAN if it can; if it can’t, it’ll contact myedimax.com as its proxy. The proxy will then send a message via udp, which, decoded, reads:

<param><code value="1040" /><devip value="92.211.XX.YYY" /><devport value="58296" /><reqip value="80.187.XX.YYY" /><reqport value="31228" /><connip value="80.187.XX.YYY" /><connport value="31228" /><samelan value="0" /><relayip value="122.248.234.231" /><relaydevport value="8767" /><relayreqport value="8768" /><relayid value="e231d35c.e3b8.fb6abb50.79fc" /><nettype value="D" /><natweight value="14" /><reqfwver value="1.0#010000" /><reqdirport value="0" /><auth value="38f989453c733de4afaf64XXXXXXXXXX" /><seq value="801F02FA70BF141XXXXXXXXXX" /><delay value="500" /><relaytype value="" /><punchingtime value="" /></param>

This will cause the Edimax to open a TCP connection to the ip referenced in the UDP packet, and the cloud will send XML requests which are identical to the HTTP post content to the smartplug.cgi service. For example:

<?xml version="1.0" encoding="UTF8"?><SMARTPLUG id="edimax"><CMD id="get"><NOW_POWER><Device.System.Power.NowCurrent></Device.System.Power.NowCurrent><Device.System.Power.NowPower></Device.System.Power.NowPower></NOW_POWER></CMD></SMARTPLUG>

To which the Edimax responds with:

PnvDataLen: 445

plus a '<' (that has a 1-7 added to it, as above) plus the encoded response of the web server, e.g.:

HTTP/1.1 200 OK
Content-Type: application/xml; charset=utf-8
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 253
Date: Fri, 05 Dec 2014 21:40:03 GMT
Server: lighttpd/1.4.31

<?xml version="1.0" encoding="UTF8"?><SMARTPLUG id="edimax"><CMD 
id="get"><NOW_POWER><Device.System.Power.NowCurrent>0.7604</Device.Syste 
m.Power.NowCurrent><Device.System.Power.NowPower>148.77</Device.System.P 
ower.NowPower></NOW_POWER></CMD></SMARTPLUG>

If you want to check the protocol yourself, you can add a dissector i've written to wireshark to see the decoded messages directly. Just put the init.lua file into $HOME/wireshark in Linux, or into
C:\Users\XX-USERNAME-XX\AppData\Roaming\Wireshark
in windows.

2 thoughts on “Deobfuscating the Edimax SP-2101W cloud protocol

Leave a Reply

Your email address will not be published. Required fields are marked *