summaryrefslogtreecommitdiffstats
path: root/nselib/anyconnect.lua
diff options
context:
space:
mode:
Diffstat (limited to 'nselib/anyconnect.lua')
-rw-r--r--nselib/anyconnect.lua144
1 files changed, 144 insertions, 0 deletions
diff --git a/nselib/anyconnect.lua b/nselib/anyconnect.lua
new file mode 100644
index 0000000..5f05d86
--- /dev/null
+++ b/nselib/anyconnect.lua
@@ -0,0 +1,144 @@
+---
+-- This library implements HTTP requests used by the Cisco AnyConnect VPN Client
+--
+-- @author Patrik Karlsson <patrik@cqure.net>
+--
+-- @args anyconnect.group AnyConnect tunnel group (default: VPN)
+-- @args anyconnect.mac MAC address of connecting client (default: random MAC)
+-- @args anyconnect.version Version of connecting client (default: 3.1.05160)
+-- @args anyconnect.ua User Agent of connecting client (default: AnyConnect Darwin_i386 3.1.05160)
+
+local http = require('http')
+local stdnse = require('stdnse')
+local url = require('url')
+local table = require('table')
+local rand = require "rand"
+
+local args_group= stdnse.get_script_args('anyconnect.group') or "VPN"
+local args_mac= stdnse.get_script_args('anyconnect.mac')
+local args_ver = stdnse.get_script_args('anyconnect.version') or "3.1.05160"
+local args_ua = stdnse.get_script_args('anyconnect.ua') or ("AnyConnect Darwin_i386 %s"):format(args_ver)
+
+_ENV = stdnse.module("anyconnect", stdnse.seeall)
+
+Cisco = {
+
+ Util = {
+
+ generate_mac = function()
+ return stdnse.format_mac(rand.random_string(6))
+ end,
+
+ },
+
+ AnyConnect = {
+
+ new = function(self, host, port)
+ local o = { host = host, port = port }
+ setmetatable(o, self)
+ self.__index = self
+ return o
+ end,
+
+ -- generate a random hex-string of length 'length'
+ --
+ generate_random = function(length)
+ return rand.random_string(length * 2, '0123456789ABCDEF')
+ end,
+
+ connect = function(self)
+ args_mac = args_mac or Cisco.Util.generate_mac()
+ local headers = {
+ ['User-Agent'] = args_ua,
+ ['Accept'] = '*/*',
+ ['Accept-Encoding'] = 'identity',
+ ['X-Transcend-Version'] = 1,
+ ['X-Aggregate-Auth'] = 1,
+ ['X-AnyConnect-Platform'] = 'mac-intel'
+ }
+
+ local data = ([[<?xml version="1.0" encoding="UTF-8"?>
+<config-auth client="vpn" type="init" aggregate-auth-version="2">
+<version who="vpn">%s</version>
+<device-id device-type="MacBookAir4,1" platform-version="10.9.2" unique-id="%s">mac-intel</device-id>
+<mac-address-list>
+<mac-address>%s</mac-address></mac-address-list>
+<group-select>%s</group-select>
+<group-access>https://%s:%s</group-access>
+</config-auth>]]):format(args_ver, self.generate_random(64), args_mac, args_group, self.host.ip, self.port.number)
+
+ local options = { header=headers , no_cache=true, redirect_ok = function(host,port)
+ local c = 5
+ return function(url)
+ if ( c==0 ) then return false end
+ c = c - 1
+ return true
+ end
+ end
+ }
+
+ local path = '/'
+ local response = http.head(self.host, self.port, path, options)
+ -- account for redirects
+ if response.status ~= 200 then
+ return false, "Failed to connect to SSL VPN server"
+ elseif response.location then
+ local u = url.parse(response.location[#response.location])
+ if u.host then
+ self.host = u.host
+ end
+ if u.path then
+ path = u.path
+ end
+ end
+
+ response = http.post(self.host, self.port, path, options, nil, data)
+
+ if response.status ~= 200 or response.body == nil then
+ return false, "Not a Cisco ASA or unsupported version"
+ end
+
+ local xmltags = {
+ 'version',
+ 'tunnel-group',
+ 'group-alias',
+ 'config-hash',
+ 'host-scan-ticket',
+ 'host-scan-token',
+ 'host-scan-base-uri',
+ 'host-scan-wait-uri',
+ 'banner'
+ }
+
+ self.conn_attr = {}
+ for _, tag in ipairs(xmltags) do
+ local body = response.body:gsub('\r?\n', '')
+ local filter = ("<%s.->(.*)</%s>"):format(tag:gsub('-', '%%-'), tag:gsub('-', '%%-'))
+ local m = body:match(filter)
+ if m then
+ self.conn_attr[tag] = m
+ end
+ end
+
+ if not self.conn_attr['version'] then
+ return false, "Not a Cisco ASA or unsupported version"
+ end
+
+ -- in case we were redirected
+ self.conn_attr['host'] = stdnse.get_hostname(self.host)
+ return true
+ end,
+
+ ---
+ -- Returns the version of the remote SSL VPN concentrator
+ -- @return table containing major, minor and rev numeric values
+ get_version = function(self)
+ local ver = {}
+ ver['major'], ver['minor'], ver['rev'] = self.conn_attr['version']:match('^(%d-)%.(%d-)%((.*)%)$')
+ return ver
+ end
+
+ }
+}
+
+return _ENV