notes

  • Home
  • Blog
  • Github
  • Dash Button Corral

    August 2015

    Amazon’s new Dash Button things are pretty cool. I bought a couple and wanted to keep the original firmware on them to not take them apart, but still control them and have them talk to my server rather than Amazon’s. So I set up my home router to be a “network of lies” to trick it into talking to my server as if it was an Amazon one. Here I’ll describe how you can do this too. For reference, my home router runs OpenWRT so this was all done with it. These changes might be possible with other Linux-based routers but I love OpenWRT and know it best so it’s what I used.

    First thing is to set up your Button. Download the Amazon Shopping app on your iPhone or Android and set up the device as per the instructions. You can stop it at the last step to select a product if you want, or go ahead and select one if you want to use it as intended later. Just make sure the SSID you have them going to is your OpenWRT router that you control.

    By running WireShark and ettercap, we can observe how the Dash Buttons behave. They phone home to “parker-gateway-na.amazon.com”. But they look up the IP for that domain by talking to Google’s 8.8.8.8 DNS servers. The Button doesn’t use the DNS server given by your home DHCP server, it insists on using Google’s. That’s pretty smart. But we can get around that.

    So we need to build fences for a corral within to keep our Buttons from roaming around, or upgrading their firmware. The first step is to trick it from using Google’s DNS servers. Instead, we want it to hit our own. For this, we SSH into our OpenWRT router and edit “/etc/firewall.user”. We tell it to redirect all traffic (TCP and UDP, DNS is UDP) to our DNS server when someone tries Google’s. So add these two lines:

    iptables -t nat -I PREROUTING -j DNAT --destination 8.8.8.8 --to 192.168.1.1
    iptables -t nat -I PREROUTING -j DNAT --destination 8.8.4.4 --to 192.168.1.1
    

    Then apply those changes by running

    /etc/init.d/firewall restart
    

    Now when the Button contacts 8.8.8.8, it’ll actually be talking to our router’s DNS server on 192.168.1.1. If yours is different, then change those iptables commands to point to your preferred DNS server.

    The second part of our corral is to make “parker-gateway-na.amazon.com” point to a server of our choice. To do that, I edited “/etc/config/dhcp” on my router which is my router’s DNS server config. In the “config dnsmasq” section, I added this line: list address '/parker-gateway-na.amazon.com/192.168.1.88'

    This tells my local DNS server that when it gets asked about “parker-gateway-na.amazon.com” it should tell everyone it’s on 192.168.1.88. You can change this to be some machine on your network, or another IP like your EC2 instance or Digital Ocean droplet or whatever. Apply the change by running /etc/init.d/dnsmasq restart

    You should now be able to test this on your machine. Run dig @8.8.8.8 parker-gateway-na.amazon.com This command tells ‘dig’ to do a DNS lookup using 8.8.8.8 as the DNS server and look for ‘dig parker-gateway-na.amazon.com’. It should resolve as an A record to that IP you gave to dnsmasq.

    If that’s ready and working, now you can set up your target server to serve your Buttons. I did this using nginx, and here is what my config file looks like:

    server {
      listen 80;
      listen [::]:80;
    
      # SSL configuration
      #
      listen 443 ssl;
      listen [::]:443 ssl;
      #
      # Note: You should disable gzip for SSL traffic.
      # See: https://bugs.debian.org/773332
      #
      # Read up on ssl_ciphers to ensure a secure configuration.
      # See: https://bugs.debian.org/765782
      #
      # Self signed certs generated by the ssl-cert package
      # Don't use them in a production server!
      #
      include snippets/snakeoil.conf;
    
      root /code/amazon-dash/www;
    
      # Add index.php to the list if you are using PHP
      index index.php index.html index.htm index.nginx-debian.html;
    
      server_name parker-gateway-na.amazon.com;
    
      # To allow POST on static pages
      error_page  405     =200 $uri;
    
      rewrite ^/2/b$ /dash.php;
    
      location / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
      }
    
      # pass the PHP scripts to FastCGI server listening on the php-fpm socket
      location ~ \.php$ {
        try_files $uri =404;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
      }
    }
    

    I’m just using PHP for this, and as you can see, I host it at “/code/amazon-dash/www”. But you can put your files wherever you want. It redirects the path that the button looks for at /2/d to “dash.php”

    Here is the code in “dash.php”

    <?php
    
    $out = fopen("/tmp/dash_request.txt", "w");
    $req = print_r($_SERVER, true);
    $req .= print_r($_POST, true);
    $bindata = file_get_contents('php://input');
    $bb = print_r(str_split($bindata), true);
    $id = substr($bindata, 6, 16);
    
    $idd = sprintf("id: %s \n\n", $id);
    
    fwrite($out, $req."\n\n".$idd."\n");
    fclose($out);
    
    $msg="";
    if($id == "G030G0055XXXXXXXX")
    {
            $output = shell_exec("bash /code/amazon-dash/scripts/x.sh 2>&1");
    }
    else if($id == "G030G0055YYYYYY")
    {
            $output = shell_exec("bash /code/amazon-dash/scripts/y.sh 2>&1");
    }
    
    print($output);
    ?>
    

    I am doing my best to parse out the unique ID given to each Dash button. This is that “DSN” code printed on the outside of the box it comes in. That way you can tell multiple buttons apart. And in this case, I just have the different buttons executing different shell scripts. So you can make them do whatever you want. I also set up some debugging so that info about the requests are logged to “/tmp/dash_request.txt”. I figured out only enough of the protocol to extract the unique ID. But it’s binary and I’m sure there are lots of interesting things in there that I’m missing out on. Remaining battery for example is probably one of them.

    But anyway, at this point you have your little Dash Button corral to keep the buttons you own from running away. Plus they can do whatever you want them to do. If your scripts return a 200 OK HTTP response, the Buttons will even light up green to say that all is cool.

    Enjoy.

    Note that I don’t know how long this will last. I very much expect Amazon to issue a firmware upgrade to fix this. One thing is that the Buttons talk to parker-gateway-na.amazon.com using HTTPS. And if you take a look under the skirt of the firmware that dekuNukem kindly posted to https://github.com/dekuNukem/Amazon_Dash_Button (you can run the “strings” command on the Cottonelle.bin file he has) you’ll notice it has quite a few certificates in there. Luckily for us, in the initial release the Amazon engineers didn’t bother validating the certificates, which they probably should have. This lets our Nginx server to sit there and pretend to be “parker” and present bullshit self-signed certificates which the Dash Buttons are happily expecting. I think one of the first changes in a subsequent firmware upgrade will be to address this. But once you corral them in on your network, you can keep them from doing this.

  • Creating a Vagrant virtual machine for OpenWRT

    June 2014

    Since my last post, there have been many great improvements to Micah’s Fadecandy project and the “fcserver” application I ported for Atheros-based OpenWRT devices. I didn’t really keep up with things and only just released builds with the newer versions. They’re at the same download URLs, just in release2. https://github.com/nemik/fadecandy-openwrt/releases/tag/release2

    To keep this from happening again, it seems like a good idea to show people how to make their own OpenWRT builds with “fcserver” and other things they might want. The buildroot for OpenWRT is simple but can still be a challenge sometimes. That’s why the Vagrant http://www.vagrantup.com/ virtual machine helper is so awesome. It makes using virtual machines very easy on just about every platform. Unfortunately the complete Vagrant box with OpenWRT buid-root is over 2.7GB and is too much for me to host here for download. If anyone wants to help with this, please get in touch. But in the meantime, I’ll show you how to do the same thing yourself.

    1. Download and install Vagrant for your OS from http://www.vagrantup.com/
    2. Set up a Debian-Wheezy-based Vagrant box. Download “Debian Wheezy 7.2 amd64 (VirtualBox Guest Additions 4.3.0) (2013/10/19)” from http://www.vagrantbox.es/
    3. Save this to some directory for this project, call it “openwrt_vagrant”
    4. Run vagrant box add fadecandy-openwrt debian-7.2.0.box to add the box
    5. Download my Vagrantfile from http://nemik.net/x/Vagrantfile into the directory. This is the config for it, it will make the virtual machine run on IP “192.168.44.10”
    6. Run vagrant init fadecandy-openwrt to initialize the box
    7. At this point it should be all set up, so let’s start the virtual machine with vagrant up
    8. The box should come up successfully, and when it does, SSH into it with vagrant ssh. You can also do this by doing: ssh -i ~/.vangrant.d/insecure_private_key vagrant@192.168.44.10
    9. Now we’re inside the Debian system! We should update it with sudo apt-get update and then sudo apt-get upgrade
    10. The upgrades will require a restart of the box. Exit from it with Ctrl+d or the exit and then shut down the vagrant machine with vagrant halt. Then start it up again with vagrant up and SSH back in like in step 8
    11. When SSH’d back into the box, let’s install the build dependencies needed for building OpenWRT. sudo apt-get install subversion build-essential git-core libncurses5-dev zlib1g-dev gawk
    12. Now let’s get OpenWRT source with git clone git://git.openwrt.org/openwrt.git
    13. Go into the new source directory with cd openwrt
    14. Add my package to the feeds. Edit the “feeds.conf.default” file with vim or nano and add the line “src-git fadecandy git://github.com/nemik/fadecandy-openwrt.git” to the file
    15. Update and install the packages from the feeds by running ./scripts/feeds update -a followed by ./scripts/feeds install -a
    16. It’s now time to set up the OpenWRT build configuration. Run make menuconfig
    17. In the menu, change your “Target Profile” to the device you’re targeting, like say a TP-Link TL-MR3040
    18. Turn off the firewall too so we don’t have to open more ports. Go to “Base System” and press space until “firewall” isn’t selected anymore.
    19. Go to “Utilities” and scroll down to “fcserver”. Press space on the option until it’s a star “*” and then exit from the menu and save it.
    20. Download some static files to go into the OpenWRT OS from http://nemik.net/x/openwrt-fc-files.tar.gz. Run wget http://nemik.net/x/openwrt-fc-files.tar.gz and then tar xzvf openwrt-fc-files.tar.gz These file set up the default root password, wifi access point, and things like that.
    21. Now to finally do the build. For this, just run make
    22. That should be it! If this finished successfully, you’ll have a build in “bin/ar71xx/” with the name “openwrt-ar71xx-generic-tl-mr3040-v2-squashfs-sysupgrade.bin” just like the ones I’ve been releasing.
  • Standalone OpenWRT Fadecandy Server for LED control

    February 2014

    I recently discovered Micah’s awesome Fadecandy USB controller for WS281x LED pixels. One of the things that I like the most about it is its “fcserver” to control LED pixels using Websockets. That is fantastic, but all implementations I’ve seen have people running it on a RaspberryPi or regular PC.

    I wanted to create a sort of “stand-alone” and embedded version of this using less expensive TP-Link routers, running OpenWRT. My current favourite of these is the TP-LINK TL-MR3040 but it would work just as well with the infamous WR703n or others; so long as they have USB support.

    To do this, I created an OpenWRT package of the fcserver code: https://github.com/nemik/fadecandy-openwrt You can add this to your own OpenWRT build by adding the line src-git fadecandy git://github.com/nemik/fadecandy-openwrt.git to your “feeds.conf.default”. Then run ./scripts/feeds update -a, then ./scripts/feeds install -a. Then run make menuconfig. Select “fcserver” from “Utilities” and your target architecture/device. Then run make

    Or, just get my pre-built binaries from here: TP-Link MR3040 v1 TP-Link MR3040 v2 TP-Link MR3020 TP-Link WR703N

    and flash them to your unit.

    The defaults are: * root password is “root” * Open wifi access point called “Fadecandy” is broadcast. You can connect to it and get an IP * The ethernet port is configured to accept DHCP leases, so if you plug it into your local router, it can give it an IP and you can get to it remotely then

    That’s it. Then just plug in the Fadecandy USB device into your OpenWRT device’s USB port. It should detect it. Then you can connect to its “Fadecandy” access point over Wifi and visit “fadecandy:7890” to get to the Fadecandy server interface. Unfortunately, my WS2812 LEDs and Fadecandy controller have not yet arrived, but I do have a Teensy3. When I flashed it with the Fadecandy firmware version 1.07 (using TeensyLoader) it worked and blinked to identify itself from the web UI! So I think that means it is working? If anyone could try it out with their pixels, please let me know how it works out for you. I hope performance is alright. Otherwise, I’ll be reporting back on it once my equipment arrives.

    I like this approach because the OpenWRT units are very cheap, small, low power, and provide great connectivity. They also use a 5V source like many LED pixel strings, so they can be more easily integrated and embedded into art pieces and installations.

  • Whitelisting IPs in Apache and RHEL

    November 2012

    I had a problem recently where I wanted to whitelist a set of IPs (and that set may change anytime) to allow them to use a reverse-proxy on Apache in an RHEL (Red Hat Enterprise Linux) environment. Here’s some instructions on how to do this yourself.

    1. So the first step is to edit your Apache config. I was doing this in an SSL context so I edited the /etc/httpd/conf.d/ssl/conf file and put this in the VirtualHost part: ``` RewriteEngine On

    RewriteMap ipslist txt:/var/www/whitelist.txt RewriteCond %{REMOTE_ADDR} ^(.*)$ RewriteCond ${ipslist:%1|black} ^black$ [NC] RewriteRule ^ - [F] ```

    The first line turns on Apache’s Rewrite module, this is required for this setup to work. Then it creates a RewriteMap called ‘ipslist’ located at /var/www/whitelist.txt. I advise putting this in /var/www because if done in some other /home directory, it won’t work with SELinux enabled as it won’t allow Apache to access files there. Chmod’ing the whitelist.txt to 664 is a decent permission.

    1. Create your whitelist. This is the “/var/www/whitelist.txt” file. For this, just put in the IP you want whitelisted followed by a space then “white”. Then the next IP on the next line. So a sample one could be: 192.168.1.1 white 55.44.33.22 white and so on.

    2. Restart Apache, just do “sudo /etc/init.d/httpd restart”

    3. That should be it! All the rest of your config can be as it is, including proxying functionality or whatever because the URL rewriting happens before all that.

    The nice thing about the “RewriteMap” function is that you can change the whitelist.txt anytime you want and you don’t have to restart or reload Apache! It’ll automatically pick up on the change from the file’s changed date and update it.

  • Belkin Wemo Teardown part 2

    July 2012

    Made progress today with using UPnP to communicate with the Wemo.

    I used the awesome miranda-upnp Python library to explore the device. Its URL for listing services is at http://10.22.22.1:49152/setup.xml

    There are ones for “rules”, “remoteaccess”, “metainfo”, “WiFiSetup”, “firmwareupdate”, “timesync” and “basicevent”.

    The most interesting one, I’m guessing, is “basicevent” because it contains an action called “SetBinaryState” and “GetBinaryState” on top of others like “GetSerialNo”, “GetMacAddr” etc. But the BinaryState thing looks cool because I’m guessing it is what is used to control the switch.

    Unfortunately, sending commands/actions to this service always returns a 500 error, with error code -111 is “Invalid Service” although from all indications I am sending the SOAP request to the proper URL and service. I tried it manually and through miranda-upnp, same results. At this point I think it is a header I might be missing, perhaps the “User-Agent”? And without the one the device is expecting, it will reject with a 500?

    I unfortunately don’t have an iOS device to test with. Has anyone with an iOS device and Belkin Wemo sniffed the traffic between them? If I can get my hands on one, next step would be use Ettercap and Wireshark on that same wifi AP to see what the iOS app is sending that I’m not.

  • « Previous