1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
|