mirror of
https://github.com/nmap/nmap.git
synced 2026-01-24 15:19:03 +00:00
merge soc07 r5049:5063 - added string split/join methods; Bruteforce telnet script; fixed a few typos; updated to escape some common url constructs; refactored shorport library; Added a family of string buffer functions to nselib as concatenation is not efficient; Updated a couple of scripts to use string buffers; resolved a couple of naming conflicts
This commit is contained in:
@@ -1,62 +1,58 @@
|
||||
module(...)
|
||||
module('shortport', package.seeall)
|
||||
|
||||
protorule = function(service, proto, state)
|
||||
return function(host,port)
|
||||
state = state or "open"
|
||||
proto = proto or "tcp"
|
||||
if port.service==service
|
||||
and port.protocol == proto
|
||||
and port.state == state
|
||||
then
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
end
|
||||
end
|
||||
end
|
||||
portnumber = function(port, _proto, _state)
|
||||
local port_table;
|
||||
local state = _state or "open"
|
||||
local proto = _proto or "tcp"
|
||||
|
||||
portnumber = function(number, proto, state)
|
||||
return function(host,port)
|
||||
state = state or "open"
|
||||
proto = proto or "tcp"
|
||||
if port.number==number
|
||||
and port.protocol == proto
|
||||
and port.state ==state
|
||||
then
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
end
|
||||
end
|
||||
end
|
||||
if(type(port) == "number") then
|
||||
port_table = {port}
|
||||
elseif(type(port) == "table") then
|
||||
port_table = port
|
||||
end
|
||||
|
||||
port_in_list = function(proto, ...)
|
||||
local list={...}
|
||||
return function(host,port)
|
||||
if not port.protocol==proto
|
||||
then
|
||||
return false
|
||||
end
|
||||
for _, v in ipairs(list) do
|
||||
if port.number == v then
|
||||
return true
|
||||
return function(host, port)
|
||||
if(port.protocol == proto and port.state == state) then
|
||||
for _, _port in ipairs(port_table) do
|
||||
if(port.number == _port) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
port_or_service = function(number, service, proto, state)
|
||||
service = function(service, _proto, _state)
|
||||
local service_table;
|
||||
local state = _state or "open"
|
||||
local proto = _proto or "tcp"
|
||||
|
||||
if(type(service) == "string") then
|
||||
service_table = {service}
|
||||
elseif(type(service) == "table") then
|
||||
service_table = service
|
||||
end
|
||||
|
||||
return function(host, port)
|
||||
state = state or "open"
|
||||
proto = proto or "tcp"
|
||||
if (port.number==number or port.service==service)
|
||||
and port.protocol==proto
|
||||
and port.state == state
|
||||
then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
if(port.protocol == proto and port.state == state) then
|
||||
for _, service in ipairs(service_table) do
|
||||
if(port.service == service) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
port_or_service = function(port, _service, proto, state)
|
||||
local port_checker = portnumber(port, proto, state)
|
||||
local service_checker = service(_service, proto, state)
|
||||
|
||||
return function(host, port)
|
||||
return port_checker(host, port) or service_checker(host, port)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,8 +9,89 @@ print_debug = function(...)
|
||||
nmap.print_debug_unformatted(verbosity, string.format(unpack(arg, start)));
|
||||
end
|
||||
|
||||
-- Concat the contents of the parameter list,
|
||||
-- separated by the string delimiter (just like in perl)
|
||||
-- example: strjoin(", ", {"Anna", "Bob", "Charlie", "Dolores"})
|
||||
function strjoin(delimiter, list)
|
||||
local len = getn(list)
|
||||
if len == 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
local string = list[1]
|
||||
for i = 2, len do
|
||||
string = string .. delimiter .. list[i]
|
||||
end
|
||||
|
||||
return string
|
||||
end
|
||||
|
||||
-- Split text into a list consisting of the strings in text,
|
||||
-- separated by strings matching delimiter (which may be a pattern).
|
||||
-- example: strsplit(",%s*", "Anna, Bob, Charlie,Dolores")
|
||||
function strsplit(delimiter, text)
|
||||
local list = {}
|
||||
local pos = 1
|
||||
|
||||
if strfind("", delimiter, 1) then -- this would result in endless loops
|
||||
error("delimiter matches empty string!")
|
||||
end
|
||||
|
||||
while 1 do
|
||||
local first, last = strfind(text, delimiter, pos)
|
||||
if first then -- found?
|
||||
tinsert(list, strsub(text, pos, first-1))
|
||||
pos = last+1
|
||||
else
|
||||
tinsert(list, strsub(text, pos))
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
-- String buffer functions. Concatenation is not efficient in
|
||||
-- lua as strings are immutable. If a large amount of '..'
|
||||
-- operations are needed a string buffer should be used instead
|
||||
|
||||
--[[
|
||||
local buf = strbuf.new()
|
||||
strbuf.add(buf, 'string') ; strbuf.add(buf, 'data')
|
||||
|
||||
print(buf) -- default seperator is a new line
|
||||
print(strbuf.dump(buf)) -- no seperator
|
||||
print(strbuf.dump(buf, ' ')) -- seperated by spaces
|
||||
strbuf.clear(buf)
|
||||
--]]
|
||||
|
||||
strbuf_dump = table.concat
|
||||
|
||||
function strbuf_new()
|
||||
local sbuf = {}
|
||||
sbuf.mt = {}
|
||||
setmetatable(sbuf, sbuf.mt)
|
||||
sbuf.mt.__tostring = function(s) return strbuf_dump(s, '\n') end
|
||||
return sbuf
|
||||
end
|
||||
|
||||
function strbuf_add(sbuf, s)
|
||||
if not (type(s) == 'string') or
|
||||
not (type(sbuf) == 'table') then
|
||||
return nil
|
||||
end
|
||||
table.insert(sbuf, s)
|
||||
return table.getn(sbuf)
|
||||
end
|
||||
|
||||
function strbuf_clear(sbuf)
|
||||
for i, v in pairs(sbuf) do
|
||||
sbuf[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- pseudo namespace for string buffers
|
||||
strbuf = { new=strbuf_new, add=strbuf_add, dump=strbuf_dump, clear=strbuf_clear }
|
||||
|
||||
-- Generic buffer implementation using lexical closures
|
||||
--
|
||||
|
||||
@@ -315,6 +315,10 @@ function parse_query(query)
|
||||
local parsed = {}
|
||||
local pos = 0
|
||||
|
||||
query = string.gsub(query, "&", "&")
|
||||
query = string.gsub(query, "<", "<")
|
||||
query = string.gsub(query, ">", ">")
|
||||
|
||||
function ginsert(qstr)
|
||||
local first, last = string.find(qstr, "=")
|
||||
if first then
|
||||
|
||||
@@ -11,6 +11,8 @@ license = "See nmaps COPYING for licence"
|
||||
|
||||
categories = {"discovery", "intrusive"}
|
||||
|
||||
require('stdnse')
|
||||
|
||||
portrule = function(host, port)
|
||||
if
|
||||
( port.number == 1433
|
||||
@@ -30,6 +32,7 @@ action = function(host, port)
|
||||
|
||||
-- create the socket used for our connection
|
||||
local socket = nmap.new_socket()
|
||||
local sb_add = stdnse.strbuf.add
|
||||
|
||||
-- set a reasonable timeout value
|
||||
socket:set_timeout(5000)
|
||||
@@ -46,24 +49,24 @@ action = function(host, port)
|
||||
local get_real_version = function(dst, dstPort)
|
||||
|
||||
local outcome
|
||||
local payload
|
||||
local payload = stdnse.strbuf.new()
|
||||
|
||||
local stat, resp
|
||||
|
||||
-- build a TDS packet - type 0x12
|
||||
-- copied from packet capture of osql connection
|
||||
payload = "\018\001\000\047\000\000\001\000\000\000"
|
||||
payload = payload .. "\026\000\006\001\000\032\000\001\002\000"
|
||||
payload = payload .. "\033\000\001\003\000\034\000\004\004\000"
|
||||
payload = payload .. "\038\000\001\255\009\000\011\226\000\000"
|
||||
payload = payload .. "\000\000\120\023\000\000\000"
|
||||
sb_add(payload, "\018\001\000\047\000\000\001\000\000\000")
|
||||
sb_add(payload, "\026\000\006\001\000\032\000\001\002\000")
|
||||
sb_add(payload, "\033\000\001\003\000\034\000\004\004\000")
|
||||
sb_add(payload, "\038\000\001\255\009\000\011\226\000\000")
|
||||
sb_add(payload, "\000\000\120\023\000\000\000")
|
||||
|
||||
socket = nmap.new_socket()
|
||||
|
||||
-- connect to the server using the tcpPort captured from the UDP probe
|
||||
try(socket:connect(dst, dstPort, "tcp"))
|
||||
|
||||
try(socket:send(payload))
|
||||
try(socket:send(stdnse.strbuf.dump(payload)))
|
||||
|
||||
-- read in any response we might get
|
||||
stat, resp = socket:receive_bytes(1)
|
||||
@@ -74,25 +77,26 @@ action = function(host, port)
|
||||
-- username = sa, blank password
|
||||
-- for information about packet structure, see http://www.freetds.org/tds.html
|
||||
|
||||
local query = "\016\001\000\128\000\000\001\000" -- TDS packet header
|
||||
query = query .. "\120\000\000\000\002\000\009\114" -- Login packet header = length, version
|
||||
query = query .. "\000\000\000\000\000\000\000\007" -- Login packet header continued = size, client version
|
||||
query = query .. "\140\018\000\000\000\000\000\000" -- Login packet header continued = Client PID, Connection ID
|
||||
query = query .. "\224\003\000\000\104\001\000\000" -- Login packet header continued = Option Flags 1 & 2, status flag, reserved flag, timezone
|
||||
query = query .. "\009\004\000\000\094\000\004\000" -- Login packet (Collation), then start offsets & lengths (client name, client length)
|
||||
query = query .. "\102\000\002\000\000\000\000\000" -- Login packet, offsets & lengths = username offset, username length, password offset, password length
|
||||
query = query .. "\106\000\004\000\114\000\000\000" -- Login packet, offsets & lengths = app name offset, app name length, server name offset, server name length
|
||||
query = query .. "\000\000\000\000\114\000\003\000" -- Login packet, offsets & lengths = unknown offset, unknown length, library name offset, library name length
|
||||
query = query .. "\120\000\000\000\120\000\000\000" -- Login packet, offsets & lengths = locale offset, locale length, database name offset, database name length
|
||||
query = query .. "\000\000\000\000\000\000\000\000" -- Login packet, MAC address + padding
|
||||
query = query .. "\000\000\000\000\000\000\000\000" -- Login packet, padding
|
||||
query = query .. "\000\000\000\000\000\000\078\000" -- Login packet, padding + start of client name (N)
|
||||
query = query .. "\077\000\065\000\080\000\115\000" -- Login packet = rest of client name (MAP) + username (s)
|
||||
query = query .. "\097\000\078\000\077\000\065\000" -- Login packet = username (a), app name (NMA)
|
||||
query = query .. "\080\000\078\000\083\000\069\000" -- Login packet = app name (P), library name (NSE)
|
||||
local query = stdnse.strbuf.new()
|
||||
sb_add(query, "\016\001\000\128\000\000\001\000") -- TDS packet header
|
||||
sb_add(query, "\120\000\000\000\002\000\009\114") -- Login packet header = length, version
|
||||
sb_add(query, "\000\000\000\000\000\000\000\007") -- Login packet header continued = size, client version
|
||||
sb_add(query, "\140\018\000\000\000\000\000\000") -- Login packet header continued = Client PID, Connection ID
|
||||
sb_add(query, "\224\003\000\000\104\001\000\000") -- Login packet header continued = Option Flags 1 & 2, status flag, reserved flag, timezone
|
||||
sb_add(query, "\009\004\000\000\094\000\004\000") -- Login packet (Collation), then start offsets & lengths (client name, client length)
|
||||
sb_add(query, "\102\000\002\000\000\000\000\000") -- Login packet, offsets & lengths = username offset, username length, password offset, password length
|
||||
sb_add(query, "\106\000\004\000\114\000\000\000") -- Login packet, offsets & lengths = app name offset, app name length, server name offset, server name length
|
||||
sb_add(query, "\000\000\000\000\114\000\003\000") -- Login packet, offsets & lengths = unknown offset, unknown length, library name offset, library name length
|
||||
sb_add(query, "\120\000\000\000\120\000\000\000") -- Login packet, offsets & lengths = locale offset, locale length, database name offset, database name length
|
||||
sb_add(query, "\000\000\000\000\000\000\000\000") -- Login packet, MAC address + padding
|
||||
sb_add(query, "\000\000\000\000\000\000\000\000") -- Login packet, padding
|
||||
sb_add(query, "\000\000\000\000\000\000\078\000") -- Login packet, padding + start of client name (N)
|
||||
sb_add(query, "\077\000\065\000\080\000\115\000") -- Login packet = rest of client name (MAP) + username (s)
|
||||
sb_add(query, "\097\000\078\000\077\000\065\000") -- Login packet = username (a), app name (NMA)
|
||||
sb_add(query, "\080\000\078\000\083\000\069\000") -- Login packet = app name (P), library name (NSE)
|
||||
|
||||
-- send the packet down the wire
|
||||
try(socket:send(query))
|
||||
try(socket:send(stdnse.strbuf.dump(query)))
|
||||
|
||||
-- read in any response we might get
|
||||
stat, resp = socket:receive_bytes(1)
|
||||
@@ -102,19 +106,20 @@ action = function(host, port)
|
||||
if string.match(resp, "S\000Q\000L\000") then
|
||||
outcome = "\n sa user appears to have blank password"
|
||||
|
||||
stdnse.strbuf.clear(query)
|
||||
-- since we have a successful login, send a query that will tell us what version the server is really running
|
||||
query = "\001\001\000\044\000\000\001\000" -- TDS Query packet
|
||||
query = query .. "\083\000\069\000\076\000\069\000" -- SELE
|
||||
query = query .. "\067\000\084\000\032\000\064\000" -- CT @
|
||||
query = query .. "\064\000\086\000\069\000\082\000" -- @VER
|
||||
query = query .. "\083\000\073\000\079\000\078\000" -- SION
|
||||
query = query .. "\013\000\010\000"
|
||||
sb_add(query, "\001\001\000\044\000\000\001\000") -- TDS Query packet
|
||||
sb_add(query, "\083\000\069\000\076\000\069\000") -- SELE
|
||||
sb_add(query, "\067\000\084\000\032\000\064\000") -- CT @
|
||||
sb_add(query, "\064\000\086\000\069\000\082\000") -- @VER
|
||||
sb_add(query, "\083\000\073\000\079\000\078\000") -- SION
|
||||
sb_add(query, "\013\000\010\000")
|
||||
|
||||
-- send the packet down the wire
|
||||
try(socket:send(query))
|
||||
try(socket:send(stdnse.strbuf.dump(query)))
|
||||
|
||||
-- read in any response we might get
|
||||
stat, resp = socket:receive_bytes(1)
|
||||
-- read in any response we might get
|
||||
stat, resp = socket:receive_bytes(1)
|
||||
|
||||
-- strip out the embedded \000 characters
|
||||
local banner = string.gsub(resp, "%z", "")
|
||||
|
||||
219
scripts/bruteTelnet.nse
Normal file
219
scripts/bruteTelnet.nse
Normal file
@@ -0,0 +1,219 @@
|
||||
id='bruteforce'
|
||||
author = 'Eddie Bell <ejlbell@gmail.com>'
|
||||
description='brute force telnet login credientials'
|
||||
license = 'See nmaps COPYING for licence'
|
||||
categories = {'intrusive'}
|
||||
|
||||
require('shortport')
|
||||
require('stdnse')
|
||||
|
||||
local soc
|
||||
local catch = function() soc.close() end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
portrule = shortport.port_or_service(23, 'telnet')
|
||||
|
||||
local escape_cred = function(cred)
|
||||
if cred == '' then
|
||||
return '<blank>'
|
||||
else
|
||||
return cred
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Returns a function which returns the next user/pass pair each time
|
||||
it is called. When no more pairs are available nil is returned.
|
||||
|
||||
There are plenty more possible pairs but we need to find
|
||||
a compromise between speed and coverage
|
||||
--]]
|
||||
|
||||
local new_auth_iter = function()
|
||||
local userpass = {
|
||||
-- guest
|
||||
{'guest', ''}, {'guest', 'guest'}, {'guest', 'visitor'},
|
||||
|
||||
-- root
|
||||
{'root', ''}, {'root', 'root'},
|
||||
{'root', 'pass'}, {'root', 'password'},
|
||||
|
||||
-- admin
|
||||
{'admin', ''}, {'admin', 'admin'},
|
||||
{'admin', 'pass'}, {'admin', 'password'},
|
||||
|
||||
-- adminstrator
|
||||
{'adminstrator', ''}, {'adminstrator', 'adminstrator'},
|
||||
{'adminstrator', 'password'}, {'adminstrator', 'pass'},
|
||||
|
||||
-- others
|
||||
{'visitor', ''}, {'netman', 'netman'}, {'Admin', 'Admin'},
|
||||
{'manager', 'manager'}, {'security', 'security'},
|
||||
{'username', 'password'}, {'user', 'pass'},
|
||||
|
||||
-- sentinel
|
||||
{nil, nil}
|
||||
}
|
||||
|
||||
local i = 1
|
||||
return function(direction)
|
||||
if not userpass[i][1] then
|
||||
return
|
||||
end
|
||||
|
||||
i = i + 1
|
||||
stdnse.print_debug(3, id .. " " ..
|
||||
userpass[i-1][1] .. ":" .. escape_cred(userpass[i-1][2]))
|
||||
return userpass[i-1][1], userpass[i-1][2]
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
Go through telnet's option palaver so we can get to the login prompt.
|
||||
We just deny every options the server asks us about.
|
||||
--]]
|
||||
|
||||
local negotiate_options = function(result)
|
||||
local index, x, opttype, opt, retbuf
|
||||
|
||||
index = 0
|
||||
retbuf = stdnse.strbuf.new()
|
||||
|
||||
while true do
|
||||
|
||||
-- 255 is IAC (Interpret As Command)
|
||||
index, x = string.find(result, '\255', index)
|
||||
|
||||
if not index then
|
||||
break
|
||||
end
|
||||
|
||||
opttype = string.byte(result, index+1)
|
||||
opt = string.byte(result, index+2)
|
||||
|
||||
-- don't want it! won't do it!
|
||||
if opttype == 251 or opttype == 252 then
|
||||
opttype = 254
|
||||
elseif opttype == 253 or opttype == 254 then
|
||||
opttype = 252
|
||||
end
|
||||
|
||||
stdnse.strbuf.add(retbuf, string.char(255))
|
||||
stdnse.strbuf.add(retbuf, string.char(opttype))
|
||||
stdnse.strbuf.add(retbuf, string.char(opt))
|
||||
index = index + 1
|
||||
end
|
||||
soc:send(stdnse.strbuf.dump(retbuf))
|
||||
end
|
||||
|
||||
--[[
|
||||
A semi-state-machine that takes action based on output from the
|
||||
server. Through pattern matching, it tries to deem if a user/pass
|
||||
pair is valid. Telnet does not have a way of telling the client
|
||||
if it was authenticated....so we have to make an educated guess
|
||||
--]]
|
||||
|
||||
local brute_line = function(line, user, pass, usent)
|
||||
|
||||
if (line:find 'incorrect' or line:find 'failed' or line:find 'denied' or
|
||||
line:find 'invalid' or line:find 'bad') and usent then
|
||||
usent = false
|
||||
return 2, nil, usent
|
||||
|
||||
elseif (line:find '[/>%%%$#]+' or line:find "last login%s*:" or
|
||||
line:find '%u:\\') and not
|
||||
(line:find 'username%s*:' and line:find 'login%s*:') and
|
||||
usent then
|
||||
return 1, escape_cred(user) .. ' - ' .. escape_cred(pass) .. '\n', usent
|
||||
|
||||
elseif line:find 'username%s*:' or line:find 'login%s*:' then
|
||||
try(soc:send(user .. '\r\0'))
|
||||
usent = true
|
||||
|
||||
elseif line:find 'password%s*:' or line:find 'passcode%s*:' then
|
||||
-- fix, add 'password only' support
|
||||
if not usent then return 1, nil, usent end
|
||||
try(soc:send(pass .. '\r\0'))
|
||||
end
|
||||
|
||||
return 0, nil, usent
|
||||
end
|
||||
|
||||
--[[
|
||||
Splits the input into lines and passes it to brute_line()
|
||||
so it can try to login with <user> and <pass>
|
||||
|
||||
return value:
|
||||
(1, user:pass) - valid pair
|
||||
(2, nil) - invalid pair
|
||||
(3, nil) - disconnected and invalid pair
|
||||
(4, nil) - disconnected and didn't send pair
|
||||
--]]
|
||||
|
||||
local brute_cred = function(user, pass)
|
||||
local status, ret, value, usent, results
|
||||
|
||||
usent = false ; ret = 0
|
||||
|
||||
while true do
|
||||
status, results = soc:receive_lines(1)
|
||||
|
||||
-- remote host disconnected
|
||||
if not status then
|
||||
if usent then return 3
|
||||
else return 4
|
||||
end
|
||||
end
|
||||
|
||||
if (string.byte(results, 1) == 255) then
|
||||
negotiate_options(results)
|
||||
end
|
||||
|
||||
results = string.lower(results)
|
||||
|
||||
for line in results:gmatch '[^\r\n]+' do
|
||||
ret, value, usent = brute_line(line, user, pass, usent)
|
||||
if (ret > 0) then
|
||||
return ret, value
|
||||
end
|
||||
end
|
||||
end
|
||||
return 1, "error -> this should never happen"
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
local pair, status, auth_iter
|
||||
local user, pass, count, rbuf
|
||||
|
||||
pair = nil ; status = 3 ; count = 0
|
||||
auth_iter = new_auth_iter();
|
||||
|
||||
soc = nmap.new_socket()
|
||||
soc:set_timeout(4000)
|
||||
|
||||
-- continually try user/pass pairs (reconnecting, if we have to)
|
||||
-- until we find a valid one or we run out of pairs
|
||||
while not (status == 1) do
|
||||
|
||||
if status == 2 or status == 3 then
|
||||
user, pass = auth_iter()
|
||||
end
|
||||
|
||||
-- make sure we don't get stuck in a loop
|
||||
if status == 4 then
|
||||
count = count + 1
|
||||
if count > 3 then return nil end
|
||||
else count = 0 end
|
||||
|
||||
-- no more users left
|
||||
if not user then break end
|
||||
|
||||
if status == 3 or status == 4 then
|
||||
try(soc:connect(host.ip, port.number, port.protocol))
|
||||
end
|
||||
|
||||
status, pair = brute_cred(user, pass)
|
||||
end
|
||||
soc:close()
|
||||
return pair
|
||||
end
|
||||
@@ -32,3 +32,4 @@ Entry{ category = "discovery", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "intrusive", filename = "SMTPcommands.nse" }
|
||||
Entry{ category = "malware", filename = "kibuvDetection.nse" }
|
||||
Entry{ category = "discovery", filename = "ircServerInfo.nse" }
|
||||
Entry{ category = "intrusive", filename = "bruteTelnet.nse" }
|
||||
|
||||
@@ -14,17 +14,7 @@ categories = {"demo", "safe"}
|
||||
require "shortport"
|
||||
require "stdnse"
|
||||
|
||||
portrule = function(host, port)
|
||||
if ( port.service=='http'
|
||||
or port.service=='https' )
|
||||
and port.protocol == 'tcp'
|
||||
and port.state == 'open'
|
||||
then
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
end
|
||||
end
|
||||
portrule = shortport.service({'http', 'https'})
|
||||
|
||||
action = function(host, port)
|
||||
local url, socket, request, result, status, s, title, protocol
|
||||
|
||||
Reference in New Issue
Block a user