1
0
mirror of https://github.com/nmap/nmap.git synced 2026-01-20 13:19:01 +00:00

o [NSE] Added the library rpcap and the scripts rpcap-brute and rpcap-info

which perform brute force password guessing and extract information from the
  WinPcap Remote Packet Capture daemon. [Patrik]
This commit is contained in:
patrik
2012-03-02 12:39:18 +00:00
parent 43253cea53
commit 480e5ac605
5 changed files with 625 additions and 0 deletions

View File

@@ -1,5 +1,9 @@
# Nmap Changelog ($Id$); -*-text-*-
o [NSE] Added the library rpcap and the scripts rpcap-brute and rpcap-info
which perform brute force password guessing and extract information from the
WinPcap Remote Packet Capture daemon. [Patrik]
o [NSE] Added authentication support to MongoDB library and modified existing
scripts to support it. Added the script mongodb-brute to perform password
brute force guessing. [Patrik]

437
nselib/rpcap.lua Normal file
View File

@@ -0,0 +1,437 @@
---
-- This library implements the fundamentals needed to communicate with the
-- WinPcap Remote Capture Deamon. It currently supports authenticating to
-- the service using either NULL-, or Password-based authentication.
-- In addition it has the capabilities to list the interfaces that may be
-- used for sniffing.
--
-- The library consist of classes handling <code>Request</code> and classes
-- handling <code>Response</code>. The communication with the service is
-- handled by the <code>Comm</code> class, and the main interface for script
-- writers is kept under the <code>Helper</code> class.
--
-- The following code snipplet illustrates how to connect to the service and
-- extract information about network interfaces:
-- <code>
-- local helper = rpcap.Helper:new(host, port)
-- helper:connect()
-- helper:login()
-- helper:findAllInterfaces()
-- helper:close()
-- </code>
--
-- For a more complete example, consult the rpcap-info.nse script.
--
-- @author "Patrik Karlsson <patrik@cqure.net>"
module(... or "rpcap", package.seeall)
require 'ipops'
require 'match'
RPCAP = {
MessageType = {
ERROR = 1,
FIND_ALL_INTERFACES = 2,
AUTH_REQUEST = 8,
},
-- Holds the two supported authentication mechanisms PWD and NULL
Authentication = {
PWD = {
new = function(self, username, password)
local o = {
type = 1,
username = username,
password = password,
}
setmetatable(o, self)
self.__index = self
return o
end,
__tostring = function(self)
local DUMMY = 0
return bin.pack(">SSSSAA", self.type, DUMMY, #self.username, #self.password, self.username, self.password)
end,
},
NULL = {
new = function(self)
local o = {
type = 0,
}
setmetatable(o, self)
self.__index = self
return o
end,
__tostring = function(self)
local DUMMY = 0
return bin.pack(">SSSS", self.type, DUMMY, 0, 0)
end,
}
},
-- The common request and response header
Header = {
size = 8,
new = function(self, type, value, length)
local o = {
version = 0,
type = type,
value= value or 0,
length = length or 0
}
setmetatable(o, self)
self.__index = self
return o
end,
parse = function(data)
local header = RPCAP.Header:new()
local pos
pos, header.version, header.type, header.value, header.length = bin.unpack(">CCSI", data)
return header
end,
__tostring = function(self)
return bin.pack(">CCSI", self.version, self.type, self.value, self.length)
end,
},
-- The implemented request types are kept here
Request = {
Authentication = {
new = function(self, data)
local o = {
header = RPCAP.Header:new(RPCAP.MessageType.AUTH_REQUEST, nil, #data),
data = data,
}
setmetatable(o, self)
self.__index = self
return o
end,
__tostring = function(self)
return tostring(self.header) .. tostring(self.data)
end,
},
FindAllInterfaces = {
new = function(self)
local o = {
header = RPCAP.Header:new(RPCAP.MessageType.FIND_ALL_INTERFACES)
}
setmetatable(o, self)
self.__index = self
return o
end,
__tostring = function(self)
return tostring(self.header)
end,
}
},
-- Parsers for responses are kept here
Response = {
Authentication = {
new = function(self)
local o = { }
setmetatable(o, self)
self.__index = self
return o
end,
parse = function(data)
local resp = RPCAP.Response.Authentication:new()
local pos = RPCAP.Header.size + 1
resp.header = RPCAP.Header.parse(data)
return resp
end
},
Error = {
new = function(self)
local o = { }
setmetatable(o, self)
self.__index = self
return o
end,
parse = function(data)
local err = RPCAP.Response.Error:new()
local pos = RPCAP.Header.size + 1
err.header = RPCAP.Header.parse(data)
pos, err.error = bin.unpack("A" .. err.header.length, data, pos)
return err
end
},
FindAllInterfaces = {
new = function(self)
local o = { }
setmetatable(o, self)
self.__index = self
return o
end,
parse = function(data)
-- Each address is made up of 4 128 byte fields, this function
-- parses these fields and return the response, if it
-- understands it. Otherwise it simply increases the pos by the
-- correct offset, to get us to the next field.
local function parseField(data, pos)
local offset = pos
local family, port
pos, family, port = bin.unpack(">SS", data, pos)
if ( family == 0x0017 ) then
-- not sure why...
pos = pos + 4
local ipv6
pos, ipv6 = bin.unpack("B16", data, pos)
return offset + 128, ipOps.bin_to_ip(ipv6)
elseif ( family == 0x0002 ) then
local ipv4
pos, ipv4 = bin.unpack("B4", data, pos)
return offset + 128, ipOps.bin_to_ip(ipv4)
end
return offset + 128, nil
end
-- Parses one of X addresses returned for an interface
local function parseAddress(data, pos)
local fields = {"ip", "netmask", "bcast", "p2p"}
local addr = {}
for _, f in ipairs(fields) do
pos, addr[f] = parseField(data, pos)
end
return pos, addr
end
local resp = RPCAP.Response.FindAllInterfaces:new()
local pos = RPCAP.Header.size + 1
resp.header = RPCAP.Header.parse(data)
resp.ifaces = {}
for i=1, resp.header.value do
local name_len, desc_len, iface_flags, addr_count, dummy
pos, name_len, desc_len, iface_flags, addr_count, dummy = bin.unpack(">SSISS", data, pos)
local name, desc
pos, name, desc = bin.unpack("A" .. name_len .. "A" .. desc_len, data, pos)
local addrs = {}
for j=1, addr_count do
local addr
pos, addr = parseAddress(data, pos)
local cidr
if ( addr.netmask ) then
local bits = ipOps.ip_to_bin(addr.netmask)
local ones = bits:match("^(1*)")
cidr = #ones
table.insert(addrs, ("%s/%d"):format(addr.ip,cidr))
else
table.insert(addrs, addr.ip)
end
end
table.insert(resp.ifaces, { name = name, desc = desc, addrs = addrs })
end
return resp
end,
}
}
}
-- Maps packet types to classes
RPCAP.TypeToClass = {
[1] = RPCAP.Response.Error,
[130] = RPCAP.Response.FindAllInterfaces,
[136] = RPCAP.Response.Authentication,
}
-- The communication class
Comm = {
-- Creates a new instance of the Comm class
-- @param host table
-- @param port table
-- @return o instance of Comm
new = function(self, host, port)
local o = { host = host, port = port, socket = nmap.new_socket() }
setmetatable(o, self)
self.__index = self
return o
end,
-- Connects the socket to the server
connect = function(self)
return self.socket:connect(self.host, self.port)
end,
-- Sends an instance of the request class to the server
-- @param req class instance
-- @return status true on success, false on failure
-- @return err string containing error message if status is false
send = function(self, req)
return self.socket:send(req)
end,
-- receives a packet and attempts to parse it if it has a supported parser
-- in RPCAP.TypeToClass
-- @return status true on success, false on failure
-- @return resp instance of a Response class or
-- err string containing the error message
recv = function(self)
local status, hdr_data = self.socket:receive_buf(match.numbytes(RPCAP.Header.size), true)
if ( not(status) ) then
return status, data
end
local header = RPCAP.Header.parse(hdr_data)
if ( not(header) ) then
return false, "rpcap: Failed to parse header"
end
local status, data = self.socket:receive_buf(match.numbytes(header.length), true)
if ( not(status) ) then
return false, "rpcap: Failed to read packet data"
end
if ( RPCAP.TypeToClass[header.type] ) then
local resp = RPCAP.TypeToClass[header.type].parse(hdr_data .. data)
if ( resp ) then
return true, resp
end
end
return false, "Failed to receive response from server"
end,
-- Sends and request and receives the response
-- @param req the instance of the Request class to send
-- @return status true on success, false on failure
-- @return resp instance of a Response class or
-- err string containing the error message
exch = function(self, req)
local status, data = self:send(tostring(req))
if ( not(status) ) then
return status, data
end
return self:recv()
end,
-- closes the socket
close = function(self)
return self.socket:close()
end,
}
Helper = {
-- Creates a new instance of the Helper class
-- @param host table
-- @param port table
-- @return o instance of Helper
new = function(self, host, port)
local o = {
host = host,
port = port,
comm = Comm:new(host, port)
}
setmetatable(o, self)
self.__index = self
return o
end,
-- Connects to the server
connect = function(self)
return self.comm:connect(self.host, self.port)
end,
-- Authenticates to the service, in case no username or password is given
-- NULL authentication is assumed.
-- @param username [optional]
-- @param password [optional]
-- @return status true on success, false on failure
-- @return err string containing error mesage on failure
login = function(self, username, password)
local auth
if ( username and password ) then
auth = RPCAP.Authentication.PWD:new(username, password)
else
auth = RPCAP.Authentication.NULL:new()
end
local req = RPCAP.Request.Authentication:new(tostring(auth))
local status, resp = self.comm:exch(req)
if ( not(status) ) then
return false, resp
end
if ( status and resp.error ) then
return false, resp.error
end
return true
end,
-- Requests a list of all interfaces
-- @return table containing interfaces and addresses
findAllInterfaces = function(self)
local req = RPCAP.Request.FindAllInterfaces:new()
local status, resp = self.comm:exch(req)
if ( not(status) ) then
return false, resp
end
local results = {}
for _, iface in ipairs(resp.ifaces) do
local entry = {}
entry.name = iface.name
table.insert(entry, iface.desc)
table.insert(entry, { name = "Addresses", iface.addrs })
table.insert(results, entry)
end
return true, results
end,
-- Closes the connection to the server
close = function(self)
return self.comm:close()
end,
}

92
scripts/rpcap-brute.nse Normal file
View File

@@ -0,0 +1,92 @@
description = [[
Performs brute force password guessing against the WinPcap Remote Capture
Daemon (rpcap).
]]
---
-- @usage
-- nmap -p 2002 <ip> --script rpcap-brute
--
-- @output
-- PORT STATE SERVICE REASON
-- 2002/tcp open globe syn-ack
-- | rpcap-brute:
-- | Accounts
-- | monkey:Password1 - Valid credentials
-- | Statistics
-- |_ Performed 3540 guesses in 3 seconds, average tps: 1180
--
--
require 'brute'
require 'rpcap'
require 'shortport'
author = "Patrik Karlsson"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"intrusive", "brute"}
portrule = shortport.port_or_service(2002, "rpcap", "tcp")
Driver = {
new = function(self, host, port)
local o = { helper = rpcap.Helper:new(host, port) }
setmetatable(o, self)
self.__index = self
return o
end,
connect = function(self)
return self.helper:connect()
end,
login = function(self, username, password)
local status, resp = self.helper:login(username, password)
if ( status ) then
return true, brute.Account:new(username, password, creds.State.VALID)
end
return false, brute.Error:new( "Incorrect password" )
end,
disconnect = function(self)
return self.helper:close()
end,
}
local function validateAuth(host, port)
local helper = rpcap.Helper:new(host, port)
local status, result = helper:connect()
if ( not(status) ) then
return false, result
end
status, result = helper:login()
helper:close()
if ( status ) then
return false, "Authentication not required"
elseif ( not(status) and
"Authentication failed; NULL autentication not permitted." == result ) then
return true
end
return status, result
end
action = function(host, port)
local status, result = validateAuth(host, port)
if ( not(status) ) then
return result
end
local engine = brute.Engine:new(Driver, host, port )
engine.options.script_name = SCRIPT_NAME
engine.options.firstonly = true
status, result = engine:start()
return result
end

90
scripts/rpcap-info.nse Normal file
View File

@@ -0,0 +1,90 @@
description = [[
Connect to the rpcap service, a service providing remote sniffing capabilities
through WinPcap, and retrieves interface information. The service can either be
setup to require authentication or not and also supports IP restrictions.
]]
---
-- @usage
-- nmap -p 2002 <ip> --script rpcap-info
-- nmap -p 2002 <ip> --script rpcap-info --script-args="creds.rpcap='administrator:foobar'"
--
-- @output
-- PORT STATE SERVICE REASON
-- 2002/tcp open rpcap syn-ack
-- | rpcap-info:
-- | \Device\NPF_{0D5D1364-1F1F-4892-8AC3-B838258F9BB8}
-- | Intel(R) PRO/1000 MT Desktop Adapter
-- | Addresses
-- | fe80:0:0:0:aabb:ccdd:eeff:0011
-- | 192.168.1.127/24
-- | \Device\NPF_{D5EAD105-B0BA-4D38-ACB4-6E95512BC228}
-- | Hamachi Virtual Network Interface Driver
-- | Addresses
-- |_ fe80:0:0:0:aabb:ccdd:eeff:0022
--
-- @args creds.rpcap username:password to use for authentication
--
author = "Patrik Karlsson"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"discover", "safe"}
dependencies = {"rpcap-brute"}
require 'creds'
require 'rpcap'
require 'shortport'
portrule = shortport.port_or_service(2002, "rpcap", "tcp")
local function fail(err) return ("\n ERROR: %s"):format(err or "") end
local function getInfo(host, port, username, password)
local helper = rpcap.Helper:new(host, port)
local status, resp = helper:connect()
if ( not(status) ) then
return false, "Failed to connect to server"
end
status, resp = helper:login(username, password)
if ( not(status) ) then
return false, resp
end
status, resp = helper:findAllInterfaces()
helper:close()
if ( not(status) ) then
return false, resp
end
port.version.name = "rpcap"
port.version.product = "WinPcap remote packet capture daemon"
nmap.set_port_version(host, port, "hardmatched")
return true, resp
end
action = function(host, port)
-- patch-up the service name, so creds.rpcap will work, ugly but needed as
-- tcp 2002 is registered to the globe service in nmap-services ...
port.service = "rpcap"
local c = creds.Credentials:new(creds.ALL_DATA, host, port)
local states = creds.State.VALID + creds.State.PARAM
local status, resp = getInfo(host, port)
if ( status ) then
return stdnse.format_output(true, resp)
end
for cred in c:getCredentials(states) do
status, resp = getInfo(host, port, cred.user, cred.pass)
if ( status ) then
return stdnse.format_output(true, resp)
end
end
return fail(resp)
end

View File

@@ -250,6 +250,8 @@ Entry { filename = "rexec-brute.nse", categories = { "brute", "intrusive", } }
Entry { filename = "riak-http-info.nse", categories = { "discovery", "safe", } }
Entry { filename = "rlogin-brute.nse", categories = { "brute", "intrusive", } }
Entry { filename = "rmi-dumpregistry.nse", categories = { "default", "discovery", "safe", } }
Entry { filename = "rpcap-brute.nse", categories = { "brute", "intrusive", } }
Entry { filename = "rpcap-info.nse", categories = { "discover", "safe", } }
Entry { filename = "rpcinfo.nse", categories = { "default", "discovery", "safe", } }
Entry { filename = "rsync-brute.nse", categories = { "brute", "intrusive", } }
Entry { filename = "rsync-list-modules.nse", categories = { "discovery", "safe", } }