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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
|
local os = require "os"
local datetime = require "datetime"
local nmap = require "nmap"
local match = require "match"
local math = require "math"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Retrieves information (including system architecture, process ID, and
server time) from distributed memory object caching system memcached.
]]
---
-- @usage
-- nmap -p 11211 --script memcached-info
--
-- @output
-- 11211/udp open unknown
-- | memcached-info:
-- | Process ID: 18568
-- | Uptime: 6950 seconds
-- | Server time: 2018-03-02T03:35:09
-- | Architecture: 64 bit
-- | Used CPU (user): 0.172010
-- | Used CPU (system): 0.200012
-- | Current connections: 10
-- | Total connections: 78
-- | Maximum connections: 1024
-- | TCP Port: 11211
-- | UDP Port: 11211
-- |_ Authentication: no
--
-- @xmloutput
-- <elem key="Process ID">17307</elem>
-- <elem key="Uptime">10662 seconds</elem>
-- <elem key="Server time">2018-03-01T16:46:59</elem>
-- <elem key="Architecture">64 bit</elem>
-- <elem key="Used CPU (user)">0.212809</elem>
-- <elem key="Used CPU (system)">0.157151</elem>
-- <elem key="Current connections">5</elem>
-- <elem key="Total connections">11</elem>
-- <elem key="Maximum connections">1024</elem>
-- <elem key="TCP Port">11211</elem>
-- <elem key="UDP Port">11211</elem>
-- <elem key="Authentication">no</elem>
author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"discovery", "safe"}
portrule = shortport.port_or_service(11211, "memcached", {"tcp", "udp"})
local filter = {
["pid"] = { name = "Process ID" },
["uptime"] = { name = "Uptime", func = function(v) return ("%d seconds"):format(v) end },
["time"] = { name = "Server time", func = datetime.format_timestamp },
["pointer_size"] = { name = "Architecture", func = function(v) return v .. " bit" end },
["rusage_user"] = { name = "Used CPU (user)" },
["rusage_system"] = { name = "Used CPU (system)"},
["curr_connections"] = { name = "Current connections"},
["total_connections"] = { name = "Total connections"},
["maxconns"] = { name = "Maximum connections" },
["tcpport"] = { name = "TCP Port" },
["udpport"] = { name = "UDP Port" },
["auth_enabled_sasl"] = { name = "Authentication" }
}
local order = {
"pid", "uptime", "time", "pointer_size", "rusage_user", "rusage_system",
"curr_connections", "total_connections", "maxconns", "tcpport", "udpport",
"auth_enabled_sasl"
}
local function fail(err) return stdnse.format_output(false, err) end
local function mergetab(tab1, tab2)
for k, v in pairs(tab2) do
tab1[k] = v
end
return tab1
end
local Comm = {
new = function(self, host, port, options)
local o = { host = host, port = port, options = options or {}}
self.protocol = port.protocol
self.req_id = math.random(0,0xfff)
setmetatable(o, self)
self.__index = self
return o
end,
connect = function(self)
self.socket = nmap.new_socket(self.protocol)
self.socket:set_timeout(self.options.timeout or stdnse.get_timeout(self.host))
return self.socket:connect(self.host, self.port)
end,
exchange = function(self, data)
local req_id = self.req_id
self.req_id = req_id + 1
if self.protocol == "udp" then
data = string.pack(">I2 I2 I2 I2",
req_id, -- request ID
0, -- sequence number
1, -- number of datagrams
0 -- reserved, must be 0
) .. data
end
local status = self.socket:send(data)
if not status then
return false, "Failed to send request to server"
end
if self.protocol == "udp" then
local msgs = {}
local dgrams = 0
repeat
local status, response = self.socket:receive_bytes(8)
if not status then return false, "Failed to receive entire response" end
local resp_id, seq, ndgrams, pos = string.unpack(">I2 I2 I2 xx", response)
if resp_id == req_id then
dgrams = ndgrams
msgs[seq+1] = string.sub(response, pos)
end
until #msgs >= dgrams
return true, table.concat(msgs)
end
-- pattern matches ERR or ERROR at the beginning of a string or after a newline
return self.socket:receive_buf(match.pattern_limit("%f[^\n\0]E[NR][DR]O?R?\r\n", 2048), true)
end,
}
local function parseResponse(response, expected)
local kvs = {}
for k, v in response:gmatch(("%%f[^\n\0]%s ([^%%s]*) (.-)\r\n"):format(expected)) do
stdnse.debug1("k=%s, v=%s", k, v)
kvs[k] = v
end
return kvs
end
action = function(host, port)
local client = Comm:new(host, port)
local status = client:connect()
if ( not(status) ) then
return fail("Failed to connect to server")
end
local request_time = os.time()
local status, response = client:exchange("stats\r\n")
if ( not(status) ) then
return fail(("Failed to send request to server: %s"):format(response))
end
local kvs = parseResponse(response, "STAT")
if kvs.time then
datetime.record_skew(host, kvs.time, request_time)
end
local status, response = client:exchange("stats settings\r\n")
if ( not(status) ) then
return fail(("Failed to send request to server: %s"):format(response))
end
local kvs2 = parseResponse(response, "STAT")
kvs = mergetab(kvs, kvs2)
local result = stdnse.output_table()
for _, item in ipairs(order) do
if ( kvs[item] ) then
local name = filter[item].name
local val = ( filter[item].func and filter[item].func(kvs[item]) or kvs[item] )
result[name] = val
end
end
return result
end
|