mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Remove trailing whitespace in lua files
Whitespace is not significant, so this should not be a problem. https://secwiki.org/w/Nmap/Code_Standards
This commit is contained in:
@@ -219,7 +219,7 @@ local lua = lpeg.locale {
|
||||
|
||||
-- This is an expression which is always truncated to 1 result, and so we can remove
|
||||
-- redundant parenthesis.
|
||||
_single_exp = P "(" * V "whitespace" * V "_single_exp" * V "whitespace" * P ")" * -(V "whitespace" * (V "suffix" + V "binop")) +
|
||||
_single_exp = P "(" * V "whitespace" * V "_single_exp" * V "whitespace" * P ")" * -(V "whitespace" * (V "suffix" + V "binop")) +
|
||||
V "exp";
|
||||
|
||||
_oneline_exp = Cg(Cg(Cc " ", "newline") * Cg(Cc "", "indent") * Cg(Cc "", "indent_space") * V "_single_exp");
|
||||
|
||||
22
nse_main.lua
22
nse_main.lua
@@ -330,7 +330,7 @@ do
|
||||
if not self.worker then
|
||||
-- Structure table and unstructured string outputs.
|
||||
local tab, str
|
||||
|
||||
|
||||
if r2 then
|
||||
tab, str = r1, tostring(r2);
|
||||
elseif type(r1) == "string" then
|
||||
@@ -340,7 +340,7 @@ do
|
||||
else
|
||||
tab, str = r1, nil;
|
||||
end
|
||||
|
||||
|
||||
if self.type == "prerule" or self.type == "postrule" then
|
||||
cnse.script_set_output(self.id, tab, str);
|
||||
elseif self.type == "hostrule" then
|
||||
@@ -572,12 +572,12 @@ do
|
||||
local postrule = rules.postrule;
|
||||
-- Assert that categories is an array of strings
|
||||
for i, category in ipairs(rawget(env, "categories")) do
|
||||
assert(type(category) == "string",
|
||||
assert(type(category) == "string",
|
||||
filename.." has non-string entries in the 'categories' array");
|
||||
end
|
||||
-- Assert that dependencies is an array of strings
|
||||
for i, dependency in ipairs(rawget(env, "dependencies")) do
|
||||
assert(type(dependency) == "string",
|
||||
assert(type(dependency) == "string",
|
||||
filename.." has non-string entries in the 'dependencies' array");
|
||||
end
|
||||
-- Return the script
|
||||
@@ -624,7 +624,7 @@ end
|
||||
-- Arguments:
|
||||
-- rules The array of rules to use for loading scripts.
|
||||
-- Returns:
|
||||
-- chosen_scripts The array of scripts loaded for the given rules.
|
||||
-- chosen_scripts The array of scripts loaded for the given rules.
|
||||
local function get_chosen_scripts (rules)
|
||||
check_rules(rules);
|
||||
|
||||
@@ -745,7 +745,7 @@ local function get_chosen_scripts (rules)
|
||||
" -> script rule expression not supported.");
|
||||
end
|
||||
-- The script rule matches a category or a pattern
|
||||
if found then
|
||||
if found then
|
||||
used_rules[rule_table.original_rule] = true;
|
||||
script_params.forced = not not forced_rules[rule_table.original_rule];
|
||||
local t, path = cnse.fetchscript(filename);
|
||||
@@ -806,7 +806,7 @@ local function get_chosen_scripts (rules)
|
||||
local chain = {}; -- chain of script names
|
||||
local function calculate_runlevel (script)
|
||||
chain[#chain+1] = script.short_basename;
|
||||
if script.runlevel == false then -- circular dependency
|
||||
if script.runlevel == false then -- circular dependency
|
||||
error("circular dependency in chain `"..concat(chain, "->").."`");
|
||||
else
|
||||
script.runlevel = false; -- placeholder
|
||||
@@ -1174,7 +1174,7 @@ do -- Load script arguments (--script-args)
|
||||
"' is invalid or is unterminated by a valid seperator");
|
||||
end
|
||||
end
|
||||
-- Takes 'str' at index 'start' and parses a table.
|
||||
-- Takes 'str' at index 'start' and parses a table.
|
||||
-- Returns the table and the place in the string it finished reading.
|
||||
local function parse_table (str, start)
|
||||
local _, j = find(str, "^%s*{", start);
|
||||
@@ -1303,13 +1303,13 @@ local function main (hosts, scantype)
|
||||
-- parent A table that contains the parent thread table (it self).
|
||||
-- close_handlers
|
||||
-- A table that contains the thread destructor handlers.
|
||||
-- info A string that contains the script name and the thread
|
||||
-- info A string that contains the script name and the thread
|
||||
-- debug information.
|
||||
-- args A table that contains the arguments passed to scripts,
|
||||
-- args A table that contains the arguments passed to scripts,
|
||||
-- arguments can be host and port tables.
|
||||
-- env A table that contains the global script environment:
|
||||
-- categories, description, author, license, nmap table,
|
||||
-- action function, rule functions, SCRIPT_PATH,
|
||||
-- action function, rule functions, SCRIPT_PATH,
|
||||
-- SCRIPT_NAME, SCRIPT_TYPE (pre|host|port|post rule).
|
||||
-- identifier
|
||||
-- A string to identify the thread address.
|
||||
|
||||
278
nselib/afp.lua
278
nselib/afp.lua
@@ -1,6 +1,6 @@
|
||||
---
|
||||
-- This library was written by Patrik Karlsson <patrik@cqure.net> to facilitate
|
||||
-- communication with the Apple AFP Service. It is not feature complete and
|
||||
-- communication with the Apple AFP Service. It is not feature complete and
|
||||
-- still missing several functions.
|
||||
--
|
||||
-- The library currently supports
|
||||
@@ -44,7 +44,7 @@
|
||||
-- status, response = helper:CloseSession()
|
||||
-- </code>
|
||||
--
|
||||
-- Here's the longer version, with some explanatory text. To start using the Helper class,
|
||||
-- Here's the longer version, with some explanatory text. To start using the Helper class,
|
||||
-- the script has to create it's own instance. We do this by issuing the following:
|
||||
-- <code>
|
||||
-- helper = afp.Helper:new()
|
||||
@@ -93,7 +93,7 @@
|
||||
|
||||
--
|
||||
-- Version 0.5
|
||||
--
|
||||
--
|
||||
-- Created 01/03/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/20/2010 - v0.2 - updated all bitmaps to hex for better readability
|
||||
-- Revised 02/15/2010 - v0.3 - added a bunch of new functions and re-designed the code to be OO
|
||||
@@ -241,25 +241,25 @@ ACLS = {
|
||||
OwnerSearch = 0x1,
|
||||
OwnerRead = 0x2,
|
||||
OwnerWrite = 0x4,
|
||||
|
||||
|
||||
GroupSearch = 0x100,
|
||||
GroupRead = 0x200,
|
||||
GroupWrite = 0x400,
|
||||
|
||||
|
||||
EveryoneSearch = 0x10000,
|
||||
EveryoneRead = 0x20000,
|
||||
EveryoneWrite = 0x40000,
|
||||
|
||||
|
||||
UserSearch = 0x100000,
|
||||
UserRead = 0x200000,
|
||||
UserWrite = 0x400000,
|
||||
|
||||
|
||||
BlankAccess = 0x10000000,
|
||||
UserIsOwner = 0x80000000
|
||||
}
|
||||
|
||||
-- User authentication modules
|
||||
UAM =
|
||||
UAM =
|
||||
{
|
||||
NoUserAuth = "No User Authent",
|
||||
ClearText = "Cleartxt Passwrd",
|
||||
@@ -271,11 +271,11 @@ UAM =
|
||||
Reconnect = "Recon1",
|
||||
}
|
||||
|
||||
ERROR =
|
||||
ERROR =
|
||||
{
|
||||
SocketError = 1000,
|
||||
CustomError = 0xdeadbeef,
|
||||
|
||||
|
||||
FPNoErr = 0,
|
||||
FPAccessDenied = -5000,
|
||||
FPAuthContinue = -5001,
|
||||
@@ -331,7 +331,7 @@ SERVERFLAGS =
|
||||
SuperClient = 0x8000
|
||||
}
|
||||
|
||||
local ERROR_MSG = {
|
||||
local ERROR_MSG = {
|
||||
[ERROR.FPAccessDenied]="Access Denied",
|
||||
[ERROR.FPAuthContinue]="Authentication is not yet complete",
|
||||
[ERROR.FPBadUAM]="Specified UAM is unknown",
|
||||
@@ -356,28 +356,28 @@ end
|
||||
|
||||
-- Response class returned by all functions in Proto
|
||||
Response = {
|
||||
|
||||
|
||||
new = function(self,o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the error code
|
||||
--
|
||||
-- @param code number containing the error code
|
||||
setErrorCode = function( self, code )
|
||||
self.error_code = code
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the error code
|
||||
--
|
||||
-- @return code number containing the error code
|
||||
getErrorCode = function( self )
|
||||
return self.error_code
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the error message
|
||||
--
|
||||
-- @return msg string containing the error
|
||||
@@ -388,7 +388,7 @@ Response = {
|
||||
return ERROR_MSG[self.error_code] or ("Unknown error (%d) occured"):format(self.error_code)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the error message
|
||||
--
|
||||
-- @param msg string containing the error message
|
||||
@@ -396,35 +396,35 @@ Response = {
|
||||
self.error_code = ERROR.CustomError
|
||||
self.error_msg = msg
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the result
|
||||
--
|
||||
-- @param result result to set
|
||||
setResult = function(self, result)
|
||||
self.result = result
|
||||
end,
|
||||
|
||||
|
||||
--- Get the result
|
||||
--
|
||||
-- @return result
|
||||
-- @return result
|
||||
getResult = function(self)
|
||||
return self.result
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the packet
|
||||
setPacket = function( self, packet )
|
||||
self.packet = packet
|
||||
end,
|
||||
|
||||
|
||||
getPacket = function( self )
|
||||
return self.packet
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the packet data
|
||||
getPacketData = function(self)
|
||||
return self.packet.data
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the packet header
|
||||
getPacketHeader = function(self)
|
||||
return self.packet.header
|
||||
@@ -436,9 +436,9 @@ Response = {
|
||||
-- For more details consult:
|
||||
-- http://developer.apple.com/mac/library/documentation/Networking/Reference/AFP_Reference/Reference/reference.html
|
||||
Proto = {
|
||||
|
||||
|
||||
RequestId = 1,
|
||||
|
||||
|
||||
new = function(self,o)
|
||||
o = o or {}
|
||||
setmetatable(o, self)
|
||||
@@ -455,7 +455,7 @@ Proto = {
|
||||
-- @param command number should be one of the commands in the COMMAND table
|
||||
-- @param data_offset number holding the offset to the data
|
||||
-- @param data the actual data of the request
|
||||
create_fp_packet = function( self, command, data_offset, data )
|
||||
create_fp_packet = function( self, command, data_offset, data )
|
||||
local reserved = 0
|
||||
local data = data or ""
|
||||
local data_len = data:len()
|
||||
@@ -493,7 +493,7 @@ Proto = {
|
||||
local packet = {}
|
||||
local buf = ""
|
||||
local status, response
|
||||
|
||||
|
||||
status, buf = self.socket:receive_bytes(16)
|
||||
if ( not status ) then
|
||||
response = Response:new()
|
||||
@@ -501,7 +501,7 @@ Proto = {
|
||||
response:setErrorMessage(buf)
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
packet.header = self:parse_fp_header( buf )
|
||||
while buf:len() < packet.header.length + packet.header.raw:len() do
|
||||
local tmp
|
||||
@@ -548,7 +548,7 @@ Proto = {
|
||||
return self:read_fp_packet()
|
||||
end,
|
||||
|
||||
--- Sends an DSICloseSession request to the server and handles the response
|
||||
--- Sends an DSICloseSession request to the server and handles the response
|
||||
dsi_close_session = function( self )
|
||||
local data_offset = 0
|
||||
local option = 0x01 -- Attention Quantum
|
||||
@@ -586,7 +586,7 @@ Proto = {
|
||||
data = data .. bin.pack(">CIP", unicode_names, unicode_hint, src_path )
|
||||
data = data .. bin.pack(">CIP", unicode_names, unicode_hint, dst_path )
|
||||
data = data .. bin.pack(">CIP", unicode_names, unicode_hint, new_name )
|
||||
|
||||
|
||||
packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
self:send_fp_packet( packet )
|
||||
return self:read_fp_packet()
|
||||
@@ -597,7 +597,7 @@ Proto = {
|
||||
--
|
||||
-- @return status (true or false)
|
||||
-- @return table with server information (if status is true) or error string
|
||||
-- (if status is false)
|
||||
-- (if status is false)
|
||||
fp_get_server_info = function(self)
|
||||
local packet
|
||||
local data_offset = 0
|
||||
@@ -800,7 +800,7 @@ Proto = {
|
||||
|
||||
--- Sends an FPGetUserInfo AFP request to the server and handles the response
|
||||
--
|
||||
-- @return response object with the following result <code>user_bitmap</code> and
|
||||
-- @return response object with the following result <code>user_bitmap</code> and
|
||||
-- <code>uid</code> fields
|
||||
fp_get_user_info = function( self )
|
||||
|
||||
@@ -821,14 +821,14 @@ Proto = {
|
||||
end
|
||||
|
||||
pos, response.result.user_bitmap, response.result.uid = bin.unpack(">S>I", packet.data)
|
||||
|
||||
|
||||
return response
|
||||
end,
|
||||
|
||||
--- Sends an FPGetSrvrParms AFP request to the server and handles the response
|
||||
--
|
||||
-- @return response object with the following result <code>server_time</code>,
|
||||
-- <code>vol_count</code>, <code>volumes</code> fields
|
||||
-- <code>vol_count</code>, <code>volumes</code> fields
|
||||
fp_get_srvr_parms = function(self)
|
||||
local packet, status, data
|
||||
local data_offset = 0
|
||||
@@ -890,7 +890,7 @@ Proto = {
|
||||
response:setErrorMessage("OpenSSL not available, aborting ...")
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
-- currently we only support AFP3.3
|
||||
if afp_version == nil or ( afp_version ~= "AFP3.3" and afp_version ~= "AFP3.2" and afp_version ~= "AFP3.1" ) then
|
||||
response = Response:new()
|
||||
@@ -934,7 +934,7 @@ Proto = {
|
||||
end
|
||||
|
||||
_, Id, Mb, EncData = bin.unpack(">SH16A32", response.packet.data )
|
||||
|
||||
|
||||
Mb = openssl.bignum_hex2bn( Mb )
|
||||
K = openssl.bignum_mod_exp (Mb, Ra, p)
|
||||
K_bin = openssl.bignum_bn2bin(K)
|
||||
@@ -942,7 +942,7 @@ Proto = {
|
||||
nonce = openssl.bignum_add( openssl.bignum_bin2bn(nonce), openssl.bignum_dec2bn("1") )
|
||||
PlainText = openssl.bignum_bn2bin(nonce) .. Util.ZeroPad(password, 64)
|
||||
auth_response = openssl.encrypt( "cast5-cbc", K_bin, dhx_c2civ, PlainText, true)
|
||||
|
||||
|
||||
data = bin.pack( "CC>SA", COMMAND.FPLoginCont, 0, Id, auth_response )
|
||||
packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
self:send_fp_packet( packet )
|
||||
@@ -956,13 +956,13 @@ Proto = {
|
||||
return response
|
||||
end,
|
||||
|
||||
-- Terminates sessions and frees server resources established by FPLoginand FPLoginExt.
|
||||
-- Terminates sessions and frees server resources established by FPLoginand FPLoginExt.
|
||||
--
|
||||
-- @return response object
|
||||
fp_logout = function( self )
|
||||
local packet, data, response
|
||||
local data_offset, pad = 0, 0
|
||||
|
||||
|
||||
data = bin.pack("CC", COMMAND.FPLogout, pad)
|
||||
packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
self:send_fp_packet( packet )
|
||||
@@ -973,7 +973,7 @@ Proto = {
|
||||
--
|
||||
-- @param bitmap number bitmask of volume information to request
|
||||
-- @param volume_name string containing the volume name to query
|
||||
-- @return response object with the following result <code>bitmap</code> and
|
||||
-- @return response object with the following result <code>bitmap</code> and
|
||||
-- <code>volume_id</code> fields
|
||||
fp_open_vol = function( self, bitmap, volume_name )
|
||||
local packet, status, pos, data
|
||||
@@ -1002,7 +1002,7 @@ Proto = {
|
||||
-- @param dir_bitmap number bitmask of directory information to query
|
||||
-- @param path string containing the name of the directory to query
|
||||
-- @return response object with the following result <code>file_bitmap</code>, <code>dir_bitmap</code>,
|
||||
-- <code>file_type</code> and (<code>dir<code> or <code>file</code> tables) depending on whether
|
||||
-- <code>file_type</code> and (<code>dir<code> or <code>file</code> tables) depending on whether
|
||||
-- <code>did</code> is a file or directory
|
||||
fp_get_file_dir_parms = function( self, volume_id, did, file_bitmap, dir_bitmap, path )
|
||||
|
||||
@@ -1042,7 +1042,7 @@ Proto = {
|
||||
-- file
|
||||
pos, parms.file = Util.decode_file_bitmap( parms.file_bitmap, response.packet.data, pos )
|
||||
end
|
||||
|
||||
|
||||
response:setResult(parms)
|
||||
return response
|
||||
end,
|
||||
@@ -1103,7 +1103,7 @@ Proto = {
|
||||
record.type = ftype
|
||||
table.insert(records, record)
|
||||
end
|
||||
|
||||
|
||||
response:setResult(records)
|
||||
return response
|
||||
end,
|
||||
@@ -1123,7 +1123,7 @@ Proto = {
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
local response, fork = {}, {}
|
||||
|
||||
|
||||
local data = bin.pack( "CC>S>I>S>S", COMMAND.FPOpenFork, flag, volume_id, did, file_bitmap, access_mode )
|
||||
|
||||
if path.type == PATH_TYPE.LongName then
|
||||
@@ -1132,7 +1132,7 @@ Proto = {
|
||||
|
||||
if path.type == PATH_TYPE.UTF8Name then
|
||||
local unicode_hint = 0x08000103
|
||||
data = data .. bin.pack( "C>I>SA", path.type, unicode_hint, path.len, path.name )
|
||||
data = data .. bin.pack( "C>I>SA", path.type, unicode_hint, path.len, path.name )
|
||||
end
|
||||
|
||||
packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
@@ -1147,7 +1147,7 @@ Proto = {
|
||||
response:setResult(fork)
|
||||
return response
|
||||
end,
|
||||
|
||||
|
||||
--- FPCloseFork
|
||||
--
|
||||
-- @param fork number containing the fork to close
|
||||
@@ -1164,7 +1164,7 @@ Proto = {
|
||||
self:send_fp_packet( packet )
|
||||
return self:read_fp_packet( )
|
||||
end,
|
||||
|
||||
|
||||
--- FPCreateDir
|
||||
--
|
||||
-- @param vol_id number containing the volume id
|
||||
@@ -1235,7 +1235,7 @@ Proto = {
|
||||
fp_write_ext = function( self, flag, fork, offset, count, fdata )
|
||||
local packet
|
||||
local data_offset = 20
|
||||
local data
|
||||
local data
|
||||
|
||||
if count > fdata:len() then
|
||||
local err = Response:new()
|
||||
@@ -1253,7 +1253,7 @@ Proto = {
|
||||
self:send_fp_packet( packet )
|
||||
return self:read_fp_packet( )
|
||||
end,
|
||||
|
||||
|
||||
--- FPCreateFile
|
||||
--
|
||||
-- @param flag number where 0 indicates a soft create and 1 indicates a hard create.
|
||||
@@ -1291,15 +1291,15 @@ Proto = {
|
||||
packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
self:send_fp_packet( packet )
|
||||
response = self:read_fp_packet( )
|
||||
|
||||
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
-- Netatalk returns the name with 1-byte length prefix,
|
||||
-- Mac OS has a 2-byte (UTF-8) length prefix
|
||||
local _, len = bin.unpack("C", response.packet.data)
|
||||
|
||||
|
||||
-- if length is zero assume 2-byte length (UTF-8 name)
|
||||
if len == 0 then
|
||||
response:setResult( select(2, bin.unpack(">P", response.packet.data )) )
|
||||
@@ -1308,7 +1308,7 @@ Proto = {
|
||||
end
|
||||
return response
|
||||
end,
|
||||
|
||||
|
||||
--- FPMapName
|
||||
--
|
||||
-- @param subfunc number containing the subfunction to call
|
||||
@@ -1323,18 +1323,18 @@ Proto = {
|
||||
packet = self:create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
self:send_fp_packet( packet )
|
||||
response = self:read_fp_packet( )
|
||||
|
||||
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return response
|
||||
end
|
||||
|
||||
|
||||
response:setResult( select(2, bin.unpack(">I", response.packet.data)))
|
||||
return response
|
||||
end,
|
||||
}
|
||||
|
||||
--- The helper class wraps the protocol class and their functions. It contains
|
||||
-- high-level functions with descriptive names, facilitating the use and
|
||||
-- high-level functions with descriptive names, facilitating the use and
|
||||
-- minimizing the need to fully understand the AFP low-level protocol details.
|
||||
Helper = {
|
||||
|
||||
@@ -1356,22 +1356,22 @@ Helper = {
|
||||
-- @return string containing error message (if status is false)
|
||||
OpenSession = function( self, host, port )
|
||||
local status, response
|
||||
|
||||
|
||||
self.socket = nmap.new_socket()
|
||||
self.socket:set_timeout( 5000 )
|
||||
status = self.socket:connect(host, port)
|
||||
if not status then
|
||||
return false, "Socket connection failed"
|
||||
end
|
||||
|
||||
|
||||
self.proto = Proto:new( { socket=self.socket} )
|
||||
response = self.proto:dsi_open_session(self.socket)
|
||||
|
||||
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
self.socket:close()
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
@@ -1382,7 +1382,7 @@ Helper = {
|
||||
CloseSession = function( self )
|
||||
local status, packet = self.proto:dsi_close_session( )
|
||||
self.socket:close()
|
||||
|
||||
|
||||
return status, packet
|
||||
end,
|
||||
|
||||
@@ -1397,18 +1397,18 @@ Helper = {
|
||||
|
||||
--- Logs in to an AFP service
|
||||
--
|
||||
-- @param username (optional) string containing the username
|
||||
-- @param username (optional) string containing the username
|
||||
-- @param password (optional) string containing the user password
|
||||
-- @param options table containing additional options <code>uam</code>
|
||||
Login = function( self, username, password, options )
|
||||
local uam = ( options and options.UAM ) and options.UAM or "DHCAST128"
|
||||
local response
|
||||
|
||||
|
||||
-- username and password arguments override the ones supplied using the
|
||||
-- script arguments afp.username and afp.password
|
||||
local username = username or self.username
|
||||
local password = password or self.password
|
||||
|
||||
|
||||
if ( username and uam == "DHCAST128" ) then
|
||||
response = self.proto:fp_login( "AFP3.1", "DHCAST128", username, password )
|
||||
elseif( username ) then
|
||||
@@ -1416,22 +1416,22 @@ Helper = {
|
||||
else
|
||||
response = self.proto:fp_login( "AFP3.1", "No User Authent" )
|
||||
end
|
||||
|
||||
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
return true, "Success"
|
||||
end,
|
||||
|
||||
|
||||
--- Logs out from the AFP service
|
||||
Logout = function(self)
|
||||
return self.proto:fp_logout()
|
||||
end,
|
||||
|
||||
|
||||
--- Walks the directory tree specified by <code>str_path</code> and returns the node information
|
||||
--
|
||||
-- @param str_path string containing the directory
|
||||
-- @param str_path string containing the directory
|
||||
-- @return status boolean true on success, otherwise false
|
||||
-- @return item table containing node information <code>DirectoryId</code> and <code>DirectoryName</code>
|
||||
WalkDirTree = function( self, str_path )
|
||||
@@ -1461,7 +1461,7 @@ Helper = {
|
||||
|
||||
return true, item
|
||||
end,
|
||||
|
||||
|
||||
--- Reads a file on the AFP server
|
||||
--
|
||||
-- @param str_path string containing the AFP sharepoint, path and filename eg. HR/Documents/File.doc
|
||||
@@ -1477,12 +1477,12 @@ Helper = {
|
||||
if ( not status ) then
|
||||
return false, response
|
||||
end
|
||||
|
||||
|
||||
vol_id = response.VolumeId
|
||||
did = response.DirectoryId
|
||||
|
||||
|
||||
path = { ['type']=PATH_TYPE.LongName, name=p.file, len=p.file:len() }
|
||||
|
||||
|
||||
response = self.proto:fp_open_fork(0, vol_id, did, 0, ACCESS_MODE.Read, path )
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return false, response:getErrorMessage()
|
||||
@@ -1499,15 +1499,15 @@ Helper = {
|
||||
content = content .. response.result
|
||||
offset = offset + count
|
||||
end
|
||||
|
||||
|
||||
response = self.proto:fp_close_fork( fork )
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
return true, content
|
||||
end,
|
||||
|
||||
|
||||
--- Writes a file to the AFP server
|
||||
--
|
||||
-- @param str_path string containing the AFP sharepoint, path and filename eg. HR/Documents/File.doc
|
||||
@@ -1519,15 +1519,15 @@ Helper = {
|
||||
local offset, count = 1, 1024
|
||||
local status, vol_id, did, path
|
||||
local p = Util.SplitPath( str_path )
|
||||
|
||||
|
||||
status, response = self:WalkDirTree( p.dir )
|
||||
vol_id = response.VolumeId
|
||||
did = response.DirectoryId
|
||||
|
||||
|
||||
if ( not status ) then
|
||||
return false, response
|
||||
end
|
||||
|
||||
|
||||
path = { ['type']=PATH_TYPE.LongName, name=p.file, len=p.file:len() }
|
||||
|
||||
status, response = self.proto:fp_create_file( 0, vol_id, did, path )
|
||||
@@ -1543,12 +1543,12 @@ Helper = {
|
||||
end
|
||||
|
||||
fork = response.result.fork_id
|
||||
|
||||
|
||||
response = self.proto:fp_write_ext( 0, fork, 0, fdata:len(), fdata )
|
||||
|
||||
return true, nil
|
||||
end,
|
||||
|
||||
|
||||
--- Maps a user id (uid) to a user name
|
||||
--
|
||||
-- @param uid number containing the uid to resolve
|
||||
@@ -1562,7 +1562,7 @@ Helper = {
|
||||
end
|
||||
return true, response.result
|
||||
end,
|
||||
|
||||
|
||||
--- Maps a group id (gid) to group name
|
||||
--
|
||||
-- @param gid number containing the gid to lookup
|
||||
@@ -1576,7 +1576,7 @@ Helper = {
|
||||
end
|
||||
return true, response.result
|
||||
end,
|
||||
|
||||
|
||||
--- Maps a username to a UID
|
||||
--
|
||||
-- @param name string containing the username to map to an UID
|
||||
@@ -1589,7 +1589,7 @@ Helper = {
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
return true, response.result
|
||||
end,
|
||||
end,
|
||||
|
||||
--- List the contents of a directory
|
||||
--
|
||||
@@ -1609,9 +1609,9 @@ Helper = {
|
||||
local f_bm = FILE_BITMAP.NodeId + FILE_BITMAP.ParentDirId + FILE_BITMAP.LongName
|
||||
local d_bm = DIR_BITMAP.NodeId + DIR_BITMAP.ParentDirId + DIR_BITMAP.LongName
|
||||
local path = { ['type']=PATH_TYPE.LongName, name="", len=0 }
|
||||
|
||||
|
||||
local TYPE_DIR = 0x80
|
||||
|
||||
|
||||
if ( parent == nil ) then
|
||||
status, response = self:WalkDirTree( str_path )
|
||||
if ( not status ) then
|
||||
@@ -1624,17 +1624,17 @@ Helper = {
|
||||
parent.dir_name = response.DirectoryName or ""
|
||||
parent.out_tbl = {}
|
||||
end
|
||||
|
||||
|
||||
if ( options and options.max_depth and options.max_depth > 0 and options.max_depth < depth ) then
|
||||
return false, "Max Depth Reached"
|
||||
end
|
||||
|
||||
response = self.proto:fp_enumerate_ext2( parent.vol_id, parent.did, f_bm, d_bm, 1000, 1, 52800, path)
|
||||
|
||||
|
||||
response = self.proto:fp_enumerate_ext2( parent.vol_id, parent.did, f_bm, d_bm, 1000, 1, 52800, path)
|
||||
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
records = response.result or {}
|
||||
local dir_item = {}
|
||||
|
||||
@@ -1652,36 +1652,36 @@ Helper = {
|
||||
end
|
||||
|
||||
table.insert( parent.out_tbl, dir_item )
|
||||
|
||||
|
||||
return true, parent.out_tbl
|
||||
end,
|
||||
|
||||
|
||||
--- Displays a directory tree
|
||||
--
|
||||
-- @param str_path string containing the sharepoint and the directory
|
||||
-- @param options table options containing zero or more of the options
|
||||
-- <code>max_depth</code> and <code>dironly</code>
|
||||
-- @return dirtree table containing the directories
|
||||
-- @return dirtree table containing the directories
|
||||
DirTree = function( self, str_path, options )
|
||||
local options = options or {}
|
||||
options.dironly = true
|
||||
return self:Dir( str_path, options )
|
||||
end,
|
||||
|
||||
|
||||
--- List the AFP sharepoints
|
||||
--
|
||||
-- @return volumes table containing the sharepoints
|
||||
ListShares = function( self )
|
||||
local response
|
||||
response = self.proto:fp_get_srvr_parms( )
|
||||
|
||||
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
return true, response.result.volumes
|
||||
end,
|
||||
|
||||
|
||||
--- Determine the sharepoint permissions
|
||||
--
|
||||
-- @param vol_name string containing the name of the volume
|
||||
@@ -1689,18 +1689,18 @@ Helper = {
|
||||
-- @return acls table containing the volume acls as returned by <code>acls_to_long_string</code>
|
||||
GetSharePermissions = function( self, vol_name )
|
||||
local status, response, vol_id, acls
|
||||
|
||||
|
||||
response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )
|
||||
|
||||
if response:getErrorCode() == ERROR.FPNoErr then
|
||||
local vol_id
|
||||
local path = {}
|
||||
|
||||
vol_id = response.result.volume_id
|
||||
|
||||
vol_id = response.result.volume_id
|
||||
path.type = PATH_TYPE.LongName
|
||||
path.name = ""
|
||||
path.len = path.name:len()
|
||||
|
||||
|
||||
response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.ALL, DIR_BITMAP.ALL, path )
|
||||
if response:getErrorCode() == ERROR.FPNoErr then
|
||||
if ( response.result.dir and response.result.dir.AccessRights ) then
|
||||
@@ -1710,10 +1710,10 @@ Helper = {
|
||||
end
|
||||
self.proto:fp_close_vol( vol_id )
|
||||
end
|
||||
|
||||
|
||||
return true, acls
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the Unix permissions of a file
|
||||
-- @param vol_name string containing the name of the volume
|
||||
-- @param str_path string containing the name of the file
|
||||
@@ -1726,18 +1726,18 @@ Helper = {
|
||||
-- @return err string (on failure) containing the error message
|
||||
GetFileUnixPermissions = function(self, vol_name, str_path)
|
||||
local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )
|
||||
|
||||
|
||||
if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
local vol_id = response.result.volume_id
|
||||
local path = { type = PATH_TYPE.LongName, name = str_path, len = #str_path }
|
||||
response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.UnixPrivileges, DIR_BITMAP.UnixPrivileges, path )
|
||||
if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
local item = ( response.result.file ) and response.result.file or response.result.dir
|
||||
local item_type = ( response.result.file ) and "-" or "d"
|
||||
local privs = ( item.UnixPrivileges and item.UnixPrivileges.ua_permissions ) and
|
||||
@@ -1749,7 +1749,7 @@ Helper = {
|
||||
return true, { uid = uid, gid = gid, privs = str_privs }
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the Unix permissions of a file
|
||||
-- @param vol_name string containing the name of the volume
|
||||
-- @param str_path string containing the name of the file
|
||||
@@ -1758,24 +1758,24 @@ Helper = {
|
||||
-- @return err string (on failure) containing the error message
|
||||
GetFileSize = function( self, vol_name, str_path )
|
||||
local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )
|
||||
|
||||
|
||||
if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
local vol_id = response.result.volume_id
|
||||
local path = { type = PATH_TYPE.LongName, name = str_path, len = #str_path }
|
||||
response = self.proto:fp_get_file_dir_parms( vol_id, 2, FILE_BITMAP.ExtendedDataForkSize, 0, path )
|
||||
if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
return true, ( response.result.file and
|
||||
response.result.file.ExtendedDataForkSize) and
|
||||
response.result.file.ExtendedDataForkSize) and
|
||||
response.result.file.ExtendedDataForkSize or 0
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Returns the creation, modification and backup dates of a file
|
||||
-- @param vol_name string containing the name of the volume
|
||||
-- @param str_path string containing the name of the file
|
||||
@@ -1787,11 +1787,11 @@ Helper = {
|
||||
-- @return err string (on failure) containing the error message
|
||||
GetFileDates = function( self, vol_name, str_path )
|
||||
local response = self.proto:fp_open_vol( VOL_BITMAP.ID, vol_name )
|
||||
|
||||
|
||||
if ( response:getErrorCode() ~= ERROR.FPNoErr ) then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
local vol_id = response.result.volume_id
|
||||
local path = { type = PATH_TYPE.LongName, name = str_path, len = #str_path }
|
||||
local f_bm = FILE_BITMAP.CreationDate + FILE_BITMAP.ModificationDate + FILE_BITMAP.BackupDate
|
||||
@@ -1802,7 +1802,7 @@ Helper = {
|
||||
end
|
||||
|
||||
local item = ( response.result.file ) and response.result.file or response.result.dir
|
||||
|
||||
|
||||
local diff = os.time{year=2000, month=1, day=1, hour=0} - os.time{year=1970, month=1, day=1, hour=0}
|
||||
local create = os.date("%Y-%m-%d %H:%M", item.CreationDate + diff)
|
||||
local backup = os.date("%Y-%m-%d %H:%M", item.BackupDate )
|
||||
@@ -1817,28 +1817,28 @@ Helper = {
|
||||
-- @return status boolean true on success, false on failure
|
||||
-- @return dirId number containing the new directory id
|
||||
CreateDir = function( self, str_path )
|
||||
local status, response, vol_id, did
|
||||
local status, response, vol_id, did
|
||||
local p = Util.SplitPath( str_path )
|
||||
local path = { ['type']=PATH_TYPE.LongName, name=p.file, len=p.file:len() }
|
||||
|
||||
|
||||
|
||||
|
||||
status, response = self:WalkDirTree( p.dir )
|
||||
if not status then
|
||||
return false, response
|
||||
end
|
||||
|
||||
|
||||
response = self.proto:fp_create_dir( response.VolumeId, response.DirectoryId, path )
|
||||
if response:getErrorCode() ~= ERROR.FPNoErr then
|
||||
return false, response:getErrorMessage()
|
||||
end
|
||||
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- Util class, containing some static functions used by Helper and Proto
|
||||
Util =
|
||||
Util =
|
||||
{
|
||||
--- Pads a string with zeroes
|
||||
--
|
||||
@@ -1853,10 +1853,10 @@ Util =
|
||||
for i=1, len - str:len() do
|
||||
str = str .. string.char(0)
|
||||
end
|
||||
|
||||
|
||||
return str
|
||||
end,
|
||||
|
||||
|
||||
--- Splits a path into two pieces, directory and file
|
||||
--
|
||||
-- @param str_path string containing the path to split
|
||||
@@ -1864,20 +1864,20 @@ Util =
|
||||
SplitPath = function( str_path )
|
||||
local elements = stdnse.strsplit("/", str_path)
|
||||
local dir, file = "", ""
|
||||
|
||||
|
||||
if #elements < 2 then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
file = elements[#elements]
|
||||
|
||||
|
||||
table.remove( elements, #elements )
|
||||
dir = stdnse.strjoin( "/", elements )
|
||||
|
||||
|
||||
return { ['dir']=dir, ['file']=file }
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
--- Converts a group bitmask of Search, Read and Write to table
|
||||
--
|
||||
-- @param acls number containing bitmasked acls
|
||||
@@ -1888,7 +1888,7 @@ Util =
|
||||
|
||||
if bit.band( acls, ACLS.OwnerSearch ) == ACLS.OwnerSearch then
|
||||
table.insert( acl_table, "Search")
|
||||
end
|
||||
end
|
||||
|
||||
if bit.band( acls, ACLS.OwnerRead ) == ACLS.OwnerRead then
|
||||
table.insert( acl_table, "Read")
|
||||
@@ -1961,8 +1961,8 @@ Util =
|
||||
|
||||
return owner .. group .. other
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Decodes a file bitmap
|
||||
--
|
||||
-- @param bitmap number containing the bitmap
|
||||
@@ -2024,10 +2024,10 @@ Util =
|
||||
end
|
||||
if ( bit.band( bitmap, FILE_BITMAP.ExtendedResourceForkSize ) == FILE_BITMAP.ExtendedResourceForkSize ) then
|
||||
pos, file.ExtendedResourceForkSize = bin.unpack(">L", data, pos )
|
||||
end
|
||||
end
|
||||
if ( bit.band( bitmap, FILE_BITMAP.UnixPrivileges ) == FILE_BITMAP.UnixPrivileges ) then
|
||||
local unixprivs = {}
|
||||
pos, unixprivs.uid, unixprivs.gid,
|
||||
pos, unixprivs.uid, unixprivs.gid,
|
||||
unixprivs.permissions, unixprivs.ua_permissions = bin.unpack(">IIII", data, pos )
|
||||
file.UnixPrivileges = unixprivs
|
||||
end
|
||||
@@ -2105,13 +2105,13 @@ Util =
|
||||
if ( bit.band( bitmap, DIR_BITMAP.UnixPrivileges ) == DIR_BITMAP.UnixPrivileges ) then
|
||||
local unixprivs = {}
|
||||
|
||||
pos, unixprivs.uid, unixprivs.gid,
|
||||
pos, unixprivs.uid, unixprivs.gid,
|
||||
unixprivs.permissions, unixprivs.ua_permissions = bin.unpack(">I>I>I>I", data, pos )
|
||||
dir.UnixPrivileges = unixprivs
|
||||
end
|
||||
return pos, dir
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
124
nselib/ajp.lua
124
nselib/ajp.lua
@@ -17,10 +17,10 @@ _ENV = stdnse.module("ajp", stdnse.seeall)
|
||||
--
|
||||
|
||||
AJP = {
|
||||
|
||||
|
||||
-- The magic prefix that has to be present in all requests
|
||||
Magic = 0x1234,
|
||||
|
||||
|
||||
-- Methods encoded as numeric values
|
||||
Method = {
|
||||
['OPTIONS'] = 1,
|
||||
@@ -51,7 +51,7 @@ AJP = {
|
||||
['BASELINE_CONTROL'] = 26,
|
||||
['MKACTIVITY'] = 27,
|
||||
},
|
||||
|
||||
|
||||
-- Request codes
|
||||
Code = {
|
||||
FORWARD_REQUEST = 2,
|
||||
@@ -62,7 +62,7 @@ AJP = {
|
||||
PING = 8,
|
||||
CPING = 10,
|
||||
},
|
||||
|
||||
|
||||
-- Request attributes
|
||||
Attribute = {
|
||||
CONTEXT = 0x01,
|
||||
@@ -78,9 +78,9 @@ AJP = {
|
||||
SSL_KEY_SIZE = 0x0B,
|
||||
ARE_DONE = 0xFF,
|
||||
},
|
||||
|
||||
|
||||
ForwardRequest = {
|
||||
|
||||
|
||||
-- Common headers encoded as numeric values
|
||||
Header = {
|
||||
['accept'] = 0xA001,
|
||||
@@ -97,7 +97,7 @@ AJP = {
|
||||
['pragma'] = 0xA00C,
|
||||
['referer'] = 0xA00D,
|
||||
['user-agent'] = 0xA00E,
|
||||
},
|
||||
},
|
||||
|
||||
new = function(self, host, port, method, uri, headers, attributes, options)
|
||||
local o = {
|
||||
@@ -118,11 +118,11 @@ AJP = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
|
||||
|
||||
-- encodes a string, prefixing it with a 2-byte length
|
||||
-- and suffixing it with a zero. P-encoding can't be used
|
||||
-- as the zero terminator should not be counted in the length
|
||||
@@ -144,7 +144,7 @@ AJP = {
|
||||
if ( not(self.headers['host']) ) then
|
||||
self.headers['host'] = stdnse.get_hostname(self.host)
|
||||
end
|
||||
|
||||
|
||||
-- add keep-alive connection header if missing
|
||||
if ( not(self.headers['connection']) ) then
|
||||
self.headers['connection'] = "keep-alive"
|
||||
@@ -153,12 +153,12 @@ AJP = {
|
||||
local p_url = url.parse(self.uri)
|
||||
|
||||
-- save the magic and data for last
|
||||
local data = bin.pack(">CCAAAAASCS", self.code, self.method,
|
||||
local data = bin.pack(">CCAAAAASCS", self.code, self.method,
|
||||
encstr(self.version), encstr(p_url.path), encstr(self.raddr),
|
||||
encstr(self.rhost), encstr(self.srv),
|
||||
self.port, (self.is_ssl and 1 or 0),
|
||||
headerCount())
|
||||
|
||||
|
||||
-- encode headers
|
||||
for k, v in pairs(self.headers) do
|
||||
local header = AJP.ForwardRequest.Header[k:lower()] or k
|
||||
@@ -167,27 +167,27 @@ AJP = {
|
||||
else
|
||||
data = data .. bin.pack(">S", header)
|
||||
end
|
||||
|
||||
|
||||
data = data .. encstr(v)
|
||||
end
|
||||
|
||||
|
||||
-- encode attributes
|
||||
if ( p_url.query ) then
|
||||
data = data .. bin.pack("C", AJP.Attribute.QUERY_STRING)
|
||||
data = data .. encstr(p_url.query)
|
||||
end
|
||||
|
||||
|
||||
-- terminate the attribute list
|
||||
data = data .. bin.pack("C", AJP.Attribute.ARE_DONE)
|
||||
|
||||
|
||||
-- returns the AJP request as a string
|
||||
return bin.pack(">SSA", AJP.Magic, #data, data)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
Header = {
|
||||
['Content-Type'] = 0xA001,
|
||||
['Content-Language'] = 0xA002,
|
||||
@@ -201,51 +201,51 @@ AJP = {
|
||||
['Status'] = 0xA00A,
|
||||
['WWW-Authenticate'] = 0xA00B,
|
||||
},
|
||||
|
||||
|
||||
SendHeaders = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = { headers = {}, rawheaders = {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local sh = AJP.Response.SendHeaders:new()
|
||||
local pos = 6
|
||||
local status_msg, hdr_count
|
||||
|
||||
|
||||
pos, sh.status = bin.unpack(">S", data, pos)
|
||||
pos, status_msg = bin.unpack(">P", data, pos)
|
||||
pos = pos + 1
|
||||
sh['status-line'] = ("AJP/1.3 %d %s"):format(sh.status, status_msg)
|
||||
|
||||
|
||||
pos, hdr_count = bin.unpack(">S", data, pos)
|
||||
|
||||
|
||||
local function headerById(id)
|
||||
for k, v in pairs(AJP.Response.Header) do
|
||||
if ( v == id ) then return k end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
for i=1, hdr_count do
|
||||
local key, val, len
|
||||
pos, len = bin.unpack(">S", data, pos)
|
||||
|
||||
|
||||
if ( len < 0xA000 ) then
|
||||
pos, key = bin.unpack("A"..len, data, pos)
|
||||
pos = pos + 1
|
||||
else
|
||||
key = headerById(len)
|
||||
end
|
||||
|
||||
|
||||
pos, val = bin.unpack(">P", data, pos)
|
||||
pos = pos + 1
|
||||
|
||||
|
||||
sh.headers[key:lower()] = val
|
||||
|
||||
|
||||
-- to keep the order, in which the headers were received,
|
||||
-- add them to the rawheader table as well. This is based
|
||||
-- on the same principle as the http library, however the
|
||||
@@ -255,16 +255,16 @@ AJP = {
|
||||
end
|
||||
return sh
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Comm class handles sending and receiving AJP requests/responses
|
||||
Comm = {
|
||||
|
||||
|
||||
-- Creates a new Comm instance
|
||||
new = function(self, host, port, options)
|
||||
local o = { host = host, port = port, options = options or {}}
|
||||
@@ -272,7 +272,7 @@ Comm = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects to the AJP server
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -282,7 +282,7 @@ Comm = {
|
||||
self.socket:set_timeout(self.options.timeout or 5000)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a request to the server
|
||||
--
|
||||
-- @param req instance of object that can be serialized with tostring
|
||||
@@ -291,7 +291,7 @@ Comm = {
|
||||
send = function(self, req)
|
||||
return self.socket:send(tostring(req))
|
||||
end,
|
||||
|
||||
|
||||
-- Receives an AJP response from the server
|
||||
--
|
||||
-- @return status true on succes, false on failure
|
||||
@@ -317,7 +317,7 @@ Comm = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
|
||||
|
||||
local pos, code = bin.unpack("C", data)
|
||||
if ( AJP.Code.SEND_HEADERS == code ) then
|
||||
local sh = AJP.Response.SendHeaders.parse(buf .. data)
|
||||
@@ -330,22 +330,22 @@ Comm = {
|
||||
end
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
-- Closes the socket
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
--- Creates a new AJP Helper instance
|
||||
--
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
-- @param opt
|
||||
-- @param opt
|
||||
-- @return o new Helper instance
|
||||
new = function(self, host, port, opt)
|
||||
local o = { host = host, port = port, opt = opt or {} }
|
||||
@@ -362,18 +362,18 @@ Helper = {
|
||||
self.comm = Comm:new(self.host, self.port, self.opt)
|
||||
return self.comm:connect()
|
||||
end,
|
||||
|
||||
|
||||
getOption = function(self, options, key)
|
||||
|
||||
|
||||
-- first check options, then global self.opt
|
||||
if ( options and options[key] ) then
|
||||
return options[key]
|
||||
elseif ( self.opt and self.opt[key] ) then
|
||||
return self.opt[key]
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
--- Sends an AJP request to the server
|
||||
--
|
||||
-- @param url string containing the URL to query
|
||||
@@ -388,22 +388,22 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to get socket information"
|
||||
end
|
||||
|
||||
|
||||
local request = AJP.ForwardRequest:new(self.host, self.port, method, url, headers, attributes, { raddr = rhost })
|
||||
if ( not(self.comm:send(request)) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
local status, result = self.comm:receive()
|
||||
|
||||
|
||||
-- support Basic authentication
|
||||
if ( status and result.status == 401 and result.headers['www-authenticate'] ) then
|
||||
|
||||
|
||||
local auth = self:getOption(options, "auth")
|
||||
if ( not(auth) or not(auth.username) and not(auth.password) ) then
|
||||
stdnse.print_debug(2, "No authentication information")
|
||||
return status, result
|
||||
end
|
||||
|
||||
|
||||
local challenges = http.parse_www_authenticate(result.headers['www-authenticate'])
|
||||
local scheme
|
||||
for _, challenge in ipairs(challenges or {}) do
|
||||
@@ -412,7 +412,7 @@ Helper = {
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( not(scheme) ) then
|
||||
stdnse.print_debug(2, "Could not find a supported authentication scheme")
|
||||
elseif ( "basic" ~= scheme ) then
|
||||
@@ -426,7 +426,7 @@ Helper = {
|
||||
end
|
||||
status, result = self.comm:receive()
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
return status, result
|
||||
end,
|
||||
@@ -443,7 +443,7 @@ Helper = {
|
||||
get = function(self, url, headers, attributes, options)
|
||||
return self:request("GET", url, headers, attributes, options)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends an AJP HEAD request to the server
|
||||
--
|
||||
-- @param url string containing the URL to query
|
||||
@@ -456,7 +456,7 @@ Helper = {
|
||||
head = function(self, url, headers, attributes, options)
|
||||
return self:request("HEAD", url, headers, attributes, options)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends an AJP TRACE request to the server
|
||||
--
|
||||
-- @param url string containing the URL to query
|
||||
@@ -469,7 +469,7 @@ Helper = {
|
||||
trace = function(self, url, headers, attributes, options)
|
||||
return self:request("TRACE", url, headers, attributes, options)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends an AJP PUT request to the server
|
||||
--
|
||||
-- @param url string containing the URL to query
|
||||
@@ -495,7 +495,7 @@ Helper = {
|
||||
delete = function(self, url, headers, attributes, options)
|
||||
return self:request("DELETE", url, headers, attributes, options)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends an AJP OPTIONS request to the server
|
||||
--
|
||||
-- @param url string containing the URL to query
|
||||
@@ -508,18 +508,18 @@ Helper = {
|
||||
options = function(self, url, headers, attributes, options)
|
||||
return self:request("OPTIONS", url, headers, attributes, options)
|
||||
end,
|
||||
|
||||
|
||||
-- should only work against 127.0.0.1
|
||||
shutdownContainer = function(self)
|
||||
self.comm:send(bin.pack("H", "1234000107"))
|
||||
self.comm:receive()
|
||||
end,
|
||||
|
||||
|
||||
--- Disconnects from the server
|
||||
close = function(self)
|
||||
return self.comm:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
return _ENV;
|
||||
|
||||
@@ -50,12 +50,12 @@ ASN1Decoder = {
|
||||
setStopOnError = function(self, val)
|
||||
self.stoponerror = val
|
||||
end,
|
||||
|
||||
|
||||
--- Registers the base simple type decoders
|
||||
--
|
||||
--
|
||||
registerBaseDecoders = function(self)
|
||||
self.decoder = {}
|
||||
|
||||
|
||||
-- Boolean
|
||||
self.decoder["01"] = function( self, encStr, elen, pos )
|
||||
local val = bin.unpack("H", encStr, pos)
|
||||
@@ -85,7 +85,7 @@ ASN1Decoder = {
|
||||
self.decoder["06"] = function( self, encStr, elen, pos )
|
||||
return self:decodeOID( encStr, elen, pos )
|
||||
end
|
||||
|
||||
|
||||
-- Context specific tags
|
||||
--
|
||||
self.decoder["30"] = function( self, encStr, elen, pos )
|
||||
@@ -104,7 +104,7 @@ ASN1Decoder = {
|
||||
end,
|
||||
|
||||
--- Decodes the ASN.1's built-in simple types
|
||||
--
|
||||
--
|
||||
-- @param encStr Encoded string.
|
||||
-- @param pos Current position in the string.
|
||||
-- @return The position after decoding
|
||||
@@ -169,7 +169,7 @@ ASN1Decoder = {
|
||||
end
|
||||
return pos, seq
|
||||
end,
|
||||
|
||||
|
||||
-- Decode one component of an OID from a byte string. 7 bits of the component
|
||||
-- are stored in each octet, most significant first, with the eigth bit set in
|
||||
-- all octets but the last. These encoding rules come from
|
||||
@@ -186,7 +186,7 @@ ASN1Decoder = {
|
||||
|
||||
return pos, n
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes an OID from a sequence of bytes.
|
||||
--
|
||||
-- @param encStr Encoded string.
|
||||
@@ -216,7 +216,7 @@ ASN1Decoder = {
|
||||
|
||||
return pos, oid
|
||||
end,
|
||||
|
||||
|
||||
---
|
||||
-- Decodes length part of encoded value according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
@@ -240,7 +240,7 @@ ASN1Decoder = {
|
||||
end
|
||||
return pos, elen
|
||||
end,
|
||||
|
||||
|
||||
---
|
||||
-- Decodes an Integer according to ASN.1 basic encoding rules.
|
||||
-- @param encStr Encoded string.
|
||||
@@ -257,7 +257,7 @@ ASN1Decoder = {
|
||||
end
|
||||
return pos, value
|
||||
end,
|
||||
|
||||
|
||||
---
|
||||
-- Decodes an SNMP packet or a part of it according to ASN.1 basic encoding
|
||||
-- rules.
|
||||
@@ -270,7 +270,7 @@ ASN1Decoder = {
|
||||
_, result = self:decode(encStr, pos)
|
||||
return result
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- The encoder class
|
||||
@@ -288,7 +288,7 @@ ASN1Encoder = {
|
||||
---
|
||||
-- Encodes an ASN1 sequence, the value of 30 below breaks down as
|
||||
-- 0x30 = 00110000 = 00 1 10000
|
||||
-- hex binary Universal Constructed value Data Type = SEQUENCE (16)
|
||||
-- hex binary Universal Constructed value Data Type = SEQUENCE (16)
|
||||
encodeSeq = function(self, seqData)
|
||||
return bin.pack('HAA' , '30', self.encodeLength(#seqData), seqData)
|
||||
end,
|
||||
@@ -309,7 +309,7 @@ ASN1Encoder = {
|
||||
|
||||
return ''
|
||||
end,
|
||||
|
||||
|
||||
--- Allows for registration of additional tag encoders
|
||||
--
|
||||
-- @param tagEncoders table containing encoding functions @see tagEncoders
|
||||
@@ -319,20 +319,20 @@ ASN1Encoder = {
|
||||
self.encoder[k] = v
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- ASN.1 Simple types encoders
|
||||
registerBaseEncoders = function(self)
|
||||
self.encoder = {}
|
||||
|
||||
-- Bolean encoder
|
||||
self.encoder['boolean'] = function( self, val )
|
||||
if val then
|
||||
if val then
|
||||
return bin.pack('H','01 01 FF')
|
||||
else
|
||||
else
|
||||
return bin.pack('H', '01 01 00')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Table encoder
|
||||
self.encoder['table'] = function( self, val )
|
||||
assert('table' == type(val), "val is not a table")
|
||||
@@ -340,7 +340,7 @@ ASN1Encoder = {
|
||||
assert(val.value ~= nil, "Table is missing the value field")
|
||||
return bin.pack("HAA", val.type, self.encodeLength(#val.value), val.value)
|
||||
end
|
||||
|
||||
|
||||
-- Integer encoder
|
||||
self.encoder['number'] = function( self, val )
|
||||
local ival = self.encodeInt(val)
|
||||
@@ -358,9 +358,9 @@ ASN1Encoder = {
|
||||
self.encoder['nil'] = function( self, val )
|
||||
return bin.pack('H', '05 00')
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
-- Encode one component of an OID as a byte string. 7 bits of the component are
|
||||
-- stored in each octet, most significant first, with the eigth bit set in all
|
||||
-- octets but the last. These encoding rules come from
|
||||
@@ -412,7 +412,7 @@ ASN1Encoder = {
|
||||
return bin.pack("x")
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
---
|
||||
-- Encodes the length part of a ASN.1 encoding triplet using the "primitive,
|
||||
-- definite-length" method.
|
||||
@@ -459,7 +459,7 @@ end
|
||||
-- Converts an integer to a BER encoded type table
|
||||
--
|
||||
-- @param i number containing the value to decode
|
||||
-- @return table with the following entries <code>class</code>, <code>constructed</code>,
|
||||
-- @return table with the following entries <code>class</code>, <code>constructed</code>,
|
||||
-- <code>primitive</code> and <code>number</code>
|
||||
function intToBER( i )
|
||||
local ber = {}
|
||||
@@ -478,7 +478,7 @@ function intToBER( i )
|
||||
ber.number = i - ber.class - 32
|
||||
else
|
||||
ber.primitive = true
|
||||
ber.number = i - ber.class
|
||||
ber.number = i - ber.class
|
||||
end
|
||||
return ber
|
||||
end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
-- Base32 encoding and decoding. Follows RFC 4648.
|
||||
--
|
||||
--
|
||||
-- @author Philip Pickering <pgpickering@gmail.com>
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
-- @ported base64 to base32 <john.r.bond@gmail.com>
|
||||
@@ -25,9 +25,9 @@ local b32standard = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7',
|
||||
}
|
||||
|
||||
|
||||
local b32dcstandard = {} -- efficency
|
||||
b32dcstandard['A'] = '00000'
|
||||
b32dcstandard['B'] = '00001'
|
||||
@@ -108,7 +108,7 @@ local b32dctable = b32dcstandard
|
||||
|
||||
local append = table.insert
|
||||
local substr = string.sub
|
||||
local bpack = bin.pack
|
||||
local bpack = bin.pack
|
||||
local bunpack = bin.unpack
|
||||
local concat = table.concat
|
||||
|
||||
@@ -155,13 +155,13 @@ function enc(bdata, hexExtend)
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "0000"))
|
||||
append(b32dataBuf, '====')
|
||||
elseif #bitstring == 2 then
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "000") )
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "000") )
|
||||
append(b32dataBuf, '=')
|
||||
elseif #bitstring == 3 then
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "00") )
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "00") )
|
||||
append(b32dataBuf, "======")
|
||||
elseif #bitstring == 4 then
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "0") )
|
||||
append(b32dataBuf, b32enc5bit(bitstring .. "0") )
|
||||
append(b32dataBuf, '===')
|
||||
end
|
||||
return concat(b32dataBuf)
|
||||
|
||||
@@ -22,10 +22,10 @@ local b64table = {
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
}
|
||||
|
||||
|
||||
local b64dctable = {} -- efficency
|
||||
b64dctable['A'] = '000000'
|
||||
b64dctable['B'] = '000001'
|
||||
@@ -95,7 +95,7 @@ b64dctable['/'] = '111111'
|
||||
|
||||
local append = table.insert
|
||||
local substr = string.sub
|
||||
local bpack = bin.pack
|
||||
local bpack = bin.pack
|
||||
local bunpack = bin.unpack
|
||||
local concat = table.concat
|
||||
|
||||
@@ -107,7 +107,7 @@ local function b64enc6bit(bits)
|
||||
-- local byte
|
||||
-- local _, byte = bunpack("C", bpack("B", "00" .. bits))
|
||||
--
|
||||
|
||||
|
||||
-- more efficient, does the same (nb: add one to byte moved up one line):
|
||||
local byte = tonumber(bits, 2) + 1
|
||||
return b64table[byte]
|
||||
@@ -146,7 +146,7 @@ function enc(bdata)
|
||||
end
|
||||
end
|
||||
if #nbyte == 2 then
|
||||
append(b64dataBuf, b64enc6bit(nbyte .. "0000") )
|
||||
append(b64dataBuf, b64enc6bit(nbyte .. "0000") )
|
||||
append(b64dataBuf, "==")
|
||||
elseif #nbyte == 4 then
|
||||
append(b64dataBuf, b64enc6bit(nbyte .. "00"))
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
--
|
||||
-- Version 0.2
|
||||
--
|
||||
--
|
||||
-- Created 11/09/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 17/02/2012 - v0.2 - fixed count parsing
|
||||
-- - changed version/verack handling to support
|
||||
@@ -43,9 +43,9 @@ _ENV = stdnse.module("bitcoin", stdnse.seeall)
|
||||
|
||||
-- A class that supports the BitCoin network address structure
|
||||
NetworkAddress = {
|
||||
|
||||
|
||||
NODE_NETWORK = 1,
|
||||
|
||||
|
||||
-- Creates a new instance of the NetworkAddress class
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
@@ -60,20 +60,20 @@ NetworkAddress = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Creates a new instance of NetworkAddress based on the data string
|
||||
-- @param data string of bytes
|
||||
-- @return na instance of NetworkAddress
|
||||
fromString = function(data)
|
||||
assert(26 == #data, "Expected 26 bytes of data")
|
||||
|
||||
|
||||
local na = NetworkAddress:new()
|
||||
local _
|
||||
_, na.service, na.ipv6_prefix, na.host, na.port = bin.unpack("<LH12I>S", data)
|
||||
na.host = ipOps.fromdword(na.host)
|
||||
return na
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the NetworkAddress instance to string
|
||||
-- @return data string containing the NetworkAddress instance
|
||||
__tostring = function(self)
|
||||
@@ -85,10 +85,10 @@ NetworkAddress = {
|
||||
|
||||
-- The request class container
|
||||
Request = {
|
||||
|
||||
|
||||
-- The version request
|
||||
Version = {
|
||||
|
||||
|
||||
-- Creates a new instance of the Version request
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
@@ -96,7 +96,7 @@ Request = {
|
||||
-- @param lport number containing the source port
|
||||
-- @return o instance of Version
|
||||
new = function(self, host, port, lhost, lport)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
lhost= lhost,
|
||||
@@ -106,7 +106,7 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the Version request to a string
|
||||
-- @return data as string
|
||||
__tostring = function(self)
|
||||
@@ -115,7 +115,7 @@ Request = {
|
||||
local len = 85
|
||||
-- ver: 0.4.0
|
||||
local ver = 0x9c40
|
||||
|
||||
|
||||
-- NODE_NETWORK = 1
|
||||
local services = 1
|
||||
local timestamp = os.time()
|
||||
@@ -132,20 +132,20 @@ Request = {
|
||||
-- Checksum is first 4 bytes of sha256(sha256(payload))
|
||||
local checksum = openssl.digest("sha256", payload)
|
||||
checksum = openssl.digest("sha256", checksum)
|
||||
|
||||
|
||||
-- Construct the header without checksum
|
||||
local header = bin.pack("<IAI", magic, cmd, len)
|
||||
|
||||
local header = bin.pack("<IAI", magic, cmd, len)
|
||||
|
||||
-- After 2012-02-20, version messages require checksums
|
||||
header = header .. bin.pack("A", checksum:sub(1,4))
|
||||
|
||||
|
||||
return header .. payload
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- The GetAddr request
|
||||
GetAddr = {
|
||||
|
||||
|
||||
-- Creates a new instance of the Version request
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
@@ -153,7 +153,7 @@ Request = {
|
||||
-- @param lport number containing the source port
|
||||
-- @return o instance of Version
|
||||
new = function(self, host, port, lhost, lport)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
lhost= lhost,
|
||||
@@ -163,7 +163,7 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the Version request to a string
|
||||
-- @return data as string
|
||||
__tostring = function(self)
|
||||
@@ -175,27 +175,27 @@ Request = {
|
||||
return bin.pack("<IAII", magic, cmd, len, chksum)
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
VerAck = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
return bin.pack("<IAII", 0xD9B4BEF9, "verack\0\0\0\0\0\0", 0, 0xe2e0f65d)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The response class container
|
||||
Response = {
|
||||
|
||||
|
||||
Header = {
|
||||
size = 24,
|
||||
new = function(self)
|
||||
@@ -213,21 +213,21 @@ Response = {
|
||||
parse = function(data)
|
||||
local header = Response.Header:new()
|
||||
local pos
|
||||
|
||||
|
||||
pos, header.magic, header.cmd, header.length, header.checksum = bin.unpack(">IA12II", data)
|
||||
return header
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
Alert = {
|
||||
|
||||
|
||||
type = "Alert",
|
||||
-- Creates a new instance of Version based on data string
|
||||
-- @param data string containing the raw response
|
||||
-- @return o instance of Version
|
||||
new = function(self, data)
|
||||
local o = {
|
||||
local o = {
|
||||
data = data,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
@@ -240,24 +240,24 @@ Response = {
|
||||
parse = function(self)
|
||||
local pos = Response.Header.size + 1
|
||||
self.header = Response.Header.parse(self.data)
|
||||
|
||||
|
||||
local p_length
|
||||
pos, p_length = Util.decodeVarInt(self.data, pos)
|
||||
local data
|
||||
pos, data = bin.unpack("A" .. p_length, self.data, pos)
|
||||
|
||||
|
||||
--
|
||||
-- TODO: Alert decoding goes here
|
||||
--
|
||||
|
||||
|
||||
return
|
||||
end,
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
-- The version response message
|
||||
Version = {
|
||||
|
||||
|
||||
-- Creates a new instance of Version based on data string
|
||||
-- @param data string containing the raw response
|
||||
-- @return o instance of Version
|
||||
@@ -268,7 +268,7 @@ Response = {
|
||||
o:parse()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the raw data and builds the Version instance
|
||||
parse = function(self)
|
||||
local pos, ra, sa
|
||||
@@ -277,7 +277,7 @@ Response = {
|
||||
pos, self.magic, self.cmd, self.len, self.checksum, self.ver_raw, self.service,
|
||||
self.timestamp, ra, sa, self.nodeid,
|
||||
self.subver, self.lastblock = bin.unpack("<IA12IIILLA26A26H8CI", self.data)
|
||||
|
||||
|
||||
local function decode_bitcoin_version(n)
|
||||
if ( n < 31300 ) then
|
||||
local minor, micro = n / 100, n % 100
|
||||
@@ -287,16 +287,16 @@ Response = {
|
||||
return ("0.%d.%d"):format(minor, micro)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self.ver = decode_bitcoin_version(self.ver_raw)
|
||||
self.sa = NetworkAddress.fromString(sa)
|
||||
self.ra = NetworkAddress.fromString(ra)
|
||||
end,
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- The verack response message
|
||||
VerAck = {
|
||||
|
||||
|
||||
-- Creates a new instance of VerAck based on data string
|
||||
-- @param data string containing the raw response
|
||||
-- @return o instance of Version
|
||||
@@ -307,7 +307,7 @@ Response = {
|
||||
o:parse()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the raw data and builds the VerAck instance
|
||||
parse = function(self)
|
||||
local pos
|
||||
@@ -318,7 +318,7 @@ Response = {
|
||||
|
||||
-- The Addr response message
|
||||
Addr = {
|
||||
|
||||
|
||||
-- Creates a new instance of VerAck based on data string
|
||||
-- @param data string containing the raw response
|
||||
-- @return o instance of Addr
|
||||
@@ -329,13 +329,13 @@ Response = {
|
||||
o:parse()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the raw data and builds the Addr instance
|
||||
parse = function(self)
|
||||
local pos, count
|
||||
pos, self.magic, self.cmd, self.len, self.chksum = bin.unpack("<IA12II", self.data)
|
||||
pos, self.magic, self.cmd, self.len, self.chksum = bin.unpack("<IA12II", self.data)
|
||||
pos, count = Util.decodeVarInt(self.data, pos)
|
||||
|
||||
|
||||
self.addresses = {}
|
||||
for c=1, count do
|
||||
if ( self.version > 31402 ) then
|
||||
@@ -345,13 +345,13 @@ Response = {
|
||||
table.insert(self.addresses, { ts = timestamp, address = na })
|
||||
end
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- The inventory server packet
|
||||
Inv = {
|
||||
|
||||
|
||||
-- Creates a new instance of VerAck based on data string
|
||||
-- @param data string containing the raw response
|
||||
-- @return o instance of Addr
|
||||
@@ -362,14 +362,14 @@ Response = {
|
||||
o:parse()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the raw data and builds the Addr instance
|
||||
parse = function(self)
|
||||
local pos, count
|
||||
pos, self.magic, self.cmd, self.len = bin.unpack("<IA12II", self.data)
|
||||
end,
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- Receives the packet and decodes it
|
||||
-- @param socket BCSocket instance
|
||||
-- @param version number containing the server version
|
||||
@@ -381,10 +381,10 @@ Response = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to read the packet header"
|
||||
end
|
||||
|
||||
|
||||
local pos, magic, cmd, len, checksum = bin.unpack("<IA12II", header)
|
||||
local data = ""
|
||||
|
||||
|
||||
-- the verack has no payload
|
||||
if ( 0 ~= len ) then
|
||||
status, data = socket:recv(len)
|
||||
@@ -394,7 +394,7 @@ Response = {
|
||||
end
|
||||
return Response.decode(header .. data, version)
|
||||
end,
|
||||
|
||||
|
||||
-- Decodes the raw packet data
|
||||
-- @param data string containing the raw packet
|
||||
-- @param version number containing the server version
|
||||
@@ -416,7 +416,7 @@ Response = {
|
||||
else
|
||||
return false, ("Unknown command (%s)"):format(cmd)
|
||||
end
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
Util = {
|
||||
@@ -438,23 +438,23 @@ Util = {
|
||||
return pos, count
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
-- A buffered socket implementation
|
||||
BCSocket =
|
||||
{
|
||||
{
|
||||
retries = 3,
|
||||
|
||||
-- Creates a new BCSocket instance
|
||||
|
||||
-- Creates a new BCSocket instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
-- @param options table containing additional options
|
||||
-- <code>timeout</code> - the socket timeout in ms
|
||||
-- @return instance of BCSocket
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
timeout = "table" == type(options) and options.timeout or 10000
|
||||
@@ -465,7 +465,7 @@ BCSocket =
|
||||
o.Buffer = nil
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Establishes a connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -474,7 +474,7 @@ BCSocket =
|
||||
self.Socket:set_timeout( self.timeout )
|
||||
return self.Socket:connect( self.host, self.port )
|
||||
end,
|
||||
|
||||
|
||||
--- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -482,7 +482,7 @@ BCSocket =
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
@@ -492,9 +492,9 @@ BCSocket =
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
@@ -502,13 +502,13 @@ BCSocket =
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -520,15 +520,15 @@ BCSocket =
|
||||
|
||||
-- The Helper class used as a primary interface to scripts
|
||||
Helper = {
|
||||
|
||||
-- Creates a new Helper instance
|
||||
|
||||
-- Creates a new Helper instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
-- @param options table containing additional options
|
||||
-- <code>timeout</code> - the socket timeout in ms
|
||||
-- @return instance of Helper
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
options = options
|
||||
@@ -544,7 +544,7 @@ Helper = {
|
||||
connect = function(self)
|
||||
self.socket = BCSocket:new(self.host, self.port, self.options)
|
||||
local status, err = self.socket:connect()
|
||||
|
||||
|
||||
if ( not(status) ) then
|
||||
return false, err
|
||||
end
|
||||
@@ -560,63 +560,63 @@ Helper = {
|
||||
if ( not(self.socket) ) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local req = Request.Version:new(
|
||||
self.host, self.port, self.lhost, self.lport
|
||||
)
|
||||
|
||||
local status, err = self.socket:send(tostring(req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send \"Version\" request to server"
|
||||
end
|
||||
|
||||
local version
|
||||
status, version = Response.recvPacket(self.socket)
|
||||
|
||||
if ( not(status) or not(version) or version.cmd ~= "version\0\0\0\0\0" ) then
|
||||
return false, "Failed to read \"Version\" response from server"
|
||||
end
|
||||
|
||||
if ( version.ver_raw > 29000 ) then
|
||||
local status, verack = Response.recvPacket(self.socket)
|
||||
end
|
||||
|
||||
local verack = Request.VerAck:new()
|
||||
local status, err = self.socket:send(tostring(verack))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send \"Version\" request to server"
|
||||
end
|
||||
|
||||
self.version = version.ver_raw
|
||||
return status, version
|
||||
end,
|
||||
|
||||
getNodes = function(self)
|
||||
local req = Request.GetAddr:new(
|
||||
self.host, self.port, self.lhost, self.lport
|
||||
)
|
||||
|
||||
local status, err = self.socket:send(tostring(req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send \"Version\" request to server"
|
||||
end
|
||||
|
||||
|
||||
local version
|
||||
status, version = Response.recvPacket(self.socket)
|
||||
|
||||
if ( not(status) or not(version) or version.cmd ~= "version\0\0\0\0\0" ) then
|
||||
return false, "Failed to read \"Version\" response from server"
|
||||
end
|
||||
|
||||
if ( version.ver_raw > 29000 ) then
|
||||
local status, verack = Response.recvPacket(self.socket)
|
||||
end
|
||||
|
||||
local verack = Request.VerAck:new()
|
||||
local status, err = self.socket:send(tostring(verack))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send \"Version\" request to server"
|
||||
end
|
||||
|
||||
self.version = version.ver_raw
|
||||
return status, version
|
||||
end,
|
||||
|
||||
getNodes = function(self)
|
||||
local req = Request.GetAddr:new(
|
||||
self.host, self.port, self.lhost, self.lport
|
||||
)
|
||||
|
||||
local status, err = self.socket:send(tostring(req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send \"Version\" request to server"
|
||||
end
|
||||
|
||||
-- take care of any alerts that may be incoming
|
||||
local status, response = Response.recvPacket(self.socket, self.version)
|
||||
while ( status and response and response.type == "Alert" ) do
|
||||
status, response = Response.recvPacket(self.socket, self.version)
|
||||
end
|
||||
|
||||
|
||||
return status, response
|
||||
end,
|
||||
|
||||
|
||||
-- Reads a message from the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response instance of response packet if status is true
|
||||
-- err string containing the error message if status is false
|
||||
readMessage = function(self)
|
||||
assert(self.version, "Version handshake has not been performed")
|
||||
return Response.recvPacket(self.socket, self.version)
|
||||
return Response.recvPacket(self.socket, self.version)
|
||||
end,
|
||||
|
||||
-- Closes the connection to the server
|
||||
@@ -624,7 +624,7 @@ Helper = {
|
||||
-- @return err code, if status is false
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
--- Bittorrent and DHT protocol library which enables users to read
|
||||
--- Bittorrent and DHT protocol library which enables users to read
|
||||
-- information from a torrent file, decode bencoded (bittorrent
|
||||
-- encoded) buffers, find peers associated with a certain torrent and
|
||||
-- retrieve nodes discovered during the search for peers.
|
||||
@@ -79,7 +79,7 @@
|
||||
-- the peers_dht_ping to be processed by the dht_ping thread and so on.
|
||||
-- That enables the three threads to cooperate and pass on peers and
|
||||
-- nodes between each other.
|
||||
--
|
||||
--
|
||||
-- There is also a bdecode function which decodes Bittorrent encoded
|
||||
-- buffers and organizes them into a structure I deemed fit for use.
|
||||
-- There are two known bittorrent structures: the list and the
|
||||
@@ -104,7 +104,7 @@ local url = require "url"
|
||||
_ENV = stdnse.module("bittorrent", stdnse.seeall)
|
||||
|
||||
--- Given a buffer and a starting position in the buffer, this function decodes
|
||||
-- a bencoded string there and returns it as a normal lua string, as well as
|
||||
-- a bencoded string there and returns it as a normal lua string, as well as
|
||||
-- the position after the string
|
||||
local bdec_string = function(buf, pos)
|
||||
local len = ""
|
||||
@@ -126,16 +126,16 @@ local bdec_string = function(buf, pos)
|
||||
end
|
||||
|
||||
--- Given a buffer and a starting position in the buffer, this function decodes
|
||||
-- a bencoded number there and returns it as a normal lua number, as well as
|
||||
-- a bencoded number there and returns it as a normal lua number, as well as
|
||||
-- the position after the number
|
||||
local bdec_number = function(buf, pos)
|
||||
local s, n = string.match(buf, "^i(%-*)(%d+)e", pos)
|
||||
if not n then return nil end
|
||||
|
||||
|
||||
local num = tonumber(n)
|
||||
-- 1 for the "i", 1 for the "e", 1 if there is a "-" plus the length of n
|
||||
pos = pos + 2 + #n
|
||||
|
||||
|
||||
if s == "-" then
|
||||
num = -num
|
||||
pos = pos + 1
|
||||
@@ -144,7 +144,7 @@ local bdec_number = function(buf, pos)
|
||||
return num, pos
|
||||
end
|
||||
|
||||
--- Parses a bencoded buffer
|
||||
--- Parses a bencoded buffer
|
||||
-- @param buf, string with the bencoded buffer
|
||||
-- @return bool indicating if parsing went ok
|
||||
-- @return table containing the decoded structure, or error string
|
||||
@@ -154,7 +154,7 @@ bdecode = function(buf)
|
||||
-- the main table
|
||||
local t = {}
|
||||
local stack = {}
|
||||
|
||||
|
||||
local pos = 1
|
||||
local cur = {}
|
||||
cur.type = "list"
|
||||
@@ -175,7 +175,7 @@ bdecode = function(buf)
|
||||
|
||||
-- next element is a number
|
||||
elseif "i" == string.char(buf:byte(pos)) then
|
||||
local num
|
||||
local num
|
||||
num, pos = bdec_number(buf, pos)
|
||||
if not num then return nil, "Error parsing number", pos end
|
||||
table.insert(cur.ref, num)
|
||||
@@ -185,7 +185,7 @@ bdecode = function(buf)
|
||||
local new_list = {}
|
||||
new_list.type="list"
|
||||
table.insert(cur.ref, new_list)
|
||||
|
||||
|
||||
cur = {}
|
||||
cur.type = "list"
|
||||
cur.ref = new_list
|
||||
@@ -193,17 +193,17 @@ bdecode = function(buf)
|
||||
pos = pos+1
|
||||
|
||||
--next element is a dict
|
||||
elseif "d" == string.char(buf:byte(pos)) then
|
||||
elseif "d" == string.char(buf:byte(pos)) then
|
||||
local new_dict = {}
|
||||
new_dict.type = "dict"
|
||||
table.insert(cur.ref, new_dict)
|
||||
|
||||
|
||||
cur = {}
|
||||
cur.type = "dict"
|
||||
cur.ref = new_dict
|
||||
table.insert(stack, cur)
|
||||
pos = pos+1
|
||||
|
||||
|
||||
--escape from the list
|
||||
elseif "e" == string.char(buf:byte(pos)) then
|
||||
table.remove(stack, #stack)
|
||||
@@ -213,16 +213,16 @@ bdecode = function(buf)
|
||||
else
|
||||
return nil, "Unknown type found.", pos
|
||||
end
|
||||
|
||||
|
||||
elseif cur.type == "dict" then
|
||||
local item = {} -- {key = <string>, value = <.*>}
|
||||
-- used to skip reading the value when escaping from a structure
|
||||
local escape_flag = false
|
||||
|
||||
|
||||
-- fill the key
|
||||
if tonumber( string.char( buf:byte(pos) ) ) then
|
||||
local str
|
||||
local tmp_pos = pos
|
||||
local tmp_pos = pos
|
||||
str, pos = bdec_string(buf, pos)
|
||||
if not str then return nil, "Error parsing string.", pos end
|
||||
item.key = str
|
||||
@@ -231,13 +231,13 @@ bdecode = function(buf)
|
||||
cur = stack[#stack]
|
||||
if not cur then return nil, "Problem with list closure:", pos end
|
||||
pos = pos+1
|
||||
|
||||
|
||||
escape_flag = true
|
||||
|
||||
|
||||
else
|
||||
return nil, "A dict key has to be a string or escape.", pos
|
||||
end
|
||||
|
||||
|
||||
if not escape_flag then
|
||||
-- value
|
||||
-- next element is a string
|
||||
@@ -250,7 +250,7 @@ bdecode = function(buf)
|
||||
|
||||
--next element is a number
|
||||
elseif "i" == string.char(buf:byte(pos)) then
|
||||
local num
|
||||
local num
|
||||
num, pos = bdec_number(buf, pos)
|
||||
if not num then return nil, "Error parsing number.", pos end
|
||||
item.value = num
|
||||
@@ -261,7 +261,7 @@ bdecode = function(buf)
|
||||
item.value = {}
|
||||
item.value.type = "list"
|
||||
table.insert(cur.ref, item)
|
||||
|
||||
|
||||
cur = {}
|
||||
cur.type = "list"
|
||||
cur.ref = item.value
|
||||
@@ -296,8 +296,8 @@ bdecode = function(buf)
|
||||
return false, "Invalid type of structure. Fix the code."
|
||||
end
|
||||
end -- while(true)
|
||||
|
||||
-- The code below is commented out because some responses from trackers are
|
||||
|
||||
-- The code below is commented out because some responses from trackers are
|
||||
-- not according to standards
|
||||
|
||||
-- next(stack) is never gonna be nil because we're always in the main list
|
||||
@@ -305,15 +305,15 @@ bdecode = function(buf)
|
||||
-- if next(stack, next(stack)) then
|
||||
-- return false, "Probably file incorrect format"
|
||||
-- end
|
||||
|
||||
|
||||
return true, t
|
||||
end
|
||||
|
||||
--- This is the thread function which sends a DHT ping probe to every peer in
|
||||
-- pnt.peers_dht_ping after which the peer is moved to the pnt.peers and
|
||||
--- This is the thread function which sends a DHT ping probe to every peer in
|
||||
-- pnt.peers_dht_ping after which the peer is moved to the pnt.peers and
|
||||
-- removed from pnt.peers_dht_ping. Every peer which responds to the DHT ping
|
||||
-- is actually a DHT node and is added to the pnt.nodes_find_node table in
|
||||
-- order to be processed byt the find_node_thread(). This operation is done
|
||||
-- is actually a DHT node and is added to the pnt.nodes_find_node table in
|
||||
-- order to be processed byt the find_node_thread(). This operation is done
|
||||
-- during the specified timeout which has a default value of about 30 seconds.
|
||||
local dht_ping_thread = function(pnt, timeout)
|
||||
local condvar = nmap.condvar(pnt)
|
||||
@@ -331,34 +331,34 @@ local dht_ping_thread = function(pnt, timeout)
|
||||
while next(pnt.peers_dht_ping) ~= nil and num_peers <= 100 and os.time() - start < timeout do
|
||||
num_peers = num_peers +1
|
||||
local peer_ip, peer_info = next(pnt.peers_dht_ping)
|
||||
|
||||
|
||||
--transaction ids are 2 bytes long
|
||||
local t_ID_hex = stdnse.tohex(transaction_id % 0xffff)
|
||||
t_ID_hex = string.rep("0",4-#t_ID_hex)..t_ID_hex
|
||||
peer_info.transaction_id = bin.pack("H",t_ID_hex)
|
||||
|
||||
-- mark it as received so we can distinguish from the others and
|
||||
|
||||
-- mark it as received so we can distinguish from the others and
|
||||
-- successfully iterate while receiving
|
||||
peer_info.received = false
|
||||
|
||||
pnt.peers[peer_ip] = peer_info
|
||||
pnt.peers_dht_ping[peer_ip] = nil
|
||||
|
||||
|
||||
-- bencoded ping query describing a dictionary with y = q (query), q = ping
|
||||
-- {"t":<transaction_id>, "y":"q", "q":"ping", "a":{"id":<node_id>}}
|
||||
local ping_query = "d1:ad2:id20:" .. pnt.node_id .. "e1:q4:ping1:t2:" ..
|
||||
peer_info.transaction_id .. "1:y1:qe"
|
||||
|
||||
|
||||
status, data = socket:sendto(peer_ip, peer_info.port, ping_query)
|
||||
|
||||
transaction_id = transaction_id +1
|
||||
if transaction_id % 0xffff == 0 then
|
||||
if transaction_id % 0xffff == 0 then
|
||||
transaction_id = 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- receive responses up to a 100
|
||||
for c = 1, 100 do
|
||||
for c = 1, 100 do
|
||||
if os.time() - start >= timeout then break end
|
||||
status, data = socket:receive()
|
||||
if not status then break end
|
||||
@@ -381,19 +381,19 @@ local dht_ping_thread = function(pnt, timeout)
|
||||
trans_id = i.value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if (not error_flag) and good_response and node_id and trans_id then
|
||||
local peer_ip
|
||||
for ip, info in pairs(pnt.peers) do
|
||||
if info.transaction_id == trans_id then
|
||||
info.received = nil
|
||||
info.received = nil
|
||||
peer_ip = ip
|
||||
break
|
||||
end
|
||||
end
|
||||
if peer_ip then
|
||||
pnt.peers[peer_ip].node_id = node_id
|
||||
if not (pnt.nodes_find_node[peer_ip] or pnt.nodes_get_peers[peer_ip] or
|
||||
pnt.peers[peer_ip].node_id = node_id
|
||||
if not (pnt.nodes_find_node[peer_ip] or pnt.nodes_get_peers[peer_ip] or
|
||||
pnt.nodes[peer_ip]) then
|
||||
pnt.nodes_find_node[peer_ip] = pnt.peers[peer_ip]
|
||||
end
|
||||
@@ -407,18 +407,18 @@ local dht_ping_thread = function(pnt, timeout)
|
||||
end
|
||||
|
||||
|
||||
--- This thread sends a DHT find_node query to every node in
|
||||
--- This thread sends a DHT find_node query to every node in
|
||||
-- pnt.nodes_find_node, after which every node is moved to pnt.nodes_get_peers
|
||||
-- to be processed by the get_peers_thread() function. The responses to these
|
||||
-- to be processed by the get_peers_thread() function. The responses to these
|
||||
-- queries contain adresses of other DHT nodes (usually 8) which are added to
|
||||
-- the pnt.nodes_find_node list. This action is done for a timeout with a
|
||||
-- the pnt.nodes_find_node list. This action is done for a timeout with a
|
||||
-- default value of 30 seconds.
|
||||
local find_node_thread = function(pnt, timeout)
|
||||
local condvar = nmap.condvar(pnt)
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:set_timeout(3000)
|
||||
local status, data
|
||||
|
||||
|
||||
local start = os.time()
|
||||
while true do
|
||||
if os.time() - start >= timeout then break end
|
||||
@@ -429,15 +429,15 @@ local find_node_thread = function(pnt, timeout)
|
||||
local node_ip, node_info = next(pnt.nodes_find_node)
|
||||
|
||||
-- standard bittorrent protocol specified find_node query with y = q (query),
|
||||
-- q = "find_node" (type of query),
|
||||
-- q = "find_node" (type of query),
|
||||
-- find_node Query = {"t":<trainsaction_id>, "y":"q", "q":"find_node", "a": {"id":<node_id>, "target":<info_hash>}}
|
||||
local find_node_query = "d1:ad2:id20:" .. pnt.node_id .. "6:target20:" ..
|
||||
local find_node_query = "d1:ad2:id20:" .. pnt.node_id .. "6:target20:" ..
|
||||
pnt.info_hash .. "e1:q9:find_node1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe"
|
||||
|
||||
|
||||
-- add the traversed nodes to pnt.nodes_get_peers so they can be traversed by get_peers_thread
|
||||
pnt.nodes_get_peers[node_ip] = node_info
|
||||
pnt.nodes_find_node[node_ip] = nil
|
||||
|
||||
|
||||
status, data = socket:sendto(node_ip, node_info.port, find_node_query)
|
||||
end
|
||||
|
||||
@@ -448,7 +448,7 @@ local find_node_thread = function(pnt, timeout)
|
||||
local s, r = bdecode(data)
|
||||
|
||||
if s then
|
||||
local nodes = nil
|
||||
local nodes = nil
|
||||
if r[1] and r[1][1] and r[1][1].key == "r" and r[1][1].value then
|
||||
for _, el in ipairs(r[1][1].value) do
|
||||
if el.key == "nodes" then
|
||||
@@ -456,9 +456,9 @@ local find_node_thread = function(pnt, timeout)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--parse the nodes an add them to pnt.nodes_find_node
|
||||
if nodes then
|
||||
if nodes then
|
||||
for node_id, bin_node_ip, bin_node_port in nodes:gmatch("(....................)(....)(..)") do
|
||||
local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2),
|
||||
bin_node_ip:byte(3), bin_node_ip:byte(4))
|
||||
@@ -466,25 +466,25 @@ local find_node_thread = function(pnt, timeout)
|
||||
local node_info = {}
|
||||
node_info.port = node_port
|
||||
node_info.node_id = node_id
|
||||
|
||||
if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip]
|
||||
|
||||
if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip]
|
||||
or pnt.nodes_find_node[node_ip]) then
|
||||
pnt.nodes_find_node[node_ip] = node_info
|
||||
end
|
||||
end
|
||||
end -- if nodes
|
||||
end -- if s
|
||||
end -- for c = 1, 100
|
||||
end -- if s
|
||||
end -- for c = 1, 100
|
||||
end -- while true
|
||||
socket:close()
|
||||
condvar("signal")
|
||||
end
|
||||
|
||||
|
||||
--- This thread sends get_peers DHT queries to all the nodes in
|
||||
-- pnt.nodes_get_peers, after which they are moved to pnt.nodes. There are two
|
||||
-- kinds of responses to these kinds of queries. One response contains peers,
|
||||
-- which would be added to the pnt.peers_dht_ping list, and the other kind of
|
||||
--- This thread sends get_peers DHT queries to all the nodes in
|
||||
-- pnt.nodes_get_peers, after which they are moved to pnt.nodes. There are two
|
||||
-- kinds of responses to these kinds of queries. One response contains peers,
|
||||
-- which would be added to the pnt.peers_dht_ping list, and the other kind of
|
||||
-- response is sent when the queried node has no peers, and contains more nodes
|
||||
-- which are added to the pnt.nodes_find_node list.
|
||||
local get_peers_thread = function(pnt, timeout)
|
||||
@@ -503,11 +503,11 @@ local get_peers_thread = function(pnt, timeout)
|
||||
local node_ip, node_info = next(pnt.nodes_get_peers)
|
||||
|
||||
-- standard bittorrent protocol specified get_peers query with y ="q" (query)
|
||||
-- and q = "get_peers" (type of query)
|
||||
-- and q = "get_peers" (type of query)
|
||||
-- {"t":<transaction_id>, "y":"q", "q":"get_peers", "a": {"id":<node_id>, "info_hash":<info_hash>}}
|
||||
local get_peers_query = "d1:ad2:id20:" .. pnt.node_id .. "9:info_hash20:" ..
|
||||
local get_peers_query = "d1:ad2:id20:" .. pnt.node_id .. "9:info_hash20:" ..
|
||||
pnt.info_hash .. "e1:q9:get_peers1:t2:" .. openssl.rand_bytes(2) .. "1:y1:qe"
|
||||
|
||||
|
||||
pnt.nodes[node_ip] = node_info
|
||||
pnt.nodes_get_peers[node_ip] = nil
|
||||
|
||||
@@ -525,7 +525,7 @@ local get_peers_thread = function(pnt, timeout)
|
||||
local nodes = nil
|
||||
local peers = nil
|
||||
for _,el in ipairs(r[1]) do
|
||||
if el.key == "y" and el.value == "r" then
|
||||
if el.key == "y" and el.value == "r" then
|
||||
good_response = true
|
||||
elseif el.key == "r" then
|
||||
for _,i in ipairs(el.value) do
|
||||
@@ -538,39 +538,39 @@ local get_peers_thread = function(pnt, timeout)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not good_response then
|
||||
|
||||
if not good_response then
|
||||
break
|
||||
end
|
||||
|
||||
if nodes then
|
||||
|
||||
for node_id, bin_node_ip, bin_node_port in
|
||||
for node_id, bin_node_ip, bin_node_port in
|
||||
nodes:gmatch("(....................)(....)(..)") do
|
||||
|
||||
|
||||
local node_ip = string.format("%d.%d.%d.%d", bin_node_ip:byte(1), bin_node_ip:byte(2),
|
||||
bin_node_ip:byte(3), bin_node_ip:byte(4))
|
||||
local node_port = bit.lshift(bin_node_port:byte(1),8) + bin_node_port:byte(2)
|
||||
local node_info = {}
|
||||
node_info.port = node_port
|
||||
node_info.node_id = node_id
|
||||
|
||||
if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or
|
||||
|
||||
if not (pnt.nodes[node_ip] or pnt.nodes_get_peers[node_ip] or
|
||||
pnt.nodes_find_node[node_ip]) then
|
||||
pnt.nodes_find_node[node_ip] = node_info
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
elseif peers then
|
||||
|
||||
for _, peer in ipairs(peers) do
|
||||
local bin_ip, bin_port = peer:match("(....)(..)")
|
||||
local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1),
|
||||
local ip = string.format("%d.%d.%d.%d", bin_ip:byte(1),
|
||||
bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4))
|
||||
local port = bit.lshift(bin_port:byte(1),8)+bin_port:byte(2)
|
||||
|
||||
|
||||
if not (pnt.peers[ip] or pnt.peers_dht_ping[ip]) then
|
||||
pnt.peers_dht_ping[ip] = {}
|
||||
pnt.peers_dht_ping[ip].port = port
|
||||
@@ -587,36 +587,36 @@ end
|
||||
|
||||
|
||||
|
||||
Torrent =
|
||||
Torrent =
|
||||
{
|
||||
new = function(self)
|
||||
local o ={}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
self.buffer = nil -- buffer to keep the torrent
|
||||
|
||||
self.buffer = nil -- buffer to keep the torrent
|
||||
self.tor_struct = nil -- the decoded structure from the bencoded buffer
|
||||
|
||||
self.trackers = {} -- list of trackers {"tr1", "tr2", "tr3"...}
|
||||
self.trackers = {} -- list of trackers {"tr1", "tr2", "tr3"...}
|
||||
self.port = 6881 -- port on which our peer "listens" / it doesn't actually listen
|
||||
self.size = nil -- size of the files in the torrent
|
||||
|
||||
|
||||
self.info_buf = nil --buffer for info_hash
|
||||
self.info_hash = nil --info_hash binary string
|
||||
self.info_hash_url = nil --info_hash escaped
|
||||
|
||||
self.peers = {} -- peers = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...}
|
||||
self.nodes = {} -- nodes = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...}
|
||||
self.nodes = {} -- nodes = { [ip1] = {port1, id1}, [ip2] = {port2, id2}, ...}
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Loads trackers and similar information for a torrent from a magnet link.
|
||||
load_from_magnet = function(self, magnet)
|
||||
local info_hash_hex = magnet:match("^magnet:%?xt=urn:btih:(%w+)&")
|
||||
if not info_hash_hex then
|
||||
if not info_hash_hex then
|
||||
return false, "Erroneous magnet link"
|
||||
end
|
||||
self.info_hash = bin.pack("H",info_hash_hex)
|
||||
self.info_hash = bin.pack("H",info_hash_hex)
|
||||
|
||||
local pos = #info_hash_hex + 21
|
||||
local name = magnet:sub(pos,#magnet):match("^&dn=(.-)&")
|
||||
@@ -631,7 +631,7 @@ Torrent =
|
||||
self.size = 50
|
||||
end,
|
||||
|
||||
--- Reads a torrent file, loads self.buffer and parses it using
|
||||
--- Reads a torrent file, loads self.buffer and parses it using
|
||||
-- self:parse_buffer(), then self:calc_info_hash()
|
||||
--
|
||||
-- @param filename, string containing filename of the torrent file
|
||||
@@ -641,13 +641,13 @@ Torrent =
|
||||
if not filename then return false, "No filename specified." end
|
||||
|
||||
local file = io.open(filename, "r")
|
||||
if not file then return false, "Cannot open file: "..filename end
|
||||
|
||||
if not file then return false, "Cannot open file: "..filename end
|
||||
|
||||
self.buffer = file:read("*a")
|
||||
|
||||
local status, err = self:parse_buffer()
|
||||
if not status then
|
||||
return false, "Could not parse file: ".. err
|
||||
return false, "Could not parse file: ".. err
|
||||
end
|
||||
|
||||
status, err = self:calc_info_hash()
|
||||
@@ -659,7 +659,7 @@ Torrent =
|
||||
if not status then
|
||||
return false, "Could not load trackers: " .. err
|
||||
end
|
||||
|
||||
|
||||
status, err = self:calc_torrent_size()
|
||||
if not status then
|
||||
if not err then err = "" end
|
||||
@@ -669,20 +669,20 @@ Torrent =
|
||||
file:close()
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Gets peers available from the loaded trackers
|
||||
trackers_peers = function(self)
|
||||
for _, tracker in ipairs(self.trackers) do
|
||||
local status, err
|
||||
|
||||
|
||||
if tracker:match("^http://") then -- http tracker
|
||||
status, err = self:http_tracker_peers(tracker)
|
||||
if not status then
|
||||
if not status then
|
||||
stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err)
|
||||
end
|
||||
elseif tracker:match("^udp://") then -- udp tracker
|
||||
status, err = self:udp_tracker_peers(tracker)
|
||||
if not status then
|
||||
if not status then
|
||||
stdnse.print_debug("Could not get peers from tracker %s, reason: %s",tracker, err)
|
||||
end
|
||||
else -- unknown tracker
|
||||
@@ -693,20 +693,20 @@ Torrent =
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Runs the three threads which do a DHT discovery of nodes and peers.
|
||||
-- The default timeout for this discovery is 30 seconds but it can be
|
||||
-- set through the timeout argument.
|
||||
-- The default timeout for this discovery is 30 seconds but it can be
|
||||
-- set through the timeout argument.
|
||||
dht_peers = function(self, timeout)
|
||||
stdnse.print_debug("bittorrent: Starting DHT peers discovery")
|
||||
|
||||
|
||||
if next(self.peers) == nil then
|
||||
stdnse.print_debug("bittorrent: No peers detected")
|
||||
return
|
||||
end
|
||||
|
||||
if not timeout or type(timeout)~="number" then timeout = 30 end
|
||||
|
||||
|
||||
-- peer node table aka the condvar!
|
||||
local pnt = {}
|
||||
pnt.peers = {}
|
||||
@@ -733,10 +733,10 @@ Torrent =
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self.peers = pnt.peers
|
||||
self.nodes = pnt.nodes
|
||||
|
||||
|
||||
-- Add some residue nodes and peers
|
||||
for peer_ip, peer_info in pairs(pnt.peers_dht_ping) do
|
||||
if not self.peers[peer_ip] then
|
||||
@@ -756,7 +756,7 @@ Torrent =
|
||||
end,
|
||||
|
||||
--- Parses self.buffer, fills self.tor_struct, self.info_buf
|
||||
-- This function is similar to the bdecode function but it has a few
|
||||
-- This function is similar to the bdecode function but it has a few
|
||||
-- additions for calculating torrent file specific fields
|
||||
parse_buffer = function(self)
|
||||
local buf = self.buffer
|
||||
@@ -767,7 +767,7 @@ Torrent =
|
||||
local t = {}
|
||||
self.tor_struct = t
|
||||
local stack = {}
|
||||
|
||||
|
||||
local pos = 1
|
||||
local cur = {}
|
||||
cur.type = "list"
|
||||
@@ -775,12 +775,12 @@ Torrent =
|
||||
table.insert(stack, cur)
|
||||
cur.ref.type="list"
|
||||
|
||||
-- starting and ending position of the info dict
|
||||
-- starting and ending position of the info dict
|
||||
local info_pos_start, info_pos_end, info_buf_count = nil, nil, 0
|
||||
|
||||
while true do
|
||||
if pos == len or (len-pos)==-1 then break end
|
||||
|
||||
|
||||
if cur.type == "list" then
|
||||
-- next element is a string
|
||||
if tonumber( string.char( buf:byte(pos) ) ) then
|
||||
@@ -791,7 +791,7 @@ Torrent =
|
||||
|
||||
-- next element is a number
|
||||
elseif "i" == string.char(buf:byte(pos)) then
|
||||
local num
|
||||
local num
|
||||
num, pos = bdec_number(buf, pos)
|
||||
if not num then return nil, "Error parsing number", pos end
|
||||
table.insert(cur.ref, num)
|
||||
@@ -801,11 +801,11 @@ Torrent =
|
||||
if info_pos_start and (not info_pos_end) then
|
||||
info_buf_count = info_buf_count +1
|
||||
end
|
||||
|
||||
|
||||
local new_list = {}
|
||||
new_list.type="list"
|
||||
table.insert(cur.ref, new_list)
|
||||
|
||||
|
||||
cur = {}
|
||||
cur.type = "list"
|
||||
cur.ref = new_list
|
||||
@@ -813,7 +813,7 @@ Torrent =
|
||||
pos = pos+1
|
||||
|
||||
--next element is a dict
|
||||
elseif "d" == string.char(buf:byte(pos)) then
|
||||
elseif "d" == string.char(buf:byte(pos)) then
|
||||
if info_pos_start and (not info_pos_end) then
|
||||
info_buf_count = info_buf_count +1
|
||||
end
|
||||
@@ -821,13 +821,13 @@ Torrent =
|
||||
local new_dict = {}
|
||||
new_dict.type = "dict"
|
||||
table.insert(cur.ref, new_dict)
|
||||
|
||||
|
||||
cur = {}
|
||||
cur.type = "dict"
|
||||
cur.ref = new_dict
|
||||
table.insert(stack, cur)
|
||||
pos = pos+1
|
||||
|
||||
|
||||
--escape from the list
|
||||
elseif "e" == string.char(buf:byte(pos)) then
|
||||
if info_buf_count == 0 then
|
||||
@@ -844,13 +844,13 @@ Torrent =
|
||||
else
|
||||
return nil, "Unknown type found.", pos
|
||||
end
|
||||
|
||||
|
||||
elseif cur.type == "dict" then
|
||||
local item = {} -- {key = <string>, value = <.*>}
|
||||
-- key
|
||||
if tonumber( string.char( buf:byte(pos) ) ) then
|
||||
local str
|
||||
local tmp_pos = pos
|
||||
local tmp_pos = pos
|
||||
str, pos = bdec_string(buf, pos)
|
||||
if not str then return nil, "Error parsing string.", pos end
|
||||
item.key = str
|
||||
@@ -869,11 +869,11 @@ Torrent =
|
||||
cur = stack[#stack]
|
||||
if not cur then return nil, "Problem with list closure:", pos end
|
||||
pos = pos+1
|
||||
|
||||
|
||||
else
|
||||
return nil, "A dict key has to be a string or escape.", pos
|
||||
end
|
||||
|
||||
|
||||
-- value
|
||||
-- next element is a string
|
||||
if tonumber( string.char( buf:byte(pos) ) ) then
|
||||
@@ -885,7 +885,7 @@ Torrent =
|
||||
|
||||
--next element is a number
|
||||
elseif "i" == string.char(buf:byte(pos)) then
|
||||
local num
|
||||
local num
|
||||
num, pos = bdec_number(buf, pos)
|
||||
if not num then return nil, "Error parsing number.", pos end
|
||||
item.value = num
|
||||
@@ -900,7 +900,7 @@ Torrent =
|
||||
item.value = {}
|
||||
item.value.type = "list"
|
||||
table.insert(cur.ref, item)
|
||||
|
||||
|
||||
cur = {}
|
||||
cur.type = "list"
|
||||
cur.ref = item.value
|
||||
@@ -945,16 +945,16 @@ Torrent =
|
||||
return false, "Invalid type of structure. Fix the code."
|
||||
end
|
||||
end -- while(true)
|
||||
|
||||
|
||||
-- next(stack) is never gonna be nil because we're always in the main list
|
||||
-- next(stack, next(stack)) should be nil if we're in the main list
|
||||
if next(stack, next(stack)) then
|
||||
return false, "Probably file incorrect format"
|
||||
end
|
||||
|
||||
|
||||
self.info_buf = buf:sub(info_pos_start, info_pos_end)
|
||||
|
||||
return true
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
--- Loads the list of trackers in self.trackers from self.tor_struct
|
||||
@@ -964,9 +964,9 @@ Torrent =
|
||||
self.trackers = trackers
|
||||
|
||||
-- load the announce tracker
|
||||
if tor and tor[1] and tor[1][1] and tor[1][1].key and
|
||||
if tor and tor[1] and tor[1][1] and tor[1][1].key and
|
||||
tor[1][1].key == "announce" and tor[1][1].value then
|
||||
|
||||
|
||||
if tor[1][1].value.type and tor[1][1].value.type == "list" then
|
||||
for _, trac in ipairs(tor[1][1].value) do
|
||||
table.insert(trackers, trac)
|
||||
@@ -993,18 +993,18 @@ Torrent =
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Calculates the size of the torrent in bytes
|
||||
-- @param tor, decoded bencoded torrent file structure
|
||||
calc_torrent_size = function(self)
|
||||
local tor = self.tor_struct
|
||||
local tor = self.tor_struct
|
||||
local size = nil
|
||||
if tor[1].type ~= "dict" then return nil end
|
||||
for _, m in ipairs(tor[1]) do
|
||||
if m.key == "info" then
|
||||
if m.value.type ~= "dict" then return nil end
|
||||
for _, n in ipairs(m.value) do
|
||||
if n.key == "files" then
|
||||
if n.key == "files" then
|
||||
size = 0
|
||||
for _, f in ipairs(n.value) do
|
||||
for _, k in ipairs(f) do
|
||||
@@ -1025,29 +1025,29 @@ Torrent =
|
||||
self.size=size
|
||||
if size == 0 then return false end
|
||||
end,
|
||||
|
||||
|
||||
--- Calculates the info hash using self.info_buf. The info_hash value
|
||||
-- is used in many communication transactions for identifying the file
|
||||
-- shared among the bittorrent peers
|
||||
calc_info_hash = function(self)
|
||||
local info_hash = openssl.sha1(self.info_buf)
|
||||
local info_hash = openssl.sha1(self.info_buf)
|
||||
self.info_hash_url = url.escape(info_hash)
|
||||
self.info_hash = info_hash
|
||||
self.info_buf = nil
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Generates a peer_id similar to the ones generated by Ktorrent version 4.1.1
|
||||
generate_peer_id = function(self)
|
||||
-- let's fool trackers that we use ktorrent just in case they control
|
||||
-- which client they give peers to
|
||||
-- which client they give peers to
|
||||
local fingerprint = "-KT4110-"
|
||||
local chars = {}
|
||||
local peer_id = fingerprint
|
||||
-- the full length of a peer_id is 20 bytes but we already have 8 from the fingerprint
|
||||
for i = 1,12 do
|
||||
local n = math.random(1,3)
|
||||
|
||||
|
||||
if n == 1 then
|
||||
peer_id = peer_id .. string.char( math.random( string.byte("a") , string.byte("z") ) )
|
||||
elseif n==2 then
|
||||
@@ -1056,7 +1056,7 @@ Torrent =
|
||||
peer_id = peer_id .. string.char( math.random( string.byte("0") , string.byte("9") ) )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return peer_id
|
||||
end,
|
||||
|
||||
@@ -1068,20 +1068,20 @@ Torrent =
|
||||
url, url_ext = tracker:match("^http://(.-)(/.*)")
|
||||
trac_port = "80"
|
||||
end
|
||||
|
||||
|
||||
trac_port = tonumber(trac_port)
|
||||
-- a http torrent tracker request specifying the info_hash of the torrent, our random
|
||||
-- generated peer_id (with some mods), notifying the tracker that we are just starting
|
||||
-- to download the torrent, with 0 downloaded and 0 uploaded bytes, an as many bytes
|
||||
-- left to download as the size of the torrent, requesting 200 peers in a compact format
|
||||
-- because some trackers refuse connection if they are not explicitly requested that way
|
||||
local request = "?info_hash=" .. self.info_hash_url .. "&peer_id=" .. self:generate_peer_id() ..
|
||||
local request = "?info_hash=" .. self.info_hash_url .. "&peer_id=" .. self:generate_peer_id() ..
|
||||
"&port=" .. self.port .. "&uploaded=0&downloaded=0&left=" .. self.size ..
|
||||
"&event=started&numwant=200&compact=1"
|
||||
|
||||
|
||||
local response = http.get(url, trac_port, url_ext .. request, nil)
|
||||
|
||||
if not response then
|
||||
|
||||
if not response then
|
||||
return false, "No response from tracker: " .. tracker
|
||||
end
|
||||
|
||||
@@ -1090,12 +1090,12 @@ Torrent =
|
||||
if not status then
|
||||
return false, "Could not parse response:"..t
|
||||
end
|
||||
|
||||
|
||||
if not t[1] then
|
||||
return nil, "No response from server."
|
||||
end
|
||||
|
||||
for _, k in ipairs(t[1]) do
|
||||
|
||||
for _, k in ipairs(t[1]) do
|
||||
if k.key == "peers" and type(k.value) == "string" then
|
||||
-- binary peers
|
||||
for bin_ip, bin_port in string.gmatch(k.value, "(....)(..)") do
|
||||
@@ -1105,7 +1105,7 @@ Torrent =
|
||||
local peer = {}
|
||||
peer.ip = ip
|
||||
peer.port = port
|
||||
|
||||
|
||||
if not self.peers[peer.ip] then
|
||||
self.peers[peer.ip] = {}
|
||||
self.peers[peer.ip].port = peer.port
|
||||
@@ -1144,18 +1144,18 @@ Torrent =
|
||||
|
||||
--- Gets the peers from udp trackers when supplied the URL of the tracker
|
||||
-- First we establish a connection to the udp server and then we can request peers
|
||||
-- for a good specification refer to:
|
||||
-- for a good specification refer to:
|
||||
-- http://www.rasterbar.com/products/libtorrent/udp_tracker_protocol.html
|
||||
udp_tracker_peers = function(self, tracker)
|
||||
local host, port = tracker:match("^udp://(.-):(.+)")
|
||||
if (not host) or (not port) then
|
||||
return false, "Could not parse tracker url"
|
||||
end
|
||||
|
||||
|
||||
local socket = nmap.new_socket("udp")
|
||||
|
||||
|
||||
-- The initial connection parameters' variables have hello_ prefixed names
|
||||
local hello_transaction_id = openssl.rand_bytes(4)
|
||||
local hello_transaction_id = openssl.rand_bytes(4)
|
||||
local hello_action = "00 00 00 00" -- 0 for a connection request
|
||||
local hello_connection_id = "00 00 04 17 27 10 19 80" -- identification of the protocol
|
||||
local hello_packet = bin.pack("HHA", hello_connection_id, hello_action, hello_transaction_id)
|
||||
@@ -1166,36 +1166,36 @@ Torrent =
|
||||
if not status then return false, "Could not connect to tracker:"..tracker.." reason:"..msg end
|
||||
|
||||
local _, r_action, r_transaction_id, r_connection_id =bin.unpack("H4A4A8",msg)
|
||||
|
||||
|
||||
if not (r_transaction_id == hello_transaction_id) then
|
||||
return false, "Received transaction ID not equivalent to sent transaction ID"
|
||||
end
|
||||
|
||||
|
||||
-- the action in the response has to be 0 too
|
||||
if not r_action == "00000000" then
|
||||
if not r_action == "00000000" then
|
||||
return false, "Wrong action field, usualy caused by an erroneous request"
|
||||
end
|
||||
|
||||
-- established a connection, and now for an announce message, to which a
|
||||
-- established a connection, and now for an announce message, to which a
|
||||
-- response holds the peers
|
||||
|
||||
-- the announce connection parameters' variables are prefixed with a_
|
||||
local a_action = "00 00 00 01" -- 1 for announce
|
||||
local a_transaction_id = openssl.rand_bytes(4)
|
||||
local a_transaction_id = openssl.rand_bytes(4)
|
||||
local a_info_hash = self.info_hash -- info_hash of the torrent
|
||||
local a_peer_id = self:generate_peer_id()
|
||||
local a_peer_id = self:generate_peer_id()
|
||||
local a_downloaded = "00 00 00 00 00 00 00 00" -- 0 bytes downloaded
|
||||
|
||||
|
||||
local a_left = stdnse.tohex(self.size) -- bytes left to download is the size of torrent
|
||||
a_left = string.rep("0", 16-#a_left) .. a_left
|
||||
|
||||
local a_uploaded = "00 00 00 00 00 00 00 00" -- 0 bytes uploaded
|
||||
local a_event = "00 00 00 02" -- value of 2 for started torrent
|
||||
local a_ip = "00 00 00 00" -- not necessary to specify our ip since it's resolved
|
||||
local a_ip = "00 00 00 00" -- not necessary to specify our ip since it's resolved
|
||||
-- by tracker automatically
|
||||
local a_key = openssl.rand_bytes(4)
|
||||
local a_num_want = "FF FF FF FF" -- request for many many peers
|
||||
local a_port = "1A E1" -- 6881 the port "we are listening on"
|
||||
local a_port = "1A E1" -- 6881 the port "we are listening on"
|
||||
local a_extensions = "00 00" -- client recognizes no extensions of the bittorrent proto
|
||||
local announce_packet = bin.pack("AHAAAHHHHHAHHH", r_connection_id, a_action, a_transaction_id,
|
||||
a_info_hash, a_peer_id, a_downloaded, a_left, a_uploaded, a_event, a_ip, a_key,
|
||||
@@ -1211,7 +1211,7 @@ Torrent =
|
||||
return false, "Didn't receive response to announce message, reason: "..msg
|
||||
end
|
||||
local pos, p_action, p_transaction_id, p_interval, p_leechers, p_seeders = bin.unpack("H4A4H4H4H4",msg)
|
||||
|
||||
|
||||
-- the action field in the response has to be 1 (like the sent response)
|
||||
if not (p_action == "00000001") then
|
||||
return false, "Action in response to announce erroneous"
|
||||
@@ -1219,9 +1219,9 @@ Torrent =
|
||||
if not (p_transaction_id == a_transaction_id) then
|
||||
return false, "Transaction ID in response to announce message not equal to original"
|
||||
end
|
||||
|
||||
|
||||
-- parse peers from msg:sub(pos, #msg)
|
||||
|
||||
|
||||
for bin_ip, bin_port in msg:sub(pos,#msg):gmatch("(....)(..)") do
|
||||
local ip = string.format("%d.%d.%d.%d",
|
||||
bin_ip:byte(1), bin_ip:byte(2), bin_ip:byte(3), bin_ip:byte(4))
|
||||
|
||||
@@ -22,7 +22,7 @@ BJNP = {
|
||||
|
||||
-- The common BJNP header
|
||||
Header = {
|
||||
|
||||
|
||||
new = function(self, o)
|
||||
o = o or {}
|
||||
o = {
|
||||
@@ -42,15 +42,15 @@ BJNP = {
|
||||
parse = function(data)
|
||||
local hdr = BJNP.Header:new({ code = -1 })
|
||||
local pos
|
||||
|
||||
|
||||
pos, hdr.id, hdr.type, hdr.code,
|
||||
hdr.seq, hdr.session, hdr.length = bin.unpack(">A4CCISI", data)
|
||||
return hdr
|
||||
end,
|
||||
|
||||
__tostring = function(self)
|
||||
return bin.pack(">ACCISI",
|
||||
self.id,
|
||||
return bin.pack(">ACCISI",
|
||||
self.id,
|
||||
self.type,
|
||||
self.code,
|
||||
self.seq,
|
||||
@@ -59,19 +59,19 @@ BJNP = {
|
||||
)
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
-- Scanner related code
|
||||
Scanner = {
|
||||
|
||||
|
||||
Code = {
|
||||
DISCOVER = 1,
|
||||
IDENTITY = 48,
|
||||
},
|
||||
|
||||
|
||||
Request = {
|
||||
|
||||
Discover = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = { header = BJNP.Header:new( { type = 2, code = BJNP.Scanner.Code.DISCOVER }) }
|
||||
setmetatable(o, self)
|
||||
@@ -83,10 +83,10 @@ BJNP = {
|
||||
return tostring(self.header)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
Identity = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = { header = BJNP.Header:new( { type = 2, code = BJNP.Scanner.Code.IDENTITY, length = 4 }), data = 0 }
|
||||
setmetatable(o, self)
|
||||
@@ -98,18 +98,18 @@ BJNP = {
|
||||
return tostring(self.header) .. bin.pack(">I", self.data)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
Identity = {
|
||||
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
@@ -123,10 +123,10 @@ BJNP = {
|
||||
return identity
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
@@ -138,7 +138,7 @@ BJNP = {
|
||||
DISCOVER = 1,
|
||||
IDENTITY = 48,
|
||||
},
|
||||
|
||||
|
||||
Request = {
|
||||
|
||||
Discover = {
|
||||
@@ -153,9 +153,9 @@ BJNP = {
|
||||
return tostring(self.header)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
Identity = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = { header = BJNP.Header:new( { code = BJNP.Printer.Code.IDENTITY }) }
|
||||
setmetatable(o, self)
|
||||
@@ -167,11 +167,11 @@ BJNP = {
|
||||
return tostring(self.header)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
Identity = {
|
||||
|
||||
new = function(self)
|
||||
@@ -184,7 +184,7 @@ BJNP = {
|
||||
parse = function(data)
|
||||
local identity = BJNP.Printer.Response.Identity:new()
|
||||
identity.header = BJNP.Header.parse(data)
|
||||
|
||||
|
||||
local pos = #tostring(identity.header) + 1
|
||||
local pos, len = bin.unpack(">S", data, pos)
|
||||
if ( len ) then
|
||||
@@ -192,19 +192,19 @@ BJNP = {
|
||||
return identity
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-- Helper class, the main script writer interface
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new Helper instance
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
@@ -222,7 +222,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects the socket to the device
|
||||
-- This should always be called, regardless if the broadcast option is set
|
||||
-- or not.
|
||||
@@ -237,7 +237,7 @@ Helper = {
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Discover network devices using either broadcast or unicast
|
||||
-- @param packet discovery packet (printer or scanner)
|
||||
-- @return status, true on success, false on failure
|
||||
@@ -267,17 +267,17 @@ Helper = {
|
||||
for host in pairs(tmp) do table.insert(devices, host) end
|
||||
return true, ( self.options.bcast and devices or ( #devices > 0 and devices[1] ))
|
||||
end,
|
||||
|
||||
|
||||
-- Discover BJNP supporting scanners
|
||||
discoverScanner = function(self)
|
||||
return self:discoverDevice(BJNP.Scanner.Request.Discover:new())
|
||||
end,
|
||||
|
||||
|
||||
-- Discover BJNP supporting printers
|
||||
discoverPrinter = function(self)
|
||||
return self:discoverDevice(BJNP.Printer.Request.Discover:new())
|
||||
end,
|
||||
|
||||
|
||||
-- Gets a printer identity (additional information)
|
||||
-- @param devtype string containing either the string printer or scanner
|
||||
-- @return status, true on success, false on failure
|
||||
@@ -285,13 +285,13 @@ Helper = {
|
||||
-- errmsg string containing the error message when status is false
|
||||
getDeviceIdentity = function(self, devtype)
|
||||
-- Were currenlty only decoding this as I don't know what the other cruft is
|
||||
local attrib_names = {
|
||||
local attrib_names = {
|
||||
["scanner"] = {
|
||||
{ ['MFG'] = "Manufacturer" },
|
||||
{ ['MDL'] = "Model" },
|
||||
{ ['DES'] = "Description" },
|
||||
{ ['CMD'] = "Command" },
|
||||
},
|
||||
},
|
||||
["printer"] = {
|
||||
{ ['MFG'] = "Manufacturer" },
|
||||
{ ['MDL'] = "Model" },
|
||||
@@ -314,7 +314,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
|
||||
|
||||
local identity
|
||||
if ( "printer" == devtype ) then
|
||||
identity = BJNP.Printer.Response.Identity.parse(data)
|
||||
@@ -337,27 +337,27 @@ Helper = {
|
||||
table.insert(attrs, ("%s: %s"):format(long, kvps[short]))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return true, attrs
|
||||
end,
|
||||
|
||||
|
||||
-- Retrieves information related to the printer
|
||||
getPrinterIdentity = function(self)
|
||||
return self:getDeviceIdentity("printer")
|
||||
end,
|
||||
|
||||
|
||||
-- Retrieves information related to the scanner
|
||||
getScannerIdentity = function(self)
|
||||
return self:getDeviceIdentity("scanner")
|
||||
end,
|
||||
|
||||
|
||||
-- Closes the connection
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return errmsg string containing the error message when status is false
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
204
nselib/brute.lua
204
nselib/brute.lua
@@ -1,6 +1,6 @@
|
||||
---
|
||||
-- The brute library is an attempt to create a common framework for performing
|
||||
-- password guessing against remote services.
|
||||
-- password guessing against remote services.
|
||||
--
|
||||
-- The library currently attempts to parallellize the guessing by starting
|
||||
-- a number of working threads. The number of threads can be defined using
|
||||
@@ -17,9 +17,9 @@
|
||||
-- * <code>Options</code>
|
||||
-- ** Stores any options that should be used during brute-forcing.
|
||||
--
|
||||
-- In order to make use of the framework a script needs to implement a Driver
|
||||
-- class. The Driver class is then to be passed as a parameter to the Engine
|
||||
-- constructor, which creates a new instance for each guess. The Driver class
|
||||
-- In order to make use of the framework a script needs to implement a Driver
|
||||
-- class. The Driver class is then to be passed as a parameter to the Engine
|
||||
-- constructor, which creates a new instance for each guess. The Driver class
|
||||
-- SHOULD implement the following four methods:
|
||||
--
|
||||
-- <code>
|
||||
@@ -38,14 +38,14 @@
|
||||
-- password guessing by calling the Error objects <code>setAbort</code> method.
|
||||
--
|
||||
-- The following example code demonstrates how the Error object can be used.
|
||||
--
|
||||
--
|
||||
-- <code>
|
||||
-- -- After a number of incorrect attempts VNC blocks us, so we abort
|
||||
-- if ( not(status) and x:match("Too many authentication failures") ) then
|
||||
-- local err = brute.Error:new( data )
|
||||
-- -- signal the engine to abort
|
||||
-- err:setAbort( true )
|
||||
-- return false, err
|
||||
-- return false, err
|
||||
-- elseif ( not(status) ) then
|
||||
-- local err = brute.Error:new( "VNC handshake failed" )
|
||||
-- -- This might be temporary, signal the engine to retry
|
||||
@@ -56,7 +56,7 @@
|
||||
-- .
|
||||
-- .
|
||||
-- -- Return a simple error, no retry needed
|
||||
-- return false, brute.Error:new( "Incorrect password" )
|
||||
-- return false, brute.Error:new( "Incorrect password" )
|
||||
-- </code>
|
||||
--
|
||||
-- The purpose of the <code>check</code> method is to be able to determine
|
||||
@@ -64,11 +64,11 @@
|
||||
-- brute force. It's the method where you should check, e.g., if the correct
|
||||
-- database or repository URL was specified or not. On success, the
|
||||
-- <code>check</code> method returns true, on failure it returns false and the
|
||||
-- brute force engine aborts.
|
||||
-- brute force engine aborts.
|
||||
--
|
||||
-- NOTE: The <code>check</code> method is deprecated and will be removed from
|
||||
-- all scripts in the future. Scripts should do this check in the action
|
||||
-- function instead.
|
||||
-- function instead.
|
||||
--
|
||||
-- The <code>connect</code> method provides the framework with the ability to
|
||||
-- ensure that the thread can run once it has been dispatched a set of
|
||||
@@ -96,8 +96,8 @@
|
||||
-- end,
|
||||
-- disconnect = function( self )
|
||||
-- return self.socket:close()
|
||||
-- end,
|
||||
-- check = function( self )
|
||||
-- end,
|
||||
-- check = function( self )
|
||||
-- return true
|
||||
-- end,
|
||||
-- login = function( self, username, password )
|
||||
@@ -127,7 +127,7 @@
|
||||
-- end
|
||||
-- </code>
|
||||
--
|
||||
-- For a complete example of a brute implementation consult the
|
||||
-- For a complete example of a brute implementation consult the
|
||||
-- <code>svn-brute.nse</code> or <code>vnc-brute.nse</code> scripts
|
||||
--
|
||||
-- @args brute.useraspass guess the username as password for each user
|
||||
@@ -173,7 +173,7 @@
|
||||
-- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver
|
||||
-- <patrik@cqure.net>
|
||||
-- Revised 07/21/2010 - v0.3 - documented missing argument brute.mode
|
||||
-- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to
|
||||
-- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to
|
||||
-- include statistics, and to display "no accounts
|
||||
-- found" message.
|
||||
-- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per
|
||||
@@ -185,7 +185,7 @@
|
||||
-- iterator to use a file handle instead of table
|
||||
-- Revised 07/21/2011 - v0.72- added code to allow script reporting invalid
|
||||
-- (non existing) accounts using setInvalidAccount
|
||||
-- Revised 11/12/2011 - v0.73- added support for max guesses per account to
|
||||
-- Revised 11/12/2011 - v0.73- added support for max guesses per account to
|
||||
-- prevent account lockouts.
|
||||
-- bugfix: added support for guessing the username
|
||||
-- as password per default, as suggested by the
|
||||
@@ -224,7 +224,7 @@ _ENV = stdnse.module("brute", stdnse.seeall)
|
||||
-- * emptypass - guesses an empty string as password (default: false)
|
||||
--
|
||||
Options = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -240,7 +240,7 @@ Options = {
|
||||
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Checks if a script argument is boolean true or false
|
||||
--
|
||||
-- @param arg string containing the name of the argument to check
|
||||
@@ -250,7 +250,7 @@ Options = {
|
||||
local val = stdnse.get_script_args(arg) or default
|
||||
return (val == "true" or val==true or tonumber(val)==1)
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the brute mode to either iterate over users or passwords
|
||||
-- @see description for more information.
|
||||
--
|
||||
@@ -260,11 +260,11 @@ Options = {
|
||||
setMode = function( self, mode )
|
||||
local modes = { "password", "user", "creds" }
|
||||
local supported = false
|
||||
|
||||
|
||||
for _, m in ipairs(modes) do
|
||||
if ( mode == m ) then supported = true end
|
||||
end
|
||||
|
||||
|
||||
if ( not(supported) ) then
|
||||
stdnse.print_debug("ERROR: brute.options.setMode: mode %s not supported", mode)
|
||||
return false, "Unsupported mode"
|
||||
@@ -279,7 +279,7 @@ Options = {
|
||||
-- @param param string containing the parameter name
|
||||
-- @param value string containing the parameter value
|
||||
setOption = function( self, param, value ) self[param] = value end,
|
||||
|
||||
|
||||
--- Set an alternate title for the result output (default: Accounts)
|
||||
--
|
||||
-- @param title string containing the title value
|
||||
@@ -303,7 +303,7 @@ Account =
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts an account object to a printable script
|
||||
--
|
||||
-- @return string representation of object
|
||||
@@ -320,7 +320,7 @@ Account =
|
||||
return ("%s"):format(c)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Error class, is currently only used to flag for retries
|
||||
@@ -328,50 +328,50 @@ Account =
|
||||
Error =
|
||||
{
|
||||
retry = false,
|
||||
|
||||
|
||||
new = function(self, msg)
|
||||
local o = { msg = msg, done = false }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Is the error recoverable?
|
||||
--
|
||||
-- @return status true if the error is recoverable, false if not
|
||||
isRetry = function( self ) return self.retry end,
|
||||
|
||||
|
||||
--- Set the error as recoverable
|
||||
--
|
||||
-- @param r boolean true if the engine should attempt to retry the
|
||||
-- credentials, unset or false if not
|
||||
setRetry = function( self, r ) self.retry = r end,
|
||||
|
||||
|
||||
--- Set the error as abort all threads
|
||||
--
|
||||
-- @param b boolean true if the engine should abort guessing on all threads
|
||||
setAbort = function( self, b ) self.abort = b end,
|
||||
|
||||
|
||||
--- Was the error abortable
|
||||
--
|
||||
-- @return status true if the driver flagged the engine to abort
|
||||
isAbort = function( self ) return self.abort end,
|
||||
|
||||
|
||||
--- Get the error message reported
|
||||
--
|
||||
-- @return msg string containing the error message
|
||||
getMessage = function( self ) return self.msg end,
|
||||
|
||||
|
||||
--- Is the thread done?
|
||||
--
|
||||
-- @return status true if done, false if not
|
||||
isDone = function( self ) return self.done end,
|
||||
|
||||
|
||||
--- Signals the engine that the thread is done and should be terminated
|
||||
--
|
||||
-- @param b boolean true if done, unset or false if not
|
||||
setDone = function( self, b ) self.done = b end,
|
||||
|
||||
|
||||
-- Marks the username as invalid, aborting further guessing.
|
||||
-- @param username
|
||||
setInvalidAccount = function(self, username)
|
||||
@@ -381,25 +381,25 @@ Error =
|
||||
-- Checks if the error reported the account as invalid.
|
||||
-- @return username string containing the invalid account
|
||||
isInvalidAccount = function(self)
|
||||
return self.invalid_account
|
||||
return self.invalid_account
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The brute engine, doing all the nasty work
|
||||
Engine =
|
||||
{
|
||||
STAT_INTERVAL = 20,
|
||||
|
||||
|
||||
--- Creates a new Engine instance
|
||||
--
|
||||
-- @param driver, the driver class that should be instantiated
|
||||
-- @param host table as passed to the action method of the script
|
||||
-- @param port table as passed to the action method of the script
|
||||
-- @param options table containing any script specific options
|
||||
-- @return o new Engine instance
|
||||
-- @return o new Engine instance
|
||||
new = function(self, driver, host, port, options)
|
||||
local o = {
|
||||
local o = {
|
||||
driver = driver,
|
||||
host = host,
|
||||
port = port,
|
||||
@@ -422,25 +422,25 @@ Engine =
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Sets the username iterator
|
||||
--
|
||||
--- Sets the username iterator
|
||||
--
|
||||
-- @param usernameIterator function to set as a username iterator
|
||||
setUsernameIterator = function(self,usernameIterator)
|
||||
self.usernames = usernameIterator
|
||||
end,
|
||||
|
||||
--- Sets the password iterator
|
||||
--
|
||||
|
||||
--- Sets the password iterator
|
||||
--
|
||||
-- @param passwordIterator function to set as a password iterator
|
||||
setPasswordIterator = function(self,passwordIterator)
|
||||
self.passwords = passwordIterator
|
||||
end,
|
||||
|
||||
|
||||
--- Limit the number of worker threads
|
||||
--
|
||||
-- @param max number containing the maximum number of allowed threads
|
||||
setMaxThreads = function( self, max ) self.max_threads = max end,
|
||||
|
||||
|
||||
--- Returns the number of non-dead threads
|
||||
--
|
||||
-- @return count number of non-dead threads
|
||||
@@ -456,7 +456,7 @@ Engine =
|
||||
end
|
||||
return count
|
||||
end,
|
||||
|
||||
|
||||
--- Calculates the number of threads that are actually doing any work
|
||||
--
|
||||
-- @return count number of threads performing activity
|
||||
@@ -465,9 +465,9 @@ Engine =
|
||||
for thread, v in pairs(self.threads) do
|
||||
if ( v.guesses ~= nil ) then count = count + 1 end
|
||||
end
|
||||
return count
|
||||
return count
|
||||
end,
|
||||
|
||||
|
||||
--- Iterator wrapper used to iterate over all registered iterators
|
||||
--
|
||||
-- @return iterator function
|
||||
@@ -486,7 +486,7 @@ Engine =
|
||||
end
|
||||
return coroutine.wrap( next_credential )
|
||||
end,
|
||||
|
||||
|
||||
--- Does the actual authentication request
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
@@ -497,7 +497,7 @@ Engine =
|
||||
local next_credential = self:get_next_credential()
|
||||
local retries = self.options.max_retries
|
||||
local username, password
|
||||
|
||||
|
||||
repeat
|
||||
local driver = self.driver:new( self.host, self.port, self.driver_options )
|
||||
status = driver:connect()
|
||||
@@ -510,22 +510,22 @@ Engine =
|
||||
if ( not(username) and not(password) ) then
|
||||
driver:disconnect()
|
||||
self.threads[coroutine.running()].terminate = true
|
||||
return false
|
||||
return false
|
||||
end
|
||||
until ( ( not(self.found_accounts) or not(self.found_accounts[username]) ) and
|
||||
( self.options.max_guesses == 0 or not(self.account_guesses[username]) or
|
||||
( self.options.max_guesses == 0 or not(self.account_guesses[username]) or
|
||||
self.options.max_guesses > self.account_guesses[username] ) )
|
||||
|
||||
|
||||
-- increases the number of guesses for an account
|
||||
self.account_guesses[username] = self.account_guesses[username] and self.account_guesses[username] + 1 or 1
|
||||
end
|
||||
|
||||
-- make sure that all threads locked in connect stat terminate quickly
|
||||
if ( Engine.terminate_all ) then
|
||||
if ( Engine.terminate_all ) then
|
||||
driver:disconnect()
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local c
|
||||
-- Do we have a username or not?
|
||||
if ( username and #username > 0 ) then
|
||||
@@ -533,13 +533,13 @@ Engine =
|
||||
else
|
||||
c = ("%s"):format(#password > 0 and password or "<empty>")
|
||||
end
|
||||
|
||||
|
||||
local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or "Trying"
|
||||
stdnse.print_debug(2, "%s %s against %s:%d", msg, c, self.host.ip, self.port.number )
|
||||
status, response = driver:login( username, password )
|
||||
|
||||
driver:disconnect()
|
||||
driver = nil
|
||||
driver = nil
|
||||
end
|
||||
|
||||
retries = retries - 1
|
||||
@@ -549,10 +549,10 @@ Engine =
|
||||
-- * The response was not set to retry
|
||||
-- * We've reached the maximum retry attempts
|
||||
until( status or ( response and not( response:isRetry() ) ) or retries == 0)
|
||||
|
||||
|
||||
-- Increase the amount of total guesses
|
||||
self.counter = self.counter + 1
|
||||
|
||||
|
||||
-- did we exhaust all retries, terminate and report?
|
||||
if ( retries == 0 ) then
|
||||
Engine.terminate_all = true
|
||||
@@ -562,18 +562,18 @@ Engine =
|
||||
end
|
||||
return status, response
|
||||
end,
|
||||
|
||||
|
||||
login = function(self, cvar )
|
||||
local condvar = nmap.condvar( cvar )
|
||||
local condvar = nmap.condvar( cvar )
|
||||
local thread_data = self.threads[coroutine.running()]
|
||||
local interval_start = os.time()
|
||||
|
||||
|
||||
while( true ) do
|
||||
-- Should we terminate all threads?
|
||||
if ( self.terminate_all or thread_data.terminate ) then break end
|
||||
|
||||
|
||||
local status, response = self:doAuthenticate()
|
||||
|
||||
|
||||
if ( status ) then
|
||||
-- Prevent locked accounts from appearing several times
|
||||
if ( not(self.found_accounts) or self.found_accounts[response.username] == nil ) then
|
||||
@@ -583,7 +583,7 @@ Engine =
|
||||
self.credstore = self.credstore or {}
|
||||
table.insert(self.credstore, response:toString() )
|
||||
end
|
||||
|
||||
|
||||
stdnse.print_debug("Discovered account: %s", response:toString())
|
||||
|
||||
-- if we're running in passonly mode, and want to continue guessing
|
||||
@@ -592,7 +592,7 @@ Engine =
|
||||
if ( not(self.options.passonly) ) then
|
||||
self.found_accounts[response.username] = true
|
||||
end
|
||||
|
||||
|
||||
-- Check if firstonly option was set, if so abort all threads
|
||||
if ( self.options.firstonly ) then self.terminate_all = true end
|
||||
end
|
||||
@@ -607,9 +607,9 @@ Engine =
|
||||
self.found_accounts[response:isInvalidAccount()] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local timediff = (os.time() - interval_start)
|
||||
|
||||
|
||||
-- This thread made another guess
|
||||
thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 )
|
||||
|
||||
@@ -626,20 +626,20 @@ Engine =
|
||||
end
|
||||
condvar "signal"
|
||||
end,
|
||||
|
||||
|
||||
--- Starts the brute-force
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing error message on failure
|
||||
start = function(self)
|
||||
|
||||
|
||||
local cvar = {}
|
||||
local condvar = nmap.condvar( cvar )
|
||||
|
||||
|
||||
assert(self.options.script_name, "SCRIPT_NAME was not set in options.script_name")
|
||||
assert(self.port.number and self.port.protocol, "Invalid port table detected")
|
||||
self.port.service = self.port.service or "unknown"
|
||||
|
||||
|
||||
-- Only run the check method if it exist. We should phase this out
|
||||
-- in favor of a check in the action function of the script
|
||||
if ( self.driver:new( self.host, self.port, self.driver_options ).check ) then
|
||||
@@ -647,7 +647,7 @@ Engine =
|
||||
local status, response = self.driver:new( self.host, self.port, self.driver_options ):check()
|
||||
if( not(status) ) then return false, response end
|
||||
end
|
||||
|
||||
|
||||
local usernames = self.usernames
|
||||
local passwords = self.passwords
|
||||
|
||||
@@ -659,7 +659,7 @@ Engine =
|
||||
end
|
||||
|
||||
local mode = self.options.mode or stdnse.get_script_args("brute.mode")
|
||||
|
||||
|
||||
-- if no mode was given, but a credfile is present, assume creds mode
|
||||
if ( not(mode) and stdnse.get_script_args("brute.credfile") ) then
|
||||
if ( stdnse.get_script_args("userdb") or
|
||||
@@ -668,7 +668,7 @@ Engine =
|
||||
end
|
||||
mode = 'creds'
|
||||
end
|
||||
|
||||
|
||||
-- Are we guessing against a service that has no username (eg. VNC)
|
||||
if ( self.options.passonly ) then
|
||||
local function single_user_iter(next)
|
||||
@@ -677,24 +677,24 @@ Engine =
|
||||
end
|
||||
-- only add this iterator if no other iterator was specified
|
||||
if self.iterator == nil then
|
||||
self.iterator = Iterators.user_pw_iterator( single_user_iter(), passwords )
|
||||
self.iterator = Iterators.user_pw_iterator( single_user_iter(), passwords )
|
||||
end
|
||||
elseif ( mode == 'creds' ) then
|
||||
local credfile = stdnse.get_script_args("brute.credfile")
|
||||
if ( not(credfile) ) then
|
||||
return false, "No credential file specified (see brute.credfile)"
|
||||
end
|
||||
|
||||
|
||||
local f = io.open( credfile, "r" )
|
||||
if ( not(f) ) then
|
||||
return false, ("Failed to open credfile (%s)"):format(credfile)
|
||||
end
|
||||
|
||||
self.iterator = Iterators.credential_iterator( f )
|
||||
|
||||
self.iterator = Iterators.credential_iterator( f )
|
||||
elseif ( mode and mode == 'user' ) then
|
||||
self.iterator = self.iterator or Iterators.user_pw_iterator( usernames, passwords )
|
||||
self.iterator = self.iterator or Iterators.user_pw_iterator( usernames, passwords )
|
||||
elseif( mode and mode == 'pass' ) then
|
||||
self.iterator = self.iterator or Iterators.pw_user_iterator( usernames, passwords )
|
||||
self.iterator = self.iterator or Iterators.pw_user_iterator( usernames, passwords )
|
||||
elseif ( mode ) then
|
||||
return false, ("Unsupported mode: %s"):format(mode)
|
||||
-- Default to the pw_user_iterator in case no iterator was specified
|
||||
@@ -708,7 +708,7 @@ Engine =
|
||||
self.iterator = unpwdb.concat_iterators(Iterators.pw_same_as_user_iterator(usernames, "lower"),self.iterator)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.emptypass ) then
|
||||
local function empty_pass_iter()
|
||||
local function next_pass()
|
||||
@@ -718,7 +718,7 @@ Engine =
|
||||
end
|
||||
self.iterator = Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass")
|
||||
end
|
||||
|
||||
|
||||
|
||||
self.starttime = os.time()
|
||||
|
||||
@@ -731,15 +731,15 @@ Engine =
|
||||
|
||||
-- wait for all threads to finnish running
|
||||
while self:threadCount()>0 do condvar "wait" end
|
||||
|
||||
|
||||
local valid_accounts
|
||||
|
||||
|
||||
if ( not(self.options.nostore) ) then
|
||||
valid_accounts = creds.Credentials:new(self.options.script_name, self.host, self.port):getTable()
|
||||
else
|
||||
valid_accounts = self.credstore
|
||||
end
|
||||
|
||||
|
||||
local result = {}
|
||||
-- Did we find any accounts, if so, do formatting
|
||||
if ( valid_accounts and #valid_accounts > 0 ) then
|
||||
@@ -748,7 +748,7 @@ Engine =
|
||||
else
|
||||
table.insert( result, {"No valid accounts found", name="Accounts"} )
|
||||
end
|
||||
|
||||
|
||||
-- calculate the average tps
|
||||
local sum = 0
|
||||
for _, v in ipairs( self.tps ) do sum = sum + v end
|
||||
@@ -761,12 +761,12 @@ Engine =
|
||||
table.insert(stats, ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) )
|
||||
stats.name = "Statistics"
|
||||
table.insert( result, stats )
|
||||
|
||||
|
||||
if ( self.options.max_guesses > 0 ) then
|
||||
-- we only display a warning if the guesses are equal to max_guesses
|
||||
for user, guesses in pairs(self.account_guesses) do
|
||||
if ( guesses == self.options.max_guesses ) then
|
||||
table.insert( result, { name = "Information",
|
||||
table.insert( result, { name = "Information",
|
||||
("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses) } )
|
||||
break
|
||||
end
|
||||
@@ -774,7 +774,7 @@ Engine =
|
||||
end
|
||||
|
||||
result = ( #result ) and stdnse.format_output( true, result ) or ""
|
||||
|
||||
|
||||
-- Did any error occure? If so add this to the result.
|
||||
if ( self.error ) then
|
||||
result = result .. (" \n ERROR: %s"):format( self.error )
|
||||
@@ -782,10 +782,10 @@ Engine =
|
||||
end
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- Default username iterator that uses unpwdb
|
||||
--- Default username iterator that uses unpwdb
|
||||
--
|
||||
usernames_iterator = function()
|
||||
local status, usernames = unpwdb.usernames()
|
||||
@@ -793,8 +793,8 @@ usernames_iterator = function()
|
||||
return usernames
|
||||
end
|
||||
|
||||
--- Default password iterator that uses unpwdb
|
||||
--
|
||||
--- Default password iterator that uses unpwdb
|
||||
--
|
||||
passwords_iterator = function()
|
||||
local status, passwords = unpwdb.passwords()
|
||||
if ( not(status) ) then return "Failed to load passwords" end
|
||||
@@ -810,7 +810,7 @@ Iterators = {
|
||||
-- @param mode string, should be either 'user' or 'pass' and controls
|
||||
-- whether the users or passwords are in the 'outer' loop
|
||||
-- @return function iterator
|
||||
account_iterator = function(users, pass, mode)
|
||||
account_iterator = function(users, pass, mode)
|
||||
local function next_credential ()
|
||||
local outer, inner
|
||||
if "table" == type(users) then
|
||||
@@ -819,7 +819,7 @@ Iterators = {
|
||||
if "table" == type(pass) then
|
||||
pass = unpwdb.table_iterator(pass)
|
||||
end
|
||||
|
||||
|
||||
if ( mode == 'pass' ) then
|
||||
outer, inner = pass, users
|
||||
elseif ( mode == 'user' ) then
|
||||
@@ -840,7 +840,7 @@ Iterators = {
|
||||
end
|
||||
while true do coroutine.yield(nil, nil) end
|
||||
end
|
||||
return coroutine.wrap( next_credential )
|
||||
return coroutine.wrap( next_credential )
|
||||
end,
|
||||
|
||||
|
||||
@@ -868,7 +868,7 @@ Iterators = {
|
||||
-- @param case string [optional] 'upper' or 'lower', specifies if user
|
||||
-- and password pairs should be case converted.
|
||||
-- @return function iterator
|
||||
pw_same_as_user_iterator = function( users, case )
|
||||
pw_same_as_user_iterator = function( users, case )
|
||||
local function next_credential ()
|
||||
for user in users do
|
||||
if ( case == 'upper' ) then
|
||||
@@ -901,7 +901,7 @@ Iterators = {
|
||||
end
|
||||
return coroutine.wrap( next_credential )
|
||||
end,
|
||||
|
||||
|
||||
--- Credential iterator (for default or known user/pass combinations)
|
||||
--
|
||||
-- @param f file handle to file containing credentials separated by '/'
|
||||
@@ -911,7 +911,7 @@ Iterators = {
|
||||
local c = {}
|
||||
for line in f:lines() do
|
||||
if ( not(line:match("^#!comment:")) ) then
|
||||
local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end
|
||||
local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end
|
||||
line = trim(line)
|
||||
local user, pass = line:match("^([^%/]*)%/(.*)$")
|
||||
coroutine.yield( user, pass )
|
||||
@@ -922,19 +922,19 @@ Iterators = {
|
||||
end
|
||||
return coroutine.wrap( next_credential )
|
||||
end,
|
||||
|
||||
|
||||
unpwdb_iterator = function( mode )
|
||||
local status, users, passwords
|
||||
|
||||
status, users = unpwdb.usernames()
|
||||
if ( not(status) ) then return end
|
||||
|
||||
|
||||
status, passwords = unpwdb.passwords()
|
||||
if ( not(status) ) then return end
|
||||
|
||||
|
||||
return Iterators.account_iterator( users, passwords, mode )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -16,7 +16,7 @@ _ENV = stdnse.module("cassandra", stdnse.seeall)
|
||||
|
||||
--[[
|
||||
|
||||
Cassandra Thrift protocol implementation.
|
||||
Cassandra Thrift protocol implementation.
|
||||
|
||||
For more information about Cassandra, see:
|
||||
|
||||
@@ -43,7 +43,7 @@ end
|
||||
--@param username to put in format
|
||||
--@param password to put in format
|
||||
--@return str : string in cassandra format for login
|
||||
function loginstr (username, password)
|
||||
function loginstr (username, password)
|
||||
local str = CASSANDRAREQ .. pack4str ("login")
|
||||
str = str .. CASSLOGINMAGIC
|
||||
str = str .. pack4str("username")
|
||||
@@ -60,7 +60,7 @@ end
|
||||
--@param cnt is protocol count
|
||||
--@return status : true if ok; false if bad
|
||||
--@return result : value if status ok, error msg if bad
|
||||
function cmdstr (command,cnt)
|
||||
function cmdstr (command,cnt)
|
||||
local str = CASSANDRAREQ .. pack4str (command)
|
||||
str = str .. bin.pack(">I",cnt)
|
||||
str = str .. string.char (0x00) -- add null on the end
|
||||
@@ -73,7 +73,7 @@ end
|
||||
--@param cnt is protocol count
|
||||
--@return status : true if ok; false if bad
|
||||
--@return result : value if status ok, error msg if bad
|
||||
function sendcmd (socket, command, cnt)
|
||||
function sendcmd (socket, command, cnt)
|
||||
local cmdstr = cmdstr (command,cnt)
|
||||
local response
|
||||
|
||||
@@ -86,7 +86,7 @@ function sendcmd (socket, command, cnt)
|
||||
if ( not(status) ) then
|
||||
return false, "error sending packet payload"
|
||||
end
|
||||
|
||||
|
||||
status, response = socket:receive_bytes(4)
|
||||
if ( not(status) ) then
|
||||
return false, "error receiving length"
|
||||
@@ -103,7 +103,7 @@ function sendcmd (socket, command, cnt)
|
||||
end
|
||||
|
||||
-- magic response starts at 5th byte for 4 bytes, 4 byte for length + length of string commmand
|
||||
if (string.sub(response,5,8+4+string.len(command)) ~= CASSANDRARESP..pack4str(command)) then
|
||||
if (string.sub(response,5,8+4+string.len(command)) ~= CASSANDRARESP..pack4str(command)) then
|
||||
return false, "protocol response error"
|
||||
end
|
||||
|
||||
@@ -115,10 +115,10 @@ end
|
||||
--@param cnt is protocol count
|
||||
--@return status : true if ok; false if bad
|
||||
--@return result : value if status ok, error msg if bad
|
||||
function describe_cluster_name (socket,cnt)
|
||||
function describe_cluster_name (socket,cnt)
|
||||
local cname = "describe_cluster_name"
|
||||
local status,resp = sendcmd(socket,cname,cnt)
|
||||
|
||||
|
||||
if (not(status)) then
|
||||
stdnse.print_debug(1, "sendcmd"..resp)
|
||||
return false, "error in communication"
|
||||
@@ -134,15 +134,15 @@ function describe_cluster_name (socket,cnt)
|
||||
return true, value
|
||||
end
|
||||
|
||||
--Return API version
|
||||
--Return API version
|
||||
--@param socket to connect to
|
||||
--@param cnt is protocol count
|
||||
--@return status : true if ok; false if bad
|
||||
--@return result : value if status ok, error msg if bad
|
||||
function describe_version (socket,cnt)
|
||||
function describe_version (socket,cnt)
|
||||
local cname = "describe_version"
|
||||
local status,resp = sendcmd(socket,cname,cnt)
|
||||
|
||||
|
||||
if (not(status)) then
|
||||
stdnse.print_debug(1, "sendcmd"..resp)
|
||||
return false, "error in communication"
|
||||
@@ -158,7 +158,7 @@ function describe_version (socket,cnt)
|
||||
return true, value
|
||||
end
|
||||
|
||||
--Login to Cassandra
|
||||
--Login to Cassandra
|
||||
--@param socket to connect to
|
||||
--@param username to connect to
|
||||
--@param password to connect to
|
||||
@@ -190,7 +190,7 @@ function login (socket,username,password)
|
||||
local _, size = bin.unpack(">I", response, 1)
|
||||
|
||||
local loginresp = string.sub(response,5,17)
|
||||
if (loginresp ~= CASSANDRARESP..pack4str("login")) then
|
||||
if (loginresp ~= CASSANDRARESP..pack4str("login")) then
|
||||
return false, "protocol error"
|
||||
end
|
||||
|
||||
@@ -205,6 +205,6 @@ function login (socket,username,password)
|
||||
else
|
||||
return false, "Login failed."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
-- This module was written by Patrik Karlsson and facilitates communication
|
||||
-- with the Citrix XML Service. It is not feature complete and is missing several
|
||||
-- functions and parameters.
|
||||
-- with the Citrix XML Service. It is not feature complete and is missing several
|
||||
-- functions and parameters.
|
||||
--
|
||||
-- The library makes little or no effort to verify that the parameters submitted
|
||||
-- to each function are compliant with the DTD
|
||||
@@ -12,7 +12,7 @@
|
||||
-- Details regarding the requests/responses and their parameters can be found in
|
||||
-- the NFuse.DTD included with Citrix MetaFrame/Xenapp
|
||||
--
|
||||
-- This code is based on the information available in:
|
||||
-- This code is based on the information available in:
|
||||
-- NFuse.DTD - Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
|
||||
@@ -25,40 +25,40 @@ local table = require "table"
|
||||
_ENV = stdnse.module("citrixxml", stdnse.seeall)
|
||||
|
||||
--- Decodes html-entities to chars eg.   => <space>
|
||||
--
|
||||
--
|
||||
-- @param xmldata string to convert
|
||||
-- @return string an e
|
||||
function decode_xml_document(xmldata)
|
||||
|
||||
|
||||
local hexval
|
||||
|
||||
|
||||
if not xmldata then
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
local newstr = xmldata
|
||||
|
||||
|
||||
for m in xmldata:gmatch("(&#%d+;)") do
|
||||
hexval = m:match("(%d+)")
|
||||
|
||||
|
||||
if ( hexval ) then
|
||||
newstr = xmldata:gsub(m, string.char(hexval))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return newstr
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Sends the request to the server using the http lib
|
||||
--
|
||||
--
|
||||
-- @param host string, the ip of the remote server
|
||||
-- @param port number, the port of the remote server
|
||||
-- @param xmldata string, the HTTP data part of the request as XML
|
||||
--
|
||||
-- @return string with the response body
|
||||
--
|
||||
function send_citrix_xml_request(host, port, xmldata)
|
||||
function send_citrix_xml_request(host, port, xmldata)
|
||||
|
||||
local response = http.post( host, port, "/scripts/WPnBr.dll", { header={["Content-Type"]="text/xml"}}, nil, xmldata)
|
||||
|
||||
@@ -66,13 +66,13 @@ function send_citrix_xml_request(host, port, xmldata)
|
||||
-- decoding should *probably* only be done on XML-values
|
||||
-- this is *probably* defined in the standard, for anyone interested
|
||||
return decode_xml_document(response.body)
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Request information about the Citrix Server Farm
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string, the ip of the remote server
|
||||
@@ -86,7 +86,7 @@ function request_server_farm_data( host, port )
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestServerFarmData></RequestServerFarmData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
@@ -97,14 +97,14 @@ end
|
||||
function parse_server_farm_data_response( response )
|
||||
|
||||
local farms = {}
|
||||
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
for farm in response:gmatch("<ServerFarmName.->([^<]+)</ServerFarmName>") do
|
||||
table.insert(farms, farm)
|
||||
end
|
||||
|
||||
|
||||
return farms
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Sends a request for application data to the Citrix XML service
|
||||
@@ -137,20 +137,20 @@ function request_appdata(host, port, params)
|
||||
|
||||
if desired_details then
|
||||
if type(desired_details) == "string" then
|
||||
xmldata = xmldata .. "<DesiredDetails>" .. desired_details .. "</DesiredDetails>"
|
||||
xmldata = xmldata .. "<DesiredDetails>" .. desired_details .. "</DesiredDetails>"
|
||||
elseif type(desired_details) == "table" then
|
||||
for _, v in ipairs(desired_details) do
|
||||
xmldata = xmldata .. "<DesiredDetails>" .. v .. "</DesiredDetails>"
|
||||
xmldata = xmldata .. "<DesiredDetails>" .. v .. "</DesiredDetails>"
|
||||
end
|
||||
else
|
||||
assert(desired_details)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestAppData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
@@ -174,32 +174,32 @@ local function extract_appdata_acls(xmldata)
|
||||
for user in acl:gmatch("<User>(.-)</User>") do
|
||||
local user_name = user:match("<UserName.->(.-)</UserName>") or ""
|
||||
local domain_name = user:match("<Domain.->(.-)</Domain>") or ""
|
||||
|
||||
|
||||
if user_name:len() > 0 then
|
||||
if domain_name:len() > 0 then
|
||||
domain_name = domain_name .. "\\"
|
||||
end
|
||||
table.insert(users, domain_name .. user_name)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
for group in acl:gmatch("<Group>(.-)</Group>") do
|
||||
|
||||
|
||||
|
||||
local group_name = group:match("<GroupName.->(.-)</GroupName>") or ""
|
||||
local domain_name = group:match("<Domain.->(.-)</Domain>") or ""
|
||||
|
||||
local domain_name = group:match("<Domain.->(.-)</Domain>") or ""
|
||||
|
||||
if group_name:len() > 0 then
|
||||
if domain_name:len() > 0 then
|
||||
domain_name = domain_name .. "\\"
|
||||
end
|
||||
table.insert(groups, domain_name .. group_name)
|
||||
table.insert(groups, domain_name .. group_name)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if #users> 0 then
|
||||
acls['User'] = users
|
||||
@@ -207,7 +207,7 @@ local function extract_appdata_acls(xmldata)
|
||||
if #groups>0 then
|
||||
acls['Group'] = groups
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
return acls
|
||||
@@ -216,7 +216,7 @@ end
|
||||
|
||||
|
||||
--- Extracts the settings section of the XML response
|
||||
--
|
||||
--
|
||||
-- @param xmldata string containing results from the request app data request
|
||||
-- @return table containing settings extracted from the settings section of the response
|
||||
local function extract_appdata_settings(xmldata)
|
||||
@@ -224,7 +224,7 @@ local function extract_appdata_settings(xmldata)
|
||||
local settings = {}
|
||||
|
||||
settings['appisdisabled'] = xmldata:match("<Settings.-appisdisabled=\"(.-)\".->")
|
||||
settings['appisdesktop'] = xmldata:match("<Settings.-appisdesktop=\"(.-)\".->")
|
||||
settings['appisdesktop'] = xmldata:match("<Settings.-appisdesktop=\"(.-)\".->")
|
||||
|
||||
for s in xmldata:gmatch("<Settings.->(.-)</Settings>") do
|
||||
settings['Encryption'] = s:match("<Encryption.->(.-)</Encryption>")
|
||||
@@ -236,7 +236,7 @@ local function extract_appdata_settings(xmldata)
|
||||
end
|
||||
|
||||
return settings
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Parses the appdata XML response
|
||||
@@ -247,21 +247,21 @@ function parse_appdata_response(xmldata)
|
||||
|
||||
local apps = {}
|
||||
xmldata = xmldata:gsub("\r?\n",""):gsub(">%s+<", "><")
|
||||
|
||||
|
||||
for AppData in xmldata:gmatch("<AppData>(.-)</AppData>") do
|
||||
|
||||
local app_name = AppData:match("<FName.->(.-)</FName>") or ""
|
||||
local app_name = AppData:match("<FName.->(.-)</FName>") or ""
|
||||
local app = {}
|
||||
|
||||
|
||||
app['FName'] = app_name
|
||||
app['AccessList'] = extract_appdata_acls(AppData)
|
||||
app['Settings'] = extract_appdata_settings(AppData)
|
||||
|
||||
table.insert(apps, app)
|
||||
|
||||
|
||||
end
|
||||
|
||||
return apps
|
||||
|
||||
return apps
|
||||
end
|
||||
|
||||
--
|
||||
@@ -278,13 +278,13 @@ function request_address(host, port, flags, appname)
|
||||
if flags then
|
||||
xmldata = xmldata .. "<Flags>" .. flags .. "</Flags>"
|
||||
end
|
||||
|
||||
|
||||
if appname then
|
||||
xmldata = xmldata .. "<Name>"
|
||||
xmldata = xmldata .. "<AppName>" .. appname .. "</AppName>"
|
||||
xmldata = xmldata .. "</Name>"
|
||||
end
|
||||
|
||||
|
||||
xmldata = xmldata .. "</RequestAddress>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
@@ -294,7 +294,7 @@ end
|
||||
--- Request information about the Citrix protocol
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
@@ -303,24 +303,24 @@ end
|
||||
-- @return string HTTP response data
|
||||
--
|
||||
function request_server_data(host, port, params)
|
||||
|
||||
|
||||
local params = params or {}
|
||||
local server_type = params.ServerType or {"all"}
|
||||
local client_type = params.ClientType or {"all"}
|
||||
|
||||
|
||||
local xmldata = "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\r\n"
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestServerData>"
|
||||
|
||||
|
||||
for _, srvtype in pairs(server_type) do
|
||||
xmldata = xmldata .. "<ServerType>" .. srvtype .. "</ServerType>"
|
||||
end
|
||||
|
||||
|
||||
for _, clitype in pairs(client_type) do
|
||||
xmldata = xmldata .. "<ClientType>" .. clitype .. "</ClientType>"
|
||||
end
|
||||
|
||||
|
||||
xmldata = xmldata .. "</RequestServerData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
@@ -334,20 +334,20 @@ end
|
||||
function parse_server_data_response(response)
|
||||
|
||||
local servers = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
for s in response:gmatch("<ServerName>([^<]+)</ServerName>") do
|
||||
table.insert(servers, s)
|
||||
end
|
||||
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Request information about the Citrix protocol
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
@@ -363,7 +363,7 @@ function request_protocol_info( host, port, params )
|
||||
xmldata = xmldata .. "<!DOCTYPE NFuseProtocol SYSTEM \"NFuse.dtd\">\r\n"
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"1.1\">"
|
||||
xmldata = xmldata .. "<RequestProtocolInfo>"
|
||||
|
||||
|
||||
if params['ServerAddress'] then
|
||||
xmldata = xmldata .. "<ServerAddress addresstype=\"" .. params['ServerAddress']['attr']['addresstype'] .. "\">"
|
||||
xmldata = xmldata .. params['ServerAddress'] .. "</ServerAddress>"
|
||||
@@ -371,14 +371,14 @@ function request_protocol_info( host, port, params )
|
||||
|
||||
xmldata = xmldata .. "</RequestProtocolInfo>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
--- Request capability information
|
||||
--- Request capability information
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
-- @param host string the host which is to be queried
|
||||
@@ -393,7 +393,7 @@ function request_capabilities( host, port )
|
||||
xmldata = xmldata .. "<RequestCapabilities>"
|
||||
xmldata = xmldata .. "</RequestCapabilities>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
end
|
||||
|
||||
@@ -404,20 +404,20 @@ end
|
||||
function parse_capabilities_response(response)
|
||||
|
||||
local servers = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
for s in response:gmatch("<CapabilityId.->([^<]+)</CapabilityId>") do
|
||||
table.insert(servers, s)
|
||||
end
|
||||
|
||||
|
||||
return servers
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Tries to validate user credentials against the XML service
|
||||
--
|
||||
-- Consult the NFuse.DTD for a complete list of supported parameters
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- This function implements all the supported parameters described in:
|
||||
-- Version 5.0 (draft 1) 24 January 2008
|
||||
--
|
||||
--
|
||||
@@ -436,7 +436,7 @@ function request_validate_credentials(host, port, params )
|
||||
xmldata = xmldata .. "<NFuseProtocol version=\"5.0\">"
|
||||
xmldata = xmldata .. "<RequestValidateCredentials>"
|
||||
xmldata = xmldata .. "<Credentials>"
|
||||
|
||||
|
||||
if credentials['UserName'] then
|
||||
xmldata = xmldata .. "<UserName>" .. credentials['UserName'] .. "</UserName>"
|
||||
end
|
||||
@@ -444,17 +444,17 @@ function request_validate_credentials(host, port, params )
|
||||
if credentials['Password'] then
|
||||
xmldata = xmldata .. "<Password encoding=\"cleartext\">" .. credentials['Password'] .. "</Password>"
|
||||
end
|
||||
|
||||
|
||||
if credentials['Domain'] then
|
||||
xmldata = xmldata .. "<Domain type=\"NT\">" .. credentials['Domain'] .. "</Domain>"
|
||||
end
|
||||
|
||||
|
||||
xmldata = xmldata .. "</Credentials>"
|
||||
xmldata = xmldata .. "</RequestValidateCredentials>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -464,14 +464,14 @@ end
|
||||
--
|
||||
function parse_validate_credentials_response(response)
|
||||
local tblResult = {}
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
|
||||
response = response:gsub("\r?\n","")
|
||||
tblResult['DaysUntilPasswordExpiry'] = response:match("<DaysUntilPasswordExpiry>(.+)</DaysUntilPasswordExpiry>")
|
||||
tblResult['ShowPasswordExpiryWarning'] = response:match("<ShowPasswordExpiryWarning>(.+)</ShowPasswordExpiryWarning>")
|
||||
tblResult['ErrorId'] = response:match("<ErrorId>(.+)</ErrorId>")
|
||||
|
||||
|
||||
return tblResult
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Sends a request to reconnect session data
|
||||
@@ -487,7 +487,7 @@ function request_reconnect_session_data(host, port, params)
|
||||
|
||||
local params = params or {}
|
||||
local Credentials = params.Credentials or {}
|
||||
|
||||
|
||||
params.ServerType = params.ServerType or {}
|
||||
params.ClientType = params.ClientType or {}
|
||||
|
||||
@@ -497,7 +497,7 @@ function request_reconnect_session_data(host, port, params)
|
||||
xmldata = xmldata .. "<RequestReconnectSessionData>"
|
||||
|
||||
xmldata = xmldata .. "<Credentials>"
|
||||
|
||||
|
||||
if Credentials.UserName then
|
||||
xmldata = xmldata .. "<UserName>" .. Credentials.UserName .. "</UserName>"
|
||||
end
|
||||
@@ -505,11 +505,11 @@ function request_reconnect_session_data(host, port, params)
|
||||
if Credentials.Password then
|
||||
xmldata = xmldata .. "<Password encoding=\"cleartext\">" .. Credentials.Password .. "</Password>"
|
||||
end
|
||||
|
||||
|
||||
if Credentials.Domain then
|
||||
xmldata = xmldata .. "<Domain type=\"NT\">" .. Credentials.Domain .. "</Domain>"
|
||||
end
|
||||
|
||||
|
||||
xmldata = xmldata .. "</Credentials>"
|
||||
|
||||
if params.ClientName then
|
||||
@@ -519,21 +519,21 @@ function request_reconnect_session_data(host, port, params)
|
||||
if params.DeviceId then
|
||||
xmldata = xmldata .. "<DeviceId>" .. params.DeviceId .. "</DeviceId>"
|
||||
end
|
||||
|
||||
|
||||
for _, srvtype in pairs(params.ServerType) do
|
||||
xmldata = xmldata .. "<ServerType>" .. srvtype .. "</ServerType>"
|
||||
end
|
||||
|
||||
|
||||
for _, clitype in pairs(params.ClientType) do
|
||||
xmldata = xmldata .. "<ClientType>" .. clitype .. "</ClientType>"
|
||||
end
|
||||
|
||||
xmldata = xmldata .. "</RequestReconnectSessionData>"
|
||||
xmldata = xmldata .. "</NFuseProtocol>\r\n"
|
||||
|
||||
|
||||
return send_citrix_xml_request(host, port, xmldata)
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -154,7 +154,7 @@ local function is_ssl(port_number)
|
||||
return not not common_ssl_ports[port_number]
|
||||
end
|
||||
|
||||
--- This function returns best protocol order for trying to open a
|
||||
--- This function returns best protocol order for trying to open a
|
||||
-- connection based on port and service information
|
||||
--
|
||||
-- The first value is the best option, the second is the worst
|
||||
@@ -173,7 +173,7 @@ local function bestoption(port)
|
||||
end
|
||||
|
||||
--- This function opens a connection, sends the first data payload and
|
||||
-- check if a response is correctly received (what means that the
|
||||
-- check if a response is correctly received (what means that the
|
||||
-- protocol used is fine)
|
||||
--
|
||||
-- Possible options:
|
||||
@@ -197,7 +197,7 @@ local function opencon(host, port, protocol, data, opts)
|
||||
|
||||
-- check for connect_timeout or timeout option
|
||||
|
||||
if opts and opts.connect_timeout then
|
||||
if opts and opts.connect_timeout then
|
||||
sd:set_timeout(opts.connect_timeout)
|
||||
elseif opts and opts.timeout then
|
||||
sd:set_timeout(opts.timeout)
|
||||
@@ -206,9 +206,9 @@ local function opencon(host, port, protocol, data, opts)
|
||||
end
|
||||
|
||||
local status = sd:connect(host, port, protocol)
|
||||
if not status then
|
||||
if not status then
|
||||
sd:close()
|
||||
return nil, nil, nil
|
||||
return nil, nil, nil
|
||||
end
|
||||
|
||||
-- check for request_timeout or timeout option
|
||||
@@ -233,9 +233,9 @@ local function opencon(host, port, protocol, data, opts)
|
||||
end
|
||||
response = early_resp
|
||||
end
|
||||
if not status then
|
||||
if not status then
|
||||
sd:close()
|
||||
return nil, response, early_resp
|
||||
return nil, response, early_resp
|
||||
end
|
||||
return sd, response, early_resp
|
||||
end
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
-- --script-args creds.http='webadmin:password'
|
||||
--
|
||||
-- The service name at this point may be anything and the entry is created
|
||||
-- dynamically without validating whether the service exists or not.
|
||||
-- dynamically without validating whether the service exists or not.
|
||||
--
|
||||
-- The credential argument is not documented in this library using the <at>args
|
||||
-- function as the argument would incorrectly show up in all scripts making use
|
||||
@@ -71,9 +71,9 @@
|
||||
--
|
||||
-- Supported output formats are CSV, verbose and plain. In both verbose and plain
|
||||
-- records are seperated by colons. The difference between the two is that verbose
|
||||
-- includes the credential state. The file extension is automatically added to
|
||||
-- includes the credential state. The file extension is automatically added to
|
||||
-- the filename based on the type requested.
|
||||
--
|
||||
--
|
||||
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
|
||||
@@ -147,7 +147,7 @@ RegStorage = {
|
||||
o.filter = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Add credentials to storage
|
||||
--
|
||||
-- @param scriptname the name of the script adding the credentials
|
||||
@@ -158,7 +158,7 @@ RegStorage = {
|
||||
-- @param pass the password of the user
|
||||
-- @param state of the account
|
||||
add = function( self, scriptname, host, port, service, user, pass, state )
|
||||
local cred = {
|
||||
local cred = {
|
||||
scriptname = scriptname,
|
||||
host = host,
|
||||
port = port,
|
||||
@@ -170,7 +170,7 @@ RegStorage = {
|
||||
nmap.registry.creds = nmap.registry.creds or {}
|
||||
table.insert( nmap.registry.creds, cred )
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the storage filter
|
||||
--
|
||||
-- @param host table containing the host
|
||||
@@ -181,7 +181,7 @@ RegStorage = {
|
||||
self.filter.port = port
|
||||
self.filter.state = state
|
||||
end,
|
||||
|
||||
|
||||
--- Returns a credential iterator matching the selected filters
|
||||
--
|
||||
-- @return a credential iterator
|
||||
@@ -190,23 +190,23 @@ RegStorage = {
|
||||
local host, port = self.filter.host, self.filter.port
|
||||
|
||||
if ( not(nmap.registry.creds) ) then return end
|
||||
|
||||
|
||||
for _, v in pairs(nmap.registry.creds) do
|
||||
local h = ( v.host.ip or v.host )
|
||||
if ( not(host) and not(port) ) then
|
||||
if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then
|
||||
if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then
|
||||
coroutine.yield(v)
|
||||
end
|
||||
elseif ( not(host) and ( port == v.port ) ) then
|
||||
if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then
|
||||
if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then
|
||||
coroutine.yield(v)
|
||||
end
|
||||
elseif ( ( host and ( h == host or h == host.ip ) ) and not(port) ) then
|
||||
if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then
|
||||
if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then
|
||||
coroutine.yield(v)
|
||||
end
|
||||
elseif ( ( host and ( h == host or h == host.ip ) ) and port.number == v.port ) then
|
||||
if ( not(self.filter.state) or ( v.state == bit.band(self.filter.state, v.state) ) ) then
|
||||
if ( not(self.filter.state) or ( v.state == bit.band(self.filter.state, v.state) ) ) then
|
||||
coroutine.yield(v)
|
||||
end
|
||||
end
|
||||
@@ -214,12 +214,12 @@ RegStorage = {
|
||||
end
|
||||
return coroutine.wrap(get_next)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The credentials class
|
||||
Credentials = {
|
||||
|
||||
|
||||
--- Creates a new instance of the Credentials class
|
||||
-- @param scriptname string containing the name of the script
|
||||
-- @param host table as received by the scripts action method
|
||||
@@ -231,12 +231,12 @@ Credentials = {
|
||||
o.storage = RegStorage:new()
|
||||
o.storage:setFilter(host, port)
|
||||
o.host = host
|
||||
o.port = ( port and port.number ) and port.number
|
||||
o.port = ( port and port.number ) and port.number
|
||||
o.service = ( port and port.service ) and port.service
|
||||
o.scriptname = scriptname
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Add a discovered credential
|
||||
--
|
||||
-- @param user the name of the user
|
||||
@@ -255,11 +255,11 @@ Credentials = {
|
||||
self.storage:add( self.scriptname, self.host, self.port, self.service, user, pass, state )
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Returns a credential iterator
|
||||
--
|
||||
-- @param state mask containing values from the <Code>State</code> table
|
||||
-- @return credential iterator, returning a credential each time it's
|
||||
-- @return credential iterator, returning a credential each time it's
|
||||
-- called. Unless filtered by the state mask all credentials
|
||||
-- for the host, port match are iterated over.
|
||||
-- The credential table has the following fields:
|
||||
@@ -273,7 +273,7 @@ Credentials = {
|
||||
-- script that added the credential
|
||||
getCredentials = function(self, state)
|
||||
local function next_credential()
|
||||
if ( state ) then
|
||||
if ( state ) then
|
||||
self.storage:setFilter(self.host, { number=self.port, service = self.service }, state)
|
||||
end
|
||||
|
||||
@@ -288,11 +288,11 @@ Credentials = {
|
||||
local creds_global = stdnse.get_script_args('creds.global')
|
||||
local creds_service
|
||||
local creds_params
|
||||
|
||||
|
||||
if ( self.service ) then
|
||||
creds_service = stdnse.get_script_args('creds.' .. self.service )
|
||||
end
|
||||
|
||||
|
||||
if ( creds_service ) then creds_params = creds_service end
|
||||
if ( creds_global and creds_service ) then
|
||||
creds_params = creds_params .. ',' .. creds_global
|
||||
@@ -311,9 +311,9 @@ Credentials = {
|
||||
else
|
||||
user = cred:match("^(.*)$")
|
||||
end
|
||||
coroutine.yield( { host = self.host,
|
||||
coroutine.yield( { host = self.host,
|
||||
port = self.port,
|
||||
user = user,
|
||||
user = user,
|
||||
pass = pass,
|
||||
state = State.PARAM,
|
||||
service = self.service } )
|
||||
@@ -322,10 +322,10 @@ Credentials = {
|
||||
end
|
||||
return coroutine.wrap( next_credential )
|
||||
end,
|
||||
|
||||
|
||||
--- Returns a table of credentials
|
||||
--
|
||||
-- @return tbl table containing the discovered credentials
|
||||
-- @return tbl table containing the discovered credentials
|
||||
getTable = function(self)
|
||||
local result = {}
|
||||
|
||||
@@ -355,7 +355,7 @@ Credentials = {
|
||||
table.insert( result[h][svc], c )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local output = {}
|
||||
for hostname, host in pairs(result) do
|
||||
local host_tbl = { name = hostname }
|
||||
@@ -369,7 +369,7 @@ Credentials = {
|
||||
table.insert( host_tbl, svc_tbl )
|
||||
end
|
||||
-- sort the services
|
||||
table.sort( host_tbl,
|
||||
table.sort( host_tbl,
|
||||
function(a,b)
|
||||
return tonumber(a.name:match("^(%d+)")) < tonumber(b.name:match("^(%d+)"))
|
||||
end
|
||||
@@ -388,27 +388,27 @@ Credentials = {
|
||||
end
|
||||
return (#output > 0 ) and output
|
||||
end,
|
||||
|
||||
|
||||
-- Saves credentials in the current object to file
|
||||
-- @param filename string name of the file
|
||||
-- @param fileformat string file format type, values = csv | verbose | plain (default)
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing the error if status is false
|
||||
saveToFile = function(self, filename, fileformat)
|
||||
|
||||
|
||||
if ( fileformat == 'csv' ) then
|
||||
filename = filename .. '.csv'
|
||||
else
|
||||
filename = filename .. '.txt'
|
||||
end
|
||||
|
||||
|
||||
local f = io.open( filename, "w")
|
||||
local output = nil
|
||||
|
||||
|
||||
if ( not(f) ) then
|
||||
return false, ("ERROR: Failed to open file (%s)"):format(filename)
|
||||
end
|
||||
|
||||
|
||||
for account in self:getCredentials() do
|
||||
if ( fileformat == 'csv' ) then
|
||||
output = "\"" .. account.user .. "\",\"" .. account.pass .. "\",\"" .. StateMsg[account.state] .. "\""
|
||||
@@ -425,7 +425,7 @@ Credentials = {
|
||||
f:close()
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Get credentials with optional host and port filter
|
||||
-- If no filters are supplied all records are returned
|
||||
--
|
||||
@@ -436,7 +436,7 @@ Credentials = {
|
||||
local all = self:getTable()
|
||||
if ( all ) then return stdnse.format_output(true, all) end
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -24,7 +24,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
connect = function(self)
|
||||
self.socket = nmap.new_socket()
|
||||
return self.socket:connect(self.host, self.port)
|
||||
@@ -35,23 +35,23 @@ Helper = {
|
||||
assert(repo, "No repository was specified")
|
||||
assert(user, "No user was specified")
|
||||
assert(pass, "No pass was specified")
|
||||
|
||||
|
||||
-- Add a leading slash if it's missing
|
||||
if ( repo:sub(1,1) ~= "/" ) then repo = "/" .. repo end
|
||||
|
||||
|
||||
table.insert(auth_tab, "BEGIN AUTH REQUEST")
|
||||
table.insert(auth_tab, repo)
|
||||
table.insert(auth_tab, user)
|
||||
table.insert(auth_tab, Util.pwscramble(pass))
|
||||
table.insert(auth_tab, "END AUTH REQUEST")
|
||||
|
||||
|
||||
local data = stdnse.strjoin("\n", auth_tab) .. "\n"
|
||||
local status = self.socket:send(data)
|
||||
if ( not(status) ) then return false, "Failed to send login request" end
|
||||
|
||||
|
||||
local status, response = self.socket:receive()
|
||||
if ( not(status) ) then return false, "Failed to read login response" end
|
||||
|
||||
|
||||
if ( response == "I LOVE YOU\n" ) then return true end
|
||||
return false, response
|
||||
end,
|
||||
@@ -59,11 +59,11 @@ Helper = {
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
Util = {
|
||||
|
||||
|
||||
--- Scrambles a password
|
||||
--
|
||||
-- @param password string containing the password to scramble
|
||||
@@ -92,7 +92,7 @@ Util = {
|
||||
end
|
||||
return 'A' .. result
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -16,7 +16,7 @@ local url = require "url"
|
||||
-- * <code>target_check</code> - Validation function of the target (optional)
|
||||
-- * <code>login_check</code> - Login function of the target
|
||||
--
|
||||
-- TODO: Update the functionality of <code>target_check</code> to differentiate
|
||||
-- TODO: Update the functionality of <code>target_check</code> to differentiate
|
||||
-- between valid HTTP/200 and a custom error page.
|
||||
---
|
||||
|
||||
@@ -52,7 +52,7 @@ end
|
||||
---
|
||||
local function try_http_post_login(host, port, path, target, failstr, params, follow_redirects)
|
||||
local req = http.post(host, port, url.absolute(path, target), {no_cache=true}, nil, params)
|
||||
|
||||
|
||||
if not req.status then return false end
|
||||
local status = tonumber(req.status) or 0
|
||||
if follow_redirects and ( status > 300 and status < 400 ) then
|
||||
@@ -68,7 +68,7 @@ end
|
||||
-- Returns authentication realm advertised in an HTTP response
|
||||
-- @param response HTTP response object, such as a result from http.get()
|
||||
-- @return realm found in response header WWW-Authenticate
|
||||
-- (or nil if not present)
|
||||
-- (or nil if not present)
|
||||
---
|
||||
local function http_auth_realm(response)
|
||||
local auth = response.header["www-authenticate"] or ""
|
||||
|
||||
@@ -12,7 +12,7 @@ local url = require "url"
|
||||
-- * <code>name</code> - Descriptive name
|
||||
-- * <code>rapidDetect</code> - Callback function that is called in the beginning
|
||||
-- of detection process. It takes the host and port of the target website as
|
||||
-- arguments.
|
||||
-- arguments.
|
||||
-- * <code>consumingDetect</code> - Callback function that is called for each
|
||||
-- spidered page. It takes the body of the response (HTML source code) and the
|
||||
-- requested path as arguments.
|
||||
@@ -25,25 +25,25 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
local response = http.get(host, port, "/admin/")
|
||||
|
||||
if response.body then
|
||||
if string.find(response.body, "Log in | Django site admin") or
|
||||
string.find(response.body, "this_is_the_login_form") or
|
||||
if string.find(response.body, "Log in | Django site admin") or
|
||||
string.find(response.body, "this_is_the_login_form") or
|
||||
string.find(response.body, "csrfmiddlewaretoken") then
|
||||
return "Django detected. Found Django admin login page on /admin/"
|
||||
return "Django detected. Found Django admin login page on /admin/"
|
||||
end
|
||||
end
|
||||
|
||||
-- In Django, the cookie sessionid is being set when you log in
|
||||
-- In Django, the cookie sessionid is being set when you log in
|
||||
-- and forms will probably set a cookie called csrftoken.
|
||||
if response.cookies then
|
||||
for _, c in pairs(response.cookies) do
|
||||
if c.name == "csrftoken" then
|
||||
return "Django detected. Found sessionid cookie which means the contrib.auth package for authentication is enabled."
|
||||
return "Django detected. Found sessionid cookie which means the contrib.auth package for authentication is enabled."
|
||||
elseif c.name == "sessionid" then
|
||||
return "Django detected. Found csrftoken cookie."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- See if DEBUG mode still happens to be true.
|
||||
response = http.get(host, port, "/random404page/")
|
||||
|
||||
@@ -54,7 +54,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
if page then
|
||||
if string.find(page, "csrfmiddlewaretoken") then
|
||||
@@ -64,7 +64,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
return "Django detected. Found id_ preffix in id attribute name on " .. path
|
||||
end
|
||||
if string.find(page, "%-TOTAL%-FORMS") or string.find(page, "%-DELETE") then
|
||||
return "Django detected. Found -TOTAL-FORMS and -DELETE hidden inputs, which means there is a Django formset on " .. path
|
||||
return "Django detected. Found -TOTAL-FORMS and -DELETE hidden inputs, which means there is a Django formset on " .. path
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -94,7 +94,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
end
|
||||
end
|
||||
|
||||
-- Make up a bad path and match the error page
|
||||
-- Make up a bad path and match the error page
|
||||
response = http.get(host, port, "/random404page/")
|
||||
|
||||
if response.body then
|
||||
@@ -104,7 +104,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
|
||||
-- Check the source and look for csrf patterns.
|
||||
@@ -113,7 +113,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
return "RoR detected. Found csrf field on" .. path
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
},
|
||||
|
||||
@@ -133,16 +133,16 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
if response.cookies then
|
||||
for _, c in pairs(response.cookies) do
|
||||
if c.name == "aspnetsessionid" then
|
||||
return "ASP.NET detected. Found aspnetsessionid cookie."
|
||||
return "ASP.NET detected. Found aspnetsessionid cookie."
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
-- Check the source and look for common traces.
|
||||
if page then
|
||||
if string.find(page, " __VIEWSTATE") or
|
||||
if string.find(page, " __VIEWSTATE") or
|
||||
string.find(page, "__EVENT") or
|
||||
string.find(page, "__doPostBack") or
|
||||
string.find(page, "aspnetForm") or
|
||||
@@ -166,7 +166,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
return
|
||||
end
|
||||
@@ -186,7 +186,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
return
|
||||
end
|
||||
@@ -203,9 +203,9 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
return "Symfony detected. Found related header."
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
return
|
||||
end
|
||||
@@ -217,7 +217,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
local response = http.get(host, port, "/")
|
||||
|
||||
if response.body then
|
||||
if string.find(response.body, "content=[\"']WordPress") or
|
||||
if string.find(response.body, "content=[\"']WordPress") or
|
||||
string.find(response.body, "wp%-content") then
|
||||
return "Wordpress detected. Found common traces on /"
|
||||
end
|
||||
@@ -233,7 +233,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
if page then
|
||||
if string.find(page, "content=[\"']WordPress") or
|
||||
if string.find(page, "content=[\"']WordPress") or
|
||||
string.find(page, "wp%-content") then
|
||||
return "Wordpress detected. Found common traces on " .. page
|
||||
end
|
||||
@@ -249,7 +249,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
|
||||
if response.body then
|
||||
if string.find(response.body, "content=[\"']Joomla!") then
|
||||
return "Joomla detected. Found common traces on /"
|
||||
return "Joomla detected. Found common traces on /"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -264,7 +264,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
if page and string.find(page, "content=[\"']Joomla!") then
|
||||
return "Joomla detected. Found common traces on " .. page
|
||||
return "Joomla detected. Found common traces on " .. page
|
||||
end
|
||||
end
|
||||
},
|
||||
@@ -283,7 +283,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
|
||||
consumingDetect = function(page, path)
|
||||
if page and string.find(page, "content=[\"']Drupal") then
|
||||
return "Drupal detected. Found common traces on " .. page
|
||||
return "Drupal detected. Found common traces on " .. page
|
||||
end
|
||||
end
|
||||
},
|
||||
@@ -294,7 +294,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
local response = http.get(host, port, "/")
|
||||
|
||||
if response.body then
|
||||
if string.find(response.body, "content=[\"']MediaWiki") or
|
||||
if string.find(response.body, "content=[\"']MediaWiki") or
|
||||
string.find(response.body, "/mediawiki/") then
|
||||
return "MediaWiki detected. Found common traces on /"
|
||||
end
|
||||
@@ -304,7 +304,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
consumingDetect = function(page, path)
|
||||
if page and string.find(page, "content=[\"']MediaWiki") or
|
||||
string.find(page, "/mediawiki/") then
|
||||
return "MediaWiki detected. Found common traces on " .. page
|
||||
return "MediaWiki detected. Found common traces on " .. page
|
||||
end
|
||||
end
|
||||
},
|
||||
@@ -316,7 +316,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
if response.cookies then
|
||||
for _, c in pairs(response.cookies) do
|
||||
if c.name == "cfid" or c.name == "cftoken" then
|
||||
return "ColdFusion detected. Found " .. c.name .. " cookie."
|
||||
return "ColdFusion detected. Found " .. c.name .. " cookie."
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -334,7 +334,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
if response.cookies then
|
||||
for _, c in pairs(response.cookies) do
|
||||
if string.find(c.name, "bv_") then
|
||||
return "Broadvision detected. Found " .. c.name .. " cookie."
|
||||
return "Broadvision detected. Found " .. c.name .. " cookie."
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -352,7 +352,7 @@ tools = { Django = { rapidDetect = function(host, port)
|
||||
if response.cookies then
|
||||
for _, c in pairs(response.cookies) do
|
||||
if string.find(c.name, "wc_") then
|
||||
return "WebSphere Commerce detected. Found " .. c.name .. " cookie."
|
||||
return "WebSphere Commerce detected. Found " .. c.name .. " cookie."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -14,17 +14,17 @@ local table = require "table"
|
||||
-- This file is released under the Nmap license; see:
|
||||
-- http://nmap.org/book/man-legal.html
|
||||
--
|
||||
-- @args http-fingerprints.nikto-db-path Looks at the given path for nikto database.
|
||||
-- It then converts the records in nikto's database into our Lua table format
|
||||
-- and adds them to our current fingerprints if they don't exist already.
|
||||
-- @args http-fingerprints.nikto-db-path Looks at the given path for nikto database.
|
||||
-- It then converts the records in nikto's database into our Lua table format
|
||||
-- and adds them to our current fingerprints if they don't exist already.
|
||||
-- Unfortunately, our current implementation has some limitations:
|
||||
-- * It doesn't support records with more than one 'dontmatch' patterns for
|
||||
-- a probe.
|
||||
-- * It doesn't support logical AND for the 'match' patterns.
|
||||
-- * It doesn't support sending additional headers for a probe.
|
||||
-- That means, if a nikto fingerprint needs one of the above features, it
|
||||
-- won't be loaded. At the time of writing this, 6546 out of the 6573 Nikto
|
||||
-- fingerprints are being loaded successfully. This runtime Nikto fingerprint integration was suggested by Nikto co-author Chris Sullo as described at http://seclists.org/nmap-dev/2013/q4/292
|
||||
-- That means, if a nikto fingerprint needs one of the above features, it
|
||||
-- won't be loaded. At the time of writing this, 6546 out of the 6573 Nikto
|
||||
-- fingerprints are being loaded successfully. This runtime Nikto fingerprint integration was suggested by Nikto co-author Chris Sullo as described at http://seclists.org/nmap-dev/2013/q4/292
|
||||
--
|
||||
-- Although this format was originally modeled after the Nikto format, that ended
|
||||
-- up being too restrictive. The current format is a simple Lua table. There are many
|
||||
@@ -11804,7 +11804,7 @@ table.insert(fingerprints, {
|
||||
method = 'HEAD'
|
||||
},
|
||||
{
|
||||
path = '/sitecore/admin/unlock_admin.aspx', -- disabled per default in 6.2.0 (rev.100507)
|
||||
path = '/sitecore/admin/unlock_admin.aspx', -- disabled per default in 6.2.0 (rev.100507)
|
||||
method = 'HEAD'
|
||||
},
|
||||
{
|
||||
@@ -11862,12 +11862,12 @@ if f then
|
||||
if not string.match(l, "^#.*") then
|
||||
|
||||
record = {}
|
||||
|
||||
|
||||
for field in string.gmatch(l, "\"(.-)\",") do
|
||||
|
||||
-- Grab every attribute and create a record.
|
||||
if field then
|
||||
string.gsub(field, '%%', '%%%%')
|
||||
string.gsub(field, '%%', '%%%%')
|
||||
table.insert(record, field)
|
||||
end
|
||||
end
|
||||
@@ -11892,7 +11892,7 @@ if f then
|
||||
-- record[2]: OSVDB-ID
|
||||
-- record[3]: Server Type
|
||||
-- record[4]: URI
|
||||
-- record[5]: HTTP Method
|
||||
-- record[5]: HTTP Method
|
||||
-- record[6]: Match 1
|
||||
-- record[7]: Match 1 (Or)
|
||||
-- record[8]: Match1 (And)
|
||||
@@ -11903,11 +11903,11 @@ if f then
|
||||
-- record[13]: Headers
|
||||
|
||||
-- Is this a valid record? Atm, with our current format we need
|
||||
-- to skip some nikto records. See NSEDoc for more info.
|
||||
|
||||
if not exists
|
||||
-- to skip some nikto records. See NSEDoc for more info.
|
||||
|
||||
if not exists
|
||||
and record[4]
|
||||
and record[8] == "" and record[10] == "" and record[12] == ""
|
||||
and record[8] == "" and record[10] == "" and record[12] == ""
|
||||
and (tonumber(record[4]) == nil or (tonumber(record[4]) and record[4] == "200")) then
|
||||
|
||||
-- Our current format does not support HTTP code matching.
|
||||
@@ -11930,11 +11930,11 @@ if f then
|
||||
}
|
||||
|
||||
-- If there is a second match, add it.
|
||||
if record[7] and record[7] ~= "" then
|
||||
if record[7] and record[7] ~= "" then
|
||||
table.insert(nikto_fingerprint.matches, { match = record[7], output = record[11] })
|
||||
end
|
||||
|
||||
table.insert(fingerprints, nikto_fingerprint)
|
||||
table.insert(fingerprints, nikto_fingerprint)
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -147,7 +147,7 @@ table.insert(fingerprints, {
|
||||
fingerprint = '^f4ed19e0c114eb516faaac0ee37daf2807b4381f000000010000138d........00000000........'
|
||||
});
|
||||
|
||||
-- Catch all Checkpoint
|
||||
-- Catch all Checkpoint
|
||||
table.insert(fingerprints, {
|
||||
category = 'vendor',
|
||||
vendor = 'Checkpoint VPN-1 / Firewall-1',
|
||||
@@ -161,7 +161,7 @@ table.insert(fingerprints, {
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Cisco
|
||||
-- Cisco
|
||||
--------------------------------------------------------------------------------
|
||||
table.insert(fingerprints, {
|
||||
category = 'vendor',
|
||||
@@ -226,7 +226,7 @@ table.insert(fingerprints, {
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Fortinet
|
||||
-- Fortinet
|
||||
--------------------------------------------------------------------------------
|
||||
table.insert(fingerprints, {
|
||||
category = 'vendor',
|
||||
@@ -241,7 +241,7 @@ table.insert(fingerprints, {
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- FreeS/WAN
|
||||
-- FreeS/WAN
|
||||
--------------------------------------------------------------------------------
|
||||
table.insert(fingerprints, {
|
||||
category = 'vendor',
|
||||
@@ -521,7 +521,7 @@ table.insert(fingerprints, {
|
||||
version = nil,
|
||||
ostype = nil,
|
||||
devicetype = nil,
|
||||
cpe = nil,
|
||||
cpe = nil,
|
||||
fingerprint = '^7003cbc1097dbe9c2600ba6983bc8b35'
|
||||
});
|
||||
|
||||
@@ -553,7 +553,7 @@ table.insert(fingerprints, {
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Microsoft
|
||||
-- Microsoft
|
||||
-- http://msdn.microsoft.com/en-us/library/cc233476.aspx
|
||||
--------------------------------------------------------------------------------
|
||||
table.insert(fingerprints, {
|
||||
@@ -659,7 +659,7 @@ table.insert(fingerprints, {
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Nortel Contivity / Nortel VPN router
|
||||
-- Nortel Contivity / Nortel VPN router
|
||||
-- The last byte might be a version ?
|
||||
-- From ike-scan:
|
||||
--- 00000004, 00000005, 00000007, 00000009, 0000000a
|
||||
@@ -692,7 +692,7 @@ table.insert(fingerprints, {
|
||||
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
-- Openswan
|
||||
-- Openswan
|
||||
--------------------------------------------------------------------------------
|
||||
table.insert(fingerprints, {
|
||||
category = 'vendor',
|
||||
@@ -791,7 +791,7 @@ table.insert(fingerprints, {
|
||||
ostype = nil,
|
||||
devicetype = nil,
|
||||
cpe = nil,
|
||||
fingerprint = '^5b362bc820f60007' -- (Maybe NSA?, SonicOS Enhanced 4.2?)
|
||||
fingerprint = '^5b362bc820f60007' -- (Maybe NSA?, SonicOS Enhanced 4.2?)
|
||||
});
|
||||
|
||||
table.insert(fingerprints, {
|
||||
@@ -1879,7 +1879,7 @@ table.insert(fingerprints, {
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
-- Attribute: Misc fingerprints
|
||||
-- not directly usable for fingerprinting
|
||||
-- not directly usable for fingerprinting
|
||||
-- but can be used for guessing
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
@@ -2348,7 +2348,7 @@ table.insert(fingerprints, {
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
-- vid_order:
|
||||
-- vid_order:
|
||||
-- By examining the ordering of the VIDs, some assumptions can be made
|
||||
-- Currently only has support for Cisco
|
||||
|
||||
@@ -2419,7 +2419,7 @@ table.insert(fingerprints, {
|
||||
-- Cisco Unity, XAUTH, IKE Fragmentation, Cisco VPN Concentrator
|
||||
});
|
||||
|
||||
--[[ Probably too
|
||||
--[[ Probably too
|
||||
table.insert(fingerprints, {
|
||||
category = 'vid_ordering',
|
||||
vendor = 'Cisco',
|
||||
@@ -2436,11 +2436,11 @@ table.insert(fingerprints, {
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
-- header_ordering:
|
||||
-- header_ordering:
|
||||
-- For possible future use
|
||||
|
||||
--- Cisco
|
||||
-- 1: SA, VID, VID, VID, VID, KeyExchange, ID, Nonce, Hash
|
||||
-- 1: SA, VID, VID, VID, VID, KeyExchange, ID, Nonce, Hash
|
||||
-- 2: SA, KeyExchange, Nonce, ID, Hash, VID, VID, VID, VID, VID, VID
|
||||
-- 3: SA, KeyExchange, Nonce, ID, Hash, VID, VID, VID, VID, VID
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---This config file is designed for adding a backdoor to the system. It has a few
|
||||
-- options by default, only one enabled by default. I suggest
|
||||
-- options by default, only one enabled by default. I suggest
|
||||
--
|
||||
-- Note that none of these modules are included with Nmap by default.
|
||||
-- Note that none of these modules are included with Nmap by default.
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
@@ -17,7 +17,7 @@ local mod
|
||||
-- a response
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Adding a user account: $username/$password"
|
||||
mod.name = "Adding a user account: $username/$password"
|
||||
mod.program = "net"
|
||||
mod.args = "user $username $password /add"
|
||||
mod.maxtime = 2
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
---This is the default configuration file. It simply runs some built-in Window
|
||||
-- programs to gather information about the remote system. It's intended to be
|
||||
-- simple, demonstrate some of the concepts, and not break/alte anything.
|
||||
-- programs to gather information about the remote system. It's intended to be
|
||||
-- simple, demonstrate some of the concepts, and not break/alte anything.
|
||||
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- key, etc.
|
||||
overrides = {}
|
||||
--overrides.timeout = 40
|
||||
@@ -13,7 +13,7 @@ modules = {}
|
||||
local mod
|
||||
|
||||
-- Get the Windows version. For some reason we can't run this directly, but it works ok
|
||||
-- if we run it through cmd.exe.
|
||||
-- if we run it through cmd.exe.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Windows version"
|
||||
@@ -24,7 +24,7 @@ mod.noblank = true
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Grab the ip and mac address(es) from ipconfig. The output requires quite a bit of cleanup
|
||||
-- to end up being usable and pretty.
|
||||
-- to end up being usable and pretty.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "IP Address and MAC Address from 'ipconfig.exe'"
|
||||
@@ -47,7 +47,7 @@ mod.remove = {"User accounts for", "The command completed", "%-%-%-%-%
|
||||
mod.noblank = true
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Get the list of accounts in the 'administrators' group.
|
||||
-- Get the list of accounts in the 'administrators' group.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Membership of 'administrators' from 'net localgroup administrators'"
|
||||
@@ -58,9 +58,9 @@ mod.remove = {"The command completed", "%-%-%-%-%-%-%-%-%-%-%-", "Memb
|
||||
mod.noblank = true
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards.
|
||||
-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards.
|
||||
-- Interestingly, in my tests against Windows 2003, ping gives weird output (but still, more or less, worked)
|
||||
-- when the SystemRoot environmental variable wasn't set.
|
||||
-- when the SystemRoot environmental variable wasn't set.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Can the host ping our address?"
|
||||
@@ -69,10 +69,10 @@ mod.args = "-n 1 $lhost"
|
||||
mod.maxtime = 5
|
||||
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time.
|
||||
-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time.
|
||||
-- Like ping, if the SystemRoot variable isn't set, the output is a bit strange (but still works)
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
@@ -85,7 +85,7 @@ mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Dump the arp cache of the system.
|
||||
-- Dump the arp cache of the system.
|
||||
mod = {}
|
||||
mod.name = "ARP Cache from arp.exe"
|
||||
mod.program = 'arp.exe'
|
||||
@@ -105,12 +105,12 @@ mod.maxtime = 1
|
||||
mod.remove = {"Active"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Get the routing table.
|
||||
-- Get the routing table.
|
||||
--
|
||||
-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be
|
||||
-- set properly, so it isn't going to work against systems with odd paths.
|
||||
-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be
|
||||
-- set properly, so it isn't going to work against systems with odd paths.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Full routing table from 'netstat -nr'"
|
||||
@@ -131,7 +131,7 @@ mod.maxtime = 5
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Get the drive configuration. For same (insane?) reason, it uses NULL characters instead of spaces
|
||||
-- for the response, so we have to do a replaceent.
|
||||
-- for the response, so we have to do a replaceent.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Drive list (for more info, try adding --script-args=config=drives,drive=C:)"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---This configuration file pulls info about a given harddrive
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- key, etc.
|
||||
overrides = {}
|
||||
--overrides.timeout = 40
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---This configuration file contains the examples given in smb-psexec.nse.
|
||||
---This configuration file contains the examples given in smb-psexec.nse.
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- key, etc.
|
||||
overrides = {}
|
||||
overrides.timeout = 40
|
||||
@@ -42,7 +42,7 @@ mod.program = "ping.exe"
|
||||
mod.args = "$lhost"
|
||||
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
|
||||
mod = {}
|
||||
@@ -52,7 +52,7 @@ mod.program = "ping.exe"
|
||||
mod.args = "$host"
|
||||
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
mod.req_args = {'host'}
|
||||
table.insert(modules, mod)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---This is the configuration file for modules that aren't quite ready for prime
|
||||
-- time yet.
|
||||
-- time yet.
|
||||
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- key, etc.
|
||||
overrides = {}
|
||||
--overrides.timeout = 40
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---More verbose network scripts
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
-- key, etc.
|
||||
overrides = {}
|
||||
--overrides.timeout = 40
|
||||
@@ -10,7 +10,7 @@ modules = {}
|
||||
local mod
|
||||
|
||||
-- Grab the ip and mac address(es) from ipconfig. The output requires quite a bit of cleanup
|
||||
-- to end up being usable and pretty.
|
||||
-- to end up being usable and pretty.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "IP Address and MAC Address from 'ipconfig.exe'"
|
||||
@@ -21,7 +21,7 @@ mod.find = {"IP Address", "Physical Address", "Ethernet adapter"}
|
||||
mod.replace = {{"%. ", ""}, {"-", ":"}, {"Physical Address", "MAC Address"}}
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Dump the arp cache of the system.
|
||||
-- Dump the arp cache of the system.
|
||||
mod = {}
|
||||
mod.name = "ARP Cache from arp.exe"
|
||||
mod.program = 'arp.exe'
|
||||
@@ -41,12 +41,12 @@ mod.maxtime = 1
|
||||
mod.remove = {"Active"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Get the routing table.
|
||||
-- Get the routing table.
|
||||
--
|
||||
-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be
|
||||
-- set properly, so it isn't going to work against systems with odd paths.
|
||||
-- Like 'ver', this has to be run through cmd.exe. This also requires the 'PATH' variable to be
|
||||
-- set properly, so it isn't going to work against systems with odd paths.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Full routing table from 'netstat -nr'"
|
||||
@@ -57,9 +57,9 @@ mod.maxtime = 1
|
||||
mod.noblank = true
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards.
|
||||
-- Try and ping back to our host. This helps check if there's a firewall in the way for connecting backwards.
|
||||
-- Interestingly, in my tests against Windows 2003, ping gives weird output (but still, more or less, worked)
|
||||
-- when the SystemRoot environmental variable wasn't set.
|
||||
-- when the SystemRoot environmental variable wasn't set.
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
mod.name = "Can the host ping our address?"
|
||||
@@ -68,10 +68,10 @@ mod.args = "-n 1 $lhost"
|
||||
mod.maxtime = 5
|
||||
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time.
|
||||
-- Try a traceroute back to our host. I limited it to the first 5 hops in the interest of saving time.
|
||||
-- Like ping, if the SystemRoot variable isn't set, the output is a bit strange (but still works)
|
||||
mod = {}
|
||||
mod.upload = false
|
||||
@@ -94,7 +94,7 @@ mod.req_args = {'address'}
|
||||
mod.maxtime = 5
|
||||
mod.remove = {"statistics", "Packet", "Approximate", "Minimum"}
|
||||
mod.noblank = true
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
mod.env = "SystemRoot=c:\\WINDOWS"
|
||||
table.insert(modules, mod)
|
||||
|
||||
-- Try a traceroute to an address given by the user
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---This config file is designed for running password-dumping scripts. So far,
|
||||
---This config file is designed for running password-dumping scripts. So far,
|
||||
-- it supports pwdump6 2.0.0 and fgdump.
|
||||
--
|
||||
-- Note that none of these modules are included with Nmap by default.
|
||||
-- Note that none of these modules are included with Nmap by default.
|
||||
|
||||
-- Any variable in the 'config' table in smb-psexec.nse can be overriden in the
|
||||
-- 'overrides' table. Most of them are not really recommended, such as the host,
|
||||
@@ -22,7 +22,7 @@ local mod
|
||||
--mod.url = "http://www.foofus.net/fizzgig/pwdump/"
|
||||
--table.insert(modules, mod)
|
||||
|
||||
---Uncomment if you'd like to use PwDump6 1.7.2 (considered obsolete, but still works).
|
||||
---Uncomment if you'd like to use PwDump6 1.7.2 (considered obsolete, but still works).
|
||||
-- Note that for some reason, this and 'fgdump' don't get along (fgdump only produces a blank
|
||||
-- file if these are run together)
|
||||
--mod = {}
|
||||
@@ -36,8 +36,8 @@ local mod
|
||||
--mod.url = "http://www.foofus.net/fizzgig/pwdump/"
|
||||
--table.insert(modules, mod)
|
||||
|
||||
-- Warning: the danger of using fgdump is that it always write the output to the harddrive unencrypted;
|
||||
-- this makes it more obvious that an attack has occurred.
|
||||
-- Warning: the danger of using fgdump is that it always write the output to the harddrive unencrypted;
|
||||
-- this makes it more obvious that an attack has occurred.
|
||||
mod = {}
|
||||
mod.upload = true
|
||||
mod.name = "FgDump"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
-- Read and parse some of Nmap's data files: <code>nmap-protocols</code>,
|
||||
-- <code>nmap-rpc</code>, <code>nmap-services</code>, and
|
||||
-- <code>nmap-rpc</code>, <code>nmap-services</code>, and
|
||||
-- <code>nmap-mac-prefixes</code>.
|
||||
--
|
||||
-- The functions in this module return values appropriate for use with exception
|
||||
|
||||
174
nselib/dhcp.lua
174
nselib/dhcp.lua
@@ -1,9 +1,9 @@
|
||||
---Implement a Dynamic Host Configuration Protocol (DHCP) client.
|
||||
---Implement a Dynamic Host Configuration Protocol (DHCP) client.
|
||||
--
|
||||
-- DHCP, defined in rfc2132 and rfc2131, is a protocol for hosts to automatically
|
||||
-- DHCP, defined in rfc2132 and rfc2131, is a protocol for hosts to automatically
|
||||
-- configure themselves on a network (that is, obtain an ip address). This library,
|
||||
-- which have a trivial one-function interface, can send out DHCP packets of many
|
||||
-- types and parse the responses.
|
||||
-- types and parse the responses.
|
||||
--
|
||||
-- @author "Ron Bowes"
|
||||
|
||||
@@ -24,7 +24,7 @@ local table = require "table"
|
||||
_ENV = stdnse.module("dhcp", stdnse.seeall)
|
||||
|
||||
|
||||
request_types =
|
||||
request_types =
|
||||
{
|
||||
DHCPDISCOVER = 1,
|
||||
DHCPOFFER = 2,
|
||||
@@ -46,19 +46,19 @@ request_types_str[6] = "DHCPNAK"
|
||||
request_types_str[7] = "DHCPRELEASE"
|
||||
request_types_str[8] = "DHCPINFORM"
|
||||
|
||||
---Read an IP address or a list of IP addresses. Print an error if the length isn't a multiple of 4.
|
||||
---Read an IP address or a list of IP addresses. Print an error if the length isn't a multiple of 4.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_ip(data, pos, length)
|
||||
if(length ~= 4) then
|
||||
if((length % 4) ~= 0) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for an ip address (%d)", length)
|
||||
pos = pos + length
|
||||
|
||||
|
||||
return pos, nil
|
||||
else
|
||||
local results = {}
|
||||
@@ -78,24 +78,24 @@ local function read_ip(data, pos, length)
|
||||
end
|
||||
end
|
||||
|
||||
---Read a string. The length of the string is given by the length field.
|
||||
---Read a string. The length of the string is given by the length field.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_string(data, pos, length)
|
||||
return bin.unpack(string.format("A%d", length), data, pos)
|
||||
end
|
||||
|
||||
---Read a single byte. Print an error if the length isn't 1.
|
||||
---Read a single byte. Print an error if the length isn't 1.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_1_byte(data, pos, length)
|
||||
if(length ~= 1) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 1)
|
||||
@@ -106,13 +106,13 @@ local function read_1_byte(data, pos, length)
|
||||
end
|
||||
|
||||
---Read a message type. This is a single-byte value that's looked up in the <code>request_types_str</code>
|
||||
-- table. Print an error if the length isn't 1.
|
||||
-- table. Print an error if the length isn't 1.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_message_type(data, pos, length)
|
||||
local value
|
||||
|
||||
@@ -125,14 +125,14 @@ local function read_message_type(data, pos, length)
|
||||
return pos, request_types_str[value]
|
||||
end
|
||||
|
||||
---Read a single byte, and return 'false' if it's 0, or 'true' if it's non-zero. Print an error if the
|
||||
-- length isn't 1.
|
||||
---Read a single byte, and return 'false' if it's 0, or 'true' if it's non-zero. Print an error if the
|
||||
-- length isn't 1.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_boolean(data, pos, length)
|
||||
local result
|
||||
pos, result = read_1_byte(data, pos, length)
|
||||
@@ -147,13 +147,13 @@ local function read_boolean(data, pos, length)
|
||||
end
|
||||
end
|
||||
|
||||
---Read a 2-byte unsigned little endian value. Print an error if the length isn't 2.
|
||||
---Read a 2-byte unsigned little endian value. Print an error if the length isn't 2.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_2_bytes(data, pos, length)
|
||||
if(length ~= 2) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 2)
|
||||
@@ -165,13 +165,13 @@ end
|
||||
|
||||
|
||||
---Read a list of 2-byte unsigned little endian values. Print an error if the length isn't a multiple
|
||||
-- of 2.
|
||||
-- of 2.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_2_bytes_list(data, pos, length)
|
||||
if((length % 2) ~= 0) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 2)
|
||||
@@ -191,13 +191,13 @@ local function read_2_bytes_list(data, pos, length)
|
||||
end
|
||||
|
||||
|
||||
---Read a 4-byte unsigned little endian value. Print an error if the length isn't 4.
|
||||
---Read a 4-byte unsigned little endian value. Print an error if the length isn't 4.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_4_bytes(data, pos, length)
|
||||
if(length ~= 4) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be %d)", length, 4)
|
||||
@@ -207,14 +207,14 @@ local function read_4_bytes(data, pos, length)
|
||||
return bin.unpack(">I", data, pos)
|
||||
end
|
||||
|
||||
---Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an
|
||||
-- error if the length isn't 4.
|
||||
---Read a 4-byte unsigned little endian value, and interpret it as a time offset value. Print an
|
||||
-- error if the length isn't 4.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_time(data, pos, length)
|
||||
local result
|
||||
if(length ~= 4) then
|
||||
@@ -244,14 +244,14 @@ local function read_time(data, pos, length)
|
||||
return pos, string.format("%d %s, %d:%02d:%02d", days, dayLabel, hours, minutes, seconds)
|
||||
end
|
||||
|
||||
---Read a list of static routes. Each of them are a pair of IP addresses, a destination and a
|
||||
-- router. Print an error if the length isn't a multiple of 8.
|
||||
---Read a list of static routes. Each of them are a pair of IP addresses, a destination and a
|
||||
-- router. Print an error if the length isn't a multiple of 8.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_static_route(data, pos, length)
|
||||
if((length % 8) ~= 0) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8)
|
||||
@@ -271,14 +271,14 @@ local function read_static_route(data, pos, length)
|
||||
end
|
||||
end
|
||||
|
||||
---Read a list of policy filters. Each of them are a pair of IP addresses, an address and a
|
||||
-- mask. Print an error if the length isn't a multiple of 8.
|
||||
---Read a list of policy filters. Each of them are a pair of IP addresses, an address and a
|
||||
-- mask. Print an error if the length isn't a multiple of 8.
|
||||
--
|
||||
--@param data The packet.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@param pos The position in the packet.
|
||||
--@param length The length that the server claims the field is.
|
||||
--@return The new position (will always be pos + length, no matter what we think it should be)
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
--@return The value of the field, or nil if the field length was wrong.
|
||||
local function read_policy_filter(data, pos, length)
|
||||
if((length % 8) ~= 0) then
|
||||
stdnse.print_debug(1, "dhcp-discover: Invalid length for data (%d; should be multiple of %d)", length, 8)
|
||||
@@ -299,7 +299,7 @@ local function read_policy_filter(data, pos, length)
|
||||
end
|
||||
|
||||
---These are the different fields for DHCP. These have to come after the read_* function
|
||||
-- definitions.
|
||||
-- definitions.
|
||||
local actions = {}
|
||||
actions[1] = {name="Subnet Mask", func=read_ip, default=true}
|
||||
actions[2] = {name="Time Offset", func=read_4_bytes, default=false}
|
||||
@@ -364,14 +364,14 @@ actions[60] = {name="Class Identifier", func=read_string,
|
||||
actions[61] = {name="Client Identifier (client)", func=read_string, default=false}
|
||||
actions[252]= {name="WPAD", func=read_string, default=false}
|
||||
|
||||
--- Does the send/receive, doesn't build/parse anything.
|
||||
--- Does the send/receive, doesn't build/parse anything.
|
||||
local function dhcp_send(socket, host, packet)
|
||||
-- Send out the packet
|
||||
return socket:sendto(host, { number=67, protocol="udp" }, packet)
|
||||
end
|
||||
|
||||
local function dhcp_receive(socket, transaction_id)
|
||||
|
||||
|
||||
local status, data = socket:receive()
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
@@ -382,7 +382,7 @@ local function dhcp_receive(socket, transaction_id)
|
||||
-- generated and different for every instance of a script (to prevent collisions)
|
||||
while status and data:sub(5, 8) ~= transaction_id do
|
||||
status, data = socket:receive()
|
||||
end
|
||||
end
|
||||
|
||||
return status, data
|
||||
end
|
||||
@@ -390,32 +390,32 @@ end
|
||||
--- Builds a DHCP packet
|
||||
--
|
||||
--@param request_type The type of request as an integer (use the <code>request_types</code> table at the
|
||||
-- top of this file).
|
||||
--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to
|
||||
-- top of this file).
|
||||
--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to
|
||||
-- send the response. Setting it to "255.255.255.255" or "0.0.0.0" is generally acceptable (if not,
|
||||
-- host.ip_src can work).
|
||||
-- host.ip_src can work).
|
||||
--@param mac_address Your mac address (as a string up to 16 bytes) where the server will send the response. Like
|
||||
-- <code>ip_address</code>, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is
|
||||
-- common (host.mac_addr_src works).
|
||||
-- <code>ip_address</code>, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is
|
||||
-- common (host.mac_addr_src works).
|
||||
--@param options [optional] A table of additional request options where each option is a table containing the
|
||||
-- following fields:
|
||||
-- * <code>number</code> - The option number
|
||||
-- * <code>type</code> - The option type ("string" or "ip")
|
||||
-- * <code>value</code> - The option value
|
||||
--@param request_options [optional] The options to request from the server, as an array of integers. For the
|
||||
--@param request_options [optional] The options to request from the server, as an array of integers. For the
|
||||
-- acceptable options, see the <code>actions</code> table above or have a look at rfc2132.
|
||||
-- Some DHCP servers (such as my Linksys WRT54g) will ignore this list and send whichever
|
||||
-- information it wants. Default: all options marked as 'default' in the <code>actions</code>
|
||||
-- table above are requested (the typical interesting ones) if no verbosity is given.
|
||||
-- If any level of verbosity is on, get all types.
|
||||
-- table above are requested (the typical interesting ones) if no verbosity is given.
|
||||
-- If any level of verbosity is on, get all types.
|
||||
--@param overrides [optional] A table of overrides. If a field in the table matches a field in the DHCP
|
||||
-- packet (see rfc2131 section 2 for a list of possible fields), the value in the table
|
||||
-- will be sent instead of the default value.
|
||||
--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second.
|
||||
-- will be sent instead of the default value.
|
||||
--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second.
|
||||
--@param transaction_id The identity of the transaction.
|
||||
--
|
||||
--@return status (true or false)
|
||||
--@return The parsed response, as a table.
|
||||
--@return The parsed response, as a table.
|
||||
function dhcp_build(request_type, ip_address, mac_address, options, request_options, overrides, lease_time, transaction_id)
|
||||
local packet = ''
|
||||
|
||||
@@ -472,13 +472,13 @@ function dhcp_build(request_type, ip_address, mac_address, options, request_opti
|
||||
end
|
||||
|
||||
---Parse a DHCP packet (either a request or a response) and return the results as a table. The
|
||||
-- table at the top of this function (<code>actions</code>) defines the name of each field, as
|
||||
-- laid out in rfc2132, and the function that parses it.
|
||||
-- table at the top of this function (<code>actions</code>) defines the name of each field, as
|
||||
-- laid out in rfc2132, and the function that parses it.
|
||||
--
|
||||
-- In theory, this should be able to parse any valid DHCP packet.
|
||||
-- In theory, this should be able to parse any valid DHCP packet.
|
||||
--
|
||||
--@param data The DHCP packet data. Any padding at the end of the packet will be ignored (by default,
|
||||
-- DHCP packets are padded with \x00 bytes).
|
||||
--@param data The DHCP packet data. Any padding at the end of the packet will be ignored (by default,
|
||||
-- DHCP packets are padded with \x00 bytes).
|
||||
function dhcp_parse(data, transaction_id)
|
||||
local pos = 1
|
||||
local result = {}
|
||||
@@ -555,7 +555,7 @@ function dhcp_parse(data, transaction_id)
|
||||
end
|
||||
|
||||
-- Handle the 'Option Overload' option specially -- if it's set, it tells us to use the file and/or sname values after we
|
||||
-- run out of data.
|
||||
-- run out of data.
|
||||
if(option == 52) then
|
||||
if(value == 1) then
|
||||
data = data .. result['file']
|
||||
@@ -573,7 +573,7 @@ function dhcp_parse(data, transaction_id)
|
||||
end
|
||||
|
||||
---Build and send any kind of DHCP packet, and parse the response. This is the only interface
|
||||
-- to the DHCP library, and should be the only one necessary.
|
||||
-- to the DHCP library, and should be the only one necessary.
|
||||
--
|
||||
-- All DHCP packet have the same structure, but different fields. It is therefore easy to build
|
||||
-- any of the possible request types:
|
||||
@@ -591,30 +591,30 @@ end
|
||||
-- type. If you're going to build some DHCP code on your own, I recommend reading rfc2131.
|
||||
--
|
||||
--@param request_type The type of request as an integer (use the <code>request_types</code> table at the
|
||||
-- top of this file).
|
||||
--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to
|
||||
-- top of this file).
|
||||
--@param ip_address Your ip address (as a dotted-decimal string). This tells the DHCP server where to
|
||||
-- send the response. Setting it to "255.255.255.255" or "0.0.0.0" is generally acceptable (if not,
|
||||
-- host.ip_src can work).
|
||||
-- host.ip_src can work).
|
||||
--@param mac_address Your mac address (as a string up to 16 bytes) where the server will send the response. Like
|
||||
-- <code>ip_address</code>, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is
|
||||
-- common (host.mac_addr_src works).
|
||||
-- <code>ip_address</code>, setting to the broadcast address (FF:FF:FF:FF:FF:FF) is
|
||||
-- common (host.mac_addr_src works).
|
||||
--@param options [optional] A table of additional request options where each option is a table containing the
|
||||
-- following fields:
|
||||
-- * <code>number</code> - The option number
|
||||
-- * <code>type</code> - The option type ("string" or "ip")
|
||||
-- * <code>value</code> - The option value
|
||||
--@param request_options [optional] The options to request from the server, as an array of integers. For the
|
||||
--@param request_options [optional] The options to request from the server, as an array of integers. For the
|
||||
-- acceptable options, see the <code>actions</code> table above or have a look at rfc2132.
|
||||
-- Some DHCP servers (such as my Linksys WRT54g) will ignore this list and send whichever
|
||||
-- information it wants. Default: all options marked as 'default' in the <code>actions</code>
|
||||
-- table above are requested (the typical interesting ones) if no verbosity is given.
|
||||
-- If any level of verbosity is on, get all types.
|
||||
-- table above are requested (the typical interesting ones) if no verbosity is given.
|
||||
-- If any level of verbosity is on, get all types.
|
||||
--@param overrides [optional] A table of overrides. If a field in the table matches a field in the DHCP
|
||||
-- packet (see rfc2131 section 2 for a list of possible fields), the value in the table
|
||||
-- will be sent instead of the default value.
|
||||
--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second.
|
||||
-- will be sent instead of the default value.
|
||||
--@param lease_time [optional] The lease time used when requestint an IP. Default: 1 second.
|
||||
--@return status (true or false)
|
||||
--@return The parsed response, as a table.
|
||||
--@return The parsed response, as a table.
|
||||
function make_request(target, request_type, ip_address, mac_address, options, request_options, overrides, lease_time)
|
||||
-- A unique id that identifies this particular session (and lets us filter out what we don't want to see)
|
||||
local transaction_id = overrides and overrides['xid'] or bin.pack("<I", math.random(0, 0x7FFFFFFF))
|
||||
@@ -643,7 +643,7 @@ function make_request(target, request_type, ip_address, mac_address, options, re
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(1, "dhcp: Couldn't receive packet: " .. response)
|
||||
return false, "Couldn't receive packet: " .. response
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse the response
|
||||
local status, parsed = dhcp_parse(response, transaction_id)
|
||||
|
||||
134
nselib/dhcp6.lua
134
nselib/dhcp6.lua
@@ -64,9 +64,9 @@ DHCP6.OptionTypes = {
|
||||
|
||||
-- DHCP6 options
|
||||
DHCP6.Option = {
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_ELAPSED_TIME] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param time in ms since last request
|
||||
-- @return o new instance of class
|
||||
@@ -82,7 +82,7 @@ DHCP6.Option = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts option to a string
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(self)
|
||||
@@ -94,11 +94,11 @@ DHCP6.Option = {
|
||||
end
|
||||
return bin.pack(">SP", self.type, data)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_CLIENTID] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param mac string containing the mac address
|
||||
-- @param duid number the duid of the client
|
||||
@@ -117,7 +117,7 @@ DHCP6.Option = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parse the data string and create an instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return opt new instance of option
|
||||
@@ -133,15 +133,15 @@ DHCP6.Option = {
|
||||
opt.time = opt.time + os.time({year=2000, day=1, month=1, hour=0, min=0, sec=0})
|
||||
return opt
|
||||
end,
|
||||
|
||||
|
||||
-- Converts option to a string
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(self)
|
||||
local data = bin.pack(">SSIA", self.duid, self.hwtype, self.time, self.mac)
|
||||
return bin.pack(">SP", self.type, data)
|
||||
end,
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_SERVERID] = {
|
||||
-- Create a new class instance
|
||||
-- @param mac string containing the mac address
|
||||
@@ -150,7 +150,7 @@ DHCP6.Option = {
|
||||
-- @param time number time since 2000-01-01 00:00:00
|
||||
-- @return o new instance of class
|
||||
new = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].new(...) end,
|
||||
|
||||
|
||||
-- Parse the data string and create an instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return opt new instance of option
|
||||
@@ -160,9 +160,9 @@ DHCP6.Option = {
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(...) return DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENTID].__tostring(...) end,
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_STATUS_CODE] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param code number containing the error code
|
||||
-- @param msg string containing the error message
|
||||
@@ -184,15 +184,15 @@ DHCP6.Option = {
|
||||
parse = function(data)
|
||||
local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_STATUS_CODE]:new()
|
||||
local pos
|
||||
|
||||
|
||||
pos, opt.code, opt.msg = bin.unpack(">SA" .. (#data - 2), data)
|
||||
return opt
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_DNS_SERVERS] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param servers table containing DNS servers
|
||||
-- @return o new instance of class
|
||||
@@ -212,7 +212,7 @@ DHCP6.Option = {
|
||||
parse = function(data)
|
||||
local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DNS_SERVERS]:new()
|
||||
local pos, count = 1, #data/16
|
||||
|
||||
|
||||
for i=1,count do
|
||||
local srv
|
||||
pos, srv = bin.unpack(">B16", data, pos)
|
||||
@@ -220,7 +220,7 @@ DHCP6.Option = {
|
||||
end
|
||||
return opt
|
||||
end,
|
||||
|
||||
|
||||
-- Converts option to a string
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(self)
|
||||
@@ -232,9 +232,9 @@ DHCP6.Option = {
|
||||
return data
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param domain table containing the search domains
|
||||
-- @return o new instance of class
|
||||
@@ -254,7 +254,7 @@ DHCP6.Option = {
|
||||
parse = function(data)
|
||||
local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_DOMAIN_LIST]:new()
|
||||
local pos = 1
|
||||
|
||||
|
||||
repeat
|
||||
local domain = {}
|
||||
repeat
|
||||
@@ -269,11 +269,11 @@ DHCP6.Option = {
|
||||
return opt
|
||||
end,
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_IA_PD] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param iad number containing iad
|
||||
-- @param t1 number containing t1
|
||||
@@ -292,18 +292,18 @@ DHCP6.Option = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts option to a string
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(self)
|
||||
local data = bin.pack(">IIIA", self.iaid, self.t1, self.t2, self.options)
|
||||
return bin.pack(">SP", self.type, data)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_IA_NA] = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param iad number containing iad
|
||||
-- @param t1 number containing t1
|
||||
@@ -322,21 +322,21 @@ DHCP6.Option = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parse the data string and create an instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return opt new instance of option
|
||||
parse = function(data)
|
||||
local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_IA_NA]:new()
|
||||
local pos
|
||||
|
||||
|
||||
pos, opt.iaid, opt.t1, opt.t2 = bin.unpack(">III", data)
|
||||
|
||||
|
||||
-- do we have any options
|
||||
while ( pos < #data ) do
|
||||
local typ, len, ipv6, pref_lt, valid_lt, options
|
||||
pos, typ, len = bin.unpack(">SS", data, pos)
|
||||
|
||||
|
||||
if ( 5 == DHCP6.OptionTypes.OPTION_IAADDR ) then
|
||||
local addr = { type = DHCP6.OptionTypes.OPTION_IAADDR }
|
||||
pos, addr.ipv6, addr.pref_lt, addr.valid_lt = bin.unpack(">A16II", data, pos)
|
||||
@@ -344,10 +344,10 @@ DHCP6.Option = {
|
||||
else
|
||||
pos = pos + len
|
||||
end
|
||||
end
|
||||
end
|
||||
return opt
|
||||
end,
|
||||
|
||||
|
||||
-- Converts option to a string
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(self)
|
||||
@@ -355,9 +355,9 @@ DHCP6.Option = {
|
||||
|
||||
-- TODO: we don't cover self.options here, we should probably add that
|
||||
return bin.pack(">SP", self.type, data)
|
||||
end,
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = {
|
||||
|
||||
-- Create a new class instance
|
||||
@@ -372,7 +372,7 @@ DHCP6.Option = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parse the data string and create an instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return opt new instance of option
|
||||
@@ -387,7 +387,7 @@ DHCP6.Option = {
|
||||
return opt
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_CLIENT_FQDN] = {
|
||||
|
||||
-- Create a new class instance
|
||||
@@ -402,7 +402,7 @@ DHCP6.Option = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parse the data string and create an instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return opt new instance of option
|
||||
@@ -410,7 +410,7 @@ DHCP6.Option = {
|
||||
local opt = DHCP6.Option[DHCP6.OptionTypes.OPTION_CLIENT_FQDN]:new()
|
||||
local pos = 2
|
||||
local pieces = {}
|
||||
|
||||
|
||||
repeat
|
||||
local tmp
|
||||
pos, tmp = bin.unpack("p", data, pos)
|
||||
@@ -419,14 +419,14 @@ DHCP6.Option = {
|
||||
opt.fqdn = stdnse.strjoin(".", pieces)
|
||||
return opt
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DHCP6.Request = {
|
||||
|
||||
|
||||
-- Create a new class instance
|
||||
-- @param msgtype number containing the message type
|
||||
-- @param xid number containing the transaction id
|
||||
@@ -442,13 +442,13 @@ DHCP6.Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Adds a new DHCP6 option to the request
|
||||
-- @param opt instance of object to add to the request
|
||||
addOption = function(self, opt)
|
||||
table.insert(self.opts, opt)
|
||||
end,
|
||||
|
||||
|
||||
-- Converts option to a string
|
||||
-- @return str string containing the class instance as string
|
||||
__tostring = function(self)
|
||||
@@ -465,7 +465,7 @@ DHCP6.Request = {
|
||||
|
||||
-- The Response class handles responses from the server
|
||||
DHCP6.Response = {
|
||||
|
||||
|
||||
-- Creates a new instance of the response class
|
||||
-- @param msgtype number containing the type of DHCP6 message
|
||||
-- @param xid number containing the transaction ID
|
||||
@@ -479,7 +479,7 @@ DHCP6.Response = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parse the data string and create an instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return opt new instance of option
|
||||
@@ -488,7 +488,7 @@ DHCP6.Response = {
|
||||
local pos, tmp = bin.unpack(">I", data)
|
||||
|
||||
resp.msgtype = bit.band(tmp, 0xFF000000)
|
||||
resp.msgtype = bit.rshift(resp.msgtype, 24)
|
||||
resp.msgtype = bit.rshift(resp.msgtype, 24)
|
||||
resp.xid = bit.band(tmp, 0x00FFFFFF)
|
||||
while( pos < #data ) do
|
||||
local opt = {}
|
||||
@@ -507,7 +507,7 @@ DHCP6.Response = {
|
||||
end
|
||||
return resp
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Table of option to string converters
|
||||
@@ -517,7 +517,7 @@ DHCP6.Response = {
|
||||
-- TODO: These functions could eventually be moved to a method in it's
|
||||
-- respective class.
|
||||
OptionToString = {
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_CLIENTID] = function(opt)
|
||||
local HWTYPE_ETHER = 1
|
||||
if ( HWTYPE_ETHER == opt.hwtype ) then
|
||||
@@ -527,12 +527,12 @@ OptionToString = {
|
||||
return "Client identifier", ("MAC: %s; Time: %s"):format(mac, tm)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_SERVERID] = function(opt)
|
||||
local topic, str = OptionToString[DHCP6.OptionTypes.OPTION_CLIENTID](opt)
|
||||
return "Server identifier", str
|
||||
end,
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_IA_NA] = function(opt)
|
||||
if ( opt.options and 1 == #opt.options ) then
|
||||
local ipv6 = opt.options[1].ipv6
|
||||
@@ -541,7 +541,7 @@ OptionToString = {
|
||||
return "Non-temporary Address", ipv6
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_DNS_SERVERS] = function(opt)
|
||||
local servers = {}
|
||||
for _, srv in ipairs(opt.servers) do
|
||||
@@ -550,15 +550,15 @@ OptionToString = {
|
||||
end
|
||||
return "DNS Servers", stdnse.strjoin(",", servers)
|
||||
end,
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_DOMAIN_LIST] = function(opt)
|
||||
return "Domain Search", stdnse.strjoin(", ", opt.domains)
|
||||
end,
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_STATUS_CODE] = function(opt)
|
||||
return "Error", ("Code: %d; Message: %s"):format(opt.code, opt.msg)
|
||||
end,
|
||||
|
||||
|
||||
[DHCP6.OptionTypes.OPTION_SNTP_SERVERS] = function(opt)
|
||||
return "NTP Servers", stdnse.strjoin(", ", opt.servers)
|
||||
end,
|
||||
@@ -566,7 +566,7 @@ OptionToString = {
|
||||
|
||||
-- The Helper class serves as the main interface to scripts
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new Helper class instance
|
||||
-- @param iface string containing the interface name
|
||||
-- @param options table containing any options, currently
|
||||
@@ -579,7 +579,7 @@ Helper = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
|
||||
local info, err = nmap.get_interface_info(iface)
|
||||
-- if we faile to get interface info, don't return a helper
|
||||
-- this is true on OS X for interfaces like: p2p0 and vboxnet0
|
||||
@@ -592,17 +592,17 @@ Helper = {
|
||||
o.socket:set_timeout(o.options.timeout or 5000)
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a DHCP6 Solicit message to the server, essentiall requesting a new
|
||||
-- IPv6 non-temporary address
|
||||
-- @return table of results suitable for use with
|
||||
-- @return table of results suitable for use with
|
||||
-- <code>stdnse.format_output</code>
|
||||
solicit = function(self)
|
||||
local req = DHCP6.Request:new( DHCP6.Type.SOLICIT )
|
||||
local option = DHCP6.Option
|
||||
req:addOption(option[DHCP6.OptionTypes.OPTION_ELAPSED_TIME]:new())
|
||||
req:addOption(option[DHCP6.OptionTypes.OPTION_CLIENTID]:new(self.mac))
|
||||
|
||||
|
||||
local iaid = select(2, bin.unpack(">I", self.mac:sub(3)))
|
||||
req:addOption(option[DHCP6.OptionTypes.OPTION_IA_NA]:new(iaid, 3600, 5400))
|
||||
|
||||
@@ -624,7 +624,7 @@ Helper = {
|
||||
return false, "Failed to receive DHCP6 request from server"
|
||||
end
|
||||
|
||||
resp = DHCP6.Response.parse(data)
|
||||
resp = DHCP6.Response.parse(data)
|
||||
if ( not(resp) ) then
|
||||
return false, "Failed to decode DHCP6 response from server"
|
||||
end
|
||||
@@ -636,7 +636,7 @@ Helper = {
|
||||
|
||||
local result, result_options = {}, { name = "Options" }
|
||||
local resptype = DHCP6.TypeStr[resp.msgtype] or ("Unknown (%d)"):format(resp.msgtype)
|
||||
|
||||
|
||||
table.insert(result, ("Message type: %s"):format(resptype))
|
||||
table.insert(result, ("Transaction id: %d"):format(resp.xid))
|
||||
|
||||
@@ -652,7 +652,7 @@ Helper = {
|
||||
end
|
||||
table.insert(result, result_options)
|
||||
return true, result
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -257,7 +257,7 @@ end
|
||||
-- * <code>subnet</code>: table, if set perform a edns-client-subnet lookup. The table should contain the fields:
|
||||
-- <code>family</code> - string can be either inet or inet6
|
||||
-- <code>address</code> - string containing the originating subnet IP address
|
||||
-- <code>mask</code> - number containing the number of subnet bits
|
||||
-- <code>mask</code> - number containing the number of subnet bits
|
||||
-- @return <code>true</code> if a dns response was received and contained an answer of the requested type,
|
||||
-- or the decoded dns response was requested (retPkt) and is being returned - or <code>false</code> otherwise.
|
||||
-- @return String answer of the requested type, table of answers or a String error message of one of the following:
|
||||
@@ -1055,10 +1055,10 @@ decoder[types.NSEC3] = function (entry, data, pos)
|
||||
entry.NSEC3.dname = entry.dname
|
||||
entry.NSEC3.salt, entry.NSEC3.hash = {}, {}
|
||||
|
||||
np, entry.NSEC3.hash.alg,flags,entry.NSEC3.iterations = bin.unpack(">CBS", data, np)
|
||||
np, entry.NSEC3.hash.alg,flags,entry.NSEC3.iterations = bin.unpack(">CBS", data, np)
|
||||
-- do we even need to decode these do we care about opt out?
|
||||
-- entry.NSEC3.flags = decodeFlagsNSEC3(flags)
|
||||
|
||||
|
||||
np, entry.NSEC3.salt.bin = bin.unpack(">p", data, np)
|
||||
_, entry.NSEC3.salt.hex = bin.unpack("H" .. #entry.NSEC3.salt.bin, entry.NSEC3.salt.bin)
|
||||
|
||||
@@ -1156,7 +1156,7 @@ decoder[types.TXT] =
|
||||
-- @param entry RR in packet.
|
||||
-- @param data Complete encoded DNS packet.
|
||||
-- @param pos Position in packet after RR.
|
||||
decoder[types.OPT] =
|
||||
decoder[types.OPT] =
|
||||
function(entry, data, pos)
|
||||
local np = pos - #entry.data - 6
|
||||
local opt = { bufsize = entry.class }
|
||||
@@ -1367,14 +1367,14 @@ end
|
||||
-- @param client_subnet table containing the following fields
|
||||
-- <code>family</code> - 1 IPv4, 2 - IPv6
|
||||
-- <code>mask</code> - byte containing the length of the subnet mask
|
||||
-- <code>address</code> - string containing the IP address
|
||||
-- <code>address</code> - string containing the IP address
|
||||
function addClientSubnet(pkt,Z,subnet)
|
||||
local udp_payload_size = 4096
|
||||
local code = 20730 -- temporary option-code http://comments.gmane.org/gmane.ietf.dnsext/19776
|
||||
local scope_mask = 0 -- In requests, it MUST be set to 0 see draft
|
||||
local data = bin.pack(">SCCA",subnet.family or 1,subnet.mask,scope_mask,ipOps.ip_to_str(subnet.address))
|
||||
local opt = bin.pack(">SS",code, #data) .. data
|
||||
addOPT(pkt,Z,opt)
|
||||
addOPT(pkt,Z,opt)
|
||||
end
|
||||
|
||||
---
|
||||
@@ -1384,7 +1384,7 @@ end
|
||||
function addNSID (pkt,Z)
|
||||
local udp_payload_size = 4096
|
||||
local opt = bin.pack(">SS",3, 0) -- nsid data
|
||||
addOPT(pkt,Z,opt)
|
||||
addOPT(pkt,Z,opt)
|
||||
end
|
||||
|
||||
---
|
||||
|
||||
@@ -49,7 +49,7 @@ _ENV = stdnse.module("dnsbl", stdnse.seeall)
|
||||
-- the TXT record, this argument and check can be omitted.
|
||||
-- When the short mode is used, the function should return a table containing
|
||||
-- the <code>state</code> field, or nil if the IP wasn't listed. When long
|
||||
-- mode is used, the function should return additional information using the
|
||||
-- mode is used, the function should return additional information using the
|
||||
-- <code>details</code> field. Eg:
|
||||
-- return { state = "SPAM" } -- short mode
|
||||
-- return { state = "PROXY", details = {
|
||||
@@ -69,11 +69,11 @@ _ENV = stdnse.module("dnsbl", stdnse.seeall)
|
||||
-- and description of the arguments that provide the configuration/options.
|
||||
-- If this function isn't specified, the library will assume the service
|
||||
-- doesn't require configuration.
|
||||
--
|
||||
--
|
||||
SERVICES = {
|
||||
|
||||
|
||||
SPAM = {
|
||||
|
||||
|
||||
["dnsbl.inps.de"] = {
|
||||
-- This service supports both long and short <code>mode</code>
|
||||
ns_type = {
|
||||
@@ -114,7 +114,7 @@ SERVICES = {
|
||||
end,
|
||||
},
|
||||
|
||||
["spam.dnsbl.sorbs.net"] = {
|
||||
["spam.dnsbl.sorbs.net"] = {
|
||||
ns_type = {
|
||||
["short"] = "A"
|
||||
},
|
||||
@@ -144,7 +144,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["all.spamrats.com"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -160,7 +160,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["list.quorum.to"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -172,11 +172,11 @@ SERVICES = {
|
||||
-- this service appears to return 127.0.0.0 when the service is
|
||||
-- "blocked because it has never been seen to send mail".
|
||||
-- This would essentially return every host as SPAM and we
|
||||
-- don't want that.
|
||||
return ( ( r[1] and r[1] ~= "127.0.0.0" ) and { state = "SPAM" } )
|
||||
-- don't want that.
|
||||
return ( ( r[1] and r[1] ~= "127.0.0.0" ) and { state = "SPAM" } )
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
["sbl.spamhaus.org"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -190,9 +190,9 @@ SERVICES = {
|
||||
["127.0.0.3"] = "SPAM",
|
||||
}
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["bl.spamcop.net"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -207,7 +207,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["dnsbl.ahbl.org"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -226,7 +226,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["l2.apews.org"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -241,11 +241,11 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
PROXY = {
|
||||
|
||||
|
||||
["dnsbl.tornevall.org"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -275,12 +275,12 @@ SERVICES = {
|
||||
if ( bit.band( code, k ) == k ) then
|
||||
table.insert(result, v)
|
||||
end
|
||||
end
|
||||
end
|
||||
return { state = "PROXY", details = result }
|
||||
end
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["ip-port.exitlist.torproject.org"] = {
|
||||
configuration = {
|
||||
["port"] = "the port to which the target can relay to",
|
||||
@@ -364,7 +364,7 @@ SERVICES = {
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["dnsbl.ahbl.org"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -379,7 +379,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["http.dnsbl.sorbs.net"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -394,7 +394,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["socks.dnsbl.sorbs.net"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -409,7 +409,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
["misc.dnsbl.sorbs.net"] = {
|
||||
new = function(self, ip, mode, config)
|
||||
local o = { ip = ip, mode = mode, config = config }
|
||||
@@ -424,7 +424,7 @@ SERVICES = {
|
||||
return ( r[1] and responses[r[1]] ) and { state = responses[r[1]] }
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
ATTACK = {
|
||||
@@ -485,7 +485,7 @@ SERVICES = {
|
||||
}
|
||||
|
||||
local result = {}
|
||||
|
||||
|
||||
-- Search engines are a special case.
|
||||
if ( octet4 == 0 ) then
|
||||
table.insert(result, ("Search engine: %s"):format(
|
||||
@@ -495,7 +495,7 @@ SERVICES = {
|
||||
octet2))
|
||||
table.insert(result, ("Threat score: %i"):format(
|
||||
octet3))
|
||||
|
||||
|
||||
local activity = {}
|
||||
activity['name'] = "Activity"
|
||||
-- Suspicious activity
|
||||
@@ -512,7 +512,7 @@ SERVICES = {
|
||||
if ( bit.band(octet4, 4) == 4) then
|
||||
table.insert(activity, "Comment spammer")
|
||||
end
|
||||
|
||||
|
||||
table.insert(result, activity)
|
||||
end
|
||||
|
||||
@@ -558,13 +558,13 @@ SERVICES = {
|
||||
end,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new Helper instance
|
||||
-- @param category string containing a valid DNSBL service category
|
||||
-- @param mode string (short|long) specifying whether short or long
|
||||
@@ -577,7 +577,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Lists all DNSBL services for the category
|
||||
-- @return services table of service names
|
||||
listServices = function(self)
|
||||
@@ -597,19 +597,19 @@ Helper = {
|
||||
table.insert(services, name)
|
||||
end
|
||||
end
|
||||
return services
|
||||
return services
|
||||
end,
|
||||
|
||||
|
||||
-- Validates the filter set by setFilter to make sure it contains only
|
||||
-- valid service names.
|
||||
-- @return status boolean, true on success false on failure
|
||||
-- @return err string containing an error message on failure
|
||||
validateFilter = function(self)
|
||||
|
||||
|
||||
if ( not(self.filterstr) ) then
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
local all = SERVICES[self.category]
|
||||
self.filter = {}
|
||||
for _, f in pairs(stdnse.strsplit(",%s*", self.filterstr)) do
|
||||
@@ -621,19 +621,19 @@ Helper = {
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Sets a new service filter to choose only a limited subset of services
|
||||
-- within a category.
|
||||
-- @param filter string containing a comma separated list of service names
|
||||
setFilter = function(self, filter) self.filterstr = filter end,
|
||||
|
||||
|
||||
-- Gets a list of filtered services, or all services if no filter is in use
|
||||
-- @return services table containing a list of services
|
||||
getServices = function(self)
|
||||
if ( not(self:validateFilter()) ) then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
if ( self.filter ) then
|
||||
local filtered = {}
|
||||
for name, svc in pairs(SERVICES[self.category]) do
|
||||
@@ -646,7 +646,7 @@ Helper = {
|
||||
return SERVICES[self.category]
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
doQuery = function(self, ip, name, svc, answers)
|
||||
|
||||
local condvar = nmap.condvar(answers)
|
||||
@@ -676,10 +676,10 @@ Helper = {
|
||||
else
|
||||
stdnse.print_debug("Query function returned nothing, skipping '%s'", name)
|
||||
end
|
||||
|
||||
|
||||
condvar "signal"
|
||||
end,
|
||||
|
||||
|
||||
-- Runs the DNS blacklist check for the given IP against all non-filtered
|
||||
-- services in the given category.
|
||||
-- @param ip string containing the IP address to check
|
||||
@@ -687,7 +687,7 @@ Helper = {
|
||||
checkBL = function(self, ip)
|
||||
local result, answers, threads = {}, {}, {}
|
||||
local condvar = nmap.condvar(answers)
|
||||
|
||||
|
||||
for name, svc in pairs(self:getServices()) do
|
||||
local co = stdnse.new_thread(self.doQuery, self, ip, name, svc, answers)
|
||||
threads[co] = true
|
||||
@@ -701,7 +701,7 @@ Helper = {
|
||||
condvar "wait"
|
||||
end
|
||||
until( next(threads) == nil )
|
||||
|
||||
|
||||
for name, answer in pairs(answers) do
|
||||
local status, answer, svc = answer.status, answer.answer, answer.svc
|
||||
if ( status ) then
|
||||
@@ -710,7 +710,7 @@ Helper = {
|
||||
local resp = ( #answer > 0 and ("UNKNOWN (%s)"):format(answer[1]) or "UNKNOWN" )
|
||||
stdnse.print_debug(2, ("%s received %s"):format(name, resp))
|
||||
end
|
||||
|
||||
|
||||
if ( svc_result ) then
|
||||
table.insert(result, { name = name, result = svc_result })
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
-- helper:setMulticast(true)
|
||||
-- return stdnse.format_output(helper:queryServices())
|
||||
-- </code>
|
||||
--
|
||||
--
|
||||
-- This next snipplet queries a specific host for the same information:
|
||||
-- <code>
|
||||
-- local helper = dnssd.Helper:new( host, port )
|
||||
@@ -57,7 +57,7 @@ Util = {
|
||||
ipCompare = function(a, b)
|
||||
return ipOps.compare_ip(a, "lt", b)
|
||||
end,
|
||||
|
||||
|
||||
--- Function used to compare discovered DNS services so they can be sorted
|
||||
--
|
||||
-- @param a table containing first item
|
||||
@@ -74,8 +74,8 @@ Util = {
|
||||
end
|
||||
return false
|
||||
end,
|
||||
|
||||
--- Creates a service host table
|
||||
|
||||
--- Creates a service host table
|
||||
--
|
||||
-- ['_ftp._tcp.local'] = {10.10.10.10,20.20.20.20}
|
||||
-- ['_http._tcp.local'] = {30.30.30.30,40.40.40.40}
|
||||
@@ -96,7 +96,7 @@ Util = {
|
||||
|
||||
return services
|
||||
end,
|
||||
|
||||
|
||||
--- Creates a unique list of services
|
||||
--
|
||||
-- @param response containing a single or multiple responses from
|
||||
@@ -104,7 +104,7 @@ Util = {
|
||||
-- @return array of strings containing service names
|
||||
getUniqueServices = function( response )
|
||||
local services = {}
|
||||
|
||||
|
||||
for _, r in ipairs(response) do
|
||||
if ( r.output ) then
|
||||
for _, svc in ipairs(r.output) do services[svc] = true end
|
||||
@@ -112,10 +112,10 @@ Util = {
|
||||
services[r] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return services
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the amount of currenlty active threads
|
||||
--
|
||||
-- @param threads table containing the list of threads
|
||||
@@ -132,7 +132,7 @@ Util = {
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
Comm = {
|
||||
@@ -189,7 +189,7 @@ Comm = {
|
||||
queryService = function( host, port, svc, multiple, svcresponse )
|
||||
local condvar = nmap.condvar(svcresponse)
|
||||
local status, response = dns.query( svc, { port = port, host = host, dtype="PTR", retPkt=true, retAll=true, multiple=multiple, sendCount=1, timeout=2000} )
|
||||
if not status then
|
||||
if not status then
|
||||
stdnse.print_debug("Failed to query service: %s; Error: %s", svc, response)
|
||||
return
|
||||
end
|
||||
@@ -266,7 +266,7 @@ Comm = {
|
||||
table.insert( result, service )
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Query the mDNS resolvers for a list of their services
|
||||
--
|
||||
-- @param host table as received by the action function
|
||||
@@ -286,7 +286,7 @@ Comm = {
|
||||
end
|
||||
return dns.query( "_services._dns-sd._udp.local", { port = port, host = ( host.ip or host ), dtype="PTR", retAll=true, multiple=multiple, sendCount=sendCount, timeout=timeout } )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Helper = {
|
||||
@@ -305,8 +305,8 @@ Helper = {
|
||||
o.mcast = false
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Instructs the helper to use unconnected sockets supporting multicast
|
||||
--
|
||||
-- @param mcast boolean true if multicast is to be used, false otherwise
|
||||
@@ -314,12 +314,12 @@ Helper = {
|
||||
assert( type(mcast)=="boolean", "mcast has to be either true or false")
|
||||
self.mcast = mcast
|
||||
end,
|
||||
|
||||
|
||||
--- Performs a DNS-SD query against a host
|
||||
--
|
||||
-- @param host table as received by the action function
|
||||
-- @param port number specifying the port to connect to
|
||||
-- @param service string or table with the service(s) to query eg.
|
||||
-- @param service string or table with the service(s) to query eg.
|
||||
-- _ssh._tcp.local, _afpovertcp._tcp.local
|
||||
-- if nil defaults to _services._dns-sd._udp.local (all)
|
||||
-- @param mcast boolean true if a multicast query is to be done
|
||||
@@ -333,7 +333,7 @@ Helper = {
|
||||
local family = nmap.address_family()
|
||||
local host = mcast and (family=="inet6" and "ff02::fb" or "224.0.0.251") or self.host
|
||||
local service = service or stdnse.get_script_args('dnssd.services')
|
||||
|
||||
|
||||
if ( not(service) ) then
|
||||
status, response = Comm.queryAllServices( host, port, mcast )
|
||||
if ( not(status) ) then return status, response end
|
||||
@@ -346,12 +346,12 @@ Helper = {
|
||||
end
|
||||
|
||||
response = Util.getUniqueServices(response)
|
||||
|
||||
|
||||
local svcresponse = {}
|
||||
local condvar = nmap.condvar( svcresponse )
|
||||
local threads = {}
|
||||
|
||||
for svc in pairs(response) do
|
||||
|
||||
for svc in pairs(response) do
|
||||
local co = stdnse.new_thread( Comm.queryService, (host.ip or host), port, svc, mcast, svcresponse )
|
||||
threads[co] = true
|
||||
end
|
||||
@@ -374,7 +374,7 @@ Helper = {
|
||||
Comm.decodeRecords( response, result )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( mcast ) then
|
||||
-- Restructure and build our output table
|
||||
for ip, svctbl in pairs( ipsvctbl ) do
|
||||
@@ -387,10 +387,10 @@ Helper = {
|
||||
else
|
||||
-- sort the tables per port
|
||||
table.sort( result, Util.serviceCompare )
|
||||
end
|
||||
end
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
156
nselib/drda.lua
156
nselib/drda.lua
@@ -39,7 +39,7 @@
|
||||
-- </code>
|
||||
--
|
||||
-- The implementation is based on packet dumps and the excellent decoding
|
||||
-- provided by Wireshark.
|
||||
-- provided by Wireshark.
|
||||
--
|
||||
-- There is some documentation at
|
||||
-- http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/topic/com.ibm.db29.doc.drda/db2z_drda.htm.
|
||||
@@ -119,7 +119,7 @@ SecMec =
|
||||
}
|
||||
|
||||
DRDAPacket = {
|
||||
|
||||
|
||||
new = function(self, drda_array)
|
||||
local o = {
|
||||
drda_array = drda_array,
|
||||
@@ -137,11 +137,11 @@ DRDAPacket = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
getDRDA = function( self, n )
|
||||
return ( #self.drda_array >= n ) and self.drda_array[n] or nil
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function( self )
|
||||
local data = ""
|
||||
-- do some DDM fixup in here
|
||||
@@ -156,12 +156,12 @@ DRDAPacket = {
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Distributed Relational Database Architecture (DRDA) Class
|
||||
DRDA = {
|
||||
|
||||
|
||||
new = function(self, ddm)
|
||||
local o = {
|
||||
Parameters = {},
|
||||
@@ -171,8 +171,8 @@ DRDA = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
--- Sets the DDM
|
||||
|
||||
--- Sets the DDM
|
||||
--
|
||||
-- @param ddm DDM to assign to the DRDA
|
||||
-- @return status boolean true on success, false on failure
|
||||
@@ -183,7 +183,7 @@ DRDA = {
|
||||
self.DDM = ddm
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Adds a DRDA parameter to the table
|
||||
--
|
||||
-- @param param DRDAParam containing the parameter to add to the table
|
||||
@@ -198,16 +198,16 @@ DRDA = {
|
||||
stdnse.print_debug("drda.DRDA.addParameter: Param cannot be nil")
|
||||
return false, "Param cannot be nil"
|
||||
end
|
||||
|
||||
|
||||
table.insert(self.Parameters, param)
|
||||
|
||||
|
||||
-- update the DDM length fields
|
||||
self.DDM.Length = self.DDM.Length + param.Length
|
||||
self.DDM.Length2 = self.DDM.Length2 + param.Length
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Gets a parameter from the DRDA parameter table
|
||||
--
|
||||
-- @param codepoint number containing the parameter type ro retrieve
|
||||
@@ -217,26 +217,26 @@ DRDA = {
|
||||
if ( v.CodePoint == codepoint ) then
|
||||
return v
|
||||
end
|
||||
end
|
||||
end
|
||||
return
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DRDA class to a string
|
||||
--
|
||||
-- @return data containing the object instance
|
||||
-- @return data containing the object instance
|
||||
__tostring = function(self)
|
||||
if ( not(self.DDM) ) then
|
||||
stdnse.print_debug("drda.DRDA.toString: DDM cannot be nil")
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local data = bin.pack(">SCCSSS", self.DDM.Length, self.DDM.Magic, self.DDM.Format, self.DDM.CorelId, self.DDM.Length2, self.DDM.CodePoint )
|
||||
for k,v in ipairs(self.Parameters) do
|
||||
data = data .. tostring(v)
|
||||
end
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends the DRDA over the db2socket
|
||||
--
|
||||
-- @param db2socket DB2Socket over which to send the data
|
||||
@@ -245,27 +245,27 @@ DRDA = {
|
||||
send = function( self, db2socket )
|
||||
return db2socket:send( tostring(self) )
|
||||
end,
|
||||
|
||||
|
||||
--- Receives data from the db2socket and builds a DRDA object
|
||||
--
|
||||
-- @param db2socket from which to read data
|
||||
-- @return Status (true or false).
|
||||
-- @return Data (if status is true) or error string (if status is false).
|
||||
-- @return Data (if status is true) or error string (if status is false).
|
||||
receive = function( self, db2socket )
|
||||
local DDM_SIZE = 10
|
||||
local pos = 1
|
||||
|
||||
|
||||
-- first read atleast enough so that we can populate the DDM
|
||||
local status, data = db2socket:receive_buf( match.numbytes(DDM_SIZE), true )
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug("drda.DRDA.receive: %s", data)
|
||||
return false, ("Failed to read at least %d bytes from socket"):format(DDM_SIZE)
|
||||
end
|
||||
|
||||
|
||||
local ddm = DDM:new()
|
||||
ddm:fromString( data )
|
||||
self:setDDM( ddm )
|
||||
|
||||
|
||||
status, data = db2socket:receive_buf( match.numbytes(ddm.Length - 10), true )
|
||||
if ( not(status) ) then
|
||||
return false, ("Failed to read the remaining %d bytes of the DRDA message")
|
||||
@@ -277,7 +277,7 @@ DRDA = {
|
||||
pos = param:fromString( data, pos )
|
||||
self:addParameter( param )
|
||||
until ( #data <= pos )
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
@@ -285,12 +285,12 @@ DRDA = {
|
||||
|
||||
-- The DRDAParameter class implements the DRDA parameters
|
||||
DRDAParameter = {
|
||||
|
||||
|
||||
--- DRDA Parameter constructor
|
||||
--
|
||||
-- @param codepoint number containing the codepoint value
|
||||
-- @param data string containing the data portion of the DRDA parameter
|
||||
-- @return o DRDAParameter object
|
||||
-- @return o DRDAParameter object
|
||||
new = function(self, codepoint, data)
|
||||
local o = {
|
||||
CodePoint = codepoint,
|
||||
@@ -306,13 +306,13 @@ DRDAParameter = {
|
||||
--
|
||||
-- @return data string containing the DRDA Parameter
|
||||
__tostring = function( self )
|
||||
local data = bin.pack(">SS", self.Length, self.CodePoint )
|
||||
local data = bin.pack(">SS", self.Length, self.CodePoint )
|
||||
if ( self.Data ) then
|
||||
data = data .. bin.pack("A", self.Data)
|
||||
end
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
--- Builds a DRDA Parameter from a string
|
||||
--
|
||||
-- @param data string from which to build the DRDA Parameter
|
||||
@@ -332,14 +332,14 @@ DRDAParameter = {
|
||||
end
|
||||
return pos
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the data portion of the parameter as an ASCII string
|
||||
--
|
||||
-- @return str containing the data portion of the DRDA parameter as ASCII
|
||||
getDataAsASCII = function( self )
|
||||
return StringUtil.toASCII( self.Data )
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the data in EBCDIC format
|
||||
--
|
||||
-- @return str containing the data portion of the DRDA parameter in EBCDIC
|
||||
@@ -351,7 +351,7 @@ DRDAParameter = {
|
||||
|
||||
-- Distributed data management (DDM)
|
||||
DDM = {
|
||||
|
||||
|
||||
Formats =
|
||||
{
|
||||
RESERVED = 0x80,
|
||||
@@ -359,7 +359,7 @@ DDM = {
|
||||
CONTINUE = 0x20,
|
||||
SAME_CORRELATION = 0x10,
|
||||
},
|
||||
|
||||
|
||||
Length = 10,
|
||||
Magic = 0xD0,
|
||||
Format = 0x41,
|
||||
@@ -388,32 +388,32 @@ DDM = {
|
||||
__tostring = function( self )
|
||||
return bin.pack(">SCCSSS", self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint)
|
||||
end,
|
||||
|
||||
|
||||
--- Constructs a DDM object from a string
|
||||
--
|
||||
-- @param str containing the data from which to construct the object
|
||||
fromString = function( self, str )
|
||||
local DDM_SIZE = 10
|
||||
local pos = 1
|
||||
|
||||
|
||||
if ( #str < DDM_SIZE ) then
|
||||
return -1, ("drda.DDM.fromString: str was less than DDM_SIZE (%d)"):format( DDM_SIZE )
|
||||
end
|
||||
|
||||
|
||||
pos, self.Length, self.Magic, self.Format, self.CorelId, self.Length2, self.CodePoint = bin.unpack( ">SCCSSS", str )
|
||||
return pos
|
||||
end,
|
||||
|
||||
|
||||
--- Verifiers if there are additional DRDA's following
|
||||
--
|
||||
-- @return true if the DRDA is to be chained, false if it's the last one
|
||||
-- @return true if the DRDA is to be chained, false if it's the last one
|
||||
isChained = function( self )
|
||||
if ( bit.band( self.Format, DDM.Formats.CHAINED ) == DDM.Formats.CHAINED ) then
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end,
|
||||
|
||||
|
||||
--- Set the DRDA as chained (more following)
|
||||
--
|
||||
-- @param chained boolean true if more DRDA's are following
|
||||
@@ -424,11 +424,11 @@ DDM = {
|
||||
self.Format = bit.bor( self.Format, self.Formats.CHAINED )
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- static DRDA packet construction class
|
||||
Command =
|
||||
Command =
|
||||
{
|
||||
--- Builds an EXCSAT DRDA packet
|
||||
--
|
||||
@@ -440,7 +440,7 @@ Command =
|
||||
-- @return drda DRDA instance
|
||||
EXCSAT = function( extname, srvname, rellev, mgrlvlls, srvclass )
|
||||
local drda = DRDA:new( DDM:new( CodePoint.EXCSAT ) )
|
||||
|
||||
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.EXTNAM, StringUtil.toEBCDIC( extname ) ) )
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.SRVNAM, StringUtil.toEBCDIC( srvname ) ) )
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.SRVRLSLV, StringUtil.toEBCDIC( rellev ) ) )
|
||||
@@ -449,7 +449,7 @@ Command =
|
||||
|
||||
return drda
|
||||
end,
|
||||
|
||||
|
||||
--- Builds an ACCSEC DRDA packet
|
||||
--
|
||||
-- @param secmec number containing the security mechanism ID
|
||||
@@ -459,7 +459,7 @@ Command =
|
||||
local drda = DRDA:new( DDM:new( CodePoint.ACCSEC ) )
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec ))
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) ))
|
||||
|
||||
|
||||
return drda
|
||||
end,
|
||||
|
||||
@@ -469,17 +469,17 @@ Command =
|
||||
-- @param database string containing the database name
|
||||
-- @param username string
|
||||
-- @param password string
|
||||
-- @return drda DRDA instance
|
||||
-- @return drda DRDA instance
|
||||
SECCHK = function( secmec, database, username, password )
|
||||
local drda = DRDA:new( DDM:new( CodePoint.SECCHK ) )
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.SECMEC, secmec ))
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) ))
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.USRID, StringUtil.toEBCDIC(username) ) )
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.PASSWORD, StringUtil.toEBCDIC(password) ) )
|
||||
|
||||
|
||||
return drda
|
||||
end,
|
||||
|
||||
|
||||
--- Builds an ACCRDB DRDA packet
|
||||
--
|
||||
-- @param database string containing the database name
|
||||
@@ -487,7 +487,7 @@ Command =
|
||||
-- @param prdid string containing the product id
|
||||
-- @param typdefnam string containing the data type definition name
|
||||
-- @param typdefovr string containing the data type definition override
|
||||
-- @return drda DRDA instance
|
||||
-- @return drda DRDA instance
|
||||
ACCRDB = function( database, rdbaccl, prdid, prddata, typdefnam, crrtkn, typdefovr )
|
||||
local drda = DRDA:new( DDM:new( CodePoint.ACCRDB ) )
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.RDBNAM, StringUtil.toEBCDIC(StringUtil.padWithChar(database,' ', 18)) ) )
|
||||
@@ -510,10 +510,10 @@ Command =
|
||||
if( typdefovr ) then
|
||||
drda:addParameter( DRDAParameter:new( CodePoint.TYPDEFOVR, typdefovr ) )
|
||||
end
|
||||
|
||||
|
||||
return drda
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -526,13 +526,13 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Connect to the DB2 host
|
||||
--
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
-- @return Status (true or false).
|
||||
-- @return Error code (if status is false).
|
||||
-- @return Error code (if status is false).
|
||||
connect = function( self, host, port )
|
||||
self.comm = Comm:new( host, port )
|
||||
return self.comm:connect()
|
||||
@@ -541,23 +541,23 @@ Helper = {
|
||||
--- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
-- @return Error code (if status is false).
|
||||
-- @return Error code (if status is false).
|
||||
close = function( self )
|
||||
self.comm:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Returns Server Information (name, platform, version)
|
||||
--
|
||||
-- @return table containing <code>extname</code>, <code>srvclass</code>,
|
||||
-- @return table containing <code>extname</code>, <code>srvclass</code>,
|
||||
-- <code>srvname</code> and <code>prodrel</code>
|
||||
getServerInfo = function( self )
|
||||
local mgrlvlls = bin.pack("H", "1403000724070008240f00081440000814740008")
|
||||
local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" )
|
||||
local response, param, err
|
||||
|
||||
|
||||
local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat } ) )
|
||||
if ( not(status) ) then return false, err end
|
||||
|
||||
|
||||
local drda = packet:getDRDAByCodePoint( CodePoint.EXCSATRD )
|
||||
if ( drda ) then
|
||||
response = {}
|
||||
@@ -578,12 +578,12 @@ Helper = {
|
||||
response.prodrel = param:getDataAsASCII()
|
||||
end
|
||||
else
|
||||
return false, "The response contained no EXCSATRD"
|
||||
return false, "The response contained no EXCSATRD"
|
||||
end
|
||||
|
||||
return true, response
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
--- Login to DB2 database server
|
||||
--
|
||||
-- @param database containing the name of the database
|
||||
@@ -596,37 +596,37 @@ Helper = {
|
||||
local secmec, prdid = "\00\03", "JCC03010"
|
||||
local tdovr = bin.pack("H", "0006119c04b80006119d04b00006119e04b8")
|
||||
local crrtkn= bin.pack("H", "d5c6f0f0f0f0f0f14bc3c6f4c4012a11168414")
|
||||
|
||||
|
||||
local drda_excsat = Command.EXCSAT( "", "", "", mgrlvlls, "" )
|
||||
local drda_accsec = Command.ACCSEC( secmec, database )
|
||||
local drda_secchk = Command.SECCHK( secmec, database, username, password )
|
||||
local drda_accrdb = Command.ACCRDB( database, string.char(0x24,0x07), "DNC10060", nil, "QTDSQLASC", crrtkn, tdovr)
|
||||
|
||||
|
||||
local status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_excsat, drda_accsec } ) )
|
||||
if( not(status) ) then return false, packet end
|
||||
|
||||
|
||||
if ( packet:getDRDAByCodePoint( CodePoint.RDBNFNRM ) or
|
||||
packet:getDRDAByCodePoint( CodePoint.RDBAFLRM ) ) then
|
||||
stdnse.print_debug("drda.Helper.login: ERROR: RDB not found")
|
||||
return false, "ERROR: Database not found"
|
||||
end
|
||||
|
||||
|
||||
local drda = packet:getDRDAByCodePoint( CodePoint.ACCSECRD )
|
||||
if ( not(drda) ) then
|
||||
return false, "ERROR: Response did not contain any valid security mechanisms"
|
||||
end
|
||||
|
||||
|
||||
local param = drda:getParameter( CodePoint.SECMEC )
|
||||
if ( not(param) ) then
|
||||
stdnse.print_debug("drda.Helper.login: ERROR: Response did not contain any valid security mechanisms")
|
||||
return false, "ERROR: Response did not contain any valid security mechanisms"
|
||||
end
|
||||
|
||||
|
||||
if ( select(2, bin.unpack(">S", param:getData())) ~= SecMec.USER_PASSWORD ) then
|
||||
stdnse.print_debug("drda.Helper.login: ERROR: Securite Mechanism not supported")
|
||||
return false, "ERROR: Security mechanism not supported"
|
||||
end
|
||||
|
||||
|
||||
status, packet = self.comm:exchDRDAPacket( DRDAPacket:new( { drda_secchk, drda_accrdb } ) )
|
||||
if( not(status) ) then return false, "ERROR: Login failed" end
|
||||
|
||||
@@ -657,12 +657,12 @@ Helper = {
|
||||
end
|
||||
return false, "ERROR: Login failed"
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The communication class
|
||||
Comm = {
|
||||
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = {
|
||||
host = host,
|
||||
@@ -673,18 +673,18 @@ Comm = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
connect = function(self)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
recvDRDA = function( self )
|
||||
local drda_tbl = {}
|
||||
|
||||
|
||||
repeat
|
||||
local drda = DRDA:new()
|
||||
local status, err = drda:receive( self.socket )
|
||||
@@ -695,7 +695,7 @@ Comm = {
|
||||
until ( not(drda.DDM:isChained()) )
|
||||
return true, drda_tbl
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a packet to the server and receives the response
|
||||
--
|
||||
-- @param DRDAPacket
|
||||
@@ -704,12 +704,12 @@ Comm = {
|
||||
exchDRDAPacket = function( self, packet )
|
||||
local drda, err
|
||||
local status, err = self.socket:send( tostring(packet) )
|
||||
|
||||
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", err )
|
||||
return false, ("ERROR: DB2Socket error: %s"):format( err )
|
||||
end
|
||||
|
||||
|
||||
status, drda = self:recvDRDA()
|
||||
if( not(status) ) then
|
||||
stdnse.print_debug("drda.Helper.login: ERROR: DB2Socket error: %s", drda )
|
||||
@@ -717,7 +717,7 @@ Comm = {
|
||||
end
|
||||
return true, DRDAPacket:new( drda )
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- EBCDIC/ASCII Conversion tables
|
||||
@@ -766,7 +766,7 @@ StringUtil =
|
||||
-- @return string containing ASCII value
|
||||
toASCII = function( ebcdic )
|
||||
local ret = ""
|
||||
|
||||
|
||||
for i=1, #ebcdic do
|
||||
local val = ebcdic.byte(ebcdic,i) + 1
|
||||
ret = ret .. e2a_tbl:sub(val, val)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
---
|
||||
---
|
||||
-- EAP (Extensible Authentication Protocol) library supporting a
|
||||
-- limited subset of features.
|
||||
--
|
||||
@@ -16,8 +16,8 @@
|
||||
-- <code>
|
||||
-- pcap:pcap_open(iface.device, 512, true, "ether proto 0x888e")
|
||||
-- ...
|
||||
-- local _, _, l2_data, l3_data, _ = pcap:pcap_receive()
|
||||
-- local packet = eap.parse(l2_data .. l3_data3)
|
||||
-- local _, _, l2_data, l3_data, _ = pcap:pcap_receive()
|
||||
-- local packet = eap.parse(l2_data .. l3_data3)
|
||||
-- if packet then
|
||||
-- if packet.eap.type == eap.eap_t.IDENTITY and packet.eap.code == eap.code_t.REQUEST then
|
||||
-- eap.send_identity_response(iface, packet.eap.id, "anonymous")
|
||||
@@ -74,7 +74,7 @@ code_t = {
|
||||
}
|
||||
|
||||
code_str = {
|
||||
[1] = "Request",
|
||||
[1] = "Request",
|
||||
[2] = "Response",
|
||||
[3] = "Success",
|
||||
[4] = "Failure",
|
||||
@@ -155,13 +155,13 @@ eap_str = {
|
||||
local make_eapol = function (arg)
|
||||
if not arg.type then arg.type = eapol_t.PACKET end
|
||||
if not arg.version then arg.version = 1 end
|
||||
if not arg.payload then arg.payload = "" end
|
||||
if not arg.payload then arg.payload = "" end
|
||||
if not arg.src then return nil end
|
||||
|
||||
local p = packet.Frame:new()
|
||||
p.mac_src = arg.src
|
||||
p.mac_dst = packet.mactobin(ETHER_BROADCAST)
|
||||
p.ether_type = ETHER_TYPE_EAPOL
|
||||
p.ether_type = ETHER_TYPE_EAPOL
|
||||
|
||||
local bin_payload = bin.pack(">A",arg.payload)
|
||||
p.buf = bin.pack("C",arg.version) .. bin.pack("C",arg.type) .. bin.pack(">S",bin_payload:len()).. bin_payload
|
||||
@@ -179,14 +179,14 @@ local make_eap = function (arg)
|
||||
|
||||
local bin_payload = bin.pack(">A",arg.payload)
|
||||
arg.header.payload = bin.pack("C",arg.code) .. bin.pack("C",arg.id) .. bin.pack(">S",bin_payload:len() + EAP_HEADER_SIZE).. bin.pack("C",arg.type) .. bin_payload
|
||||
|
||||
|
||||
local v = make_eapol(arg.header)
|
||||
stdnse.print_debug(2, "make eapol %s", arg.header.src)
|
||||
|
||||
|
||||
return v
|
||||
end
|
||||
|
||||
parse = function (packet)
|
||||
parse = function (packet)
|
||||
local tb = {}
|
||||
local _
|
||||
|
||||
@@ -199,14 +199,14 @@ parse = function (packet)
|
||||
-- parsing eapol header
|
||||
_, tb.version, tb.type, tb.length = bin.unpack(">CCS", packet, ETHER_HEADER_SIZE + 1)
|
||||
|
||||
stdnse.print_debug(1, "mac_src: %s, mac_dest: %s, ether_type: 0x%X",
|
||||
stdnse.print_debug(1, "mac_src: %s, mac_dest: %s, ether_type: 0x%X",
|
||||
tb.mac_src_str, tb.mac_dst_str, tb.ether_type)
|
||||
|
||||
if tb.ether_type ~= ETHER_TYPE_EAPOL_N then return nil, "not an eapol packet" end
|
||||
if tb.ether_type ~= ETHER_TYPE_EAPOL_N then return nil, "not an eapol packet" end
|
||||
|
||||
stdnse.print_debug(2, "version: %X, type: %s, length: 0x%X",
|
||||
stdnse.print_debug(2, "version: %X, type: %s, length: 0x%X",
|
||||
tb.version, eapol_str[tb.type] or "unknown",
|
||||
tb.length)
|
||||
tb.length)
|
||||
|
||||
tb.eap = {}
|
||||
|
||||
@@ -214,7 +214,7 @@ parse = function (packet)
|
||||
-- parsing body
|
||||
|
||||
_, tb.eap.code, tb.eap.id, tb.eap.length, tb.eap.type = bin.unpack(">CCSC", packet,
|
||||
ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + 1)
|
||||
ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + 1)
|
||||
stdnse.print_debug(2, "code: %s, id: 0x%X, length: 0x%X, type: %s",
|
||||
code_str[tb.eap.code] or "unknown",
|
||||
tb.eap.id, tb.eap.length, eap_str[tb.eap.type] or "unknown" )
|
||||
@@ -233,7 +233,7 @@ parse = function (packet)
|
||||
end
|
||||
|
||||
if tb.length > 5 and tb.eap.type == eap_t.MD5 then
|
||||
_, tb.eap.body.challenge = bin.unpack("p", packet, ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + EAP_HEADER_SIZE + 1)
|
||||
_, tb.eap.body.challenge = bin.unpack("p", packet, ETHER_HEADER_SIZE + EAPOL_HEADER_SIZE + EAP_HEADER_SIZE + 1)
|
||||
end
|
||||
|
||||
return tb
|
||||
@@ -244,9 +244,9 @@ send_identity_response = function (iface, id, identity)
|
||||
if not iface then
|
||||
stdnse.print_debug(1, "no interface given")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local dnet = nmap.new_dnet()
|
||||
local tb = {src = iface.mac, type = eapol_t.PACKET}
|
||||
local response = make_eap{header = tb, code = code_t.RESPONSE, type = eap_t.IDENTITY, id = id, payload = identity}
|
||||
|
||||
@@ -260,9 +260,9 @@ send_nak_response = function (iface, id, auth)
|
||||
if not iface then
|
||||
stdnse.print_debug(1, "no interface given")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local dnet = nmap.new_dnet()
|
||||
local tb = {src = iface.mac, type = eapol_t.PACKET}
|
||||
local response = make_eap{header = tb, code = code_t.RESPONSE, type = eap_t.NAK, id = id, payload = bin.pack("C",auth)}
|
||||
|
||||
@@ -273,19 +273,19 @@ end
|
||||
|
||||
|
||||
send_start = function (iface)
|
||||
|
||||
|
||||
if not iface then
|
||||
stdnse.print_debug(1, "no interface given")
|
||||
return
|
||||
end
|
||||
|
||||
local dnet = nmap.new_dnet()
|
||||
local start = make_eapol{type = eapol_t.START, src = iface.mac}
|
||||
local start = make_eapol{type = eapol_t.START, src = iface.mac}
|
||||
|
||||
dnet:ethernet_open(iface.device)
|
||||
dnet:ethernet_send(start)
|
||||
dnet:ethernet_close()
|
||||
|
||||
|
||||
end
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -70,7 +70,7 @@ EIGRP = {
|
||||
-- @param Checksum integer EIGRP packet checksum. Calculated automatically
|
||||
-- if not manually set.
|
||||
-- @param Table TLVs table.
|
||||
-- @return o Instance of EIGRP
|
||||
-- @return o Instance of EIGRP
|
||||
new = function(self, opcode, as, routerid, flags, seq, ack, checksum, tlvs)
|
||||
local o = {
|
||||
ver = 2,
|
||||
@@ -253,7 +253,7 @@ EIGRP = {
|
||||
-- @return status true if tlvtype is a routing information tlv.
|
||||
isRoutingTLV = function(tlvtype)
|
||||
if tlvtype == 0x101 or tlvtype == 0x102
|
||||
or tlvtype == 0x103 or tlvtype == 0x104
|
||||
or tlvtype == 0x103 or tlvtype == 0x104
|
||||
or tlvtype == 0x402 or tlvtype == 0x403
|
||||
or tlvtype == 0x404 then
|
||||
return true
|
||||
@@ -273,7 +273,7 @@ EIGRP = {
|
||||
--- Sets the EIGRP packet checksum
|
||||
-- @param integer checksum Checksum to set.
|
||||
setChecksum = function(self, checksum)
|
||||
self.checksum = checksum
|
||||
self.checksum = checksum
|
||||
end,
|
||||
--- Sets the EIGRP packet flags field.
|
||||
-- @param flags Flags integer value.
|
||||
@@ -288,19 +288,19 @@ EIGRP = {
|
||||
--- Sets the EIGRP Packet acknowledge field.
|
||||
-- @param ack EIGRP acknowledge.
|
||||
setAcknowledge = function(self, ack)
|
||||
self.ack = ack
|
||||
self.ack = ack
|
||||
end,
|
||||
--- Sets the EIGRP Packet Virtual Router ID.
|
||||
--- Sets the EIGRP Packet Virtual Router ID.
|
||||
-- @param routerid EIGRP Virtual Router ID.
|
||||
setRouterID = function(self, routerid)
|
||||
self.routerid = routerid
|
||||
self.routerid = routerid
|
||||
end,
|
||||
--- Sets the EIGRP Packet Autonomous System.
|
||||
-- @param as EIGRP A.S.
|
||||
setAS = function(self, as)
|
||||
self.as = as
|
||||
end,
|
||||
--- Sets the EIGRP Packet tlvs
|
||||
--- Sets the EIGRP Packet tlvs
|
||||
-- @param tlvs table of EIGRP tlvs.
|
||||
setTlvs = function(self, tlvs)
|
||||
self.tlvs = tlvs
|
||||
@@ -314,7 +314,7 @@ EIGRP = {
|
||||
-- If checksum not manually.
|
||||
-- set to 0, then calculate it later
|
||||
if self.checksum then
|
||||
data = data .. bin.pack(">S", self.checksum)
|
||||
data = data .. bin.pack(">S", self.checksum)
|
||||
else
|
||||
data = data .. bin.pack(">S", 0x0000) -- Calculated later.
|
||||
end
|
||||
@@ -322,7 +322,7 @@ EIGRP = {
|
||||
data = data .. bin.pack(">I", self.seq) -- Sequence 0
|
||||
data = data .. bin.pack(">I", self.ack) -- Acknowledge 0
|
||||
data = data .. bin.pack(">S", self.routerid) -- Virtual Router ID 0
|
||||
data = data .. bin.pack(">S", self.as) -- Autonomous system
|
||||
data = data .. bin.pack(">S", self.as) -- Autonomous system
|
||||
for _, tlv in pairs(self.tlvs) do
|
||||
if tlv.type == TLV.PARAM then
|
||||
data = data .. bin.pack(">S", TLV.PARAM)
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
-- Formula functions for various calculations.
|
||||
--
|
||||
-- The library lets scripts to use common mathematical functions to compute percentages,
|
||||
-- averages, entropy, randomness and other calculations. Scripts that generate statistics
|
||||
-- averages, entropy, randomness and other calculations. Scripts that generate statistics
|
||||
-- and metrics can also make use of this library.
|
||||
--
|
||||
-- Functions included:
|
||||
--
|
||||
-- <code>calcPwdEntropy</code> - Calculate the entropy of a password. A random
|
||||
-- password's information entropy, H, is given by the formula: H = L * (logN) / (log2),
|
||||
-- where N is the number of possible symbols and L is the number of symbols in the
|
||||
-- password. Based on https://en.wikipedia.org/wiki/Password_strength
|
||||
-- password's information entropy, H, is given by the formula: H = L * (logN) / (log2),
|
||||
-- where N is the number of possible symbols and L is the number of symbols in the
|
||||
-- password. Based on https://en.wikipedia.org/wiki/Password_strength
|
||||
--
|
||||
-- <code>looksRandom</code> - Returns true if the value looks random.
|
||||
--
|
||||
|
||||
208
nselib/giop.lua
208
nselib/giop.lua
@@ -24,7 +24,7 @@
|
||||
-- - A helper class that provides easy access to the rest of the library
|
||||
--
|
||||
-- o Socket
|
||||
-- - This is a copy of the DB2Socket class which provides fundamental
|
||||
-- - This is a copy of the DB2Socket class which provides fundamental
|
||||
-- buffering
|
||||
--
|
||||
--
|
||||
@@ -66,24 +66,24 @@ _ENV = stdnse.module("giop", stdnse.seeall)
|
||||
|
||||
-- A bunch of constants
|
||||
Constants = {
|
||||
|
||||
|
||||
SyncScope = {
|
||||
WITH_TARGET = 3,
|
||||
},
|
||||
|
||||
|
||||
ServiceContext = {
|
||||
CODESETS = 1,
|
||||
SENDING_CONTEXT_RUNTIME = 6,
|
||||
NEO_FIRST_SERVICE_CONTEXT = 1313165056,
|
||||
},
|
||||
|
||||
|
||||
ReplyStatus = {
|
||||
SYSTEM_EXCEPTION = 2,
|
||||
},
|
||||
|
||||
|
||||
VERSION_1_0 = 1,
|
||||
VERSION_1_2 = 0x0201,
|
||||
|
||||
|
||||
NAMESERVICE = "NameService\0",
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ Constants = {
|
||||
Packet = {}
|
||||
|
||||
Packet.GIOP = {
|
||||
|
||||
|
||||
magic = "GIOP",
|
||||
version = 0x0001,
|
||||
byte_order = 0,
|
||||
@@ -110,19 +110,19 @@ Packet.GIOP = {
|
||||
o.size = data and #data or 0
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the class to a string suitable to send over the socket
|
||||
--
|
||||
-- @return string containing the instance data
|
||||
-- @return string containing the instance data
|
||||
__tostring = function( self )
|
||||
return bin.pack("<ASCC>IA", self.magic, self.version, self.byte_order, self.type, self.size, self.data )
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the packet version
|
||||
--
|
||||
-- @param version number containing the version to use
|
||||
setVersion = function( self, version ) self.version = version end,
|
||||
|
||||
|
||||
--- Receives the packet over the socket
|
||||
--
|
||||
-- @param socket containing the already connected socket
|
||||
@@ -131,14 +131,14 @@ Packet.GIOP = {
|
||||
recv = function( self, socket )
|
||||
local status, data = socket:recv( 12 )
|
||||
local pos
|
||||
|
||||
|
||||
if ( not(status) ) then return false, "Failed to read Packet.GIOP" end
|
||||
|
||||
pos, self.magic, self.version, self.byte_order,
|
||||
|
||||
pos, self.magic, self.version, self.byte_order,
|
||||
self.type = bin.unpack("<A4SCC", data )
|
||||
|
||||
|
||||
pos, self.size = bin.unpack( ( self.byte_order == 0 and ">" or "<") .. "I", data, pos )
|
||||
|
||||
|
||||
status, data = socket:recv( self.size )
|
||||
if ( not(status) ) then return false, "Failed to read Packet.GIOP" end
|
||||
|
||||
@@ -164,10 +164,10 @@ ServiceContext = {
|
||||
o.pad = pad
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the class to a string suitable to send over the socket
|
||||
--
|
||||
-- @return string containing the instance data
|
||||
-- @return string containing the instance data
|
||||
__tostring = function( self )
|
||||
if ( self.pad ) then
|
||||
return bin.pack(">IIAS", self.id, #self.data, self.data, self.pad)
|
||||
@@ -178,7 +178,7 @@ ServiceContext = {
|
||||
}
|
||||
|
||||
--- Creates a SendingContextRuntime
|
||||
SendingContextRuntime =
|
||||
SendingContextRuntime =
|
||||
{
|
||||
--- Creates a SendingContextRuntime
|
||||
--
|
||||
@@ -188,12 +188,12 @@ SendingContextRuntime =
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.data = bin.pack(">HIAH",
|
||||
[[
|
||||
o.data = bin.pack(">HIAH",
|
||||
[[
|
||||
000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746
|
||||
578742f436f6465426173653a312e300000000001000000000000006e000102
|
||||
00
|
||||
]], #lhost + 1, lhost .. "\0",
|
||||
]], #lhost + 1, lhost .. "\0",
|
||||
[[
|
||||
00ec5100000019afabcb000000000249765d6900000008000000000000000014
|
||||
0000000000000200000001000000200000000000010001000000020501000100
|
||||
@@ -201,15 +201,15 @@ SendingContextRuntime =
|
||||
]] )
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the class to a string suitable to send over the socket
|
||||
--
|
||||
-- @return string containing the instance data
|
||||
-- @return string containing the instance data
|
||||
__tostring = function( self ) return self.data end,
|
||||
}
|
||||
|
||||
Packet.GIOP.reply = {
|
||||
|
||||
|
||||
--- Creates a new Packet.GIOP.reply instance
|
||||
--
|
||||
-- @return obj a new Packet.GIOP.get instance
|
||||
@@ -221,7 +221,7 @@ Packet.GIOP.reply = {
|
||||
self.GIOP = Packet.GIOP:new()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Receives a Packet.GIOP.reply from the socket
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
@@ -231,7 +231,7 @@ Packet.GIOP.reply = {
|
||||
local status, err = self.GIOP:recv( socket )
|
||||
local pos, tmp
|
||||
local bo = ( self.GIOP.byte_order == 0 and ">" or "<")
|
||||
|
||||
|
||||
if( not(status) ) then return false, err end
|
||||
|
||||
if ( self.GIOP.version == Constants.VERSION_1_2 ) then
|
||||
@@ -248,24 +248,24 @@ Packet.GIOP.reply = {
|
||||
if ( i ~= tmp ) then pos = pos + 2 end
|
||||
table.insert( self.sc, ServiceContext:new( ctx_id, ctx_data ) )
|
||||
end
|
||||
|
||||
|
||||
if ( self.GIOP.version == Constants.VERSION_1_0 ) then
|
||||
pos, self.request_id, self.reply_status, self.stub_data = bin.unpack( bo .. "IIA" .. ( #self.GIOP.data - pos - 8 ), self.GIOP.data, pos )
|
||||
elseif ( pos < #self.GIOP.data ) then
|
||||
pos, self.data = bin.unpack("A" .. (#self.GIOP.data - pos), self.GIOP.data, pos )
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Packet.GIOP.get = {
|
||||
|
||||
|
||||
resp_expected = 1,
|
||||
key_length = 4,
|
||||
princ_len = 0,
|
||||
|
||||
|
||||
--- Creates a new Packet.GIOP._is_a instance
|
||||
--
|
||||
-- @param id the packet identifier
|
||||
@@ -283,21 +283,21 @@ Packet.GIOP.get = {
|
||||
o.sc = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Creates and adds a service context to the packet
|
||||
--
|
||||
-- @param id number containing the context id
|
||||
-- @param data the service context data
|
||||
-- @param pad [optional] number used to pad after the service context
|
||||
addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end,
|
||||
|
||||
|
||||
--- Converts the class to a string suitable to send over the socket
|
||||
--
|
||||
-- @return string containing the packet data
|
||||
-- @return string containing the packet data
|
||||
__tostring = function( self )
|
||||
local data = bin.pack(">I", #self.sc)
|
||||
local pad = 0
|
||||
|
||||
|
||||
for i=1, #self.sc do
|
||||
local tmp = tostring( self.sc[i])
|
||||
data = data .. bin.pack("A", tmp )
|
||||
@@ -308,12 +308,12 @@ Packet.GIOP.get = {
|
||||
|
||||
return tostring( Packet.GIOP:new( 0, data ) )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Packet.GIOP._is_a =
|
||||
{
|
||||
|
||||
|
||||
--- Creates a new Packet.GIOP._is_a instance
|
||||
--
|
||||
-- @param id the packet identifier
|
||||
@@ -326,46 +326,46 @@ Packet.GIOP._is_a =
|
||||
self.__index = self
|
||||
o.op = "_is_a\0"
|
||||
o.id = id
|
||||
o.target_addr = 0 -- KeyAddr
|
||||
o.target_addr = 0 -- KeyAddr
|
||||
o.key_addr = key_addr
|
||||
o.flags = flags or Constants.SyncScope.WITH_TARGET -- SyncScope WITH_TARGET
|
||||
o.sc = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Creates and adds a service context to the packet
|
||||
--
|
||||
-- @param id number containing the context id
|
||||
-- @param data the service context data
|
||||
-- @param pad [optional] number used to pad after the service context
|
||||
addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end,
|
||||
|
||||
|
||||
--- Converts the class to a string suitable to send over the socket
|
||||
--
|
||||
-- @return string containing the packet data
|
||||
-- @return string containing the packet data
|
||||
__tostring = function( self )
|
||||
local TYPE_ID = "IDL:omg.org/CosNaming/NamingContextExt:1.0\0"
|
||||
local RESERVED = 0
|
||||
local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 0
|
||||
local data = bin.pack(">ICCCCSSIAIASI", self.id, self.flags, RESERVED, RESERVED, RESERVED, self.target_addr,
|
||||
UNKNOWN, #self.key_addr, self.key_addr, #self.op, self.op, UNKNOWN2, #self.sc )
|
||||
|
||||
|
||||
for i=1, #self.sc do
|
||||
local tmp = tostring( self.sc[i])
|
||||
data = data .. bin.pack("A", tmp )
|
||||
end
|
||||
|
||||
|
||||
data = data .. bin.pack(">IA", #TYPE_ID, TYPE_ID)
|
||||
|
||||
local packet = Packet.GIOP:new( 0, data )
|
||||
packet:setVersion( Constants.VERSION_1_2 )
|
||||
|
||||
return tostring( packet )
|
||||
|
||||
return tostring( packet )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Packet.GIOP.list =
|
||||
Packet.GIOP.list =
|
||||
{
|
||||
--- Creates a new Packet.GIOP.list instance
|
||||
--
|
||||
@@ -387,7 +387,7 @@ Packet.GIOP.list =
|
||||
o.sc = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Creates and adds a service context to the packet
|
||||
--
|
||||
-- @param id number containing the context id
|
||||
@@ -397,33 +397,33 @@ Packet.GIOP.list =
|
||||
|
||||
--- Converts the class to a string suitable to send over the socket
|
||||
--
|
||||
-- @return string containing the packet data
|
||||
-- @return string containing the packet data
|
||||
__tostring = function( self )
|
||||
local RESERVED = 0
|
||||
local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 6
|
||||
|
||||
|
||||
local data = bin.pack(">ICCCCSSIAIACCCI", self.id, self.flags, RESERVED, RESERVED,
|
||||
RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr,
|
||||
RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr,
|
||||
#self.op, self.op, RESERVED, RESERVED, UNKNOWN2, #self.sc )
|
||||
|
||||
|
||||
for i=1, #self.sc do
|
||||
local tmp = tostring( self.sc[i])
|
||||
data = data .. bin.pack("A", tmp )
|
||||
end
|
||||
|
||||
|
||||
data = data .. bin.pack(">II", UNKNOWN3, self.how_many )
|
||||
local packet = Packet.GIOP:new( 0, data )
|
||||
packet:setVersion( Constants.VERSION_1_2 )
|
||||
|
||||
|
||||
return tostring( packet )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- A socket implementation that provides fundamental buffering and allows for
|
||||
-- reading of an exact number of bytes, instead of atleast ...
|
||||
Socket =
|
||||
{
|
||||
{
|
||||
new = function(self, socket)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -438,7 +438,7 @@ Socket =
|
||||
if (not(status)) then return false, "Error failed to get socket information" end
|
||||
return true, lhost
|
||||
end,
|
||||
|
||||
|
||||
--- Establishes a connection.
|
||||
--
|
||||
-- @param hostid Hostname or IP address.
|
||||
@@ -450,7 +450,7 @@ Socket =
|
||||
local status = self.Socket:set_timeout(10000)
|
||||
return self.Socket:connect( hostid, port, protocol )
|
||||
end,
|
||||
|
||||
|
||||
--- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -458,7 +458,7 @@ Socket =
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
@@ -468,9 +468,9 @@ Socket =
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
@@ -478,13 +478,13 @@ Socket =
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -496,10 +496,10 @@ Socket =
|
||||
|
||||
-- Static class containing various message decoders
|
||||
MessageDecoder = {
|
||||
|
||||
|
||||
--- Decodes a get response
|
||||
--
|
||||
-- @param packet the GIOP packet as recieved by the comm
|
||||
-- @param packet the GIOP packet as recieved by the comm
|
||||
-- <code>exchGIOPPacket</code> function
|
||||
-- @return status true on success, false on failure
|
||||
-- @return table containing <code>ip</code> and <code>ctx</code>
|
||||
@@ -507,7 +507,7 @@ MessageDecoder = {
|
||||
local bo = ( packet.GIOP.byte_order == 0 and ">" or "<")
|
||||
local pos, len = bin.unpack(bo .. "I", packet.stub_data)
|
||||
local ip, ctx
|
||||
|
||||
|
||||
pos = pos + len + 16
|
||||
|
||||
pos, len = bin.unpack(bo .. "I", packet.stub_data, pos)
|
||||
@@ -516,13 +516,13 @@ MessageDecoder = {
|
||||
pos = pos + 3
|
||||
pos, len = bin.unpack(bo .. "I", packet.stub_data, pos)
|
||||
pos, ctx = bin.unpack( bo .. "A" .. len, packet.stub_data, pos)
|
||||
|
||||
|
||||
return true, { ip = ip, ctx = ctx}
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes a _is_a response (not implemented)
|
||||
--
|
||||
-- @param packet the GIOP packet as recieved by the comm
|
||||
-- @param packet the GIOP packet as recieved by the comm
|
||||
-- <code>exchGIOPPacket</code> function
|
||||
-- @return status, always true
|
||||
["_is_a"] = function( packet )
|
||||
@@ -531,7 +531,7 @@ MessageDecoder = {
|
||||
|
||||
--- Decodes a list response
|
||||
--
|
||||
-- @param packet the GIOP packet as recieved by the comm
|
||||
-- @param packet the GIOP packet as recieved by the comm
|
||||
-- <code>exchGIOPPacket</code> function
|
||||
-- @return status true on success, false on failure
|
||||
-- @return table containing <code>id</code>, <code>kind</code> and
|
||||
@@ -540,40 +540,40 @@ MessageDecoder = {
|
||||
local bo = ( packet.GIOP.byte_order == 0 and ">" or "<")
|
||||
local pos, seq_len = bin.unpack( bo .. "I", packet.data, 7)
|
||||
local objs = {}
|
||||
|
||||
|
||||
for i=1, seq_len do
|
||||
local seq_len_of_bind_name
|
||||
local len, name
|
||||
local obj = {}
|
||||
|
||||
|
||||
pos, seq_len_of_bind_name = bin.unpack( bo .. "I", packet.data, pos)
|
||||
if ( seq_len_of_bind_name ~= 1 ) then return false, "Sequence length of Binding_binding_name was greater than 1" end
|
||||
|
||||
|
||||
pos, len = bin.unpack( bo .. "I", packet.data, pos )
|
||||
pos, obj.id = bin.unpack( "A" .. len - 1, packet.data, pos )
|
||||
|
||||
-- Account for terminating zero
|
||||
pos = pos + 1
|
||||
|
||||
|
||||
-- Account for undecoded data
|
||||
pos = pos + ( ( len % 4 > 0 ) and ( 4 - ( len % 4 ) ) or 0 )
|
||||
pos = pos + 3
|
||||
|
||||
|
||||
pos, obj.kind = bin.unpack("C", packet.data, pos)
|
||||
|
||||
|
||||
-- Account for undecoded data
|
||||
pos = pos + 4
|
||||
pos, obj.enum = bin.unpack( bo .. "I", packet.data, pos )
|
||||
table.insert( objs, obj )
|
||||
end
|
||||
|
||||
|
||||
return true, objs
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Comm = {
|
||||
|
||||
|
||||
--- Creates a new Comm instance
|
||||
--
|
||||
-- @param socket containing a buffered socket connected to the server
|
||||
@@ -585,7 +585,7 @@ Comm = {
|
||||
o.socket = socket
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sends and recieves a GIOP packet
|
||||
--
|
||||
-- @param packet containing a Packet.* object, the object must
|
||||
@@ -597,7 +597,7 @@ Comm = {
|
||||
local status, err = self.socket:send( tostring(packet) )
|
||||
local op = packet.op:sub(1, -2)
|
||||
local data
|
||||
|
||||
|
||||
if( not(status) ) then return false, err end
|
||||
packet = Packet.GIOP.reply:new()
|
||||
|
||||
@@ -609,15 +609,15 @@ Comm = {
|
||||
else
|
||||
return false, ("No message decoder for op (%s)"):format(op)
|
||||
end
|
||||
|
||||
|
||||
return status, data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
new = function(self, host, port )
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -627,48 +627,48 @@ Helper = {
|
||||
o.socket = Socket:new()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
GetNamingContext = function( self )
|
||||
local packet = Packet.GIOP.get:new( 5, 0x494e4954, bin.pack(">IA", #Constants.NAMESERVICE, Constants.NAMESERVICE) )
|
||||
local status, ctx, lhost, pos, len, bo, tmp
|
||||
|
||||
|
||||
packet:addServiceContext( 17, string.char(0x00, 0x02), 0)
|
||||
packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0)
|
||||
packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 )
|
||||
|
||||
|
||||
status, packet = self.comm:exchGIOPPacket( packet )
|
||||
if( not(status) ) then return status, packet end
|
||||
|
||||
|
||||
return true, packet.ctx
|
||||
end,
|
||||
|
||||
|
||||
ListObjects = function( self, keyaddr )
|
||||
-- SyncScope WITH_TARGET
|
||||
local packet = Packet.GIOP._is_a:new( 5, Constants.SyncScope.WITH_TARGET, keyaddr )
|
||||
local status, err, lhost
|
||||
|
||||
|
||||
status, err = self:Reconnect()
|
||||
if( not(status) ) then return false, err end
|
||||
|
||||
|
||||
packet:addServiceContext( 17, "\0\2", 0x000d)
|
||||
packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" )
|
||||
packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x5d69)
|
||||
packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 )
|
||||
|
||||
|
||||
status, packet = self.comm:exchGIOPPacket( packet )
|
||||
if( not(status) ) then return status, packet end
|
||||
|
||||
|
||||
packet = Packet.GIOP.list:new( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, Constants.SyncScope.WITH_TARGET, keyaddr, 1000 )
|
||||
packet:addServiceContext( 17, "\0\2", 0x000d)
|
||||
packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" )
|
||||
packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, string.char(0x00, 0x14), 0x9c9b)
|
||||
|
||||
|
||||
status, packet = self.comm:exchGIOPPacket( packet )
|
||||
if( not(status) ) then return status, packet end
|
||||
|
||||
|
||||
return true, packet
|
||||
end,
|
||||
|
||||
|
||||
--- Connects and performs protocol negotiation with the Oracle server
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
@@ -679,22 +679,22 @@ Helper = {
|
||||
self.comm = Comm:new( self.socket )
|
||||
|
||||
status, self.lhost = self.socket:getSrcIp()
|
||||
if ( not(status) ) then
|
||||
if ( not(status) ) then
|
||||
self.socket:close()
|
||||
return false, self.lhost
|
||||
return false, self.lhost
|
||||
end
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
Close = function( self )
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
Reconnect = function( self )
|
||||
local status = self:Close()
|
||||
if( not(status) ) then return false, "Failed to close socket" end
|
||||
|
||||
|
||||
status = self:Connect()
|
||||
if( not(status) ) then return false, "Failed to re-connect socket" end
|
||||
|
||||
|
||||
@@ -13,27 +13,27 @@ _ENV = stdnse.module("gps", stdnse.seeall)
|
||||
--
|
||||
--
|
||||
|
||||
NMEA = {
|
||||
NMEA = {
|
||||
|
||||
-- Parser for the RMC sentence
|
||||
RMC = {
|
||||
|
||||
|
||||
parse = function(str)
|
||||
|
||||
|
||||
local time, status, latitude, ns_indicator, longitude,
|
||||
ew_indicator, speed, course, date, variation,
|
||||
ew_variation, checksum = str:match("^%$GPRMC,([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^,]*),([^%*]*)(.*)$")
|
||||
|
||||
|
||||
if ( not(latitude) or not(longitude) ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local deg, min = latitude:match("^(..)(.*)$")
|
||||
if ( not(deg) or not(min) ) then
|
||||
return
|
||||
end
|
||||
latitude = tonumber(deg) + (tonumber(min)/60)
|
||||
|
||||
|
||||
deg, min = longitude:match("^(..)(.*)$")
|
||||
if ( not(deg) or not(min) ) then
|
||||
return
|
||||
@@ -46,15 +46,15 @@ NMEA = {
|
||||
if ( ns_indicator == 'S' ) then
|
||||
latitude = -latitude
|
||||
end
|
||||
|
||||
|
||||
return { time = time, status = status, latitude = latitude,
|
||||
longitude = longitude, speed = speed, course = course,
|
||||
date = date, variation = variation,
|
||||
date = date, variation = variation,
|
||||
ew_variation = ew_variation }
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- Calculates an verifies the message checksum
|
||||
--
|
||||
-- @param str containing the GPS sentence
|
||||
@@ -65,7 +65,7 @@ NMEA = {
|
||||
for c in str:sub(2,-4):gmatch(".") do
|
||||
val = bit.bxor(val, string.byte(c))
|
||||
end
|
||||
|
||||
|
||||
if ( str:sub(-2):upper() ~= stdnse.tohex(string.char(val)):upper() ) then
|
||||
return false, ("Failed to verify checksum (got: %s; expected: %s)"):format(stdnse.tohex(string.char(val)), str:sub(-2))
|
||||
end
|
||||
@@ -75,16 +75,16 @@ NMEA = {
|
||||
-- Parses a GPS sentence using the apropriate parser
|
||||
--
|
||||
-- @param str containing the GPS sentence
|
||||
-- @return entry table containing the parsed response or
|
||||
-- @return entry table containing the parsed response or
|
||||
-- err string if status is false
|
||||
-- @return status true on success, false on failure
|
||||
parse = function(str)
|
||||
|
||||
|
||||
local status, err = NMEA.checksum(str)
|
||||
if ( not(status) ) then
|
||||
return false, err
|
||||
end
|
||||
|
||||
|
||||
local prefix = str:match("^%$GP([^,]*)")
|
||||
if ( not(prefix) ) then
|
||||
return false, "Not a NMEA sentence"
|
||||
@@ -101,9 +101,9 @@ NMEA = {
|
||||
stdnse.print_debug(2, err)
|
||||
return false, err
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
Util = {
|
||||
@@ -111,7 +111,7 @@ Util = {
|
||||
convertTime = function(date, time)
|
||||
local d = {}
|
||||
d.hour, d.min, d.sec = time:match("(..)(..)(..)")
|
||||
d.day, d.month, d.year = date:match("(..)(..)(..)")
|
||||
d.day, d.month, d.year = date:match("(..)(..)(..)")
|
||||
d.year = d.year + 2000
|
||||
return os.time(d)
|
||||
end
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
--
|
||||
-- * <code>Crawler</code>
|
||||
-- ** This class is responsible for the actual crawling.
|
||||
--
|
||||
--
|
||||
-- The following sample code shows how the spider could be used:
|
||||
-- <code>
|
||||
-- local crawler = httpspider.Crawler:new( host, port, '/', { scriptname = SCRIPT_NAME } )
|
||||
@@ -38,9 +38,9 @@
|
||||
-- return result
|
||||
-- </code>
|
||||
--
|
||||
-- For advanced use, the library currently supports a number of closures (withinhost,
|
||||
-- withindomain, doscraping). Please note, that withinhost and withindomain options also
|
||||
-- support boolean values. You will want to override them only for advanced use. You can
|
||||
-- For advanced use, the library currently supports a number of closures (withinhost,
|
||||
-- withindomain, doscraping). Please note, that withinhost and withindomain options also
|
||||
-- support boolean values. You will want to override them only for advanced use. You can
|
||||
-- define them using the following ultities:
|
||||
--
|
||||
-- * <code>iswithinhost</code>
|
||||
@@ -51,18 +51,18 @@
|
||||
--
|
||||
-- * <code>isresource</code>
|
||||
-- ** You can use this ultity to check the type of the resource (for example "js").
|
||||
-- ** A third option may hold a number of signs that may exist after the extension
|
||||
-- ** of the resource. By default, these are [#, ?]. For example, if we want to return
|
||||
-- only php resources, the function will also return example.php?query=foo or
|
||||
-- ** A third option may hold a number of signs that may exist after the extension
|
||||
-- ** of the resource. By default, these are [#, ?]. For example, if we want to return
|
||||
-- only php resources, the function will also return example.php?query=foo or
|
||||
-- example.php#foo.
|
||||
--
|
||||
-- The following sample code shows an example usage. We override the default
|
||||
-- withinhost method and we allow spidering only on resources within the host
|
||||
-- The following sample code shows an example usage. We override the default
|
||||
-- withinhost method and we allow spidering only on resources within the host
|
||||
-- that they are not "js" or "css".
|
||||
-- <code>
|
||||
-- crawler.options.withinhost = function(url)
|
||||
-- if crawler:iswithinhost(url)
|
||||
-- and not crawler:isresource(url, "js")
|
||||
-- if crawler:iswithinhost(url)
|
||||
-- and not crawler:isresource(url, "js")
|
||||
-- and not crawler:isresource(url, "css") then
|
||||
-- return true
|
||||
-- end
|
||||
@@ -70,7 +70,7 @@
|
||||
-- </code>
|
||||
--
|
||||
-- @author Patrik Karlsson <patrik@cqure.net>
|
||||
--
|
||||
--
|
||||
-- @args httpspider.maxdepth the maximum amount of directories beneath
|
||||
-- the initial url to spider. A negative value disables the limit.
|
||||
-- (default: 3)
|
||||
@@ -78,24 +78,24 @@
|
||||
-- A negative value disables the limit (default: 20)
|
||||
-- @args httpspider.url the url to start spidering. This is a URL
|
||||
-- relative to the scanned host eg. /default.html (default: /)
|
||||
-- @args httpspider.withinhost Closure that overrides the default withinhost
|
||||
-- function that only spiders URLs within the same host. If this is
|
||||
-- set to false the crawler will spider URLs both inside and outside
|
||||
-- the host. See the closure section above to override the default
|
||||
-- @args httpspider.withinhost Closure that overrides the default withinhost
|
||||
-- function that only spiders URLs within the same host. If this is
|
||||
-- set to false the crawler will spider URLs both inside and outside
|
||||
-- the host. See the closure section above to override the default
|
||||
-- behaviour. (default: true)
|
||||
-- @args httpspider.withindomain Closure that overrides the default
|
||||
-- @args httpspider.withindomain Closure that overrides the default
|
||||
-- withindomain function that only spiders URLs within the same
|
||||
-- domain. This widens the scope from <code>withinhost</code> and can
|
||||
-- not be used in combination. See the closure section above to
|
||||
-- not be used in combination. See the closure section above to
|
||||
-- override the default behaviour. (default: false)
|
||||
-- @args httpspider.noblacklist if set, doesn't load the default blacklist
|
||||
-- @args httpspider.useheadfornonwebfiles if set, the crawler would use
|
||||
-- HEAD instead of GET for files that do not have extensions indicating
|
||||
-- that they are webpages (the list of webpage extensions is located in
|
||||
-- nselib/data/http-web-files-extensions.lst)
|
||||
-- @args httpspider.doscraping Closure that overrides the default doscraping
|
||||
-- function used to check if the resource should be scraped (in terms
|
||||
-- of extracting any links within it). See the closure section above to
|
||||
-- @args httpspider.doscraping Closure that overrides the default doscraping
|
||||
-- function used to check if the resource should be scraped (in terms
|
||||
-- of extracting any links within it). See the closure section above to
|
||||
-- override the default behaviour.
|
||||
---
|
||||
|
||||
@@ -114,10 +114,10 @@ local PREFETCH_SIZE = 5
|
||||
|
||||
-- The Options class, handling all spidering options
|
||||
Options = {
|
||||
|
||||
|
||||
new = function(self, options)
|
||||
local o = { }
|
||||
|
||||
|
||||
-- copy all options as class members
|
||||
for k, v in pairs(options) do o[k] = v end
|
||||
|
||||
@@ -126,12 +126,12 @@ Options = {
|
||||
o.whitelist = o.whitelist or {}
|
||||
o.blacklist = o.blacklist or {}
|
||||
local removewww = function(url) return string.gsub(url, "^www%.", "") end
|
||||
|
||||
|
||||
-- set up the appropriate matching functions
|
||||
if ( o.withinhost ) then
|
||||
o.withinhost = function(u)
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
|
||||
|
||||
if ( o.base_url:getPort() ~= 80 and o.base_url:getPort() ~= 443 ) then
|
||||
if ( tonumber(parsed_u.port) ~= tonumber(o.base_url:getPort()) ) then
|
||||
return false
|
||||
@@ -147,7 +147,7 @@ Options = {
|
||||
end
|
||||
if ( o.withindomain ) then
|
||||
o.withindomain = function(u)
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
if ( o.base_url:getPort() ~= 80 and o.base_url:getPort() ~= 443 ) then
|
||||
if ( tonumber(parsed_u.port) ~= tonumber(o.base_url:getPort()) ) then
|
||||
return false
|
||||
@@ -162,7 +162,7 @@ Options = {
|
||||
end
|
||||
|
||||
if (not o.doscraping) then
|
||||
|
||||
|
||||
o.doscraping = function(u)
|
||||
return true
|
||||
end
|
||||
@@ -172,7 +172,7 @@ Options = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
addWhitelist = function(self, func) table.insert(self.whitelist, func) end,
|
||||
addBlacklist = function(self, func) table.insert(self.blacklist, func) end,
|
||||
|
||||
@@ -180,11 +180,11 @@ Options = {
|
||||
|
||||
-- Placeholder for form extraction code
|
||||
FormExtractor = {
|
||||
|
||||
|
||||
}
|
||||
|
||||
LinkExtractor = {
|
||||
|
||||
|
||||
-- Creates a new instance of LinkExtractor
|
||||
-- @return o instance of LinkExtractor
|
||||
new = function(self, url, html, options)
|
||||
@@ -200,7 +200,7 @@ LinkExtractor = {
|
||||
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- is the link absolute or not?
|
||||
isAbsolute = function(url)
|
||||
-- at this point we don't care about the protocol
|
||||
@@ -208,7 +208,7 @@ LinkExtractor = {
|
||||
-- feed:http://example.com/rss.xml
|
||||
return ( url:match('^%w*:') ~= nil )
|
||||
end,
|
||||
|
||||
|
||||
-- Creates an absolute link from a relative one based on the base_url
|
||||
-- The functionality is very simple and does not take any ../../ in
|
||||
-- consideration.
|
||||
@@ -235,7 +235,7 @@ LinkExtractor = {
|
||||
|
||||
if ( ( base_url:getProto() == 'https' and base_url:getPort() == 443 ) or
|
||||
( base_url:getProto() == 'http' and base_url:getPort() == 80 ) ) then
|
||||
|
||||
|
||||
if ( leading_slash ) then
|
||||
return ("%s://%s/%s"):format(base_url:getProto(), base_url:getHost(), rel_url)
|
||||
else
|
||||
@@ -257,7 +257,7 @@ LinkExtractor = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Gets the depth of the link, relative to our base url eg.
|
||||
-- base_url = http://www.cqure.net/wp/
|
||||
-- url = http://www.cqure.net/wp/ - depth: 0
|
||||
@@ -279,7 +279,7 @@ LinkExtractor = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
validate_link = function(self, url)
|
||||
local valid = true
|
||||
|
||||
@@ -296,7 +296,7 @@ LinkExtractor = {
|
||||
if ( -1 == depth or depth > self.options.maxdepth ) then
|
||||
stdnse.print_debug(3, "%s: Skipping link depth: %d; b_url=%s; url=%s", LIBRARY_NAME, depth, tostring(self.options.base_url), tostring(url))
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- withindomain trumps any whitelisting
|
||||
@@ -315,7 +315,7 @@ LinkExtractor = {
|
||||
end
|
||||
end
|
||||
|
||||
-- run through all blacklists
|
||||
-- run through all blacklists
|
||||
if ( #self.options.blacklist > 0 ) then
|
||||
for _, func in ipairs(self.options.blacklist) do
|
||||
if ( func(url) ) then
|
||||
@@ -353,12 +353,12 @@ LinkExtractor = {
|
||||
'[sS][rR][cC]%s*=%s*([^\'\"][^%s>]+)',
|
||||
'[aA][cC][tT][iI][oO][nN]%s*=%s*[\'"]%s*([^"^\']+%s*)[\'"]',
|
||||
}
|
||||
|
||||
|
||||
local base_hrefs = {
|
||||
'[Bb][Aa][Ss][Ee]%s*[Hh][Rr][Ee][Ff]%s*=%s*[\'"](%s*[^"^\']+%s*)[\'"]',
|
||||
'[Bb][Aa][Ss][Ee]%s*[Hh][Rr][Ee][Ff]%s*=%s*([^\'\"][^%s>]+)'
|
||||
}
|
||||
|
||||
|
||||
local base_href
|
||||
for _, pattern in ipairs(base_hrefs) do
|
||||
base_href = self.html:match(pattern)
|
||||
@@ -373,11 +373,11 @@ LinkExtractor = {
|
||||
if ( not(LinkExtractor.isAbsolute(l)) ) then
|
||||
link = LinkExtractor.createAbsolute(self.url, l, base_href)
|
||||
end
|
||||
|
||||
|
||||
local url = URL:new(link)
|
||||
|
||||
|
||||
local valid = self:validate_link(url)
|
||||
|
||||
|
||||
if ( valid ) then
|
||||
stdnse.print_debug(3, "%s: Adding link: %s", LIBRARY_NAME, tostring(url))
|
||||
links[tostring(url)] = true
|
||||
@@ -386,24 +386,24 @@ LinkExtractor = {
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
for link in pairs(links) do
|
||||
table.insert(self.links, link)
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
|
||||
-- Gets a table containing all of the retrieved URLs, after filtering
|
||||
-- has been applied.
|
||||
getLinks = function(self) return self.links end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The URL class, containing code to process URLS
|
||||
-- This class is heavily inspired by the Java URL class
|
||||
URL = {
|
||||
|
||||
|
||||
-- Creates a new instance of URL
|
||||
-- @param url string containing the text representation of a URL
|
||||
-- @return o instance of URL, in case of parsing being successful
|
||||
@@ -412,14 +412,14 @@ URL = {
|
||||
local o = {
|
||||
raw = url,
|
||||
}
|
||||
|
||||
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
if ( o:parse() ) then
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the string representation of the URL and splits it into different
|
||||
-- URL components
|
||||
-- @return status true on success, false on failure
|
||||
@@ -430,15 +430,15 @@ URL = {
|
||||
self.port = tonumber(self.port)
|
||||
if ( not(self.port) ) then
|
||||
if ( self.proto:match("https") ) then
|
||||
self.port = 443
|
||||
self.port = 443
|
||||
elseif ( self.proto:match("http")) then
|
||||
self.port = 80
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self.path = self.file:match("^([^?]*)[%?]?")
|
||||
self.dir = self.path:match("^(.+%/)") or "/"
|
||||
self.domain= self.host:match("^[^%.]-%.(.*)")
|
||||
self.domain= self.host:match("^[^%.]-%.(.*)")
|
||||
return true
|
||||
elseif( self.raw:match("^javascript:") ) then
|
||||
stdnse.print_debug(2, "%s: Skipping javascript url: %s", LIBRARY_NAME, self.raw)
|
||||
@@ -449,42 +449,42 @@ URL = {
|
||||
end
|
||||
return false
|
||||
end,
|
||||
|
||||
|
||||
-- Get's the host portion of the URL
|
||||
-- @return host string containing the hostname
|
||||
getHost = function(self) return self.host end,
|
||||
|
||||
|
||||
-- Get's the protocol representation of the URL
|
||||
-- @return proto string containing the protocol (ie. http, https)
|
||||
getProto = function(self) return self.proto end,
|
||||
|
||||
-- Returns the filename component of the URL.
|
||||
|
||||
-- Returns the filename component of the URL.
|
||||
-- @return file string containing the path and query components of the url
|
||||
getFile = function(self) return self.file end,
|
||||
|
||||
|
||||
-- Gets the port component of the URL
|
||||
-- @return port number containing the port of the URL
|
||||
getPort = function(self) return self.port end,
|
||||
|
||||
|
||||
-- Gets the path component of the URL
|
||||
-- @return the full path and filename of the URL
|
||||
getPath = function(self) return self.path end,
|
||||
|
||||
|
||||
-- Gets the directory component of the URL
|
||||
-- @return directory string containing the directory part of the URL
|
||||
-- @return directory string containing the directory part of the URL
|
||||
getDir = function(self) return self.dir end,
|
||||
|
||||
|
||||
-- Gets the domain component of the URL
|
||||
-- @return domain string containing the hosts domain
|
||||
getDomain = function(self)
|
||||
if ( self.domain ) then
|
||||
return self.domain
|
||||
return self.domain
|
||||
-- fallback to the host, if we can't find a domain
|
||||
else
|
||||
return self.host
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the URL to a string
|
||||
-- @return url string containing the string representation of the url
|
||||
__tostring = function(self) return self.raw end,
|
||||
@@ -492,12 +492,12 @@ URL = {
|
||||
|
||||
-- An UrlQueue
|
||||
UrlQueue = {
|
||||
|
||||
|
||||
-- creates a new instance of UrlQueue
|
||||
-- @param options table containing options
|
||||
-- @return o new instance of UrlQueue
|
||||
new = function(self, options)
|
||||
local o = {
|
||||
local o = {
|
||||
urls = {},
|
||||
options = options
|
||||
}
|
||||
@@ -505,23 +505,23 @@ UrlQueue = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- get's the next available url in the queue
|
||||
getNext = function(self)
|
||||
return table.remove(self.urls,1)
|
||||
end,
|
||||
|
||||
|
||||
-- adds a new url to the queue
|
||||
-- @param url can be either a string or a URL or a table of URLs
|
||||
add = function(self, url)
|
||||
assert( type(url) == 'string' or type(url) == 'table', "url was neither a string or table")
|
||||
local urls = ( 'string' == type(url) ) and URL:new(url) or url
|
||||
|
||||
|
||||
-- if it's a table, it can be either a single URL or an array of URLs
|
||||
if ( 'table' == type(url) and url.raw ) then
|
||||
urls = { url }
|
||||
end
|
||||
|
||||
|
||||
for _, u in ipairs(urls) do
|
||||
u = ( 'string' == type(u) ) and URL:new(u) or u
|
||||
if ( u ) then
|
||||
@@ -531,27 +531,27 @@ UrlQueue = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- dumps the contents of the UrlQueue
|
||||
dump = function(self)
|
||||
for _, url in ipairs(self.urls) do
|
||||
print("url:", url)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Crawler class
|
||||
Crawler = {
|
||||
|
||||
options = {},
|
||||
|
||||
|
||||
removewww = function(url) return string.gsub(url, "^www%.", "") end,
|
||||
|
||||
-- An ultity when defining closures. Checks if the resource exists within host.
|
||||
-- @param u URL that points to the resource we want to check.
|
||||
-- @param u URL that points to the resource we want to check.
|
||||
iswithinhost = function(self, u)
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
if ( self.options.base_url:getPort() ~= 80 and self.options.base_url:getPort() ~= 443 ) then
|
||||
if ( tonumber(parsed_u.port) ~= tonumber(self.options.base_url:getPort()) ) then
|
||||
return false
|
||||
@@ -566,9 +566,9 @@ Crawler = {
|
||||
end,
|
||||
|
||||
-- An ultity when defining closures. Checks if the resource exists within domain.
|
||||
-- @param u URL that points to the resource we want to check.
|
||||
-- @param u URL that points to the resource we want to check.
|
||||
iswithindomain = function(self, u)
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
local parsed_u = url.parse(tostring(u))
|
||||
if ( self.options.base_url:getPort() ~= 80 and self.options.base_url:getPort() ~= 443 ) then
|
||||
if ( tonumber(parsed_u.port) ~= tonumber(self.options.base_url:getPort()) ) then
|
||||
return false
|
||||
@@ -581,10 +581,10 @@ Crawler = {
|
||||
return true
|
||||
end,
|
||||
|
||||
-- An ultity when defining closures. Checks the type of the resource.
|
||||
-- @param u URL that points to the resource we want to check.
|
||||
-- An ultity when defining closures. Checks the type of the resource.
|
||||
-- @param u URL that points to the resource we want to check.
|
||||
-- @param ext the extension of the resource.
|
||||
-- @param signs table of signs that may exist after the extension of the resource.
|
||||
-- @param signs table of signs that may exist after the extension of the resource.
|
||||
isresource = function(self, u, ext, signs)
|
||||
u = tostring(u)
|
||||
|
||||
@@ -596,7 +596,7 @@ Crawler = {
|
||||
if signs then
|
||||
for _, s in signs do
|
||||
signstring = signstring .. s
|
||||
end
|
||||
end
|
||||
signstring:gsub('?', '%?')
|
||||
else
|
||||
signstring = "#%?"
|
||||
@@ -604,8 +604,8 @@ Crawler = {
|
||||
|
||||
return string.match(u, "." .. ext .. "[" .. signstring .. "]" .. "[^.]*$")
|
||||
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
-- creates a new instance of the Crawler instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
@@ -643,13 +643,13 @@ Crawler = {
|
||||
o:loadDefaultArguments()
|
||||
|
||||
local response = http.get(o.host, o.port, '/', { timeout = o.options.timeout, redirect_ok = o.options.redirect_ok, no_cache = o.options.no_cache } )
|
||||
|
||||
|
||||
if ( not(response) or 'table' ~= type(response) ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
o.url = o.url:match("/?(.*)")
|
||||
|
||||
|
||||
local u_host = o.host.targetname or o.host.name
|
||||
if ( not(u_host) or 0 == #u_host ) then
|
||||
u_host = o.host.ip
|
||||
@@ -662,17 +662,17 @@ Crawler = {
|
||||
|
||||
o.options.timeout = o.options.timeout or 10000
|
||||
o.processed = {}
|
||||
|
||||
|
||||
-- script arguments have precedense
|
||||
if ( not(o.options.maxdepth) ) then
|
||||
o.options.maxdepth = tonumber(stdnse.get_script_args("httpspider.maxdepth"))
|
||||
end
|
||||
|
||||
|
||||
-- script arguments have precedense
|
||||
if ( not(o.options.maxpagecount) ) then
|
||||
o.options.maxpagecount = tonumber(stdnse.get_script_args("httpspider.maxpagecount"))
|
||||
end
|
||||
|
||||
|
||||
if ( not(o.options.noblacklist) ) then
|
||||
o:addDefaultBlacklist()
|
||||
end
|
||||
@@ -689,18 +689,18 @@ Crawler = {
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
stdnse.print_debug(2, "%s: %s", LIBRARY_NAME, o:getLimitations())
|
||||
|
||||
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Set's the timeout used by the http library
|
||||
-- @param timeout number containing the timeout in ms.
|
||||
set_timeout = function(self, timeout)
|
||||
self.options.timeout = timeout
|
||||
end,
|
||||
|
||||
|
||||
-- Get's the amount of pages that has been retrieved
|
||||
-- @return count number of pages retrieved by the instance
|
||||
getPageCount = function(self)
|
||||
@@ -710,7 +710,7 @@ Crawler = {
|
||||
end
|
||||
return count
|
||||
end,
|
||||
|
||||
|
||||
-- Adds a default blacklist blocking binary files such as images,
|
||||
-- compressed archives and executable files
|
||||
addDefaultBlacklist = function(self)
|
||||
@@ -740,7 +740,7 @@ Crawler = {
|
||||
end
|
||||
end )
|
||||
end,
|
||||
|
||||
|
||||
-- does the heavy crawling
|
||||
--
|
||||
-- The crawler may exit due to a number of different reasons, including
|
||||
@@ -759,12 +759,12 @@ Crawler = {
|
||||
end
|
||||
|
||||
while(true) do
|
||||
|
||||
|
||||
if ( self.quit or coroutine.status(self.basethread) == 'dead' ) then
|
||||
table.insert(response_queue, {false, { err = false, msg = "Quit signalled by crawler" } })
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
-- in case the user set a max page count to retrieve check how many
|
||||
-- pages we have retrieved so far
|
||||
local count = self:getPageCount()
|
||||
@@ -774,7 +774,7 @@ Crawler = {
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
-- pull links from the queue until we get a valid one
|
||||
local url
|
||||
repeat
|
||||
@@ -787,18 +787,18 @@ Crawler = {
|
||||
condvar "signal"
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if ( self.options.maxpagecount ) then
|
||||
stdnse.print_debug(2, "%s: Fetching url [%d of %d]: %s", LIBRARY_NAME, count, self.options.maxpagecount, tostring(url))
|
||||
else
|
||||
stdnse.print_debug(2, "%s: Fetching url: %s", LIBRARY_NAME, tostring(url))
|
||||
end
|
||||
end
|
||||
|
||||
local scrape = true
|
||||
local scrape = true
|
||||
|
||||
|
||||
if not (self.options.doscraping(url)) then
|
||||
stdnse.print_debug(2, "%s: Scraping is not allowed for url: %s", LIBRARY_NAME, tostring(url))
|
||||
stdnse.print_debug(2, "%s: Scraping is not allowed for url: %s", LIBRARY_NAME, tostring(url))
|
||||
scrape = false
|
||||
end
|
||||
|
||||
@@ -828,9 +828,9 @@ Crawler = {
|
||||
-- fetch the url, and then push it to the processed table
|
||||
response = http.get(url:getHost(), url:getPort(), url:getFile(), { timeout = self.options.timeout, redirect_ok = self.options.redirect_ok, no_cache = self.options.no_cache } )
|
||||
end
|
||||
|
||||
|
||||
self.processed[tostring(url)] = true
|
||||
|
||||
|
||||
if ( response ) then
|
||||
-- were we redirected?
|
||||
if ( response.location ) then
|
||||
@@ -847,7 +847,7 @@ Crawler = {
|
||||
if ( response.body ) and scrape then
|
||||
local links = LinkExtractor:new(url, response.body, self.options):getLinks()
|
||||
self.urlqueue:add(links)
|
||||
end
|
||||
end
|
||||
else
|
||||
response = { body = "", headers = {} }
|
||||
end
|
||||
@@ -860,7 +860,7 @@ Crawler = {
|
||||
end
|
||||
condvar "signal"
|
||||
end,
|
||||
|
||||
|
||||
-- Loads the argument set on a script level
|
||||
loadScriptArguments = function(self)
|
||||
local sn = self.options.scriptname
|
||||
@@ -868,7 +868,7 @@ Crawler = {
|
||||
stdnse.print_debug("%s: WARNING: Script argument could not be loaded as scriptname was not set", LIBRARY_NAME)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if ( nil == self.options.maxdepth ) then
|
||||
self.options.maxdepth = tonumber(stdnse.get_script_args(sn .. ".maxdepth"))
|
||||
end
|
||||
@@ -893,9 +893,9 @@ Crawler = {
|
||||
if ( nil == self.options.doscraping ) then
|
||||
self.options.doscraping = stdnse.get_script_args(sn .. ".doscraping")
|
||||
end
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
-- Loads the argument on a library level
|
||||
loadLibraryArguments = function(self)
|
||||
local ln = LIBRARY_NAME
|
||||
@@ -925,7 +925,7 @@ Crawler = {
|
||||
self.options.doscraping = stdnse.get_script_args(ln .. ".doscraping")
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Loads any defaults for arguments that were not set
|
||||
loadDefaultArguments = function(self)
|
||||
local function tobool(b)
|
||||
@@ -948,7 +948,7 @@ Crawler = {
|
||||
end
|
||||
return b
|
||||
end
|
||||
|
||||
|
||||
if self.options.withinhost == 0 then
|
||||
self.options.withinhost = false
|
||||
end
|
||||
@@ -977,8 +977,8 @@ Crawler = {
|
||||
self.options.maxdepth = tonumber(self.options.maxdepth) or 3
|
||||
self.options.maxpagecount = tonumber(self.options.maxpagecount) or 20
|
||||
self.url = self.url or '/'
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
-- gets a string of limitations imposed on the crawl
|
||||
getLimitations = function(self)
|
||||
local o = self.options
|
||||
@@ -998,12 +998,12 @@ Crawler = {
|
||||
table.insert(limits, ("withinhost=%s"):format(o.base_url:getHost()))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( #limits > 0 ) then
|
||||
return ("Spidering limited to: %s"):format(stdnse.strjoin("; ", limits))
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- does the crawling
|
||||
crawl = function(self)
|
||||
self.response_queue = self.response_queue or {}
|
||||
@@ -1013,7 +1013,7 @@ Crawler = {
|
||||
end
|
||||
|
||||
if ( #self.response_queue == 0 and coroutine.status(self.thread) ~= 'dead') then
|
||||
condvar "wait"
|
||||
condvar "wait"
|
||||
end
|
||||
condvar "signal"
|
||||
if ( #self.response_queue == 0 ) then
|
||||
@@ -1022,7 +1022,7 @@ Crawler = {
|
||||
return table.unpack(table.remove(self.response_queue, 1))
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- signals the crawler to stop
|
||||
stop = function(self)
|
||||
local condvar = nmap.condvar(self.response_queue)
|
||||
|
||||
@@ -17,11 +17,11 @@ _ENV = stdnse.module("iax2", stdnse.seeall)
|
||||
|
||||
|
||||
IAX2 = {
|
||||
|
||||
|
||||
FrameType = {
|
||||
IAX = 6,
|
||||
IAX = 6,
|
||||
},
|
||||
|
||||
|
||||
SubClass = {
|
||||
ACK = 0x04,
|
||||
REGACK = 0x0f,
|
||||
@@ -29,21 +29,21 @@ IAX2 = {
|
||||
REGREL = 0x11,
|
||||
CALLTOKEN = 0x28,
|
||||
},
|
||||
|
||||
|
||||
InfoElement = {
|
||||
USERNAME = 0x06,
|
||||
USERNAME = 0x06,
|
||||
CHALLENGE = 0x0f,
|
||||
MD5_RESULT = 0x10,
|
||||
CALLTOKEN = 0x36,
|
||||
},
|
||||
|
||||
|
||||
PacketType = {
|
||||
FULL = 1,
|
||||
},
|
||||
|
||||
|
||||
-- The IAX2 Header
|
||||
Header = {
|
||||
|
||||
|
||||
-- Creates a new Header instance
|
||||
-- @param src_call number containing the source call
|
||||
-- @param dst_call number containing the dest call
|
||||
@@ -68,9 +68,9 @@ IAX2 = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses data, a byte string, and creates a new Header instance
|
||||
-- @return header instance of Header
|
||||
-- @return header instance of Header
|
||||
parse = function(data)
|
||||
local header = IAX2.Header:new()
|
||||
local pos, frame_type = bin.unpack("C", data)
|
||||
@@ -90,13 +90,13 @@ IAX2 = {
|
||||
end
|
||||
pos, header.dst_call = bin.unpack(">S", data, pos - 1)
|
||||
header.dst_call = bit.band(header.dst_call, 0x7FFF)
|
||||
|
||||
pos, header.timestamp, header.oseqno,
|
||||
|
||||
pos, header.timestamp, header.oseqno,
|
||||
header.iseqno, header.frametype, header.subclass = bin.unpack(">ICCCC", data, pos)
|
||||
|
||||
|
||||
return header
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to a string
|
||||
-- @return str containing the instance
|
||||
__tostring = function(self)
|
||||
@@ -114,10 +114,10 @@ IAX2 = {
|
||||
self.oseqno, self.iseqno, self.frametype, self.subclass)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- The IAX2 Request class
|
||||
Request = {
|
||||
|
||||
|
||||
-- Creates a new instance
|
||||
-- @param header instance of Header
|
||||
new = function(self, header)
|
||||
@@ -127,9 +127,9 @@ IAX2 = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Sets an Info Element or adds one, in case it's missing
|
||||
-- @param key the key value of the IE to add
|
||||
-- @param value string containing the value to set or add
|
||||
@@ -141,7 +141,7 @@ IAX2 = {
|
||||
end
|
||||
table.insert(self.ies, { type = key, value = value } )
|
||||
end,
|
||||
|
||||
|
||||
-- Gets an information element
|
||||
-- @param key number containing the element number to retrieve
|
||||
-- @return ie table containing the info element if it exists
|
||||
@@ -160,24 +160,24 @@ IAX2 = {
|
||||
for _, ie in ipairs(self.ies) do
|
||||
data = data .. bin.pack("Cp", ie.type, ie.value )
|
||||
end
|
||||
|
||||
|
||||
return tostring(self.header) .. data
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
-- The IAX2 Response
|
||||
Response = {
|
||||
|
||||
|
||||
-- Creates a new instance
|
||||
new = function(self)
|
||||
local o = { ies = {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Sets an Info Element or adds one, in case it's missing
|
||||
-- @param key the key value of the IE to add
|
||||
-- @param value string containing the value to set or add
|
||||
@@ -189,7 +189,7 @@ IAX2 = {
|
||||
end
|
||||
table.insert(self.ies, { type = key, value = value } )
|
||||
end,
|
||||
|
||||
|
||||
-- Gets an information element
|
||||
-- @param key number containing the element number to retrieve
|
||||
-- @return ie table containing the info element if it exists
|
||||
@@ -200,16 +200,16 @@ IAX2 = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Parses data, a byte string, and creates a response
|
||||
-- @return resp instance of response
|
||||
-- @return resp instance of response
|
||||
parse = function(data)
|
||||
local resp = IAX2.Response:new()
|
||||
if ( not(resp) ) then return end
|
||||
|
||||
resp.header = IAX2.Header.parse(data)
|
||||
if ( not(resp.header) ) then return end
|
||||
|
||||
|
||||
local pos = 13
|
||||
resp.ies = {}
|
||||
repeat
|
||||
@@ -219,14 +219,14 @@ IAX2 = {
|
||||
until( pos > #data )
|
||||
return resp
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new Helper instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
@@ -237,9 +237,9 @@ Helper = {
|
||||
local o = { host = host, port = port, options = options or {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects the UDP socket to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err message containing error if status is false
|
||||
@@ -248,7 +248,7 @@ Helper = {
|
||||
self.socket:set_timeout(self.options.timeout or 5000)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a request to the server and receives the response
|
||||
-- @param req instance containing the request to send to the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -263,11 +263,11 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
|
||||
|
||||
local resp = IAX2.Response.parse(data)
|
||||
return true, resp
|
||||
end,
|
||||
|
||||
|
||||
-- Request a session release
|
||||
-- @param username string containing the extention (username)
|
||||
-- @param password string containing the password
|
||||
@@ -299,45 +299,45 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, resp
|
||||
end
|
||||
|
||||
|
||||
local challenge = resp:getIE(IAX2.InfoElement.CHALLENGE)
|
||||
if ( not(challenge) ) then
|
||||
return false, "Failed to retrieve challenge from server"
|
||||
end
|
||||
|
||||
|
||||
regrel.header.iseqno = 1
|
||||
regrel.header.oseqno = 1
|
||||
regrel.header.dst_call = resp.header.src_call
|
||||
regrel.ies = {}
|
||||
|
||||
|
||||
local hash = stdnse.tohex(openssl.md5(challenge.value .. password))
|
||||
regrel:setIE(IAX2.InfoElement.USERNAME, username)
|
||||
regrel:setIE(IAX2.InfoElement.MD5_RESULT, hash)
|
||||
|
||||
|
||||
status, resp = self:exch(regrel)
|
||||
if ( not(status) ) then
|
||||
return false, resp
|
||||
end
|
||||
|
||||
|
||||
if ( IAX2.SubClass.ACK == resp.header.subclass ) then
|
||||
local data
|
||||
status, data = self.socket:receive()
|
||||
status, data = self.socket:receive()
|
||||
resp = IAX2.Response.parse(data)
|
||||
end
|
||||
|
||||
|
||||
if ( status and IAX2.SubClass.REGACK == resp.header.subclass ) then
|
||||
return true
|
||||
end
|
||||
return false, "Release failed"
|
||||
end,
|
||||
|
||||
|
||||
-- Close the connection with the server
|
||||
-- @return true on success, false on failure
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -33,7 +33,7 @@ _ENV = stdnse.module("imap", stdnse.seeall)
|
||||
|
||||
|
||||
IMAP = {
|
||||
|
||||
|
||||
--- Creates a new instance of the IMAP class
|
||||
--
|
||||
-- @param host table as received by the script action method
|
||||
@@ -42,17 +42,17 @@ IMAP = {
|
||||
-- <code>timeout<code> - number containing the seconds to wait for
|
||||
-- a response
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
counter = 1,
|
||||
timeout = ( options and options.timeout ) or 10000
|
||||
counter = 1,
|
||||
timeout = ( options and options.timeout ) or 10000
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Receives a response from the IMAP server
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -64,10 +64,10 @@ IMAP = {
|
||||
if( not(status) ) then return false, tmp end
|
||||
data = data .. tmp
|
||||
until( tmp:match(("^A%04d"):format(self.counter - 1)) or tmp:match("^%+"))
|
||||
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a request to the IMAP server
|
||||
--
|
||||
-- @param cmd string containing the command to send to the server eg.
|
||||
@@ -87,7 +87,7 @@ IMAP = {
|
||||
self.counter = self.counter + 1
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Connect to the server
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -100,7 +100,7 @@ IMAP = {
|
||||
self.socket = socket
|
||||
return true, banner
|
||||
end,
|
||||
|
||||
|
||||
--- Authenticate to the server (non PLAIN text mode)
|
||||
-- Currently supported algorithms are CRAM-MD5 and CRAM-SHA1
|
||||
--
|
||||
@@ -113,26 +113,26 @@ IMAP = {
|
||||
authenticate = function(self, username, pass, mech)
|
||||
assert( mech == "NTLM" or
|
||||
mech == "DIGEST-MD5" or
|
||||
mech == "CRAM-MD5" or
|
||||
mech == "CRAM-MD5" or
|
||||
mech == "PLAIN",
|
||||
"Unsupported authentication mechanism")
|
||||
|
||||
|
||||
local status, err = self:send("AUTHENTICATE", mech)
|
||||
|
||||
if( not(status) ) then return false, "ERROR: Failed to send data" end
|
||||
|
||||
local status, data = self:receive()
|
||||
if( not(status) ) then return false, "ERROR: Failed to receive challenge" end
|
||||
|
||||
|
||||
if ( mech == "NTLM" ) then
|
||||
-- sniffed of the wire, seems to always be the same
|
||||
-- decodes to some NTLMSSP blob greatness
|
||||
status, data = self.socket:send("TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==\r\n")
|
||||
if ( not(status) ) then return false, "ERROR: Failed to send NTLM packet" end
|
||||
status, data = self:receive()
|
||||
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
|
||||
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
|
||||
end
|
||||
|
||||
|
||||
if ( data:match(("^A%04d "):format(self.counter-1)) ) then
|
||||
return false, "ERROR: Authentication mechanism not supported"
|
||||
end
|
||||
@@ -142,19 +142,19 @@ IMAP = {
|
||||
return false, "ERROR: Failed to receive proper response from server"
|
||||
end
|
||||
data = base64.dec(data:match("^+ (.*)"))
|
||||
|
||||
|
||||
-- All mechanisms expect username and pass
|
||||
-- add the otheronce for those who need them
|
||||
local mech_params = { username, pass, data, "imap" }
|
||||
auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params))
|
||||
auth_data = base64.enc(auth_data) .. "\r\n"
|
||||
|
||||
|
||||
status, data = self.socket:send(auth_data)
|
||||
if( not(status) ) then return false, "ERROR: Failed to send data" end
|
||||
|
||||
status, data = self:receive()
|
||||
if( not(status) ) then return false, "ERROR: Failed to receive data" end
|
||||
|
||||
|
||||
if ( mech == "DIGEST-MD5" ) then
|
||||
local rspauth = data:match("^+ (.*)")
|
||||
if ( rspauth ) then
|
||||
@@ -168,7 +168,7 @@ IMAP = {
|
||||
end
|
||||
return false, "Login failed"
|
||||
end,
|
||||
|
||||
|
||||
--- Login to the server using PLAIN text authentication
|
||||
--
|
||||
-- @param username string containing the username
|
||||
@@ -178,16 +178,16 @@ IMAP = {
|
||||
login = function(self, username, password)
|
||||
local status, err = self:send("LOGIN", ("\"%s\" \"%s\""):format(username, password))
|
||||
if( not(status) ) then return false, "ERROR: Failed to send data" end
|
||||
|
||||
|
||||
local status, data = self:receive()
|
||||
if( not(status) ) then return false, "ERROR: Failed to receive data" end
|
||||
|
||||
|
||||
if ( data:match(("^A%04d OK"):format(self.counter - 1)) ) then
|
||||
return true
|
||||
end
|
||||
return false, "Login failed"
|
||||
end,
|
||||
|
||||
|
||||
--- Retrieves a list of server capabilities (eg. supported authentication
|
||||
-- mechanisms, QUOTA, UIDPLUS, ACL ...)
|
||||
--
|
||||
@@ -198,11 +198,11 @@ IMAP = {
|
||||
local proto = (self.port.version and self.port.version.service_tunnel == "ssl" and "ssl") or "tcp"
|
||||
local status, err = self:send("CAPABILITY")
|
||||
if( not(status) ) then return false, err end
|
||||
|
||||
|
||||
local status, line = self:receive()
|
||||
if (not(status)) then
|
||||
capas.CAPABILITY = false
|
||||
else
|
||||
else
|
||||
while status do
|
||||
if ( line:match("^%*%s+CAPABILITY") ) then
|
||||
line = line:gsub("^%*%s+CAPABILITY", "")
|
||||
@@ -216,17 +216,17 @@ IMAP = {
|
||||
end
|
||||
return true, capas
|
||||
end,
|
||||
|
||||
|
||||
--- Closes the connection to the IMAP server
|
||||
-- @return true on success, false on failure
|
||||
close = function(self) return self.socket:close() end
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
-- The helper class, that servers as interface to script writers
|
||||
Helper = {
|
||||
|
||||
|
||||
-- @param host table as received by the script action method
|
||||
-- @param port table as received by the script action method
|
||||
-- @param options table containing options, currently
|
||||
@@ -238,13 +238,13 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Connects to the IMAP server
|
||||
-- @return status true on success, false on failure
|
||||
connect = function(self)
|
||||
return self.client:connect()
|
||||
end,
|
||||
|
||||
|
||||
--- Login to the server using eithe plain-text or using the authentication
|
||||
-- mechanism provided in the mech argument.
|
||||
--
|
||||
@@ -259,7 +259,7 @@ Helper = {
|
||||
return self.client:authenticate(username, password, mech)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Retrieves a list of server capabilities (eg. supported authentication
|
||||
-- mechanisms, QUOTA, UIDPLUS, ACL ...)
|
||||
--
|
||||
@@ -268,13 +268,13 @@ Helper = {
|
||||
capabilities = function(self)
|
||||
return self.client:capabilities()
|
||||
end,
|
||||
|
||||
|
||||
--- Closes the connection to the IMAP server
|
||||
-- @return true on success, false on failure
|
||||
close = function(self)
|
||||
return self.client:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -154,12 +154,12 @@ todword = function( ip )
|
||||
end
|
||||
|
||||
---
|
||||
-- Converts the supplied IPv4 address from a DWORD value into a dotted string.
|
||||
-- Converts the supplied IPv4 address from a DWORD value into a dotted string.
|
||||
--
|
||||
-- For example, the address (((a*256+b)*256+c)*256+d) becomes a.b.c.d.
|
||||
-- For example, the address (((a*256+b)*256+c)*256+d) becomes a.b.c.d.
|
||||
--
|
||||
--@param ip DWORD representing an IPv4 address.
|
||||
--@return The string representing the address.
|
||||
--@param ip DWORD representing an IPv4 address.
|
||||
--@return The string representing the address.
|
||||
fromdword = function( ip )
|
||||
if type( ip ) ~= "number" then
|
||||
stdnse.print_debug(1, "Error in ipOps.todword: Expected IPv4 address.")
|
||||
@@ -354,7 +354,7 @@ expand_ip = function( ip, family )
|
||||
if family == "inet6" then
|
||||
return ( table.concat( { 0,0,0,0,0,"ffff",
|
||||
stdnse.tohex( 256*octets[1]+octets[2] ),
|
||||
stdnse.tohex( 256*octets[3]+octets[4] )
|
||||
stdnse.tohex( 256*octets[3]+octets[4] )
|
||||
}, ":" ) )
|
||||
else
|
||||
return ( table.concat( octets, "." ) )
|
||||
|
||||
132
nselib/ipp.lua
132
nselib/ipp.lua
@@ -17,11 +17,11 @@ _ENV = stdnse.module("ipp", stdnse.seeall)
|
||||
|
||||
-- The IPP layer
|
||||
IPP = {
|
||||
|
||||
|
||||
StatusCode = {
|
||||
OK = 0,
|
||||
},
|
||||
|
||||
|
||||
State = {
|
||||
IPP_JOB_PENDING = 3,
|
||||
IPP_JOB_HELD = 4,
|
||||
@@ -31,7 +31,7 @@ IPP = {
|
||||
IPP_JOB_ABORTED = 8,
|
||||
IPP_JOB_COMPLETED = 9,
|
||||
},
|
||||
|
||||
|
||||
StateName = {
|
||||
[3] = "Pending",
|
||||
[4] = "Held",
|
||||
@@ -41,7 +41,7 @@ IPP = {
|
||||
[8] = "Aborted",
|
||||
[9] = "Completed",
|
||||
},
|
||||
|
||||
|
||||
OperationID = {
|
||||
IPP_CANCEL_JOB = 0x0008,
|
||||
IPP_GET_JOB_ATTRIBUTES = 0x0009,
|
||||
@@ -49,13 +49,13 @@ IPP = {
|
||||
CUPS_GET_PRINTERS = 0x4002,
|
||||
CUPS_GET_DOCUMENT = 0x4027
|
||||
},
|
||||
|
||||
|
||||
PrinterState = {
|
||||
IPP_PRINTER_IDLE = 3,
|
||||
IPP_PRINTER_PROCESSING = 4,
|
||||
IPP_PRINTER_STOPPED = 5,
|
||||
},
|
||||
|
||||
|
||||
Attribute = {
|
||||
|
||||
IPP_TAG_OPERATION = 0x01,
|
||||
@@ -69,25 +69,25 @@ IPP = {
|
||||
IPP_TAG_URI = 0x45,
|
||||
IPP_TAG_CHARSET = 0x47,
|
||||
IPP_TAG_LANGUAGE = 0x48,
|
||||
|
||||
|
||||
new = function(self, tag, name, value)
|
||||
local o = { tag = tag, name = name, value = value }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data, pos)
|
||||
local attrib = IPP.Attribute:new()
|
||||
local val
|
||||
pos, attrib.tag, attrib.name, val = bin.unpack(">CPP", data, pos)
|
||||
-- print(attrib.name, stdnse.tohex(val))
|
||||
attrib.value = {}
|
||||
attrib.value = {}
|
||||
table.insert(attrib.value, { tag = attrib.tag, val = val })
|
||||
|
||||
|
||||
repeat
|
||||
local tag, name_len, val
|
||||
|
||||
|
||||
if ( #data < pos + 3 ) then
|
||||
break
|
||||
end
|
||||
@@ -100,7 +100,7 @@ IPP = {
|
||||
pos = pos - 3
|
||||
end
|
||||
until( name_len ~= 0 )
|
||||
|
||||
|
||||
-- do minimal decoding
|
||||
for i=1, #attrib.value do
|
||||
if ( attrib.value[i].tag == IPP.Attribute.IPP_TAG_INTEGER ) then
|
||||
@@ -109,15 +109,15 @@ IPP = {
|
||||
attrib.value[i].val = select(2, bin.unpack(">I", attrib.value[i].val))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( 1 == #attrib.value ) then
|
||||
attrib.value = attrib.value[1].val
|
||||
end
|
||||
--print(attrib.name, attrib.value, stdnse.tohex(val))
|
||||
|
||||
|
||||
return pos, attrib
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
if ( "string" == type(self.value) ) then
|
||||
return bin.pack(">CSASA", self.tag, #self.name, self.name, #self.value, self.value)
|
||||
@@ -129,23 +129,23 @@ IPP = {
|
||||
return data
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- An attribute group, groups several attributes
|
||||
AttributeGroup = {
|
||||
|
||||
|
||||
new = function(self, tag, attribs)
|
||||
local o = { tag = tag, attribs = attribs or {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
addAttribute = function(self, attrib)
|
||||
table.insert(self.attribs, attrib)
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Gets the first attribute matching name and optionally tag from the
|
||||
-- attribute group.
|
||||
@@ -175,23 +175,23 @@ IPP = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
local data = bin.pack("C", self.tag)
|
||||
|
||||
|
||||
for _, attrib in ipairs(self.attribs) do
|
||||
data = data .. tostring(attrib)
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- The IPP request
|
||||
Request = {
|
||||
|
||||
|
||||
new = function(self, opid, reqid)
|
||||
local o = {
|
||||
local o = {
|
||||
version = 0x0101,
|
||||
opid = opid,
|
||||
reqid = reqid,
|
||||
@@ -201,11 +201,11 @@ IPP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
addAttributeGroup = function(self, group)
|
||||
table.insert( self.attrib_groups, group )
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
local data = bin.pack(">SSI", self.version, self.opid, self.reqid )
|
||||
|
||||
@@ -215,12 +215,12 @@ IPP = {
|
||||
data = data .. bin.pack("C", IPP.Attribute.IPP_TAG_END)
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- A class to handle responses from the server
|
||||
Response = {
|
||||
|
||||
|
||||
-- Creates a new instance of response
|
||||
new = function(self)
|
||||
local o = {}
|
||||
@@ -228,7 +228,7 @@ IPP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
getAttributeGroups = function(self, tag)
|
||||
local groups = {}
|
||||
for _, v in ipairs(self.attrib_groups or {}) do
|
||||
@@ -238,24 +238,24 @@ IPP = {
|
||||
end
|
||||
return groups
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local resp = IPP.Response:new()
|
||||
local pos
|
||||
|
||||
|
||||
pos, resp.version, resp.status, resp.reqid = bin.unpack(">SSI", data)
|
||||
|
||||
|
||||
resp.attrib_groups = {}
|
||||
local group
|
||||
repeat
|
||||
local tag, attrib
|
||||
pos, tag = bin.unpack(">C", data, pos)
|
||||
|
||||
|
||||
if ( tag == IPP.Attribute.IPP_TAG_OPERATION or
|
||||
tag == IPP.Attribute.IPP_TAG_JOB or
|
||||
tag == IPP.Attribute.IPP_TAG_PRINTER or
|
||||
tag == IPP.Attribute.IPP_TAG_END ) then
|
||||
|
||||
|
||||
if ( group ) then
|
||||
table.insert(resp.attrib_groups, group)
|
||||
group = IPP.AttributeGroup:new(tag)
|
||||
@@ -265,27 +265,27 @@ IPP = {
|
||||
else
|
||||
pos = pos - 1
|
||||
end
|
||||
|
||||
|
||||
if ( not(group) ) then
|
||||
stdnse.print_debug(2, "Unexpected tag: %d", tag)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
pos, attrib = IPP.Attribute.parse(data, pos)
|
||||
group:addAttribute(attrib)
|
||||
|
||||
|
||||
until( pos == #data + 1)
|
||||
|
||||
|
||||
return resp
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
HTTP = {
|
||||
|
||||
|
||||
Request = function(host, port, request)
|
||||
local headers = {
|
||||
['Content-Type'] = 'application/ipp',
|
||||
@@ -301,28 +301,28 @@ HTTP = {
|
||||
if ( not(response) ) then
|
||||
return false, "Failed to parse response"
|
||||
end
|
||||
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
new = function(self, host, port, options)
|
||||
local o = { host = host, port = port, options = options or {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
connect = function(self)
|
||||
self.socket = nmap.new_socket()
|
||||
self.socket:set_timeout(self.options.timeout or 10000)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
getPrinters = function(self)
|
||||
|
||||
local attribs = {
|
||||
@@ -340,17 +340,17 @@ Helper = {
|
||||
end
|
||||
|
||||
local printers = {}
|
||||
|
||||
|
||||
for _, ag in ipairs(response:getAttributeGroups(IPP.Attribute.IPP_TAG_PRINTER)) do
|
||||
local attrib = {
|
||||
["printer-name"] = "name",
|
||||
local attrib = {
|
||||
["printer-name"] = "name",
|
||||
["printer-location"] = "location",
|
||||
["printer-make-and-model"] = "model",
|
||||
["printer-state"] = "state",
|
||||
["queued-job-count"] = "queue_count",
|
||||
["printer-dns-sd-name"] = "dns_sd_name",
|
||||
}
|
||||
|
||||
|
||||
local printer = {}
|
||||
for k, v in pairs(attrib) do
|
||||
if ( ag:getAttributeValue(k) ) then
|
||||
@@ -361,10 +361,10 @@ Helper = {
|
||||
end
|
||||
return true, printers
|
||||
end,
|
||||
|
||||
|
||||
getQueueInfo = function(self, uri)
|
||||
local uri = uri or ("ipp://%s/"):format(self.host.ip)
|
||||
|
||||
|
||||
local attribs = {
|
||||
IPP.Attribute:new(IPP.Attribute.IPP_TAG_CHARSET, "attributes-charset", "utf-8" ),
|
||||
IPP.Attribute:new(IPP.Attribute.IPP_TAG_LANGUAGE, "attributes-natural-language", "en-us"),
|
||||
@@ -384,16 +384,16 @@ Helper = {
|
||||
{ tag = IPP.Attribute.IPP_TAG_KEYWORD, val = "time-at-creation" } } ),
|
||||
IPP.Attribute:new(IPP.Attribute.IPP_TAG_KEYWORD, "which-jobs", "not-completed" )
|
||||
}
|
||||
|
||||
|
||||
local ag = IPP.AttributeGroup:new(IPP.Attribute.IPP_TAG_OPERATION, attribs)
|
||||
local request = IPP.Request:new(IPP.OperationID.IPP_GET_JOBS, 1)
|
||||
request:addAttributeGroup(ag)
|
||||
|
||||
|
||||
local status, response = HTTP.Request( self.host, self.port, tostring(request) )
|
||||
if ( not(response) ) then
|
||||
return status, response
|
||||
end
|
||||
|
||||
|
||||
local results = {}
|
||||
for _, ag in ipairs(response:getAttributeGroups(IPP.Attribute.IPP_TAG_JOB)) do
|
||||
local uri = ag:getAttributeValue("printer-uri")
|
||||
@@ -407,19 +407,19 @@ Helper = {
|
||||
local size = ag:getAttributeValue("job-k-octets") .. "k"
|
||||
local jobname = ag:getAttributeValue("com.apple.print.JobInfo.PMJobName") or "Unknown"
|
||||
local owner = ag:getAttributeValue("com.apple.print.JobInfo.PMJobOwner") or "Unknown"
|
||||
|
||||
|
||||
results[printer] = results[printer] or {}
|
||||
table.insert(results[printer], {
|
||||
id = id,
|
||||
id = id,
|
||||
time = os.date("%Y-%m-%d %H:%M:%S", tm),
|
||||
state = ( IPP.StateName[tonumber(state)] or "Unknown" ),
|
||||
size = size,
|
||||
owner = owner,
|
||||
jobname = jobname })
|
||||
jobname = jobname })
|
||||
end
|
||||
|
||||
|
||||
local output = {}
|
||||
for name, entries in pairs(results) do
|
||||
for name, entries in pairs(results) do
|
||||
local t = tab.new(5)
|
||||
tab.addrow(t, "id", "time", "state", "size (kb)", "owner", "jobname")
|
||||
for _, entry in ipairs(entries) do
|
||||
@@ -429,10 +429,10 @@ Helper = {
|
||||
table.insert(output, { name = name, tab.dump(t) })
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return output
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
240
nselib/iscsi.lua
240
nselib/iscsi.lua
@@ -8,7 +8,7 @@
|
||||
-- E.g. <code>LoginRequest</code> and <code>LoginResponse</code>
|
||||
--
|
||||
-- Each request can be "serialized" to a string using:
|
||||
-- <code>tostring(request)</code>.
|
||||
-- <code>tostring(request)</code>.
|
||||
-- All responses can be read and instantiated from the socket by calling:
|
||||
-- <code>local status,resp = Response.fromSocket(sock)</code>
|
||||
--
|
||||
@@ -48,15 +48,15 @@ _ENV = stdnse.module("iscsi", stdnse.seeall)
|
||||
|
||||
|
||||
Packet = {
|
||||
|
||||
Opcode = {
|
||||
|
||||
Opcode = {
|
||||
LOGIN = 0x03,
|
||||
TEXT = 0x04,
|
||||
LOGOUT = 0x06,
|
||||
},
|
||||
|
||||
|
||||
LoginRequest = {
|
||||
|
||||
|
||||
CSG = {
|
||||
SecurityNegotiation = 0,
|
||||
LoginOperationalNegotiation = 1,
|
||||
@@ -68,7 +68,7 @@ Packet = {
|
||||
LoginOperationalNegotiation = 1,
|
||||
FullFeaturePhase = 3,
|
||||
},
|
||||
|
||||
|
||||
--- Creates a new instance of LoginRequest
|
||||
--
|
||||
-- @return instance of LoginRequest
|
||||
@@ -92,9 +92,9 @@ Packet = {
|
||||
o.kvp = KVP:new()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
setImmediate = function(self, b) self.immediate = ( b and 1 or 0 ) end,
|
||||
|
||||
|
||||
--- Sets the transit bit
|
||||
--
|
||||
-- @param b boolean containing the new transit value
|
||||
@@ -107,12 +107,12 @@ Packet = {
|
||||
|
||||
--- Sets the CSG values
|
||||
--
|
||||
-- @param csg number containing the new NSG value
|
||||
-- @param csg number containing the new NSG value
|
||||
setCSG = function(self, csg) self.flags.csg = csg end,
|
||||
|
||||
--- Sets the NSG values
|
||||
--
|
||||
-- @param nsg number containing the new NSG value
|
||||
-- @param nsg number containing the new NSG value
|
||||
setNSG = function(self, nsg) self.flags.nsg = nsg end,
|
||||
|
||||
--- Converts the class instance to string
|
||||
@@ -130,27 +130,27 @@ Packet = {
|
||||
for i=1, pad do kvps = kvps .. "\0" end
|
||||
|
||||
local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len
|
||||
local flags = bit.lshift( ( self.flags.transit or 0 ), 7 )
|
||||
local flags = bit.lshift( ( self.flags.transit or 0 ), 7 )
|
||||
flags = flags + bit.lshift( ( self.flags.continue or 0 ), 6)
|
||||
flags = flags + ( self.flags.nsg or 0 )
|
||||
flags = flags + bit.lshift( ( self.flags.csg or 0 ), 2 )
|
||||
|
||||
|
||||
local opcode = self.opcode + bit.lshift((self.immediate or 0), 6)
|
||||
|
||||
local data = bin.pack(">CCCCICSCSSISSIILLA", opcode,
|
||||
flags, self.ver_max, self.ver_min, len,
|
||||
bit.lshift( self.isid.t, 6 ) + bit.band( self.isid.a, 0x3f),
|
||||
self.isid.b, self.isid.c, self.isid.d, self.tsih,
|
||||
self.initiator_task_tag, self.cid, reserved, self.cmdsn,
|
||||
|
||||
local data = bin.pack(">CCCCICSCSSISSIILLA", opcode,
|
||||
flags, self.ver_max, self.ver_min, len,
|
||||
bit.lshift( self.isid.t, 6 ) + bit.band( self.isid.a, 0x3f),
|
||||
self.isid.b, self.isid.c, self.isid.d, self.tsih,
|
||||
self.initiator_task_tag, self.cid, reserved, self.cmdsn,
|
||||
self.expstatsn, reserved, reserved, kvps )
|
||||
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
LoginResponse = {
|
||||
|
||||
|
||||
-- Error messages
|
||||
ErrorMsgs = {
|
||||
[0x0000] = "Success",
|
||||
@@ -172,13 +172,13 @@ Packet = {
|
||||
[0x0301] = "Service unavailable",
|
||||
[0x0302] = "Out of resources",
|
||||
},
|
||||
|
||||
|
||||
-- Error constants
|
||||
Errors = {
|
||||
SUCCESS = 0,
|
||||
AUTH_FAILED = 0x0201,
|
||||
},
|
||||
|
||||
|
||||
--- Creates a new instance of LoginResponse
|
||||
--
|
||||
-- @return instance of LoginResponse
|
||||
@@ -188,54 +188,54 @@ Packet = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the error message
|
||||
getErrorMessage = function( self )
|
||||
return Packet.LoginResponse.ErrorMsgs[self.status_code] or "Unknown error"
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the error code
|
||||
getErrorCode = function( self ) return self.status_code or 0 end,
|
||||
|
||||
|
||||
--- Creates a LoginResponse with data read from the socket
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
-- @return resp instance of LoginResponse
|
||||
fromSocket = function( s )
|
||||
local status, header = s:recv(48)
|
||||
|
||||
if ( not(status) ) then
|
||||
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to read header from socket"
|
||||
end
|
||||
|
||||
|
||||
local resp = Packet.LoginResponse:new()
|
||||
local pos, len = bin.unpack(">I", header, 5)
|
||||
|
||||
|
||||
resp.total_ahs_len = bit.rshift(len, 24)
|
||||
resp.data_seg_len = bit.band(len, 0x00ffffff)
|
||||
pos, resp.status_code = bin.unpack(">S", header, 37)
|
||||
|
||||
|
||||
local pad = ( 4 - ( resp.data_seg_len % 4 ) )
|
||||
pad = ( pad == 4 ) and 0 or pad
|
||||
|
||||
|
||||
local status, data = s:recv( resp.data_seg_len + pad )
|
||||
if ( not(status) ) then
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to read data from socket"
|
||||
end
|
||||
|
||||
|
||||
resp.kvp = KVP:new()
|
||||
for _, kvp in ipairs(stdnse.strsplit( "\0", data )) do
|
||||
local k, v = kvp:match("(.*)=(.*)")
|
||||
if ( v ) then resp.kvp:add( k, v ) end
|
||||
end
|
||||
|
||||
|
||||
return true, resp
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
TextRequest = {
|
||||
|
||||
|
||||
--- Creates a new instance of TextRequest
|
||||
--
|
||||
-- @return instance of TextRequest
|
||||
@@ -243,7 +243,7 @@ Packet = {
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
o.opcode = Packet.Opcode.TEXT
|
||||
o.opcode = Packet.Opcode.TEXT
|
||||
o.flags = {}
|
||||
o.flags.final = 0
|
||||
o.flags.continue = 0
|
||||
@@ -257,37 +257,37 @@ Packet = {
|
||||
o.kvp = KVP:new()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the final bit of the TextRequest
|
||||
setFinal = function( self, b ) self.flags.final = ( b and 1 or 0 ) end,
|
||||
|
||||
--- Sets the continue bit of the TextRequest
|
||||
setContinue = function( self, b ) self.flags.continue = ( b and 1 or 0 ) end,
|
||||
|
||||
|
||||
--- Converts the class instance to string
|
||||
--
|
||||
-- @return string containing the converted instance
|
||||
__tostring = function(self)
|
||||
local flags = bit.lshift( ( self.flags.final or 0 ), 7 )
|
||||
flags = flags + bit.lshift( (self.flags.continue or 0), 6 )
|
||||
flags = flags + bit.lshift( (self.flags.continue or 0), 6 )
|
||||
|
||||
local kvps = tostring(self.kvp)
|
||||
for i=1, (#kvps % 2) do kvps = kvps .. "\0" end
|
||||
self.data_seg_len = #kvps
|
||||
|
||||
|
||||
local len = bit.lshift( self.total_ahs_len, 24 ) + self.data_seg_len
|
||||
local reserved = 0
|
||||
local data = bin.pack(">CCSILIIIILLA", self.opcode, flags, reserved,
|
||||
len, self.lun, self.initiator_task_tag, self.target_trans_tag,
|
||||
len, self.lun, self.initiator_task_tag, self.target_trans_tag,
|
||||
self.cmdsn, self.expstatsn, reserved, reserved, kvps)
|
||||
|
||||
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
TextResponse = {
|
||||
|
||||
|
||||
--- Creates a new instance of TextResponse
|
||||
--
|
||||
-- @return instance of TextResponse
|
||||
@@ -297,7 +297,7 @@ Packet = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Creates a TextResponse with data read from the socket
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -306,7 +306,7 @@ Packet = {
|
||||
fromSocket = function( s )
|
||||
local resp = Packet.TextResponse:new()
|
||||
local textdata = ""
|
||||
|
||||
|
||||
repeat
|
||||
local status, header = s:recv(48)
|
||||
local pos, _, flags, _, _, len = bin.unpack(">CCCCI", header)
|
||||
@@ -314,23 +314,23 @@ Packet = {
|
||||
|
||||
resp.total_ahs_len = bit.rshift(len, 24)
|
||||
resp.data_seg_len = bit.band(len, 0x00ffffff)
|
||||
|
||||
|
||||
local data
|
||||
status, data = s:recv( resp.data_seg_len )
|
||||
|
||||
|
||||
textdata = textdata .. data
|
||||
|
||||
|
||||
until( not(cont) )
|
||||
|
||||
|
||||
resp.records = {}
|
||||
|
||||
local kvps = stdnse.strsplit( "\0", textdata )
|
||||
local record
|
||||
|
||||
|
||||
-- Each target record starts with one text key of the form:
|
||||
-- TargetName=<target-name-goes-here>
|
||||
-- Followed by zero or more address keys of the form:
|
||||
-- TargetAddress=<hostname-or-ipaddress>[:<tcp-port>],
|
||||
-- TargetAddress=<hostname-or-ipaddress>[:<tcp-port>],
|
||||
-- <portal-group-tag>
|
||||
for _, kvp in ipairs(kvps) do
|
||||
local k, v = kvp:match("(.*)%=(.*)")
|
||||
@@ -344,7 +344,7 @@ Packet = {
|
||||
elseif ( k == "TargetAddress" ) then
|
||||
record.addr = record.addr or {}
|
||||
table.insert( record.addr, v )
|
||||
elseif ( not(k) ) then
|
||||
elseif ( not(k) ) then
|
||||
-- this should be the ending empty kvp
|
||||
table.insert(resp.records, record)
|
||||
break
|
||||
@@ -352,14 +352,14 @@ Packet = {
|
||||
stdnse.print_debug("ERROR: iscsi.TextResponse: Unknown target record (%s)", k)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return true, resp
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
--- Class handling a login request
|
||||
LogoutRequest = {
|
||||
|
||||
|
||||
--- Creates a new instance of LogoutRequest
|
||||
--
|
||||
-- @return instance of LogoutRequest
|
||||
@@ -377,8 +377,8 @@ Packet = {
|
||||
o.cmdsn = 0
|
||||
o.expstatsn = 1
|
||||
return o
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
--- Converts the class instance to string
|
||||
--
|
||||
-- @return string containing the converted instance
|
||||
@@ -389,15 +389,15 @@ Packet = {
|
||||
local data = bin.pack(">CCSILISSIILL", opcode, (0x80 + self.reasoncode),
|
||||
reserved, len, reserved,self.initiator_task_tag, self.cid,
|
||||
reserved, self.cmdsn, self.expstatsn, reserved, reserved )
|
||||
|
||||
|
||||
return data
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
--- Class handling the Logout response
|
||||
LogoutResponse = {
|
||||
|
||||
|
||||
--- Creates a new instance of LogoutResponse
|
||||
--
|
||||
-- @return instance of LogoutResponse
|
||||
@@ -407,7 +407,7 @@ Packet = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Creates a LogoutResponse with data read from the socket
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -419,7 +419,7 @@ Packet = {
|
||||
if ( not(status) ) then return status, header end
|
||||
return true, resp
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,7 +427,7 @@ Packet = {
|
||||
-- In addition it keeps track of both immediate packets and the amount of read
|
||||
-- packets and updates cmdsn and expstatsn accordingly.
|
||||
Comm = {
|
||||
|
||||
|
||||
--- Creates a new instance of Comm
|
||||
--
|
||||
-- @return instance of Comm
|
||||
@@ -440,39 +440,39 @@ Comm = {
|
||||
o.socket = socket
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a packet and retrieves the response
|
||||
--
|
||||
-- @param out_packet instance of a packet to send
|
||||
-- @param in_class class of the packet to read
|
||||
-- @return status true on success, false on failure
|
||||
-- @return r decoded instance of in_class
|
||||
-- @return r decoded instance of in_class
|
||||
exchange = function( self, out_packet, in_class )
|
||||
|
||||
|
||||
local expstatsn = ( self.expstatsn == 0 ) and 1 or self.expstatsn
|
||||
|
||||
|
||||
if ( out_packet.immediate and out_packet.immediate == 1 ) then
|
||||
self.cmdsn = self.cmdsn + 1
|
||||
end
|
||||
|
||||
|
||||
out_packet.expstatsn = expstatsn
|
||||
out_packet.cmdsn = self.cmdsn
|
||||
|
||||
|
||||
self.socket:send( tostring( out_packet ) )
|
||||
|
||||
local status, r = in_class.fromSocket( self.socket )
|
||||
self.expstatsn = self.expstatsn + 1
|
||||
|
||||
|
||||
return status, r
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- A buffered socket implementation
|
||||
Socket =
|
||||
{
|
||||
|
||||
{
|
||||
|
||||
--- Creates a new instance of Socket
|
||||
--
|
||||
-- @return instance of Socket
|
||||
@@ -484,7 +484,7 @@ Socket =
|
||||
o.Buffer = nil
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
|
||||
--- Establishes a connection.
|
||||
--
|
||||
@@ -497,7 +497,7 @@ Socket =
|
||||
self.Socket:set_timeout(10000)
|
||||
return self.Socket:connect( hostid, port, protocol )
|
||||
end,
|
||||
|
||||
|
||||
--- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -505,7 +505,7 @@ Socket =
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
@@ -515,9 +515,9 @@ Socket =
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
@@ -525,13 +525,13 @@ Socket =
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -543,7 +543,7 @@ Socket =
|
||||
|
||||
--- Key/Value pairs class
|
||||
KVP = {
|
||||
|
||||
|
||||
--- Creates a new instance of KVP
|
||||
--
|
||||
-- @return instance of KVP
|
||||
@@ -554,7 +554,7 @@ KVP = {
|
||||
o.kvp = {}
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Adds a key/value pair
|
||||
--
|
||||
-- @param key string containing the key name
|
||||
@@ -562,12 +562,12 @@ KVP = {
|
||||
add = function( self, key, value )
|
||||
table.insert( self.kvp, {[key]=value} )
|
||||
end,
|
||||
|
||||
|
||||
--- Gets all values for a specific key
|
||||
--
|
||||
-- @param key string containing the name of the key to retrieve
|
||||
-- @return values table containing all values for the specified key
|
||||
get = function( self, key )
|
||||
get = function( self, key )
|
||||
local values = {}
|
||||
for _, kvp in ipairs(self.kvp) do
|
||||
for k, v in pairs( kvp ) do
|
||||
@@ -578,7 +578,7 @@ KVP = {
|
||||
end
|
||||
return values
|
||||
end,
|
||||
|
||||
|
||||
--- Returns all key value pairs as string delimited by \0
|
||||
-- eg. "key1=val1\0key2=val2\0"
|
||||
--
|
||||
@@ -592,12 +592,12 @@ KVP = {
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- CHAP authentication class
|
||||
CHAP = {
|
||||
|
||||
|
||||
--- Calculate a CHAP - response
|
||||
--
|
||||
-- @param identifier number containing the CHAP identifier
|
||||
@@ -607,12 +607,12 @@ CHAP = {
|
||||
calcResponse = function( identifier, challenge, secret )
|
||||
return openssl.md5( identifier .. secret .. challenge )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- The helper class contains functions with more descriptive names
|
||||
Helper = {
|
||||
|
||||
|
||||
--- Creates a new instance of the Helper class
|
||||
--
|
||||
-- @param host table as received by the script action function
|
||||
@@ -626,7 +626,7 @@ Helper = {
|
||||
o.socket = Socket:new()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Connects to the iSCSI target
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -638,7 +638,7 @@ Helper = {
|
||||
self.comm = Comm:new( self.socket )
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Attempts to discover accessible iSCSI targets on the remote server
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -648,18 +648,18 @@ Helper = {
|
||||
-- err string containing an error message is status is false
|
||||
discoverTargets = function( self )
|
||||
local p = Packet.LoginRequest:new()
|
||||
|
||||
|
||||
p:setTransit(true)
|
||||
p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation)
|
||||
p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" )
|
||||
p.kvp:add( "SessionType", "Discovery" )
|
||||
p.kvp:add( "AuthMethod", "None" )
|
||||
|
||||
|
||||
local status, resp = self.comm:exchange( p, Packet.LoginResponse )
|
||||
if ( not(status) ) then
|
||||
return false, ("ERROR: iscsi.Helper.discoverTargets: %s"):format(resp)
|
||||
end
|
||||
|
||||
|
||||
local auth_method = resp.kvp:get("AuthMethod")[1]
|
||||
if ( auth_method:upper() ~= "NONE" ) then
|
||||
return false, "ERROR: iscsi.Helper.discoverTargets: Unsupported authentication method"
|
||||
@@ -674,9 +674,9 @@ Helper = {
|
||||
p.kvp:add( "MaxRecvDataSegmentLength", "65536")
|
||||
p.kvp:add( "DefaultTime2Wait", "0")
|
||||
p.kvp:add( "DefaultTime2Retain", "60")
|
||||
|
||||
|
||||
status, resp = self.comm:exchange( p, Packet.LoginResponse )
|
||||
|
||||
|
||||
p = Packet.TextRequest:new()
|
||||
p:setFinal(true)
|
||||
p.kvp:add( "SendTargets", "All" )
|
||||
@@ -685,13 +685,13 @@ Helper = {
|
||||
if ( not(resp.records) ) then
|
||||
return false, "iscsi.discoverTargets: response returned no targets"
|
||||
end
|
||||
|
||||
|
||||
for _, record in ipairs(resp.records) do
|
||||
table.sort( record.addr, function(a, b) local c = ipOps.compare_ip(a:match("(.-):"), "le", b:match("(.-):")); return c end )
|
||||
end
|
||||
return true, resp.records
|
||||
end,
|
||||
|
||||
|
||||
--- Logs out from the iSCSI target
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -713,25 +713,25 @@ Helper = {
|
||||
login = function( self, target_name, username, password, auth_method )
|
||||
|
||||
local auth_method = auth_method or "None"
|
||||
|
||||
|
||||
if ( not(target_name) ) then
|
||||
return false, "No target name specified"
|
||||
end
|
||||
|
||||
if ( auth_method:upper()~= "NONE" and
|
||||
|
||||
if ( auth_method:upper()~= "NONE" and
|
||||
auth_method:upper()~= "CHAP" ) then
|
||||
return false, "Unknown authentication method"
|
||||
end
|
||||
|
||||
|
||||
local p = Packet.LoginRequest:new()
|
||||
|
||||
|
||||
p:setTransit(true)
|
||||
p:setNSG(Packet.LoginRequest.NSG.LoginOperationalNegotiation)
|
||||
p.kvp:add( "InitiatorName", "iqn.1991-05.com.microsoft:nmap_iscsi_probe" )
|
||||
p.kvp:add( "SessionType", "Normal" )
|
||||
p.kvp:add( "TargetName", target_name )
|
||||
p.kvp:add( "AuthMethod", auth_method )
|
||||
|
||||
|
||||
if ( not(self.comm) ) then
|
||||
return false, "ERROR: iscsi.Helper.login: Not connected"
|
||||
end
|
||||
@@ -746,28 +746,28 @@ Helper = {
|
||||
elseif ( auth_method:upper()=="NONE" ) then
|
||||
return true, resp
|
||||
end
|
||||
|
||||
|
||||
p = Packet.LoginRequest:new()
|
||||
p.kvp:add( "CHAP_A", "5" )
|
||||
status, resp = self.comm:exchange( p, Packet.LoginResponse )
|
||||
if ( not(status) ) then
|
||||
return false, ("ERROR: iscsi.Helper.login: %s"):format(resp)
|
||||
end
|
||||
|
||||
|
||||
local alg = resp.kvp:get("CHAP_A")[1]
|
||||
if ( alg ~= "5" ) then return false, "Unsupported authentication algorithm" end
|
||||
|
||||
local chall = resp.kvp:get("CHAP_C")[1]
|
||||
if ( not(chall) ) then return false, "Failed to decode challenge" end
|
||||
chall = bin.pack("H", chall:sub(3))
|
||||
|
||||
|
||||
local ident = resp.kvp:get("CHAP_I")[1]
|
||||
if (not(ident)) then return false, "Failed to decoded identifier" end
|
||||
ident = string.char(tonumber(ident))
|
||||
|
||||
local resp = CHAP.calcResponse( ident, chall, password )
|
||||
resp = "0x" .. select(2, bin.unpack("H16", resp))
|
||||
|
||||
|
||||
p = Packet.LoginRequest:new()
|
||||
p:setImmediate(true)
|
||||
p:setTransit(true)
|
||||
@@ -783,13 +783,13 @@ Helper = {
|
||||
if ( resp:getErrorCode() ~= Packet.LoginResponse.Errors.SUCCESS ) then
|
||||
return false, "Login failed"
|
||||
end
|
||||
|
||||
|
||||
return true, resp
|
||||
end,
|
||||
|
||||
|
||||
--- Disconnects the socket from the server
|
||||
close = function(self) self.socket:close() end
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
118
nselib/isns.lua
118
nselib/isns.lua
@@ -15,20 +15,20 @@ local table = require('table')
|
||||
_ENV = stdnse.module("isns", stdnse.seeall);
|
||||
|
||||
iSCSI = {
|
||||
|
||||
|
||||
NodeType = {
|
||||
TARGET = 1,
|
||||
INITIATOR = 2,
|
||||
INITIATOR = 2,
|
||||
CONTROL = 4,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Header = {
|
||||
|
||||
VERSION = 1,
|
||||
|
||||
|
||||
--
|
||||
-- Creates a header instance
|
||||
--
|
||||
@@ -41,7 +41,7 @@ Header = {
|
||||
new = function(self, func_id, pdu_len, flags, trans_id, seq_id)
|
||||
local o = {
|
||||
ver = Header.VERSION,
|
||||
func_id = func_id,
|
||||
func_id = func_id,
|
||||
flags = flags,
|
||||
trans_id = trans_id,
|
||||
seq_id = seq_id,
|
||||
@@ -51,7 +51,7 @@ Header = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Parses a opaque string and creates a new Header instance
|
||||
--
|
||||
@@ -60,25 +60,25 @@ Header = {
|
||||
parse = function(data)
|
||||
local hdr = Header:new()
|
||||
local pos
|
||||
|
||||
|
||||
pos, hdr.ver, hdr.func_id, hdr.pdu_len, hdr.flags, hdr.trans_id,
|
||||
hdr.seq_id = bin.unpack(">SSSSSS", data)
|
||||
|
||||
|
||||
return hdr
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Converts the instance to an opaque string
|
||||
-- @return str containing an opaque string
|
||||
__tostring = function(self)
|
||||
return bin.pack(">SSSSSS", self.ver, self.func_id,
|
||||
return bin.pack(">SSSSSS", self.ver, self.func_id,
|
||||
self.pdu_len, self.flags, self.trans_id, self.seq_id )
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
Attribute = {
|
||||
|
||||
|
||||
Tag = {
|
||||
ISNS_TAG_DELIMITER = 0,
|
||||
ISNS_TAG_ENTITY_IDENTIFIER = 1,
|
||||
@@ -164,7 +164,7 @@ Attribute = {
|
||||
ISNS_VENDOR_SPECIFIC_DDSET_BASE = 1281,
|
||||
ISNS_VENDOR_SPECIFIC_OTHER_BASE = 1537,
|
||||
},
|
||||
|
||||
|
||||
--
|
||||
-- Creates a new Attribute instance
|
||||
--
|
||||
@@ -178,7 +178,7 @@ Attribute = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Creates a new Attribute instance
|
||||
--
|
||||
@@ -187,20 +187,20 @@ Attribute = {
|
||||
parse = function(data)
|
||||
local attr = Attribute:new()
|
||||
local pos
|
||||
|
||||
|
||||
pos, attr.tag, attr.len = bin.unpack(">II", data)
|
||||
pos, attr.val = bin.unpack(">A" .. attr.len, pos)
|
||||
|
||||
|
||||
return attr
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Converts the instance to an opaque string
|
||||
-- @return str containing an opaque string
|
||||
__tostring = function(self)
|
||||
return bin.pack(">IIA", self.tag, self.len, self.val)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Attributes = {
|
||||
@@ -214,7 +214,7 @@ Attributes = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Adds a new Attribute to the table
|
||||
-- @param tag number containing the tag number
|
||||
@@ -223,7 +223,7 @@ Attributes = {
|
||||
add = function(self, tag, val, len)
|
||||
table.insert(self, Attribute:new(tag, val, len))
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Converts the instance to an opaque string
|
||||
-- @return str containing an opaque string
|
||||
@@ -234,11 +234,11 @@ Attributes = {
|
||||
end
|
||||
return str
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Request = {
|
||||
|
||||
|
||||
FuncId = {
|
||||
DevAttrReg = 0x0001,
|
||||
DevAttrQry = 0x0002,
|
||||
@@ -255,7 +255,7 @@ Request = {
|
||||
ESI = 0x000D,
|
||||
Heartbeat = 0x000E,
|
||||
},
|
||||
|
||||
|
||||
--
|
||||
-- Creates a new Request message
|
||||
-- @param func_id number containing the function ID of the message
|
||||
@@ -274,7 +274,7 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Converts the instance to an opaque string
|
||||
-- @return str containing an opaque string
|
||||
@@ -282,12 +282,12 @@ Request = {
|
||||
return tostring(self.header) .. tostring(self.data) ..
|
||||
( self.auth and self.auth or "" )
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
Error = {
|
||||
[0] = "Successful",
|
||||
[1] = "Unknown Error",
|
||||
@@ -314,7 +314,7 @@ Response = {
|
||||
[22] = "Invalid Deregistration",
|
||||
[23] = "Registration Feature Not Supported",
|
||||
},
|
||||
|
||||
|
||||
--
|
||||
-- Creates a new Response instance
|
||||
-- @return o new instance of Response
|
||||
@@ -324,7 +324,7 @@ Response = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Creates a new Response instance
|
||||
--
|
||||
@@ -334,12 +334,12 @@ Response = {
|
||||
local hdr = Header.parse(data)
|
||||
local pos = #(tostring(hdr)) + 1
|
||||
local resp = Response:new()
|
||||
|
||||
|
||||
pos, resp.error = bin.unpack(">I", data, pos)
|
||||
if ( resp.error ~= 0 ) then
|
||||
return resp
|
||||
end
|
||||
|
||||
|
||||
while( pos < #data ) do
|
||||
local tag, len, val
|
||||
pos, tag, len = bin.unpack(">II", data, pos)
|
||||
@@ -348,12 +348,12 @@ Response = {
|
||||
end
|
||||
return resp
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Session = {
|
||||
|
||||
|
||||
--
|
||||
-- Creates a new Session instance
|
||||
-- @param host table
|
||||
@@ -370,7 +370,7 @@ Session = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Connects to the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -379,7 +379,7 @@ Session = {
|
||||
self.socket:set_timeout(5000)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Sends data to the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -392,13 +392,13 @@ Session = {
|
||||
-- update the sequence and transaction ID's
|
||||
req.header.seq_id = self.seq_id
|
||||
req.header.trans_id = self.trans_id
|
||||
|
||||
|
||||
local status, err = self.socket:send(tostring(req))
|
||||
self.trans_id = self.trans_id + 1
|
||||
|
||||
|
||||
return status, err
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Receives data from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -409,19 +409,19 @@ Session = {
|
||||
if ( not(status) ) then
|
||||
return status, buf_hdr
|
||||
end
|
||||
|
||||
|
||||
local hdr = Header.parse(buf_hdr)
|
||||
|
||||
|
||||
-- receive the data
|
||||
local buf_data = nil
|
||||
status, buf_data = self.socket:receive_buf(match.numbytes(hdr.pdu_len), true)
|
||||
if ( not(status) ) then
|
||||
return status, buf_data
|
||||
end
|
||||
|
||||
|
||||
return true, Response.parse(buf_hdr .. buf_data)
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.close()
|
||||
end
|
||||
@@ -429,8 +429,8 @@ Session = {
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
--
|
||||
|
||||
--
|
||||
-- Creates a new Helper instance
|
||||
-- @param host param
|
||||
-- @param port param
|
||||
@@ -441,14 +441,14 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Connects to the server
|
||||
-- @return status true on success, false on failure
|
||||
connect = function(self)
|
||||
return self.session:connect()
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Lists portals
|
||||
-- @return status true on success, false on failure
|
||||
@@ -464,23 +464,23 @@ Helper = {
|
||||
attribs:add(Attribute.Tag.ISNS_TAG_ENTITY_IDENTIFIER)
|
||||
|
||||
local flags = 0x8c00 -- Sender is iSNS client, Last PDU, First PDU
|
||||
|
||||
|
||||
local req = Request:new(Request.FuncId.DevAttrQry, flags, tostring(attribs))
|
||||
if ( not(self.session:send(req)) ) then
|
||||
return false, "Failed to send message to server"
|
||||
end
|
||||
|
||||
|
||||
local status, resp = self.session:receive()
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive message from server"
|
||||
end
|
||||
|
||||
|
||||
local results = {}
|
||||
local addr, proto, port
|
||||
for _, attr in ipairs(resp.attrs) do
|
||||
if ( attr.tag == Attribute.Tag.ISNS_TAG_PORTAL_IP_ADDRESS ) then
|
||||
addr = attr.val
|
||||
local pos, is_ipv4 = bin.unpack("A12", addr)
|
||||
local pos, is_ipv4 = bin.unpack("A12", addr)
|
||||
if ( is_ipv4 == "\0\0\0\0\0\0\0\0\0\0\xFF\xFF" ) then
|
||||
local pos, bin_ip = bin.unpack("B4", addr, 13)
|
||||
addr = ipops.bin_to_ip(bin_ip)
|
||||
@@ -491,7 +491,7 @@ Helper = {
|
||||
elseif ( attr.tag == Attribute.Tag.ISNS_TAG_PORTAL_TCP_UDP_PORT ) then
|
||||
local pos, s1
|
||||
pos, s1, port = bin.unpack(">SS", attr.val)
|
||||
|
||||
|
||||
if ( s1 == 1 ) then
|
||||
proto = "udp"
|
||||
elseif ( s1 == 0 ) then
|
||||
@@ -506,12 +506,12 @@ Helper = {
|
||||
end
|
||||
return true, results
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Lists iSCSI nodes
|
||||
-- @return status true on success, false on failure
|
||||
-- @return resulst list of iSCSI nodes, err string on failure
|
||||
listISCINodes = function(self)
|
||||
listISCINodes = function(self)
|
||||
local attribs = Attributes:new()
|
||||
local name = "iqn.control.node\0por"
|
||||
attribs:add(Attribute.Tag.ISNS_TAG_ISCSI_NAME, name)
|
||||
@@ -521,17 +521,17 @@ Helper = {
|
||||
attribs:add(Attribute.Tag.ISNS_TAG_ISCSI_NODE_TYPE)
|
||||
|
||||
local flags = 0x8c00 -- Sender is iSNS client, Last PDU, First PDU
|
||||
|
||||
|
||||
local req = Request:new(Request.FuncId.DevAttrQry, flags, tostring(attribs))
|
||||
if ( not(self.session:send(req)) ) then
|
||||
return false, "Failed to send message to server"
|
||||
end
|
||||
|
||||
|
||||
local status, resp = self.session:receive()
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive message from server"
|
||||
end
|
||||
|
||||
|
||||
local name, ntype
|
||||
local results = {}
|
||||
for _, attr in ipairs(resp.attrs) do
|
||||
@@ -553,14 +553,14 @@ Helper = {
|
||||
table.insert(results, { name = name:match("^([^\0]*)"), type = ntype })
|
||||
name, ntype = nil, nil
|
||||
end
|
||||
end
|
||||
end
|
||||
return true, results
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.session:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
326
nselib/jdwp.lua
326
nselib/jdwp.lua
@@ -1,20 +1,20 @@
|
||||
--- JDWP (Java Debug Wire Protocol) library implementing a set of commands needed to
|
||||
-- use remote debugging port and inject java bytecode.
|
||||
--- JDWP (Java Debug Wire Protocol) library implementing a set of commands needed to
|
||||
-- use remote debugging port and inject java bytecode.
|
||||
--
|
||||
-- There are two basic packet types in JDWP protool.
|
||||
-- Command packet and reply packet. Command packets are sent by
|
||||
-- There are two basic packet types in JDWP protool.
|
||||
-- Command packet and reply packet. Command packets are sent by
|
||||
-- a debugger to a remote port which replies with a reply packet.
|
||||
--
|
||||
-- Simple handshake is needed to start the communication.
|
||||
--
|
||||
-- Simple handshake is needed to start the communication.
|
||||
-- The debugger sends a "JDWP-Handshake" string and gets the same as a reply.
|
||||
-- Each (command and reply packet) has an id field since communication can be asynchronous.
|
||||
-- Packet id can be monothonicaly increasing.
|
||||
-- Although communication can be asynchronous, it is not (at least in my tests) so the same
|
||||
-- packet id can be used for all communication.
|
||||
--
|
||||
-- packet id can be used for all communication.
|
||||
--
|
||||
-- To start the connection, script should call <code>jdwp.connect()</code> which returns success
|
||||
-- status and a socket. All other protocol functions require a socket as their first parameter.
|
||||
--
|
||||
--
|
||||
-- Example of initiating connection:
|
||||
-- <code>
|
||||
-- local status,socket = jdwp.connect(host,port)
|
||||
@@ -27,7 +27,7 @@
|
||||
--
|
||||
-- References:
|
||||
-- * http://docs.oracle.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html
|
||||
--
|
||||
--
|
||||
--@copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
--@author Aleksandar Nikolic
|
||||
--
|
||||
@@ -42,7 +42,7 @@ local nmap = require "nmap"
|
||||
|
||||
_ENV = stdnse.module("jdwp", stdnse.seeall)
|
||||
|
||||
-- JDWP protocol specific constants
|
||||
-- JDWP protocol specific constants
|
||||
JDWP_CONSTANTS = {
|
||||
handshake = "JDWP-Handshake" -- Connection initialization handshake
|
||||
}
|
||||
@@ -105,7 +105,7 @@ ERROR_CODES = {
|
||||
[509] = "TRANSPORT_LOAD Unable to load the transport.",
|
||||
[510] = "TRANSPORT_INIT Unable to initialize the transport.",
|
||||
[511] = "NATIVE_METHOD",
|
||||
[512] = "INVALID_COUNT The count is invalid."
|
||||
[512] = "INVALID_COUNT The count is invalid."
|
||||
}
|
||||
|
||||
-- JDWP protocol Command packet as described at
|
||||
@@ -123,17 +123,17 @@ JDWPCommandPacket = {
|
||||
data = data
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Packs command packet as a string od bytes, ready to be sent
|
||||
-- to the target debugee.
|
||||
pack = function(self)
|
||||
local packed_packet
|
||||
if self.data == nil then
|
||||
if self.data == nil then
|
||||
packed_packet = bin.pack(">I",11) -- lenght - minimal header is 11 bytes
|
||||
else
|
||||
else
|
||||
packed_packet = bin.pack(">I",11 + #self.data) -- lenght with data
|
||||
end
|
||||
packed_packet = packed_packet .. bin.pack(">I",self.id)
|
||||
@@ -147,7 +147,7 @@ JDWPCommandPacket = {
|
||||
end
|
||||
}
|
||||
|
||||
-- JDWP protocol Reply packet as described at
|
||||
-- JDWP protocol Reply packet as described at
|
||||
-- http://docs.oracle.com/javase/6/docs/technotes/guides/jpda/jdwp-spec.html
|
||||
-- Reply packets are recognized by 0x80 in flag field.
|
||||
JDWPReplyPacket = {
|
||||
@@ -161,10 +161,10 @@ JDWPReplyPacket = {
|
||||
data = data -- reply data, contents depend on the command
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the reply into JDWPReplyPacket table.
|
||||
parse_reply = function(self,reply_packet)
|
||||
local pos,length,id,flags,error_code,data
|
||||
@@ -172,21 +172,21 @@ JDWPReplyPacket = {
|
||||
pos, id = bin.unpack(">I",reply_packet,pos)
|
||||
pos, flags = bin.unpack(">C",reply_packet,pos)
|
||||
pos, error_code = bin.unpack(">S",reply_packet,pos)
|
||||
data = string.sub(reply_packet,pos)
|
||||
data = string.sub(reply_packet,pos)
|
||||
if flags == 0x80 then
|
||||
return true, JDWPReplyPacket:new(length,id,error_code,data)
|
||||
end
|
||||
stdnse.print_debug(2,"JDWP error parsing reply. Wrong reply packet flag. Raw data: ", stdnse.tohex(reply_packet))
|
||||
return false, "JDWP error parsing reply."
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- Negotiates the initial debugger-debugee handshake.
|
||||
--
|
||||
--@param host Host to connect to.
|
||||
--@param port Port to connect to.
|
||||
--@return (status,socket) If status is false, socket is error message, otherwise socket is
|
||||
--@return (status,socket) If status is false, socket is error message, otherwise socket is
|
||||
-- a newly created socket with initial handshake finished.
|
||||
function connect(host,port)
|
||||
local status, result,err
|
||||
@@ -195,7 +195,7 @@ function connect(host,port)
|
||||
local status, err = socket:connect(host, port)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP could not connect: %s",err)
|
||||
return status, err
|
||||
return status, err
|
||||
end
|
||||
status, err = socket:send(JDWP_CONSTANTS.handshake)
|
||||
if not status then
|
||||
@@ -215,7 +215,7 @@ function connect(host,port)
|
||||
end
|
||||
|
||||
--- Helper function to pack regular string into UTF-8 string.
|
||||
--
|
||||
--
|
||||
--@param data String to pack into UTF-8.
|
||||
--@return utf8_string UTF-8 packed string. Four bytes lenght followed by the string its self.
|
||||
function toUTF8(data)
|
||||
@@ -223,8 +223,8 @@ function toUTF8(data)
|
||||
return utf8_string
|
||||
end
|
||||
|
||||
--- Helper function to read all Reply packed data which might be fragmented
|
||||
-- over multipe packets.
|
||||
--- Helper function to read all Reply packed data which might be fragmented
|
||||
-- over multipe packets.
|
||||
--
|
||||
--@param socket Socket to receive from.
|
||||
--@return (status,data) If status is false, error string is returned, else data contains read ReplyPacket bytes.
|
||||
@@ -238,17 +238,17 @@ function receive_all(socket)
|
||||
while expected_length > #data do -- read until we get all the ReplyPacket data
|
||||
status,result = socket:receive()
|
||||
if not status then
|
||||
return true, data -- if somethign is wrong,return partial data
|
||||
return true, data -- if somethign is wrong,return partial data
|
||||
end
|
||||
data = data .. result
|
||||
end
|
||||
return true,data
|
||||
return true,data
|
||||
end
|
||||
|
||||
--- Helper function to extract ascii string from UTF-8
|
||||
--
|
||||
-- Writen in this way so it can be used interchangeably with bin.unpack().
|
||||
--
|
||||
--
|
||||
--@param data Data from which to extract the string.
|
||||
--@param pos Offset into data string where to begin.
|
||||
--@return (pos,ascii_string) Returns position where the string extraction ended and actuall ascii string.
|
||||
@@ -268,32 +268,32 @@ end
|
||||
--- Helper function that sends the Command packet and parses the reply.
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param command <code>JDWPCommandPacket</code> to send.
|
||||
--@param command <code>JDWPCommandPacket</code> to send.
|
||||
--@return (status,data) If status is false, data contains specified error code message. If true, data contains data from the reply.
|
||||
function executeCommand(socket,command)
|
||||
socket:send(command:pack())
|
||||
local status, result = receive_all(socket)
|
||||
if not status then
|
||||
return false, "JDWP executeCommand() didn't get a reply."
|
||||
end
|
||||
end
|
||||
local reply_packet
|
||||
status, reply_packet = JDWPReplyPacket:parse_reply(result)
|
||||
status, reply_packet = JDWPReplyPacket:parse_reply(result)
|
||||
if not status then
|
||||
return false, reply_packet
|
||||
end
|
||||
end
|
||||
if not (reply_packet.error_code == 0) then -- we have a packet with error , error code 0 means no error occured
|
||||
return false, ERROR_CODES[reply_packet.error_code]
|
||||
end
|
||||
end
|
||||
local data = reply_packet.data
|
||||
return true, data
|
||||
end
|
||||
|
||||
--- VirtualMachine Command Set (1)
|
||||
--- VirtualMachine Command Set (1)
|
||||
-- Commands targeted at the debugggee virtual machine.
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine
|
||||
|
||||
|
||||
--- Version Command (1)
|
||||
--- Version Command (1)
|
||||
-- Returns the JDWP version implemented by the target VM as a table.
|
||||
--
|
||||
-- Returns a table with following values:
|
||||
@@ -303,10 +303,10 @@ end
|
||||
-- * 'vmVersion' String representing version of the debuggee VM.
|
||||
-- * 'vmName' Name of the debuggee VM.
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_Version
|
||||
--
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@return (status,version_info) If status is false, version_info is an error string, else it contains remote VM version info.
|
||||
--@param id Packet id.
|
||||
--@return (status,version_info) If status is false, version_info is an error string, else it contains remote VM version info.
|
||||
function getVersion(socket,id)
|
||||
local command = JDWPCommandPacket:new(id,1,1,nil) -- Version Command (1)
|
||||
local status, data = executeCommand(socket,command)
|
||||
@@ -332,17 +332,17 @@ end
|
||||
|
||||
--- Classes by Signature command (2)
|
||||
-- Returns reference types for all the classes loaded by the target VM which match the given signature.
|
||||
--
|
||||
--
|
||||
-- Given the class signature (like "Ljava/lang/Class") returns it's reference ID which can be used to reference that class
|
||||
-- in other commands. Returns a list of tables containing following values:
|
||||
-- * 'refTypeTag' JNI type tag
|
||||
-- * 'referenceTypeID' Reference type of the class
|
||||
-- * 'referenceTypeID' Reference type of the class
|
||||
-- * 'status' Current class status.
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_ClassesBySignature
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param signature Signature of the class.
|
||||
--@param id Packet id.
|
||||
--@param signature Signature of the class.
|
||||
--@return (status,classes) If status is false, classes is an error string, else it contains list of found classes.
|
||||
function getClassBySignature(socket,id,signature)
|
||||
local command = JDWPCommandPacket:new(id,1,2,toUTF8(signature))
|
||||
@@ -356,11 +356,11 @@ function getClassBySignature(socket,id,signature)
|
||||
local pos,number_of_classes = bin.unpack(">i",data)
|
||||
|
||||
for i = 1, number_of_classes do
|
||||
local class_info = {
|
||||
local class_info = {
|
||||
refTypeTag = nil,
|
||||
referenceTypeID = nil,
|
||||
status = nil
|
||||
}
|
||||
}
|
||||
pos, class_info.refTypeTag = bin.unpack("c",data,pos)
|
||||
pos, class_info.referenceTypeID = bin.unpack(">L",data,pos)
|
||||
pos, class_info.status = bin.unpack(">i",data,pos)
|
||||
@@ -369,13 +369,13 @@ function getClassBySignature(socket,id,signature)
|
||||
return true, classes
|
||||
end
|
||||
|
||||
--- AllThreads Command (4)
|
||||
--- AllThreads Command (4)
|
||||
-- Returns all threads currently running in the target VM .
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_AllThreads
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@return (status, threads) If status is false threads contains an error string, else it conatins a list of all threads in the debuggee VM.
|
||||
function getAllThreads(socket,id)
|
||||
local command = JDWPCommandPacket:new(id,1,4,nil)
|
||||
@@ -387,7 +387,7 @@ function getAllThreads(socket,id)
|
||||
-- parse data
|
||||
local pos,number_of_threads = bin.unpack(">i",data)
|
||||
local threads = {}
|
||||
for i = 1, number_of_threads do
|
||||
for i = 1, number_of_threads do
|
||||
local thread
|
||||
pos, thread = bin.unpack(">L",data,pos)
|
||||
table.insert(threads,thread)
|
||||
@@ -395,13 +395,13 @@ function getAllThreads(socket,id)
|
||||
return true, threads
|
||||
end
|
||||
|
||||
--- Resume Command (9)
|
||||
--- Resume Command (9)
|
||||
-- Resumes execution of the application after the suspend command or an event has stopped it.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_Resume
|
||||
--
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@return (status, nil) If status is false error string is returned, else it's null since this command has no data in the reply.
|
||||
function resumeVM(socket,id)
|
||||
local command = JDWPCommandPacket:new(id,1,9,nil)
|
||||
@@ -409,9 +409,9 @@ function resumeVM(socket,id)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP resumeVM() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
-- wait for event notification
|
||||
status, data = receive_all(socket)
|
||||
status, data = receive_all(socket)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP resumeVM() event notification failed: %s", data)
|
||||
end
|
||||
@@ -424,7 +424,7 @@ end
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_CreateString
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param ascii_string String to create.
|
||||
--@return (status, stringID) If status is false error string is returned, else stringID is newly created string.
|
||||
function createString(socket,id,ascii_string)
|
||||
@@ -433,24 +433,24 @@ function createString(socket,id,ascii_string)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP createString() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
local _,stringID = bin.unpack(">L",data)
|
||||
return true, stringID
|
||||
end
|
||||
|
||||
--- AllClassesWithGeneric Command (20)
|
||||
-- Returns reference types and signatures for all classes currently loaded by the target VM.
|
||||
--
|
||||
-- Returns a list of tables containing following info:
|
||||
-- * 'refTypeTag' Kind of following reference type.
|
||||
-- * 'typeID' Loaded reference type
|
||||
-- * 'signature' The JNI signature of the loaded reference type.
|
||||
--
|
||||
-- Returns a list of tables containing following info:
|
||||
-- * 'refTypeTag' Kind of following reference type.
|
||||
-- * 'typeID' Loaded reference type
|
||||
-- * 'signature' The JNI signature of the loaded reference type.
|
||||
-- * 'genericSignature' The generic signature of the loaded reference type or an empty string if there is none.
|
||||
-- * 'status' The current class status.
|
||||
-- * 'status' The current class status.
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_VirtualMachine_AllClassesWithGeneric
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@return (status, all_classes) If status is false all_classes contains an error string, else it is a list of loaded classes information.
|
||||
function getAllClassesWithGeneric(socket,id)
|
||||
local command = JDWPCommandPacket:new(id,1,20,nil)
|
||||
@@ -462,7 +462,7 @@ function getAllClassesWithGeneric(socket,id)
|
||||
-- parse data
|
||||
local all_classes = {}
|
||||
local pos,number_of_classes = bin.unpack(">i",data)
|
||||
|
||||
|
||||
for i = 0 , number_of_classes do
|
||||
local class = {
|
||||
refTypeTag = nil,
|
||||
@@ -482,7 +482,7 @@ function getAllClassesWithGeneric(socket,id)
|
||||
return true, all_classes
|
||||
end
|
||||
|
||||
--- ReferenceType Command Set (2)
|
||||
--- ReferenceType Command Set (2)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType
|
||||
|
||||
|
||||
@@ -492,7 +492,7 @@ end
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType_SignatureWithGeneric
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param classID Reference type id of the class to get the signature from.
|
||||
--@return (status, signature) If status is false signature contains an error string, else it is class signature (like "Ljava/lang/Class").
|
||||
function getSignatureWithGeneric(socket,id,classID)
|
||||
@@ -512,14 +512,14 @@ end
|
||||
--
|
||||
-- Returns a list of tables containing following fields for each method:
|
||||
-- * 'methodID' Method ID which can be used to call the method.
|
||||
-- * 'name' The name of the method.
|
||||
-- * 'signature' The JNI signature of the method.
|
||||
-- * 'generic_signature' The generic signature of the method, or an empty string if there is none.
|
||||
-- * 'name' The name of the method.
|
||||
-- * 'signature' The JNI signature of the method.
|
||||
-- * 'generic_signature' The generic signature of the method, or an empty string if there is none.
|
||||
-- * 'modBits' The modifier bit flags (also known as access flags) which provide additional information on the method declaration.
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ReferenceType_MethodsWithGeneric
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param classID Reference type id of the class to get the list of methods.
|
||||
--@return (status, signature) If status is false methods contains an error string, else it a list of methods information.
|
||||
function getMethodsWithGeneric(socket,id,classID)
|
||||
@@ -529,7 +529,7 @@ function getMethodsWithGeneric(socket,id,classID)
|
||||
stdnse.print_debug(2,"JDWP getMethodsWithGeneric() error : %s",data)
|
||||
return false,data
|
||||
end
|
||||
-- parse data
|
||||
-- parse data
|
||||
local methods = {}
|
||||
local pos,number_of_methods = bin.unpack(">i",data)
|
||||
|
||||
@@ -540,7 +540,7 @@ function getMethodsWithGeneric(socket,id,classID)
|
||||
signature = nil,
|
||||
generic_signature = nil,
|
||||
modBits = nil
|
||||
}
|
||||
}
|
||||
pos, method_info.methodID = bin.unpack(">i",data,pos)
|
||||
pos,method_info.name = extract_string(data,pos)
|
||||
pos, method_info.signature = extract_string(data,pos)
|
||||
@@ -554,14 +554,14 @@ end
|
||||
--- ClassType Command Set (3)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType
|
||||
|
||||
--- InvokeMethod Command (3)
|
||||
--- InvokeMethod Command (3)
|
||||
-- Invokes a class' static method and returns the reply data.
|
||||
--
|
||||
--
|
||||
-- Reply data can vary so parsing is left to the function caller.
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType_InvokeMethod
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param classID Reference type id of the class.
|
||||
--@param methodID ID of the static method to call.
|
||||
--@numberOfArguments Number of method arguments.
|
||||
@@ -575,7 +575,7 @@ function invokeStaticMethod(socket,id,classID,methodID,numberOfArguments,argumen
|
||||
else
|
||||
params = bin.pack(">Lii",classID,methodID,numberOfArguments) .. arguments .. bin.pack(">i",options)
|
||||
end
|
||||
|
||||
|
||||
local command = JDWPCommandPacket:new(id,3,3,params)
|
||||
local status, data = executeCommand(socket,command)
|
||||
if not status then
|
||||
@@ -586,16 +586,16 @@ function invokeStaticMethod(socket,id,classID,methodID,numberOfArguments,argumen
|
||||
end
|
||||
|
||||
--- NewInstance Command (4)
|
||||
-- Creates a new object of this type, invoking the specified constructor.
|
||||
-- Creates a new object of this type, invoking the specified constructor.
|
||||
-- The constructor method ID must be a member of the class type.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassType_NewInstance
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param classID Reference type id of the class.
|
||||
--@param threadID The thread in which to invoke the constructor.
|
||||
--@param methodID The constructor to invoke.
|
||||
--@param threadID The thread in which to invoke the constructor.
|
||||
--@param methodID The constructor to invoke.
|
||||
--@numberOfArguments Number of constructor arguments.
|
||||
--@arguments Already packed arguments.
|
||||
--@return (status, objectID) If status is false data contains an error string, else it contains a reference ID of the newly created object.
|
||||
@@ -606,7 +606,7 @@ function newClassInstance(socket,id,classID,threadID,methodID,numberOfArguments,
|
||||
else
|
||||
params = bin.pack(">LLii",classID,threadID,methodID,numberOfArguments) .. arguments
|
||||
end
|
||||
|
||||
|
||||
local command = JDWPCommandPacket:new(id,3,4,params)
|
||||
local status, data = executeCommand(socket,command)
|
||||
if not status then
|
||||
@@ -621,16 +621,16 @@ function newClassInstance(socket,id,classID,threadID,methodID,numberOfArguments,
|
||||
return true,objectID
|
||||
end
|
||||
|
||||
--- ArrayType Command Set (4)
|
||||
--- ArrayType Command Set (4)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayType
|
||||
|
||||
--- NewInstance Command (1)
|
||||
-- Creates a new array object of the specified type with a given length.
|
||||
-- Creates a new array object of the specified type with a given length.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayType_NewInstance
|
||||
--
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param arrayType The array type of the new instance as per JNI (http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502).
|
||||
--@param length Length of the new array.
|
||||
--@return (status, arrayID) If status is false data contains an error string, else it contains a reference ID of the newly created array.
|
||||
@@ -642,23 +642,23 @@ function newArrayInstance(socket,id,arrayType,length)
|
||||
stdnse.print_debug(2,"JDWP newArrayInstance() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
local pos,_ , tag, arrayID
|
||||
local pos,_ , tag, arrayID
|
||||
pos, tag = bin.unpack("C",data)
|
||||
_, arrayID = bin.unpack(">L",data,pos)
|
||||
return true, arrayID
|
||||
end
|
||||
|
||||
--- ObjectReference Command Set (9)
|
||||
--- ObjectReference Command Set (9)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference
|
||||
|
||||
--- ReferenceType Command (1)
|
||||
-- Returns the runtime type of the object. The runtime type will be a class or an array.
|
||||
-- Returns the runtime type of the object. The runtime type will be a class or an array.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_ReferenceType
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_ReferenceType
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param objectID The ID of an object.
|
||||
--@param id Packet id.
|
||||
--@param objectID The ID of an object.
|
||||
--@return (status, runtime_type) If status is false runtime_type contains an error string, else it contains runtime type of an object.
|
||||
function getRuntimeType(socket,id,objectID)
|
||||
local command = JDWPCommandPacket:new(id,9,1,bin.pack(">L",objectID))
|
||||
@@ -666,35 +666,35 @@ function getRuntimeType(socket,id,objectID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP resumeVM() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
local _,tag,runtime_type = bin.unpack(">CL",data)
|
||||
stdnse.print_debug("runtime type: %d",runtime_type)
|
||||
return true,runtime_type
|
||||
end
|
||||
|
||||
--- InvokeMethod Command (6)
|
||||
-- Invokes a instance method with specified parameters.
|
||||
--
|
||||
--- InvokeMethod Command (6)
|
||||
-- Invokes a instance method with specified parameters.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ObjectReference_InvokeMethod
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param objectID The ID of an object.
|
||||
--@param threadID The thread in which to invoke.
|
||||
--@param classID The class type.
|
||||
--@param id Packet id.
|
||||
--@param objectID The ID of an object.
|
||||
--@param threadID The thread in which to invoke.
|
||||
--@param classID The class type.
|
||||
--@param methodID ID of the method to invoke.
|
||||
--@param numberOfArguments Number of method arguments.
|
||||
--@param numberOfArguments Number of method arguments.
|
||||
--@arguments Already packed arguments.
|
||||
--@return (status, data) If status is false data contains an error string, else it contains a reply data and needs to be parsed manualy.
|
||||
function invokeObjectMethod(socket,id,objectID,threadID,classID,methodID,numberOfArguments,arguments)
|
||||
local params
|
||||
|
||||
|
||||
if numberOfArguments == 0 then
|
||||
params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments)
|
||||
else
|
||||
params = bin.pack(">LLLii",objectID,threadID,classID,methodID,numberOfArguments) .. arguments
|
||||
end
|
||||
|
||||
|
||||
local command = JDWPCommandPacket:new(id,9,6,params)
|
||||
local status, data = executeCommand(socket,command)
|
||||
if not status then
|
||||
@@ -705,17 +705,17 @@ function invokeObjectMethod(socket,id,objectID,threadID,classID,methodID,numberO
|
||||
return true,data
|
||||
end
|
||||
|
||||
--- StringReference Command Set (10)
|
||||
--- StringReference Command Set (10)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_StringReference
|
||||
|
||||
--- Value Command (1)
|
||||
-- Returns the characters contained in the string.
|
||||
-- Returns the characters contained in the string.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_StringReference_Value
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param stringID The ID of a string to read.
|
||||
--@param id Packet id.
|
||||
--@param stringID The ID of a string to read.
|
||||
--@return (status, data) If status is false result contains an error string, else it contains read string.
|
||||
function readString(socket,id,stringID)
|
||||
local command = JDWPCommandPacket:new(id,10,1,bin.pack(">L",stringID))
|
||||
@@ -723,22 +723,22 @@ function readString(socket,id,stringID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP readString() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
local _,result = extract_string(data,0)
|
||||
return true,result
|
||||
end
|
||||
|
||||
--- ThreadReference Command Set (11)
|
||||
--- ThreadReference Command Set (11)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference
|
||||
|
||||
|
||||
--- Name Command (1)
|
||||
-- Returns the thread name.
|
||||
-- Returns the thread name.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Name
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param threadID The ID of a thread.
|
||||
--@return (status, thread_name) If status is false thread_name contains an error string, else it contains thread's name.
|
||||
function getThreadName(socket,id,threadID)
|
||||
@@ -751,16 +751,16 @@ function getThreadName(socket,id,threadID)
|
||||
end
|
||||
-- parse data
|
||||
local _,thread_name = extract_string(data,0)
|
||||
return true, thread_name
|
||||
return true, thread_name
|
||||
end
|
||||
|
||||
--- Suspend Command (2)
|
||||
-- Suspends the thread.
|
||||
-- Suspends the thread.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Suspend
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param threadID The ID of a thread.
|
||||
--@return (status, thread_name) If status is false an error string is returned, else it's nil.
|
||||
function suspendThread(socket,id,threadID)
|
||||
@@ -770,7 +770,7 @@ function suspendThread(socket,id,threadID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP suspendThread() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
return true, nil
|
||||
end
|
||||
|
||||
@@ -781,7 +781,7 @@ end
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ThreadReference_Status
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param threadID The ID of a thread.
|
||||
--@return (status, thread_name) If status is false an error string is returned, else unparsed thread status data.
|
||||
function threadStatus(socket,id,threadID)
|
||||
@@ -791,12 +791,12 @@ function threadStatus(socket,id,threadID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP threadStatus() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
stdnse.print_debug("threadStatus %s",stdnse.tohex(data))
|
||||
return true, data
|
||||
end
|
||||
|
||||
--- ArrayReference Command Set (13)
|
||||
--- ArrayReference Command Set (13)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayReference
|
||||
|
||||
--- SetValues Command (3)
|
||||
@@ -805,7 +805,7 @@ end
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ArrayReference_SetValues
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param objectID The ID of an array object.
|
||||
--@return (status, data) If status is false an error string is returned, else it's nil.
|
||||
function setArrayValues(socket,id,objectID,idx,values)
|
||||
@@ -816,18 +816,18 @@ function setArrayValues(socket,id,objectID,idx,values)
|
||||
stdnse.print_debug(2,"JDWP setArrayValues() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
return true, nil
|
||||
return true, nil
|
||||
end
|
||||
|
||||
--- EventRequest Command Set (15)
|
||||
--- EventRequest Command Set (15)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest
|
||||
|
||||
--- Uses Set Command (1) to set singlesteping to specified thread.
|
||||
--
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Set
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param threadID The ID of the thread.
|
||||
--@return (status, requestID) If status is false an error string is returned, else it contains assigned request id.
|
||||
function setThreadSinglestep(socket,id,threadID)
|
||||
@@ -837,9 +837,9 @@ function setThreadSinglestep(socket,id,threadID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP setThreadSinglestep() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
local _, requestID = bin.unpack(">i",data)
|
||||
return true, requestID
|
||||
return true, requestID
|
||||
end
|
||||
|
||||
--- Uses Clear Command (2) to unset singlesteping from a thread by specified event.
|
||||
@@ -847,7 +847,7 @@ end
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_EventRequest_Clear
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param eventID The ID of the thread.
|
||||
--@return (status, requestID) If status is false an error string is returned, else it's nil.
|
||||
function clearThreadSinglestep(socket,id,eventID)
|
||||
@@ -857,21 +857,21 @@ function clearThreadSinglestep(socket,id,eventID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP clearThreadSinglestep() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
return true,nil
|
||||
end
|
||||
|
||||
--- ClassObjectReference Command Set (17)
|
||||
--- ClassObjectReference Command Set (17)
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassObjectReference
|
||||
|
||||
|
||||
--- ReflectedType Command (1)
|
||||
-- Returns the reference type reflected by this class object.
|
||||
-- Returns the reference type reflected by this class object.
|
||||
--
|
||||
-- http://docs.oracle.com/javase/1.5.0/docs/guide/jpda/jdwp/jdwp-protocol.html#JDWP_ClassObjectReference_ReflectedType
|
||||
--
|
||||
--@param socket Socket to use to send the command.
|
||||
--@param id Packet id.
|
||||
--@param id Packet id.
|
||||
--@param classObjectID The ID of the object.
|
||||
--@return (status, reflected_type) If status is false an error string is returned, else reflected_type is object's reference type.
|
||||
function getReflectedType(socket,id,classObjectID)
|
||||
@@ -881,13 +881,13 @@ function getReflectedType(socket,id,classObjectID)
|
||||
if not status then
|
||||
stdnse.print_debug(2,"JDWP getReflectedType() error: %s", data)
|
||||
return false,data
|
||||
end
|
||||
end
|
||||
local reflected_type = {
|
||||
refTypeTag = nil,
|
||||
typeID = nil
|
||||
}
|
||||
_,reflected_type.refTypeTag, reflected_type.typeID = bin.unpack(">CL",data)
|
||||
|
||||
|
||||
return true, reflected_type
|
||||
end
|
||||
|
||||
@@ -914,7 +914,7 @@ function findMethod(socket,class,methodName,skipFirst)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return methodID
|
||||
end
|
||||
|
||||
@@ -948,7 +948,7 @@ function injectClass(socket,class_bytes)
|
||||
return false
|
||||
end
|
||||
stdnse.print_debug("Found byte[] id %d",byteArrayID)
|
||||
|
||||
|
||||
-- find SecureClassLoader id by signature
|
||||
status, classes = getClassBySignature(socket,0,"Ljava/security/SecureClassLoader;")
|
||||
if not status then
|
||||
@@ -956,19 +956,19 @@ function injectClass(socket,class_bytes)
|
||||
end
|
||||
local secureClassLoader = classes[1].referenceTypeID
|
||||
stdnse.print_debug("Found SecureClassLoader id %d",secureClassLoader)
|
||||
-- find SecureClassLoader() constructor
|
||||
-- find SecureClassLoader() constructor
|
||||
local constructorMethodID = findMethod(socket,secureClassLoader,"<init>",true)
|
||||
-- find ClassLoader id by signature
|
||||
status, classes = getClassBySignature(socket,0,"Ljava/lang/ClassLoader;")
|
||||
if not status then
|
||||
return false
|
||||
end
|
||||
end
|
||||
local classLoader = classes[1].referenceTypeID
|
||||
stdnse.print_debug("Found ClassLoader id %d",classes[1].referenceTypeID)
|
||||
-- find ClassLoader's defineClass() method
|
||||
local defineClassMethodID = findMethod(socket,classLoader,"defineClass",false)
|
||||
-- find ClassLoader's resolveClass() method
|
||||
local resolveClassMethodID = findMethod(socket,classLoader,"resolveClass",false)
|
||||
local resolveClassMethodID = findMethod(socket,classLoader,"resolveClass",false)
|
||||
if constructorMethodID == nil or defineClassMethodID == nil or resolveClassMethodID == nil then
|
||||
stdnse.print_debug("Either constructor, defineClass or resolveClass method could not be found %s,%s,%s", type(constructorMethodID), type(defineClassMethodID),type(resolveClassMethodID))
|
||||
return false
|
||||
@@ -976,13 +976,13 @@ function injectClass(socket,class_bytes)
|
||||
|
||||
|
||||
-- create array to load bytecode into
|
||||
local arrayID
|
||||
local arrayID
|
||||
status, arrayID = newArrayInstance(socket,0,byteArrayID,#class_bytes)
|
||||
if not status then
|
||||
stdnse.print_debug("New array failed: %s", arrayID)
|
||||
return false
|
||||
end
|
||||
stdnse.print_debug("Created new byte array of length %d",#class_bytes)
|
||||
stdnse.print_debug("Created new byte array of length %d",#class_bytes)
|
||||
-- set array values
|
||||
local temp
|
||||
status, temp = setArrayValues(socket,0,arrayID,0,class_bytes)
|
||||
@@ -990,12 +990,12 @@ function injectClass(socket,class_bytes)
|
||||
stdnse.print_debug("Set values failed: %s", temp)
|
||||
return
|
||||
end
|
||||
stdnse.print_debug("Set array values to injected class bytes")
|
||||
|
||||
-- get main thread id
|
||||
stdnse.print_debug("Set array values to injected class bytes")
|
||||
|
||||
-- get main thread id
|
||||
-- in order to load a new class file, thread must be suspended by an event
|
||||
-- so we set it to singlestep, let it run and it get suspended right away
|
||||
local threads
|
||||
local threads
|
||||
status,threads = getAllThreads(socket,0)
|
||||
if not status then
|
||||
stdnse.print_debug("get threads failed: %s", threads)
|
||||
@@ -1003,7 +1003,7 @@ function injectClass(socket,class_bytes)
|
||||
end
|
||||
local main_thread
|
||||
local eventID
|
||||
stdnse.print_debug("Looking for main thread...")
|
||||
stdnse.print_debug("Looking for main thread...")
|
||||
for _,thread in ipairs(threads) do
|
||||
local thread_name
|
||||
status, thread_name = getThreadName(socket,0,thread)
|
||||
@@ -1012,7 +1012,7 @@ function injectClass(socket,class_bytes)
|
||||
return false
|
||||
end
|
||||
if thread_name == "main" then
|
||||
stdnse.print_debug("Setting singlesteping to main thread.")
|
||||
stdnse.print_debug("Setting singlesteping to main thread.")
|
||||
status, eventID = setThreadSinglestep(socket,0,thread)
|
||||
main_thread = thread
|
||||
break
|
||||
@@ -1023,25 +1023,25 @@ function injectClass(socket,class_bytes)
|
||||
return false
|
||||
end
|
||||
-- to trigger the singlestep event, VM must be resumed
|
||||
stdnse.print_debug("Resuming VM and waiting for single step event from main thread...")
|
||||
stdnse.print_debug("Resuming VM and waiting for single step event from main thread...")
|
||||
local status, _ = resumeVM(socket,0)
|
||||
-- clear singlestep since we need to run our code in this thread and we don't want it to stop after each instruction
|
||||
clearThreadSinglestep(socket,0,eventID)
|
||||
stdnse.print_debug("Cleared singlesteping from main thread.")
|
||||
|
||||
-- instantiate new class loader
|
||||
local class_loader_instance
|
||||
stdnse.print_debug("Cleared singlesteping from main thread.")
|
||||
|
||||
-- instantiate new class loader
|
||||
local class_loader_instance
|
||||
status, class_loader_instance = newClassInstance(socket,0,secureClassLoader,main_thread,constructorMethodID,0,nil)
|
||||
if not status then
|
||||
stdnse.print_debug("newClassInstance failed: %s", class_loader_instance)
|
||||
return false
|
||||
end
|
||||
stdnse.print_debug("Created new instance of SecureClassLoader.")
|
||||
|
||||
local injectedClass
|
||||
stdnse.print_debug("Created new instance of SecureClassLoader.")
|
||||
|
||||
local injectedClass
|
||||
-- invoke defineClass with byte array that contains our bytecode
|
||||
local defineClassArgs = bin.pack(">CLCiCi",0x5b,arrayID,0x49,0,0x49,#class_bytes) -- argument tags taken from http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/types.html#wp9502
|
||||
stdnse.print_debug("Calling secureClassLoader.defineClass(byte[],int,int) ...")
|
||||
stdnse.print_debug("Calling secureClassLoader.defineClass(byte[],int,int) ...")
|
||||
status, injectedClass = invokeObjectMethod(socket,0,class_loader_instance,main_thread,secureClassLoader,defineClassMethodID,3,defineClassArgs)
|
||||
if not status then
|
||||
stdnse.print_debug("invokeObjectMethod failed: %s", injectedClass)
|
||||
@@ -1053,34 +1053,34 @@ function injectClass(socket,class_bytes)
|
||||
end
|
||||
-- extract the injected class' ID
|
||||
local tag,injectedClassID
|
||||
_,tag,injectedClassID = bin.unpack(">CL",injectedClass)
|
||||
_,tag,injectedClassID = bin.unpack(">CL",injectedClass)
|
||||
|
||||
-- our class is now injected, but we need to find it's methods by calling Class.getMethods() on it
|
||||
-- and for that we need its runtime_type which is Class
|
||||
local runtime_type
|
||||
local runtime_type
|
||||
status, runtime_type = getRuntimeType(socket,0,injectedClassID) -- should be Class
|
||||
-- find the getMethods() id
|
||||
-- find the getMethods() id
|
||||
local getMethodsMethod = findMethod(socket,runtime_type,"getMethods",false)
|
||||
status, _ = invokeObjectMethod(socket,0,injectedClassID,main_thread,runtime_type,getMethodsMethod,0,nil)
|
||||
status, _ = invokeObjectMethod(socket,0,injectedClassID,main_thread,runtime_type,getMethodsMethod,0,nil)
|
||||
|
||||
|
||||
stdnse.print_debug("New class defined. Injected class id : %d",injectedClassID)
|
||||
stdnse.print_debug("New class defined. Injected class id : %d",injectedClassID)
|
||||
local sig, reflected_type
|
||||
status, sig = getSignatureWithGeneric(socket,0,injectedClassID)
|
||||
stdnse.print_debug("Injected class signature: %s", sig)
|
||||
status, reflected_type = getReflectedType(socket,0,injectedClassID)
|
||||
status, reflected_type = getReflectedType(socket,0,injectedClassID)
|
||||
|
||||
-- find injected class constructor
|
||||
local injectedConstructor = findMethod(socket,injectedClassID,"<init>",false)
|
||||
|
||||
|
||||
if injectedConstructor == nil then
|
||||
stdnse.print_debug("Couldn't find either evil method or constructor")
|
||||
return false
|
||||
end
|
||||
|
||||
-- instantiate our evil class
|
||||
end
|
||||
|
||||
-- instantiate our evil class
|
||||
local injectedClassInstance
|
||||
status, injectedClassInstance = newClassInstance(socket,0,injectedClassID,main_thread,injectedConstructor,0,nil)
|
||||
status, injectedClassInstance = newClassInstance(socket,0,injectedClassID,main_thread,injectedConstructor,0,nil)
|
||||
if not status then
|
||||
return false, injectedClassInstance
|
||||
end
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
-- Library methods for handling JSON data. It handles JSON encoding and
|
||||
-- decoding according to RFC 4627.
|
||||
--
|
||||
-- There is a test section at the bottom which shows some example
|
||||
-- There is a test section at the bottom which shows some example
|
||||
-- parsing. If you want to parse JSON, you can test it by pasting sample JSON
|
||||
-- into the <code>TESTS</code> table and run the <code>test</code> method
|
||||
--
|
||||
@@ -19,9 +19,9 @@
|
||||
-- Version 0.4
|
||||
-- Created 01/25/2010 - v0.1 - created by Martin Holst Swende <martin@swende.se>
|
||||
-- Heavily modified 02/22/2010 - v0.3. Rewrote the parser into an OO-form, to not have to handle
|
||||
-- all kinds of state with parameters and return values.
|
||||
-- Modified 02/27/2010 - v0.4 Added unicode handling (written by David Fifield). Renamed toJson
|
||||
-- and fromJson intogenerate() and parse(), implemented more proper numeric parsing and added some more error checking.
|
||||
-- all kinds of state with parameters and return values.
|
||||
-- Modified 02/27/2010 - v0.4 Added unicode handling (written by David Fifield). Renamed toJson
|
||||
-- and fromJson intogenerate() and parse(), implemented more proper numeric parsing and added some more error checking.
|
||||
|
||||
local bit = require "bit"
|
||||
local nmap = require "nmap"
|
||||
@@ -72,7 +72,7 @@ do
|
||||
end
|
||||
end
|
||||
|
||||
-- Escapes a string
|
||||
-- Escapes a string
|
||||
--@param str the string
|
||||
--@return a string where the special chars have been escaped
|
||||
local function escape(str)
|
||||
@@ -123,7 +123,7 @@ end
|
||||
--@return a string containing valid json
|
||||
function generate(obj)
|
||||
|
||||
-- NULL-check must be performed before
|
||||
-- NULL-check must be performed before
|
||||
-- checking type == table, since the NULL-object
|
||||
-- is a table
|
||||
if obj == NULL then
|
||||
@@ -177,11 +177,11 @@ end
|
||||
function Json:eatWhiteSpace()
|
||||
--Find next non-white char
|
||||
local a,b = self.input:find("%S",self.pos)
|
||||
if not a then
|
||||
if not a then
|
||||
self:syntaxerror("Empty data")
|
||||
return
|
||||
end
|
||||
self.pos = a
|
||||
self.pos = a
|
||||
end
|
||||
|
||||
-- Jumps to a specified position
|
||||
@@ -206,7 +206,7 @@ end
|
||||
-- If false, triggers a syntax error
|
||||
--@param str the string to test
|
||||
function Json:assertStr(str)
|
||||
local content = self.input:sub(self.pos,self.pos+str:len()-1)
|
||||
local content = self.input:sub(self.pos,self.pos+str:len()-1)
|
||||
if(content == str) then-- All ok
|
||||
-- Jump forward
|
||||
self:jumpTo(self.pos+str:len())
|
||||
@@ -231,9 +231,9 @@ function Json:parseStart()
|
||||
-- of the outermost container can other types appear.
|
||||
self:eatWhiteSpace()
|
||||
local c = self:peek()
|
||||
if c == '{' then
|
||||
if c == '{' then
|
||||
return self:parseObject()
|
||||
elseif c == '[' then
|
||||
elseif c == '[' then
|
||||
return self:parseArray()
|
||||
else
|
||||
self:syntaxerror(("JSON must start with object or array (started with %s)"):format(c))
|
||||
@@ -246,13 +246,13 @@ end
|
||||
function Json:parseValue()
|
||||
self:eatWhiteSpace()
|
||||
local c = self:peek()
|
||||
|
||||
|
||||
local value
|
||||
if c == '{' then
|
||||
if c == '{' then
|
||||
value = self:parseObject()
|
||||
elseif c == '[' then
|
||||
elseif c == '[' then
|
||||
value = self:parseArray()
|
||||
elseif c == '"' then
|
||||
elseif c == '"' then
|
||||
value = self:parseString()
|
||||
elseif c == 'n' then
|
||||
self:assertStr("null")
|
||||
@@ -271,7 +271,7 @@ function Json:parseValue()
|
||||
return
|
||||
end
|
||||
value = tonumber(self.input:sub(a,b))
|
||||
if(value == nil) then
|
||||
if(value == nil) then
|
||||
self:syntaxerror("Error 2 parsing numeric value")
|
||||
return
|
||||
end
|
||||
@@ -285,7 +285,7 @@ function Json:parseObject()
|
||||
local object = {}
|
||||
make_object(object)
|
||||
local _= self:next() -- Eat {
|
||||
|
||||
|
||||
while(self:hasMore() and not self:errors()) do
|
||||
self:eatWhiteSpace()
|
||||
local c = self:peek()
|
||||
@@ -293,14 +293,14 @@ function Json:parseObject()
|
||||
self:next() -- Eat it
|
||||
return object
|
||||
end
|
||||
|
||||
|
||||
if(c ~= '"') then
|
||||
self:syntaxerror(("Expected '\"', got '%s'"):format(c))
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local key = self:parseString()
|
||||
if self:errors() then
|
||||
if self:errors() then
|
||||
return
|
||||
end
|
||||
self:eatWhiteSpace()
|
||||
@@ -310,18 +310,18 @@ function Json:parseObject()
|
||||
return
|
||||
end
|
||||
local value = self:parseValue()
|
||||
|
||||
if self:errors() then
|
||||
|
||||
if self:errors() then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
object[key] = value
|
||||
|
||||
|
||||
self:eatWhiteSpace()
|
||||
c = self:next()
|
||||
-- Valid now is , or }
|
||||
if(c == '}') then
|
||||
return object
|
||||
if(c == '}') then
|
||||
return object
|
||||
end
|
||||
if(c ~= ',') then
|
||||
self:syntaxerror("Expected ',' or '}', got "..c)
|
||||
@@ -448,20 +448,20 @@ function Json:parseString()
|
||||
assert( c == '"')
|
||||
while(self:hasMore()) do
|
||||
local c = self:next()
|
||||
|
||||
|
||||
if(c == '"') then -- end of string
|
||||
break
|
||||
elseif(c == '\\') then-- Escaped char
|
||||
local d = self:next()
|
||||
if REVERSE_ESCAPE_TABLE[d] ~= nil then
|
||||
val = val .. REVERSE_ESCAPE_TABLE[d]
|
||||
if REVERSE_ESCAPE_TABLE[d] ~= nil then
|
||||
val = val .. REVERSE_ESCAPE_TABLE[d]
|
||||
elseif d == 'u' then -- Unicode chars
|
||||
local codepoint = self:parseUnicodeEscape()
|
||||
if not codepoint then
|
||||
return
|
||||
end
|
||||
val = val .. utf8_enc(codepoint)
|
||||
else
|
||||
else
|
||||
self:syntaxerror(("Undefined escape character '%s'"):format(d))
|
||||
return false
|
||||
end
|
||||
@@ -472,7 +472,7 @@ function Json:parseString()
|
||||
return val
|
||||
end
|
||||
--- Parses json data into an object form
|
||||
-- This is the method you probably want to use if you
|
||||
-- This is the method you probably want to use if you
|
||||
-- use this library from a script.
|
||||
--@param data a json string
|
||||
--@return status true if ok, false if bad
|
||||
@@ -510,7 +510,7 @@ local TESTS = {
|
||||
'{"a" bad :1}', -- error
|
||||
'["a\\\\t"]', -- Should become Lua {"a\\t"}
|
||||
'[0.0.0]', -- error
|
||||
'[-1]',
|
||||
'[-1]',
|
||||
'[-1.123e-2]',
|
||||
'[5e3]',
|
||||
'[5e+3]',
|
||||
@@ -531,7 +531,7 @@ function test()
|
||||
print(v)
|
||||
status,res = parse(v)
|
||||
if not status then print( res) end
|
||||
if(status) then
|
||||
if(status) then
|
||||
print(generate(res))
|
||||
else
|
||||
print("Error:".. res)
|
||||
|
||||
182
nselib/ldap.lua
182
nselib/ldap.lua
@@ -1,5 +1,5 @@
|
||||
---
|
||||
-- Library methods for handling LDAP.
|
||||
-- Library methods for handling LDAP.
|
||||
--
|
||||
-- @author Patrik Karlsson
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
@@ -14,7 +14,7 @@
|
||||
-- formats
|
||||
-- Revised 10/29/2011 - v0.5 - Added support for performing wildcard searches via the substring filter.
|
||||
-- Revised 10/30/2011 - v0.6 - Added support for the ldap extensibleMatch filter type for searches
|
||||
--
|
||||
--
|
||||
|
||||
local asn1 = require "asn1"
|
||||
local bin = require "bin"
|
||||
@@ -48,7 +48,7 @@ APPNO = {
|
||||
UnbindRequest = 2,
|
||||
SearchRequest = 3,
|
||||
SearchResponse = 4,
|
||||
SearchResDone = 5
|
||||
SearchResDone = 5
|
||||
}
|
||||
|
||||
-- Filter operation constants
|
||||
@@ -67,10 +67,10 @@ FILTER = {
|
||||
|
||||
-- Scope constants
|
||||
SCOPE = {
|
||||
base=0,
|
||||
one=1,
|
||||
sub= 2,
|
||||
children=3,
|
||||
base=0,
|
||||
one=1,
|
||||
sub= 2,
|
||||
children=3,
|
||||
default = 0
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ tagEncoder['table'] = function(self, val)
|
||||
if (val._ldap == '0A') then
|
||||
local ival = self.encodeInt(val[1])
|
||||
local len = self.encodeLength(#ival)
|
||||
return bin.pack('HAA', '0A', len, ival)
|
||||
return bin.pack('HAA', '0A', len, ival)
|
||||
end
|
||||
if (val._ldaptype) then
|
||||
local len
|
||||
@@ -101,17 +101,17 @@ tagEncoder['table'] = function(self, val)
|
||||
return bin.pack('HAA', val._ldaptype, len, val[1])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local encVal = ""
|
||||
for _, v in ipairs(val) do
|
||||
encVal = encVal .. encode(v) -- todo: buffer?
|
||||
end
|
||||
local tableType = bin.pack("H", "30")
|
||||
if (val["_snmp"]) then
|
||||
tableType = bin.pack("H", val["_snmp"])
|
||||
end
|
||||
if (val["_snmp"]) then
|
||||
tableType = bin.pack("H", val["_snmp"])
|
||||
end
|
||||
return bin.pack('AAA', tableType, self.encodeLength(#encVal), encVal)
|
||||
|
||||
|
||||
end
|
||||
|
||||
---
|
||||
@@ -120,17 +120,17 @@ end
|
||||
-- @param val Value to be encoded.
|
||||
-- @return Encoded value.
|
||||
function encode(val)
|
||||
|
||||
|
||||
local encoder = asn1.ASN1Encoder:new()
|
||||
local encValue
|
||||
|
||||
|
||||
encoder:registerTagEncoders(tagEncoder)
|
||||
encValue = encoder:encode(val)
|
||||
|
||||
|
||||
if encValue then
|
||||
return encValue
|
||||
end
|
||||
|
||||
|
||||
return ''
|
||||
end
|
||||
|
||||
@@ -179,7 +179,7 @@ local function decodeSeq(encStr, len, pos)
|
||||
local sPos = 1
|
||||
local sStr
|
||||
pos, sStr = bin.unpack("A" .. len, encStr, pos)
|
||||
if(sStr==nil) then
|
||||
if(sStr==nil) then
|
||||
return pos,seq
|
||||
end
|
||||
while (sPos < len) do
|
||||
@@ -217,7 +217,7 @@ end
|
||||
-- @param socket socket already connected to the ldap server
|
||||
-- @param params table containing at least <code>scope</code>, <code>derefPolicy</code>, <code>baseObject</code>
|
||||
-- the field <code>maxObjects</code> may also be included to restrict the amount of records returned
|
||||
-- @return success true or false.
|
||||
-- @return success true or false.
|
||||
-- @return err string containing error message
|
||||
function searchRequest( socket, params )
|
||||
|
||||
@@ -229,35 +229,35 @@ function searchRequest( socket, params )
|
||||
local attrSeq = ''
|
||||
local requestData, messageSeq, data
|
||||
local maxObjects = params.maxObjects or -1
|
||||
|
||||
|
||||
local encoder = asn1.ASN1Encoder:new()
|
||||
local decoder = asn1.ASN1Decoder:new()
|
||||
|
||||
|
||||
encoder:registerTagEncoders(tagEncoder)
|
||||
decoder:registerTagDecoders(tagDecoder)
|
||||
|
||||
|
||||
request = request .. encode( { _ldap='0A', params.scope } )--scope
|
||||
request = request .. encode( { _ldap='0A', params.derefPolicy } )--derefpolicy
|
||||
request = request .. encode( params.sizeLimit or 0)--sizelimit
|
||||
request = request .. encode( params.timeLimit or 0)--timelimit
|
||||
request = request .. encode( params.typesOnly or false)--TypesOnly
|
||||
|
||||
|
||||
if params.filter then
|
||||
request = request .. createFilter( params.filter )
|
||||
else
|
||||
request = request .. encode( { _ldaptype='87', "objectclass" } )-- filter : string, presence
|
||||
end
|
||||
if attributes~= nil then
|
||||
for _,attr in ipairs(attributes) do
|
||||
for _,attr in ipairs(attributes) do
|
||||
attrSeq = attrSeq .. encode(attr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
request = request .. encoder:encodeSeq(attrSeq)
|
||||
requestData = encodeLDAPOp(APPNO.SearchRequest, true, request)
|
||||
messageSeq = encode(ldapMessageId)
|
||||
messageSeq = encode(ldapMessageId)
|
||||
ldapMessageId = ldapMessageId +1
|
||||
messageSeq = messageSeq .. requestData
|
||||
messageSeq = messageSeq .. requestData
|
||||
data = encoder:encodeSeq(messageSeq)
|
||||
try( socket:send( data ) )
|
||||
data = ""
|
||||
@@ -268,13 +268,13 @@ function searchRequest( socket, params )
|
||||
local _, objectName, attributes, ldapOp
|
||||
local attributes
|
||||
local searchResEntry = {}
|
||||
|
||||
|
||||
if ( maxObjects == 0 ) then
|
||||
break
|
||||
elseif ( maxObjects > 0 ) then
|
||||
maxObjects = maxObjects - 1
|
||||
end
|
||||
|
||||
|
||||
if data:len() > 6 then
|
||||
pos, len = decoder.decodeLength( data, pos )
|
||||
else
|
||||
@@ -285,13 +285,13 @@ function searchRequest( socket, params )
|
||||
while ( len + pos - 1 > data:len() ) do
|
||||
data = data .. try( socket:receive() )
|
||||
end
|
||||
|
||||
|
||||
pos, messageId = decode( data, pos )
|
||||
pos, tmp = bin.unpack("C", data, pos)
|
||||
pos, len = decoder.decodeLength( data, pos )
|
||||
ldapOp = asn1.intToBER( tmp )
|
||||
searchResEntry = {}
|
||||
|
||||
|
||||
if ldapOp.number == APPNO.SearchResDone then
|
||||
pos, searchResEntry.resultCode = decode( data, pos )
|
||||
-- errors may occur after a large amount of data has been received (eg. size limit exceeded)
|
||||
@@ -303,7 +303,7 @@ function searchRequest( socket, params )
|
||||
local error_msg
|
||||
pos, searchResEntry.matchedDN = decode( data, pos )
|
||||
pos, searchResEntry.errorMessage = decode( data, pos )
|
||||
error_msg = ERROR_MSG[searchResEntry.resultCode]
|
||||
error_msg = ERROR_MSG[searchResEntry.resultCode]
|
||||
-- if the table is empty return a hard error
|
||||
if #searchResEntries == 0 then
|
||||
return false, string.format("Code: %d|Error: %s|Details: %s", searchResEntry.resultCode, error_msg or "", searchResEntry.errorMessage or "" )
|
||||
@@ -319,7 +319,7 @@ function searchRequest( socket, params )
|
||||
pos, searchResEntry.objectName = decode( data, pos )
|
||||
if ldapOp.number == APPNO.SearchResponse then
|
||||
pos, searchResEntry.attributes = decode( data, pos )
|
||||
|
||||
|
||||
table.insert( searchResEntries, searchResEntry )
|
||||
end
|
||||
if data:len() > pos then
|
||||
@@ -345,23 +345,23 @@ function bindRequest( socket, params )
|
||||
local ldapAuth = encode( { _ldaptype = 80, params.password } )
|
||||
local bindReq = encode( params.version ) .. encode( params.username ) .. ldapAuth
|
||||
local ldapMsg = encode(ldapMessageId) .. encodeLDAPOp( APPNO.BindRequest, true, bindReq )
|
||||
local packet
|
||||
local packet
|
||||
local pos, packet_len, resultCode, tmp, len, _
|
||||
local response = {}
|
||||
|
||||
local encoder = asn1.ASN1Encoder:new()
|
||||
local decoder = asn1.ASN1Decoder:new()
|
||||
|
||||
|
||||
encoder:registerTagEncoders(tagEncoder)
|
||||
decoder:registerTagDecoders(tagDecoder)
|
||||
|
||||
|
||||
packet = encoder:encodeSeq( ldapMsg )
|
||||
ldapMessageId = ldapMessageId +1
|
||||
try( socket:send( packet ) )
|
||||
packet = try( socket:receive() )
|
||||
|
||||
pos, packet_len = decoder.decodeLength( packet, 2 )
|
||||
pos, response.messageID = decode( packet, pos )
|
||||
pos, response.messageID = decode( packet, pos )
|
||||
pos, tmp = bin.unpack("C", packet, pos)
|
||||
pos, len = decoder.decodeLength( packet, pos )
|
||||
response.protocolOp = asn1.intToBER( tmp )
|
||||
@@ -369,20 +369,20 @@ function bindRequest( socket, params )
|
||||
if response.protocolOp.number ~= APPNO.BindResponse then
|
||||
return false, string.format("Recieved incorrect Op in packet: %d, expected %d", response.protocolOp.number, APPNO.BindResponse)
|
||||
end
|
||||
|
||||
|
||||
pos, response.resultCode = decode( packet, pos )
|
||||
|
||||
|
||||
if ( response.resultCode ~= 0 ) then
|
||||
local error_msg
|
||||
pos, response.matchedDN = decode( packet, pos )
|
||||
pos, response.errorMessage = decode( packet, pos )
|
||||
error_msg = ERROR_MSG[response.resultCode]
|
||||
return false, string.format("\n Error: %s\n Details: %s",
|
||||
error_msg = ERROR_MSG[response.resultCode]
|
||||
return false, string.format("\n Error: %s\n Details: %s",
|
||||
error_msg or "Unknown error occured (code: " .. response.resultCode ..
|
||||
")", response.errorMessage or "" )
|
||||
else
|
||||
return true, "Success"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Performs an LDAP Unbind
|
||||
@@ -422,13 +422,13 @@ function createFilter( filter )
|
||||
filter_str = filter_str .. createFilter( v )
|
||||
end
|
||||
else
|
||||
local obj = encode( filter.obj )
|
||||
local obj = encode( filter.obj )
|
||||
local val = ''
|
||||
if ( filter.op == FILTER['substrings'] ) then
|
||||
|
||||
|
||||
local tmptable = stdnse.strsplit('*', filter.val)
|
||||
local tmp_result = ''
|
||||
|
||||
|
||||
if (#tmptable <= 1 ) then
|
||||
-- 0x81 = 10000001 = 10 0 00001
|
||||
-- hex binary Context Primitive value Field: Sequence Value: 1 (any / any position in string)
|
||||
@@ -446,7 +446,7 @@ function createFilter( filter )
|
||||
-- hex binary Context Primitive value Field: Sequence Value: 1 (any / match in any position in string)
|
||||
tmp_result = tmp_result .. bin.pack('HAA' , '81', string.char(#substr), substr)
|
||||
end
|
||||
|
||||
|
||||
if (indexval == #tmptable) and (substr ~= '') then
|
||||
-- 0x82 = 10000010 = 10 0 00010
|
||||
-- hex binary Context Primitive value Field: Sequence Value: 2 (final / match at end of string)
|
||||
@@ -456,30 +456,30 @@ function createFilter( filter )
|
||||
end
|
||||
|
||||
val = asn1.ASN1Encoder:encodeSeq( tmp_result )
|
||||
|
||||
|
||||
elseif ( filter.op == FILTER['extensibleMatch'] ) then
|
||||
|
||||
|
||||
local tmptable = stdnse.strsplit(':=', filter.val)
|
||||
local tmp_result = ''
|
||||
local OID, bitmask
|
||||
|
||||
|
||||
if ( tmptable[1] ~= nil ) then
|
||||
OID = tmptable[1]
|
||||
else
|
||||
return false, ("ERROR: Invalid extensibleMatch query format")
|
||||
end
|
||||
|
||||
|
||||
if ( tmptable[2] ~= nil ) then
|
||||
bitmask = tmptable[2]
|
||||
else
|
||||
return false, ("ERROR: Invalid extensibleMatch query format")
|
||||
end
|
||||
|
||||
|
||||
-- Format and create matchingRule using OID
|
||||
-- 0x81 = 10000001 = 10 0 00001
|
||||
-- hex binary Context Primitive value Field: matchingRule Value: 1
|
||||
tmp_result = bin.pack('HAA' , '81', string.char(#OID), OID)
|
||||
|
||||
|
||||
-- Format and create type using ldap attribute
|
||||
-- 0x82 = 10000010 = 10 0 00010
|
||||
-- hex binary Context Primitive value Field: Type Value: 2
|
||||
@@ -488,7 +488,7 @@ function createFilter( filter )
|
||||
-- Format and create matchValue using bitmask
|
||||
-- 0x83 = 10000011 = 10 0 00011
|
||||
-- hex binary Context Primitive value Field: matchValue Value: 3
|
||||
tmp_result = tmp_result .. bin.pack('HAA' , '83', string.char(#bitmask), bitmask)
|
||||
tmp_result = tmp_result .. bin.pack('HAA' , '83', string.char(#bitmask), bitmask)
|
||||
|
||||
-- Format and create dnAttributes, defaulting to false
|
||||
-- 0x84 = 10000100 = 10 0 00100
|
||||
@@ -496,7 +496,7 @@ function createFilter( filter )
|
||||
--
|
||||
-- 0x01 = field length
|
||||
-- 0x00 = boolean value, in this case false
|
||||
tmp_result = tmp_result .. bin.pack('H' , '840100')
|
||||
tmp_result = tmp_result .. bin.pack('H' , '840100')
|
||||
|
||||
-- Format the overall extensibleMatch block
|
||||
-- 0xa9 = 10101001 = 10 1 01001
|
||||
@@ -505,9 +505,9 @@ function createFilter( filter )
|
||||
|
||||
else
|
||||
val = encode( filter.val )
|
||||
end
|
||||
|
||||
filter_str = filter_str .. obj .. val
|
||||
end
|
||||
|
||||
filter_str = filter_str .. obj .. val
|
||||
|
||||
end
|
||||
return encode( { _ldaptype=bin.pack("A", string.format("%X", asn1_type)), filter_str } )
|
||||
@@ -528,7 +528,7 @@ function searchResultToTable( searchEntries )
|
||||
for _, v in ipairs( searchEntries ) do
|
||||
local result_part = {}
|
||||
if v.objectName and v.objectName:len() > 0 then
|
||||
result_part.name = string.format("dn: %s", v.objectName)
|
||||
result_part.name = string.format("dn: %s", v.objectName)
|
||||
else
|
||||
result_part.name = "<ROOT>"
|
||||
end
|
||||
@@ -553,7 +553,7 @@ function searchResultToTable( searchEntries )
|
||||
end
|
||||
end
|
||||
table.insert( result_part, attribs )
|
||||
end
|
||||
end
|
||||
table.insert( result, result_part )
|
||||
end
|
||||
return result
|
||||
@@ -573,16 +573,16 @@ end
|
||||
function searchResultToFile( searchEntries, filename )
|
||||
|
||||
local f = io.open( filename, "w")
|
||||
|
||||
|
||||
if ( not(f) ) then
|
||||
return false, ("ERROR: Failed to open file (%s)"):format(filename)
|
||||
end
|
||||
|
||||
|
||||
-- Build table structure. Using a multi pass approach ( build table then populate table )
|
||||
-- because the objects returned may not necessarily have the same number of attributes
|
||||
-- making single pass CSV output generation problematic.
|
||||
-- Unfortunately the searchEntries table passed to this function is not organized in a
|
||||
-- way that make particular attributes for a given hostname directly addressable.
|
||||
-- Unfortunately the searchEntries table passed to this function is not organized in a
|
||||
-- way that make particular attributes for a given hostname directly addressable.
|
||||
--
|
||||
-- At some point restructuring the searchEntries table may be a good optimization target
|
||||
|
||||
@@ -597,15 +597,15 @@ function searchResultToFile( searchEntries, filename )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- build table of hosts
|
||||
local host_table = {}
|
||||
for _, v in ipairs( searchEntries ) do
|
||||
if v.objectName and v.objectName:len() > 0 then
|
||||
local host = {}
|
||||
|
||||
|
||||
if v.objectName and v.objectName:len() > 0 then
|
||||
-- use a copy of the table here, assigning attrib_table into host_table
|
||||
-- links the values so setting it for one host changes the specific attribute
|
||||
@@ -614,44 +614,44 @@ function searchResultToFile( searchEntries, filename )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- populate the host table with values for each attribute that has valid data
|
||||
for _, v in ipairs( searchEntries ) do
|
||||
if ( v.attributes ~= nil ) then
|
||||
for _, attrib in ipairs( v.attributes ) do
|
||||
for i=2, #attrib do
|
||||
for i=2, #attrib do
|
||||
-- do some additional Windows decoding
|
||||
if ( attrib[1] == "objectSid" ) then
|
||||
host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%d", decode( attrib[i] ) )
|
||||
|
||||
|
||||
elseif ( attrib[1] == "objectGUID") then
|
||||
local _, o1, o2, o3, o4, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of = bin.unpack("C16", attrib[i] )
|
||||
host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = string.format( "%x%x%x%x-%x%x-%x%x-%x%x-%x%x%x%x%x", o4, o3, o2, o1, o5, o6, o7, o8, o9, oa, ob, oc, od, oe, of )
|
||||
|
||||
|
||||
elseif ( attrib[1] == "lastLogon" or attrib[1] == "lastLogonTimestamp" or attrib[1] == "pwdLastSet" or attrib[1] == "accountExpires" or attrib[1] == "badPasswordTime" ) then
|
||||
host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertADTimeStamp(attrib[i])
|
||||
|
||||
|
||||
elseif ( attrib[1] == "whenChanged" or attrib[1] == "whenCreated" or attrib[1] == "dSCorePropagationData" ) then
|
||||
host_table[string.format("%s", v.objectName)].attributes[attrib[1]] = convertZuluTimeStamp(attrib[i])
|
||||
|
||||
|
||||
else
|
||||
host_table[v.objectName].attributes[attrib[1]] = string.format( "%s", attrib[i] )
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- write the new, fully populuated table out to CSV
|
||||
|
||||
|
||||
-- initialize header row
|
||||
local output = "\"name\""
|
||||
for attribute, value in pairs(attrib_table) do
|
||||
output = output .. ",\"" .. attribute .. "\""
|
||||
end
|
||||
end
|
||||
output = output .. "\n"
|
||||
|
||||
|
||||
-- gather host data from fields, add to output.
|
||||
for name, attribs in pairs(host_table) do
|
||||
output = output .. "\"" .. name .. "\""
|
||||
@@ -661,13 +661,13 @@ function searchResultToFile( searchEntries, filename )
|
||||
end
|
||||
output = output .. "\n"
|
||||
end
|
||||
|
||||
|
||||
-- write the output to file
|
||||
if ( not(f:write( output .."\n" ) ) ) then
|
||||
f:close()
|
||||
return false, ("ERROR: Failed to write file (%s)"):format(filename)
|
||||
end
|
||||
|
||||
|
||||
f:close()
|
||||
return true
|
||||
end
|
||||
@@ -684,7 +684,7 @@ function extractAttribute( searchEntries, attributeName )
|
||||
if ( v.attributes ~= nil ) then
|
||||
for _, attrib in ipairs( v.attributes ) do
|
||||
local attribType = attrib[1]
|
||||
for i=2, #attrib do
|
||||
for i=2, #attrib do
|
||||
if ( attribType:upper() == attributeName:upper() ) then
|
||||
table.insert( attributeTbl, attrib[i])
|
||||
end
|
||||
@@ -706,22 +706,22 @@ function convertADTimeStamp(timestamp)
|
||||
local base_time = tonumber(os.time({year=1601, month=1, day=1, hour=0, minute=0, sec =0}))
|
||||
|
||||
timestamp = tonumber(timestamp)
|
||||
|
||||
|
||||
if (timestamp and timestamp > 0) then
|
||||
|
||||
|
||||
-- The result value was 3036 seconds off what Microsoft says it should be.
|
||||
-- I have been unable to find an explaination for this, and have resorted to
|
||||
-- manually adjusting the formula.
|
||||
|
||||
|
||||
result = ( timestamp / 10000000 ) - 3036
|
||||
result = result + base_time
|
||||
result = os.date("%Y/%m/%d %H:%M:%S UTC", result)
|
||||
else
|
||||
result = 'Never'
|
||||
end
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Converts a non-delimited Zulu timestamp format to a human readable form
|
||||
@@ -741,13 +741,13 @@ function convertZuluTimeStamp(timestamp)
|
||||
local mins = string.sub(timestamp,11,12)
|
||||
local secs = string.sub(timestamp,13,14)
|
||||
local result = year .. "/" .. month .. "/" .. day .. " " .. hour .. ":" .. mins .. ":" .. secs .. " UTC"
|
||||
|
||||
|
||||
return result
|
||||
|
||||
|
||||
else
|
||||
return 'Invalid date format'
|
||||
return 'Invalid date format'
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Creates a copy of a table
|
||||
@@ -757,8 +757,8 @@ end
|
||||
-- @return table object containing copy of original
|
||||
function copyTable(targetTable)
|
||||
local temp = { }
|
||||
for key, val in pairs(targetTable) do
|
||||
temp[key] = val
|
||||
for key, val in pairs(targetTable) do
|
||||
temp[key] = val
|
||||
end
|
||||
return setmetatable(temp, getmetatable(targetTable))
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
-- <code>listop</code> module tries to bring much of the functionality from
|
||||
-- functional languages to Lua using Lua's central data structure, the table, as
|
||||
-- a base for its list operations. Highlights include a <code>map</code>
|
||||
-- function applying a given function to each element of a list.
|
||||
-- function applying a given function to each element of a list.
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
|
||||
local stdnse = require "stdnse"
|
||||
@@ -32,8 +32,8 @@ Functional programming style 'list' operations
|
||||
value ncar(list, x)
|
||||
list cdr(list)
|
||||
list ncdr(list, x)
|
||||
|
||||
where 'list' is an indexed table
|
||||
|
||||
where 'list' is an indexed table
|
||||
where 'value' is an lua datatype
|
||||
--]]
|
||||
|
||||
@@ -58,7 +58,7 @@ end
|
||||
-- @param f The function to call.
|
||||
-- @param l A list.
|
||||
-- @return List of function results.
|
||||
function map(f, l)
|
||||
function map(f, l)
|
||||
local results = {}
|
||||
for _, v in ipairs(l) do
|
||||
results[#results+1] = f(v);
|
||||
@@ -87,7 +87,7 @@ end
|
||||
-- @param f The function.
|
||||
-- @param l The list.
|
||||
-- @return Filtered list.
|
||||
function filter(f, l)
|
||||
function filter(f, l)
|
||||
local results = {}
|
||||
for i, v in ipairs(l) do
|
||||
if(f(v)) then
|
||||
@@ -125,7 +125,7 @@ end
|
||||
-- @param x Element index.
|
||||
-- @return Elements after index <code>x</code> or after index <code>1</code> if
|
||||
-- <code>x</code> is not given.
|
||||
function ncdr(l, x)
|
||||
function ncdr(l, x)
|
||||
return {table.unpack(l, x or 2)};
|
||||
end
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ _ENV = stdnse.module("match", stdnse.seeall)
|
||||
|
||||
--various functions for use with nse's nsock:receive_buf - function
|
||||
|
||||
-- e.g.
|
||||
-- e.g.
|
||||
-- sock:receive_buf(regex("myregexpattern")) - does a match using pcre- regular-
|
||||
-- - expressions
|
||||
-- sock:receive_buf(numbytes(80)) - is the buffered version of
|
||||
-- sock:receive_buf(numbytes(80)) - is the buffered version of
|
||||
-- sock:receive_bytes(80) - i.e. it returns
|
||||
-- exactly 80 bytes and no more
|
||||
-- exactly 80 bytes and no more
|
||||
|
||||
--- Return a function that allows delimiting with a regular expression.
|
||||
--
|
||||
@@ -41,7 +41,7 @@ end
|
||||
-- This function can be used to get a buffered version of
|
||||
-- <code>sock:receive_bytes(n)</code> in case a script requires more than one
|
||||
-- fixed-size chunk, as the unbuffered version may return more bytes than
|
||||
-- requested and thus would require you to do the parsing on your own.
|
||||
-- requested and thus would require you to do the parsing on your own.
|
||||
-- @param num Number of bytes.
|
||||
-- @usage sock:receive_buf(match.numbytes(80))
|
||||
-- @see nmap.receive_buf
|
||||
|
||||
@@ -17,16 +17,16 @@ _ENV = stdnse.module("membase", stdnse.seeall)
|
||||
|
||||
-- A minimalistic implementation of the Couchbase Membase TAP protocol
|
||||
TAP = {
|
||||
|
||||
|
||||
-- Operations
|
||||
Op = {
|
||||
LIST_SASL_MECHS = 0x20,
|
||||
AUTHENTICATE = 0x21,
|
||||
},
|
||||
|
||||
|
||||
-- Requests
|
||||
Request = {
|
||||
|
||||
|
||||
-- Header breakdown
|
||||
-- Field (offset) (value)
|
||||
-- Magic (0): 0x80 (PROTOCOL_BINARY_REQ)
|
||||
@@ -38,8 +38,8 @@ TAP = {
|
||||
-- Total body (8-11): 0x00000000 (0)
|
||||
-- Opaque (12-15): 0x00000000 (0)
|
||||
-- CAS (16-23): 0x0000000000000000 (0)
|
||||
Header = {
|
||||
|
||||
Header = {
|
||||
|
||||
-- Creates a new instance of Header
|
||||
-- @param opcode number containing the operation
|
||||
-- @return o new instance of Header
|
||||
@@ -59,7 +59,7 @@ TAP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the header to string
|
||||
-- @return string containing the Header as string
|
||||
__tostring = function(self)
|
||||
@@ -68,7 +68,7 @@ TAP = {
|
||||
self.opaque, self.CAS)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- List SASL authentication mechanism
|
||||
SASLList = {
|
||||
|
||||
@@ -90,7 +90,7 @@ TAP = {
|
||||
return tostring(self.header)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- Authenticates using SASL
|
||||
Authenticate = {
|
||||
|
||||
@@ -119,7 +119,7 @@ TAP = {
|
||||
if ( self.mech == "PLAIN" ) then
|
||||
local mech_params = { self.username, self.password }
|
||||
local auth_data = sasl.Helper:new(self.mech):encode(table.unpack(mech_params))
|
||||
|
||||
|
||||
self.header.keylen = #self.mech
|
||||
self.header.total_body = #auth_data + #self.mech
|
||||
return tostring(self.header) .. self.mech .. auth_data
|
||||
@@ -127,12 +127,12 @@ TAP = {
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- Responses
|
||||
Response = {
|
||||
|
||||
|
||||
-- The response header
|
||||
-- Header breakdown
|
||||
-- Field (offset) (value)
|
||||
@@ -146,7 +146,7 @@ TAP = {
|
||||
-- Opaque (12-15): 0x00000000 (0)
|
||||
-- CAS (16-23): 0x0000000000000000 (0)
|
||||
Header = {
|
||||
|
||||
|
||||
-- Creates a new instance of Header
|
||||
-- @param data string containing the raw data
|
||||
-- @return o new instance of Header
|
||||
@@ -160,7 +160,7 @@ TAP = {
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Parse the raw header and populates the class members
|
||||
-- @return status true on success, false on failure
|
||||
parse = function(self)
|
||||
@@ -169,17 +169,17 @@ TAP = {
|
||||
return false, "Packet to short"
|
||||
end
|
||||
local pos
|
||||
pos, self.magic, self.opcode, self.keylen, self.extlen,
|
||||
pos, self.magic, self.opcode, self.keylen, self.extlen,
|
||||
self.data_type, self.status, self.total_body, self.opaque,
|
||||
self.CAS = bin.unpack(">CCSCCSIIL", self.data)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- Decoders
|
||||
Decoder = {
|
||||
|
||||
|
||||
-- TAP.Op.LIST_SASL_MECHS
|
||||
[0x20] = {
|
||||
-- Creates a new instance of the decoder
|
||||
@@ -203,7 +203,7 @@ TAP = {
|
||||
return true
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
-- Login response
|
||||
[0x21] = {
|
||||
-- Creates a new instance of the decoder
|
||||
@@ -227,23 +227,23 @@ TAP = {
|
||||
return true
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Helper class is the main script interface
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new instance of the helper
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
-- @param options table including options to the helper, currently:
|
||||
-- <code>timeout</code> - socket timeout in milliseconds
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
mech = stdnse.get_script_args("membase.authmech"),
|
||||
@@ -253,7 +253,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects the socket to the server
|
||||
-- @return true on success, false on failure
|
||||
connect = function(self)
|
||||
@@ -261,12 +261,12 @@ Helper = {
|
||||
self.socket:set_timeout(self.options.timeout or 10000)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
-- Closes the socket
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a request to the server, receives and parses the response
|
||||
-- @param req a Request instance
|
||||
-- @return status true on success, false on failure
|
||||
@@ -276,7 +276,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data"
|
||||
end
|
||||
|
||||
|
||||
local data
|
||||
status, data = self.socket:receive_buf(match.numbytes(24), true)
|
||||
if ( not(status) ) then
|
||||
@@ -284,32 +284,32 @@ Helper = {
|
||||
end
|
||||
|
||||
local header = TAP.Response.Header:new(data)
|
||||
|
||||
|
||||
if ( header.opcode ~= req.header.opcode ) then
|
||||
stdnse.print_debug("WARNING: Received invalid op code, request contained (%d), response contained (%d)", req.header.opcode, header.opcode)
|
||||
end
|
||||
|
||||
|
||||
if ( not(TAP.Response.Decoder[tonumber(header.opcode)]) ) then
|
||||
return false, ("No response handler for opcode: %d"):format(header.opcode)
|
||||
end
|
||||
|
||||
|
||||
local status, data = self.socket:receive_buf(match.numbytes(header.total_body), true)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data"
|
||||
end
|
||||
|
||||
|
||||
local response = TAP.Response.Decoder[tonumber(header.opcode)]:new(data)
|
||||
if ( not(response) ) then
|
||||
return false, "Failed to parse response from server"
|
||||
end
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
-- Gets list of supported SASL authentication mechanisms
|
||||
getSASLMechList = function(self)
|
||||
return self:exch(TAP.Request.SASLList:new())
|
||||
end,
|
||||
|
||||
|
||||
-- Logins to the server
|
||||
-- @param username string containing the username
|
||||
-- @param password string containing the password
|
||||
|
||||
@@ -7,15 +7,15 @@ _ENV = stdnse.module("mobileme", stdnse.seeall)
|
||||
|
||||
---
|
||||
-- A MobileMe web service client that allows discovering Apple devices
|
||||
-- using the "find my iPhone" functionality.
|
||||
--
|
||||
-- using the "find my iPhone" functionality.
|
||||
--
|
||||
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||
--
|
||||
|
||||
MobileMe = {
|
||||
|
||||
|
||||
-- headers used in all requests
|
||||
headers = {
|
||||
headers = {
|
||||
["Content-Type"] = "application/json; charset=utf-8",
|
||||
["X-Apple-Find-Api-Ver"] = "2.0",
|
||||
["X-Apple-Authscheme"] = "UserIdGuest",
|
||||
@@ -26,7 +26,7 @@ MobileMe = {
|
||||
["Accept-Language"] = "en-us",
|
||||
["Connection"] = "keep-alive"
|
||||
},
|
||||
|
||||
|
||||
-- Creates a MobileMe instance
|
||||
-- @param username string containing the Apple ID username
|
||||
-- @param password string containing the Apple ID password
|
||||
@@ -42,7 +42,7 @@ MobileMe = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a message to an iOS device
|
||||
-- @param devid string containing the device id to which the message should
|
||||
-- be sent
|
||||
@@ -59,14 +59,14 @@ MobileMe = {
|
||||
local auth = { username = self.username, password = self.password }
|
||||
|
||||
local response = http.post(self.host, self.port, url, { header = self.headers, auth = auth, timeout = 10000 }, nil, data)
|
||||
|
||||
|
||||
if ( response.status == 200 ) then
|
||||
local status, resp = json.parse(response.body)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(2, "Failed to parse JSON response from server")
|
||||
return false, "Failed to parse JSON response from server"
|
||||
end
|
||||
|
||||
|
||||
if ( resp.statusCode ~= "200" ) then
|
||||
stdnse.print_debug(2, "Failed to send message to server")
|
||||
return false, "Failed to send message to server"
|
||||
@@ -74,7 +74,7 @@ MobileMe = {
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Updates location information for all devices controlled by the Apple ID
|
||||
-- @return status true on success, false on failure
|
||||
-- @return json parsed json table or string containing an error message on
|
||||
@@ -97,20 +97,20 @@ MobileMe = {
|
||||
if ( response.header["x-apple-mme-host"] ) then
|
||||
self.host = response.header["x-apple-mme-host"]
|
||||
end
|
||||
|
||||
|
||||
if ( response.status == 401 ) then
|
||||
return false, "Authentication failed"
|
||||
elseif ( response.status ~= 200 and response.status ~= 330 ) then
|
||||
return false, "An unexpected error occured"
|
||||
end
|
||||
|
||||
|
||||
retries = retries - 1
|
||||
until ( 200 == response.status or 0 == retries)
|
||||
|
||||
if ( response.status ~= 200 ) then
|
||||
return false, "Received unexpected response from server"
|
||||
end
|
||||
|
||||
|
||||
local status, parsed_json = json.parse(response.body)
|
||||
|
||||
if ( not(status) or parsed_json.statusCode ~= "200" ) then
|
||||
@@ -122,7 +122,7 @@ MobileMe = {
|
||||
|
||||
return true, parsed_json
|
||||
end,
|
||||
|
||||
|
||||
-- Get's a list of devices
|
||||
-- @return devices table containing a list of devices
|
||||
getDevices = function(self)
|
||||
@@ -136,7 +136,7 @@ MobileMe = {
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
|
||||
-- Creates a Helper instance
|
||||
-- @param username string containing the Apple ID username
|
||||
-- @param password string containing the Apple ID password
|
||||
@@ -150,7 +150,7 @@ Helper = {
|
||||
o.mm:update()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Get's the geolocation from each device
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -162,7 +162,7 @@ Helper = {
|
||||
-- * <code>accuracy</code> - the location accuracy
|
||||
-- * <code>timestamp</code> - the time the location was acquired
|
||||
-- * <code>postype</code> - the position type (GPS or WiFi)
|
||||
-- * <code>finished</code> -
|
||||
-- * <code>finished</code> -
|
||||
-- or string containing an error message on failure
|
||||
getLocation = function(self)
|
||||
-- do 3 tries, with a 5 second timeout to allow the location to update
|
||||
@@ -171,10 +171,10 @@ Helper = {
|
||||
-- success with that.
|
||||
local tries, timeout = 3, 5
|
||||
local result = {}
|
||||
|
||||
|
||||
repeat
|
||||
local status, response = self.mm:update()
|
||||
|
||||
|
||||
if ( not(status) or not(response) ) then
|
||||
return false, "Failed to retrieve response from server"
|
||||
end
|
||||
@@ -197,7 +197,7 @@ Helper = {
|
||||
until( tries == 0 )
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
-- Gets a list of names and ids of devices associated with the Apple ID
|
||||
-- @return status true on success, false on failure
|
||||
-- @return table of devices containing the following fields:
|
||||
@@ -209,7 +209,7 @@ Helper = {
|
||||
end
|
||||
return true, devices
|
||||
end,
|
||||
|
||||
|
||||
-- Send a message to an iOS Device
|
||||
--
|
||||
-- @param devid string containing the device id to which the message should
|
||||
@@ -222,7 +222,7 @@ Helper = {
|
||||
sendMessage = function(self, ...)
|
||||
return self.mm:sendMessage(...)
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -35,14 +35,14 @@ end
|
||||
local err =stdnse.print_debug
|
||||
|
||||
----------------------------------------------------------------------
|
||||
-- First of all comes a Bson parsing library. This can easily be moved out into a separate library should other
|
||||
-- First of all comes a Bson parsing library. This can easily be moved out into a separate library should other
|
||||
-- services start to use Bson
|
||||
----------------------------------------------------------------------
|
||||
-- Library methods for handling the BSON format
|
||||
-- Library methods for handling the BSON format
|
||||
--
|
||||
-- For more documentation about the BSON format,
|
||||
---and more details about it's implementations, check out the
|
||||
-- python BSON implementation which is available at
|
||||
-- For more documentation about the BSON format,
|
||||
---and more details about it's implementations, check out the
|
||||
-- python BSON implementation which is available at
|
||||
-- http://github.com/mongodb/mongo-python-driver/blob/master/pymongo/bson.py
|
||||
-- and licensed under the Apache License, Version 2.0 http://www.apache.org/licenses/LICENSE-2.0)
|
||||
--
|
||||
@@ -72,18 +72,18 @@ end
|
||||
--@return status : true if ok, false if error
|
||||
--@return result : the packed binary data OR error message
|
||||
local function _element_to_bson(key, value)
|
||||
|
||||
|
||||
--Some constraints-checking
|
||||
if type(key) ~= 'string' then
|
||||
return false, "Documents must have only string keys, key was " .. type(key)
|
||||
if type(key) ~= 'string' then
|
||||
return false, "Documents must have only string keys, key was " .. type(key)
|
||||
end
|
||||
if key:sub(1,1) == "$" then
|
||||
return false, "key must not start with $: ".. key
|
||||
if key:sub(1,1) == "$" then
|
||||
return false, "key must not start with $: ".. key
|
||||
end
|
||||
if key:find("%.") then
|
||||
return false, ("key %r must not contain '.'"):format(tostring(key))
|
||||
end
|
||||
|
||||
|
||||
local name =bin.pack("z",key) -- null-terminated string
|
||||
if type(value) == 'string' then
|
||||
local cstring = bin.pack("z",value) -- null-terminated string
|
||||
@@ -99,17 +99,17 @@ local function _element_to_bson(key, value)
|
||||
-- Use 01 - double for - works better than 10
|
||||
return true, bin.pack('H','01') .. name .. bin.pack("<d", value)
|
||||
end
|
||||
|
||||
|
||||
local _ = ("cannot convert value of type %s to bson"):format(type(value))
|
||||
return false, _
|
||||
end
|
||||
|
||||
--Converts a table of elements to binary bson format
|
||||
--@param dict the table
|
||||
--@return status : true if ok, false if error
|
||||
--@return status : true if ok, false if error
|
||||
--@return result : a string of binary data OR error message
|
||||
function toBson(dict)
|
||||
|
||||
|
||||
local elements = ""
|
||||
--Put id first
|
||||
if dict._id then
|
||||
@@ -134,7 +134,7 @@ function toBson(dict)
|
||||
end
|
||||
-- Get length
|
||||
local length = #elements + 5
|
||||
|
||||
|
||||
if length > 4 * 1024 * 1024 then
|
||||
return false, "document too large - BSON documents are limited to 4 MB"
|
||||
end
|
||||
@@ -143,24 +143,24 @@ function toBson(dict)
|
||||
return true, bin.pack("I", length) .. elements .. bin.pack('H',"00")
|
||||
end
|
||||
|
||||
-- Reads a null-terminated string. If length is supplied, it is just cut
|
||||
-- out from the data, otherwise the data is scanned for at null-char.
|
||||
-- Reads a null-terminated string. If length is supplied, it is just cut
|
||||
-- out from the data, otherwise the data is scanned for at null-char.
|
||||
--@param data the data which starts with a c-string
|
||||
--@param length optional length of the string
|
||||
--@return the string
|
||||
--@return the string
|
||||
--@return the remaining data (*without* null-char)
|
||||
local function get_c_string(data,length)
|
||||
if not length then
|
||||
local index = data:find(string.char(0))
|
||||
if index == nil then
|
||||
error({code="C-string did not contain NULL char"})
|
||||
if index == nil then
|
||||
error({code="C-string did not contain NULL char"})
|
||||
end
|
||||
length = index
|
||||
end
|
||||
local value = data:sub(1,length-1)
|
||||
|
||||
|
||||
--dbg("Found char at pos %d, data is %s c-string is %s",length, data, value)
|
||||
|
||||
|
||||
return value, data:sub(length+1)
|
||||
end
|
||||
|
||||
@@ -170,25 +170,25 @@ end
|
||||
-- @return Unpacked value
|
||||
-- @return error string if error occurred
|
||||
local function parse(code,data)
|
||||
if 1 == code then -- double
|
||||
if 1 == code then -- double
|
||||
return bin.unpack("<d", data)
|
||||
elseif 2 == code then -- string
|
||||
-- data length = first four bytes
|
||||
local _,len = bin.unpack("<i",data)
|
||||
local _,len = bin.unpack("<i",data)
|
||||
-- string data = data[5] -->
|
||||
local value = get_c_string(data:sub(5), len)
|
||||
-- Count position as header (=4) + length of string (=len)+ null char (=1)
|
||||
return 4+len+1,value
|
||||
elseif 3 == code or 4 == code then -- table or array
|
||||
elseif 3 == code or 4 == code then -- table or array
|
||||
local object, err
|
||||
|
||||
|
||||
-- Need to know the length, to return later
|
||||
local _,obj_size = bin.unpack("<i", data)
|
||||
-- Now, get the data object
|
||||
dbg("Recursing into bson array")
|
||||
object, data, err = fromBson(data)
|
||||
dbg("Recurse finished, got data object")
|
||||
-- And return where the parsing stopped
|
||||
-- And return where the parsing stopped
|
||||
return obj_size+1, object
|
||||
--6 = _get_null
|
||||
--7 = _get_oid
|
||||
@@ -206,7 +206,7 @@ local function parse(code,data)
|
||||
elseif 16 == code then -- 4 byte integer
|
||||
return bin.unpack("<i", data)
|
||||
--17= _get_timestamp
|
||||
elseif 18 == code then -- long
|
||||
elseif 18 == code then -- long
|
||||
return bin.unpack("<l", data)
|
||||
end
|
||||
local err = ("Getter for %d not implemented"):format(code)
|
||||
@@ -217,7 +217,7 @@ end
|
||||
-- Reads an element from binary to BSon
|
||||
--@param data a string of data to convert
|
||||
--@return Name of the element
|
||||
--@return Value of the element
|
||||
--@return Value of the element
|
||||
--@return Residual data not used
|
||||
--@return any error that occurred
|
||||
local function _element_to_dict(data)
|
||||
@@ -225,17 +225,17 @@ local function _element_to_dict(data)
|
||||
--local element_size = data:byte(1)
|
||||
element_type = data:byte(1)
|
||||
element_name, data = get_c_string(data:sub(2))
|
||||
|
||||
|
||||
dbg(" Read element name '%s' (type:%s), data left: %d",element_name, element_type,data:len())
|
||||
--pos,value,err = parsers.get(element_type)(data)
|
||||
pos,value,err = parse(element_type,data)
|
||||
if(err ~= nil) then
|
||||
if(err ~= nil) then
|
||||
dbg_err(err)
|
||||
return nil,nil, data, err
|
||||
return nil,nil, data, err
|
||||
end
|
||||
|
||||
|
||||
data=data:sub(pos)
|
||||
|
||||
|
||||
dbg(" Read element value '%s', data left: %d",tostring(value), data:len())
|
||||
return element_name, value, data
|
||||
end
|
||||
@@ -265,11 +265,11 @@ function isPacketComplete(data)
|
||||
local err_msg = "Not enough data in buffer, at least 4 bytes header info expected"
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local _,obj_size = bin.unpack("<i", data)
|
||||
|
||||
|
||||
dbg("BSon packet size is %s", obj_size)
|
||||
|
||||
|
||||
-- Check that all data is read and the packet is complete
|
||||
if data:len() < obj_size then
|
||||
return false,obj_size
|
||||
@@ -284,16 +284,16 @@ end
|
||||
--@return remaining data
|
||||
--@return error message if not enough data was in packet
|
||||
function fromBson(data)
|
||||
|
||||
|
||||
dbg("Decoding, got %s bytes of data", data:len())
|
||||
local complete, object_size = isPacketComplete(data)
|
||||
|
||||
if not complete then
|
||||
local err_msg = ("Not enough data in buffer, expected %s but only has %d"):format(object_size or "?", data:len())
|
||||
dbg(err_msg)
|
||||
return {},data, err_msg
|
||||
return {},data, err_msg
|
||||
end
|
||||
|
||||
|
||||
local element_portion = data:sub(5,object_size)
|
||||
local remainder = data:sub(object_size+1)
|
||||
return _elements_to_dict(element_portion), remainder
|
||||
@@ -305,7 +305,7 @@ end
|
||||
----------------------------------------------------------------------------------
|
||||
function testBson()
|
||||
local p = toBson({hello="world", test="ok"})
|
||||
|
||||
|
||||
print( "Encoded something ok")
|
||||
local orig = fromBson(p)
|
||||
print(" Decoded something else ok")
|
||||
@@ -321,7 +321,7 @@ end
|
||||
|
||||
--[[ MongoDB wire protocol format
|
||||
|
||||
Standard message header :
|
||||
Standard message header :
|
||||
struct {
|
||||
int32 messageLength; // total size of the message, including the 4 bytes of length
|
||||
int32 requestID; // client or database-generated identifier for this message
|
||||
@@ -329,7 +329,7 @@ struct {
|
||||
int32 opCode; // request type - see table below
|
||||
}
|
||||
|
||||
Opcodes :
|
||||
Opcodes :
|
||||
OP_REPLY 1 Reply to a client request. responseTo is set
|
||||
OP_MSG 1000 generic msg command followed by a string
|
||||
OP_UPDATE 2001 update document
|
||||
@@ -338,9 +338,9 @@ OP_GET_BY_OID 2003 is this used?
|
||||
OP_QUERY 2004 query a collection
|
||||
OP_GET_MORE 2005 Get more data from a query. See Cursors
|
||||
OP_DELETE 2006 Delete documents
|
||||
OP_KILL_CURSORS 2007 Tell database client is done with a cursor
|
||||
OP_KILL_CURSORS 2007 Tell database client is done with a cursor
|
||||
|
||||
Query message :
|
||||
Query message :
|
||||
struct {
|
||||
MsgHeader header; // standard message header
|
||||
int32 opts; // query options. See below for details.
|
||||
@@ -399,11 +399,11 @@ end
|
||||
function MongoData:addBSON(dict)
|
||||
-- status, res = bson.toBson(dict)
|
||||
local status, res = toBson(dict)
|
||||
if not status then
|
||||
if not status then
|
||||
dbg(res)
|
||||
return status,res
|
||||
end
|
||||
|
||||
|
||||
self.valueString = self.valueString..res
|
||||
return true
|
||||
end
|
||||
@@ -429,15 +429,15 @@ local function createQuery(collectionName, query)
|
||||
packet:addUnsignedInt32(0) -- number to skip
|
||||
packet:addUnsignedInt32(-1) -- number to return : no limit
|
||||
local status, error = packet:addBSON(query)
|
||||
|
||||
|
||||
if not status then
|
||||
return status, error
|
||||
end
|
||||
|
||||
|
||||
return true, packet:data()
|
||||
end
|
||||
-- Creates a get last error query
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
--@return status : true for OK, false for error
|
||||
--@return packet data OR error message
|
||||
function lastErrorQuery(responseTo)
|
||||
@@ -446,7 +446,7 @@ function lastErrorQuery(responseTo)
|
||||
return createQuery(collectionName, query)
|
||||
end
|
||||
-- Creates a server status query
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
--@return status : true for OK, false for error
|
||||
--@return packet data OR error message
|
||||
function serverStatusQuery(responseTo)
|
||||
@@ -455,7 +455,7 @@ function serverStatusQuery(responseTo)
|
||||
return createQuery(collectionName, query)
|
||||
end
|
||||
-- Creates a optime query
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
--@return status : true for OK, false for error
|
||||
--@return packet data OR error message
|
||||
function opTimeQuery(responseTo)
|
||||
@@ -464,7 +464,7 @@ function opTimeQuery(responseTo)
|
||||
return createQuery(collectionName, query)
|
||||
end
|
||||
-- Creates a list databases query
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
--@return status : true for OK, false for error
|
||||
--@return packet data OR error message
|
||||
function listDbQuery(responseTo)
|
||||
@@ -473,7 +473,7 @@ function listDbQuery(responseTo)
|
||||
return createQuery(collectionName, query)
|
||||
end
|
||||
-- Creates a build info query
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
-- @param responseTo optional identifier this packet is a response to
|
||||
--@return status : true for OK, false for error
|
||||
--@return packet data OR error message
|
||||
--@return status : true for OK, false for error
|
||||
@@ -487,15 +487,15 @@ end
|
||||
--@return int32 value
|
||||
--@return data unread
|
||||
local function parseInt32(data)
|
||||
local pos,val = bin.unpack("<i",data)
|
||||
local pos,val = bin.unpack("<i",data)
|
||||
return val, data:sub(pos)
|
||||
end
|
||||
local function parseInt64(data)
|
||||
local pos,val = bin.unpack("<l",data)
|
||||
local pos,val = bin.unpack("<l",data)
|
||||
return val, data:sub(pos)
|
||||
end
|
||||
-- Parses response header
|
||||
-- The response header looks like this :
|
||||
-- The response header looks like this :
|
||||
--[[
|
||||
struct {
|
||||
MsgHeader header; // standard message header
|
||||
@@ -511,7 +511,7 @@ struct {
|
||||
local function parseResponseHeader(data)
|
||||
local response= {}
|
||||
local hdr, rflag, cID, sfrom, nRet, docs
|
||||
|
||||
|
||||
-- First std message header
|
||||
hdr ={}
|
||||
hdr["messageLength"], data = parseInt32(data)
|
||||
@@ -537,11 +537,11 @@ function isPacketComplete(data)
|
||||
local err_msg = "Not enough data in buffer, at least 4 bytes header info expected"
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local _,obj_size = bin.unpack("<i", data)
|
||||
|
||||
|
||||
dbg("MongoDb Packet size is %s, (got %d)", obj_size,data:len())
|
||||
|
||||
|
||||
-- Check that all data is read and the packet is complete
|
||||
if data:len() < obj_size then
|
||||
return false,obj_size
|
||||
@@ -572,25 +572,25 @@ function query(socket, data)
|
||||
dbg("mongo: waiting for data from socket, got %d bytes so far...",data:len())
|
||||
data = data .. try( socket:receive() )
|
||||
isComplete, pSize = isPacketComplete(data)
|
||||
end
|
||||
end
|
||||
-- All required data shold be read now
|
||||
local packetData = data:sub(1,pSize)
|
||||
local residualData = data:sub(pSize+1)
|
||||
local responseHeader = parseResponseHeader(packetData)
|
||||
|
||||
|
||||
if responseHeader["responseFlag"] ~= 0 then
|
||||
dbg("Response flag not zero : %d, some error occurred", responseHeader["responseFlag"])
|
||||
end
|
||||
|
||||
|
||||
local bsonData = responseHeader["bson"]
|
||||
if #bsonData == 0 then
|
||||
if #bsonData == 0 then
|
||||
dbg("No BSon data returned ")
|
||||
return false, "No Bson data returned"
|
||||
end
|
||||
|
||||
|
||||
-- result, data, err_msg = bson.fromBson(bsonData)
|
||||
result, data, err_msg = fromBson(bsonData)
|
||||
|
||||
|
||||
if err_msg then
|
||||
dbg("Got error converting from bson: %s" , err_msg)
|
||||
return false, ("Got error converting from bson: %s"):format(err_msg)
|
||||
@@ -608,11 +608,11 @@ function login(socket, db, username, password)
|
||||
if ( not(status) or not(response.nonce) ) then
|
||||
return false, "Failed to retrieve nonce"
|
||||
end
|
||||
|
||||
|
||||
local nonce = response.nonce
|
||||
local pwdigest = stdnse.tohex(openssl.md5(username .. ':mongo:' ..password))
|
||||
local digest = stdnse.tohex(openssl.md5(nonce .. username .. pwdigest))
|
||||
|
||||
|
||||
query = { user = username, nonce = nonce, key = digest }
|
||||
query._cmd = { authenticate = 1 }
|
||||
|
||||
@@ -642,16 +642,16 @@ function queryResultToTable( resultTable )
|
||||
else
|
||||
table.insert(result,(("%s = %s"):format(tostring(k), tostring(v))))
|
||||
end
|
||||
end
|
||||
end
|
||||
return result
|
||||
|
||||
|
||||
end
|
||||
----------------------------------------------------------------------------------
|
||||
-- Test-code for debugging purposes below
|
||||
----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
--- Prints data (string) as Hex values, e.g so it more easily can
|
||||
--- Prints data (string) as Hex values, e.g so it more easily can
|
||||
-- be compared with a packet dump
|
||||
-- @param strData the data in a string format
|
||||
|
||||
@@ -667,7 +667,7 @@ local function printBuffer(strData)
|
||||
end
|
||||
--if ch > 64 and ch < 123 then
|
||||
-- out = out .. string.char(ch)
|
||||
--else
|
||||
--else
|
||||
out = out .. ch
|
||||
--end
|
||||
end
|
||||
|
||||
672
nselib/msrpc.lua
672
nselib/msrpc.lua
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,17 @@
|
||||
---
|
||||
-- This module is designed to parse the <code>PERF_DATA_BLOCK</code> structure, which is
|
||||
-- stored in the registry under HKEY_PERFORMANCE_DATA. By querying this structure, you can
|
||||
-- get a whole lot of information about what's going on.
|
||||
-- get a whole lot of information about what's going on.
|
||||
--
|
||||
-- To use this from a script, see <code>get_performance_data</code>, it is the only
|
||||
-- "public" function in this module.
|
||||
-- To use this from a script, see <code>get_performance_data</code>, it is the only
|
||||
-- "public" function in this module.
|
||||
--
|
||||
-- My primary sources of information were:
|
||||
-- * This 1996 journal by Matt Pietrek: <http://www.microsoft.com/msj/archive/S271.aspx>
|
||||
-- * The followup article: <http://www.microsoft.com/msj/archive/S2A9.aspx>
|
||||
-- * The WinPerf.h header file
|
||||
--
|
||||
-- And my primary inspiration was PsTools, specifically, <code>pstasklist.exe</code>.
|
||||
-- And my primary inspiration was PsTools, specifically, <code>pstasklist.exe</code>.
|
||||
--
|
||||
--@author Ron Bowes <ron@skullsecurity.net>
|
||||
--@copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
@@ -24,10 +24,10 @@ local msrpctypes = require "msrpctypes"
|
||||
local stdnse = require "stdnse"
|
||||
_ENV = stdnse.module("msrpcperformance", stdnse.seeall)
|
||||
|
||||
---Parses the title database, which is a series of null-terminated string pairs.
|
||||
---Parses the title database, which is a series of null-terminated string pairs.
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_title_database(data, pos)
|
||||
@@ -86,8 +86,8 @@ end
|
||||
-- } PERF_DATA_BLOCK, *PPERF_DATA_BLOCK;
|
||||
--</code>
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_data_block(data, pos)
|
||||
@@ -121,7 +121,7 @@ local function parse_perf_data_block(data, pos)
|
||||
|
||||
-- Ensure that the system name is directly after the header. This technically shouldn't matter, but Microsoft's documentation
|
||||
-- (in WinPref.h) says that the actual object comes "after the PERF_DATA_BLOCK", so it doesn't make sense that the SystemName
|
||||
-- could be anywhere else.
|
||||
-- could be anywhere else.
|
||||
if(pos ~= result['SystemNameOffset'] + 1) then
|
||||
return false, "MSRPC: PERF_DATA_BLOCK has SystemName in the wrong location"
|
||||
end
|
||||
@@ -215,8 +215,8 @@ end
|
||||
-- } PERF_OBJECT_TYPE, *PPERF_OBJECT_TYPE;
|
||||
--</code>
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_object_type(data, pos)
|
||||
@@ -285,8 +285,8 @@ end
|
||||
-- } PERF_COUNTER_DEFINITION, *PPERF_COUNTER_DEFINITION;
|
||||
--</code>
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_counter_definition(data, pos)
|
||||
@@ -310,14 +310,14 @@ local function parse_perf_counter_definition(data, pos)
|
||||
end
|
||||
|
||||
---Parse the actual counter value. This is a fairly simple function, it takes a counter
|
||||
-- definition and pulls out data based on it.
|
||||
-- definition and pulls out data based on it.
|
||||
--
|
||||
-- Note: I don't think this is doing the 8-byte values right, I suspect that they're supposed
|
||||
-- to be doubles.
|
||||
-- to be doubles.
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param counter_definition The matching counter_definition.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param counter_definition The matching counter_definition.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_counter(data, pos, counter_definition)
|
||||
@@ -374,8 +374,8 @@ end
|
||||
-- } PERF_INSTANCE_DEFINITION, *PPERF_INSTANCE_DEFINITION;
|
||||
--</code>
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_instance_definition(data, pos)
|
||||
@@ -406,11 +406,11 @@ end
|
||||
-- DWORD ByteLength; // Length in bytes of this structure,
|
||||
-- // including the following counters
|
||||
-- } PERF_COUNTER_BLOCK, *PPERF_COUNTER_BLOCK;
|
||||
--
|
||||
--
|
||||
--</code>
|
||||
--
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@param data The data being processed.
|
||||
--@param pos The position within <code>data</code>.
|
||||
--@return (status, pos, result) The status (true if successful), the new position in <code>data</code> (or an error
|
||||
-- message), and a table representing the datatype, if any.
|
||||
local function parse_perf_counter_block(data, pos)
|
||||
@@ -422,12 +422,12 @@ local function parse_perf_counter_block(data, pos)
|
||||
end
|
||||
|
||||
---Retrieve the parsed performance data from the given host for the requested object values. To get a list of possible
|
||||
-- object values, leave 'objects' blank and look at <code>result['title_database']</code> -- it'll contain a list of
|
||||
-- indexes that can be looked up. These indexes are passed as a string or as a series of space-separated strings (eg,
|
||||
-- "230" for "Process" and "238" for "Process" and "Processor").
|
||||
-- object values, leave 'objects' blank and look at <code>result['title_database']</code> -- it'll contain a list of
|
||||
-- indexes that can be looked up. These indexes are passed as a string or as a series of space-separated strings (eg,
|
||||
-- "230" for "Process" and "238" for "Process" and "Processor").
|
||||
--
|
||||
--@param host The host object
|
||||
--@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database.
|
||||
--@param objects [optional] The space-separated list of object numbers to retrieve. Default: only retrieve the database.
|
||||
function get_performance_data(host, objects)
|
||||
|
||||
-- Create the SMB session
|
||||
@@ -508,7 +508,7 @@ function get_performance_data(host, objects)
|
||||
--stdnse.print_debug("Index = %d\n", object_type['ObjectNameTitleIndex'])
|
||||
local object_name = result['title_database'][object_type['ObjectNameTitleIndex']]
|
||||
result[object_name] = {}
|
||||
|
||||
|
||||
--stdnse.print_debug("\n\nOBJECT: %s\n", object_name)
|
||||
--stdnse.print_debug(" Counters: %d\n", object_type['NumCounters'])
|
||||
--stdnse.print_debug(" Instances: %d\n", object_type['NumInstances'])
|
||||
@@ -547,7 +547,7 @@ function get_performance_data(host, objects)
|
||||
-- Set up the instance array
|
||||
local instance_name = object_instances[j]['InstanceName']
|
||||
result[object_name][instance_name] = {}
|
||||
|
||||
|
||||
-- Bring the pos to the start of the counter block
|
||||
pos = instance_start + object_instances[j]['ByteLength']
|
||||
|
||||
@@ -556,7 +556,7 @@ function get_performance_data(host, objects)
|
||||
--stdnse.print_debug(" NameOffset: %d\n", object_instances[j]['NameOffset'])
|
||||
--stdnse.print_debug(" NameLength: %d\n", object_instances[j]['NameLength'])
|
||||
--stdnse.print_debug(" --------------\n")
|
||||
|
||||
|
||||
-- The counter block
|
||||
local status, counter_block
|
||||
status, pos, counter_block = parse_perf_counter_block(queryvalue_result['value'], pos)
|
||||
@@ -564,7 +564,7 @@ function get_performance_data(host, objects)
|
||||
msrpc.stop_smb(smbstate)
|
||||
return false, pos
|
||||
end
|
||||
|
||||
|
||||
for k = 1, object_type['NumCounters'], 1 do
|
||||
-- Each individual counter
|
||||
local status, counter_result
|
||||
@@ -580,7 +580,7 @@ function get_performance_data(host, objects)
|
||||
-- Save it in the result
|
||||
result[object_name][instance_name][counter_name] = counter_result
|
||||
end
|
||||
|
||||
|
||||
-- Bring the pos to the end of the next section
|
||||
pos = instance_start + object_instances[j]['ByteLength'] + counter_block['ByteLength']
|
||||
end
|
||||
@@ -608,7 +608,7 @@ function get_performance_data(host, objects)
|
||||
end
|
||||
|
||||
msrpc.stop_smb(smbstate)
|
||||
|
||||
|
||||
return true, result
|
||||
end
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
806
nselib/mssql.lua
806
nselib/mssql.lua
File diff suppressed because it is too large
Load Diff
174
nselib/mysql.lua
174
nselib/mysql.lua
@@ -17,7 +17,7 @@ _ENV = stdnse.module("mysql", stdnse.seeall)
|
||||
|
||||
-- Version 0.3
|
||||
--
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Created 01/15/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 01/23/2010 - v0.2 - added query support, cleanup, documentation
|
||||
-- Revised 08/24/2010 - v0.3 - added error handling for recieveGreeting
|
||||
-- fixed a number of incorrect receives and changed
|
||||
@@ -27,7 +27,7 @@ local tab = require('tab')
|
||||
|
||||
local HAVE_SSL, openssl = pcall(require,'openssl')
|
||||
|
||||
Capabilities =
|
||||
Capabilities =
|
||||
{
|
||||
LongPassword = 0x1,
|
||||
FoundRows = 0x2,
|
||||
@@ -58,7 +58,7 @@ Charset =
|
||||
latin1_COLLATE_latin1_swedish_ci = 0x8
|
||||
}
|
||||
|
||||
ServerStatus =
|
||||
ServerStatus =
|
||||
{
|
||||
InTransaction = 0x1,
|
||||
AutoCommit = 0x2,
|
||||
@@ -72,7 +72,7 @@ ServerStatus =
|
||||
NoBackslashEscapes = 0x200
|
||||
}
|
||||
|
||||
Command =
|
||||
Command =
|
||||
{
|
||||
Query = 3
|
||||
}
|
||||
@@ -86,14 +86,14 @@ local HEADER_SIZE = 4
|
||||
-- @param data string of raw data
|
||||
-- @return response table containing the fields <code>len</code> and <code>packetno</code>
|
||||
local function decodeHeader( data, pos )
|
||||
|
||||
|
||||
local response = {}
|
||||
local pos, tmp = pos or 1, 0
|
||||
|
||||
|
||||
pos, tmp = bin.unpack( "I", data, pos )
|
||||
response.len = bit.band( tmp,255 )
|
||||
response.number = bit.rshift( tmp, 24 )
|
||||
|
||||
|
||||
return pos, response
|
||||
end
|
||||
|
||||
@@ -106,21 +106,21 @@ end
|
||||
-- <code>status</code> or error message on failure (status == false)
|
||||
function receiveGreeting( socket )
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("receiveGreeting(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
local data = try( socket:receive_bytes(HEADER_SIZE) )
|
||||
local pos, response, tmp, _
|
||||
|
||||
pos, response = decodeHeader( data, 1 )
|
||||
|
||||
pos, response = decodeHeader( data, 1 )
|
||||
|
||||
-- do we need to read the remainder
|
||||
if ( #data - HEADER_SIZE < response.len ) then
|
||||
local tmp = try( socket:receive_bytes( response.len - #data + HEADER_SIZE ) )
|
||||
data = data .. tmp
|
||||
end
|
||||
|
||||
|
||||
local is_error
|
||||
pos, is_error = bin.unpack( "C", data, pos )
|
||||
pos, is_error = bin.unpack( "C", data, pos )
|
||||
|
||||
if ( is_error == 0xff ) then
|
||||
pos, response.errorcode = bin.unpack( "S", data, pos )
|
||||
@@ -128,7 +128,7 @@ function receiveGreeting( socket )
|
||||
|
||||
return false, response.errormsg
|
||||
end
|
||||
|
||||
|
||||
pos, response.proto = bin.unpack( "C", data, pos )
|
||||
pos, response.version = bin.unpack( "z", data, pos )
|
||||
pos, response.threadid = bin.unpack( "I", data, pos )
|
||||
@@ -138,12 +138,12 @@ function receiveGreeting( socket )
|
||||
pos, response.status = bin.unpack( "S", data, pos )
|
||||
pos, _ = bin.unpack( "A13", data, pos )
|
||||
pos, tmp = bin.unpack( "A12", data, pos )
|
||||
|
||||
|
||||
response.salt = response.salt .. tmp
|
||||
response.errorcode = 0
|
||||
|
||||
|
||||
return true, response
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ end
|
||||
-- @return response table or error message on failure
|
||||
function loginRequest( socket, params, username, password, salt )
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
local packetno = 1
|
||||
local authversion = params.authversion or "post41"
|
||||
@@ -201,7 +201,7 @@ function loginRequest( socket, params, username, password, salt )
|
||||
|
||||
if not(HAVE_SSL) then
|
||||
return false, "No OpenSSL"
|
||||
end
|
||||
end
|
||||
|
||||
if authversion ~= "post41" then
|
||||
return false, "Unsupported authentication version: " .. authversion
|
||||
@@ -214,70 +214,70 @@ function loginRequest( socket, params, username, password, salt )
|
||||
clicap = clicap + Capabilities.InteractiveClient
|
||||
clicap = clicap + Capabilities.SupportsTransactions
|
||||
clicap = clicap + Capabilities.Support41Auth
|
||||
|
||||
|
||||
local extcapabilities = ExtCapabilities.SupportsMultipleStatments
|
||||
extcapabilities = extcapabilities + ExtCapabilities.SupportsMultipleResults
|
||||
|
||||
|
||||
local packet = bin.pack( "S", clicap )
|
||||
packet = packet .. bin.pack( "S", extcapabilities )
|
||||
packet = packet .. bin.pack( "I", MAXPACKET )
|
||||
packet = packet .. bin.pack( "C", Charset.latin1_COLLATE_latin1_swedish_ci )
|
||||
packet = packet .. bin.pack( "A", string.char(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) )
|
||||
packet = packet .. bin.pack( "z", username )
|
||||
|
||||
|
||||
if ( password ~= nil and password:len() > 0 ) then
|
||||
local hash = createLoginHash( password, salt )
|
||||
packet = packet .. bin.pack( "A", string.char( 0x14 ) .. hash )
|
||||
else
|
||||
packet = packet .. bin.pack( "C", 0 )
|
||||
end
|
||||
|
||||
|
||||
local tmp = packet:len() + bit.lshift( packetno, 24 )
|
||||
|
||||
|
||||
packet = bin.pack( "I", tmp ) .. packet
|
||||
|
||||
|
||||
try( socket:send(packet) )
|
||||
packet = try( socket:receive_bytes(HEADER_SIZE) )
|
||||
local pos, response = decodeHeader( packet )
|
||||
|
||||
|
||||
-- do we need to read the remainder
|
||||
if ( #packet - HEADER_SIZE < response.len ) then
|
||||
local tmp = try( socket:receive_bytes( response.len - #packet + HEADER_SIZE ) )
|
||||
packet = packet .. tmp
|
||||
end
|
||||
|
||||
|
||||
local is_error
|
||||
|
||||
pos, is_error = bin.unpack( "C", packet, pos )
|
||||
|
||||
|
||||
pos, is_error = bin.unpack( "C", packet, pos )
|
||||
|
||||
if is_error > 0 then
|
||||
pos, response.errorcode = bin.unpack( "S", packet, pos )
|
||||
|
||||
|
||||
local has_sqlstate
|
||||
pos, has_sqlstate = bin.unpack( "C", packet, pos )
|
||||
|
||||
|
||||
if has_sqlstate == 35 then
|
||||
pos, response.sqlstate = bin.unpack( "A5", packet, pos )
|
||||
end
|
||||
|
||||
pos, response.errormessage = bin.unpack( "z", packet, pos )
|
||||
|
||||
pos, response.errormessage = bin.unpack( "z", packet, pos )
|
||||
|
||||
return false, response.errormessage
|
||||
else
|
||||
response.errorcode = 0
|
||||
pos, response.affectedrows = bin.unpack( "C", packet, pos )
|
||||
pos, response.serverstatus = bin.unpack( "S", packet, pos )
|
||||
pos, response.warnings = bin.unpack( "S", packet, pos )
|
||||
pos, response.warnings = bin.unpack( "S", packet, pos )
|
||||
end
|
||||
|
||||
|
||||
return true, response
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Decodes a single column field
|
||||
--
|
||||
-- http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Field_Packet
|
||||
--
|
||||
--
|
||||
-- @param data string containing field packets
|
||||
-- @param pos number containing position from which to start decoding
|
||||
-- the position should point to the data in this buffer (ie. after the header)
|
||||
@@ -286,14 +286,14 @@ end
|
||||
-- <code>origt_table</code>, <code>name</code>, <code>orig_name</code>,
|
||||
-- <code>length</code> and <code>type</code>
|
||||
function decodeField( data, pos )
|
||||
|
||||
|
||||
local header, len
|
||||
local def, _
|
||||
local field = {}
|
||||
|
||||
pos, len = bin.unpack( "C", data, pos )
|
||||
pos, field.catalog = bin.unpack( "A" .. len, data, pos )
|
||||
|
||||
|
||||
pos, len = bin.unpack( "C", data, pos )
|
||||
pos, field.database = bin.unpack( "A" .. len, data, pos )
|
||||
|
||||
@@ -311,15 +311,15 @@ function decodeField( data, pos )
|
||||
|
||||
-- should be 0x0C
|
||||
pos, _ = bin.unpack( "C", data, pos )
|
||||
|
||||
|
||||
-- charset, in my case 0x0800
|
||||
pos, _ = bin.unpack( "S", data, pos )
|
||||
|
||||
pos, field.length = bin.unpack( "I", data, pos )
|
||||
pos, field.type = bin.unpack( "A6", data, pos )
|
||||
|
||||
|
||||
return pos, field
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Decodes the result set header packet into it's sub components
|
||||
@@ -330,13 +330,13 @@ end
|
||||
-- @return table containing the following <code>header</code>, <code>fields</code> and <code>data</code>
|
||||
function decodeQueryResponse( socket )
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("decodeQueryResponse(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
local data, header, pos
|
||||
local rs, blocks = {}, {}
|
||||
local block_start, block_end
|
||||
local EOF_MARKER = 254
|
||||
|
||||
|
||||
data = try( socket:receive_bytes(HEADER_SIZE) )
|
||||
pos, header = decodeHeader( data, pos )
|
||||
|
||||
@@ -346,9 +346,9 @@ function decodeQueryResponse( socket )
|
||||
if data:len() < header.len then
|
||||
data = data .. try( socket:receive_bytes( header.len - #data + HEADER_SIZE ) )
|
||||
end
|
||||
|
||||
|
||||
rs.header = data:sub( 1, HEADER_SIZE + header.len )
|
||||
|
||||
|
||||
-- abort on MySQL error
|
||||
if rs.header:sub(HEADER_SIZE + 1, HEADER_SIZE + 1) == string.char(0xFF) then
|
||||
-- is this a 4.0 or 4.1 error message
|
||||
@@ -360,29 +360,29 @@ function decodeQueryResponse( socket )
|
||||
end
|
||||
|
||||
pos = HEADER_SIZE + header.len + 1
|
||||
|
||||
|
||||
-- Second, Let's attempt to read the "Field Packets" and "Row Data Packets"
|
||||
-- They're separated by an "EOF Packet"
|
||||
for i=1,2 do
|
||||
|
||||
|
||||
-- marks the start of our block
|
||||
block_start = pos
|
||||
|
||||
|
||||
while true do
|
||||
|
||||
|
||||
if data:len() - pos < HEADER_SIZE then
|
||||
data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) )
|
||||
data = data .. try( socket:receive_bytes( HEADER_SIZE - ( data:len() - pos ) ) )
|
||||
end
|
||||
|
||||
|
||||
pos, header = decodeHeader( data, pos )
|
||||
|
||||
|
||||
if data:len() - pos < header.len - 1 then
|
||||
data = data .. try( socket:receive_bytes( header.len - ( data:len() - pos ) ) )
|
||||
end
|
||||
|
||||
|
||||
if header.len > 0 then
|
||||
local _, b = bin.unpack("C", data, pos )
|
||||
|
||||
local _, b = bin.unpack("C", data, pos )
|
||||
|
||||
-- Is this the EOF packet?
|
||||
if b == EOF_MARKER then
|
||||
-- we don't want the EOF Packet included
|
||||
@@ -397,15 +397,15 @@ function decodeQueryResponse( socket )
|
||||
end
|
||||
|
||||
blocks[i] = data:sub( block_start, block_end )
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
rs.fields = blocks[1]
|
||||
rs.data = blocks[2]
|
||||
|
||||
|
||||
return true, rs
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Decodes as field packet and returns a table of field tables
|
||||
@@ -415,23 +415,23 @@ end
|
||||
-- @param data string containing field packets
|
||||
-- @param count number containing the amount of fields to decode
|
||||
-- @return status boolean (true on success, false on failure)
|
||||
-- @return fields table containing field tables as returned by <code>decodeField</code>
|
||||
-- @return fields table containing field tables as returned by <code>decodeField</code>
|
||||
-- or string containing error message if status is false
|
||||
function decodeFieldPackets( data, count )
|
||||
|
||||
|
||||
local pos, header
|
||||
local field, fields = {}, {}
|
||||
|
||||
|
||||
if count < 1 then
|
||||
return false, "Field count was less than one, aborting"
|
||||
end
|
||||
|
||||
|
||||
for i=1, count do
|
||||
pos, header = decodeHeader( data, pos )
|
||||
pos, field = decodeField( data, pos )
|
||||
table.insert( fields, field )
|
||||
end
|
||||
|
||||
|
||||
return true, fields
|
||||
end
|
||||
|
||||
@@ -442,15 +442,15 @@ end
|
||||
-- @param data string containing the result set header packet
|
||||
-- @return number containing the amount of fields
|
||||
function decodeResultSetHeader( data )
|
||||
|
||||
|
||||
local _, fields
|
||||
|
||||
|
||||
if data:len() ~= HEADER_SIZE + 1 then
|
||||
return false, "Result set header was incorrect"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
_, fields = bin.unpack( "C", data, HEADER_SIZE + 1 )
|
||||
|
||||
|
||||
return true, fields
|
||||
end
|
||||
|
||||
@@ -463,25 +463,25 @@ end
|
||||
-- @return status true on success, false on failure
|
||||
-- @return rows table containing row tables
|
||||
function decodeDataPackets( data, count )
|
||||
|
||||
|
||||
local len, pos = 0, 1, 1
|
||||
local header, row, rows = {}, {}, {}
|
||||
|
||||
while pos < data:len() do
|
||||
row = {}
|
||||
pos, header = decodeHeader( data, pos )
|
||||
|
||||
|
||||
for i=1, count do
|
||||
pos, len = bin.unpack("C", data, pos )
|
||||
pos, row[i] = bin.unpack("A" .. len, data, pos)
|
||||
end
|
||||
|
||||
|
||||
table.insert( rows, row )
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return true, rows
|
||||
|
||||
|
||||
end
|
||||
|
||||
--- Sends the query to the MySQL server and then attempts to decode the response
|
||||
@@ -492,13 +492,13 @@ end
|
||||
-- @return rows table containing row tabels as decoded by <code>decodeDataPackets</code>
|
||||
function sqlQuery( socket, query )
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("sqlQuery(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
local packetno = 0
|
||||
local querylen = query:len() + 1
|
||||
local packet, packet_len, pos, header
|
||||
local status, fields, field_count, rows, rs
|
||||
|
||||
|
||||
packet = bin.pack("ICA", querylen, Command.Query, query )
|
||||
|
||||
--
|
||||
@@ -509,18 +509,18 @@ function sqlQuery( socket, query )
|
||||
-- (EOF Packet) marker: end of Field Packets
|
||||
-- (Row Data Packets) row contents
|
||||
-- (EOF Packet) marker: end of Data Packets
|
||||
|
||||
|
||||
try( socket:send(packet) )
|
||||
|
||||
|
||||
--
|
||||
-- Let's read all the data into a table
|
||||
-- This way we avoid the hustle with reading from the socket
|
||||
status, rs = decodeQueryResponse( socket )
|
||||
|
||||
|
||||
if not status then
|
||||
return false, rs
|
||||
end
|
||||
|
||||
|
||||
status, field_count = decodeResultSetHeader(rs.header)
|
||||
|
||||
if not status then
|
||||
@@ -528,7 +528,7 @@ function sqlQuery( socket, query )
|
||||
end
|
||||
|
||||
status, fields = decodeFieldPackets(rs.fields, field_count)
|
||||
|
||||
|
||||
if not status then
|
||||
return false, fields
|
||||
end
|
||||
@@ -538,8 +538,8 @@ function sqlQuery( socket, query )
|
||||
if not status then
|
||||
return false, rows
|
||||
end
|
||||
|
||||
return true, { cols = fields, rows = rows }
|
||||
|
||||
return true, { cols = fields, rows = rows }
|
||||
end
|
||||
|
||||
---
|
||||
@@ -557,16 +557,16 @@ function formatResultset(rs, options)
|
||||
|
||||
local restab = tab.new(#rs.cols)
|
||||
local colnames = {}
|
||||
|
||||
|
||||
if ( not(options.noheaders) ) then
|
||||
for _, col in ipairs(rs.cols) do table.insert(colnames, col.name) end
|
||||
tab.addrow(restab, table.unpack(colnames))
|
||||
end
|
||||
|
||||
|
||||
for _, row in ipairs(rs.rows) do
|
||||
tab.addrow(restab, table.unpack(row))
|
||||
end
|
||||
|
||||
|
||||
return tab.dump(restab)
|
||||
end
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
-- This library implements the basics of NAT-PMP as described in the
|
||||
-- NAT Port Mapping Protocol (NAT-PMP) draft:
|
||||
-- o http://tools.ietf.org/html/draft-cheshire-nat-pmp-03
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||
--
|
||||
@@ -12,7 +12,7 @@ local nmap = require "nmap"
|
||||
local stdnse = require "stdnse"
|
||||
_ENV = stdnse.module("natpmp", stdnse.seeall)
|
||||
|
||||
local ResultCode = {
|
||||
local ResultCode = {
|
||||
SUCCESS = 0,
|
||||
UNSUPPORTED_VERSION = 1,
|
||||
NOT_AUTHORIZED = 2,
|
||||
@@ -31,27 +31,27 @@ local ErrorMessage = {
|
||||
|
||||
|
||||
Request = {
|
||||
|
||||
|
||||
GetWANIP = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = { version = 0, op = 0 }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
return bin.pack(">CC", self.version, self.op)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
MapPort = {
|
||||
|
||||
|
||||
new = function(self, pubport, privport, proto, lifetime)
|
||||
assert(proto == "udp" or proto == "tcp", "Unsupported protocol")
|
||||
local o = {
|
||||
local o = {
|
||||
version = 0,
|
||||
pubport = pubport,
|
||||
privport = privport,
|
||||
@@ -64,22 +64,22 @@ Request = {
|
||||
end,
|
||||
|
||||
__tostring = function(self)
|
||||
return bin.pack(">CCSSSI",
|
||||
self.version,
|
||||
return bin.pack(">CCSSSI",
|
||||
self.version,
|
||||
(self.proto=="udp" and 1 or 2),
|
||||
0, -- reserved
|
||||
self.privport, self.pubport,
|
||||
self.lifetime)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
GetWANIP = {
|
||||
|
||||
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
setmetatable(o, self)
|
||||
@@ -93,24 +93,24 @@ Response = {
|
||||
if ( #self.data ~= 12 ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local pos
|
||||
pos, self.version, self.op, self.rescode = bin.unpack("<CCS", self.data)
|
||||
|
||||
|
||||
if ( self.rescode ~= ResultCode.SUCCESS or self.op ~= 128 ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
pos, self.time, self.ip = bin.unpack("<II", self.data, pos)
|
||||
self.ip = ipOps.fromdword(self.ip)
|
||||
self.time = stdnse.format_timestamp(self.time)
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
MapPort = {
|
||||
|
||||
|
||||
new = function(self, data)
|
||||
local o = { data = data }
|
||||
setmetatable(o, self)
|
||||
@@ -124,20 +124,20 @@ Response = {
|
||||
if ( #self.data ~= 16 ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
local pos
|
||||
pos, self.version, self.op, self.rescode = bin.unpack("<CCS", self.data)
|
||||
|
||||
|
||||
if ( self.rescode ~= ResultCode.SUCCESS ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
pos, self.time, self.privport, self.pubport, self.lifetime = bin.unpack(">ISSI", self.data, pos)
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -145,33 +145,33 @@ Response = {
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = { host = host, port = port }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
exchPacket = function(self, data)
|
||||
local socket = nmap.new_socket("udp")
|
||||
socket:set_timeout(5000)
|
||||
|
||||
|
||||
local status = socket:sendto(self.host, self.port, data)
|
||||
if ( not(status) ) then
|
||||
socket:close()
|
||||
return false, "Failed to send request to device"
|
||||
end
|
||||
|
||||
|
||||
local response
|
||||
status, response = socket:receive()
|
||||
status, response = socket:receive()
|
||||
socket:close()
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from router"
|
||||
end
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the WAN ip of the router
|
||||
getWANIP = function(self)
|
||||
local packet = Request.GetWANIP:new()
|
||||
@@ -179,7 +179,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return status, response
|
||||
end
|
||||
|
||||
|
||||
response = Response.GetWANIP:new(response)
|
||||
if ( not(response) ) then
|
||||
return false, "Failed to parse response from router"
|
||||
@@ -187,7 +187,7 @@ Helper = {
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
--- Maps a public port to a private port
|
||||
-- @param pubport number containing the public external port to map
|
||||
-- @param privport number containing the private internal port to map
|
||||
@@ -199,7 +199,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return status, response
|
||||
end
|
||||
|
||||
|
||||
response = Response.MapPort:new(response)
|
||||
if ( not(response) ) then
|
||||
return false, "Failed to parse response from router"
|
||||
@@ -207,15 +207,15 @@ Helper = {
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
unmapPort = function(self, pubport, privport)
|
||||
return self:mapPort(pubport, privport, 0)
|
||||
end,
|
||||
|
||||
|
||||
unmapAllPorts = function(self)
|
||||
return self.mapPort(0, 0, 0)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
322
nselib/ncp.lua
322
nselib/ncp.lua
@@ -24,7 +24,7 @@
|
||||
-- * NCP
|
||||
-- - Contains the "native" NCP functions sending the actual request to the
|
||||
-- server.
|
||||
--
|
||||
--
|
||||
-- * Socket
|
||||
-- - A buffered socket implementation
|
||||
--
|
||||
@@ -85,13 +85,13 @@ NCPFunction = {
|
||||
|
||||
NCPVerb = {
|
||||
Resolve = 1,
|
||||
List = 5,
|
||||
List = 5,
|
||||
Search = 6,
|
||||
}
|
||||
|
||||
-- The NCP Packet
|
||||
Packet = {
|
||||
|
||||
|
||||
--- Creates a new instance of Packet
|
||||
-- @return o instance of Packet
|
||||
new = function(self)
|
||||
@@ -111,7 +111,7 @@ Packet = {
|
||||
--- Sets the NCP packet length
|
||||
-- @param n number containing the length
|
||||
setNCPLength = function(self, n) self.ncp_ip.length = n end,
|
||||
|
||||
|
||||
--- Gets the NCP packet length
|
||||
-- @return n number containing the NCP length
|
||||
getNCPLength = function(self) return self.ncp_ip.length end,
|
||||
@@ -119,59 +119,59 @@ Packet = {
|
||||
--- Sets the NCP packet type
|
||||
-- @param t number containing the NCP packet type
|
||||
setType = function(self, t) self.type = t end,
|
||||
|
||||
|
||||
--- Gets the NCP packet type
|
||||
-- @return type number containing the NCP packet type
|
||||
getType = function(self) return self.type end,
|
||||
|
||||
|
||||
--- Sets the NCP packet function
|
||||
-- @param t number containing the NCP function
|
||||
setFunc = function(self, f) self.func = f end,
|
||||
|
||||
|
||||
--- Gets the NCP packet function
|
||||
-- @return func number containing the NCP packet function
|
||||
getFunc = function(self) return self.func end,
|
||||
|
||||
|
||||
--- Sets the NCP sequence number
|
||||
-- @param seqno number containing the sequence number
|
||||
setSeqNo = function(self, n) self.seqno = n end,
|
||||
|
||||
|
||||
--- Sets the NCP connection number
|
||||
-- @param conn number containing the connection number
|
||||
setConnNo = function(self, n) self.conn = n end,
|
||||
|
||||
|
||||
--- Gets the NCP connection number
|
||||
-- @return conn number containing the connection number
|
||||
getConnNo = function(self) return self.conn end,
|
||||
|
||||
|
||||
--- Sets the NCP sub function
|
||||
-- @param subfunc number containing the subfunction
|
||||
setSubFunc = function(self, n) self.subfunc = n end,
|
||||
|
||||
|
||||
--- Gets the NCP sub function
|
||||
-- @return subfunc number containing the subfunction
|
||||
getSubFunc = function(self) return self.subfunc end,
|
||||
|
||||
|
||||
--- Gets the Sequence number
|
||||
-- @return seqno number containing the sequence number
|
||||
getSeqNo = function(self) return self.seqno end,
|
||||
|
||||
|
||||
--- Sets the packet length
|
||||
-- @param len number containing the packet length
|
||||
setLength = function(self, n) self.length = n end,
|
||||
|
||||
|
||||
--- Sets the packet data
|
||||
-- @param data string containing the packet data
|
||||
setData = function(self, data) self.data = data end,
|
||||
|
||||
|
||||
--- Gets the packet data
|
||||
-- @return data string containing the packet data
|
||||
getData = function(self) return self.data end,
|
||||
|
||||
|
||||
--- Sets the packet task
|
||||
-- @param task number containing the packet number
|
||||
setTask = function(self, task) self.task = task end,
|
||||
|
||||
|
||||
--- "Serializes" the packet to a string
|
||||
__tostring = function(self)
|
||||
local UNKNOWN = 0
|
||||
@@ -183,15 +183,15 @@ Packet = {
|
||||
if ( self.length ) then data = data .. bin.pack(">S", self.length) end
|
||||
if ( self.subfunc ) then data = data .. bin.pack("C", self.subfunc) end
|
||||
if ( self.data ) then data = data .. bin.pack("A", self.data) end
|
||||
|
||||
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Parses different responses into suitable tables
|
||||
ResponseParser = {
|
||||
|
||||
|
||||
--- Determines what parser to call based on the contents of the client
|
||||
-- request and server response.
|
||||
-- @param req instance of Packet containing the request to the server
|
||||
@@ -224,10 +224,10 @@ ResponseParser = {
|
||||
return false, "ResponseParser: Failed to parse response"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return false, "ResponseParser: Failed to parse response"
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes a GetFileServerInfo response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -258,28 +258,28 @@ ResponseParser = {
|
||||
[NCPFunction.GetFileServerInfo] = function(resp)
|
||||
local data = resp:getData()
|
||||
local len = #data
|
||||
|
||||
|
||||
if ( len < 78 ) then
|
||||
return false, "Failed to decode GetFileServerInfo"
|
||||
end
|
||||
|
||||
|
||||
local result = {}
|
||||
local pos
|
||||
|
||||
pos, result.srvname, result.os_major, result.os_minor,
|
||||
pos, result.srvname, result.os_major, result.os_minor,
|
||||
result.conns_supported, result.conns_inuse,
|
||||
result.vols_supported, result.os_rev, result.sft_support,
|
||||
result.tts_level, result.conns_max_use, result.acct_version,
|
||||
result.vap_version, result.qms_version, result.print_version,
|
||||
result.virt_console_ver, result.sec_restrict_ver,
|
||||
result.internet_bridge_ver, result.mixed_mode_path,
|
||||
result.virt_console_ver, result.sec_restrict_ver,
|
||||
result.internet_bridge_ver, result.mixed_mode_path,
|
||||
result.local_login_info, result.product_major,
|
||||
result.product_minor, result.product_rev, result.os_lang_id,
|
||||
result.product_minor, result.product_rev, result.os_lang_id,
|
||||
result.support_64_bit = bin.unpack(">A48CCSSSCCCSCCCCCCCCCSSSCC", data)
|
||||
|
||||
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes a GetMountVolumeList response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -290,7 +290,7 @@ ResponseParser = {
|
||||
[NCPFunction.GetMountVolumeList] = function(resp)
|
||||
local data = resp:getData()
|
||||
local len = #data
|
||||
|
||||
|
||||
local pos, items, next_vol_no = bin.unpack("<II", data)
|
||||
local vols = {}
|
||||
for i=1, items do
|
||||
@@ -300,7 +300,7 @@ ResponseParser = {
|
||||
end
|
||||
return true, vols
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes a Ping response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -312,19 +312,19 @@ ResponseParser = {
|
||||
local len = #data
|
||||
local pos
|
||||
local result = {}
|
||||
|
||||
|
||||
if ( len < 40 ) then return false, "NCP Ping result too short" end
|
||||
|
||||
|
||||
pos, result.nds_version = bin.unpack("C", data)
|
||||
-- move to the offset of the
|
||||
pos = pos + 7
|
||||
pos, result.tree_name = bin.unpack("A32", data, pos)
|
||||
|
||||
|
||||
result.tree_name = (result.tree_name:match("^([^_]*)_*$"))
|
||||
|
||||
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes a EnumerateNetworkAddress response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -336,24 +336,24 @@ ResponseParser = {
|
||||
local items
|
||||
local data = resp:getData()
|
||||
local len = #data
|
||||
|
||||
|
||||
pos, result.time_since_boot, result.console_version, result.console_revision,
|
||||
result.srvinfo_flags, result.guid, result.next_search,
|
||||
items = bin.unpack("<ICCSA16II", data)
|
||||
|
||||
|
||||
local function DecodeAddress(data, pos)
|
||||
local COMM_TYPES = { [5] = "udp", [6] = "tcp" }
|
||||
local comm_type, port, ip, _
|
||||
pos, comm_type, _, _, _, port, ip = bin.unpack(">CCISS<I", data, pos)
|
||||
|
||||
return pos, { port = port, ip = ipOps.fromdword(ip),
|
||||
|
||||
return pos, { port = port, ip = ipOps.fromdword(ip),
|
||||
proto = COMM_TYPES[comm_type] or "unknown" }
|
||||
end
|
||||
|
||||
|
||||
if ( ( pos - 1 ) + (items * 14 ) > len ) then
|
||||
return false, "EnumerateNetworkAddress packet too short"
|
||||
end
|
||||
|
||||
|
||||
result.addr = {}
|
||||
for i=1, items do
|
||||
local addr = {}
|
||||
@@ -362,8 +362,8 @@ ResponseParser = {
|
||||
end
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Decodes a Resolve response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -373,28 +373,28 @@ ResponseParser = {
|
||||
Resolve = function(resp)
|
||||
local data = resp:getData()
|
||||
local len = #data
|
||||
|
||||
|
||||
if ( len < 12 ) then
|
||||
return false, "ResponseParser: NCP Resolve, packet too short"
|
||||
end
|
||||
|
||||
|
||||
local pos, frag_size, frag_handle, comp_code = bin.unpack("<III", data)
|
||||
|
||||
if ( len < 38 ) then
|
||||
return false, "ResponseParser: message too short"
|
||||
end
|
||||
|
||||
|
||||
if ( comp_code ~= 0 ) then
|
||||
return false, ("ResponseParser: Completion code returned" ..
|
||||
" non-zero value (%d)"):format(comp_code)
|
||||
end
|
||||
|
||||
|
||||
local pos, tag, entry = bin.unpack("<II", data, pos)
|
||||
|
||||
|
||||
return true, { tag = tag, id = entry }
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Decodes a Search response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -405,22 +405,22 @@ ResponseParser = {
|
||||
local data = resp:getData()
|
||||
local len = #data
|
||||
local entries = {}
|
||||
|
||||
|
||||
if ( len < 12 ) then
|
||||
return false, "ResponseParser: NCP Resolve, packet too short"
|
||||
end
|
||||
|
||||
|
||||
local pos, frag_size, frag_handle, comp_code, iter_handle = bin.unpack("<IIII", data)
|
||||
|
||||
|
||||
if ( comp_code ~= 0 ) then
|
||||
return false, ("ResponseParser: Completion code returned" ..
|
||||
" non-zero value (%d)"):format(comp_code)
|
||||
end
|
||||
|
||||
|
||||
pos = pos + 12
|
||||
local entry_count
|
||||
pos, entry_count = bin.unpack("<I", data, pos)
|
||||
|
||||
|
||||
for i=1, entry_count do
|
||||
local entry
|
||||
pos, entry = ResponseParser.EntryDecoder(data, pos)
|
||||
@@ -430,14 +430,14 @@ ResponseParser = {
|
||||
end
|
||||
return true, entries
|
||||
end,
|
||||
|
||||
|
||||
--- The EntryDecoder is used by the Search and List function, for decoding
|
||||
-- the returned entries.
|
||||
-- @param data containing the response as returned by the server
|
||||
-- @param pos number containing the offset into data to start decoding
|
||||
-- @return pos number containing the new offset after decoding
|
||||
-- @return entry table containing the decoded entry, currently it contains
|
||||
-- one or more of the following fields:
|
||||
-- one or more of the following fields:
|
||||
-- <code>flags</code>
|
||||
-- <code>mod_time</code>
|
||||
-- <code>sub_count</code>
|
||||
@@ -490,11 +490,11 @@ ResponseParser = {
|
||||
if ( iflags.Entry ) then
|
||||
pos, entry.flags, entry.sub_count = bin.unpack("<II", data, pos)
|
||||
end
|
||||
|
||||
|
||||
if ( iflags.ModTime ) then
|
||||
pos, entry.mod_time = bin.unpack("<I", data, pos)
|
||||
end
|
||||
|
||||
|
||||
if ( iflags.BaseClass ) then
|
||||
pos, len = bin.unpack("<I", data, pos)
|
||||
pos, entry.baseclass = bin.unpack("A" .. len, data, pos)
|
||||
@@ -510,7 +510,7 @@ ResponseParser = {
|
||||
entry.rdn = Util.CToLuaString(entry.rdn)
|
||||
pos = ( len % 4 == 0 ) and pos or pos + ( 4 - ( len % 4 ) )
|
||||
end
|
||||
|
||||
|
||||
if ( iflags.DN ) then
|
||||
pos, len = bin.unpack("<I", data, pos)
|
||||
pos, entry.name = bin.unpack("A" .. len, data, pos)
|
||||
@@ -518,11 +518,11 @@ ResponseParser = {
|
||||
entry.name = Util.CToLuaString(entry.name)
|
||||
pos = ( len % 4 == 0 ) and pos or pos + ( 4 - ( len % 4 ) )
|
||||
end
|
||||
|
||||
|
||||
return pos, entry
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Decodes a List response
|
||||
-- @param resp string containing the response as received from the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -532,29 +532,29 @@ ResponseParser = {
|
||||
List = function(resp)
|
||||
local data = resp:getData()
|
||||
local len = #data
|
||||
|
||||
|
||||
if ( len < 12 ) then
|
||||
return false, "ResponseParser: NCP Resolve, packet too short"
|
||||
end
|
||||
|
||||
|
||||
local pos, frag_size, frag_handle, comp_code, iter_handle = bin.unpack("<IIII", data)
|
||||
|
||||
|
||||
if ( comp_code ~= 0 ) then
|
||||
return false, ("ResponseParser: Completion code returned" ..
|
||||
" non-zero value (%d)"):format(comp_code)
|
||||
end
|
||||
|
||||
|
||||
local entry_count
|
||||
pos, entry_count = bin.unpack("<I", data, pos)
|
||||
|
||||
|
||||
local entries = {}
|
||||
|
||||
|
||||
for i=1, entry_count do
|
||||
local entry = {}
|
||||
pos, entry = ResponseParser.EntryDecoder(data, pos)
|
||||
table.insert(entries, entry)
|
||||
end
|
||||
|
||||
|
||||
return true, entries
|
||||
end,
|
||||
}
|
||||
@@ -581,11 +581,11 @@ Response = {
|
||||
--- Parses the Response
|
||||
parse = function(self)
|
||||
local pos, _
|
||||
|
||||
pos, self.signature, self.length, self.type,
|
||||
self.seqno, self.conn, _, self.compl_code,
|
||||
|
||||
pos, self.signature, self.length, self.type,
|
||||
self.seqno, self.conn, _, self.compl_code,
|
||||
self.status_code = bin.unpack(">IISCCSCC", self.header)
|
||||
|
||||
|
||||
if ( self.data ) then
|
||||
local len = #self.data - pos
|
||||
if ( ( #self.data - pos ) ~= ( self.length - 33 ) ) then
|
||||
@@ -594,7 +594,7 @@ Response = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the sequence number
|
||||
-- @return seqno number
|
||||
getSeqNo = function(self) return self.seqno end,
|
||||
@@ -602,11 +602,11 @@ Response = {
|
||||
--- Gets the connection number
|
||||
-- @return conn number
|
||||
getConnNo = function(self) return self.conn end,
|
||||
|
||||
|
||||
--- Gets the data portion of the response
|
||||
-- @return data string
|
||||
getData = function(self) return self.data end,
|
||||
|
||||
|
||||
--- Gets the header portion of the response
|
||||
getHeader = function(self) return self.header end,
|
||||
|
||||
@@ -615,9 +615,9 @@ Response = {
|
||||
hasErrors = function(self)
|
||||
return not( ( self.compl_code == Status.COMPLETION_OK ) and
|
||||
( self.status_code == Status.CONNECTION_OK ) )
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
--- Creates a Response instance from the data read of the socket
|
||||
-- @param socket socket connected to server and ready to receive data
|
||||
-- @return Response containing a new Response instance
|
||||
@@ -627,26 +627,26 @@ Response = {
|
||||
|
||||
local pos, sig, len = bin.unpack(">II", header)
|
||||
if ( len < 8 ) then return false, "NCP packet too short" end
|
||||
|
||||
|
||||
local data
|
||||
|
||||
|
||||
if ( 0 < len - 16 ) then
|
||||
status, data = socket:recv(len - 16)
|
||||
if ( not(status) ) then return false, "Failed to receive data" end
|
||||
end
|
||||
return true, Response:new(header, data)
|
||||
end,
|
||||
|
||||
|
||||
--- "Serializes" the Response instance to a string
|
||||
__tostring = function(self)
|
||||
return bin.pack("AA", self.header, self.data)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The NCP class
|
||||
NCP = {
|
||||
|
||||
|
||||
--- Creates a new NCP instance
|
||||
-- @param socket containing a socket connected to the NCP server
|
||||
-- @return o instance of NCP
|
||||
@@ -659,7 +659,7 @@ NCP = {
|
||||
o.conn = 0
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Handles sending and receiving a NCP message
|
||||
-- @param p Packet containing the request to send to the server
|
||||
-- @return status true on success false on failure
|
||||
@@ -669,16 +669,16 @@ NCP = {
|
||||
Exch = function(self, p)
|
||||
local status, err = self:SendPacket(p)
|
||||
if ( not(status) ) then return status, err end
|
||||
|
||||
|
||||
local status, resp = Response.fromSocket(self.socket)
|
||||
if ( not(status) or resp:hasErrors() ) then return false, resp end
|
||||
|
||||
self.seqno = resp:getSeqNo()
|
||||
self.conn = resp:getConnNo()
|
||||
|
||||
|
||||
return ResponseParser.parse(p, resp)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a packet to the server
|
||||
-- @param p Packet to be sent to the server
|
||||
-- @return status true on success, false on failure
|
||||
@@ -686,34 +686,34 @@ NCP = {
|
||||
SendPacket = function(self, p)
|
||||
if ( not(p:getSeqNo() ) ) then p:setSeqNo(self.seqno + 1) end
|
||||
if ( not(p:getConnNo() ) ) then p:setConnNo(self.conn) end
|
||||
|
||||
|
||||
if ( not(p:getNCPLength()) ) then
|
||||
local len = #(tostring(p))
|
||||
p:setNCPLength(len)
|
||||
end
|
||||
|
||||
|
||||
local status, err = self.socket:send(tostring(p))
|
||||
if ( not(status) ) then return status, "Failed to send data" end
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Creates a connection to the NCP server
|
||||
-- @return status true on success, false on failure
|
||||
CreateConnect = function(self)
|
||||
local p = Packet:new()
|
||||
p:setType(NCPType.CreateConnection)
|
||||
|
||||
|
||||
local resp = self:Exch( p )
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Destroys a connection established with the NCP server
|
||||
-- @return status true on success, false on failure
|
||||
DestroyConnect = function(self)
|
||||
local p = Packet:new()
|
||||
p:setType(NCPType.DestroyConnection)
|
||||
|
||||
|
||||
local resp = self:Exch( p )
|
||||
return true
|
||||
end,
|
||||
@@ -767,12 +767,12 @@ NCP = {
|
||||
-- p:setLength(5)
|
||||
-- p:setSubFunc(28)
|
||||
-- p:setTask(4)
|
||||
--
|
||||
--
|
||||
-- local data = bin.pack("<I", conn_no)
|
||||
-- p:setData(data)
|
||||
-- return self:Exch( p )
|
||||
-- end,
|
||||
|
||||
|
||||
--- Sends a PING to the server which responds with the tree name
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response table (if status is true) containing:
|
||||
@@ -810,28 +810,28 @@ NCP = {
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response table (if status is true) containing:
|
||||
-- <code>tag</code> and <code>id</code>
|
||||
-- @return error message (if status is false)
|
||||
-- @return error message (if status is false)
|
||||
ResolveName = function(self, name)
|
||||
local p = Packet:new()
|
||||
p:setType(NCPType.ServiceRequest)
|
||||
p:setFunc(NCPFunction.SendFragmentedRequest)
|
||||
p:setSubFunc(2)
|
||||
p:setNCPReplyBuf(4108)
|
||||
|
||||
|
||||
local pad = (4 - ( #name % 4 ) )
|
||||
name = Util.ZeroPad(name, #name + pad)
|
||||
|
||||
|
||||
local w_name = Util.ToWideChar(name)
|
||||
local frag_handle, frag_size = 0xffffffff, 64176
|
||||
local msg_size, unknown, proto_flags, nds_verb = 44 + #w_name, 0, 0, 1
|
||||
local nds_reply_buf, version, flags, scope = 4096, 1, 0x2062, 0
|
||||
local unknown2 = 0x0e
|
||||
local ZERO = 0
|
||||
|
||||
|
||||
local data = bin.pack("<IIISSIIISSIIA", frag_handle, frag_size, msg_size,
|
||||
unknown, proto_flags, nds_verb, nds_reply_buf, version, flags,
|
||||
unknown, scope, #w_name, w_name, ZERO)
|
||||
|
||||
|
||||
local comms = { { transport = "TCP" } }
|
||||
local walkers= { { transport = "TCP" } }
|
||||
local PROTOCOLS = { ["TCP"] = 9 }
|
||||
@@ -840,7 +840,7 @@ NCP = {
|
||||
for _, comm in ipairs(comms) do
|
||||
data = data .. bin.pack("<I", PROTOCOLS[comm.transport])
|
||||
end
|
||||
|
||||
|
||||
data = data .. bin.pack("<I", #walkers)
|
||||
for _, walker in ipairs(walkers) do
|
||||
data = data .. bin.pack("<I", PROTOCOLS[walker.transport])
|
||||
@@ -849,7 +849,7 @@ NCP = {
|
||||
p:setData(data)
|
||||
return self:Exch( p )
|
||||
end,
|
||||
|
||||
|
||||
--- Gets a list of volumes from the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response table of vol entries (if status is true)
|
||||
@@ -864,16 +864,16 @@ NCP = {
|
||||
p:setNCPReplyBuf(538)
|
||||
p:setTask(4)
|
||||
p:setLength(12)
|
||||
|
||||
|
||||
local start_vol = 0
|
||||
local vol_req_flags = 1
|
||||
local src_name_space = 0
|
||||
|
||||
|
||||
local data = bin.pack("<III", start_vol, vol_req_flags, src_name_space )
|
||||
p:setData(data)
|
||||
p:setData(data)
|
||||
return self:Exch( p )
|
||||
end,
|
||||
|
||||
|
||||
--- Searches the directory
|
||||
-- @param base entry as resolved by <code>Resolve</code>
|
||||
-- @param class string containing a class name (or * wildcard)
|
||||
@@ -886,7 +886,7 @@ NCP = {
|
||||
-- @return error string (if status is false) containing the error
|
||||
Search = function(self, base, class, name, options)
|
||||
assert( ( base and base.id ), "No base entry was specified")
|
||||
|
||||
|
||||
local class = class and class .. '\0' or '*\0'
|
||||
local name = name and name .. '\0' or '*\0'
|
||||
local w_name = Util.ToWideChar(name)
|
||||
@@ -898,7 +898,7 @@ NCP = {
|
||||
p:setSubFunc(2)
|
||||
p:setNCPReplyBuf(64520)
|
||||
p:setTask(5)
|
||||
|
||||
|
||||
local frag_handle, frag_size, msg_size = 0xffffffff, 64176, 98
|
||||
local unknown, proto_flags, nds_verb, version, flags = 0, 0, 6, 3, 0
|
||||
local nds_reply_buf = 64520
|
||||
@@ -909,16 +909,16 @@ NCP = {
|
||||
local info_flags = 0x0000381d
|
||||
-- a bunch of unknowns
|
||||
local u2, u3, u4, u5, u6, u7, u8, u9 = 0, 0, 2, 2, 0, 0x10, 0, 0x11
|
||||
|
||||
local data = bin.pack("<IIISSIIIIIIIIIIIIIIIIIAIIIA",
|
||||
|
||||
local data = bin.pack("<IIISSIIIIIIIIIIIIIIIIIAIIIA",
|
||||
frag_handle, frag_size, msg_size, unknown, proto_flags,
|
||||
nds_verb, nds_reply_buf, version, flags, iter_handle,
|
||||
base.id, repl_type, numobjs, info_types, info_flags, u2, u3, u4,
|
||||
u5, u6, u7, #w_name, w_name, u8, u9, #w_class, w_class )
|
||||
p:setData(data)
|
||||
p:setData(data)
|
||||
return self:Exch( p )
|
||||
end,
|
||||
|
||||
|
||||
--- Lists the contents of entry
|
||||
-- @param entry entry as resolved by <code>Resolve</code>
|
||||
-- @return status true on success false on failure
|
||||
@@ -932,7 +932,7 @@ NCP = {
|
||||
p:setSubFunc(2)
|
||||
p:setNCPReplyBuf(4112)
|
||||
p:setTask(2)
|
||||
|
||||
|
||||
local frag_handle, frag_size = 0xffffffff, 64176
|
||||
local msg_size, unknown, proto_flags, nds_verb = 40, 0, 0, 5
|
||||
local nds_reply_buf, version, flags = 4100, 1, 0x0001
|
||||
@@ -940,24 +940,24 @@ NCP = {
|
||||
local unknown2 = 0x0e
|
||||
local ZERO = 0
|
||||
local info_flags = 0x0000381d
|
||||
|
||||
|
||||
local data = bin.pack("<IIISSIIISSIII", frag_handle, frag_size, msg_size,
|
||||
unknown, proto_flags, nds_verb, nds_reply_buf, version, flags,
|
||||
unknown, iter_handle, entry.id, info_flags )
|
||||
|
||||
|
||||
-- no name filter
|
||||
data = data .. "\0\0\0\0"
|
||||
|
||||
-- no class filter
|
||||
data = data .. "\0\0\0\0"
|
||||
|
||||
|
||||
p:setData(data)
|
||||
local status, entries = self:Exch( p )
|
||||
if ( not(status) ) then return false, entries end
|
||||
|
||||
|
||||
return true, entries
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -980,17 +980,17 @@ Helper = {
|
||||
self.socket = Socket:new()
|
||||
local status, err = self.socket:connect(self.host, self.port)
|
||||
if ( not(status) ) then return status, err end
|
||||
|
||||
|
||||
self.ncp = NCP:new(self.socket)
|
||||
return self.ncp:CreateConnect()
|
||||
end,
|
||||
|
||||
|
||||
--- Closes the helper connection
|
||||
close = function(self)
|
||||
self.ncp:DestroyConnect()
|
||||
self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Performs a directory search
|
||||
-- @param base string containing the name of the base to search
|
||||
-- @param class string containing the type of class to search
|
||||
@@ -1000,17 +1000,17 @@ Helper = {
|
||||
search = function(self, base, class, name, options)
|
||||
local base = base or "[Root]"
|
||||
local status, entry = self.ncp:ResolveName(base)
|
||||
|
||||
|
||||
if ( not(status) ) then
|
||||
return false, "Search failed, base could not be resolved"
|
||||
end
|
||||
|
||||
local status, result = self.ncp:Search(entry, class, name, options)
|
||||
if (not(status)) then return false, result end
|
||||
|
||||
|
||||
return status, result
|
||||
end,
|
||||
|
||||
|
||||
--- Retrieves some information from the server using the following NCP
|
||||
-- functions:
|
||||
-- <code>GetFileServerInfo</code>
|
||||
@@ -1021,33 +1021,33 @@ Helper = {
|
||||
getServerInfo = function(self)
|
||||
local status, srv_info = self.ncp:GetFileServerInfo()
|
||||
if ( not(status) ) then return false, srv_info end
|
||||
|
||||
|
||||
local status, ping_info = self.ncp:Ping()
|
||||
if ( not(status) ) then return false, ping_info end
|
||||
|
||||
|
||||
local status, net_info = self.ncp:EnumerateNetworkAddress()
|
||||
if ( not(status) ) then return false, net_info end
|
||||
|
||||
|
||||
local status, mnt_list = self.ncp:GetMountVolumeList()
|
||||
if ( not(status) ) then return false, mnt_list end
|
||||
|
||||
|
||||
local output = {}
|
||||
table.insert(output, ("Server name: %s"):format(srv_info.srvname))
|
||||
table.insert(output, ("Tree Name: %s"):format(ping_info.tree_name))
|
||||
table.insert(output,
|
||||
("OS Version: %d.%d (rev %d)"):format(srv_info.os_major,
|
||||
table.insert(output,
|
||||
("OS Version: %d.%d (rev %d)"):format(srv_info.os_major,
|
||||
srv_info.os_minor, srv_info.os_rev))
|
||||
table.insert(output,
|
||||
table.insert(output,
|
||||
("Product version: %d.%d (rev %d)"):format(srv_info.product_major,
|
||||
srv_info.product_minor, srv_info.product_rev))
|
||||
table.insert(output, ("OS Language ID: %d"):format(srv_info.os_lang_id))
|
||||
|
||||
|
||||
local niceaddr = {}
|
||||
for _, addr in ipairs(net_info.addr) do
|
||||
table.insert(niceaddr, ("%s %d/%s"):format(addr.ip,addr.port,
|
||||
table.insert(niceaddr, ("%s %d/%s"):format(addr.ip,addr.port,
|
||||
addr.proto))
|
||||
end
|
||||
|
||||
|
||||
niceaddr.name = "Addresses"
|
||||
table.insert(output, niceaddr)
|
||||
|
||||
@@ -1055,15 +1055,15 @@ Helper = {
|
||||
for _, mount in ipairs(mnt_list) do
|
||||
table.insert(mounts, mount.vol_name)
|
||||
end
|
||||
|
||||
|
||||
mounts.name = "Mounts"
|
||||
table.insert(output, mounts)
|
||||
|
||||
|
||||
if ( nmap.debugging() > 0 ) then
|
||||
table.insert(output, ("Acct version: %d"):format(srv_info.acct_version))
|
||||
table.insert(output, ("VAP version: %d"):format(srv_info.vap_version))
|
||||
table.insert(output, ("QMS version: %d"):format(srv_info.qms_version))
|
||||
table.insert(output,
|
||||
table.insert(output,
|
||||
("Print server version: %d"):format(srv_info.print_version))
|
||||
table.insert(output,
|
||||
("Virtual console version: %d"):format(srv_info.virt_console_ver))
|
||||
@@ -1074,11 +1074,11 @@ Helper = {
|
||||
end
|
||||
|
||||
return true, output
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
Socket =
|
||||
{
|
||||
{
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -1087,7 +1087,7 @@ Socket =
|
||||
o.Buffer = nil
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the socket timeout (@see nmap.set_timeout)
|
||||
-- @param tm number containing the socket timeout in ms
|
||||
set_timeout = function(self, tm) self.Socket:set_timeout(tm) end,
|
||||
@@ -1103,7 +1103,7 @@ Socket =
|
||||
self.Socket:set_timeout(5000)
|
||||
return self.Socket:connect( hostid, port, protocol )
|
||||
end,
|
||||
|
||||
|
||||
--- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -1111,7 +1111,7 @@ Socket =
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
@@ -1121,9 +1121,9 @@ Socket =
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
@@ -1131,13 +1131,13 @@ Socket =
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -1148,7 +1148,7 @@ Socket =
|
||||
}
|
||||
|
||||
--- "static" Utility class containing mostly conversion functions
|
||||
Util =
|
||||
Util =
|
||||
{
|
||||
--- Converts a string to a wide string
|
||||
--
|
||||
@@ -1158,8 +1158,8 @@ Util =
|
||||
ToWideChar = function( str )
|
||||
return str:gsub("(.)", "%1" .. string.char(0x00) )
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Concerts a wide string to string
|
||||
--
|
||||
-- @param wstr containing the wide string to convert
|
||||
@@ -1181,27 +1181,27 @@ Util =
|
||||
for i=1, len - str:len() do str = str .. string.char(0) end
|
||||
return str
|
||||
end,
|
||||
|
||||
|
||||
-- Removes trailing nulls
|
||||
--
|
||||
-- @param str containing the string
|
||||
-- @return ret the string with any trailing nulls removed
|
||||
CToLuaString = function( str )
|
||||
local ret
|
||||
|
||||
|
||||
if ( not(str) ) then return "" end
|
||||
if ( str:sub(-1, -1 ) ~= "\0" ) then return str end
|
||||
|
||||
for i=1, #str do
|
||||
if ( str:sub(-i,-i) == "\0" ) then
|
||||
ret = str:sub(1, -i - 1)
|
||||
if ( str:sub(-i,-i) == "\0" ) then
|
||||
ret = str:sub(1, -i - 1)
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
124
nselib/ndmp.lua
124
nselib/ndmp.lua
@@ -14,7 +14,7 @@ local table = require "table"
|
||||
_ENV = stdnse.module("ndmp", stdnse.seeall)
|
||||
|
||||
NDMP = {
|
||||
|
||||
|
||||
-- Message types
|
||||
MessageType = {
|
||||
CONFIG_GET_HOST_INFO = 0x00000100,
|
||||
@@ -23,24 +23,24 @@ NDMP = {
|
||||
CONFIG_GET_SERVER_INFO = 0x00000108,
|
||||
CONNECT_CLIENT_AUTH = 0x00000901,
|
||||
},
|
||||
|
||||
|
||||
-- The fragment header, 4 bytes where the highest bit is used to determine
|
||||
-- whether the fragment is the last or not.
|
||||
-- whether the fragment is the last or not.
|
||||
FragmentHeader = {
|
||||
size = 4,
|
||||
|
||||
|
||||
-- Creates a new instance of fragment header
|
||||
-- @return o instance of Class
|
||||
new = function(self)
|
||||
local o = {
|
||||
local o = {
|
||||
last = true,
|
||||
length = 24,
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parse data stream and create a new instance of this class
|
||||
-- @param data opaque string
|
||||
-- @return fh new instance of FragmentHeader class
|
||||
@@ -51,7 +51,7 @@ NDMP = {
|
||||
fh.last= bit.rshift(tmp, 31)
|
||||
return fh
|
||||
end,
|
||||
|
||||
|
||||
-- Serializes the instance to an opaque string
|
||||
-- @return str string containing the serialized class
|
||||
__tostring = function(self)
|
||||
@@ -62,9 +62,9 @@ NDMP = {
|
||||
tmp = tmp + self.length
|
||||
return bin.pack(">I", tmp)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- The ndmp 24 byte header
|
||||
Header = {
|
||||
size = 24,
|
||||
@@ -72,7 +72,7 @@ NDMP = {
|
||||
-- creates a new instance of Header
|
||||
-- @return o instance of Header
|
||||
new = function(self)
|
||||
local o = {
|
||||
local o = {
|
||||
seq = 0,
|
||||
time = os.time(),
|
||||
type = 0,
|
||||
@@ -82,9 +82,9 @@ NDMP = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Create a Header instance from opaque data string
|
||||
-- @param data opaque string
|
||||
-- @return hdr new instance of Header
|
||||
@@ -94,16 +94,16 @@ NDMP = {
|
||||
pos, hdr.seq, hdr.time, hdr.type, hdr.msg, hdr.reply_seq, hdr.error = bin.unpack(">IIIIII", data)
|
||||
return hdr
|
||||
end,
|
||||
|
||||
|
||||
-- Serializes the instance to an opaque string
|
||||
-- @return str string containing the serialized class instance
|
||||
__tostring = function(self)
|
||||
return bin.pack(">IIIIII", self.seq, self.time, self.type, self.msg, self.reply_seq, self.error)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
NDMP.Message = {}
|
||||
|
||||
NDMP.Message.ConfigGetServerInfo = {
|
||||
@@ -119,9 +119,9 @@ NDMP.Message.ConfigGetServerInfo = {
|
||||
o.header.msg = NDMP.MessageType.CONFIG_GET_SERVER_INFO
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Create a ConfigGetServerInfo instance from opaque data string
|
||||
-- @param data opaque string
|
||||
-- @return msg new instance of ConfigGetServerInfo
|
||||
@@ -131,7 +131,7 @@ NDMP.Message.ConfigGetServerInfo = {
|
||||
data = data:sub(NDMP.FragmentHeader.size + 1)
|
||||
msg.header = NDMP.Header.parse(data)
|
||||
msg.data = data:sub(NDMP.Header.size + 1)
|
||||
|
||||
|
||||
msg.serverinfo = {}
|
||||
local pos, err = bin.unpack(">I", msg.data)
|
||||
pos, msg.serverinfo.vendor = Util.parseString(msg.data, pos)
|
||||
@@ -144,7 +144,7 @@ NDMP.Message.ConfigGetServerInfo = {
|
||||
-- @return str string containing the serialized class instance
|
||||
__tostring = function(self)
|
||||
return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "")
|
||||
end,
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
@@ -158,7 +158,7 @@ NDMP.Message.ConfigGetHostInfo = {
|
||||
o.header.msg = NDMP.MessageType.CONFIG_GET_HOST_INFO
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
parse = function(data)
|
||||
@@ -167,19 +167,19 @@ NDMP.Message.ConfigGetHostInfo = {
|
||||
data = data:sub(NDMP.FragmentHeader.size + 1)
|
||||
msg.header = NDMP.Header.parse(data)
|
||||
msg.data = data:sub(NDMP.Header.size + 1)
|
||||
|
||||
|
||||
msg.hostinfo = {}
|
||||
local pos, err = bin.unpack(">I", msg.data)
|
||||
pos, msg.hostinfo.hostname = Util.parseString(msg.data, pos)
|
||||
pos, msg.hostinfo.ostype = Util.parseString(msg.data, pos)
|
||||
pos, msg.hostinfo.osver = Util.parseString(msg.data, pos)
|
||||
pos, msg.hostinfo.hostid = Util.parseString(msg.data, pos)
|
||||
pos, msg.hostinfo.hostid = Util.parseString(msg.data, pos)
|
||||
return msg
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "")
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
NDMP.Message.ConfigGetFsInfo = {
|
||||
@@ -194,16 +194,16 @@ NDMP.Message.ConfigGetFsInfo = {
|
||||
o.header.msg = NDMP.MessageType.CONFIG_GET_FS_INFO
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local msg = NDMP.Message.ConfigGetFsInfo:new()
|
||||
msg.frag_header = NDMP.FragmentHeader.parse(data)
|
||||
data = data:sub(NDMP.FragmentHeader.size + 1)
|
||||
msg.header = NDMP.Header.parse(data)
|
||||
msg.data = data:sub(NDMP.Header.size + 1)
|
||||
|
||||
|
||||
local pos, err, count = bin.unpack(">II", msg.data)
|
||||
for i=1, count do
|
||||
local item = {}
|
||||
@@ -217,19 +217,19 @@ NDMP.Message.ConfigGetFsInfo = {
|
||||
pos, item.total_inodes = bin.unpack(">L", msg.data, pos)
|
||||
pos, item.used_inodes = bin.unpack(">L", msg.data, pos)
|
||||
pos, item.fs_env = Util.parseString(msg.data, pos)
|
||||
pos, item.fs_status = Util.parseString(msg.data, pos)
|
||||
pos, item.fs_status = Util.parseString(msg.data, pos)
|
||||
table.insert(msg.fsinfo, item)
|
||||
end
|
||||
return msg
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "")
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
NDMP.Message.UnhandledMessage = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = {
|
||||
frag_header = NDMP.FragmentHeader:new(),
|
||||
@@ -238,9 +238,9 @@ NDMP.Message.UnhandledMessage = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local msg = NDMP.Message.ConfigGetFsInfo:new()
|
||||
msg.frag_header = NDMP.FragmentHeader.parse(data)
|
||||
@@ -249,22 +249,22 @@ NDMP.Message.UnhandledMessage = {
|
||||
msg.data = data:sub(NDMP.Header.size + 1)
|
||||
return msg
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
return tostring(self.frag_header) .. tostring(self.header) .. tostring(self.data or "")
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
Util = {
|
||||
|
||||
|
||||
parseString = function(data, pos)
|
||||
local pos, str = bin.unpack(">a", data, pos)
|
||||
local pad = ( 4 - ( #str % 4 ) ~= 4 ) and 4 - ( #str % 4 ) or 0
|
||||
return pos + pad, str
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
NDMP.TypeToMessage = {
|
||||
@@ -275,7 +275,7 @@ NDMP.TypeToMessage = {
|
||||
|
||||
-- Handles the communication with the NDMP service
|
||||
Comm = {
|
||||
|
||||
|
||||
-- Creates new Comm instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as receuved by the action method
|
||||
@@ -290,9 +290,9 @@ Comm = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects to the NDMP server
|
||||
-- @return status true on success, false on failure
|
||||
connect = function(self)
|
||||
@@ -300,11 +300,11 @@ Comm = {
|
||||
self.socket:set_timeout(10000)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
-- Receives a message from the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return msg NDMP message when a parser exists, otherwise nil
|
||||
sock_recv = function(self)
|
||||
sock_recv = function(self)
|
||||
local status, frag_data = self.socket:receive_buf(match.numbytes(4), true)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to read NDMP 4-byte fragment header"
|
||||
@@ -321,20 +321,20 @@ Comm = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to read NDMP data"
|
||||
end
|
||||
|
||||
|
||||
if ( NDMP.TypeToMessage[header.msg] ) then
|
||||
return true, NDMP.TypeToMessage[header.msg].parse(frag_data .. header_data .. data)
|
||||
end
|
||||
return true, NDMP.Message.UnhandledMessage.parse(frag_data .. header_data .. data)
|
||||
end,
|
||||
|
||||
|
||||
recv = function(self)
|
||||
if ( #self.in_queue > 0 ) then
|
||||
return true, table.remove(self.in_queue, 1)
|
||||
end
|
||||
return self:sock_recv()
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a message to the server
|
||||
-- @param msg NDMP message
|
||||
-- @return status true on success, false on failure
|
||||
@@ -344,8 +344,8 @@ Comm = {
|
||||
msg.header.seq = self.seq
|
||||
return self.socket:send(tostring(msg))
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
exch = function(self, msg)
|
||||
local status, err = self:send(msg)
|
||||
if ( not(status) ) then
|
||||
@@ -363,50 +363,50 @@ Comm = {
|
||||
local reply
|
||||
status, reply = self:sock_recv()
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive msg from server"
|
||||
return false, "Failed to receive msg from server"
|
||||
elseif ( reply and reply.header and reply.header.reply_seq == s_seq ) then
|
||||
return true, reply
|
||||
else
|
||||
table.insert(self.in_queue, reply)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
close = function(self) return self.socket:close() end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = { comm = Comm:new(host, port) }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
connect = function(self)
|
||||
return self.comm:connect()
|
||||
end,
|
||||
|
||||
|
||||
getFsInfo = function(self)
|
||||
return self.comm:exch(NDMP.Message.ConfigGetFsInfo:new())
|
||||
end,
|
||||
|
||||
|
||||
getHostInfo = function(self)
|
||||
return self.comm:exch(NDMP.Message.ConfigGetHostInfo:new())
|
||||
end,
|
||||
|
||||
|
||||
getServerInfo = function(self)
|
||||
return self.comm:exch(NDMP.Message.ConfigGetServerInfo:new())
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.comm:close()
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
-- Creates and parses NetBIOS traffic. The primary use for this is to send
|
||||
-- NetBIOS name requests.
|
||||
-- NetBIOS name requests.
|
||||
--
|
||||
-- @author Ron Bowes <ron@skullsecurity.net>
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
@@ -27,15 +27,15 @@ types = {
|
||||
-- pass case-sensitive data in a case-insensitive way)
|
||||
--
|
||||
-- There are two levels of encoding performed:
|
||||
-- * L1: Pad the string to 16 characters withs spaces (or NULLs if it's the
|
||||
-- * L1: Pad the string to 16 characters withs spaces (or NULLs if it's the
|
||||
-- wildcard "*") and replace each byte with two bytes representing each
|
||||
-- of its nibbles, plus 0x41.
|
||||
-- of its nibbles, plus 0x41.
|
||||
-- * L2: Prepend the length to the string, and to each substring in the scope
|
||||
-- (separated by periods).
|
||||
--@param name The name that will be encoded (eg. "TEST1").
|
||||
-- (separated by periods).
|
||||
--@param name The name that will be encoded (eg. "TEST1").
|
||||
--@param scope [optional] The scope to encode it with. I've never seen scopes used
|
||||
-- in the real world (eg, "insecure.org").
|
||||
--@return The L2-encoded name and scope
|
||||
-- in the real world (eg, "insecure.org").
|
||||
--@return The L2-encoded name and scope
|
||||
-- (eg. "\x20FEEFFDFEDBCACACACACACACACACAAA\x08insecure\x03org")
|
||||
function name_encode(name, scope)
|
||||
|
||||
@@ -65,7 +65,7 @@ function name_encode(name, scope)
|
||||
L1_encoded = L1_encoded .. string.char(bit.rshift(bit.band(b, 0x0F), 0) + 0x41)
|
||||
end
|
||||
|
||||
-- Do the L2 encoding
|
||||
-- Do the L2 encoding
|
||||
local L2_encoded = string.char(32) .. L1_encoded
|
||||
|
||||
if scope ~= nil then
|
||||
@@ -84,7 +84,7 @@ end
|
||||
|
||||
--- Does the exact opposite of name_encode. Converts an encoded name to
|
||||
-- the string representation. If the encoding is invalid, it will still attempt
|
||||
-- to decode the string as best as possible.
|
||||
-- to decode the string as best as possible.
|
||||
--@param encoded_name The L2-encoded name
|
||||
--@return the decoded name and the scope. The name will still be padded, and the
|
||||
-- scope will never be nil (empty string is returned if no scope is present)
|
||||
@@ -124,11 +124,11 @@ function name_decode(encoded_name)
|
||||
end
|
||||
|
||||
--- Sends out a UDP probe on port 137 to get a human-readable list of names the
|
||||
-- the system is using.
|
||||
--@param host The IP or hostname to check.
|
||||
--@param prefix [optional] The prefix to put on each line when it's returned.
|
||||
--@return (status, result) If status is true, the result is a human-readable
|
||||
-- list of names. Otherwise, result is an error message.
|
||||
-- the system is using.
|
||||
--@param host The IP or hostname to check.
|
||||
--@param prefix [optional] The prefix to put on each line when it's returned.
|
||||
--@return (status, result) If status is true, the result is a human-readable
|
||||
-- list of names. Otherwise, result is an error message.
|
||||
function get_names(host, prefix)
|
||||
|
||||
local status, names, statistics = do_nbstat(host)
|
||||
@@ -151,11 +151,11 @@ function get_names(host, prefix)
|
||||
end
|
||||
|
||||
--- Sends out a UDP probe on port 137 to get the server's name (that is, the
|
||||
-- entry in its NBSTAT table with a 0x20 suffix).
|
||||
--@param host The IP or hostname of the server.
|
||||
--@param names [optional] The names to use, from <code>do_nbstat</code>.
|
||||
--@return (status, result) If status is true, the result is the NetBIOS name.
|
||||
-- otherwise, result is an error message.
|
||||
-- entry in its NBSTAT table with a 0x20 suffix).
|
||||
--@param host The IP or hostname of the server.
|
||||
--@param names [optional] The names to use, from <code>do_nbstat</code>.
|
||||
--@return (status, result) If status is true, the result is the NetBIOS name.
|
||||
-- otherwise, result is an error message.
|
||||
function get_server_name(host, names)
|
||||
|
||||
local status
|
||||
@@ -163,7 +163,7 @@ function get_server_name(host, names)
|
||||
|
||||
if names == nil then
|
||||
status, names = do_nbstat(host)
|
||||
|
||||
|
||||
if(status == false) then
|
||||
return false, names
|
||||
end
|
||||
@@ -178,13 +178,13 @@ function get_server_name(host, names)
|
||||
return false, "Couldn't find NetBIOS server name"
|
||||
end
|
||||
|
||||
--- Sends out a UDP probe on port 137 to get the user's name (that is, the
|
||||
--- Sends out a UDP probe on port 137 to get the user's name (that is, the
|
||||
-- entry in its NBSTAT table with a 0x03 suffix, that isn't the same as
|
||||
-- the server's name. If the username can't be determined, which is frequently
|
||||
-- the case, nil is returned.
|
||||
--@param host The IP or hostname of the server.
|
||||
--@param names [optional] The names to use, from <code>do_nbstat</code>.
|
||||
--@return (status, result) If status is true, the result is the NetBIOS name or nil.
|
||||
-- the case, nil is returned.
|
||||
--@param host The IP or hostname of the server.
|
||||
--@param names [optional] The names to use, from <code>do_nbstat</code>.
|
||||
--@return (status, result) If status is true, the result is the NetBIOS name or nil.
|
||||
-- otherwise, result is an error message.
|
||||
function get_user_name(host, names)
|
||||
|
||||
@@ -196,7 +196,7 @@ function get_user_name(host, names)
|
||||
|
||||
if(names == nil) then
|
||||
status, names = do_nbstat(host)
|
||||
|
||||
|
||||
if(status == false) then
|
||||
return false, names
|
||||
end
|
||||
@@ -207,15 +207,15 @@ function get_user_name(host, names)
|
||||
return true, names[i]['name']
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return true, nil
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- This is the function that actually handles the UDP query to retrieve
|
||||
-- the NBSTAT information. We make use of the Nmap registry here, so if another
|
||||
-- script has already performed a nbstat query, the result can be re-used.
|
||||
-- script has already performed a nbstat query, the result can be re-used.
|
||||
--
|
||||
-- The NetBIOS request's header looks like this:
|
||||
--<code>
|
||||
@@ -231,7 +231,7 @@ end
|
||||
--</code>
|
||||
--
|
||||
-- In this case, the TRN_ID is a constant (0x1337, what else?), the flags
|
||||
-- are 0, and we have one question. All fields are network byte order.
|
||||
-- are 0, and we have one question. All fields are network byte order.
|
||||
--
|
||||
-- The body of the packet is a list of names to check for in the following
|
||||
-- format:
|
||||
@@ -254,10 +254,10 @@ end
|
||||
-- * (2 bytes) flags
|
||||
-- * (variable) statistics (usually mac address)
|
||||
--
|
||||
--@param host The IP or hostname of the system.
|
||||
--@param host The IP or hostname of the system.
|
||||
--@return (status, names, statistics) If status is true, then the servers names are
|
||||
-- returned as a table containing 'name', 'suffix', and 'flags'.
|
||||
-- Otherwise, names is an error message and statistics is undefined.
|
||||
-- returned as a table containing 'name', 'suffix', and 'flags'.
|
||||
-- Otherwise, names is an error message and statistics is undefined.
|
||||
function do_nbstat(host)
|
||||
|
||||
local status, err
|
||||
@@ -288,7 +288,7 @@ function do_nbstat(host)
|
||||
end
|
||||
|
||||
-- Create the query header
|
||||
local query = bin.pack(">SSSSSS",
|
||||
local query = bin.pack(">SSSSSS",
|
||||
0x1337, -- Transaction id
|
||||
0x0000, -- Flags
|
||||
1, -- Questions
|
||||
@@ -297,7 +297,7 @@ function do_nbstat(host)
|
||||
0 -- Extra
|
||||
)
|
||||
|
||||
query = query .. bin.pack(">zSS",
|
||||
query = query .. bin.pack(">zSS",
|
||||
encoded_name, -- Encoded name
|
||||
0x0021, -- Query type (0x21 = NBSTAT)
|
||||
0x0001 -- Class = IN
|
||||
@@ -364,8 +364,8 @@ function do_nbstat(host)
|
||||
for i = 1, name_count do
|
||||
local name, suffix, flags
|
||||
|
||||
-- Instead of reading the 16-byte name and pulling off the suffix,
|
||||
-- we read the first 15 bytes and then the 1-byte suffix.
|
||||
-- Instead of reading the 16-byte name and pulling off the suffix,
|
||||
-- we read the first 15 bytes and then the 1-byte suffix.
|
||||
pos, name, suffix, flags = bin.unpack(">A15CS", result, pos)
|
||||
name = string.gsub(name, "[ ]*$", "")
|
||||
|
||||
@@ -403,14 +403,14 @@ function nbquery(host, nbname, options)
|
||||
options.host = host.ip
|
||||
options.flags = options.flags or ( options.multiple and 0x0110 )
|
||||
options.id = math.random(0xFFFF)
|
||||
|
||||
|
||||
-- encode and chop off the leading byte, as the dns library takes care of
|
||||
-- specifying the length
|
||||
local encoded_name = name_encode(nbname):sub(2)
|
||||
|
||||
local status, response = dns.query( encoded_name, options )
|
||||
if ( not(status) ) then return false, "ERROR: nbquery failed" end
|
||||
|
||||
|
||||
local results = {}
|
||||
-- discard any additional responses
|
||||
if ( options.multiple and #response > 0 ) then
|
||||
@@ -426,10 +426,10 @@ function nbquery(host, nbname, options)
|
||||
else
|
||||
local dname = string.char(#response.answers[1].dname) .. response.answers[1].dname
|
||||
return true, { { peer = host.ip, name = name_decode(dname) } }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Convert the 16-bit flags field to a string.
|
||||
---Convert the 16-bit flags field to a string.
|
||||
--@param flags The 16-bit flags field
|
||||
--@return A string representing the flags
|
||||
function flags_to_string(flags)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
-- - A helper class that provides easy access to the rest of the library
|
||||
--
|
||||
-- o DominoSocket
|
||||
-- - This is a copy of the DB2Socket class which provides fundamental
|
||||
-- - This is a copy of the DB2Socket class which provides fundamental
|
||||
-- buffering
|
||||
--
|
||||
--
|
||||
@@ -50,11 +50,11 @@ _ENV = stdnse.module("nrpc", stdnse.seeall)
|
||||
|
||||
-- The Domino Packet
|
||||
DominoPacket = {
|
||||
|
||||
|
||||
--- Creates a new DominoPacket instance
|
||||
--
|
||||
-- @param data string containing the packet data
|
||||
-- @return a new DominoPacket instance
|
||||
-- @return a new DominoPacket instance
|
||||
new = function( self, data )
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -62,7 +62,7 @@ DominoPacket = {
|
||||
o.data = data
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Reads a packet from the DominoSocket
|
||||
--
|
||||
-- @param domsock DominoSocket connected to the server
|
||||
@@ -79,12 +79,12 @@ DominoPacket = {
|
||||
__tostring = function(self)
|
||||
return bin.pack("<SA", #self.data, self.data )
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
DominoSocket =
|
||||
{
|
||||
|
||||
{
|
||||
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -105,7 +105,7 @@ DominoSocket =
|
||||
self.Socket:set_timeout(5000)
|
||||
return self.Socket:connect( hostid, port, protocol )
|
||||
end,
|
||||
|
||||
|
||||
--- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -113,7 +113,7 @@ DominoSocket =
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
@@ -123,9 +123,9 @@ DominoSocket =
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
@@ -133,13 +133,13 @@ DominoSocket =
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -150,7 +150,7 @@ DominoSocket =
|
||||
}
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
--- Creates a new Helper instance
|
||||
--
|
||||
-- @param host table as recieved by the script action method
|
||||
@@ -169,13 +169,13 @@ Helper = {
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err error message if status is false
|
||||
connect = function( self )
|
||||
connect = function( self )
|
||||
if( not( self.domsock:connect( self.host.ip, self.port.number, "tcp" ) ) ) then
|
||||
return false, ("ERROR: Failed to connect to Domino server %s:%d\n"):format(self.host, self.port)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Disconnects from the Lotus Domino Server
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -190,14 +190,14 @@ Helper = {
|
||||
-- @return status true on success false on failure
|
||||
-- @return domino_id if it exists and status is true
|
||||
-- err if status is false
|
||||
isValidUser = function( self, username )
|
||||
isValidUser = function( self, username )
|
||||
local data = bin.pack("H", "00001e00000001000080000007320000700104020000fb2b2d00281f1e000000124c010000000000")
|
||||
local status, id_data
|
||||
local data_len, pos, total_len, pkt_type, valid_user
|
||||
|
||||
|
||||
self.domsock:send( tostring(DominoPacket:new( data )) )
|
||||
data = DominoPacket:new():read( self.domsock )
|
||||
|
||||
|
||||
data = bin.pack("HCHAH", "0100320002004f000100000500000900", #username + 1, "000000000000000000000000000000000028245573657273290000", username, "00")
|
||||
self.domsock:send( tostring(DominoPacket:new( data ) ) )
|
||||
status, id_data = DominoPacket:new():read( self.domsock )
|
||||
@@ -205,7 +205,7 @@ Helper = {
|
||||
pos, pkt_type = bin.unpack("C", id_data, 3)
|
||||
pos, valid_user = bin.unpack("C", id_data, 11)
|
||||
pos, total_len = bin.unpack("<S", id_data, 13)
|
||||
|
||||
|
||||
if ( pkt_type == 0x16 ) then
|
||||
if ( valid_user == 0x19 ) then
|
||||
return true
|
||||
@@ -213,7 +213,7 @@ Helper = {
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( pkt_type ~= 0x7e ) then
|
||||
return false, "Failed to retrieve ID file"
|
||||
end
|
||||
@@ -222,10 +222,10 @@ Helper = {
|
||||
|
||||
id_data = id_data:sub(33)
|
||||
id_data = id_data .. data:sub(11, total_len - #id_data + 11)
|
||||
|
||||
|
||||
return true, id_data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
---
|
||||
-- Debugging functions for Nmap scripts.
|
||||
-- Debugging functions for Nmap scripts.
|
||||
--
|
||||
-- This module contains various handy functions for debugging. These should
|
||||
-- never be used for actual results, only during testing.
|
||||
--
|
||||
-- never be used for actual results, only during testing.
|
||||
--
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
|
||||
local coroutine = require "coroutine"
|
||||
@@ -18,13 +18,13 @@ _ENV = stdnse.module("nsedebug", stdnse.seeall)
|
||||
local EMPTY = {}; -- Empty constant table
|
||||
|
||||
---
|
||||
-- Converts an arbitrary data type into a string. Will recursively convert
|
||||
-- tables. This can be very useful for debugging.
|
||||
-- Converts an arbitrary data type into a string. Will recursively convert
|
||||
-- tables. This can be very useful for debugging.
|
||||
--
|
||||
--@param data The data to convert.
|
||||
--@param data The data to convert.
|
||||
--@param indent (optional) The number of times to indent the line. Default
|
||||
-- is 0.
|
||||
--@return A string representation of a data, will be one or more full lines.
|
||||
-- is 0.
|
||||
--@return A string representation of a data, will be one or more full lines.
|
||||
function tostr(data, indent)
|
||||
local str
|
||||
|
||||
@@ -108,7 +108,7 @@ function print_hex(str)
|
||||
end
|
||||
io.write(string.format("%c", ch))
|
||||
end
|
||||
|
||||
|
||||
io.write("\n")
|
||||
end
|
||||
|
||||
@@ -117,7 +117,7 @@ function print_hex(str)
|
||||
|
||||
end
|
||||
|
||||
---Print out a stacktrace. The stacktrace will naturally include this function call.
|
||||
---Print out a stacktrace. The stacktrace will naturally include this function call.
|
||||
function print_stack()
|
||||
local thread = coroutine.running()
|
||||
local trace = debug.traceback(thread);
|
||||
|
||||
@@ -39,7 +39,7 @@ if pcall(require,'openssl') then
|
||||
HAVE_SSL = true
|
||||
end
|
||||
|
||||
--- A Session class holds connection and interaction with the server
|
||||
--- A Session class holds connection and interaction with the server
|
||||
Session = {
|
||||
|
||||
--- Creates a new session object
|
||||
|
||||
@@ -114,7 +114,7 @@ OSPF = {
|
||||
|
||||
Hello = {
|
||||
new = function(self)
|
||||
local o = {
|
||||
local o = {
|
||||
header = OSPF.Header:new(OSPF.Message.HELLO),
|
||||
options = 0x02,
|
||||
prio = 0,
|
||||
@@ -250,7 +250,7 @@ OSPF = {
|
||||
if ( self.more ) then flags = flags + 2 end
|
||||
if ( self.master) then flags= flags + 1 end
|
||||
|
||||
local data = bin.pack(">SCCI", self.mtu, self.options, flags, self.sequence)
|
||||
local data = bin.pack(">SCCI", self.mtu, self.options, flags, self.sequence)
|
||||
self.header:setLength(#data)
|
||||
return tostring(self.header) .. data
|
||||
end
|
||||
@@ -276,7 +276,7 @@ OSPF = {
|
||||
return desc
|
||||
end
|
||||
|
||||
return desc
|
||||
return desc
|
||||
end,
|
||||
|
||||
},
|
||||
@@ -291,9 +291,9 @@ OSPF = {
|
||||
return OSPF.DBDescription.parse(data)
|
||||
end
|
||||
return
|
||||
end,
|
||||
end,
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -625,14 +625,14 @@ function Packet:u32(index)
|
||||
return u32(self.buf, index)
|
||||
end
|
||||
--- Return part of the packet contents as a byte string.
|
||||
-- @param index The beginning of the part of the packet to extract. The index
|
||||
-- @param index The beginning of the part of the packet to extract. The index
|
||||
-- is 0-based. If omitted the default value is 0 (begining of the string)
|
||||
-- @param length The length of the part of the packet to extract. If omitted
|
||||
-- @param length The length of the part of the packet to extract. If omitted
|
||||
-- the remaining contents from index to the end of the string are returned.
|
||||
-- @return A string.
|
||||
function Packet:raw(index, length)
|
||||
if not index then index = 0 end
|
||||
if not length then length = #self.buf-index end
|
||||
if not length then length = #self.buf-index end
|
||||
return string.char(string.byte(self.buf, index+1, index+1+length-1))
|
||||
end
|
||||
|
||||
@@ -753,7 +753,7 @@ end
|
||||
-- @param id packet ID.
|
||||
function Packet:ip_set_id(id)
|
||||
self:set_u16(self.ip_offset + 4, id)
|
||||
self.ip_id = id
|
||||
self.ip_id = id
|
||||
end
|
||||
--- Set the TTL.
|
||||
-- @param ttl TTL.
|
||||
@@ -1125,7 +1125,7 @@ function Packet:udp_parse(force_continue)
|
||||
end
|
||||
self.udp_len = self:u16(self.udp_offset + 4)
|
||||
self.udp_sum = self:u16(self.udp_offset + 6)
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ local table = require "table"
|
||||
_ENV = stdnse.module("pgsql", stdnse.seeall)
|
||||
|
||||
-- Version 0.3
|
||||
-- Created 02/05/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 02/20/2010 - v0.2 - added detectVersion to automaticaly detect and return
|
||||
-- Created 02/05/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 02/20/2010 - v0.2 - added detectVersion to automaticaly detect and return
|
||||
-- the correct version class
|
||||
-- Revised 03/04/2010 - v0.3 - added support for trust authentication method
|
||||
|
||||
@@ -46,7 +46,7 @@ AuthenticationType = {
|
||||
-- Version 2 of the protocol
|
||||
v2 =
|
||||
{
|
||||
|
||||
|
||||
--- Pad a string with zeroes
|
||||
--
|
||||
-- @param str string containing the string to be padded
|
||||
@@ -63,9 +63,9 @@ v2 =
|
||||
end
|
||||
return str
|
||||
end,
|
||||
|
||||
|
||||
messageDecoder = {
|
||||
|
||||
|
||||
--- Decodes an Auth Request packet
|
||||
--
|
||||
-- @param data string containing raw data recieved from socket
|
||||
@@ -105,7 +105,7 @@ v2 =
|
||||
-- @param pos number containing the offset into the data buffer
|
||||
-- @return pos number containing the offset after decoding
|
||||
-- @return response table containing zero or more of the following <code>error.severity</code>,
|
||||
-- <code>error.code</code>, <code>error.message</code>, <code>error.file</code>,
|
||||
-- <code>error.code</code>, <code>error.message</code>, <code>error.file</code>,
|
||||
-- <code>error.line</code> and <code>error.routine</code>
|
||||
[MessageType.Error] = function( data, len, pos )
|
||||
local tmp = data:sub(pos, pos + len - 4)
|
||||
@@ -143,8 +143,8 @@ v2 =
|
||||
end
|
||||
return -1, "Decoding failed"
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
--- Reads a packet and handles additional socket reads to retrieve remaining data
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
@@ -159,7 +159,7 @@ v2 =
|
||||
local tmp = ""
|
||||
local ptype, len
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
if ( data == nil or data:len() == 0 ) then
|
||||
@@ -167,7 +167,7 @@ v2 =
|
||||
end
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a startup message to the server containing the username and database to connect to
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
@@ -204,9 +204,9 @@ v2 =
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
--- Attempts to authenticate to the pgsql server
|
||||
-- Supports plain-text and MD5 authentication
|
||||
-- Supports plain-text and MD5 authentication
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
-- @param params table containing any additional parameters <code>authtype</code>, <code>version</code>
|
||||
@@ -215,10 +215,10 @@ v2 =
|
||||
-- @param salt string containing the crypthographic salt value
|
||||
-- @return status true on success, false on failure
|
||||
-- @return result table containing parameter status information,
|
||||
-- result string containing an error message if login fails
|
||||
-- result string containing an error message if login fails
|
||||
loginRequest = function ( socket, params, username, password, salt )
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
local response = {}
|
||||
local status, data, len, pos, tmp
|
||||
@@ -251,7 +251,7 @@ v2 =
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Version 3 of the protocol
|
||||
@@ -313,7 +313,7 @@ v3 =
|
||||
-- @param pos number containing the offset into the data buffer
|
||||
-- @return pos number containing the offset after decoding
|
||||
-- @return response table containing zero or more of the following <code>error.severity</code>,
|
||||
-- <code>error.code</code>, <code>error.message</code>, <code>error.file</code>,
|
||||
-- <code>error.code</code>, <code>error.message</code>, <code>error.file</code>,
|
||||
-- <code>error.line</code> and <code>error.routine</code>
|
||||
[MessageType.Error] = function( data, len, pos )
|
||||
local tmp = data:sub(pos, pos + len - 4)
|
||||
@@ -353,7 +353,7 @@ v3 =
|
||||
-- error string containing error message if pos is -1
|
||||
[MessageType.BackendKeyData] = function( data, len, pos )
|
||||
local response = {}
|
||||
|
||||
|
||||
if len ~= 12 then
|
||||
return -1, "ERROR: Invalid BackendKeyData packet"
|
||||
end
|
||||
@@ -376,12 +376,12 @@ v3 =
|
||||
if len ~= 5 then
|
||||
return -1, "ERROR: Invalid ReadyForQuery packet"
|
||||
end
|
||||
|
||||
|
||||
pos, response.status = bin.unpack("C", data, pos )
|
||||
return pos, response
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
--- Reads a packet and handles additional socket reads to retrieve remaining data
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
@@ -397,7 +397,7 @@ v3 =
|
||||
local ptype, len
|
||||
local header
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("processResponse(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
if ( data:len() - pos < 5 ) then
|
||||
@@ -419,7 +419,7 @@ v3 =
|
||||
end
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
--- Decodes the postgres header
|
||||
--
|
||||
-- @param data string containing the server response
|
||||
@@ -428,11 +428,11 @@ v3 =
|
||||
-- @return header table containing <code>type</code> and <code>len</code>
|
||||
decodeHeader = function(data, pos)
|
||||
local ptype, len
|
||||
|
||||
|
||||
pos, ptype, len = bin.unpack("C>I", data, pos)
|
||||
return pos, { ['type'] = ptype, ['len'] = len }
|
||||
end,
|
||||
|
||||
|
||||
--- Process the server response
|
||||
--
|
||||
-- @param data string containing the server response
|
||||
@@ -444,7 +444,7 @@ v3 =
|
||||
local ptype, len, status, response
|
||||
local pos = pos or 1
|
||||
local header
|
||||
|
||||
|
||||
pos, header = v3.decodeHeader( data, pos )
|
||||
|
||||
if v3.messageDecoder[header.type] then
|
||||
@@ -460,9 +460,9 @@ v3 =
|
||||
end
|
||||
return -1, "Decoding failed"
|
||||
end,
|
||||
|
||||
|
||||
--- Attempts to authenticate to the pgsql server
|
||||
-- Supports plain-text and MD5 authentication
|
||||
-- Supports plain-text and MD5 authentication
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
-- @param params table containing any additional parameters <code>authtype</code>, <code>version</code>
|
||||
@@ -471,10 +471,10 @@ v3 =
|
||||
-- @param salt string containing the crypthographic salt value
|
||||
-- @return status true on success, false on failure
|
||||
-- @return result table containing parameter status information,
|
||||
-- result string containing an error message if login fails
|
||||
-- result string containing an error message if login fails
|
||||
loginRequest = function ( socket, params, username, password, salt )
|
||||
|
||||
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
|
||||
local catch = function() socket:close() stdnse.print_debug("loginRequest(): failed") end
|
||||
local try = nmap.new_try(catch)
|
||||
local response, header = {}, {}
|
||||
local status, data, len, tmp, _
|
||||
@@ -516,7 +516,7 @@ v3 =
|
||||
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a startup message to the server containing the username and database to connect to
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
@@ -541,7 +541,7 @@ v3 =
|
||||
if ( not(status) ) then
|
||||
return false, "sendStartup failed"
|
||||
end
|
||||
|
||||
|
||||
if ( not(status) or data:match("^EF") ) then
|
||||
return false, "Incorrect version"
|
||||
end
|
||||
@@ -559,7 +559,7 @@ v3 =
|
||||
|
||||
|
||||
--- Sends a packet requesting SSL communication to be activated
|
||||
--
|
||||
--
|
||||
-- @param socket socket already connected to the pgsql server
|
||||
-- @return boolean true if request was accepted, false if request was denied
|
||||
function requestSSL(socket)
|
||||
@@ -567,14 +567,14 @@ function requestSSL(socket)
|
||||
local ssl_req_code = 80877103
|
||||
local data = bin.pack( ">I>I", 8, ssl_req_code)
|
||||
local status, response
|
||||
|
||||
|
||||
socket:send(data)
|
||||
status, response = socket:receive_bytes(1)
|
||||
|
||||
|
||||
if ( not(status) ) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
if ( response == 'S' ) then
|
||||
return true
|
||||
end
|
||||
@@ -617,11 +617,11 @@ function detectVersion(host, port)
|
||||
socket:connect(host, port)
|
||||
status, response = v3.sendStartup(socket, "versionprobe", "versionprobe")
|
||||
socket:close()
|
||||
|
||||
if ( not(status) and response == 'Incorrect version' ) then
|
||||
|
||||
if ( not(status) and response == 'Incorrect version' ) then
|
||||
return v2
|
||||
end
|
||||
|
||||
|
||||
return v3
|
||||
end
|
||||
|
||||
|
||||
@@ -44,10 +44,10 @@ function login_user(socket, user, pw)
|
||||
local status, line = socket:receive_lines(1)
|
||||
if not stat(line) then return false, err.user_error end
|
||||
socket:send("PASS " .. pw .. "\r\n")
|
||||
|
||||
|
||||
status, line = socket:receive_lines(1)
|
||||
|
||||
if stat(line) then return true, err.none
|
||||
|
||||
if stat(line) then return true, err.none
|
||||
else return false, err.pwError
|
||||
end
|
||||
end
|
||||
@@ -61,15 +61,15 @@ end
|
||||
-- @return Status (true or false).
|
||||
-- @return Error code if status is false.
|
||||
function login_sasl_plain(socket, user, pw)
|
||||
|
||||
|
||||
local auth64 = base64.enc(user .. "\0" .. user .. "\0" .. pw)
|
||||
socket:send("AUTH PLAIN " .. auth64 .. "\r\n")
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
|
||||
if stat(line) then
|
||||
|
||||
if stat(line) then
|
||||
return true, err.none
|
||||
else
|
||||
else
|
||||
return false, err.pwError
|
||||
end
|
||||
end
|
||||
@@ -84,28 +84,28 @@ end
|
||||
function login_sasl_login(socket, user, pw)
|
||||
|
||||
local user64 = base64.enc(user)
|
||||
|
||||
|
||||
local pw64 = base64.enc(pw)
|
||||
|
||||
socket:send("AUTH LOGIN\r\n")
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
if not base64.dec(string.sub(line, 3)) == "User Name:" then
|
||||
return false, err.userError
|
||||
if not base64.dec(string.sub(line, 3)) == "User Name:" then
|
||||
return false, err.userError
|
||||
end
|
||||
|
||||
socket:send(user64)
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
|
||||
if not base64.dec(string.sub(line, 3)) == "Password:" then
|
||||
if not base64.dec(string.sub(line, 3)) == "Password:" then
|
||||
return false, err.userError
|
||||
end
|
||||
|
||||
socket:send(pw64)
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
|
||||
|
||||
if stat(line) then
|
||||
return true, err.none
|
||||
else
|
||||
@@ -126,10 +126,10 @@ function login_apop(socket, user, pw, challenge)
|
||||
|
||||
local apStr = stdnse.tohex(openssl.md5(challenge .. pw))
|
||||
socket:send(("APOP %s %s\r\n"):format(user, apStr))
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
|
||||
if (stat(line)) then
|
||||
|
||||
if (stat(line)) then
|
||||
return true, err.none
|
||||
else
|
||||
return false, err.pwError
|
||||
@@ -153,17 +153,17 @@ function capabilities(host, port)
|
||||
if not stat(first_line) then
|
||||
return nil, "No Response"
|
||||
end
|
||||
|
||||
|
||||
local capas = {}
|
||||
if string.find(first_line, "<[%p%w]+>") then
|
||||
capas.APOP = {}
|
||||
end
|
||||
|
||||
|
||||
local status = socket:send("CAPA\r\n")
|
||||
if( not(status) ) then
|
||||
return nil, "Failed to send"
|
||||
end
|
||||
|
||||
|
||||
status, line = socket:receive_buf("%.", false)
|
||||
if( not(status) ) then
|
||||
return nil, "Failed to receive"
|
||||
@@ -171,11 +171,11 @@ function capabilities(host, port)
|
||||
socket:close()
|
||||
|
||||
local lines = stdnse.strsplit("\r\n",line)
|
||||
if not stat(table.remove(lines,1)) then
|
||||
if not stat(table.remove(lines,1)) then
|
||||
capas.capa = false
|
||||
return capas
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
for _, line in ipairs(lines) do
|
||||
if ( line and #line>0 ) then
|
||||
local capability = line:sub(line:find("[%w-]+"))
|
||||
@@ -187,7 +187,7 @@ function capabilities(host, port)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return capas
|
||||
end
|
||||
|
||||
@@ -201,20 +201,20 @@ end
|
||||
function login_sasl_crammd5(socket, user, pw)
|
||||
|
||||
socket:send("AUTH CRAM-MD5\r\n")
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
|
||||
|
||||
local challenge = base64.dec(string.sub(line, 3))
|
||||
|
||||
local digest = stdnse.tohex(openssl.hmac('md5', pw, challenge))
|
||||
local authStr = base64.enc(user .. " " .. digest)
|
||||
socket:send(authStr .. "\r\n")
|
||||
|
||||
|
||||
local status, line = socket:receive_lines(1)
|
||||
|
||||
if stat(line) then
|
||||
|
||||
if stat(line) then
|
||||
return true, err.none
|
||||
else
|
||||
else
|
||||
return false, err.pwError
|
||||
end
|
||||
end
|
||||
|
||||
304
nselib/pppoe.lua
304
nselib/pppoe.lua
@@ -1,7 +1,7 @@
|
||||
--- A minimalistic PPPoE (Point-to-point protocol over Ethernet)
|
||||
-- library, implementing basic support for PPPoE
|
||||
-- Discovery and Configuration requests. The PPPoE protocol is ethernet based
|
||||
-- and hence does not use any IPs or port numbers.
|
||||
-- and hence does not use any IPs or port numbers.
|
||||
--
|
||||
-- The library contains a number of classes to support packet creation,
|
||||
-- parsing and sending/receiving responses. The classes are:
|
||||
@@ -33,14 +33,14 @@ _ENV = stdnse.module("pppoe", stdnse.seeall)
|
||||
|
||||
EtherType = {
|
||||
PPPOE_DISCOVERY = 0x8863,
|
||||
PPPOE_SESSION = 0x8864,
|
||||
PPPOE_SESSION = 0x8864,
|
||||
}
|
||||
|
||||
-- A Class to handle the Link Control Protocol LCP
|
||||
LCP = {
|
||||
|
||||
|
||||
ConfigOption = {
|
||||
|
||||
|
||||
RESERVED = 0,
|
||||
MRU = 1,
|
||||
AUTH_PROTO = 3,
|
||||
@@ -51,7 +51,7 @@ LCP = {
|
||||
|
||||
-- Value has already been encoded, treat it as a byte stream
|
||||
RAW = -1,
|
||||
|
||||
|
||||
-- Creates a new config option
|
||||
-- @param option number containing the configuration option
|
||||
-- @param value containing the configuration option value
|
||||
@@ -67,7 +67,7 @@ LCP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the ConfigOption
|
||||
-- class
|
||||
-- @param data string containing raw bytes to parse
|
||||
@@ -76,14 +76,14 @@ LCP = {
|
||||
local opt, pos, len = {}, 1, 0
|
||||
pos, opt.option, len = bin.unpack("CC", data, pos)
|
||||
pos, opt.raw = bin.unpack("A" .. ( len - 2 ), data, pos)
|
||||
|
||||
|
||||
-- MRU
|
||||
if ( 1 == opt.option ) then
|
||||
opt.value = select(2, bin.unpack(">S", opt.raw))
|
||||
end
|
||||
return LCP.ConfigOption:new(opt.option, opt.value, opt.raw)
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the class instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -97,10 +97,10 @@ LCP = {
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
-- A class to hold multiple ordered config options
|
||||
ConfigOptions = {
|
||||
|
||||
|
||||
new = function(self, options)
|
||||
local o = {
|
||||
options = options or {},
|
||||
@@ -109,13 +109,13 @@ LCP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Adds a new config option to the table
|
||||
-- @param option instance of ConfigOption
|
||||
add = function(self, option)
|
||||
table.insert(self.options, option)
|
||||
end,
|
||||
|
||||
|
||||
-- Gets a config option by ID
|
||||
-- @param opt number containing the configuration option to retrieve
|
||||
-- @return v instance of ConfigOption
|
||||
@@ -126,7 +126,7 @@ LCP = {
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Returns all config options in an ordered table
|
||||
-- @return tab table containing all configuration options
|
||||
getTable = function(self)
|
||||
@@ -136,8 +136,8 @@ LCP = {
|
||||
end
|
||||
return tab
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the ConfigOptions
|
||||
-- class
|
||||
-- @param data string containing raw bytes to parse
|
||||
@@ -148,13 +148,13 @@ LCP = {
|
||||
|
||||
repeat
|
||||
pos, opt, len = bin.unpack(">CC", data, pos)
|
||||
if ( 0 == opt ) then break end
|
||||
if ( 0 == opt ) then break end
|
||||
pos, opt_val = bin.unpack("A"..len, data, (pos - 2))
|
||||
options:add(LCP.ConfigOption.parse(opt_val))
|
||||
until( pos == #data )
|
||||
return options
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the class instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -164,9 +164,9 @@ LCP = {
|
||||
end
|
||||
return str
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
ConfigOptionName = {
|
||||
[0] = "Reserved",
|
||||
[1] = "Maximum receive unit",
|
||||
@@ -176,7 +176,7 @@ LCP = {
|
||||
[7] = "Protocol field compression",
|
||||
[8] = "Address and control field compression",
|
||||
},
|
||||
|
||||
|
||||
Code = {
|
||||
CONFIG_REQUEST = 1,
|
||||
CONFIG_ACK = 2,
|
||||
@@ -184,10 +184,10 @@ LCP = {
|
||||
TERMINATE_REQUEST = 5,
|
||||
TERMINATE_ACK = 6,
|
||||
},
|
||||
|
||||
|
||||
-- The LCP Header
|
||||
Header = {
|
||||
|
||||
|
||||
-- Creates a new instance of the LCP header
|
||||
-- @param code number containing the LCP code of the request
|
||||
-- @param identifier number containing the LCP identifier
|
||||
@@ -218,11 +218,11 @@ LCP = {
|
||||
__tostring = function(self)
|
||||
return bin.pack(">CCS", self.code, self.identifier, self.length)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
ConfigRequest = {
|
||||
|
||||
|
||||
-- Creates a new instance of the ConfigRequest class
|
||||
-- @param identifier number containing the LCP identifier
|
||||
-- @param options table of <code>LCP.ConfigOption</code> options
|
||||
@@ -236,7 +236,7 @@ LCP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the ConfigRequest
|
||||
-- class
|
||||
-- @param data string containing raw bytes to parse
|
||||
@@ -247,7 +247,7 @@ LCP = {
|
||||
req.options = LCP.ConfigOptions.parse(data:sub(#tostring(req.header) + 1))
|
||||
return req
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the class instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -255,9 +255,9 @@ LCP = {
|
||||
return tostring(self.header) .. tostring(self.options)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
ConfigNak = {
|
||||
|
||||
|
||||
-- Creates a new instance of the ConfigNak class
|
||||
-- @param identifier number containing the LCP identifier
|
||||
-- @param options table of <code>LCP.ConfigOption</code> options
|
||||
@@ -271,7 +271,7 @@ LCP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the class instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -279,9 +279,9 @@ LCP = {
|
||||
return tostring(self.header) .. tostring(self.options)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
ConfigAck = {
|
||||
|
||||
|
||||
-- Creates a new instance of the ConfigAck class
|
||||
-- @param identifier number containing the LCP identifier
|
||||
-- @param options table of <code>LCP.ConfigOption</code> options
|
||||
@@ -295,7 +295,7 @@ LCP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the ConfigAck class
|
||||
-- @param data string containing raw bytes to parse
|
||||
-- @return o instance of ConfigRequest
|
||||
@@ -305,21 +305,21 @@ LCP = {
|
||||
ack.options = LCP.ConfigOptions.parse(data:sub(#tostring(ack.header) + 1))
|
||||
return ack
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the class instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
self.header.length = 4 + #tostring(self.options)
|
||||
return tostring(self.header) .. tostring(self.options)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
TerminateRequest = {
|
||||
|
||||
-- Creates a new instance of the TerminateRequest class
|
||||
-- @param identifier number containing the LCP identifier
|
||||
-- @return o instance of ConfigNak
|
||||
-- @return o instance of ConfigNak
|
||||
new = function(self, identifier, data)
|
||||
local o = {
|
||||
header = LCP.Header:new(LCP.Code.TERMINATE_REQUEST, identifier),
|
||||
@@ -329,7 +329,7 @@ LCP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the class instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -337,22 +337,22 @@ LCP = {
|
||||
return tostring(self.header) .. self.data
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The PPPoE class
|
||||
PPPoE = {
|
||||
|
||||
|
||||
-- Supported PPPoE codes (requests/responses)
|
||||
Code = {
|
||||
SESSION_DATA = 0x00,
|
||||
PADO = 0x07,
|
||||
PADI = 0x09,
|
||||
PADR = 0x19,
|
||||
PADS = 0x65,
|
||||
PADT = 0xa7,
|
||||
PADS = 0x65,
|
||||
PADT = 0xa7,
|
||||
},
|
||||
|
||||
|
||||
-- Support PPPoE Tag types
|
||||
TagType = {
|
||||
SERVICE_NAME = 0x0101,
|
||||
@@ -360,7 +360,7 @@ PPPoE = {
|
||||
HOST_UNIQUE = 0x0103,
|
||||
AC_COOKIE = 0x0104,
|
||||
},
|
||||
|
||||
|
||||
-- Table used to convert table IDs to Names
|
||||
TagName = {
|
||||
[0x0101] = "Service-Name",
|
||||
@@ -368,14 +368,14 @@ PPPoE = {
|
||||
[0x0103] = "Host-Uniq",
|
||||
[0x0104] = "AC-Cookie",
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
Header = {
|
||||
|
||||
|
||||
-- Creates a new instance of the PPPoE header class
|
||||
-- @param code number containing the PPPoE code
|
||||
-- @param session number containing the PPPoE session
|
||||
-- @return o instance of Header
|
||||
-- @return o instance of Header
|
||||
new = function(self, code, session)
|
||||
local o = {
|
||||
version = 1,
|
||||
@@ -388,7 +388,7 @@ PPPoE = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the class
|
||||
-- @param data string containing raw bytes to parse
|
||||
-- @return o instance of Header
|
||||
@@ -397,56 +397,56 @@ PPPoE = {
|
||||
local header = PPPoE.Header:new()
|
||||
pos, vertyp, header.code, header.session, header.length = bin.unpack(">CCSS", data)
|
||||
header.version = bit.rshift(vertyp,4)
|
||||
header.type = bit.band(vertyp, 0x0F)
|
||||
header.type = bit.band(vertyp, 0x0F)
|
||||
return header
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
local vertype = bit.lshift(self.version, 4) + self.type
|
||||
return bin.pack(">CCSS", vertype, self.code, self.session, self.length)
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- The TAG NVP Class
|
||||
Tag = {
|
||||
|
||||
Tag = {
|
||||
|
||||
-- Creates a new instance of the Tag class
|
||||
-- @param tag number containing the tag type
|
||||
-- @param value string/number containing the tag value
|
||||
-- @return o instance of Tag
|
||||
-- @return o instance of Tag
|
||||
new = function(self, tag, value)
|
||||
local o = { tag = tag, value = value or "" }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
return bin.pack(">SSA", self.tag, #self.value, self.value)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
PADI = {
|
||||
|
||||
|
||||
-- Creates a new instance of the PADI class
|
||||
-- @param tags table of <code>PPPoE.Tag</code> instances
|
||||
-- @param value string/number containing the tag value
|
||||
-- @return o instance of ConfigNak
|
||||
-- @return o instance of ConfigNak
|
||||
new = function(self, tags)
|
||||
local c = ""
|
||||
for i=1, 4 do
|
||||
c = c .. math.random(255)
|
||||
end
|
||||
|
||||
|
||||
local o = {
|
||||
header = PPPoE.Header:new(PPPoE.Code.PADI),
|
||||
tags = tags or {
|
||||
tags = tags or {
|
||||
PPPoE.Tag:new(PPPoE.TagType.SERVICE_NAME),
|
||||
PPPoE.Tag:new(PPPoE.TagType.HOST_UNIQUE, bin.pack("A", c))
|
||||
}
|
||||
@@ -455,7 +455,7 @@ PPPoE = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -466,20 +466,20 @@ PPPoE = {
|
||||
self.header.length = #tags
|
||||
return tostring(self.header) .. tags
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
PADO = {
|
||||
|
||||
|
||||
-- Creates a new instance of the PADO class
|
||||
-- @return o instance of PADO
|
||||
-- @return o instance of PADO
|
||||
new = function(self)
|
||||
local o = { tags = {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the class
|
||||
-- @param data string containing raw bytes to parse
|
||||
-- @return o instance of PADO
|
||||
@@ -488,7 +488,7 @@ PPPoE = {
|
||||
pado.header = PPPoE.Header.parse(data)
|
||||
local pos = #tostring(pado.header) + 1
|
||||
pado.data = data:sub(pos)
|
||||
|
||||
|
||||
repeat
|
||||
local tag, len, decoded, raw
|
||||
pos, tag, len = bin.unpack(">SS", data, pos)
|
||||
@@ -503,18 +503,18 @@ PPPoE = {
|
||||
t.decoded = decoded
|
||||
table.insert(pado.tags, t)
|
||||
until( pos >= #data )
|
||||
|
||||
|
||||
return pado
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
PADR = {
|
||||
|
||||
|
||||
-- Creates a new instance of the PADR class
|
||||
-- @param tags table of <code>PPPoE.Tag</code> instances
|
||||
-- @return o instance of PADR
|
||||
-- @return o instance of PADR
|
||||
new = function(self, tags)
|
||||
local o = {
|
||||
local o = {
|
||||
tags = tags or {},
|
||||
header = PPPoE.Header:new(PPPoE.Code.PADR)
|
||||
}
|
||||
@@ -522,7 +522,7 @@ PPPoE = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -533,20 +533,20 @@ PPPoE = {
|
||||
self.header.length = #tags
|
||||
return tostring(self.header) .. tags
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
PADS = {
|
||||
|
||||
|
||||
-- Creates a new instance of the PADS class
|
||||
-- @return o instance of PADS
|
||||
-- @return o instance of PADS
|
||||
new = function(self)
|
||||
local o = { tags = {} }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the class
|
||||
-- @param data string containing raw bytes to parse
|
||||
-- @return o instance of PADS
|
||||
@@ -559,12 +559,12 @@ PPPoE = {
|
||||
end,
|
||||
|
||||
},
|
||||
|
||||
|
||||
PADT = {
|
||||
|
||||
|
||||
-- Creates a new instance of the PADT class
|
||||
-- @param session number containing the PPPoE session
|
||||
-- @return o instance of PADT
|
||||
-- @return o instance of PADT
|
||||
new = function(self, session)
|
||||
local o = { header = PPPoE.Header:new(PPPoE.Code.PADT) }
|
||||
setmetatable(o, self)
|
||||
@@ -572,7 +572,7 @@ PPPoE = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the class
|
||||
-- @param data string containing raw bytes to parse
|
||||
-- @return o instance of PADI
|
||||
@@ -581,22 +581,22 @@ PPPoE = {
|
||||
padt.header = PPPoE.Header.parse(data)
|
||||
return padt
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
return tostring(self.header)
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
SessionData = {
|
||||
|
||||
|
||||
-- Creates a new instance of the SessionData class
|
||||
-- @param session number containing the PPPoE session
|
||||
-- @param data string containing the LCP data to send
|
||||
-- @return o instance of ConfigNak
|
||||
-- @return o instance of ConfigNak
|
||||
new = function(self, session, data)
|
||||
local o = {
|
||||
local o = {
|
||||
data = data or "",
|
||||
header = PPPoE.Header:new(PPPoE.Code.SESSION_DATA)
|
||||
}
|
||||
@@ -605,7 +605,7 @@ PPPoE = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Parses a byte stream and builds a new instance of the class
|
||||
-- @param data string containing raw bytes to parse
|
||||
-- @return o instance of SessionData
|
||||
@@ -616,7 +616,7 @@ PPPoE = {
|
||||
sess.data = data:sub(pos)
|
||||
return sess
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string
|
||||
-- @return string containing the raw config option
|
||||
__tostring = function(self)
|
||||
@@ -624,24 +624,24 @@ PPPoE = {
|
||||
self.header.length = 2 + 4 + #self.data
|
||||
return tostring(self.header) .. bin.pack(">S", 0xC021) .. self.data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- A bunch of tag decoders
|
||||
PPPoE.TagDecoder = {}
|
||||
PPPoE.TagDecoder.decodeHex = function(data, pos, len) return pos + len, stdnse.tohex(data:sub(pos, pos+len)) end
|
||||
PPPoE.TagDecoder.decodeStr = function(data, pos, len) return pos + len, data:sub(pos, pos + len - 1) end
|
||||
PPPoE.TagDecoder[PPPoE.TagType.SERVICE_NAME]= PPPoE.TagDecoder.decodeStr
|
||||
PPPoE.TagDecoder[PPPoE.TagType.SERVICE_NAME]= PPPoE.TagDecoder.decodeStr
|
||||
PPPoE.TagDecoder[PPPoE.TagType.AC_NAME] = PPPoE.TagDecoder.decodeStr
|
||||
PPPoE.TagDecoder[PPPoE.TagType.AC_COOKIE] = PPPoE.TagDecoder.decodeHex
|
||||
PPPoE.TagDecoder[PPPoE.TagType.HOST_UNIQUE] = PPPoE.TagDecoder.decodeHex
|
||||
|
||||
-- The Comm class responsible for communication with the PPPoE server
|
||||
Comm = {
|
||||
|
||||
|
||||
-- Creates a new instance of the Comm class
|
||||
-- @param iface string containing the interface name
|
||||
-- @param src_mac string containing the source MAC address
|
||||
@@ -657,24 +657,24 @@ Comm = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Sets up the pcap receiving socket
|
||||
-- @return status true on success
|
||||
connect = function(self)
|
||||
self.socket = nmap.new_socket()
|
||||
self.socket:set_timeout(10000)
|
||||
|
||||
|
||||
-- there's probably a more elegant way of doing this
|
||||
local mac = {}
|
||||
for i=1, #self.src_mac do table.insert(mac, select(2,bin.unpack("H", self.src_mac, i))) end
|
||||
mac = stdnse.strjoin(":", mac)
|
||||
|
||||
-- let's set a filter on PPPoE we can then check what packet is ours,
|
||||
|
||||
-- let's set a filter on PPPoE we can then check what packet is ours,
|
||||
-- based on the HOST_UNIQUE tag, if we need to
|
||||
self.socket:pcap_open(self.iface, 1500, false, "ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 and ether dst " .. mac)
|
||||
self.socket:pcap_open(self.iface, 1500, false, "ether[0x0c:2] == 0x8863 or ether[0x0c:2] == 0x8864 and ether dst " .. mac)
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a packet
|
||||
-- @param data class containing the request to send
|
||||
-- @return status true on success, false on failure
|
||||
@@ -682,12 +682,12 @@ Comm = {
|
||||
local eth_type = ( data.header.code == PPPoE.Code.SESSION_DATA ) and 0x8864 or 0x8863
|
||||
local ether = bin.pack(">AAS", self.dst_mac, self.src_mac, eth_type)
|
||||
local p = packet.Frame:new(ether .. tostring(data))
|
||||
|
||||
|
||||
local sock = nmap.new_dnet()
|
||||
if ( not(sock) ) then
|
||||
return false, "Failed to create raw socket"
|
||||
end
|
||||
|
||||
|
||||
local status = sock:ethernet_open(self.iface)
|
||||
-- we don't actually need to do this as the script simply crashes
|
||||
-- if we don't have the right permissions at this point
|
||||
@@ -700,9 +700,9 @@ Comm = {
|
||||
return false, "Failed to send data"
|
||||
end
|
||||
sock:ethernet_close()
|
||||
return true
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Receive a response from the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response class containing the response or
|
||||
@@ -714,10 +714,10 @@ Comm = {
|
||||
if ( not(status) ) then
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local header = PPPoE.Header.parse(l3)
|
||||
local p = packet.Frame:new(l2..l3)
|
||||
|
||||
|
||||
-- there's probably a more elegant way of doing this
|
||||
if ( EtherType.PPPOE_DISCOVERY == p.ether_type ) then
|
||||
if ( header.code == PPPoE.Code.PADO ) then
|
||||
@@ -736,19 +736,19 @@ Comm = {
|
||||
end
|
||||
return false, ("Received unsupported response, can't decode code (%d)"):format(header.code)
|
||||
end,
|
||||
|
||||
|
||||
-- Does an "exchange", ie, sends a request and waits for a response
|
||||
-- @param data class containing the request to send
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response class containing the response or
|
||||
-- err string on error
|
||||
exch = function(self, data)
|
||||
exch = function(self, data)
|
||||
local status, err = self:send(data)
|
||||
if ( not(status) ) then
|
||||
return false, err
|
||||
end
|
||||
local retries, resp = 3, nil
|
||||
|
||||
|
||||
repeat
|
||||
status, resp = self:recv()
|
||||
if ( data.header and 0 == data.header.session ) then
|
||||
@@ -758,15 +758,15 @@ Comm = {
|
||||
end
|
||||
retries = retries - 1
|
||||
until(retries == 0)
|
||||
|
||||
|
||||
return false, "Failed to retrieve proper PPPoE response"
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Helper class is the main script interface
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new instance of Helper
|
||||
-- @param iface string containing the name of the interface to use
|
||||
-- @return o new instance on success, nil on failure
|
||||
@@ -779,11 +779,11 @@ Helper = {
|
||||
}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
|
||||
if ( not(nmap.is_privileged()) ) then
|
||||
return nil, "The PPPoE library requires Nmap to be run in privileged mode"
|
||||
end
|
||||
|
||||
|
||||
-- get src_mac
|
||||
local info = nmap.get_interface_info(iface)
|
||||
if ( not(info) or not(info.mac) ) then
|
||||
@@ -792,14 +792,14 @@ Helper = {
|
||||
o.comm = Comm:new(iface, info.mac)
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Sets up the pcap socket for listening and does some other preparations
|
||||
-- @return status true on success, false on failure
|
||||
connect = function(self)
|
||||
return self.comm:connect()
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
-- Performs a PPPoE discovery initiation by sending a PADI request to the
|
||||
-- ethernet broadcast address
|
||||
-- @return status true on success, false on failure
|
||||
@@ -813,12 +813,12 @@ Helper = {
|
||||
end
|
||||
-- wait for a pado
|
||||
local pado, retries = nil, 3
|
||||
|
||||
|
||||
repeat
|
||||
status, pado = self.comm:recv()
|
||||
if ( not(status) ) then
|
||||
return status, pado
|
||||
end
|
||||
end
|
||||
retries = retries - 1
|
||||
until( pado.tags or retries == 0 )
|
||||
if ( not(pado.tags) ) then
|
||||
@@ -831,7 +831,7 @@ Helper = {
|
||||
pado_host_unique = tag.raw
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- store the tags for later use
|
||||
self.tags = pado.tags
|
||||
self.comm.dst_mac = pado.mac_srv
|
||||
@@ -846,13 +846,13 @@ Helper = {
|
||||
|
||||
return true, pado
|
||||
end,
|
||||
|
||||
|
||||
-- Performs a Discovery Request by sending PADR to the PPPoE ethernet
|
||||
-- address
|
||||
-- @return status true on success, false on failure
|
||||
-- @return pads instance of PADS on success
|
||||
discoverRequest = function(self)
|
||||
|
||||
|
||||
-- remove the AC-Name tag if there is one
|
||||
local function getTag(tag)
|
||||
for _, t in ipairs(self.tags) do
|
||||
@@ -861,20 +861,20 @@ Helper = {
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local taglist = {
|
||||
|
||||
local taglist = {
|
||||
PPPoE.TagType.SERVICE_NAME,
|
||||
PPPoE.TagType.HOST_UNIQUE,
|
||||
PPPoE.TagType.AC_COOKIE
|
||||
}
|
||||
|
||||
|
||||
local tags = {}
|
||||
for _, t in ipairs(taglist) do
|
||||
if ( getTag(t) ) then
|
||||
table.insert(tags, getTag(t))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local padr = PPPoE.PADR:new(tags)
|
||||
local status, pads = self.comm:exch(padr)
|
||||
|
||||
@@ -910,7 +910,7 @@ Helper = {
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
AuthMethod.byValue = function(value)
|
||||
for _, m in ipairs(AuthMethod.methods) do
|
||||
if ( m.value == value ) then
|
||||
@@ -918,49 +918,49 @@ Helper = {
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local auth_data = ( AuthMethod.byName(method) and AuthMethod.byName(method).value )
|
||||
if ( not(auth_data) ) then
|
||||
return false, ("Unsupported authentication mode (%s)"):format(method)
|
||||
end
|
||||
|
||||
|
||||
self.identifier = self.identifier + 1
|
||||
|
||||
|
||||
-- First do a Configuration Request
|
||||
local options = { LCP.ConfigOption:new(LCP.ConfigOption.MRU, 1492) }
|
||||
local lcp_req = LCP.ConfigRequest:new(self.identifier, options)
|
||||
local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req))
|
||||
local status, resp = self.comm:exch(sess_req)
|
||||
|
||||
|
||||
if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then
|
||||
return false, "Unexpected packet type was received"
|
||||
end
|
||||
|
||||
|
||||
-- Make sure we got a Configuration Request in return
|
||||
local lcp_header = LCP.Header.parse(resp.data)
|
||||
local lcp_header = LCP.Header.parse(resp.data)
|
||||
if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then
|
||||
return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code)
|
||||
end
|
||||
|
||||
|
||||
local config_req = LCP.ConfigRequest.parse(resp.data)
|
||||
if ( not(config_req.options) ) then
|
||||
return false, "Failed to retrieve any options from response"
|
||||
end
|
||||
|
||||
|
||||
local auth_proposed = config_req.options:getById(LCP.ConfigOption.AUTH_PROTO)
|
||||
|
||||
|
||||
if ( auth_proposed.raw ~= auth_data ) then
|
||||
local options = { LCP.ConfigOption:new(LCP.ConfigOption.AUTH_PROTO, nil, bin.pack("A", auth_data)) }
|
||||
local lcp_req = LCP.ConfigNak:new(self.identifier, options)
|
||||
local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req))
|
||||
local status, resp = self.comm:exch(sess_req)
|
||||
|
||||
|
||||
if ( not(status) or PPPoE.Code.SESSION_DATA ~= resp.header.code ) then
|
||||
return false, "Unexpected packet type was received"
|
||||
end
|
||||
|
||||
|
||||
-- Make sure we got a Configuration Request in return
|
||||
local lcp_header = LCP.Header.parse(resp.data)
|
||||
local lcp_header = LCP.Header.parse(resp.data)
|
||||
if ( LCP.Code.CONFIG_REQUEST ~= lcp_header.code ) then
|
||||
return false, ("Unexpected packet type was received (%d)"):format(lcp_header.code)
|
||||
end
|
||||
@@ -972,26 +972,26 @@ Helper = {
|
||||
-- The ACK is essential the Config Request, only with a different code
|
||||
-- Do a dirty attempt to just replace the code and send the request back as an ack
|
||||
self.identifier = self.identifier + 1
|
||||
|
||||
|
||||
local lcp_req = LCP.ConfigAck:new(config_req.header.identifier, config_req.options:getTable())
|
||||
local sess_req = PPPoE.SessionData:new(self.session, tostring(lcp_req))
|
||||
local status, resp = self.comm:send(sess_req)
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
return false, "Authentication method was not accepted"
|
||||
end
|
||||
|
||||
|
||||
return false, "Failed to negotiate authentication mechanism"
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a LCP Terminate Request and waits for an ACK
|
||||
-- Attempts to do so 10 times before aborting
|
||||
-- @return status true on success false on failure
|
||||
close = function(self)
|
||||
local tries = 10
|
||||
local tries = 10
|
||||
repeat
|
||||
if ( 0 == self.session ) then
|
||||
break
|
||||
@@ -1001,20 +1001,20 @@ Helper = {
|
||||
local status, resp = self.comm:exch(sess_req)
|
||||
if ( status and resp.header and resp.header.code ) then
|
||||
if ( PPPoE.Code.SESSION_DATA == resp.header.code ) then
|
||||
local lcp_header = LCP.Header.parse(resp.data)
|
||||
local lcp_header = LCP.Header.parse(resp.data)
|
||||
if ( LCP.Code.TERMINATE_ACK == lcp_header.code ) then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
tries = tries - 1
|
||||
until( tries == 0 )
|
||||
|
||||
until( tries == 0 )
|
||||
|
||||
self.comm:exch(PPPoE.PADT:new(self.session))
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -59,7 +59,7 @@ local function check(result, pattern)
|
||||
return s_code, s_pattern
|
||||
end
|
||||
|
||||
--- Performs a request to the web server and calls check to check if
|
||||
--- Performs a request to the web server and calls check to check if
|
||||
-- the response is a valid result
|
||||
--
|
||||
--@param socket The socket to send the request through
|
||||
@@ -151,7 +151,7 @@ end
|
||||
function return_args()
|
||||
local url = false
|
||||
local pattern = false
|
||||
if nmap.registry.args['proxy.url']
|
||||
if nmap.registry.args['proxy.url']
|
||||
then url = nmap.registry.args['proxy.url']
|
||||
elseif nmap.registry.args.proxy and nmap.registry.args.proxy.url
|
||||
then url = nmap.registry.args.proxy.url
|
||||
@@ -197,7 +197,7 @@ function socksHandshake(socket, version, hostname)
|
||||
end
|
||||
if version == 4 then
|
||||
paystring = '04 01 00 50 ' .. sip .. ' 6e 6d 61 70 00'
|
||||
payload = bin.pack("H",paystring)
|
||||
payload = bin.pack("H",paystring)
|
||||
try(socket:send(payload))
|
||||
local response = try(socket:receive())
|
||||
local request_status = string.byte(response, 2)
|
||||
@@ -205,11 +205,11 @@ function socksHandshake(socket, version, hostname)
|
||||
stdnse.print_debug("Socks4: Received \"Request Granted\" from proxy server\n")
|
||||
return socket
|
||||
end
|
||||
if(request_status == 0x5b) then
|
||||
if(request_status == 0x5b) then
|
||||
stdnse.print_debug("Socks4: Received \"Request rejected or failed\" from proxy server")
|
||||
elseif (request_status == 0x5c) then
|
||||
elseif (request_status == 0x5c) then
|
||||
stdnse.print_debug("Socks4: Received \"request failed because client is not running identd\" from proxy server")
|
||||
elseif (request_status == 0x5d) then
|
||||
elseif (request_status == 0x5d) then
|
||||
stdnse.print_debug("Socks4: Received \"request failed because client's identd could not confirm" ..
|
||||
"\nthe user ID string in the request from proxy server")
|
||||
end
|
||||
@@ -220,33 +220,33 @@ function socksHandshake(socket, version, hostname)
|
||||
try(socket:send(payload))
|
||||
local auth = try(socket:receive())
|
||||
local r2 = string.byte(auth,2)
|
||||
|
||||
|
||||
-- If Auth is required, proxy is closed, skip next test
|
||||
if(r2 ~= 0x00) then
|
||||
if(r2 ~= 0x00) then
|
||||
stdnse.print_debug("Socks5: Authentication required")
|
||||
else
|
||||
-- If no Auth is required, try to estabilish connection
|
||||
stdnse.print_debug("Socks5: No authentication required")
|
||||
-- Socks5 second payload: Version, Command, Null, Address type, Ip-Address, Port number
|
||||
-- Socks5 second payload: Version, Command, Null, Address type, Ip-Address, Port number
|
||||
paystring = '05 01 00 01 ' .. sip .. '00 50'
|
||||
payload = bin.pack("H",paystring)
|
||||
payload = bin.pack("H",paystring)
|
||||
try(socket:send(payload))
|
||||
local z = try(socket:receive())
|
||||
local z = try(socket:receive())
|
||||
local request_status = string.byte(z, 2)
|
||||
if (request_status == 0x00) then
|
||||
stdnse.print_debug("Socks5: Received \"Request Granted\" from proxy server\n")
|
||||
return socket
|
||||
elseif(request_status == 0x01) then
|
||||
elseif(request_status == 0x01) then
|
||||
stdnse.print_debug("Socks5: Received \"General failure\" from proxy server")
|
||||
elseif (request_status == 0x02) then
|
||||
elseif (request_status == 0x02) then
|
||||
stdnse.print_debug("Socks5: Received \"Connection not allowed by ruleset\" from proxy server")
|
||||
elseif (request_status == 0x03) then
|
||||
elseif (request_status == 0x03) then
|
||||
stdnse.print_debug("Socks5: Received \"Network unreachable\" from proxy server")
|
||||
elseif (request_status == 0x04) then
|
||||
elseif (request_status == 0x04) then
|
||||
stdnse.print_debug("Socks5: Received \"Host unreachable\" from proxy server")
|
||||
elseif (request_status == 0x05) then
|
||||
elseif (request_status == 0x05) then
|
||||
stdnse.print_debug("Socks5: Received \"Connection refused by destination host\" from proxy server")
|
||||
elseif (request_status == 0x06) then
|
||||
elseif (request_status == 0x06) then
|
||||
stdnse.print_debug("Socks5: Received \"TTL Expired\" from proxy server")
|
||||
elseif (request_status == 0x07) then
|
||||
stdnse.print_debug("Socks5: Received \"command not supported / protocol error\" from proxy server")
|
||||
@@ -255,7 +255,7 @@ function socksHandshake(socket, version, hostname)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
end
|
||||
stdnse.print_debug("Unrecognized proxy type");
|
||||
return false
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
-- A minimal RDP (Remote Desktop Protocol) library. Currently has functionality to determine encryption
|
||||
-- and cipher support.
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
@@ -13,7 +13,7 @@ local stdnse = require("stdnse")
|
||||
_ENV = stdnse.module("rdp", stdnse.seeall)
|
||||
|
||||
Packet = {
|
||||
|
||||
|
||||
TPKT = {
|
||||
|
||||
new = function(self, data)
|
||||
@@ -31,7 +31,7 @@ Packet = {
|
||||
self.data
|
||||
)
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local tpkt = Packet.TPKT:new()
|
||||
local pos
|
||||
@@ -41,66 +41,66 @@ Packet = {
|
||||
return tpkt
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
ITUT = {
|
||||
|
||||
|
||||
new = function(self, code, data)
|
||||
local o = { data = tostring(data), code = code }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local itut = Packet.ITUT:new()
|
||||
local pos
|
||||
|
||||
pos, itut.length, itut.code = bin.unpack("CC", data)
|
||||
|
||||
|
||||
if ( itut.code == 0xF0 ) then
|
||||
pos, itut.eot = bin.unpack("C", data, pos)
|
||||
elseif ( itut.code == 0xD0 ) then
|
||||
pos, itut.dstref, itut.srcref, itut.class = bin.unpack(">SSC", data, pos)
|
||||
end
|
||||
|
||||
|
||||
pos, itut.data = bin.unpack("A" .. (#data - pos), data, pos)
|
||||
return itut
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
local len = (self.code ~= 0xF0 and #self.data + 1 or 2)
|
||||
local data = bin.pack("CC",
|
||||
len,
|
||||
self.code or 0
|
||||
)
|
||||
|
||||
|
||||
if ( self.code == 0xF0 ) then
|
||||
data = data .. bin.pack("C", 0x80) -- EOT
|
||||
end
|
||||
|
||||
|
||||
return data .. self.data
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
Request = {
|
||||
|
||||
|
||||
ConnectionRequest = {
|
||||
|
||||
|
||||
new = function(self, proto)
|
||||
local o = { proto = proto }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
local cookie = "mstshash=nmap"
|
||||
local itpkt_len = 21 + #cookie
|
||||
local itut_len = 16 + #cookie
|
||||
|
||||
|
||||
local data = bin.pack(">SSCA",
|
||||
0x0000, -- dst reference
|
||||
0x0000, -- src reference
|
||||
@@ -116,18 +116,18 @@ Request = {
|
||||
return tostring(Packet.TPKT:new(Packet.ITUT:new(0xE0, data)))
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
MCSConnectInitial = {
|
||||
|
||||
|
||||
new = function(self, cipher)
|
||||
local o = { cipher = cipher }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
|
||||
|
||||
local data = bin.pack("<HIH",
|
||||
"7f 65" .. -- BER: Application-Defined Type = APPLICATION 101,
|
||||
"82 01 90" .. -- BER: Type Length = 404 bytes
|
||||
@@ -164,7 +164,7 @@ Request = {
|
||||
"04 82 01 2f" .. -- Connect-Initial::userData (307 bytes)
|
||||
"00 05" .. -- object length = 5 bytes
|
||||
"00 14 7c 00 01" .. -- object
|
||||
"81 26" .. -- ConnectData::connectPDU length = 298 bytes
|
||||
"81 26" .. -- ConnectData::connectPDU length = 298 bytes
|
||||
"00 08 00 10 00 01 c0 00 44 75 63 61 81 18" .. -- PER encoded (ALIGNED variant of BASIC-PER) GCC Conference Create Request PDU
|
||||
"01 c0 d4 00" .. -- TS_UD_HEADER::type = CS_CORE (0xc001), length = 216 bytes
|
||||
"04 00 08 00" .. -- TS_UD_CS_CORE::version = 0x0008004
|
||||
@@ -173,12 +173,12 @@ Request = {
|
||||
"01 ca" .. -- TS_UD_CS_CORE::colorDepth = RNS_UD_COLOR_8BPP (0xca01)
|
||||
"03 aa" .. -- TS_UD_CS_CORE::SASSequence
|
||||
"09 08 00 00" .. -- TS_UD_CS_CORE::keyboardLayout = 0x409 = 1033 = English (US)
|
||||
"28 0a 00 00" .. -- TS_UD_CS_CORE::clientBuild = 3790
|
||||
"28 0a 00 00" .. -- TS_UD_CS_CORE::clientBuild = 3790
|
||||
"45 00 4d 00 50 00 2d 00 4c 00 41 00 50 00 2d 00 30 00 30 00 31 00 34 00 00 00 00 00 00 00 00 00" .. -- TS_UD_CS_CORE::clientName = ELTONS-TEST2
|
||||
"04 00 00 00" .. -- TS_UD_CS_CORE::keyboardType
|
||||
"00 00 00 00" .. -- TS_UD_CS_CORE::keyboardSubtype
|
||||
"0c 00 00 00" .. -- TS_UD_CS_CORE::keyboardFunctionKey
|
||||
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " ..
|
||||
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " ..
|
||||
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " ..
|
||||
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " ..
|
||||
"00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 " .. -- TS_UD_CS_CORE::imeFileName = ""
|
||||
@@ -208,40 +208,40 @@ Request = {
|
||||
"00 00 80 80" .. -- CHANNEL_DEF::options = 0x80800000
|
||||
"63 6c 69 70 72 64 72 00" .. -- CHANNEL_DEF::name = "cliprdr"
|
||||
"00 00 a0 c0" .. -- CHANNEL_DEF::options = 0xc0a00000
|
||||
"72 64 70 73 6e 64 00 00" .. -- CHANNEL_DEF::name = "rdpsnd"
|
||||
"72 64 70 73 6e 64 00 00" .. -- CHANNEL_DEF::name = "rdpsnd"
|
||||
"00 00 00 c0" -- CHANNEL_DEF::options = 0xc0000000
|
||||
)
|
||||
return tostring(Packet.TPKT:new(Packet.ITUT:new(0xF0, data)))
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
ConnectionConfirm = {
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = { }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local cc = Response.ConnectionConfirm:new()
|
||||
local pos, _
|
||||
|
||||
|
||||
cc.tpkt = Packet.TPKT.parse(data)
|
||||
cc.itut = Packet.ITUT.parse(cc.tpkt.data)
|
||||
cc.itut = Packet.ITUT.parse(cc.tpkt.data)
|
||||
return cc
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
MCSConnectResponse = {
|
||||
new = function(self)
|
||||
local o = { }
|
||||
@@ -249,20 +249,20 @@ Response = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local cr = Response.MCSConnectResponse:new()
|
||||
|
||||
|
||||
cr.tpkt = Packet.TPKT.parse(data)
|
||||
cr.itut = Packet.ITUT.parse(cr.tpkt.data)
|
||||
return cr
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Comm = {
|
||||
|
||||
|
||||
-- Creates a new Comm instance
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
@@ -273,7 +273,7 @@ Comm = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connect to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing error message, if status is false
|
||||
@@ -285,13 +285,13 @@ Comm = {
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Close the connection to the server
|
||||
-- @return status true on success, false on failure
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a message to the server
|
||||
-- @param pkt an instance of Request.*
|
||||
-- @return status true on success, false on failure
|
||||
@@ -306,7 +306,7 @@ Comm = {
|
||||
recv = function(self)
|
||||
return self.socket:receive()
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a message to the server and receives the response
|
||||
-- @param pkt an instance of Request.*
|
||||
-- @return status true on success, false on failure
|
||||
@@ -330,7 +330,7 @@ Comm = {
|
||||
return true, Response.ConnectionConfirm.parse(data)
|
||||
elseif ( itut_code == 0xF0 ) then
|
||||
return true, Response.MCSConnectResponse.parse(data)
|
||||
end
|
||||
end
|
||||
return false, "Received unhandled packet"
|
||||
end,
|
||||
}
|
||||
|
||||
@@ -16,23 +16,23 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
local output = ("*%s\r\n$%d\r\n%s\r\n"):format(#self.args + 1, #self.cmd, self.cmd)
|
||||
|
||||
|
||||
for _, arg in ipairs(self.args) do
|
||||
arg = tostring(arg)
|
||||
output = output .. ("$%s\r\n%s\r\n"):format(#arg, arg)
|
||||
end
|
||||
|
||||
|
||||
return output
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
Type = {
|
||||
STATUS = 0,
|
||||
ERROR = 1,
|
||||
@@ -40,21 +40,21 @@ Response = {
|
||||
BULK = 3,
|
||||
MULTIBULK = 4,
|
||||
},
|
||||
|
||||
|
||||
new = function(self, socket)
|
||||
local o = { socket = socket }
|
||||
setmetatable (o,self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
receive = function(self)
|
||||
local status, data = self.socket:receive_buf("\r\n", false)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
|
||||
-- if we have a status, integer or error message
|
||||
-- if we have a status, integer or error message
|
||||
if ( data:match("^[%-%+%:]") ) then
|
||||
local response = { data = data }
|
||||
local t = data:match("^([-+:])")
|
||||
@@ -65,32 +65,32 @@ Response = {
|
||||
elseif ( t == ":" ) then
|
||||
response.type = Response.Type.INTEGER
|
||||
end
|
||||
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
|
||||
-- process bulk reply
|
||||
if ( data:match("^%$") ) then
|
||||
-- non existing key
|
||||
if ( data == "$-1" ) then
|
||||
return true, nil
|
||||
end
|
||||
|
||||
|
||||
local len = tonumber(data:match("^%$(%d*)"))
|
||||
-- we should only have a single line, so we can just peel of the length
|
||||
-- we should only have a single line, so we can just peel of the length
|
||||
status, data = self.socket:receive_buf(match.numbytes(len), false)
|
||||
if( not(status) ) then
|
||||
return false, "Failed to receive data from server"
|
||||
end
|
||||
|
||||
|
||||
return true, { data = data, type = Response.Type.BULK }
|
||||
end
|
||||
|
||||
|
||||
-- process multi-bulk reply
|
||||
if ( data:match("^%*%d*") ) then
|
||||
local count = data:match("^%*(%d*)")
|
||||
local results = {}
|
||||
|
||||
|
||||
for i=1, count do
|
||||
-- peel of the length
|
||||
local status = self.socket:receive_buf("\r\n", false)
|
||||
@@ -106,28 +106,28 @@ Response = {
|
||||
end
|
||||
return true, { data = results, type = Response.Type.MULTIBULK }
|
||||
end
|
||||
|
||||
|
||||
return false, "Unsupported response"
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = { host = host, port = port }
|
||||
setmetatable (o,self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
connect = function(self)
|
||||
self.socket = nmap.new_socket()
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
reqCmd = function(self, cmd, ...)
|
||||
local req = Request:new(cmd, ...)
|
||||
local status, err = self.socket:send(tostring(req))
|
||||
@@ -136,11 +136,11 @@ Helper = {
|
||||
end
|
||||
return Response:new(self.socket):receive()
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
480
nselib/rmi.lua
480
nselib/rmi.lua
File diff suppressed because it is too large
Load Diff
168
nselib/rpc.lua
168
nselib/rpc.lua
@@ -87,11 +87,11 @@ _ENV = stdnse.module("rpc", stdnse.seeall)
|
||||
|
||||
-- Version 0.3
|
||||
--
|
||||
-- Created 01/24/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Created 01/24/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||
-- Revised 02/22/2010 - v0.2 - cleanup, revised the way TCP/UDP are handled fo
|
||||
-- encoding an decoding
|
||||
-- Revised 03/13/2010 - v0.3 - re-worked library to be OO
|
||||
-- Revised 04/18/2010 - v0.4 - Applied patch from Djalal Harouni with improved
|
||||
-- Revised 04/18/2010 - v0.4 - Applied patch from Djalal Harouni with improved
|
||||
-- error checking and re-designed Comm class. see:
|
||||
-- http://seclists.org/nmap-dev/2010/q2/232
|
||||
-- Revised 06/02/2010 - v0.5 - added code to the Util class to check for file
|
||||
@@ -110,7 +110,7 @@ RPC_args = {
|
||||
|
||||
-- Defines the order in which to try to connect to the RPC programs
|
||||
-- TCP appears to be more stable than UDP in most cases, so try it first
|
||||
local RPC_PROTOCOLS = (nmap.registry.args and nmap.registry.args[RPC_args['rpcbind'].proto] and
|
||||
local RPC_PROTOCOLS = (nmap.registry.args and nmap.registry.args[RPC_args['rpcbind'].proto] and
|
||||
type(nmap.registry.args[RPC_args['rpcbind'].proto]) == 'table') and
|
||||
nmap.registry.args[RPC_args['rpcbind'].proto] or { "tcp", "udp" }
|
||||
|
||||
@@ -169,7 +169,7 @@ Comm = {
|
||||
local resvport = math.random(1, 1024)
|
||||
socket = nmap.new_socket()
|
||||
status, err = socket:bind(nil, resvport)
|
||||
if status then
|
||||
if status then
|
||||
status, err = socket:connect(host, port)
|
||||
if status or err == "TIMEOUT" then break end
|
||||
socket:close()
|
||||
@@ -255,7 +255,7 @@ Comm = {
|
||||
-- @return status boolean true
|
||||
SetVersion = function(self, version)
|
||||
if self.checkprogver then
|
||||
if (RPC_version[self.program] and RPC_args[self.program] and
|
||||
if (RPC_version[self.program] and RPC_args[self.program] and
|
||||
nmap.registry.args and nmap.registry.args[RPC_args[self.program].ver]) then
|
||||
self.version = tonumber(nmap.registry.args[RPC_args[self.program].ver])
|
||||
elseif (not(self.version) and version) then
|
||||
@@ -271,7 +271,7 @@ Comm = {
|
||||
-- before trying to connecting.
|
||||
-- @param check boolean to enable or disable checking of program and version support.
|
||||
SetCheckProgVer = function(self, check)
|
||||
self.checkprogver = check
|
||||
self.checkprogver = check
|
||||
end,
|
||||
|
||||
--- Sets the RPC program ID to use.
|
||||
@@ -379,7 +379,7 @@ Comm = {
|
||||
end
|
||||
|
||||
pos, header.verifier.flavor = bin.unpack(">I", data, pos)
|
||||
pos, header.verifier.length = bin.unpack(">I", data, pos)
|
||||
pos, header.verifier.length = bin.unpack(">I", data, pos)
|
||||
|
||||
if header.verifier.length - 8 > 0 then
|
||||
status, data = self:GetAdditionalBytes( data, pos, header.verifier.length - 8 )
|
||||
@@ -407,7 +407,7 @@ Comm = {
|
||||
-- as the packet contains no length field. It's up to each decoding function
|
||||
-- to do appropriate checks
|
||||
return self.socket:receive_bytes(1)
|
||||
else
|
||||
else
|
||||
local tmp, lastfragment, length
|
||||
local data, pos = "", 1
|
||||
|
||||
@@ -439,7 +439,7 @@ Comm = {
|
||||
-- When multiple packets are received they look like this
|
||||
-- H = Header data
|
||||
-- D = Data
|
||||
--
|
||||
--
|
||||
-- We don't want the Header
|
||||
--
|
||||
-- HHHHDDDDDDDDDDDDDDHHHHDDDDDDDDDDD
|
||||
@@ -448,7 +448,7 @@ Comm = {
|
||||
--
|
||||
-- eg. we want
|
||||
-- data:sub(5, 18) and data:sub(22)
|
||||
--
|
||||
--
|
||||
|
||||
local bufcopy = data:sub(pos)
|
||||
|
||||
@@ -483,17 +483,17 @@ Comm = {
|
||||
if ( not(status) ) then
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
packet = packet .. ( data or "" )
|
||||
if ( self.proto == "udp") then
|
||||
return packet
|
||||
else
|
||||
-- set the high bit as this is our last fragment
|
||||
len = 0x80000000 + packet:len()
|
||||
return bin.pack(">I", len) .. packet
|
||||
return bin.pack(">I", len) .. packet
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
SendPacket = function( self, packet )
|
||||
if ( self.host and self.port ) then
|
||||
return self.socket:sendto(self.host, self.port, packet)
|
||||
@@ -509,11 +509,11 @@ Comm = {
|
||||
}
|
||||
|
||||
--- Portmap (rpcbind) class
|
||||
Portmap =
|
||||
Portmap =
|
||||
{
|
||||
PROTOCOLS = {
|
||||
['tcp'] = 6,
|
||||
['udp'] = 17,
|
||||
PROTOCOLS = {
|
||||
['tcp'] = 6,
|
||||
['udp'] = 17,
|
||||
},
|
||||
|
||||
-- TODO: add more Authentication Protocols
|
||||
@@ -556,7 +556,7 @@ Portmap =
|
||||
|
||||
Procedure =
|
||||
{
|
||||
[2] =
|
||||
[2] =
|
||||
{
|
||||
GETPORT = 3,
|
||||
DUMP = 4,
|
||||
@@ -564,13 +564,13 @@ Portmap =
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
|
||||
State =
|
||||
{
|
||||
MSG_ACCEPTED = 0,
|
||||
MSG_DENIED = 1,
|
||||
},
|
||||
|
||||
|
||||
AcceptState =
|
||||
{
|
||||
SUCCESS = 0,
|
||||
@@ -594,7 +594,7 @@ Portmap =
|
||||
RejectState =
|
||||
{
|
||||
RPC_MISMATCH = 0,
|
||||
AUTH_ERROR = 1,
|
||||
AUTH_ERROR = 1,
|
||||
},
|
||||
|
||||
RejectMsg =
|
||||
@@ -686,7 +686,7 @@ Portmap =
|
||||
if ( vfollows == 0 ) then
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
pos, program, version, protocol, port = bin.unpack(">IIII", data, pos)
|
||||
if ( protocol == Portmap.PROTOCOLS.tcp ) then
|
||||
protocol = "tcp"
|
||||
@@ -721,15 +721,15 @@ Portmap =
|
||||
if ( not( Portmap.PROTOCOLS[protocol] ) ) then
|
||||
return false, ("Portmap.Callit: Protocol %s not supported"):format(protocol)
|
||||
end
|
||||
|
||||
|
||||
if ( Util.ProgNameToNumber(program) == nil ) then
|
||||
return false, ("Portmap.Callit: Unknown program name: %s"):format(program)
|
||||
end
|
||||
|
||||
|
||||
local data = bin.pack(">IIII", Util.ProgNameToNumber(program), version, 0, 0 )
|
||||
local packet = comm:EncodePacket(nil, Portmap.Procedure[comm.version].CALLIT,
|
||||
{ type=Portmap.AuthType.NULL }, data )
|
||||
|
||||
|
||||
if (not(comm:SendPacket(packet))) then
|
||||
return false, "Portmap.Callit: Failed to send data"
|
||||
end
|
||||
@@ -753,7 +753,7 @@ Portmap =
|
||||
end,
|
||||
|
||||
|
||||
--- Queries the portmapper for the port of the selected program,
|
||||
--- Queries the portmapper for the port of the selected program,
|
||||
-- protocol and version
|
||||
--
|
||||
-- @param comm object handles rpc program information and
|
||||
@@ -765,20 +765,20 @@ Portmap =
|
||||
GetPort = function( self, comm, program, protocol, version )
|
||||
local status, data, response, header, pos, packet
|
||||
local xid
|
||||
|
||||
|
||||
if ( not( Portmap.PROTOCOLS[protocol] ) ) then
|
||||
return false, ("Portmap.GetPort: Protocol %s not supported"):format(protocol)
|
||||
end
|
||||
|
||||
|
||||
if ( Util.ProgNameToNumber(program) == nil ) then
|
||||
return false, ("Portmap.GetPort: Unknown program name: %s"):format(program)
|
||||
end
|
||||
|
||||
|
||||
data = bin.pack(">I>I>I>I", Util.ProgNameToNumber(program), version,
|
||||
Portmap.PROTOCOLS[protocol], 0 )
|
||||
packet = comm:EncodePacket(xid, Portmap.Procedure[comm.version].GETPORT,
|
||||
{ type=Portmap.AuthType.NULL }, data )
|
||||
|
||||
|
||||
if (not(comm:SendPacket(packet))) then
|
||||
return false, "Portmap.GetPort: Failed to send data"
|
||||
end
|
||||
@@ -790,7 +790,7 @@ Portmap =
|
||||
end
|
||||
|
||||
pos, header = comm:DecodeHeader( data, 1 )
|
||||
|
||||
|
||||
if ( not(header) ) then
|
||||
return false, "Portmap.GetPort: Failed to decode RPC header"
|
||||
end
|
||||
@@ -862,7 +862,7 @@ Mount = {
|
||||
MNTERR_SERVERFAULT = 10006,
|
||||
},
|
||||
|
||||
Procedure =
|
||||
Procedure =
|
||||
{
|
||||
MOUNT = 1,
|
||||
DUMP = 2,
|
||||
@@ -991,7 +991,7 @@ Mount = {
|
||||
|
||||
-- decode groups
|
||||
while true do
|
||||
local group
|
||||
local group
|
||||
|
||||
status, data = comm:GetAdditionalBytes( data, pos, 4 )
|
||||
if (not(status)) then
|
||||
@@ -1288,7 +1288,7 @@ NFS = {
|
||||
},
|
||||
|
||||
-- Unfortunately the NFS procedure numbers differ in between versions
|
||||
Procedure =
|
||||
Procedure =
|
||||
{
|
||||
-- NFS Version 1
|
||||
[1] =
|
||||
@@ -1302,7 +1302,7 @@ NFS = {
|
||||
},
|
||||
|
||||
-- NFS Version 2
|
||||
[2] =
|
||||
[2] =
|
||||
{
|
||||
GETATTR = 1,
|
||||
ROOT = 3,
|
||||
@@ -1313,7 +1313,7 @@ NFS = {
|
||||
},
|
||||
|
||||
-- NFS Version 3
|
||||
[3] =
|
||||
[3] =
|
||||
{
|
||||
GETATTR = 1,
|
||||
SETATTR = 2,
|
||||
@@ -1365,7 +1365,7 @@ NFS = {
|
||||
if (status ~= NFS.StatCode[version].NFS_OK) then
|
||||
if (NFS.StatMsg[status]) then
|
||||
stdnse.print_debug(4,
|
||||
string.format("%s failed: %s", procedurename, NFS.StatMsg[status]))
|
||||
string.format("%s failed: %s", procedurename, NFS.StatMsg[status]))
|
||||
else
|
||||
stdnse.print_debug(4,
|
||||
string.format("%s failed: code %d", procedurename, status))
|
||||
@@ -1440,7 +1440,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
pos, status = Util.unmarshall_uint32(data, pos)
|
||||
if (not self:CheckStat("READDIR", comm.version, status)) then
|
||||
return -1, nil
|
||||
@@ -1483,7 +1483,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
pos, value_follows = Util.unmarshall_uint32(data, pos)
|
||||
if ( value_follows == 0 ) then
|
||||
break
|
||||
@@ -1510,7 +1510,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.ReadDirDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
pos, entry.length = Util.unmarshall_uint32(data, pos)
|
||||
status, data = comm:GetAdditionalBytes( data, pos, entry.length )
|
||||
if (not(status)) then
|
||||
@@ -1597,7 +1597,7 @@ NFS = {
|
||||
if (not self:CheckStat("LOOKUP", comm.version, status)) then
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
if (comm.version == 3) then
|
||||
status, data = comm:GetAdditionalBytes( data, pos, 4)
|
||||
if (not(status)) then
|
||||
@@ -1718,7 +1718,7 @@ NFS = {
|
||||
if (not self:CheckStat("READDIRPLUS", comm.version, status)) then
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
status, data = comm:GetAdditionalBytes(data, pos, 4)
|
||||
if not status then
|
||||
stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
|
||||
@@ -1755,7 +1755,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
pos, value_follows = bin.unpack(">I", data, pos)
|
||||
|
||||
if (value_follows == 0) then
|
||||
@@ -1781,7 +1781,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
pos, entry.name = Util.unmarshall_vopaque(entry.length, data, pos)
|
||||
status, data = comm:GetAdditionalBytes(data, pos, 8)
|
||||
if not status then
|
||||
@@ -1823,7 +1823,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.ReadDirPlusDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
_, len = bin.unpack(">I", data, pos)
|
||||
status, data = comm:GetAdditionalBytes(data, pos, len + 4)
|
||||
if not status then
|
||||
@@ -1834,7 +1834,7 @@ NFS = {
|
||||
else
|
||||
stdnse.print_debug(4, "NFS.ReadDirPlusDecode: %s handle follow failed",
|
||||
entry.name)
|
||||
end
|
||||
end
|
||||
table.insert(response.entries, entry)
|
||||
end
|
||||
|
||||
@@ -1854,7 +1854,7 @@ NFS = {
|
||||
|
||||
if not file_handle then
|
||||
return false, "ReadDirPlus: No filehandle received"
|
||||
end
|
||||
end
|
||||
|
||||
data = bin.pack("A>L>L>I>I", file_handle, cookie,
|
||||
opaque_data, dircount, maxcount)
|
||||
@@ -1994,7 +1994,7 @@ NFS = {
|
||||
stdnse.print_debug(4, "NFS.FsStatDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
|
||||
|
||||
pos, fsinfo.rtmax, fsinfo.rtpref, fsinfo.rtmult,
|
||||
fsinfo.wtmax, fsinfo.wtpref, fsinfo.wtmult,
|
||||
fsinfo.dtpref = Util.unmarshall_uint32(data, pos, 7)
|
||||
@@ -2207,7 +2207,7 @@ NFS = {
|
||||
-- low-level packet manipulation
|
||||
-- @param file_handle string containing the filehandle to query
|
||||
-- @return status true on success, false on failure
|
||||
-- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
|
||||
-- @return errormsg if status is false
|
||||
StatFs = function( self, comm, file_handle )
|
||||
@@ -2256,7 +2256,7 @@ NFS = {
|
||||
-- @param data string containing the full statfs reply
|
||||
-- @param pos number pointing to the statfs section of the reply
|
||||
-- @return pos number containing the offset after decoding
|
||||
-- @return statfs table with the following fields: <code>type</code>, <code>mode</code>,
|
||||
-- @return statfs table with the following fields: <code>type</code>, <code>mode</code>,
|
||||
-- <code>nlink</code>, <code>uid</code>, <code>gid</code>, <code>size</code>,
|
||||
-- <code>blocksize</code>, <code>rdev</code>, <code>blocks</code>, <code>fsid</code>,
|
||||
-- <code>fileid</code>, <code>atime</code>, <code>mtime</code> and <code>ctime</code>
|
||||
@@ -2296,7 +2296,7 @@ NFS = {
|
||||
-- low-level packet manipulation
|
||||
-- @param file_handle string containing the filehandle to query
|
||||
-- @return status true on success, false on failure
|
||||
-- @return attribs table with the fields <code>type</code>, <code>mode</code>,
|
||||
-- @return attribs table with the fields <code>type</code>, <code>mode</code>,
|
||||
-- <code>nlink</code>, <code>uid</code>, <code>gid</code>, <code>size</code>,
|
||||
-- <code>blocksize</code>, <code>rdev</code>, <code>blocks</code>, <code>fsid</code>,
|
||||
-- <code>fileid</code>, <code>atime</code>, <code>mtime</code> and <code>ctime</code>
|
||||
@@ -2335,7 +2335,7 @@ NFS = {
|
||||
-- @param data string containing the full statfs reply
|
||||
-- @param pos number pointing to the statfs section of the reply
|
||||
-- @return pos number containing the offset after decoding
|
||||
-- @return statfs table with the following fields: <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- @return statfs table with the following fields: <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
|
||||
StatFsDecode = function( self, comm, data, pos )
|
||||
local status
|
||||
@@ -2357,8 +2357,8 @@ NFS = {
|
||||
stdnse.print_debug(4, "StatFsDecode: Failed to call GetAdditionalBytes")
|
||||
return -1, nil
|
||||
end
|
||||
pos, statfs.transfer_size, statfs.block_size,
|
||||
statfs.total_blocks, statfs.free_blocks,
|
||||
pos, statfs.transfer_size, statfs.block_size,
|
||||
statfs.total_blocks, statfs.free_blocks,
|
||||
statfs.available_blocks = Util.unmarshall_uint32(data, pos, 5)
|
||||
return pos, statfs
|
||||
end,
|
||||
@@ -2375,7 +2375,7 @@ Helper = {
|
||||
-- @return result table of string entries or error message on failure
|
||||
ShowMounts = function( host, port )
|
||||
|
||||
local status, result, mounts
|
||||
local status, result, mounts
|
||||
local mountd, mnt_comm
|
||||
local mnt = Mount:new()
|
||||
local portmap = Portmap:new()
|
||||
@@ -2520,7 +2520,7 @@ Helper = {
|
||||
-- @param port table
|
||||
-- @param path string containing the nfs export path
|
||||
-- @return status true on success, false on failure
|
||||
-- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
|
||||
ExportStats = function( host, port, path )
|
||||
local fhandle
|
||||
@@ -2528,7 +2528,7 @@ Helper = {
|
||||
local mnt_comm, nfs_comm
|
||||
local mountd, nfsd = {}, {}
|
||||
local mnt, nfs = Mount:new(), NFS:new()
|
||||
|
||||
|
||||
status, mountd = Helper.GetProgramInfo( host, port, "mountd", 2)
|
||||
if ( not(status) ) then
|
||||
stdnse.print_debug(4, "rpc.Helper.ExportStats: GetProgramInfo failed")
|
||||
@@ -2576,7 +2576,7 @@ Helper = {
|
||||
stdnse.print_debug(4, "rpc.Helper.ExportStats: %s", stats)
|
||||
return status, stats
|
||||
end
|
||||
|
||||
|
||||
status, fhandle = mnt:Unmount(mnt_comm, path)
|
||||
mnt_comm:Disconnect()
|
||||
nfs_comm:Disconnect()
|
||||
@@ -2651,7 +2651,7 @@ Helper = {
|
||||
stdnse.print_debug(4, "rpc.Helper.Dir: %s", dirs)
|
||||
return status, dirs
|
||||
end
|
||||
|
||||
|
||||
status, fhandle = mnt:Unmount(mnt_comm, path)
|
||||
mnt_comm:Disconnect()
|
||||
nfs_comm:Disconnect()
|
||||
@@ -2668,7 +2668,7 @@ Helper = {
|
||||
-- @param port table
|
||||
-- @param path string containing the nfs export path
|
||||
-- @return status true on success, false on failure
|
||||
-- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- @return statfs table with the fields <code>transfer_size</code>, <code>block_size</code>,
|
||||
-- <code>total_blocks</code>, <code>free_blocks</code> and <code>available_blocks</code>
|
||||
GetAttributes = function( host, port, path )
|
||||
local fhandle
|
||||
@@ -2688,7 +2688,7 @@ Helper = {
|
||||
stdnse.print_debug(4, "rpc.Helper.GetAttributes: GetProgramInfo failed")
|
||||
return status, "rpc.Helper.GetAttributes: GetProgramInfo failed"
|
||||
end
|
||||
|
||||
|
||||
mnt_comm, result = Comm:new('mountd', mountd.version)
|
||||
nfs_comm, result = Comm:new('nfs', nfsd.version)
|
||||
|
||||
@@ -2730,7 +2730,7 @@ Helper = {
|
||||
end
|
||||
|
||||
status, fhandle = mnt:Unmount(mnt_comm, path)
|
||||
|
||||
|
||||
mnt_comm:Disconnect()
|
||||
nfs_comm:Disconnect()
|
||||
if ( not(status) ) then
|
||||
@@ -2740,13 +2740,13 @@ Helper = {
|
||||
|
||||
return true, attribs
|
||||
end,
|
||||
|
||||
|
||||
--- Queries the portmapper for a list of programs
|
||||
--
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
-- @return status true on success, false on failure
|
||||
-- @return table containing the portmapper information as returned by
|
||||
-- @return table containing the portmapper information as returned by
|
||||
-- <code>Portmap.Dump</code>
|
||||
RpcInfo = function( host, port )
|
||||
local status, result
|
||||
@@ -2754,7 +2754,7 @@ Helper = {
|
||||
local comm = Comm:new('rpcbind', 2)
|
||||
|
||||
mutex "lock"
|
||||
|
||||
|
||||
if nmap.registry[host.ip] == nil then
|
||||
nmap.registry[host.ip] = {}
|
||||
end
|
||||
@@ -2790,13 +2790,13 @@ Helper = {
|
||||
-- @param program string containing the RPC program name
|
||||
-- @param protocol string containing either "tcp" or "udp"
|
||||
-- @return status true on success, false on failure
|
||||
-- @return table containing the portmapper information as returned by
|
||||
-- @return table containing the portmapper information as returned by
|
||||
-- <code>Portmap.Dump</code>
|
||||
GetPortForProgram = function( host, port, program, protocol )
|
||||
local status, result
|
||||
local portmap = Portmap:new()
|
||||
local comm = Comm:new('rpcbind', 2)
|
||||
|
||||
|
||||
status, result = comm:Connect(host, port)
|
||||
if (not(status)) then
|
||||
stdnse.print_debug(4, "rpc.Helper.GetPortForProgram: %s", result)
|
||||
@@ -2808,10 +2808,10 @@ Helper = {
|
||||
if (not(status)) then
|
||||
stdnse.print_debug(4, "rpc.Helper.GetPortForProgram: %s", result)
|
||||
end
|
||||
|
||||
|
||||
return status, result
|
||||
end,
|
||||
|
||||
|
||||
--- Get RPC program information
|
||||
--
|
||||
-- @param host table
|
||||
@@ -2882,7 +2882,7 @@ Util =
|
||||
-- S_IWUSR
|
||||
[0x00000080] = { idx = 2, char = "w" },
|
||||
-- S_IXUSR
|
||||
[0x00000040] = { idx = 3, char = "x" },
|
||||
[0x00000040] = { idx = 3, char = "x" },
|
||||
-- S_ISUID
|
||||
[0x00000800] = { idx = 3, char = "S" },
|
||||
},
|
||||
@@ -3238,7 +3238,7 @@ Util =
|
||||
end
|
||||
return string.format("%.1f%s", size, unit[idx])
|
||||
end,
|
||||
|
||||
|
||||
format_access = function(mask, version)
|
||||
local ret, nfsobj = "", NFS:new()
|
||||
|
||||
@@ -3285,7 +3285,7 @@ Util =
|
||||
--
|
||||
-- @param pconf table returned by the NFSv3 PATHCONF call
|
||||
-- @param nfsversion the version of the remote NFS server
|
||||
-- @return fs table that contains the remote filesystem
|
||||
-- @return fs table that contains the remote filesystem
|
||||
-- pathconf information.
|
||||
calc_pathconf_table = function(pconf, nfsversion)
|
||||
local fs = {}
|
||||
@@ -3301,7 +3301,7 @@ Util =
|
||||
else
|
||||
fs.chown_restricted = "False"
|
||||
end
|
||||
|
||||
|
||||
return fs, nil
|
||||
end,
|
||||
|
||||
@@ -3309,9 +3309,9 @@ Util =
|
||||
--
|
||||
-- @param fsinfo table returned by the NFSv3 FSINFO call
|
||||
-- @param nfsversion the version of the remote NFS server
|
||||
-- @param human if set show the size in the human
|
||||
-- @param human if set show the size in the human
|
||||
-- readable format.
|
||||
-- @return fs table that contains the remote filesystem
|
||||
-- @return fs table that contains the remote filesystem
|
||||
-- information.
|
||||
calc_fsinfo_table = function(fsinfo, nfsversion, human)
|
||||
local fs = {}
|
||||
@@ -3339,15 +3339,15 @@ Util =
|
||||
|
||||
--- Calculate and return the fsstat filesystem table
|
||||
--
|
||||
-- @param stats table returned by the NFSv3 FSSTAT or
|
||||
-- @param stats table returned by the NFSv3 FSSTAT or
|
||||
-- NFSv2 STATFS calls
|
||||
-- @param nfsversion the version of the remote NFS server
|
||||
-- @param human if set show the size in the human
|
||||
-- @param human if set show the size in the human
|
||||
-- readable format.
|
||||
-- @return df table that contains the remote filesystem
|
||||
-- @return df table that contains the remote filesystem
|
||||
-- attributes.
|
||||
calc_fsstat_table = function(stats, nfsversion, human)
|
||||
local df, base = {}, 1024
|
||||
local df, base = {}, 1024
|
||||
local size, free, total, avail, used, use
|
||||
if (nfsversion == 3) then
|
||||
free = stats.fbytes
|
||||
@@ -3392,7 +3392,7 @@ Util =
|
||||
-- @return num number containing the program ID
|
||||
ProgNameToNumber = function(prog_name)
|
||||
local status
|
||||
|
||||
|
||||
if not( RPC_PROGRAMS ) then
|
||||
status, RPC_PROGRAMS = datafiles.parse_rpc()
|
||||
if ( not(status) ) then
|
||||
@@ -3404,17 +3404,17 @@ Util =
|
||||
return num
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the RPC program number to it's equivalent name
|
||||
--
|
||||
-- @param num number containing the RPC program identifier
|
||||
-- @return string containing the RPC program name
|
||||
ProgNumberToName = function( num )
|
||||
local status
|
||||
|
||||
|
||||
if not( RPC_PROGRAMS ) then
|
||||
status, RPC_PROGRAMS = datafiles.parse_rpc()
|
||||
if ( not(status) ) then
|
||||
@@ -3423,7 +3423,7 @@ Util =
|
||||
end
|
||||
return RPC_PROGRAMS[num]
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- Calculates the number of fill bytes needed
|
||||
-- @param length contains the length of the string
|
||||
|
||||
132
nselib/rpcap.lua
132
nselib/rpcap.lua
@@ -34,20 +34,20 @@ local table = require "table"
|
||||
_ENV = stdnse.module("rpcap", stdnse.seeall)
|
||||
|
||||
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 = {
|
||||
local o = {
|
||||
type = 1,
|
||||
username = username,
|
||||
password = password,
|
||||
@@ -56,37 +56,37 @@ RPCAP = {
|
||||
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 = {
|
||||
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,
|
||||
size = 8,
|
||||
new = function(self, type, value, length)
|
||||
local o = {
|
||||
version = 0,
|
||||
@@ -98,25 +98,25 @@ RPCAP = {
|
||||
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
|
||||
|
||||
-- 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),
|
||||
@@ -126,15 +126,15 @@ RPCAP = {
|
||||
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)
|
||||
@@ -143,27 +143,27 @@ RPCAP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function(self)
|
||||
return tostring(self.header)
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
-- Parsers for responses are kept here
|
||||
Response = {
|
||||
|
||||
Authentication = {
|
||||
|
||||
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
|
||||
@@ -171,7 +171,7 @@ RPCAP = {
|
||||
return resp
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
Error = {
|
||||
new = function(self)
|
||||
local o = { }
|
||||
@@ -187,9 +187,9 @@ RPCAP = {
|
||||
pos, err.error = bin.unpack("A" .. err.header.length, data, pos)
|
||||
return err
|
||||
end
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
FindAllInterfaces = {
|
||||
new = function(self)
|
||||
local o = { }
|
||||
@@ -197,7 +197,7 @@ RPCAP = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
|
||||
-- Each address is made up of 4 128 byte fields, this function
|
||||
@@ -208,11 +208,11 @@ RPCAP = {
|
||||
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)
|
||||
@@ -221,27 +221,27 @@ RPCAP = {
|
||||
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)
|
||||
@@ -268,10 +268,10 @@ RPCAP = {
|
||||
return resp
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Maps packet types to classes
|
||||
@@ -284,7 +284,7 @@ RPCAP.TypeToClass = {
|
||||
|
||||
-- The communication class
|
||||
Comm = {
|
||||
|
||||
|
||||
-- Creates a new instance of the Comm class
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
@@ -295,12 +295,12 @@ Comm = {
|
||||
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
|
||||
@@ -313,7 +313,7 @@ Comm = {
|
||||
-- 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
|
||||
-- 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
|
||||
@@ -324,7 +324,7 @@ Comm = {
|
||||
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"
|
||||
@@ -336,10 +336,10 @@ Comm = {
|
||||
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
|
||||
@@ -352,23 +352,23 @@ Comm = {
|
||||
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 = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
comm = Comm:new(host, port)
|
||||
@@ -382,7 +382,7 @@ Helper = {
|
||||
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]
|
||||
@@ -391,20 +391,20 @@ Helper = {
|
||||
-- @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
|
||||
@@ -416,11 +416,11 @@ Helper = {
|
||||
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 = {}
|
||||
@@ -431,7 +431,7 @@ Helper = {
|
||||
end
|
||||
return true, results
|
||||
end,
|
||||
|
||||
|
||||
-- Closes the connection to the server
|
||||
close = function(self)
|
||||
return self.comm:close()
|
||||
|
||||
@@ -15,7 +15,7 @@ _ENV = stdnse.module("rsync", stdnse.seeall)
|
||||
|
||||
-- The Helper class serves as the main interface for script writers
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new instance of the Helper class
|
||||
-- @param host table as received by the action function
|
||||
-- @param port table as received by the action function
|
||||
@@ -28,7 +28,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Handles send and receive of control messages
|
||||
-- @param data string containing the command to send
|
||||
-- @return status true on succes, false on failure
|
||||
@@ -45,7 +45,7 @@ Helper = {
|
||||
end
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
-- Connects to the rsync server
|
||||
-- @return status, true on success, false on failure
|
||||
-- @return err string containing an error message if status is false
|
||||
@@ -56,7 +56,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, err
|
||||
end
|
||||
|
||||
|
||||
local data
|
||||
status, data = self:ctrl_exch("@RSYNCD: 29")
|
||||
if ( not(status) ) then
|
||||
@@ -67,7 +67,7 @@ Helper = {
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- Authenticates against the rsync module. If no username is given, assume
|
||||
-- no authentication is required.
|
||||
-- @param username [optional] string containing the username
|
||||
@@ -78,7 +78,7 @@ Helper = {
|
||||
if (not(status)) then
|
||||
return false, data
|
||||
end
|
||||
|
||||
|
||||
local chall
|
||||
if ( data:match("@RSYNCD: OK") ) then
|
||||
return true, "No authentication was required"
|
||||
@@ -94,7 +94,7 @@ Helper = {
|
||||
if ( chall and not(username) ) then
|
||||
return false, "Authentication required"
|
||||
end
|
||||
|
||||
|
||||
local md4 = openssl.md4("\0\0\0\0" .. password .. chall)
|
||||
local resp = base64.enc(md4):sub(1,-3)
|
||||
status, data = self:ctrl_exch(username .. " " .. resp)
|
||||
@@ -105,9 +105,9 @@ Helper = {
|
||||
if ( data == "@RSYNCD: OK" ) then
|
||||
return true, "Authentication successfull"
|
||||
end
|
||||
return false, "Authentication failed"
|
||||
return false, "Authentication failed"
|
||||
end,
|
||||
|
||||
|
||||
-- Lists accessible modules from the rsync server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return modules table containing a list of modules
|
||||
@@ -116,7 +116,7 @@ Helper = {
|
||||
if (not(status)) then
|
||||
return false, data
|
||||
end
|
||||
|
||||
|
||||
local modules = {}
|
||||
while(true) do
|
||||
status, data = self.socket:receive_buf("\n", false)
|
||||
@@ -131,7 +131,7 @@ Helper = {
|
||||
end
|
||||
return true, modules
|
||||
end,
|
||||
|
||||
|
||||
-- Lists the files available for the directory/module
|
||||
-- TODO: Add support for parsing results, seemed straight forward at
|
||||
-- first, but wasn't.
|
||||
@@ -146,7 +146,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, data
|
||||
end
|
||||
|
||||
|
||||
status, data = self.socket:send("\0\0\0\0")
|
||||
if ( not(status) ) then
|
||||
return false, data
|
||||
@@ -156,7 +156,7 @@ Helper = {
|
||||
if ( not(status) ) then
|
||||
return false, data
|
||||
end
|
||||
|
||||
|
||||
local pos, len = bin.unpack("<S", data)
|
||||
status, data = self.socket:receive_buf(match.numbytes(len), false)
|
||||
if ( not(status) ) then
|
||||
@@ -165,11 +165,11 @@ Helper = {
|
||||
|
||||
-- Parsing goes here
|
||||
end,
|
||||
|
||||
|
||||
-- Disconnects from the rsync server
|
||||
-- @return status true on success, false on failure
|
||||
disconnect = function(self) return self.socket:close() end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -41,7 +41,7 @@ _ENV = stdnse.module("rtsp", stdnse.seeall)
|
||||
|
||||
-- The RTSP Request object
|
||||
Request = {
|
||||
|
||||
|
||||
--- Creates a new Request instance
|
||||
-- @return o instance of Request
|
||||
new = function(self, url, headers)
|
||||
@@ -50,53 +50,53 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the RTSP Request method
|
||||
-- @param method string containing the RTSP method
|
||||
setMethod = function(self, method)
|
||||
self.method = method
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the RTSP sequence number
|
||||
-- @param cseq number containing the sequence number
|
||||
setCSeq = function(self, cseq)
|
||||
self.cseq = cseq
|
||||
end,
|
||||
|
||||
|
||||
--- Adds an optional header to the RTSP request
|
||||
-- @param header string containing the header name
|
||||
-- @param value string containing the header value
|
||||
addHeader = function(self, header, value)
|
||||
table.insert( self.headers, { header = value } )
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the Request to a string
|
||||
--
|
||||
-- @return req string containing the request as a string
|
||||
__tostring = function(self)
|
||||
assert(self.cseq, "Request is missing required header CSeq")
|
||||
assert(self.url, "Request is missing URL")
|
||||
|
||||
local req = stdnse.strjoin("\r\n", {
|
||||
("%s %s RTSP/1.0"):format(self.method, self.url),
|
||||
|
||||
local req = stdnse.strjoin("\r\n", {
|
||||
("%s %s RTSP/1.0"):format(self.method, self.url),
|
||||
("CSeq: %d"):format(self.cseq)
|
||||
} ) .. "\r\n"
|
||||
if ( #self.headers > 0 ) then
|
||||
req = req .. stdnse.strjoin("\r\n", self.headers) .. "\r\n"
|
||||
end
|
||||
|
||||
|
||||
return req .. "\r\n"
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
-- The RTSP response instance
|
||||
Response = {
|
||||
|
||||
|
||||
--- Creates a new Response instance
|
||||
-- @param data string containing the unparsed data
|
||||
-- @param data string containing the unparsed data
|
||||
new = function(self, data)
|
||||
assert(data, "No data was supplied")
|
||||
local o = {
|
||||
local o = {
|
||||
raw = data,
|
||||
status = tonumber(data:match("^RTSP%/1%.0 (%d*) "))
|
||||
}
|
||||
@@ -104,7 +104,7 @@ Response = {
|
||||
-- Split the response into a temporary array
|
||||
local tmp = stdnse.strsplit("\r\n", data)
|
||||
if ( not(tmp) ) then return nil end
|
||||
|
||||
|
||||
-- we should have atleas one entry
|
||||
if ( #tmp > 1 ) then
|
||||
o.headers = {}
|
||||
@@ -121,19 +121,19 @@ Response = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
-- RTSP Client class
|
||||
Client = {
|
||||
|
||||
|
||||
-- Creates a new Client instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
-- @return o instance of Client
|
||||
new = function(self, host, port)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
cseq = 0,
|
||||
@@ -145,22 +145,22 @@ Client = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the number of retries for socket reads
|
||||
-- @param retries number containing the number of retries
|
||||
setRetries = function(self, retries) self.retries = retries end,
|
||||
|
||||
|
||||
--- Sets the socket connection timeout in ms
|
||||
-- @param timeout number containing the timeout in ms
|
||||
setTimeout = function(self, timeout) self.timeout = timeout end,
|
||||
|
||||
|
||||
--- Adds a RTSP header to the request
|
||||
-- @param header string containing the header name
|
||||
-- @param value string containing the header value
|
||||
addHeader = function(self, header, value)
|
||||
table.insert(self.headers, { ("%s: %s"):format(header,value) } )
|
||||
end,
|
||||
|
||||
|
||||
--- Connects to the RTSP server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing the error message on failure
|
||||
@@ -173,8 +173,8 @@ Client = {
|
||||
return false, ("Failed to connect to the server: %s"):format(self.host.ip)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
--- Sends a DESCRIBE request to the server and receives the response
|
||||
-- @param url string containing the RTSP URL
|
||||
-- @return status true on success, false on failure
|
||||
@@ -185,15 +185,15 @@ Client = {
|
||||
req:setMethod("DESCRIBE")
|
||||
return self:exch(req)
|
||||
end,
|
||||
|
||||
|
||||
options = function(self, url)
|
||||
local req = Request:new(url, self.headers)
|
||||
req:setMethod("OPTIONS")
|
||||
return self:exch(req)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a request to the server and receives the response and attempts
|
||||
-- to retry if either send or receive fails.
|
||||
-- to retry if either send or receive fails.
|
||||
-- @param request instance of Request
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response Response instance on success
|
||||
@@ -203,7 +203,7 @@ Client = {
|
||||
local status, data
|
||||
self.cseq = self.cseq + 1
|
||||
req:setCSeq( self.cseq )
|
||||
|
||||
|
||||
repeat
|
||||
local err
|
||||
status, err = self.socket:send( tostring(req) )
|
||||
@@ -217,7 +217,7 @@ Client = {
|
||||
status, data = self.socket:receive()
|
||||
-- if we got the response allright, break out of retry loop
|
||||
if ( status ) then break end
|
||||
end
|
||||
end
|
||||
-- if either send or receive fails, re-connect the socket
|
||||
if ( not(status) ) then
|
||||
self:close()
|
||||
@@ -227,7 +227,7 @@ Client = {
|
||||
stdnse.print_debug(2, "Failed to reconnect socket to server (%s)", err)
|
||||
return false, ("Failed to reconnect socket to server (%s)"):format(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
retries = retries - 1
|
||||
until( status or retries == 0 )
|
||||
|
||||
@@ -235,20 +235,20 @@ Client = {
|
||||
stdnse.print_debug(2, "Failed to receive response from server (%s)", data)
|
||||
return false, ("Failed to receive response from server (%s)"):format(data)
|
||||
end
|
||||
|
||||
|
||||
return true, Response:new(data)
|
||||
end,
|
||||
|
||||
|
||||
--- Closes the RTSP socket with the server
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Helper class is the main script interface
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Creates a new Helper instance
|
||||
-- @param host table as received by the action method
|
||||
-- @param port table as received by the action method
|
||||
@@ -259,19 +259,19 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects to the RTSP server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing the error message on failure
|
||||
connect = function(self)
|
||||
return self.client:connect()
|
||||
end,
|
||||
|
||||
|
||||
-- Closes the RTSP socket with the server
|
||||
close = function(self)
|
||||
return self.client:close()
|
||||
end,
|
||||
|
||||
|
||||
-- Sends a DESCRIBE request to the server and receives the response
|
||||
--
|
||||
-- @param url string containing the RTSP URL
|
||||
@@ -281,11 +281,11 @@ Helper = {
|
||||
describe = function(self, url)
|
||||
return self.client:describe(url)
|
||||
end,
|
||||
|
||||
|
||||
options = function(self, url)
|
||||
return self.client:options(url)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
-- local dmd5 = DigestMD5:new(chall, user, pass, "AUTHENTICATE", nil, "imap")
|
||||
-- local digest = dmd5:calcDigest()
|
||||
-- </code>
|
||||
--
|
||||
--
|
||||
-- The <code>NTLM</code> class contains all code necessary to calculate a
|
||||
-- NTLM response based on the servers challenge and the other necessary
|
||||
-- arguments (@see NTLM.new). It can be called through the SASL helper or
|
||||
@@ -55,7 +55,7 @@ if ( not(HAVE_SSL) ) then
|
||||
end
|
||||
local MECHANISMS = { }
|
||||
|
||||
if HAVE_SSL then
|
||||
if HAVE_SSL then
|
||||
-- Calculates a DIGEST MD5 response
|
||||
DigestMD5 = {
|
||||
|
||||
@@ -64,8 +64,8 @@ if HAVE_SSL then
|
||||
-- @param chall string containing the base64 decoded challenge
|
||||
-- @return a new instance of DigestMD5
|
||||
new = function(self, chall, username, password, method, uri, service, realm)
|
||||
local o = { nc = 0,
|
||||
chall = chall,
|
||||
local o = { nc = 0,
|
||||
chall = chall,
|
||||
challnvs = {},
|
||||
username = username,
|
||||
password = password,
|
||||
@@ -150,7 +150,7 @@ if HAVE_SSL then
|
||||
response = response .. (",%s=\"%s\""):format("digest-uri", uri)
|
||||
response = response .. (",%s=%s"):format("response", digest)
|
||||
response = response .. (",%s=%s"):format("charset", "utf-8")
|
||||
|
||||
|
||||
-- response_table is used in http library because the request should
|
||||
-- be a little bit different then the string generated above
|
||||
local response_table = {
|
||||
@@ -164,7 +164,7 @@ if HAVE_SSL then
|
||||
algorithm = self.challnvs.algorithm,
|
||||
response = digest_http
|
||||
}
|
||||
|
||||
|
||||
return response, response_table
|
||||
end,
|
||||
|
||||
@@ -180,8 +180,8 @@ if HAVE_SSL then
|
||||
-- @param password string containing the password
|
||||
-- @return new instance of NTML
|
||||
new = function(self, chall, username, password)
|
||||
local o = { nc = 0,
|
||||
chall = chall,
|
||||
local o = { nc = 0,
|
||||
chall = chall,
|
||||
username = username,
|
||||
password = password}
|
||||
setmetatable(o, self)
|
||||
@@ -207,8 +207,8 @@ if HAVE_SSL then
|
||||
local NTLM_NegotiateExtendedSecurity = 0x00080000
|
||||
local pos, _, message_type
|
||||
|
||||
pos, _, message_type, _, _,
|
||||
_, self.flags, self.chall, _,
|
||||
pos, _, message_type, _, _,
|
||||
_, self.flags, self.chall, _,
|
||||
_, _, _ = bin.unpack("<A8ISSIIA8LSSI", self.chall)
|
||||
|
||||
if ( message_type ~= 0x02 ) then
|
||||
@@ -225,7 +225,7 @@ if HAVE_SSL then
|
||||
self.workstation = self.to_unicode(self.workstation)
|
||||
self.username = self.to_unicode(self.username)
|
||||
self.domain = self.to_unicode(self.domain)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
--- Calculates the response
|
||||
@@ -277,7 +277,7 @@ if HAVE_SSL then
|
||||
end
|
||||
|
||||
}
|
||||
|
||||
|
||||
--- Encodes the parameters using the <code>CRAM-MD5</code> mechanism.
|
||||
--
|
||||
-- @param username string.
|
||||
@@ -303,11 +303,11 @@ if HAVE_SSL then
|
||||
-- @return string The encoded string on success, or nil if Nmap was
|
||||
-- compiled without OpenSSL.
|
||||
function digest_md5_enc(username, password, challenge, service, uri)
|
||||
return DigestMD5:new(challenge,
|
||||
username,
|
||||
password,
|
||||
"AUTHENTICATE",
|
||||
uri,
|
||||
return DigestMD5:new(challenge,
|
||||
username,
|
||||
password,
|
||||
"AUTHENTICATE",
|
||||
uri,
|
||||
service):calcDigest()
|
||||
end
|
||||
|
||||
@@ -448,7 +448,7 @@ Helper = {
|
||||
end,
|
||||
|
||||
--- Returns the current authentication mechanism.
|
||||
--
|
||||
--
|
||||
-- @return mechanism on success, or nil on failures.
|
||||
get_mechanism = function(self)
|
||||
return self.mechanism
|
||||
|
||||
@@ -73,7 +73,7 @@ end
|
||||
-- <code>"smtp"</code>, or <code>"ftp"</code>. These service names are
|
||||
-- determined by Nmap's version scan or (if no version scan information is
|
||||
-- available) the service assigned to the port in <code>nmap-services</code>
|
||||
-- (e.g. <code>"http"</code> for TCP port 80).
|
||||
-- (e.g. <code>"http"</code> for TCP port 80).
|
||||
-- @param services Service name or a list of names to run against.
|
||||
-- @param protos The protocol or list of protocols to match against, default
|
||||
-- <code>"tcp"</code>.
|
||||
@@ -110,7 +110,7 @@ end
|
||||
-- a list of values as in those functions. This function exists because many
|
||||
-- scripts explicitly try to run against the well-known ports, but want also to
|
||||
-- run against any other port which was discovered to run the named service.
|
||||
-- @usage portrule = shortport.port_or_service(22,"ssh").
|
||||
-- @usage portrule = shortport.port_or_service(22,"ssh").
|
||||
-- @param ports A single port number or a list of port numbers.
|
||||
-- @param services Service name or a list of names to run against.
|
||||
-- @param protos The protocol or list of protocols to match against, default
|
||||
|
||||
204
nselib/sip.lua
204
nselib/sip.lua
@@ -6,7 +6,7 @@
|
||||
-- Overview
|
||||
-- --------
|
||||
-- The library consists of the following classes:
|
||||
--
|
||||
--
|
||||
-- o SessionData
|
||||
-- - Holds session data for the SIP session
|
||||
--
|
||||
@@ -31,7 +31,7 @@
|
||||
--
|
||||
-- o Helper
|
||||
-- - A class containing code used as a primary interface by scripts
|
||||
--
|
||||
--
|
||||
--
|
||||
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||
@@ -75,7 +75,7 @@ Error = {
|
||||
|
||||
-- The SessionData class
|
||||
SessionData = {
|
||||
|
||||
|
||||
--- Creates a new instance of sessiondata
|
||||
-- @return o an instance of SessionData
|
||||
new = function(self, o)
|
||||
@@ -85,7 +85,7 @@ SessionData = {
|
||||
o.user = "user"
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the session username
|
||||
-- @param user string containing the username
|
||||
setUsername = function(self, user) self.user = user end,
|
||||
@@ -111,19 +111,19 @@ SessionData = {
|
||||
--- Sets the SIP users Full Name
|
||||
-- @param name string containing the full name of the user
|
||||
setName = function(self, name) self.name = name end,
|
||||
|
||||
|
||||
--- Retrieves the username
|
||||
-- @return user string containing the sessions username
|
||||
getUsername = function(self) return self.user end,
|
||||
|
||||
|
||||
--- Retrieves the session password
|
||||
-- @return pass string containing the session password
|
||||
getPassword = function(self) return self.pass end,
|
||||
|
||||
|
||||
--- Retrieves the SIP domain
|
||||
-- @return domain string containing the SIP domain
|
||||
getDomain = function(self) return self.domain end,
|
||||
|
||||
|
||||
--- Retrieves the client IP and port
|
||||
-- @return host string containing the client IP
|
||||
-- @return port number containing the client port
|
||||
@@ -158,13 +158,13 @@ Session = {
|
||||
o.expires = (options and options.expires) or 300
|
||||
o.conn = Connection:new(host,port)
|
||||
o.cseq = (options and options.cseq) or 1234
|
||||
local timeout = ( ( options and options.timeout ) and
|
||||
local timeout = ( ( options and options.timeout ) and
|
||||
options.timeout * 1000 ) or 5000
|
||||
o.conn.socket:set_timeout( timeout )
|
||||
o.sessdata = sessdata or SessionData:new()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Connect the session
|
||||
-- @return true on success, false on failure
|
||||
-- @return err string containing error message
|
||||
@@ -181,12 +181,12 @@ Session = {
|
||||
self.sessdata:setServer(rhost, rport)
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Closes the session
|
||||
-- TODO: We should probably send some "closing" packets here
|
||||
-- @return true on success, false on failure
|
||||
close = function(self) return self.conn:close() end,
|
||||
|
||||
|
||||
--- Sends and SIP invite
|
||||
-- @param uri
|
||||
invite = function(self, uri)
|
||||
@@ -194,10 +194,10 @@ Session = {
|
||||
|
||||
local lhost, _ = self.sessdata:getClient()
|
||||
local tm = os.time()
|
||||
|
||||
local uri = (uri and uri:match("^sip:.*@.*")) or
|
||||
|
||||
local uri = (uri and uri:match("^sip:.*@.*")) or
|
||||
("sip:%s@%s"):format(uri, self.sessdata:getDomain())
|
||||
|
||||
|
||||
request:setUri(uri)
|
||||
request:setSessionData(self.sessdata)
|
||||
|
||||
@@ -212,13 +212,13 @@ Session = {
|
||||
|
||||
request:setContent(stdnse.strjoin("\r\n", data))
|
||||
request:setContentType("application/sdp")
|
||||
|
||||
|
||||
local status, response = self:exch(request)
|
||||
if ( not(status) ) then return false, response end
|
||||
|
||||
local errcode = response:getErrorCode()
|
||||
|
||||
if ( Error.PROXY_AUTH_REQUIRED == errcode or
|
||||
if ( Error.PROXY_AUTH_REQUIRED == errcode or
|
||||
Error.UNAUTHORIZED == errcode ) then
|
||||
|
||||
-- Send an ACK to the server
|
||||
@@ -232,19 +232,19 @@ Session = {
|
||||
status, data = self:authenticate(request, response)
|
||||
if ( not(status) ) then return false, "SIP Authentication failed" end
|
||||
response = Response:new(data)
|
||||
|
||||
|
||||
-- read a bunch of 180 Ringing and 100 Trying requests, until we get a 200 OK
|
||||
while ( response:getErrorCode() ~= Error.OK ) do
|
||||
status, data = self.conn:recv()
|
||||
if ( not(status) ) then return status, "ERROR: Failed to receive response" end
|
||||
response = Response:new(data)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Prepares and sends the challenge response authentication to the server
|
||||
-- @param request instance of the request object requiring authentication
|
||||
-- @param authdata string containing authentication data
|
||||
@@ -252,7 +252,7 @@ Session = {
|
||||
-- @return err string containing an error message if status is false
|
||||
authenticate = function(self, request, response)
|
||||
local rhost, _ = self.sessdata:getServer()
|
||||
local auth_header, auth_data = response:getAuthData()
|
||||
local auth_header, auth_data = response:getAuthData()
|
||||
local auth = SipAuth:new(auth_data)
|
||||
auth:setUsername(self.sessdata:getUsername())
|
||||
auth:setPassword(self.sessdata:getPassword())
|
||||
@@ -276,7 +276,7 @@ Session = {
|
||||
end
|
||||
return status, data
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a SIP Request and receives the Response
|
||||
-- @param request instance of Request
|
||||
-- @return status true on success, false on failure
|
||||
@@ -284,31 +284,31 @@ Session = {
|
||||
-- err containing error message if status is false
|
||||
exch = function(self, request)
|
||||
request:setCseq(self.cseq)
|
||||
|
||||
|
||||
local status, err = self.conn:send( tostring(request) )
|
||||
if ( not(status) ) then return status, "ERROR: Failed to send request" end
|
||||
|
||||
local status, data = self.conn:recv()
|
||||
if ( not(status) ) then return status, "ERROR: Failed to receive response" end
|
||||
|
||||
|
||||
return true, Response:new(data)
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a register request to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return msg string containing the error message (if status is false)
|
||||
register = function(self)
|
||||
local request = Request:new(Method.REGISTER, self.protocol)
|
||||
|
||||
|
||||
request:setUri("sip:" .. self.sessdata:getServer())
|
||||
request:setSessionData(self.sessdata)
|
||||
request:setExpires(self.expires)
|
||||
|
||||
|
||||
local status, response = self:exch(request)
|
||||
if (not(status)) then return false, response end
|
||||
|
||||
local errcode = response:getErrorCode()
|
||||
|
||||
|
||||
if ( status and errcode == Error.OK ) then
|
||||
return true, response
|
||||
elseif ( Error.PROXY_AUTH_REQUIRED == errcode or Error.UNAUTHORIZED == errcode ) then
|
||||
@@ -316,7 +316,7 @@ Session = {
|
||||
self.cseq = self.cseq + 1
|
||||
status, data = self:authenticate(request, response)
|
||||
response = Response:new(data)
|
||||
errcode = response:getErrorCode()
|
||||
errcode = response:getErrorCode()
|
||||
if ( not(status) or ( errcode and errcode ~= Error.OK ) ) then
|
||||
return false, "ERROR: Failed to authenticate"
|
||||
end
|
||||
@@ -327,7 +327,7 @@ Session = {
|
||||
end
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Sends an option request to the server and handles the response
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response if status is true, nil else.
|
||||
@@ -348,7 +348,7 @@ Session = {
|
||||
|
||||
-- The connection class contains basic communication code
|
||||
Connection = {
|
||||
|
||||
|
||||
--- Creates a new SIP Connection
|
||||
-- @param host table containing the host to connect to
|
||||
-- @param port table containing the port to connect to
|
||||
@@ -362,7 +362,7 @@ Connection = {
|
||||
o.socket = nmap.new_socket()
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Connects to the server
|
||||
-- @return status containing true on success and false on failure
|
||||
-- @return err containing the error message (if status is false)
|
||||
@@ -377,7 +377,7 @@ Connection = {
|
||||
end
|
||||
return status, err
|
||||
end,
|
||||
|
||||
|
||||
--- Sends the data over the socket
|
||||
-- @return status true on success, false on failure
|
||||
send = function(self, data)
|
||||
@@ -395,7 +395,7 @@ Connection = {
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
--- Retrieves the client ip and port
|
||||
-- @return lhost string containing the local ip
|
||||
-- @return lport number containing the local port
|
||||
@@ -405,13 +405,13 @@ Connection = {
|
||||
-- @return rhost string containing the server ip
|
||||
-- @return rport number containing the server port
|
||||
getServer = function(self) return ( self.host.ip or self.host ), ( self.port.number or self.port ) end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The response class holds the necessary methods and parameters to parse a response
|
||||
Response = {
|
||||
|
||||
|
||||
--- Creates a new Response instance
|
||||
-- @param str containing the data as received over the socket
|
||||
-- @return o table containing a new Response instance
|
||||
@@ -422,7 +422,7 @@ Response = {
|
||||
o.tbl = stdnse.strsplit("\r\n", str)
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Retrieves a given header value from the response
|
||||
-- @param name string containing the name of the header
|
||||
-- @return value string containing the header value
|
||||
@@ -430,40 +430,40 @@ Response = {
|
||||
for _, line in ipairs(self.tbl) do
|
||||
local header, value = line:match("^(.-): (.*)$")
|
||||
if ( header and header:lower() == name:lower() ) then
|
||||
return value
|
||||
return value
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the error code from the SIP response
|
||||
-- @return err number containing the error code
|
||||
getErrorCode = function(self)
|
||||
return tonumber(self.tbl[1]:match("SIP/%d%.%d (%d+)"))
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the error message returned by the server
|
||||
-- @return errmsg string containing the error message
|
||||
getErrorMessage = function(self)
|
||||
return self.tbl[1]:match("^SIP/%d%.%d %d+ (.+)$")
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the message method
|
||||
-- @return method string containing the method
|
||||
getMethod = function(self)
|
||||
return self.tbl[1]:match("^(.-)%s.*SIP/2%.0$")
|
||||
end,
|
||||
|
||||
|
||||
--- Returns the authentication data from the SIP response
|
||||
-- @return auth string containing the raw authentication data
|
||||
getAuthData = function(self)
|
||||
local auth = self:getHeader("WWW-Authenticate") or self:getHeader("Proxy-Authenticate")
|
||||
if ( auth ) then
|
||||
return ( self:getHeader("WWW-Authenticate") and
|
||||
"WWW-Authenticate" or
|
||||
return ( self:getHeader("WWW-Authenticate") and
|
||||
"WWW-Authenticate" or
|
||||
"Proxy-Authenticate"), auth
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Retrieves the current sequence number
|
||||
-- @return cseq number containing the current sequence number
|
||||
getCSeq = function(self)
|
||||
@@ -471,12 +471,12 @@ Response = {
|
||||
cseq = (cseq and cseq:match("^(%d+)"))
|
||||
return (cseq and tonumber(cseq))
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The request class holds the necessary functions and parameters for a basic SIP request
|
||||
Request = {
|
||||
|
||||
|
||||
--- Creates a new Request instance
|
||||
-- @param method string containing the request method to use
|
||||
-- @param proto Used protocol, could be "UDP" or "TCP"
|
||||
@@ -485,7 +485,7 @@ Request = {
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
|
||||
|
||||
o.ua = "Nmap NSE"
|
||||
o.protocol = proto or "UDP"
|
||||
o.expires = 0
|
||||
@@ -498,11 +498,11 @@ Request = {
|
||||
o.cid = Util.get_random_string(60)
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the sessiondata so that session information may be fetched
|
||||
-- @param data instance of SessionData
|
||||
setSessionData = function(self, data) self.sessdata = data end,
|
||||
|
||||
|
||||
--- Adds a custom header to the request
|
||||
-- @param name string containing the header name
|
||||
-- @param value string containing the header value
|
||||
@@ -510,28 +510,28 @@ Request = {
|
||||
self.headers = self.headers or {}
|
||||
table.insert(self.headers, ("%s: %s"):format(name, value))
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the SIP uri
|
||||
-- @param uri string containing the SIP uri
|
||||
setUri = function(self, uri) self.uri = uri end,
|
||||
|
||||
|
||||
--- Sets an error
|
||||
-- @param code number containing the error code
|
||||
-- @param msg string containing the error message
|
||||
setError = function(self, code, msg) self.error = { code = code, msg = msg } end,
|
||||
|
||||
|
||||
--- Sets the request method
|
||||
-- @param method string containing a valid SIP method (@see Method constant)
|
||||
setMethod = function(self, method) self.method = method end,
|
||||
|
||||
|
||||
--- Sets the sequence number
|
||||
-- @param seq number containing the sequence number to set
|
||||
setCseq = function(self, seq) self.cseq = seq end,
|
||||
|
||||
|
||||
--- Sets the allow header
|
||||
-- @param allow table containing all of the allowed SIP methods
|
||||
setAllow = function(self, allow) self.allow = stdnse.strjoin(", ", allow) end,
|
||||
|
||||
|
||||
--- Sets the request content data
|
||||
-- @param string containing the content data
|
||||
setContent = function(self, content) self.content = content end,
|
||||
@@ -543,15 +543,15 @@ Request = {
|
||||
--- Sets the supported SIP methods
|
||||
-- @param supported string containing the supported methods
|
||||
setSupported = function(self, supported) self.supported = supported end,
|
||||
|
||||
|
||||
--- Sets the content-length of the SIP request
|
||||
-- @param len number containing the length of the actual request
|
||||
setContentLength = function(self, len) self.length = len end,
|
||||
|
||||
|
||||
--- Sets the expires header of the SIP request
|
||||
-- @param expires number containing the expire value
|
||||
setExpires = function(self, expires) self.expires = expires end,
|
||||
|
||||
|
||||
--- Sets the User Agent being used to connect to the SIP server
|
||||
-- @param ua string containing the User-Agent name (defaults to Nmap NSE)
|
||||
setUA = function(self, ua) self.ua = ua end,
|
||||
@@ -559,19 +559,19 @@ Request = {
|
||||
--- Sets the caller ID information of the SIP request
|
||||
-- @param cid string containing the callers id
|
||||
setCallId = function(self, cid) self.cid = cid end,
|
||||
|
||||
|
||||
--- Sets the maximum forwards allowed of this request
|
||||
-- @param maxfwd number containing the maximum allowed forwards
|
||||
setForwards = function(self, maxfwd) self.maxfwd = maxfwd end,
|
||||
|
||||
|
||||
--- Sets the proxy authentication data
|
||||
-- @param auth string containing properly formatted proxy authentication data
|
||||
setProxyAuth = function(self, auth) self.proxyauth = auth end,
|
||||
|
||||
|
||||
--- Sets the www authentication data
|
||||
-- @param auth string containing properly formatted proxy authentication data
|
||||
setWWWAuth = function(self, auth) self.wwwauth = auth end,
|
||||
|
||||
|
||||
--- Specifies the network protocol being used
|
||||
-- @param proto should be either "UDP" or "TCP"
|
||||
setProtocol = function(self, proto)
|
||||
@@ -579,7 +579,7 @@ Request = {
|
||||
self.protocol = proto
|
||||
end,
|
||||
|
||||
|
||||
|
||||
--- Converts the request to a String suitable to be sent over the socket
|
||||
-- @return ret string containing the complete request for sending over the socket
|
||||
__tostring = function(self)
|
||||
@@ -590,15 +590,15 @@ Request = {
|
||||
local sessdata = self.sessdata
|
||||
local lhost, lport = sessdata:getClient()
|
||||
local rhost, rport = sessdata:getServer()
|
||||
|
||||
|
||||
local name, user, domain = sessdata:getName(), sessdata:getUsername(), sessdata:getDomain()
|
||||
|
||||
|
||||
assert(self.method, "No method specified")
|
||||
assert(self.maxfwd, "Max forward not set")
|
||||
|
||||
|
||||
-- if no domain was specified use the remote host instead
|
||||
domain = domain or rhost
|
||||
|
||||
|
||||
if ( self.error ) then
|
||||
table.insert(data, ("SIP/2.0 %s %d"):format(self.error.msg, self.error.code))
|
||||
else
|
||||
@@ -611,15 +611,15 @@ Request = {
|
||||
table.insert(data, ("Via: SIP/2.0/%s %s:%d;rport;branch=%s"):format(self.protocol, lhost, lport, branch))
|
||||
table.insert(data, ("Max-Forwards: %d"):format(self.maxfwd))
|
||||
table.insert(data, ("From: \"%s\" <sip:%s@%s>;tag=%s"):format(name, user, domain, self.from_tag))
|
||||
|
||||
|
||||
if ( self.method == Method.INVITE ) then
|
||||
table.insert(data, ("To: <sip:%s@%s>"):format(user, domain))
|
||||
else
|
||||
table.insert(data, ("To: \"%s\" <sip:%s@%s>"):format(name, user, domain))
|
||||
end
|
||||
|
||||
|
||||
table.insert(data, ("Call-ID: %s"):format(self.cid))
|
||||
|
||||
|
||||
if ( self.error and self.error.code == Error.OK ) then
|
||||
table.insert(data, ("CSeq: %d OPTIONS"):format(self.cseq))
|
||||
else
|
||||
@@ -638,9 +638,9 @@ Request = {
|
||||
if ( self.supported ) then
|
||||
table.insert(data, ("Supported: %s"):format(self.supported))
|
||||
end
|
||||
|
||||
|
||||
if ( not(self.error) ) then
|
||||
if ( self.proxyauth ) then
|
||||
if ( self.proxyauth ) then
|
||||
table.insert(data, ("Proxy-Authorization: %s"):format(self.proxyauth))
|
||||
end
|
||||
if ( self.wwwauth ) then
|
||||
@@ -664,22 +664,22 @@ Request = {
|
||||
table.insert(data, "")
|
||||
else
|
||||
self.length = (self.content and #self.content +2 or 0)
|
||||
|
||||
|
||||
table.insert(data, ("Content-Length: %d"):format(self.length))
|
||||
table.insert(data, "")
|
||||
end
|
||||
return stdnse.strjoin("\r\n", data)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- A minimal Util class with supporting functions
|
||||
Util = {
|
||||
|
||||
--- Generates a random string of the requested length.
|
||||
-- @param length (optional) The length of the string to return. Default: 8.
|
||||
-- @param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore.
|
||||
-- @return The random string.
|
||||
|
||||
--- Generates a random string of the requested length.
|
||||
-- @param length (optional) The length of the string to return. Default: 8.
|
||||
-- @param set (optional) The set of letters to choose from. Default: upper, lower, numbers, and underscore.
|
||||
-- @return The random string.
|
||||
get_random_string = function(length, set)
|
||||
if(length == nil) then
|
||||
length = 8
|
||||
@@ -698,12 +698,12 @@ Util = {
|
||||
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The SIP authentication class, supporting MD5 digest authentication
|
||||
SipAuth = {
|
||||
|
||||
|
||||
--- Creates a new SipAuth instance
|
||||
-- @param auth string containing the auth data as received from the server
|
||||
new = function(self, auth)
|
||||
@@ -713,46 +713,46 @@ SipAuth = {
|
||||
o.auth = auth
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Sets the username used for authentication
|
||||
-- @param username string containing the name of the user
|
||||
setUsername = function(self, username) self.username = username end,
|
||||
|
||||
|
||||
--- Sets the password used for authentication
|
||||
-- @param password string containing the password of the user
|
||||
setPassword = function(self, password) self.password = password end,
|
||||
|
||||
|
||||
--- Sets the method used for authentication
|
||||
-- @param method string containing the method (Usually REGISTER)
|
||||
setMethod = function(self, method) self.method = method end,
|
||||
|
||||
|
||||
--- Sets the uri used for authentication
|
||||
-- @param uri string containing the uri (Usually sip:<ip>)
|
||||
setUri = function(self, uri) self.uri = uri end,
|
||||
|
||||
|
||||
--- Processes and parses a challenge as received from the server
|
||||
parseChallenge = function(self)
|
||||
if ( not(self.auth) ) then return end
|
||||
self.nonce = self.auth:match("nonce=[\"]([^,]-)[\"]")
|
||||
self.algorithm = self.auth:match("algorithm=[\"]*(.-)[\"]*,")
|
||||
self.realm = self.auth:match("realm=[\"]([^,]-)[\"]")
|
||||
assert(self.algorithm:upper() == "MD5",
|
||||
assert(self.algorithm:upper() == "MD5",
|
||||
("Unsupported algorithm detected in authentication challenge (%s)"):format(self.algorithm:upper()))
|
||||
end,
|
||||
|
||||
|
||||
--- Calculates the authentication response
|
||||
-- @return reponse string containing the authentication response
|
||||
calculateResponse = function(self)
|
||||
|
||||
|
||||
if ( not(self.nonce) or not(self.algorithm) or not(self.realm) ) then
|
||||
self:parseChallenge()
|
||||
end
|
||||
|
||||
|
||||
assert(self.username, "SipAuth: No username specified")
|
||||
assert(self.password, "SipAuth: No password specified")
|
||||
assert(self.method, "SipAuth: No method specified")
|
||||
assert(self.uri, "SipAuth: No uri specified")
|
||||
|
||||
|
||||
local result
|
||||
if ( self.algorithm == "MD5" ) then
|
||||
local HA1 = select(2, bin.unpack("H16", openssl.md5(self.username .. ":" .. self.realm .. ":" .. self.password)))
|
||||
@@ -761,7 +761,7 @@ SipAuth = {
|
||||
end
|
||||
return select(2, bin.unpack("H16", result)):lower()
|
||||
end,
|
||||
|
||||
|
||||
--- Creates the complete authentication response
|
||||
-- @return auth string containing the complete authentication digest
|
||||
createResponse = function(self)
|
||||
@@ -770,12 +770,12 @@ SipAuth = {
|
||||
" uri=\"%s\", response=\"%s\", algorithm=%s"):format(self.username, self.realm,
|
||||
self.nonce, self.uri, response, self.algorithm)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Helper class used as main script interface
|
||||
Helper = {
|
||||
|
||||
|
||||
--- Creates a new instance of the Helper class
|
||||
-- @param host table containing the remote host
|
||||
-- @param port table containing the remote port
|
||||
@@ -789,7 +789,7 @@ Helper = {
|
||||
local timeout = stdnse.get_script_args("sip.timeout")
|
||||
if ( timeout ) then options.timeout = timeout end
|
||||
o.sessdata = SessionData:new()
|
||||
o.session = Session:new(host, port, o.sessdata, options)
|
||||
o.session = Session:new(host, port, o.sessdata, options)
|
||||
return o
|
||||
end,
|
||||
|
||||
@@ -797,7 +797,7 @@ Helper = {
|
||||
connect = function(self) return self.session:connect() end,
|
||||
|
||||
--- Disconnects and closes the helper instance
|
||||
close = function(self) return self.session:close() end,
|
||||
close = function(self) return self.session:close() end,
|
||||
|
||||
--- Sets the credentials used when performing authentication
|
||||
-- @param username string containing the username to use for authentication
|
||||
@@ -812,7 +812,7 @@ Helper = {
|
||||
setDomain = function(self, domain) self.sessdata:setDomain(domain) end,
|
||||
|
||||
--- Register the UAC with the server
|
||||
-- @param options table containing zero or more options
|
||||
-- @param options table containing zero or more options
|
||||
-- (@see Session:register for more details)
|
||||
-- @return status true on success, false on failure
|
||||
-- @return msg containing the error message if status is false
|
||||
@@ -823,14 +823,14 @@ Helper = {
|
||||
end,
|
||||
|
||||
options = function(self) return self.session:options() end,
|
||||
|
||||
|
||||
--- Attempts to INVITE the user at uri to a call
|
||||
-- @param uri string containing the sip uri
|
||||
-- @return status true on success, false on failure
|
||||
invite = function(self, uri)
|
||||
return self.session:invite(uri)
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
754
nselib/smb.lua
754
nselib/smb.lua
File diff suppressed because it is too large
Load Diff
@@ -1,54 +1,54 @@
|
||||
---
|
||||
-- This module takes care of the authentication used in SMB (LM, NTLM, LMv2, NTLMv2).
|
||||
-- This module takes care of the authentication used in SMB (LM, NTLM, LMv2, NTLMv2).
|
||||
--
|
||||
-- There is a lot to this functionality, so if you're interested in how it works, read
|
||||
-- on.
|
||||
-- on.
|
||||
-- In SMB authentication, there are two distinct concepts. Each will be dealt with
|
||||
-- separately. There are:
|
||||
-- * Stored hashes
|
||||
-- * Authentication
|
||||
--
|
||||
-- What's confusing is that the same names are used for each of those.
|
||||
-- What's confusing is that the same names are used for each of those.
|
||||
--
|
||||
-- Stored Hashes:
|
||||
-- Windows stores two types of hashes: Lanman and NT Lanman (or NTLM). Vista and later
|
||||
-- store NTLM only. Lanman passwords are divided into two 7-character passwords and
|
||||
-- used as a key in DES, while NTLM is converted to unicode and MD4ed.
|
||||
-- store NTLM only. Lanman passwords are divided into two 7-character passwords and
|
||||
-- used as a key in DES, while NTLM is converted to unicode and MD4ed.
|
||||
--
|
||||
-- The stored hashes can be dumped in a variety of ways (pwdump6, fgdump, Metasploit's
|
||||
-- <code>priv</code> module, <code>smb-psexec.nse</code>, etc). Generally, two hashes are dumped together
|
||||
-- <code>priv</code> module, <code>smb-psexec.nse</code>, etc). Generally, two hashes are dumped together
|
||||
-- (generally, Lanman:NTLM). Sometimes, Lanman is empty and only NTLM is given. Lanman
|
||||
-- is never required.
|
||||
-- is never required.
|
||||
--
|
||||
-- The password hashes can be given instead of passwords when supplying credentials;
|
||||
-- The password hashes can be given instead of passwords when supplying credentials;
|
||||
-- this is done by using the <code>smbhash</code> argument. Either a pair of hashes
|
||||
-- can be passed, in the form of Lanman:NTLM, or a single hash, which is assumed to
|
||||
-- be NTLM.
|
||||
-- be NTLM.
|
||||
--
|
||||
-- Authentication:
|
||||
-- There are four types of authentication. Confusingly, these have the same names as
|
||||
-- stored hashes, but only slight relationships. The four types are Lanmanv1, NTLMv1,
|
||||
-- Lanmanv2, and NTLMv2. By default, Lanmanv1 and NTLMv1 are used together in most
|
||||
-- applications. These Nmap scripts default to NTLMv1 alone, except in special cases,
|
||||
-- but it can be overridden by the user.
|
||||
-- stored hashes, but only slight relationships. The four types are Lanmanv1, NTLMv1,
|
||||
-- Lanmanv2, and NTLMv2. By default, Lanmanv1 and NTLMv1 are used together in most
|
||||
-- applications. These Nmap scripts default to NTLMv1 alone, except in special cases,
|
||||
-- but it can be overridden by the user.
|
||||
--
|
||||
-- Lanmanv1 and NTLMv1 both use DES for their response. The DES mixes a server challenge
|
||||
-- with the hash (Lanman hash for Lanmanv1 response and NTLMv1 hash for NTLM response).
|
||||
-- The way the challenge is DESed with the hashes is identical for Lanmanv1 and NTLMv1,
|
||||
-- the only difference is the starting hash (Lanman vs NTLM).
|
||||
-- with the hash (Lanman hash for Lanmanv1 response and NTLMv1 hash for NTLM response).
|
||||
-- The way the challenge is DESed with the hashes is identical for Lanmanv1 and NTLMv1,
|
||||
-- the only difference is the starting hash (Lanman vs NTLM).
|
||||
--
|
||||
-- Lanmanv2 and NTLMv2 both use HMAC-MD5 for their response. The HMAC-MD5 mixes a
|
||||
-- server challenge and a client challenge with the NTLM hash, in both cases. The
|
||||
-- Lanmanv2 and NTLMv2 both use HMAC-MD5 for their response. The HMAC-MD5 mixes a
|
||||
-- server challenge and a client challenge with the NTLM hash, in both cases. The
|
||||
-- difference between Lanmanv2 and NTLMv2 is the length of the client challenge;
|
||||
-- Lanmanv2 has a maximum client challenge of 8 bytes, whereas NTLMv2 doesn't limit
|
||||
-- the length of the client challenge.
|
||||
-- the length of the client challenge.
|
||||
--
|
||||
-- The primary advantage to the 'v2' protocols is the client challenge -- by
|
||||
-- The primary advantage to the 'v2' protocols is the client challenge -- by
|
||||
-- incorporating a client challenge, a malicious server can't use a precomputation
|
||||
-- attack.
|
||||
-- attack.
|
||||
--
|
||||
-- In addition to hashing the passwords, messages are also signed, by default, if a
|
||||
-- v1 protocol is being used (I (Ron Bowes) couldn't get signatures to work on v2
|
||||
-- In addition to hashing the passwords, messages are also signed, by default, if a
|
||||
-- v1 protocol is being used (I (Ron Bowes) couldn't get signatures to work on v2
|
||||
-- protocols; if anybody knows how I'd love to implement it).
|
||||
--
|
||||
--@args smbusername The SMB username to log in with. The forms "DOMAIN\username" and "username@DOMAIN"
|
||||
@@ -59,7 +59,7 @@
|
||||
-- accounts if the incorrect password is given. Although it's rare that the
|
||||
-- Administrator account can be locked out, in the off chance that it can, you could
|
||||
-- get yourself in trouble. To use a blank password, leave this parameter off
|
||||
-- altogether.
|
||||
-- altogether.
|
||||
--@args smbhash A password hash to use when logging in. This is given as a single hex string (32
|
||||
-- characters) or a pair of hex strings (both 32 characters, optionally separated by a
|
||||
-- single character). These hashes are the LanMan or NTLM hash of the user's password,
|
||||
@@ -71,14 +71,14 @@
|
||||
-- * <code>NTLMv1</code>: Sends NTLMv1 only (default).
|
||||
-- * <code>v2</code>: Sends LMv2 and NTLMv2.
|
||||
-- * <code>LMv2</code>: Sends LMv2 only.
|
||||
-- * <code>NTLMv2</code>: Doesn't exist; the protocol doesn't support NTLMv2 alone.
|
||||
-- The default, <code>NTLMv1</code>, is a pretty decent compromise between security and
|
||||
-- compatibility. If you are paranoid, you might want to use <code>v2</code> or
|
||||
-- <code>lmv2</code> for this. (Actually, if you're paranoid, you should be avoiding this
|
||||
-- protocol altogether!). If you're using an extremely old system, you might need to set
|
||||
-- this to <code>v1</code> or <code>lm</code>, which are less secure but more compatible.
|
||||
-- For information, see <code>smbauth.lua</code>.
|
||||
--@args smbnoguest Use to disable usage of the 'guest' account.
|
||||
-- * <code>NTLMv2</code>: Doesn't exist; the protocol doesn't support NTLMv2 alone.
|
||||
-- The default, <code>NTLMv1</code>, is a pretty decent compromise between security and
|
||||
-- compatibility. If you are paranoid, you might want to use <code>v2</code> or
|
||||
-- <code>lmv2</code> for this. (Actually, if you're paranoid, you should be avoiding this
|
||||
-- protocol altogether!). If you're using an extremely old system, you might need to set
|
||||
-- this to <code>v1</code> or <code>lm</code>, which are less secure but more compatible.
|
||||
-- For information, see <code>smbauth.lua</code>.
|
||||
--@args smbnoguest Use to disable usage of the 'guest' account.
|
||||
|
||||
local bin = require "bin"
|
||||
local nmap = require "nmap"
|
||||
@@ -136,17 +136,17 @@ end
|
||||
-- * registry[ip]['smbaccounts'] => array of table containing 'username', 'password', and 'is_admin'
|
||||
--
|
||||
-- The final place, 'smbaccount', is reserved for the "best" account. This is an administrator
|
||||
-- account, if one's found; otherwise, it's the first account discovered that isn't <code>guest</code>.
|
||||
-- account, if one's found; otherwise, it's the first account discovered that isn't <code>guest</code>.
|
||||
--
|
||||
-- This has to be called while no SMB connections are made, since it potentially makes its own connection.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@param username The username to add.
|
||||
--@param domain The domain to add.
|
||||
--@param password The password to add.
|
||||
--@param password_hash The password hash to add.
|
||||
--@param host The host object.
|
||||
--@param username The username to add.
|
||||
--@param domain The domain to add.
|
||||
--@param password The password to add.
|
||||
--@param password_hash The password hash to add.
|
||||
--@param hash_type The hash type to use.
|
||||
--@param is_admin [optional] Set to 'true' the account is known to be an administrator.
|
||||
--@param is_admin [optional] Set to 'true' the account is known to be an administrator.
|
||||
function add_account(host, username, domain, password, password_hash, hash_type, is_admin)
|
||||
-- Save the username in a global list -- TODO: restore this
|
||||
-- if(nmap.registry.usernames == nil) then
|
||||
@@ -216,11 +216,11 @@ function add_account(host, username, domain, password, password_hash, hash_type,
|
||||
end
|
||||
|
||||
---Retrieve the current set of credentials set in the registry. If these fail, <code>next_credentials</code> should be
|
||||
-- called.
|
||||
-- called.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@param host The host object.
|
||||
--@return (result, username, domain, password, password_hash, hash_type) If result is false, username is an error message. Otherwise, username and password are
|
||||
-- the current username and password that should be used.
|
||||
-- the current username and password that should be used.
|
||||
function get_account(host)
|
||||
if(host.registry['smbindex'] == nil) then
|
||||
host.registry['smbindex'] = 1
|
||||
@@ -237,9 +237,9 @@ function get_account(host)
|
||||
end
|
||||
|
||||
---Create the account table with the anonymous and guest users, as well as the user given in the script's
|
||||
-- arguments, if there is one.
|
||||
-- arguments, if there is one.
|
||||
--
|
||||
--@param host The host object.
|
||||
--@param host The host object.
|
||||
function init_account(host)
|
||||
-- Don't run this more than once for each host
|
||||
if(host.registry['smbaccounts'] ~= nil) then
|
||||
@@ -327,8 +327,8 @@ end
|
||||
|
||||
---Generate the Lanman v1 hash (LMv1). The generated hash is incredibly easy to reverse, because the input
|
||||
-- is padded or truncated to 14 characters, then split into two 7-character strings. Each of these strings
|
||||
-- are used as a key to encrypt the string, "KGS!@#$%" in DES. Because the keys are no longer than
|
||||
-- 7-characters long, it's pretty trivial to bruteforce them.
|
||||
-- are used as a key to encrypt the string, "KGS!@#$%" in DES. Because the keys are no longer than
|
||||
-- 7-characters long, it's pretty trivial to bruteforce them.
|
||||
--
|
||||
--@param password the password to hash
|
||||
--@return (status, hash) If status is true, the hash is returned; otherwise, an error message is returned.
|
||||
@@ -364,7 +364,7 @@ local function lm_create_hash(password)
|
||||
end
|
||||
|
||||
---Generate the NTLMv1 hash. This hash is quite a bit better than LMv1, and is far easier to generate. Basically,
|
||||
-- it's the MD4 of the Unicode password.
|
||||
-- it's the MD4 of the Unicode password.
|
||||
--
|
||||
--@param password the password to hash
|
||||
--@return (status, hash) If status is true, the hash is returned; otherwise, an error message is returned.
|
||||
@@ -376,12 +376,12 @@ function ntlm_create_hash(password)
|
||||
return true, openssl.md4(to_unicode(password))
|
||||
end
|
||||
|
||||
---Create the Lanman response to send back to the server. To do this, the Lanman password is padded to 21
|
||||
---Create the Lanman response to send back to the server. To do this, the Lanman password is padded to 21
|
||||
-- characters and split into three 7-character strings. Each of those strings is used as a key to encrypt
|
||||
-- the server challenge. The three encrypted strings are concatenated and returned.
|
||||
-- the server challenge. The three encrypted strings are concatenated and returned.
|
||||
--
|
||||
--@param lanman The LMv1 hash
|
||||
--@param challenge The server's challenge.
|
||||
--@param challenge The server's challenge.
|
||||
--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
|
||||
function lm_create_response(lanman, challenge)
|
||||
if(have_ssl ~= true) then
|
||||
@@ -406,7 +406,7 @@ function lm_create_response(lanman, challenge)
|
||||
key3 = openssl.DES_string_to_key(str3)
|
||||
|
||||
-- Print a warning message if a blank challenge is received, and create a phony challenge. A blank challenge is
|
||||
-- invalid in the protocol, and causes some versions of OpenSSL to abort with no possible error handling.
|
||||
-- invalid in the protocol, and causes some versions of OpenSSL to abort with no possible error handling.
|
||||
if(challenge == "") then
|
||||
stdnse.print_debug(1, "SMB: ERROR: Server returned invalid (blank) challenge value (should be 8 bytes); failing login to avoid OpenSSL crash.")
|
||||
challenge = "AAAAAAAA"
|
||||
@@ -419,10 +419,10 @@ function lm_create_response(lanman, challenge)
|
||||
end
|
||||
|
||||
---Create the NTLM response to send back to the server. This is actually done the exact same way as the Lanman hash,
|
||||
-- so I call the <code>Lanman</code> function.
|
||||
-- so I call the <code>Lanman</code> function.
|
||||
--
|
||||
--@param ntlm The NTLMv1 hash
|
||||
--@param challenge The server's challenge.
|
||||
--@param challenge The server's challenge.
|
||||
--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
|
||||
function ntlm_create_response(ntlm, challenge)
|
||||
if(have_ssl ~= true) then
|
||||
@@ -432,12 +432,12 @@ function ntlm_create_response(ntlm, challenge)
|
||||
return lm_create_response(ntlm, challenge)
|
||||
end
|
||||
|
||||
---Create the NTLM mac key, which is used for message signing. For basic authentication, this is the md4 of the
|
||||
---Create the NTLM mac key, which is used for message signing. For basic authentication, this is the md4 of the
|
||||
-- NTLM hash, concatenated with the response hash; for extended authentication, this is just the md4 of the NTLM
|
||||
-- hash.
|
||||
--@param ntlm_hash The NTLM hash.
|
||||
--@param ntlm_response The NTLM response.
|
||||
--@param is_extended Should be set if extended security negotiations are being used.
|
||||
-- hash.
|
||||
--@param ntlm_hash The NTLM hash.
|
||||
--@param ntlm_response The NTLM response.
|
||||
--@param is_extended Should be set if extended security negotiations are being used.
|
||||
function ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended)
|
||||
if(have_ssl ~= true) then
|
||||
return false, "SMB: OpenSSL not present"
|
||||
@@ -449,12 +449,12 @@ function ntlm_create_mac_key(ntlm_hash, ntlm_response, is_extended)
|
||||
end
|
||||
end
|
||||
|
||||
---Create the LM mac key, which is used for message signing. For basic authentication, it's the first 8 bytes
|
||||
-- of the lanman hash, followed by 8 null bytes, followed by the lanman response; for extended authentication,
|
||||
-- this is just the first 8 bytes of the lanman hash followed by 8 null bytes.
|
||||
--@param lm_hash The NTLM hash.
|
||||
--@param lm_response The NTLM response.
|
||||
--@param is_extended Should be set if extended security negotiations are being used.
|
||||
---Create the LM mac key, which is used for message signing. For basic authentication, it's the first 8 bytes
|
||||
-- of the lanman hash, followed by 8 null bytes, followed by the lanman response; for extended authentication,
|
||||
-- this is just the first 8 bytes of the lanman hash followed by 8 null bytes.
|
||||
--@param lm_hash The NTLM hash.
|
||||
--@param lm_response The NTLM response.
|
||||
--@param is_extended Should be set if extended security negotiations are being used.
|
||||
function lm_create_mac_key(lm_hash, lm_response, is_extended)
|
||||
if(have_ssl ~= true) then
|
||||
return false, "SMB: OpenSSL not present"
|
||||
@@ -467,13 +467,13 @@ function lm_create_mac_key(lm_hash, lm_response, is_extended)
|
||||
end
|
||||
end
|
||||
|
||||
---Create the NTLMv2 hash, which is based on the NTLMv1 hash (for easy upgrading), the username, and the domain.
|
||||
-- Essentially, the NTLM hash is used as a HMAC-MD5 key, which is used to hash the unicode domain concatenated
|
||||
-- with the unicode username.
|
||||
---Create the NTLMv2 hash, which is based on the NTLMv1 hash (for easy upgrading), the username, and the domain.
|
||||
-- Essentially, the NTLM hash is used as a HMAC-MD5 key, which is used to hash the unicode domain concatenated
|
||||
-- with the unicode username.
|
||||
--
|
||||
--@param ntlm The NTLMv1 hash.
|
||||
--@param username The username we're using.
|
||||
--@param domain The domain.
|
||||
--@param ntlm The NTLMv1 hash.
|
||||
--@param username The username we're using.
|
||||
--@param domain The domain.
|
||||
--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
|
||||
function ntlmv2_create_hash(ntlm, username, domain)
|
||||
if(have_ssl ~= true) then
|
||||
@@ -488,8 +488,8 @@ function ntlmv2_create_hash(ntlm, username, domain)
|
||||
return true, openssl.hmac("MD5", ntlm, username .. domain)
|
||||
end
|
||||
|
||||
---Create the LMv2 response, which can be sent back to the server. This is identical to the <code>NTLMv2</code> function,
|
||||
-- except that it uses an 8-byte client challenge.
|
||||
---Create the LMv2 response, which can be sent back to the server. This is identical to the <code>NTLMv2</code> function,
|
||||
-- except that it uses an 8-byte client challenge.
|
||||
--
|
||||
-- The reason for LMv2 is a long and twisted story. Well, not really. The reason is basically that the v1 hashes
|
||||
-- are always 24-bytes, and some servers expect 24 bytes, but the NTLMv2 hash is more than 24 bytes. So, the only
|
||||
@@ -498,9 +498,9 @@ end
|
||||
-- learned something
|
||||
--
|
||||
--@param ntlm The NVLMv1 hash.
|
||||
--@param username The username we're using.
|
||||
--@param domain The domain.
|
||||
--@param challenge The server challenge.
|
||||
--@param username The username we're using.
|
||||
--@param domain The domain.
|
||||
--@param challenge The server challenge.
|
||||
--@return (status, response) If status is true, the response is returned; otherwise, an error message is returned.
|
||||
function lmv2_create_response(ntlm, username, domain, challenge)
|
||||
if(have_ssl ~= true) then
|
||||
@@ -511,14 +511,14 @@ function lmv2_create_response(ntlm, username, domain, challenge)
|
||||
end
|
||||
|
||||
---Create the NTLMv2 response, which can be sent back to the server. This is done by using the HMAC-MD5 algorithm
|
||||
-- with the NTLMv2 hash as a key, and the server challenge concatenated with the client challenge for the data.
|
||||
-- with the NTLMv2 hash as a key, and the server challenge concatenated with the client challenge for the data.
|
||||
-- The resulting hash is concatenated with the client challenge and returned.
|
||||
--
|
||||
-- The "proper" implementation for this uses a certain structure for the client challenge, involving the time
|
||||
-- and computer name and stuff (if you don't do this, Wireshark tells you it's a malformed packet). In my tests,
|
||||
-- and computer name and stuff (if you don't do this, Wireshark tells you it's a malformed packet). In my tests,
|
||||
-- however, I couldn't get Vista to recognize a client challenge longer than 24 bytes, and this structure was
|
||||
-- guaranteed to be much longer than 24 bytes. So, I just use a random string generated by OpenSSL. I've tested
|
||||
-- it on every Windows system from Windows 2000 to Windows Vista, and it has always worked.
|
||||
-- it on every Windows system from Windows 2000 to Windows Vista, and it has always worked.
|
||||
function ntlmv2_create_response(ntlm, username, domain, challenge, client_challenge_length)
|
||||
if(have_ssl ~= true) then
|
||||
return false, "SMB: OpenSSL not present"
|
||||
@@ -533,21 +533,21 @@ end
|
||||
|
||||
---Generate the Lanman and NTLM password hashes. The password itself is taken from the function parameters,
|
||||
-- the nmap arguments, and the registry (in that order). If no password is set, then the password hash
|
||||
-- is used (which is read from all the usual places). If neither is set, then a blank password is used.
|
||||
-- is used (which is read from all the usual places). If neither is set, then a blank password is used.
|
||||
--
|
||||
-- The output passwords are hashed based on the hash type.
|
||||
-- The output passwords are hashed based on the hash type.
|
||||
--
|
||||
--@param ip The ip address of the host, used for registry lookups.
|
||||
--@param username The username, which is used for v2 passwords.
|
||||
--@param domain The username, which is used for v2 passwords.
|
||||
--@param password [optional] The overriding password.
|
||||
--@param password_hash [optional] The overriding password hash. Shouldn't be set if password is set.
|
||||
--@param ip The ip address of the host, used for registry lookups.
|
||||
--@param username The username, which is used for v2 passwords.
|
||||
--@param domain The username, which is used for v2 passwords.
|
||||
--@param password [optional] The overriding password.
|
||||
--@param password_hash [optional] The overriding password hash. Shouldn't be set if password is set.
|
||||
--@param challenge The server challenge.
|
||||
--@param hash_type The way in which to hash the password.
|
||||
--@param hash_type The way in which to hash the password.
|
||||
--@param is_extended Set to 'true' if extended security negotiations are being used (this has to be known for the
|
||||
-- message-signing key to be generated properly).
|
||||
--@return (lm_response, ntlm_response, mac_key) The two strings that can be sent directly back to the server,
|
||||
-- and the mac_key, which is used for message signing.
|
||||
-- message-signing key to be generated properly).
|
||||
--@return (lm_response, ntlm_response, mac_key) The two strings that can be sent directly back to the server,
|
||||
-- and the mac_key, which is used for message signing.
|
||||
function get_password_response(ip, username, domain, password, password_hash, hash_type, challenge, is_extended)
|
||||
local status
|
||||
local lm_hash = nil
|
||||
@@ -601,7 +601,7 @@ function get_password_response(ip, username, domain, password, password_hash, ha
|
||||
-- Output what we've got so far
|
||||
stdnse.print_debug(2, "SMB: Lanman hash: %s", stdnse.tohex(lm_hash))
|
||||
stdnse.print_debug(2, "SMB: NTLM hash: %s", stdnse.tohex(ntlm_hash))
|
||||
|
||||
|
||||
-- Hash the password the way the user wants
|
||||
if(hash_type == "v1") then
|
||||
-- LM and NTLM are hashed with their respective algorithms
|
||||
@@ -646,7 +646,7 @@ function get_password_response(ip, username, domain, password, password_hash, ha
|
||||
else
|
||||
stdnse.print_debug(1, "SMB: No login type specified, using default (NTLM)")
|
||||
end
|
||||
|
||||
|
||||
status, lm_response = ntlm_create_response(ntlm_hash, challenge)
|
||||
status, ntlm_response = ntlm_create_response(ntlm_hash, challenge)
|
||||
|
||||
@@ -665,10 +665,10 @@ function get_security_blob(security_blob, ip, username, domain, password, passwo
|
||||
|
||||
if(security_blob == nil) then
|
||||
-- If security_blob is nil, this is the initial packet
|
||||
new_blob = bin.pack("<zIILL",
|
||||
new_blob = bin.pack("<zIILL",
|
||||
"NTLMSSP", -- Identifier
|
||||
NTLMSSP_NEGOTIATE, -- Type
|
||||
flags, -- Flags
|
||||
flags, -- Flags
|
||||
0, -- Calling workstation domain
|
||||
0 -- Calling workstation name
|
||||
)
|
||||
@@ -721,7 +721,7 @@ function get_security_blob(security_blob, ip, username, domain, password, passwo
|
||||
lanman,
|
||||
ntlm,
|
||||
session_key)
|
||||
|
||||
|
||||
return true, new_blob, mac_key
|
||||
end
|
||||
|
||||
@@ -731,7 +731,7 @@ function get_host_info_from_security_blob(security_blob)
|
||||
local ntlm_challenge = {}
|
||||
--local pos, identifier, message_type, domain_length, domain_max, domain_offset, server_flags, challenge, reserved, target_info_length, target_info_max, target_info_offset = bin.unpack("<A8ISSIILLSSI", security_blob)
|
||||
local pos, identifier, message_type, domain_length, domain_max, domain_offset, server_flags, challenge, reserved, target_info_length, target_info_max, target_info_offset = bin.unpack("<A8ISSIILLSSI", security_blob)
|
||||
|
||||
|
||||
-- Do some validation on the NTLMSSP message
|
||||
if ( identifier ~= "NTLMSSP\0" ) then
|
||||
stdnse.print_debug( 1, "SMB: Invalid NTLM challenge message: unexpected signature." )
|
||||
@@ -741,7 +741,7 @@ function get_host_info_from_security_blob(security_blob)
|
||||
stdnse.print_debug( 1, "SMB: Invalid NTLM challenge message: unexpected message type: %d.", message_type )
|
||||
return false, "Invalid message type in NTLM challenge message"
|
||||
end
|
||||
|
||||
|
||||
-- Parse the TargetName data (i.e. the server authentication realm)
|
||||
if ( domain_length > 0 ) then
|
||||
local length = domain_length
|
||||
@@ -750,10 +750,10 @@ function get_host_info_from_security_blob(security_blob)
|
||||
pos, target_realm = bin.unpack( string.format( "A%d", length ), security_blob, pos )
|
||||
ntlm_challenge[ "target_realm" ] = from_unicode( target_realm )
|
||||
end
|
||||
|
||||
|
||||
-- Parse the TargetInfo data (Wireshark calls this the "Address List")
|
||||
if ( target_info_length > 0 ) then
|
||||
|
||||
|
||||
-- Definition of AvId values (IDs for AV_PAIR (attribute-value pair) structures),
|
||||
-- as definied by the NTLM Authentication Protocol specification [MS-NLMP].
|
||||
local NTLM_AV_ID_VALUES = {
|
||||
@@ -779,20 +779,20 @@ function get_host_info_from_security_blob(security_blob)
|
||||
[NTLM_AV_ID_VALUES.MsvAvDnsTreeName] = "dns_forest_name",
|
||||
[NTLM_AV_ID_VALUES.MsvAvTimestamp] = "timestamp",
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
local length = target_info_length
|
||||
local pos = target_info_offset + 1 -- +1 to convert to Lua's 1-based indexes
|
||||
local target_info
|
||||
pos, target_info = bin.unpack( string.format( "A%d", length ), security_blob, pos )
|
||||
|
||||
|
||||
pos = 1 -- reset pos to 1, since we'll be working out of just the target_info
|
||||
repeat
|
||||
local value, av_id, av_len
|
||||
pos, av_id, av_len = bin.unpack( "<SS", target_info, pos )
|
||||
pos, value = bin.unpack( string.format( "A%d", av_len ), target_info, pos )
|
||||
local friendly_name = NTLM_AV_ID_NAMES[ av_id ]
|
||||
|
||||
|
||||
if ( av_id == NTLM_AV_ID_VALUES.MsvAvEOL ) then
|
||||
break
|
||||
elseif ( av_id == NTLM_AV_ID_VALUES.MsvAvTimestamp ) then
|
||||
@@ -803,16 +803,16 @@ function get_host_info_from_security_blob(security_blob)
|
||||
end
|
||||
until ( pos >= #target_info )
|
||||
end
|
||||
|
||||
|
||||
return ntlm_challenge
|
||||
end
|
||||
|
||||
---Create an 8-byte message signature that's sent with all SMB packets.
|
||||
---Create an 8-byte message signature that's sent with all SMB packets.
|
||||
--
|
||||
--@param mac_key The key used for authentication. It's the concatination of the session key and the
|
||||
-- response hash.
|
||||
-- response hash.
|
||||
--@param data The packet to generate the signature for. This should be the packet that's about to be
|
||||
-- sent, except with the signature slot replaced with the sequence number.
|
||||
-- sent, except with the signature slot replaced with the sequence number.
|
||||
--@return The 8-byte signature. The signature is equal to the first eight bytes of md5(mac_key .. smb_data)
|
||||
function calculate_signature(mac_key, data)
|
||||
if(have_ssl) then
|
||||
|
||||
@@ -162,7 +162,7 @@ local SMTP_CMD = {
|
||||
-- Returns a domain to be used in the SMTP commands that need it. If the
|
||||
-- user specified one through the script argument <code>smtp.domain</code>
|
||||
-- this function will return it. Otherwise it will try to find the domain
|
||||
-- from the typed hostname and from the rDNS name. If it still can't find
|
||||
-- from the typed hostname and from the rDNS name. If it still can't find
|
||||
-- one it will return the nmap.scanme.org by default.
|
||||
--
|
||||
-- @param host The host table
|
||||
@@ -290,7 +290,7 @@ connect = function(host, port, opts)
|
||||
if opts.ssl then
|
||||
local socket, _, _, ret = comm.tryssl(host, port, '', opts)
|
||||
if not socket then
|
||||
return socket, (ERROR_MESSAGES[ret] or 'unspecified error')
|
||||
return socket, (ERROR_MESSAGES[ret] or 'unspecified error')
|
||||
end
|
||||
return socket, ret
|
||||
else
|
||||
@@ -324,7 +324,7 @@ end
|
||||
|
||||
--- Switches the plain text connection to be protected by the TLS protocol
|
||||
-- by using the SMTP STARTTLS command.
|
||||
--
|
||||
--
|
||||
-- The socket will be reconnected by using SSL. On network errors or if the
|
||||
-- SMTP command fails, the connection will be closed and the socket cleared.
|
||||
--
|
||||
@@ -334,7 +334,7 @@ end
|
||||
-- to the client's STARTTLS command, or an error message on failures.
|
||||
starttls = function(socket)
|
||||
local st, reply, ret
|
||||
|
||||
|
||||
st, reply = query(socket, "STARTTLS")
|
||||
if not st then
|
||||
return st, reply
|
||||
@@ -356,7 +356,7 @@ starttls = function(socket)
|
||||
end
|
||||
|
||||
--- Sends the EHLO command to the SMTP server.
|
||||
--
|
||||
--
|
||||
-- On network errors or if the SMTP command fails, the connection
|
||||
-- will be closed and the socket cleared.
|
||||
--
|
||||
@@ -382,7 +382,7 @@ ehlo = function(socket, domain)
|
||||
end
|
||||
|
||||
--- Sends the HELP command to the SMTP server.
|
||||
--
|
||||
--
|
||||
-- On network errors or if the SMTP command fails, the connection
|
||||
-- will be closed and the socket cleared.
|
||||
--
|
||||
@@ -565,7 +565,7 @@ end
|
||||
verify = function(socket, mailbox)
|
||||
local st, ret, response
|
||||
st, response = query(socket, "VRFY", mailbox)
|
||||
|
||||
|
||||
st, ret = check_reply("VRFY", response)
|
||||
if not st then
|
||||
quit(socket)
|
||||
@@ -596,7 +596,7 @@ end
|
||||
-- error message on failures.
|
||||
|
||||
login = function(socket, username, password, mech)
|
||||
assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5"
|
||||
assert(mech == "LOGIN" or mech == "PLAIN" or mech == "CRAM-MD5"
|
||||
or mech == "DIGEST-MD5" or mech == "NTLM",
|
||||
("Unsupported authentication mechanism (%s)"):format(mech or "nil"))
|
||||
local status, response = query(socket, "AUTH", mech)
|
||||
@@ -634,16 +634,16 @@ login = function(socket, username, password, mech)
|
||||
end
|
||||
return false, response
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
if ( mech == "NTLM" ) then
|
||||
-- sniffed of the wire, seems to always be the same
|
||||
-- decodes to some NTLMSSP blob greatness
|
||||
status, response = query(socket, "TlRMTVNTUAABAAAAB7IIogYABgA3AAAADwAPACgAAAAFASgKAAAAD0FCVVNFLUFJUi5MT0NBTERPTUFJTg==")
|
||||
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
|
||||
if ( not(status) ) then return false, "ERROR: Failed to receieve NTLM challenge" end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local chall = response:match("^334 (.*)")
|
||||
chall = (chall and base64.dec(chall))
|
||||
if (not(chall)) then return false, "ERROR: Failed to retrieve challenge" end
|
||||
@@ -653,12 +653,12 @@ login = function(socket, username, password, mech)
|
||||
local mech_params = { username, password, chall, "smtp" }
|
||||
local auth_data = sasl.Helper:new(mech):encode(table.unpack(mech_params))
|
||||
auth_data = base64.enc(auth_data)
|
||||
|
||||
|
||||
status, response = query(socket, auth_data)
|
||||
if ( not(status) ) then
|
||||
return false, ("ERROR: Failed to authenticate using SASL %s"):format(mech)
|
||||
end
|
||||
|
||||
|
||||
if ( mech == "DIGEST-MD5" ) then
|
||||
local rspauth = response:match("^334 (.*)")
|
||||
if ( rspauth ) then
|
||||
@@ -666,9 +666,9 @@ login = function(socket, username, password, mech)
|
||||
status, response = query(socket,"")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( response:match("^235") ) then return true, "Login success" end
|
||||
|
||||
|
||||
return false, response
|
||||
end
|
||||
|
||||
|
||||
@@ -29,26 +29,26 @@ tagEncoder['table'] = function(self, val)
|
||||
local oidStr = string.char(val[1]*40 + val[2])
|
||||
for i = 3, #val do
|
||||
oidStr = oidStr .. self.encode_oid_component(val[i])
|
||||
end
|
||||
return bin.pack("HAA", '06', self.encodeLength(#oidStr), oidStr)
|
||||
end
|
||||
return bin.pack("HAA", '06', self.encodeLength(#oidStr), oidStr)
|
||||
|
||||
elseif (val._snmp == '40') then -- ipAddress
|
||||
return bin.pack("HC4", '40 04', table.unpack(val))
|
||||
|
||||
|
||||
-- counter or gauge or timeticks or opaque
|
||||
elseif (val._snmp == '41' or val._snmp == '42' or val._snmp == '43' or val._snmp == '44') then
|
||||
local val = self:encodeInt(val[1])
|
||||
return bin.pack("HAA", val._snmp, self.encodeLength(#val), val)
|
||||
end
|
||||
|
||||
|
||||
local encVal = ""
|
||||
for _, v in ipairs(val) do
|
||||
encVal = encVal .. self:encode(v) -- todo: buffer?
|
||||
end
|
||||
|
||||
local tableType = bin.pack("H", "30")
|
||||
if (val["_snmp"]) then
|
||||
tableType = bin.pack("H", val["_snmp"])
|
||||
if (val["_snmp"]) then
|
||||
tableType = bin.pack("H", val["_snmp"])
|
||||
end
|
||||
return bin.pack('AAA', tableType, self.encodeLength(#encVal), encVal)
|
||||
end
|
||||
@@ -62,14 +62,14 @@ function encode(val)
|
||||
local vtype = type(val)
|
||||
local encoder = asn1.ASN1Encoder:new()
|
||||
encoder:registerTagEncoders( tagEncoder )
|
||||
|
||||
|
||||
|
||||
local encVal = encoder:encode(val)
|
||||
|
||||
|
||||
if encVal then
|
||||
return encVal
|
||||
end
|
||||
|
||||
|
||||
return ''
|
||||
end
|
||||
|
||||
@@ -84,7 +84,7 @@ local tagDecoder = {}
|
||||
-- TOOD: Figure out how to remove these dependancies
|
||||
tagDecoder["A2"] = function( self, encStr, elen, pos )
|
||||
local seq = {}
|
||||
|
||||
|
||||
pos, seq = self:decodeSeq(encStr, elen, pos)
|
||||
seq._snmp = "A2"
|
||||
return pos, seq
|
||||
@@ -106,7 +106,7 @@ end
|
||||
-- @return The decoded value(s).
|
||||
function decode(encStr, pos)
|
||||
local decoder = asn1.ASN1Decoder:new()
|
||||
|
||||
|
||||
if ( #tagDecoder == 0 ) then
|
||||
decoder:registerBaseDecoders()
|
||||
-- Application specific tags
|
||||
@@ -129,8 +129,8 @@ function decode(encStr, pos)
|
||||
tagDecoder["A6"] = decoder.decoder["30"] -- InformRequest-PDU (not implemented here yet)
|
||||
tagDecoder["A7"] = decoder.decoder["30"] -- SNMPv2-Trap-PDU (not implemented here yet)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
decoder:registerTagDecoders( tagDecoder )
|
||||
|
||||
return decoder:decode( encStr, pos )
|
||||
@@ -170,7 +170,7 @@ function buildPacket(PDU, version, commStr)
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
---
|
||||
-- Create an SNMP Get Request PDU.
|
||||
-- @param options A table containing the following fields:
|
||||
-- * <code>"reqId"</code>: Request ID.
|
||||
@@ -190,7 +190,7 @@ function buildGetRequest(options, ...)
|
||||
req[1] = options.reqId
|
||||
req[2] = options.err
|
||||
req[3] = options.errIdx
|
||||
|
||||
|
||||
local payload = {}
|
||||
for i=1, select('#', ...) do
|
||||
payload[i] = {}
|
||||
@@ -205,7 +205,7 @@ function buildGetRequest(options, ...)
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
---
|
||||
-- Create an SNMP Get Next Request PDU.
|
||||
-- @param options A table containing the following fields:
|
||||
-- * <code>"reqId"</code>: Request ID.
|
||||
@@ -225,7 +225,7 @@ function buildGetNextRequest(options, ...)
|
||||
req[1] = options.reqId
|
||||
req[2] = options.err
|
||||
req[3] = options.errIdx
|
||||
|
||||
|
||||
local payload = {}
|
||||
for i=1, select('#', ...) do
|
||||
payload[i] = {}
|
||||
@@ -239,7 +239,7 @@ function buildGetNextRequest(options, ...)
|
||||
return req
|
||||
end
|
||||
|
||||
---
|
||||
---
|
||||
-- Create an SNMP Set Request PDU.
|
||||
--
|
||||
-- Takes one OID/value pair or an already prepared table.
|
||||
@@ -266,7 +266,7 @@ function buildSetRequest(options, oid, value)
|
||||
|
||||
if (type(value) == "table") then
|
||||
req[4] = value
|
||||
else
|
||||
else
|
||||
local payload = {}
|
||||
if (type(oid) == "string") then
|
||||
payload[1] = str2oid(oid)
|
||||
@@ -280,13 +280,13 @@ function buildSetRequest(options, oid, value)
|
||||
return req
|
||||
end
|
||||
|
||||
---
|
||||
---
|
||||
-- Create an SNMP Trap PDU.
|
||||
-- @return Table representing PDU
|
||||
function buildTrap(enterpriseOid, agentIp, genTrap, specTrap, timeStamp)
|
||||
local req = {}
|
||||
req._snmp = 'A4'
|
||||
if (type(enterpriseOid) == "string") then
|
||||
if (type(enterpriseOid) == "string") then
|
||||
req[1] = str2oid(enterpriseOid)
|
||||
else
|
||||
req[1] = enterpriseOid
|
||||
@@ -308,7 +308,7 @@ function buildTrap(enterpriseOid, agentIp, genTrap, specTrap, timeStamp)
|
||||
return req
|
||||
end
|
||||
|
||||
---
|
||||
---
|
||||
-- Create an SNMP Get Response PDU.
|
||||
--
|
||||
-- Takes one OID/value pair or an already prepared table.
|
||||
@@ -319,7 +319,7 @@ end
|
||||
-- @param oid Object identifiers of object to be sent back.
|
||||
-- @param value If given a table, use the table instead of OID/value pair.
|
||||
-- @return Table representing PDU.
|
||||
function buildGetResponse(options, oid, value)
|
||||
function buildGetResponse(options, oid, value)
|
||||
if not options then options = {} end
|
||||
|
||||
-- if really a response, should use reqId of request!
|
||||
@@ -335,7 +335,7 @@ function buildGetResponse(options, oid, value)
|
||||
|
||||
if (type(value) == "table") then
|
||||
resp[4] = value
|
||||
else
|
||||
else
|
||||
|
||||
local payload = {}
|
||||
if (type(oid) == "string") then
|
||||
@@ -350,7 +350,7 @@ function buildGetResponse(options, oid, value)
|
||||
return resp
|
||||
end
|
||||
|
||||
---
|
||||
---
|
||||
-- Transforms a string into an object identifier table.
|
||||
-- @param oidStr Object identifier as string, for example
|
||||
-- <code>"1.3.6.1.2.1.1.1.0"</code>.
|
||||
@@ -407,7 +407,7 @@ function fetchResponseValues(resp)
|
||||
_, resp = decode(resp)
|
||||
end
|
||||
|
||||
if (type(resp) ~= "table") then
|
||||
if (type(resp) ~= "table") then
|
||||
return {}
|
||||
end
|
||||
|
||||
@@ -452,7 +452,7 @@ function fetchFirst(response)
|
||||
|
||||
if type(result) == "table" and result[1] and result[1][1] then
|
||||
return result[1][1]
|
||||
else
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
@@ -465,13 +465,13 @@ end
|
||||
-- @return status true on success, false on failure
|
||||
-- @return table containing <code>oid</code> and <code>value</code>
|
||||
function snmpWalk( socket, base_oid )
|
||||
|
||||
|
||||
local snmp_table = {}
|
||||
local oid = base_oid
|
||||
local status, err, payload
|
||||
|
||||
|
||||
while ( true ) do
|
||||
|
||||
|
||||
local value, response, snmpdata, options, item = nil, nil, nil, {}, {}
|
||||
payload = encode( buildPacket( buildGetNextRequest(options, oid) ) )
|
||||
|
||||
@@ -480,8 +480,8 @@ function snmpWalk( socket, base_oid )
|
||||
stdnse.print_debug("snmp.snmpWalk: Send failed")
|
||||
return false, err
|
||||
end
|
||||
|
||||
status, response = socket:receive_bytes(1)
|
||||
|
||||
status, response = socket:receive_bytes(1)
|
||||
if ( not( status ) ) then
|
||||
-- Unless we don't have a usefull error message, don't report it
|
||||
if ( response ~= "ERROR" ) then
|
||||
@@ -490,27 +490,27 @@ function snmpWalk( socket, base_oid )
|
||||
end
|
||||
return false, nil
|
||||
end
|
||||
|
||||
|
||||
snmpdata = fetchResponseValues( response )
|
||||
|
||||
|
||||
value = snmpdata[1][1]
|
||||
oid = snmpdata[1][2]
|
||||
|
||||
|
||||
if not oid:match( base_oid ) or base_oid == oid then
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
item.oid = oid
|
||||
item.value = value
|
||||
|
||||
|
||||
table.insert( snmp_table, item )
|
||||
|
||||
|
||||
end
|
||||
|
||||
snmp_table.baseoid = base_oid
|
||||
|
||||
return true, snmp_table
|
||||
|
||||
|
||||
end
|
||||
|
||||
return _ENV;
|
||||
|
||||
106
nselib/socks.lua
106
nselib/socks.lua
@@ -18,15 +18,15 @@ AuthMethod = {
|
||||
}
|
||||
|
||||
Request = {
|
||||
|
||||
|
||||
-- Class that handles the connection request to the server
|
||||
Connect = {
|
||||
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param auth_method table of requested authentication methods
|
||||
-- @return o instance on success, nil on failure
|
||||
new = function(self, auth_method)
|
||||
local o = {
|
||||
local o = {
|
||||
version = 5,
|
||||
auth_method = ( "table" ~= type(auth_method) and { auth_method } or auth_method )
|
||||
}
|
||||
@@ -34,7 +34,7 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string, so that it can be sent to the
|
||||
-- server.
|
||||
-- @return string containing the raw request
|
||||
@@ -42,15 +42,15 @@ Request = {
|
||||
local methods = ""
|
||||
for _, m in ipairs(self.auth_method) do
|
||||
methods = methods .. string.char(m)
|
||||
end
|
||||
end
|
||||
return bin.pack("Cp", self.version, methods)
|
||||
end,
|
||||
end,
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- Class that handles the authentication request to the server
|
||||
Authenticate = {
|
||||
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param auth_method number with the requested authentication method
|
||||
-- @param creds method specific table of credentials
|
||||
@@ -59,7 +59,7 @@ Request = {
|
||||
-- <code>username</code> and <code>password</code>
|
||||
-- @return o instance on success, nil on failure
|
||||
new = function(self, auth_method, creds)
|
||||
local o = {
|
||||
local o = {
|
||||
auth_method = auth_method,
|
||||
creds = creds
|
||||
}
|
||||
@@ -69,7 +69,7 @@ Request = {
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Converts the instance to string, so that it can be sent to the
|
||||
-- server.
|
||||
-- @return string containing the raw request
|
||||
@@ -87,16 +87,16 @@ Request = {
|
||||
return bin.pack("Cpp", version, username, password)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Response = {
|
||||
|
||||
|
||||
-- Class that handles the connection response
|
||||
Connect = {
|
||||
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return o instance on success, nil on failure
|
||||
@@ -108,7 +108,7 @@ Response = {
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the received data and populates member variables
|
||||
-- @return true on success, false on failure
|
||||
parse = function(self)
|
||||
@@ -116,21 +116,21 @@ Response = {
|
||||
return
|
||||
end
|
||||
local pos
|
||||
pos, self.version, self.method = bin.unpack("CC", self.data)
|
||||
pos, self.version, self.method = bin.unpack("CC", self.data)
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- Class that handles the authentication response
|
||||
Authenticate = {
|
||||
|
||||
|
||||
Status = {
|
||||
SUCCESS = 0,
|
||||
-- could be anything but zero
|
||||
FAIL = 1,
|
||||
},
|
||||
|
||||
|
||||
-- Creates a new instance of the class
|
||||
-- @param data string containing the data as received over the socket
|
||||
-- @return o instance on success, nil on failure
|
||||
@@ -142,7 +142,7 @@ Response = {
|
||||
return o
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the received data and populates member variables
|
||||
-- @return true on success, false on failure
|
||||
parse = function(self)
|
||||
@@ -150,25 +150,25 @@ Response = {
|
||||
return
|
||||
end
|
||||
local pos
|
||||
pos, self.version, self.status = bin.unpack("CC", self.data)
|
||||
pos, self.version, self.status = bin.unpack("CC", self.data)
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
-- checks if the authentication was successful or not
|
||||
-- @return true on success, false on failure
|
||||
isSuccess = function(self)
|
||||
return ( self.status == self.Status.SUCCESS )
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- A buffered socket implementation
|
||||
Socket =
|
||||
{
|
||||
{
|
||||
retries = 3,
|
||||
|
||||
|
||||
-- Creates a new socket instance
|
||||
-- @param host table containing the host table
|
||||
-- @param port table containing the port table
|
||||
@@ -176,8 +176,8 @@ Socket =
|
||||
-- <code>timeout</code> - socket timeout in ms
|
||||
-- @return o new instance of Socket
|
||||
new = function(self, host, port, options)
|
||||
local o = {
|
||||
host = host,
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
options = options or {}
|
||||
}
|
||||
@@ -187,7 +187,7 @@ Socket =
|
||||
o.Buffer = nil
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Connects the socket to the server
|
||||
-- @return status true on success false on failure
|
||||
-- @return err string containing error message on failure
|
||||
@@ -195,7 +195,7 @@ Socket =
|
||||
self.Socket:set_timeout(self.options.timeout or 10000)
|
||||
return self.Socket:connect( self.host, self.port )
|
||||
end,
|
||||
|
||||
|
||||
-- Closes an open connection.
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -203,7 +203,7 @@ Socket =
|
||||
close = function( self )
|
||||
return self.Socket:close()
|
||||
end,
|
||||
|
||||
|
||||
-- Opposed to the <code>socket:receive_bytes</code> function, that returns
|
||||
-- at least x bytes, this function returns the amount of bytes requested.
|
||||
--
|
||||
@@ -213,9 +213,9 @@ Socket =
|
||||
-- err containing error message if status is false
|
||||
recv = function( self, count )
|
||||
local status, data
|
||||
|
||||
|
||||
self.Buffer = self.Buffer or ""
|
||||
|
||||
|
||||
if ( #self.Buffer < count ) then
|
||||
status, data = self.Socket:receive_bytes( count - #self.Buffer )
|
||||
if ( not(status) or #data < count - #self.Buffer ) then
|
||||
@@ -223,13 +223,13 @@ Socket =
|
||||
end
|
||||
self.Buffer = self.Buffer .. data
|
||||
end
|
||||
|
||||
|
||||
data = self.Buffer:sub( 1, count )
|
||||
self.Buffer = self.Buffer:sub( count + 1)
|
||||
|
||||
return true, data
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
-- Sends data over the socket
|
||||
--
|
||||
-- @return Status (true or false).
|
||||
@@ -242,7 +242,7 @@ Socket =
|
||||
|
||||
-- The main script interface
|
||||
Helper = {
|
||||
|
||||
|
||||
-- Create a new instance of the class
|
||||
-- @param host table containing the host table
|
||||
-- @param port table containing the port table
|
||||
@@ -255,7 +255,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- Get the authentication method name by number
|
||||
-- @param method number containing the authentication method
|
||||
-- @return string containing the method name or Unknown
|
||||
@@ -267,31 +267,31 @@ Helper = {
|
||||
}
|
||||
return methods[method] or ("Unknown method (%d)"):format(method)
|
||||
end,
|
||||
|
||||
|
||||
-- Connects to the SOCKS server
|
||||
-- @param auth_method table containing the auth. methods to request
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response table containing the respons or err string on failure
|
||||
connect = function(self, auth_method)
|
||||
self.socket = Socket:new(self.host, self.port, self.options)
|
||||
self.socket = Socket:new(self.host, self.port, self.options)
|
||||
local status, err = self.socket:connect()
|
||||
if ( not(status) ) then
|
||||
return status, err
|
||||
end
|
||||
|
||||
|
||||
auth_method = auth_method or {AuthMethod.NONE, AuthMethod.GSSAPI, AuthMethod.USERPASS}
|
||||
status = self.socket:send( tostring(Request.Connect:new(auth_method)) )
|
||||
if ( not(status) ) then
|
||||
self.socket:close()
|
||||
return false, "Failed to send connection request to server"
|
||||
end
|
||||
|
||||
|
||||
local status, data = self.socket:recv(2)
|
||||
if ( not(status) ) then
|
||||
self.socket:close()
|
||||
return false, "Failed to receive connection response from server"
|
||||
end
|
||||
|
||||
|
||||
local response = Response.Connect:new(data)
|
||||
if ( not(response) ) then
|
||||
return false, "Failed to parse response from server"
|
||||
@@ -303,12 +303,12 @@ Helper = {
|
||||
if ( response.method == 0xFF ) then
|
||||
return false, "No acceptable authentication methods"
|
||||
end
|
||||
|
||||
|
||||
-- store the method so authenticate knows what to use
|
||||
self.auth_method = response.method
|
||||
return true, response
|
||||
end,
|
||||
|
||||
|
||||
-- Authenticates to the SOCKS server
|
||||
-- @param creds table containing authentication method specific fields
|
||||
-- currently only authentication method 2 (username and pass) is
|
||||
@@ -325,20 +325,20 @@ Helper = {
|
||||
if ( not(req) ) then
|
||||
return false, "Failed to create authentication request"
|
||||
end
|
||||
|
||||
|
||||
local status = self.socket:send(tostring(req))
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send authentication request"
|
||||
end
|
||||
|
||||
|
||||
if ( 2 == self.auth_method ) then
|
||||
local status, data = self.socket:recv(2)
|
||||
local auth = Response.Authenticate:new(data)
|
||||
|
||||
|
||||
if ( not(auth) ) then
|
||||
return false, "Failed to parse authentication response"
|
||||
end
|
||||
|
||||
|
||||
if ( auth:isSuccess() ) then
|
||||
return true, "Authentication was successfull"
|
||||
else
|
||||
@@ -353,7 +353,7 @@ Helper = {
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- The implementation is based on the following classes:
|
||||
-- * Request.Service
|
||||
-- - Contains necessary code to produce a service request
|
||||
--
|
||||
--
|
||||
-- * Request.Attributes
|
||||
-- - Contains necessary code to produce a attribute request
|
||||
--
|
||||
@@ -58,17 +58,17 @@ Reply = {
|
||||
o:parse(data)
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Parses the service reply raw packet data
|
||||
-- @param data string containing the raw reply as read from the socket
|
||||
parse = function(self, data)
|
||||
local pos
|
||||
local len_hi, len_lo
|
||||
|
||||
|
||||
pos, self.version, self.func, len_hi, len_lo = bin.unpack(">CCCS", data)
|
||||
self.len = bit.lshift(len_hi, 16) + len_lo
|
||||
pos, self.flags = bin.unpack(">S", data, pos)
|
||||
|
||||
|
||||
local neo_hi, neo_lo
|
||||
pos, neo_hi, neo_lo = bin.unpack(">CS", data, pos)
|
||||
self.next_extension_offset = bit.lshift(neo_hi, 16) + neo_lo
|
||||
@@ -76,10 +76,10 @@ Reply = {
|
||||
local lang_tag_len
|
||||
pos, self.xid, lang_tag_len = bin.unpack(">SS", data, pos)
|
||||
pos, self.lang_tag = bin.unpack("A" .. lang_tag_len, data, pos)
|
||||
|
||||
|
||||
local no_urls, reserved, url_len
|
||||
pos, self.error_code, no_urls = bin.unpack(">SS", data, pos)
|
||||
|
||||
|
||||
if ( no_urls > 0 ) then
|
||||
pos, reserved, self.url_lifetime, url_len = bin.unpack(">CSS", data, pos)
|
||||
|
||||
@@ -87,7 +87,7 @@ Reply = {
|
||||
pos, self.url, num_auths = bin.unpack("A" .. url_len .. "C", data, pos)
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Attempts to create an instance by reading data off the socket
|
||||
-- @param socket socket conected to the SRVLOC service
|
||||
-- @return new instance of the Reply.Service class
|
||||
@@ -96,7 +96,7 @@ Reply = {
|
||||
if ( not(status) ) then return end
|
||||
return Reply.Service:new(data)
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the url value from the reply
|
||||
-- @return uri string containing the reply url
|
||||
getUrl = function(self) return self.url end,
|
||||
@@ -152,21 +152,21 @@ Reply = {
|
||||
end,
|
||||
|
||||
--- Gets the attribute list
|
||||
-- @return attrib_list
|
||||
-- @return attrib_list
|
||||
getAttribList = function(self) return self.attrib_list end,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Request = {
|
||||
|
||||
|
||||
-- The attribute request
|
||||
Attribute = {
|
||||
|
||||
--- Creates a new instance of the Attribue request
|
||||
-- @return o instance of Attribute
|
||||
new = function(self)
|
||||
local o = {
|
||||
local o = {
|
||||
lang_tag = "en", version = 2, service_type = "",
|
||||
scope = "", next_extension_offset = 0,
|
||||
prev_resp_list_len = 0, slp_spi_len = 0 }
|
||||
@@ -186,7 +186,7 @@ Request = {
|
||||
--- Sets the request flags
|
||||
-- @param flags number containing the numeric flag representation
|
||||
setFlags = function(self, flags) self.flags = flags end,
|
||||
|
||||
|
||||
--- Sets the request XID
|
||||
-- @param xid number containing the request XID
|
||||
setXID = function(self, xid) self.xid = xid end,
|
||||
@@ -194,11 +194,11 @@ Request = {
|
||||
--- Sets the request function
|
||||
-- @param func number containing the request function number
|
||||
setFunction = function(self, func) self.func = func end,
|
||||
|
||||
|
||||
--- Sets the request taglist
|
||||
-- @param tl string containing the taglist
|
||||
setTagList = function(self, tl) self.tag_list = tl end,
|
||||
|
||||
|
||||
--- Sets the request url
|
||||
-- @param u string containing the url
|
||||
setUrl = function(self, u) self.url = u end,
|
||||
@@ -208,17 +208,17 @@ Request = {
|
||||
__tostring = function(self)
|
||||
assert(self.func, "Packet function was not specified")
|
||||
assert(self.scope, "Packet scope was not specified")
|
||||
|
||||
|
||||
local BASE_LEN = 24
|
||||
local len = BASE_LEN + #self.lang_tag + self.prev_resp_list_len +
|
||||
self.slp_spi_len + #self.service_type + #self.url +
|
||||
self.slp_spi_len + #self.service_type + #self.url +
|
||||
#self.tag_list + #self.scope
|
||||
local len_hi = bit.band(bit.rshift(len, 16), 0x00FF)
|
||||
local len_lo = bit.band(len, 0xFFFF)
|
||||
local neo_hi = bit.band(bit.rshift(self.next_extension_offset, 16),
|
||||
0x00FF)
|
||||
local neo_lo = bit.band(self.next_extension_offset, 0xFFFF)
|
||||
|
||||
|
||||
local data = bin.pack(">CCCSSCSSSASSASASAS", self.version, self.func,
|
||||
len_hi, len_lo, self.flags, neo_hi, neo_lo, self.xid, #self.lang_tag, self.lang_tag,
|
||||
self.prev_resp_list_len, #self.url, self.url, #self.scope, self.scope,
|
||||
@@ -227,14 +227,14 @@ Request = {
|
||||
return data
|
||||
end
|
||||
},
|
||||
|
||||
|
||||
-- The Service request
|
||||
Service = {
|
||||
|
||||
|
||||
--- Creates a new instance of the Service request
|
||||
-- @return o instance of Service
|
||||
new = function(self)
|
||||
local o = {
|
||||
local o = {
|
||||
lang_tag = "en", version = 2, service_type = "",
|
||||
scope = "", next_extension_offset = 0,
|
||||
prev_resp_list_len = 0, predicate_len = 0, slp_spi_len = 0 }
|
||||
@@ -246,7 +246,7 @@ Request = {
|
||||
--- Sets the service type of the request
|
||||
-- @param t string containing the type of the request
|
||||
setServiceType = function(self, t) self.service_type = t end,
|
||||
|
||||
|
||||
--- Sets the request scope
|
||||
-- @param scope string containing the request scope
|
||||
setScope = function(self, scope) self.scope = scope end,
|
||||
@@ -258,7 +258,7 @@ Request = {
|
||||
--- Sets the request flags
|
||||
-- @param flags number containing the numeric flag representation
|
||||
setFlags = function(self, flags) self.flags = flags end,
|
||||
|
||||
|
||||
--- Sets the request XID
|
||||
-- @param xid number containing the request XID
|
||||
setXID = function(self, xid) self.xid = xid end,
|
||||
@@ -291,13 +291,13 @@ Request = {
|
||||
return data
|
||||
end
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
-- The Helper class serves as primary interface for scripts using the libraryy
|
||||
Helper = {
|
||||
|
||||
|
||||
new = function(self, host, port)
|
||||
local o = { xid = 1, socket = nmap.new_socket("udp") }
|
||||
setmetatable(o, self)
|
||||
@@ -326,7 +326,7 @@ Helper = {
|
||||
|
||||
self.socket:set_timeout(5000)
|
||||
self.socket:sendto( self.host, self.port, tostring(sr) )
|
||||
|
||||
|
||||
local result = {}
|
||||
repeat
|
||||
local r = Reply.Service.fromSocket(self.socket)
|
||||
@@ -335,13 +335,13 @@ Helper = {
|
||||
end
|
||||
self.xid = self.xid + 1
|
||||
until(not(r))
|
||||
|
||||
|
||||
if ( #result == 0 ) then
|
||||
return false, "ERROR: Helper.Locate no response received"
|
||||
end
|
||||
return true, result
|
||||
end,
|
||||
|
||||
|
||||
--- Requests an attribute from the server
|
||||
-- @param url as retrieved by the Service request
|
||||
-- @param scope string containing the request scope
|
||||
@@ -360,16 +360,16 @@ Helper = {
|
||||
|
||||
self.socket:set_timeout(5000)
|
||||
self.socket:sendto( self.host, self.port, tostring(ar) )
|
||||
|
||||
|
||||
local r = Reply.Attribute.fromSocket(self.socket)
|
||||
|
||||
|
||||
self.xid = self.xid + 1
|
||||
if ( not(r) ) then
|
||||
return false, "ERROR: Helper.Locate no response received"
|
||||
end
|
||||
return true, r:getAttribList()
|
||||
end,
|
||||
|
||||
|
||||
close = function(self)
|
||||
return self.socket:close()
|
||||
end,
|
||||
|
||||
@@ -20,7 +20,7 @@ _ENV = stdnse.module("ssh1", stdnse.seeall)
|
||||
|
||||
--- Retrieve the size of the packet that is being received
|
||||
-- and checks if it is fully received
|
||||
--
|
||||
--
|
||||
-- This function is very similar to the function generated
|
||||
-- with match.numbytes(num) function, except that this one
|
||||
-- will check for the number of bytes on-the-fly, based on
|
||||
@@ -179,10 +179,10 @@ fingerprint_visual = function( fingerprint, algorithm, bits )
|
||||
local x, y = math.ceil(fieldsize_x/2), math.ceil(fieldsize_y/2)
|
||||
field[x][y] = #characters - 1;
|
||||
|
||||
-- iterate over fingerprint
|
||||
-- iterate over fingerprint
|
||||
for i=1,#fingerprint do
|
||||
input = fingerprint:byte(i)
|
||||
-- each byte conveys four 2-bit move commands
|
||||
-- each byte conveys four 2-bit move commands
|
||||
for j=1,4 do
|
||||
if bit.band( input, 1) == 1 then x = x + 1 else x = x - 1 end
|
||||
if bit.band( input, 2) == 2 then y = y + 1 else y = y - 1 end
|
||||
|
||||
@@ -20,7 +20,7 @@ local SSH2
|
||||
|
||||
--- Retrieve the size of the packet that is being received
|
||||
-- and checks if it is fully received
|
||||
--
|
||||
--
|
||||
-- This function is very similar to the function generated
|
||||
-- with match.numbytes(num) function, except that this one
|
||||
-- will check for the number of bytes on-the-fly, based on
|
||||
@@ -122,7 +122,7 @@ end
|
||||
--- Parse a <code>kexinit</code> package.
|
||||
--
|
||||
-- Returns an empty table in case of an error
|
||||
transport.parse_kex_init = function( payload )
|
||||
transport.parse_kex_init = function( payload )
|
||||
local _, offset, msg_code, parsed, fields, fieldname
|
||||
parsed = {}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ local xmpp = require "xmpp"
|
||||
_ENV = stdnse.module("sslcert", stdnse.seeall)
|
||||
|
||||
StartTLS = {
|
||||
|
||||
|
||||
ftp_prepare_tls_without_reconnect = function(host, port)
|
||||
local s = nmap.new_socket()
|
||||
-- Attempt to negotiate TLS over FTP for services that support it
|
||||
@@ -56,7 +56,7 @@ StartTLS = {
|
||||
-- Should have a solid TLS over FTP session now...
|
||||
return true, s
|
||||
end,
|
||||
|
||||
|
||||
ftp_prepare_tls = function(host, port)
|
||||
local err
|
||||
local status, s = StartTLS.ftp_prepare_tls_without_reconnect(host, port)
|
||||
@@ -66,7 +66,7 @@ StartTLS = {
|
||||
stdnse.print_debug("1","Could not establish SSL session after STARTTLS command.")
|
||||
s:close()
|
||||
return false, "Failed to connect to SMTP server"
|
||||
else
|
||||
else
|
||||
return true,s
|
||||
end
|
||||
end
|
||||
@@ -79,11 +79,11 @@ StartTLS = {
|
||||
-- Works for SMTP (25) and SMTP Submission (587)
|
||||
|
||||
-- Open a standard TCP socket
|
||||
local status, error = s:connect(host, port, "tcp")
|
||||
local status, error = s:connect(host, port, "tcp")
|
||||
|
||||
if not status then
|
||||
if not status then
|
||||
return nil
|
||||
else
|
||||
else
|
||||
local resultEHLO
|
||||
-- Loop until the service presents a banner to deal with server
|
||||
-- load and timing issues. There may be a better way to handle this.
|
||||
@@ -108,7 +108,7 @@ StartTLS = {
|
||||
|
||||
resultEHLO = ""
|
||||
|
||||
-- Send STARTTLS command ask the service to start encryption
|
||||
-- Send STARTTLS command ask the service to start encryption
|
||||
local query = "STARTTLS\r\n"
|
||||
status = s:send(query)
|
||||
status, resultEHLO = s:receive_lines(1)
|
||||
@@ -119,16 +119,16 @@ StartTLS = {
|
||||
|
||||
-- Send QUIT to clean up server side connection
|
||||
local query = "QUIT\r\n"
|
||||
status = s:send(query)
|
||||
status = s:send(query)
|
||||
resultEHLO = ""
|
||||
|
||||
return false, "Failed to connect to SMTP server"
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Should have a solid TLS over SMTP session now...
|
||||
return true, s
|
||||
end,
|
||||
|
||||
|
||||
smtp_prepare_tls = function(host, port)
|
||||
local err
|
||||
local status,s = StartTLS.smtp_prepare_tls_without_reconnect(host, port)
|
||||
@@ -138,7 +138,7 @@ StartTLS = {
|
||||
stdnse.print_debug("1","Could not establish SSL session after STARTTLS command.")
|
||||
s:close()
|
||||
return false, "Failed to connect to SMTP server"
|
||||
else
|
||||
else
|
||||
return true,s
|
||||
end
|
||||
end
|
||||
@@ -156,7 +156,7 @@ StartTLS = {
|
||||
sock:close()
|
||||
stdnse.print_debug("Can't send: %s", err)
|
||||
return false, "Failed to connect to XMPP server"
|
||||
end
|
||||
end
|
||||
status, err = sock:send(xmppStreamStart)
|
||||
if not status then
|
||||
stdnse.print_debug("Couldn't send: %s", err)
|
||||
@@ -168,7 +168,7 @@ StartTLS = {
|
||||
stdnse.print_debug("Couldn't receive: %s", err)
|
||||
sock:close()
|
||||
return false, "Failed to connect to XMPP server"
|
||||
end
|
||||
end
|
||||
status, err = sock:send(xmppStartTLS)
|
||||
if not status then
|
||||
stdnse.print_debug("Couldn't send: %s", err)
|
||||
@@ -180,24 +180,24 @@ StartTLS = {
|
||||
stdnse.print_debug("Couldn't receive: %s", err)
|
||||
sock:close()
|
||||
return false, "Failed to connect to XMPP server"
|
||||
end
|
||||
end
|
||||
if string.find(result,"proceed") then
|
||||
return true,sock
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
status, result = sock:receive() -- might not be in the first reply
|
||||
if not status then
|
||||
stdnse.print_debug("Couldn't receive: %s", err)
|
||||
sock:close()
|
||||
return false, "Failed to connect to XMPP server"
|
||||
end
|
||||
end
|
||||
if string.find(result,"proceed") then
|
||||
return true,sock
|
||||
else
|
||||
else
|
||||
return false, "Failed to connect to XMPP server"
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
xmpp_prepare_tls = function(host, port)
|
||||
local ls = xmpp.XMPP:new(host, port, { starttls = true } )
|
||||
ls.socket = nmap.new_socket()
|
||||
@@ -213,7 +213,7 @@ StartTLS = {
|
||||
return false, "Failed to connected"
|
||||
end
|
||||
return true, ls.socket
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
-- A table mapping port numbers to specialized SSL negotiation functions.
|
||||
@@ -251,14 +251,14 @@ end
|
||||
function getCertificate(host, port)
|
||||
local mutex = nmap.mutex("sslcert-cache-mutex")
|
||||
mutex "lock"
|
||||
|
||||
|
||||
if ( host.registry["ssl-cert"] and
|
||||
host.registry["ssl-cert"][port.number] ) then
|
||||
stdnse.print_debug(2, "sslcert: Returning cached SSL certificate")
|
||||
mutex "done"
|
||||
return true, host.registry["ssl-cert"][port.number]
|
||||
end
|
||||
|
||||
|
||||
-- Is there a specialized function for this port?
|
||||
local specialized = SPECIALIZED_PREPARE_TLS[port.number]
|
||||
local status
|
||||
@@ -276,12 +276,12 @@ function getCertificate(host, port)
|
||||
mutex "done"
|
||||
return false, "Failed to connect to server"
|
||||
end
|
||||
end
|
||||
end
|
||||
local cert = socket:get_ssl_certificate()
|
||||
if ( cert == nil ) then
|
||||
return false, "Unable to get cert"
|
||||
end
|
||||
|
||||
|
||||
host.registry["ssl-cert"] = host.registry["ssl-cert"] or {}
|
||||
host.registry["ssl-cert"][port.number] = host.registry["ssl-cert"][port.number] or {}
|
||||
host.registry["ssl-cert"][port.number] = cert
|
||||
|
||||
@@ -60,7 +60,7 @@ _ENV.sleep = nmap.socket.sleep;
|
||||
---
|
||||
-- Prints a formatted debug message if the current debugging level is greater
|
||||
-- than or equal to a given level.
|
||||
--
|
||||
--
|
||||
-- This is a convenience wrapper around
|
||||
-- <code>nmap.log_write</code>. The first optional numeric
|
||||
-- argument, <code>level</code>, is used as the debugging level necessary
|
||||
@@ -81,7 +81,7 @@ end
|
||||
---
|
||||
-- Prints a formatted verbosity message if the current verbosity level is greater
|
||||
-- than or equal to a given level.
|
||||
--
|
||||
--
|
||||
-- This is a convenience wrapper around
|
||||
-- <code>nmap.log_write</code>. The first optional numeric
|
||||
-- argument, <code>level</code>, is used as the verbosity level necessary
|
||||
@@ -101,7 +101,7 @@ end
|
||||
|
||||
|
||||
--- Join a list of strings with a separator string.
|
||||
--
|
||||
--
|
||||
-- This is Lua's <code>table.concat</code> function with the parameters
|
||||
-- swapped for coherence.
|
||||
-- @usage
|
||||
@@ -112,7 +112,7 @@ end
|
||||
-- @return Concatenated string.
|
||||
function strjoin(delimiter, list)
|
||||
assert(type(delimiter) == "string" or type(delimiter) == nil, "delimiter is of the wrong type! (did you get the parameters backward?)")
|
||||
|
||||
|
||||
return concat(list, delimiter);
|
||||
end
|
||||
|
||||
@@ -167,7 +167,7 @@ end
|
||||
|
||||
--- Return a wrapper closure around a socket that buffers socket reads into
|
||||
-- chunks separated by a pattern.
|
||||
--
|
||||
--
|
||||
-- This function operates on a socket attempting to read data. It separates the
|
||||
-- data by <code>sep</code> and, for each invocation, returns a piece of the
|
||||
-- separated data. Typically this is used to iterate over the lines of data
|
||||
@@ -274,7 +274,7 @@ end
|
||||
-- @param s String or number to be encoded.
|
||||
-- @param options Table specifiying formatting options.
|
||||
-- @return String in hexadecimal format.
|
||||
function tohex( s, options )
|
||||
function tohex( s, options )
|
||||
options = options or EMPTY
|
||||
local separator = options.separator
|
||||
local hex
|
||||
@@ -539,7 +539,7 @@ function clock_us()
|
||||
return nmap.clock() * 1000000
|
||||
end
|
||||
|
||||
---Get the indentation symbols at a given level.
|
||||
---Get the indentation symbols at a given level.
|
||||
local function format_get_indent(indent, at_end)
|
||||
local str = ""
|
||||
local had_continue = false
|
||||
@@ -642,7 +642,7 @@ local function format_output_sub(status, data, indent)
|
||||
end
|
||||
|
||||
insert(output, format_output_sub(status, value, new_indent))
|
||||
|
||||
|
||||
elseif(type(value) == 'string') then
|
||||
local lines = splitlines(value)
|
||||
|
||||
@@ -657,30 +657,30 @@ local function format_output_sub(status, data, indent)
|
||||
return concat(output)
|
||||
end
|
||||
|
||||
---Takes a table of output on the commandline and formats it for display to the
|
||||
-- user. This is basically done by converting an array of nested tables into a
|
||||
-- string. In addition to numbered array elements, each table can have a 'name'
|
||||
-- and a 'warning' value. The 'name' will be displayed above the table, and
|
||||
---Takes a table of output on the commandline and formats it for display to the
|
||||
-- user. This is basically done by converting an array of nested tables into a
|
||||
-- string. In addition to numbered array elements, each table can have a 'name'
|
||||
-- and a 'warning' value. The 'name' will be displayed above the table, and
|
||||
-- 'warning' will be displayed, with a 'WARNING' tag, if and only if debugging
|
||||
-- is enabled.
|
||||
--
|
||||
-- is enabled.
|
||||
--
|
||||
-- Here's an example of a table:
|
||||
-- <code>
|
||||
-- local domains = {}
|
||||
-- domains['name'] = "DOMAINS"
|
||||
-- table.insert(domains, 'Domain 1')
|
||||
-- table.insert(domains, 'Domain 2')
|
||||
--
|
||||
--
|
||||
-- local names = {}
|
||||
-- names['name'] = "NAMES"
|
||||
-- names['warning'] = "Not all names could be determined!"
|
||||
-- table.insert(names, "Name 1")
|
||||
--
|
||||
--
|
||||
-- local response = {}
|
||||
-- table.insert(response, "Apple pie")
|
||||
-- table.insert(response, domains)
|
||||
-- table.insert(response, names)
|
||||
--
|
||||
--
|
||||
-- return stdnse.format_output(true, response)
|
||||
-- </code>
|
||||
--
|
||||
@@ -696,13 +696,13 @@ end
|
||||
-- |_ Name 1
|
||||
-- </code>
|
||||
--
|
||||
--@param status A boolean value dictating whether or not the script succeeded.
|
||||
--@param status A boolean value dictating whether or not the script succeeded.
|
||||
-- If status is false, and debugging is enabled, 'ERROR' is prepended
|
||||
-- to every line. If status is false and debugging is disabled, no output
|
||||
-- occurs.
|
||||
--@param data The table of output.
|
||||
-- occurs.
|
||||
--@param data The table of output.
|
||||
--@param indent Used for indentation on recursive calls; should generally be set to
|
||||
-- nil when callling from a script.
|
||||
-- nil when callling from a script.
|
||||
-- @return <code>nil</code>, if <code>data</code> is empty, otherwise a
|
||||
-- multiline string.
|
||||
function format_output(status, data, indent)
|
||||
@@ -772,7 +772,7 @@ end
|
||||
function get_script_args (...)
|
||||
local args = {}
|
||||
|
||||
for i, set in ipairs({...}) do
|
||||
for i, set in ipairs({...}) do
|
||||
if type(set) == "string" then
|
||||
set = {set}
|
||||
end
|
||||
@@ -788,10 +788,10 @@ function get_script_args (...)
|
||||
return unpack(args, 1, select("#", ...))
|
||||
end
|
||||
|
||||
---Get the best possible hostname for the given host. This can be the target as given on
|
||||
-- the commandline, the reverse dns name, or simply the ip address.
|
||||
--@param host The host table (or a string that'll simply be returned).
|
||||
--@return The best possible hostname, as a string.
|
||||
---Get the best possible hostname for the given host. This can be the target as given on
|
||||
-- the commandline, the reverse dns name, or simply the ip address.
|
||||
--@param host The host table (or a string that'll simply be returned).
|
||||
--@return The best possible hostname, as a string.
|
||||
function get_hostname(host)
|
||||
if type(host) == "table" then
|
||||
return host.targetname or ( host.name ~= '' and host.name ) or host.ip
|
||||
@@ -801,7 +801,7 @@ function get_hostname(host)
|
||||
end
|
||||
|
||||
---Retrieve an item from the registry, checking if each sub-key exists. If any key doesn't
|
||||
-- exist, return nil.
|
||||
-- exist, return nil.
|
||||
function registry_get(subkeys)
|
||||
local registry = nmap.registry
|
||||
local i = 1
|
||||
@@ -819,7 +819,7 @@ function registry_get(subkeys)
|
||||
return registry
|
||||
end
|
||||
|
||||
--Check if the given element exists in the registry. If 'key' is nil, it isn't checked.
|
||||
--Check if the given element exists in the registry. If 'key' is nil, it isn't checked.
|
||||
function registry_exists(subkeys, key, value)
|
||||
local subkey = registry_get(subkeys)
|
||||
|
||||
@@ -836,12 +836,12 @@ function registry_exists(subkeys, key, value)
|
||||
return false
|
||||
end
|
||||
|
||||
---Add an item to an array in the registry, creating all sub-keys if necessary.
|
||||
---Add an item to an array in the registry, creating all sub-keys if necessary.
|
||||
-- For example, calling:
|
||||
-- <code>registry_add_array({'192.168.1.100', 'www', '80', 'pages'}, 'index.html')</code>
|
||||
-- Will create nmap.registry['192.168.1.100'] as a table, if necessary, then add a table
|
||||
-- under the 'www' key, and so on. 'pages', finally, is treated as an array and the value
|
||||
-- given is added to the end.
|
||||
-- given is added to the end.
|
||||
function registry_add_array(subkeys, value, allow_duplicates)
|
||||
local registry = nmap.registry
|
||||
local i = 1
|
||||
@@ -871,7 +871,7 @@ function registry_add_array(subkeys, value, allow_duplicates)
|
||||
end
|
||||
|
||||
---Similar to <code>registry_add_array</code>, except instead of adding a value to the
|
||||
-- end of an array, it adds a key:value pair to the table.
|
||||
-- end of an array, it adds a key:value pair to the table.
|
||||
function registry_add_table(subkeys, key, value, allow_duplicates)
|
||||
local registry = nmap.registry
|
||||
local i = 1
|
||||
@@ -1019,39 +1019,39 @@ do end -- no function here, see nse_main.lua
|
||||
--@param port_range a port range string in Nmap standard format (ex. "T:80,1-30,U:31337,21-25")
|
||||
--@returns boolean indicating whether the port is in the port range
|
||||
function in_port_range(port,port_range)
|
||||
assert(port and type(port.number)=="number" and type(port.protocol)=="string" and
|
||||
assert(port and type(port.number)=="number" and type(port.protocol)=="string" and
|
||||
(port.protocol=="udp" or port.protocol=="tcp"),"Port structure missing or invalid: port={ number=<port_number>, protocol=<port_protocol> }")
|
||||
assert((type(port_range)=="string" or type(port_range)=="number") and port_range~="","Incorrect port range specification.")
|
||||
|
||||
|
||||
-- Proto - true for TCP, false for UDP
|
||||
local proto
|
||||
if(port.protocol=="tcp") then proto = true else proto = false end
|
||||
|
||||
|
||||
--TCP flag for iteration - true for TCP, false for UDP, if not specified we presume TCP
|
||||
local tcp_flag = true
|
||||
|
||||
-- in case the port_range is a single number
|
||||
|
||||
-- in case the port_range is a single number
|
||||
if type(port_range)=="number" then
|
||||
if proto and port_range==port.number then return true
|
||||
else return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--clean the string a bit
|
||||
port_range=port_range:gsub("%s+","")
|
||||
|
||||
|
||||
-- single_pr - single port range
|
||||
for i, single_pr in ipairs(strsplit(",",port_range)) do
|
||||
if single_pr:match("T:") then
|
||||
tcp_flag = true
|
||||
single_pr = single_pr:gsub("T:","")
|
||||
else
|
||||
if single_pr:match("U:") then
|
||||
tcp_flag = false
|
||||
else
|
||||
if single_pr:match("U:") then
|
||||
tcp_flag = false
|
||||
single_pr = single_pr:gsub("U:","")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- compare ports only when the port's protocol is the same as
|
||||
-- the current single port range
|
||||
if tcp_flag == proto then
|
||||
@@ -1059,9 +1059,9 @@ function in_port_range(port,port_range)
|
||||
if pone then
|
||||
pone = tonumber(pone)
|
||||
assert(pone>-1 and pone<65536, "Port range number out of range (0-65535).")
|
||||
|
||||
if pone == port.number then
|
||||
return true
|
||||
|
||||
if pone == port.number then
|
||||
return true
|
||||
end
|
||||
else
|
||||
local pstart, pend = single_pr:match("^(%d+)%-(%d+)$")
|
||||
@@ -1069,7 +1069,7 @@ function in_port_range(port,port_range)
|
||||
assert(pstart,"Incorrect port range specification.")
|
||||
assert(pstart<=pend,"Incorrect port range specification, the starting port should have a smaller value than the ending port.")
|
||||
assert(pstart>-1 and pstart<65536 and pend>-1 and pend<65536, "Port range number out of range (0-65535).")
|
||||
|
||||
|
||||
if port.number >=pstart and port.number <= pend then
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -51,7 +51,7 @@ local concat = table.concat;
|
||||
|
||||
_ENV = stdnse.module("strbuf", stdnse.seeall)
|
||||
|
||||
-- String buffer functions. Concatenation is not efficient in
|
||||
-- String buffer functions. Concatenation is not efficient in
|
||||
-- lua as strings are immutable. If a large amount of '..' sequential
|
||||
-- operations are needed a string buffer should be used instead
|
||||
-- e.g. for i = 1, 10 do s = s..i end
|
||||
|
||||
@@ -51,9 +51,9 @@ end
|
||||
local function strict (env)
|
||||
local mt = getmetatable(env) or setmetatable(env, {}) and getmetatable(env);
|
||||
local _newindex, _index = mt.__newindex, mt.__index;
|
||||
|
||||
|
||||
mt.__declared = {};
|
||||
|
||||
|
||||
function mt.__newindex (t, n, v)
|
||||
if type(_newindex) == "function" then
|
||||
_newindex(t, n, v); -- hook it
|
||||
@@ -67,7 +67,7 @@ local function strict (env)
|
||||
end
|
||||
rawset(t, n, v);
|
||||
end
|
||||
|
||||
|
||||
function mt.__index (t, n)
|
||||
if type(_index) == "function" then
|
||||
local v = _index(t, n); -- hook it
|
||||
|
||||
104
nselib/stun.lua
104
nselib/stun.lua
@@ -22,13 +22,13 @@ MessageType = {
|
||||
BINDING_REQUEST = 0x0001,
|
||||
BINDING_RESPONSE = 0x0101,
|
||||
}
|
||||
|
||||
|
||||
-- The header used in both request and responses
|
||||
Header = {
|
||||
|
||||
-- the header size in bytes
|
||||
|
||||
-- the header size in bytes
|
||||
size = 20,
|
||||
|
||||
|
||||
-- creates a new instance of Header
|
||||
-- @param type number the request/response type
|
||||
-- @param trans_id string the 128-bit transaction id
|
||||
@@ -39,9 +39,9 @@ Header = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- parses an opaque string and creates a new Header instance
|
||||
-- @param data opaque string
|
||||
-- @param data opaque string
|
||||
-- @return header new instance of Header
|
||||
parse = function(data)
|
||||
local header = Header:new()
|
||||
@@ -49,24 +49,24 @@ Header = {
|
||||
pos, header.type, header.length, header.trans_id = bin.unpack(">SSA16", data)
|
||||
return header
|
||||
end,
|
||||
|
||||
|
||||
-- converts the header to an opaque string
|
||||
-- @return string containing the header instance
|
||||
__tostring = function(self)
|
||||
return bin.pack(">SSA", self.type, self.length, self.trans_id)
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
Request = {
|
||||
|
||||
|
||||
-- The binding request
|
||||
Bind = {
|
||||
|
||||
|
||||
-- Creates a new Bind request
|
||||
-- @param trans_id string containing the 128 bit transaction ID
|
||||
-- @return o new instance of the Bind request
|
||||
new = function(self, trans_id)
|
||||
local o = {
|
||||
local o = {
|
||||
header = Header:new(MessageType.BINDING_REQUEST, trans_id),
|
||||
attributes = {}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ Request = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- converts the instance to an opaque string
|
||||
-- @return string containing the Bind request as string
|
||||
__tostring = function(self)
|
||||
@@ -83,10 +83,10 @@ Request = {
|
||||
data = data .. tostring(attrib)
|
||||
end
|
||||
self.header.length = #data
|
||||
return tostring(self.header) .. data
|
||||
end,
|
||||
return tostring(self.header) .. data
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The attribute class
|
||||
@@ -104,7 +104,7 @@ Attribute = {
|
||||
UNKNOWN_ATTRIBUTES = 0x000a,
|
||||
REFLECTED_FROM = 0x000b,
|
||||
SERVER = 0x8022,
|
||||
|
||||
|
||||
-- creates a new attribute instance
|
||||
-- @param type number containing the attribute type
|
||||
-- @param data string containing the attribute value
|
||||
@@ -119,16 +119,16 @@ Attribute = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- parses a string and creates an Attribute instance
|
||||
-- @param data string containing the raw attribute
|
||||
-- @return o new attribute instance
|
||||
parse = function(data)
|
||||
local attr = Attribute:new()
|
||||
local pos = 1
|
||||
|
||||
|
||||
pos, attr.type, attr.length = bin.unpack(">SS", data, pos)
|
||||
|
||||
|
||||
local function parseAddress(data, pos)
|
||||
local _, addr = nil, {}
|
||||
pos, _, addr.family, addr.port, addr.ip = bin.unpack("<CCSI", data, pos)
|
||||
@@ -137,7 +137,7 @@ Attribute = {
|
||||
end
|
||||
return addr
|
||||
end
|
||||
|
||||
|
||||
if ( ( attr.type == Attribute.MAPPED_ADDRESS ) or
|
||||
( attr.type == Attribute.RESPONSE_ADDRESS ) or
|
||||
( attr.type == Attribute.SOURCE_ADDRESS ) or
|
||||
@@ -149,24 +149,24 @@ Attribute = {
|
||||
elseif( attr.type == Attribute.SERVER ) then
|
||||
pos, attr.server = bin.unpack("A" .. attr.length-1, data, pos)
|
||||
end
|
||||
|
||||
|
||||
return attr
|
||||
end,
|
||||
|
||||
|
||||
-- converts an attribute to string
|
||||
-- @return string containing the serialized attribute
|
||||
__tostring = function(self)
|
||||
return bin.pack(">SSA", self.type, self.length, self.data or "")
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Response class container
|
||||
Response = {
|
||||
|
||||
|
||||
-- Bind response class
|
||||
Bind = {
|
||||
|
||||
|
||||
-- creates a new instance of the Bind response
|
||||
-- @param trans_id string containing the 128 bit transaction id
|
||||
-- @return o new Bind instance
|
||||
@@ -183,7 +183,7 @@ Response = {
|
||||
parse = function(data)
|
||||
local resp = Response.Bind:new()
|
||||
local pos = Header.size
|
||||
|
||||
|
||||
resp.header = Header.parse(data)
|
||||
resp.attributes = {}
|
||||
|
||||
@@ -191,7 +191,7 @@ Response = {
|
||||
local attr = Attribute.parse(data:sub(pos))
|
||||
table.insert(resp.attributes, attr)
|
||||
pos = pos + attr.length + 4
|
||||
end
|
||||
end
|
||||
return resp
|
||||
end
|
||||
}
|
||||
@@ -199,16 +199,16 @@ Response = {
|
||||
|
||||
-- The communication class
|
||||
Comm = {
|
||||
|
||||
|
||||
-- creates a new Comm instance
|
||||
-- @param host table
|
||||
-- @param port table
|
||||
-- @param options table, currently supporting:
|
||||
-- <code>timeout</code> - socket timeout in ms.
|
||||
-- @param mode containing the mode
|
||||
-- @param mode containing the mode
|
||||
-- @return o new instance of Comm
|
||||
new = function(self, host, port, options, mode)
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
options = options or { timeout = 10000 },
|
||||
@@ -218,7 +218,7 @@ Comm = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- connects the socket to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message, if status is false
|
||||
@@ -226,7 +226,7 @@ Comm = {
|
||||
self.socket:set_timeout(self.options.timeout)
|
||||
return self.socket:connect(self.host, self.port)
|
||||
end,
|
||||
|
||||
|
||||
-- sends a request to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message, if status is false
|
||||
@@ -238,26 +238,26 @@ Comm = {
|
||||
-- @return status true on success, false on failure
|
||||
-- @return response containing a response instance
|
||||
-- err string containing an error message, if status is false
|
||||
recv = function(self)
|
||||
recv = function(self)
|
||||
local status, hdr_data = self.socket:receive_buf(match.numbytes(Header.size), false)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to receive response from server"
|
||||
end
|
||||
|
||||
|
||||
local header = Header.parse(hdr_data)
|
||||
if ( not(header) ) then
|
||||
return false, "Failed to parse response header"
|
||||
end
|
||||
|
||||
|
||||
local status, data = self.socket:receive_buf(match.numbytes(header.length), false)
|
||||
if ( header.type == MessageType.BINDING_RESPONSE ) then
|
||||
local resp = Response.Bind.parse(hdr_data .. data)
|
||||
return true, resp
|
||||
end
|
||||
|
||||
|
||||
return false, "Unknown response message received"
|
||||
end,
|
||||
|
||||
|
||||
-- sends the request instance to the server and receives the response
|
||||
-- @param req request class instance
|
||||
-- @return status true on success, false on failure
|
||||
@@ -268,9 +268,9 @@ Comm = {
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send request to server"
|
||||
end
|
||||
return self:recv()
|
||||
return self:recv()
|
||||
end,
|
||||
|
||||
|
||||
-- closes the connection to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message, if status is false
|
||||
@@ -281,7 +281,7 @@ Comm = {
|
||||
|
||||
-- The Util class
|
||||
Util = {
|
||||
|
||||
|
||||
-- creates a random string
|
||||
-- @param len number containg the length of the generated random string
|
||||
-- @return str containing the random string
|
||||
@@ -290,7 +290,7 @@ Util = {
|
||||
for i=1, len do str = str .. string.char(math.random(255)) end
|
||||
return str
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- The Helper class
|
||||
@@ -305,7 +305,7 @@ Helper = {
|
||||
-- supported container
|
||||
-- @return o new instance of Comm
|
||||
new = function(self, host, port, options, mode)
|
||||
local o = {
|
||||
local o = {
|
||||
mode = mode,
|
||||
comm = Comm:new(host, port, options, mode),
|
||||
}
|
||||
@@ -315,32 +315,32 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
-- connects to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message, if status is false
|
||||
connect = function(self)
|
||||
return self.comm:connect()
|
||||
end,
|
||||
|
||||
|
||||
-- Get's the external public IP
|
||||
-- @return status true on success, false on failure
|
||||
-- @return result containing the IP as tring
|
||||
getExternalAddress = function(self)
|
||||
local trans_id
|
||||
|
||||
|
||||
if ( self.mode == "classic" ) then
|
||||
trans_id = Util.randomString(16)
|
||||
else
|
||||
trans_id = bin.pack("HA","2112A442", Util.randomString(12))
|
||||
end
|
||||
local req = Request.Bind:new(trans_id)
|
||||
|
||||
|
||||
local status, response = self.comm:exch(req)
|
||||
if ( not(status) ) then
|
||||
return false, "Failed to send data to server"
|
||||
end
|
||||
|
||||
|
||||
local result
|
||||
for k, attr in pairs(response.attributes) do
|
||||
if (attr.type == Attribute.MAPPED_ADDRESS ) then
|
||||
@@ -351,14 +351,14 @@ Helper = {
|
||||
self.cache.server = attr.server
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ( not(result) and not(self.cache) ) then
|
||||
return false, "Server returned no response"
|
||||
end
|
||||
|
||||
|
||||
return status, result
|
||||
end,
|
||||
|
||||
|
||||
-- Gets the server version if it was returned by the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return version string containing the server product and version
|
||||
@@ -374,14 +374,14 @@ Helper = {
|
||||
end
|
||||
return true, (self.cache and self.cache.server or "")
|
||||
end,
|
||||
|
||||
|
||||
-- closes the connection to the server
|
||||
-- @return status true on success, false on failure
|
||||
-- @return err string containing an error message, if status is false
|
||||
close = function(self)
|
||||
return self.comm:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -90,7 +90,7 @@ end
|
||||
function dump(t)
|
||||
assert(t)
|
||||
|
||||
local column_width = {}
|
||||
local column_width = {}
|
||||
local num_columns = {}
|
||||
local buf = strbuf.new()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
--
|
||||
-- Currently only write-operations are supported so that script can trigger
|
||||
-- TFTP transfers and receive the files and return them as result.
|
||||
--
|
||||
--
|
||||
-- The library contains the following classes
|
||||
-- * <code>Packet</code>
|
||||
-- ** The <code>Packet</code> classes contain one class for each TFTP operation.
|
||||
@@ -45,7 +45,7 @@ OpCode = {
|
||||
WRQ = 2,
|
||||
DATA = 3,
|
||||
ACK = 4,
|
||||
ERROR = 5,
|
||||
ERROR = 5,
|
||||
}
|
||||
|
||||
|
||||
@@ -54,10 +54,10 @@ OpCode = {
|
||||
-- The current code only implements the ACK and ERROR packets
|
||||
-- As the server is write-only the other packet types are not needed
|
||||
Packet = {
|
||||
|
||||
|
||||
-- Implements the ACK packet
|
||||
ACK = {
|
||||
|
||||
|
||||
new = function( self, block )
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
@@ -65,13 +65,13 @@ Packet = {
|
||||
o.block = block
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function( self )
|
||||
return bin.pack(">SS", OpCode.ACK, self.block)
|
||||
end,
|
||||
|
||||
|
||||
},
|
||||
|
||||
|
||||
-- Implements the error packet
|
||||
ERROR = {
|
||||
|
||||
@@ -83,17 +83,17 @@ Packet = {
|
||||
o.code = code
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
__tostring = function( self )
|
||||
return bin.pack(">SSz", OpCode.ERROR, self.code, self.msg)
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- The File class holds files received by the TFTP server
|
||||
File = {
|
||||
|
||||
|
||||
--- Creates a new file object
|
||||
--
|
||||
-- @param filename string containing the filename
|
||||
@@ -114,7 +114,7 @@ File = {
|
||||
|
||||
getName = function(self) return self.name end,
|
||||
setName = function(self, name) self.name = name end,
|
||||
|
||||
|
||||
setSender = function(self, sender) self.sender = sender end,
|
||||
getSender = function(self) return self.sender end,
|
||||
}
|
||||
@@ -124,21 +124,21 @@ File = {
|
||||
local function dispatcher()
|
||||
|
||||
local last = os.time()
|
||||
local f_condvar = nmap.condvar(infiles)
|
||||
local f_condvar = nmap.condvar(infiles)
|
||||
local s_condvar = nmap.condvar(state)
|
||||
|
||||
while(true) do
|
||||
|
||||
|
||||
-- check if other scripts are active
|
||||
local counter = 0
|
||||
for t in pairs(running) do
|
||||
counter = counter + 1
|
||||
end
|
||||
if ( counter == 0 ) then
|
||||
if ( counter == 0 ) then
|
||||
state = "STOPPING"
|
||||
s_condvar "broadcast"
|
||||
end
|
||||
|
||||
|
||||
if #threads == 0 then break end
|
||||
for i, thread in ipairs(threads) do
|
||||
local status, res = coroutine.resume(thread)
|
||||
@@ -147,14 +147,14 @@ local function dispatcher()
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Make sure to process waitFile atleast every 2 seconds
|
||||
-- in case no files have arrived
|
||||
if ( os.time() - last >= 2 ) then
|
||||
last = os.time()
|
||||
f_condvar "broadcast"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
state = "STOPPED"
|
||||
s_condvar "broadcast"
|
||||
@@ -176,7 +176,7 @@ local function processConnection( host, port, data )
|
||||
if ( not(status) ) then return status, err end
|
||||
|
||||
socket:set_timeout(10)
|
||||
|
||||
|
||||
-- If we get anything else than a write request, abort the connection
|
||||
if ( OpCode.WRQ ~= op ) then
|
||||
stdnse.print_debug("Unsupported opcode")
|
||||
@@ -185,10 +185,10 @@ local function processConnection( host, port, data )
|
||||
|
||||
local pos, filename, enctype = bin.unpack("zz", data, pos)
|
||||
status, err = socket:send( tostring( Packet.ACK:new(0) ) )
|
||||
|
||||
|
||||
local blocks = {}
|
||||
local lastread = os.time()
|
||||
|
||||
|
||||
while( true ) do
|
||||
local status, pdata = socket:receive()
|
||||
if ( not(status) ) then
|
||||
@@ -205,18 +205,18 @@ local function processConnection( host, port, data )
|
||||
if ( OpCode.DATA ~= op ) then
|
||||
stdnse.print_debug("Expected a data packet, terminating TFTP transfer")
|
||||
end
|
||||
|
||||
|
||||
local block, data
|
||||
pos, block, data = bin.unpack(">SA" .. #pdata - 4, pdata, pos )
|
||||
|
||||
|
||||
blocks[block] = data
|
||||
|
||||
|
||||
-- First block was not 1
|
||||
if ( #blocks == 0 ) then
|
||||
socket:send( tostring(Packet.ERROR:new(0, "Did not receive block 1")))
|
||||
break
|
||||
end
|
||||
|
||||
|
||||
-- for every fith block check that we've received the preceeding four
|
||||
if ( ( #blocks % 5 ) == 0 ) then
|
||||
for b = #blocks - 4, #blocks do
|
||||
@@ -225,7 +225,7 @@ local function processConnection( host, port, data )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Ack the data block
|
||||
status, err = socket:send( tostring(Packet.ACK:new(block)) )
|
||||
|
||||
@@ -233,17 +233,17 @@ local function processConnection( host, port, data )
|
||||
-- yield every 5th iteration so other threads may work
|
||||
coroutine.yield(true)
|
||||
end
|
||||
|
||||
-- If the data length was less than 512, this was our last block
|
||||
|
||||
-- If the data length was less than 512, this was our last block
|
||||
if ( #data < 512 ) then
|
||||
socket:close()
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local filecontent = ""
|
||||
|
||||
|
||||
-- Make sure we received all the blocks needed to proceed
|
||||
for i=1, #blocks do
|
||||
if ( not(blocks[i]) ) then
|
||||
@@ -252,27 +252,27 @@ local function processConnection( host, port, data )
|
||||
filecontent = filecontent .. blocks[i]
|
||||
end
|
||||
stdnse.print_debug("Finnished receiving file \"%s\"", filename)
|
||||
|
||||
|
||||
-- Add anew file to the global infiles table
|
||||
table.insert( infiles, File:new(filename, filecontent, host) )
|
||||
|
||||
|
||||
local condvar = nmap.condvar(infiles)
|
||||
condvar "broadcast"
|
||||
end
|
||||
|
||||
-- Waits for a connection from a client
|
||||
local function waitForConnection()
|
||||
|
||||
|
||||
local srvsock = nmap.new_socket("udp")
|
||||
local status = srvsock:bind(nil, 69)
|
||||
assert(status, "Failed to bind to TFTP server port")
|
||||
|
||||
|
||||
srvsock:set_timeout(0)
|
||||
|
||||
|
||||
while( state == "RUNNING" ) do
|
||||
local status, data = srvsock:receive()
|
||||
if ( not(status) ) then
|
||||
coroutine.yield(true)
|
||||
if ( not(status) ) then
|
||||
coroutine.yield(true)
|
||||
else
|
||||
local status, _, _, rhost, rport = srvsock:get_info()
|
||||
local x = coroutine.create( function() processConnection(rhost, rport, data) end )
|
||||
@@ -293,13 +293,13 @@ function start()
|
||||
|
||||
mutex "lock"
|
||||
if ( state == "STOPPED" ) then
|
||||
srvthread = coroutine.running()
|
||||
srvthread = coroutine.running()
|
||||
table.insert( threads, coroutine.create( waitForConnection ) )
|
||||
stdnse.new_thread( dispatcher )
|
||||
state = "RUNNING"
|
||||
end
|
||||
mutex "done"
|
||||
|
||||
|
||||
end
|
||||
|
||||
local function waitLast()
|
||||
@@ -328,10 +328,10 @@ function waitFile( filename, timeout )
|
||||
local t = os.time()
|
||||
while(os.time() - t < timeout) do
|
||||
for _, f in ipairs(infiles) do
|
||||
if (f:getName() == filename) then
|
||||
if (f:getName() == filename) then
|
||||
running[coroutine.running()] = nil
|
||||
waitLast()
|
||||
return true, f
|
||||
return true, f
|
||||
end
|
||||
end
|
||||
condvar "wait"
|
||||
|
||||
296
nselib/tns.lua
296
nselib/tns.lua
@@ -7,7 +7,7 @@
|
||||
-- to the Oracle database server. Some preliminary query support has been
|
||||
-- added, which only works against a few specific versions. The library has
|
||||
-- been tested against and known to work with Oracle 10g and 11g. Please check
|
||||
-- the matrix below for tested versions that are known to work.
|
||||
-- the matrix below for tested versions that are known to work.
|
||||
--
|
||||
-- Due to the lack of documentation the library is based mostly on guesswork
|
||||
-- with a lot of unknowns. Bug reports are therefore both welcome and
|
||||
@@ -25,10 +25,10 @@
|
||||
-- contain a function to parse the servers response.
|
||||
--
|
||||
-- o Comm
|
||||
-- - Implements a number of functions to handle communication
|
||||
-- - Implements a number of functions to handle communication
|
||||
--
|
||||
-- o Crypt
|
||||
-- - Implements encryption algorithms and functions to support
|
||||
-- - Implements encryption algorithms and functions to support
|
||||
-- authentication with Oracle 10G and Oracle 11G.
|
||||
--
|
||||
-- o Helper
|
||||
@@ -131,9 +131,9 @@ DataTypes = {
|
||||
}
|
||||
|
||||
-- A class containing some basic authentication options
|
||||
AuthOptions =
|
||||
AuthOptions =
|
||||
{
|
||||
|
||||
|
||||
-- Creates a new AuthOptions instance
|
||||
-- @return o new instance of AuthOptions
|
||||
new = function( self )
|
||||
@@ -147,22 +147,22 @@ AuthOptions =
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
-- Decodes different datatypes from the byte arrays or strings read from the
|
||||
-- tns data packets
|
||||
DataTypeDecoders = {
|
||||
|
||||
|
||||
-- Decodes a number
|
||||
[DataTypes.NUMBER] = function(val)
|
||||
if ( #val == 0 ) then return "" end
|
||||
if ( #val == 1 and val == '\128' ) then return 0 end
|
||||
|
||||
|
||||
local bytes = {}
|
||||
for i=1, #val do bytes[i] = select(2, bin.unpack("C", val, i)) end
|
||||
|
||||
|
||||
local positive = ( bit.band(bytes[1], 0x80) ~= 0 )
|
||||
|
||||
local function convert_bytes(bytes, positive)
|
||||
@@ -176,37 +176,37 @@ DataTypeDecoders = {
|
||||
ret_bytes[1] = bit.band(bit.bxor(bytes[1], 0xFF), 0x7F) - 65
|
||||
for i=2, len do ret_bytes[i] = 101 - bytes[i] end
|
||||
end
|
||||
|
||||
|
||||
return ret_bytes
|
||||
end
|
||||
|
||||
|
||||
bytes = convert_bytes(bytes, positive)
|
||||
|
||||
local k = ( #bytes - 1 > bytes[1] +1 ) and ( bytes[1] + 1 ) or #bytes - 1
|
||||
local l = 0
|
||||
for m=1, k do l = l * 100 + bytes[m+1] end
|
||||
for m=bytes[1]-#bytes - 1, 0, -1 do l = l * 100 end
|
||||
|
||||
|
||||
return (positive and l or -l)
|
||||
end,
|
||||
|
||||
-- Decodes a date
|
||||
[DataTypes.DATE] = function(val)
|
||||
local bytes = {}
|
||||
|
||||
|
||||
if (#val == 0) then
|
||||
return ""
|
||||
elseif( #val ~= 7 ) then
|
||||
return "ERROR: Failed to decode date"
|
||||
end
|
||||
|
||||
|
||||
for i=1, 7 do bytes[i] = select(2, bin.unpack("C", val, i)) end
|
||||
|
||||
return ("%d-%02d-%02d"):format( (bytes[1] - 100 ) * 100 + bytes[2] - 100, bytes[3], bytes[4] )
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Packet class table
|
||||
@@ -216,7 +216,7 @@ DataTypeDecoders = {
|
||||
-- o toString - A function that serializes the object to string
|
||||
--
|
||||
-- Each Packet MAY also optionally implement:
|
||||
-- o parseResponse
|
||||
-- o parseResponse
|
||||
-- x An optional function that parses the servers response
|
||||
-- x The function should return status and an undefined second return value
|
||||
--
|
||||
@@ -231,7 +231,7 @@ Packet.TNS = {
|
||||
length = 0,
|
||||
reserved = 0,
|
||||
|
||||
Type =
|
||||
Type =
|
||||
{
|
||||
CONNECT = 1,
|
||||
ACCEPT = 2,
|
||||
@@ -249,7 +249,7 @@ Packet.TNS = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Read a TNS packet of the socket
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
@@ -263,22 +263,22 @@ Packet.TNS = {
|
||||
|
||||
local _
|
||||
_, self.length = bin.unpack(">S", data )
|
||||
|
||||
|
||||
status, data = self.socket:receive_buf( match.numbytes(6), true )
|
||||
if ( not(status) ) then
|
||||
return status, data
|
||||
end
|
||||
|
||||
|
||||
_, self.checksum, self.type, self.reserved, self.hdr_checksum = bin.unpack(">SCCS", data)
|
||||
|
||||
|
||||
status, data = self.socket:receive_buf( match.numbytes(self.length - 8), true )
|
||||
if ( status ) then
|
||||
self.data = data
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
parse = function(data)
|
||||
local tns = Packet.TNS:new()
|
||||
local pos
|
||||
@@ -286,7 +286,7 @@ Packet.TNS = {
|
||||
pos, tns.data = bin.unpack("A" .. ( tns.length - 8 ), data, pos)
|
||||
return tns
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the TNS packet to string suitable to be sent over the socket
|
||||
--
|
||||
-- @return string containing the TNS packet
|
||||
@@ -294,17 +294,17 @@ Packet.TNS = {
|
||||
local data = bin.pack(">SSCCSA", self.length, self.checksum, self.type, self.reserved, self.hdr_checksum, self.data )
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Initiates the connection to the listener
|
||||
Packet.Connect = {
|
||||
|
||||
|
||||
CONN_STR = [[
|
||||
(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=%s)(PORT=%d))
|
||||
(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=%s)(CID=
|
||||
(PROGRAM=sqlplus)(HOST=%s)(USER=nmap))))]],
|
||||
|
||||
|
||||
version = 314,
|
||||
version_comp = 300,
|
||||
svc_options = 0x0c41,
|
||||
@@ -322,7 +322,7 @@ Packet.Connect = {
|
||||
trace_cross_2 = 0,
|
||||
trace_unique_conn = 0,
|
||||
tns_type = Packet.TNS.Type.CONNECT,
|
||||
|
||||
|
||||
-- Creates a new Connect instance
|
||||
-- @param rhost string containing host or ip
|
||||
-- @param rport string containing the port number
|
||||
@@ -339,14 +339,14 @@ Packet.Connect = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
setCmd = function( self, cmd )
|
||||
local tmp = [[
|
||||
local tmp = [[
|
||||
(DESCRIPTION=(CONNECT_DATA=(CID=(PROGRAM=)(HOST=%s)(USER=nmap))(COMMAND=%s)(ARGUMENTS=64)(SERVICE=%s:%d)(VERSION=185599488)))
|
||||
]]
|
||||
self.conn_data = tmp:format( self.rhost, cmd, self.rhost, self.rport )
|
||||
end,
|
||||
|
||||
|
||||
--- Parses the server response from the CONNECT
|
||||
--
|
||||
-- @param tns Packet.TNS containing the TNS packet received from the
|
||||
@@ -356,18 +356,18 @@ Packet.Connect = {
|
||||
-- server or an error message on failure
|
||||
parseResponse = function( self, tns )
|
||||
local pos, version
|
||||
|
||||
|
||||
if ( tns.type ~= Packet.TNS.Type.ACCEPT ) then
|
||||
if ( tns.data:match("ERR=12514") ) then
|
||||
return false, ("TNS: The listener could not resolve \"%s\""):format(self.dbinstance)
|
||||
end
|
||||
return false, tns.data:match("%(ERR=(%d*)%)")
|
||||
end
|
||||
|
||||
|
||||
pos, version = bin.unpack(">S", tns.data )
|
||||
return true, version
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the CONNECT packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
@@ -381,8 +381,8 @@ Packet.Connect = {
|
||||
self.conn_data_flags_1, self.trace_cross_1, self.trace_cross_2,
|
||||
self.trace_unique_conn, 0, self.conn_data )
|
||||
end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- A TNS data packet, one of the most common packets
|
||||
@@ -401,7 +401,7 @@ Packet.Data = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
@@ -410,7 +410,7 @@ Packet.Data = {
|
||||
self.TNS.length = #data + 8
|
||||
return tostring(self.TNS) .. data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Packet received by the server to indicate errors or end of
|
||||
@@ -430,26 +430,26 @@ Packet.Attention = {
|
||||
|
||||
--- Converts the MARKER packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
return bin.pack( ">C", self.att_type ) .. self.data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Packet initializing challenge response authentication
|
||||
Packet.PreAuth = {
|
||||
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0,
|
||||
param_order = {
|
||||
flags = 0,
|
||||
param_order = {
|
||||
{ ["AUTH_TERMINAL"] = "auth_term" },
|
||||
{ ["AUTH_PROGRAM_NM"] = "auth_prog" },
|
||||
{ ["AUTH_MACHINE"] = "auth_machine" },
|
||||
{ ["AUTH_PID"] = "auth_pid" },
|
||||
{ ["AUTH_SID"] = "auth_sid" }
|
||||
},
|
||||
|
||||
|
||||
--- Creates a new PreAuth packet
|
||||
--
|
||||
-- @param user string containing the user name
|
||||
@@ -463,7 +463,7 @@ Packet.PreAuth = {
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
local packet_type = 0x0376
|
||||
local UNKNOWN_MAP = {
|
||||
@@ -481,10 +481,10 @@ Packet.PreAuth = {
|
||||
data = data .. Marshaller.marshalKvp( k, self.auth_options[v2] )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
--- Parses the PreAuth packet response and extracts data needed to
|
||||
-- perform authentication
|
||||
--
|
||||
@@ -494,7 +494,7 @@ Packet.PreAuth = {
|
||||
local kvps = {}
|
||||
local pos, kvp_count = bin.unpack( "C", tns.data, 4 )
|
||||
pos = 6
|
||||
|
||||
|
||||
for kvp_itr=1, kvp_count do
|
||||
local key, val, kvp_flags
|
||||
pos, key, val, kvp_flags = Marshaller.unmarshalKvp( tns.data, pos )
|
||||
@@ -509,10 +509,10 @@ Packet.PreAuth = {
|
||||
|
||||
-- Packet containing authentication data
|
||||
Packet.Auth = {
|
||||
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0,
|
||||
param_order = {
|
||||
param_order = {
|
||||
{ ['key'] = "AUTH_RTT", ['def'] = "25456" },
|
||||
{ ['key'] = "AUTH_CLNT_MEM", ['def'] = "4096" },
|
||||
{ ['key'] = "AUTH_TERMINAL", ['var'] = "auth_term" },
|
||||
@@ -546,7 +546,7 @@ Packet.Auth = {
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
local UNKNOWN_MAP = {
|
||||
["Linuxi386/Linux-2.0.34-8.1.0"] = bin.pack("HCH","0338be0808", #self.user, "00000001010000cc7dbfbf0d000000747abfbf608abfbf"),
|
||||
@@ -554,14 +554,14 @@ Packet.Auth = {
|
||||
["IBMPC/WIN_NT64-9.1.0"] = bin.pack("H","03010400000001010000010d0000000101"),
|
||||
["x86_64/Linux 2.4.xx"] = bin.pack("H","03010400000001010000010d0000000101")
|
||||
}
|
||||
|
||||
|
||||
local sess_id = select(2, bin.unpack("H16", openssl.rand_pseudo_bytes(16)))
|
||||
local unknown = UNKNOWN_MAP[self.version] or ""
|
||||
local data = bin.pack(">SSA", self.flags, 0x0373, unknown)
|
||||
data = data .. bin.pack("CA", #self.user, self.user )
|
||||
data = data .. Marshaller.marshalKvp( "AUTH_SESSKEY", self.auth_sesskey, 1 )
|
||||
data = data .. Marshaller.marshalKvp( "AUTH_PASSWORD", self.auth_pass )
|
||||
|
||||
|
||||
for k, v in ipairs( self.param_order ) do
|
||||
if ( v['def'] ) then
|
||||
data = data .. Marshaller.marshalKvp( v['key'], v['def'] )
|
||||
@@ -571,9 +571,9 @@ Packet.Auth = {
|
||||
data = data .. Marshaller.marshalKvp( v['key'], self[ v['var'] ] )
|
||||
end
|
||||
end
|
||||
return data
|
||||
return data
|
||||
end,
|
||||
|
||||
|
||||
-- Parses the response of an Auth packet
|
||||
--
|
||||
-- @param tns Packet.TNS containing the TNS packet recieved from the server
|
||||
@@ -582,7 +582,7 @@ Packet.Auth = {
|
||||
local kvps = {}
|
||||
local pos, kvp_count = bin.unpack( "C", tns.data, 4 )
|
||||
pos = 6
|
||||
|
||||
|
||||
for kvp_itr=1, kvp_count do
|
||||
local key, val, kvp_flags
|
||||
pos, key, val, kvp_flags = Marshaller.unmarshalKvp( tns.data, pos )
|
||||
@@ -592,14 +592,14 @@ Packet.Auth = {
|
||||
|
||||
return true, kvps
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Packet.SNS = {
|
||||
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0,
|
||||
|
||||
|
||||
-- Creates a new SNS instance
|
||||
--
|
||||
-- @return o new instance of the SNS packet
|
||||
@@ -609,21 +609,21 @@ Packet.SNS = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
return bin.pack("SH", self.flags,
|
||||
[[
|
||||
return bin.pack("SH", self.flags,
|
||||
[[
|
||||
deadbeef00920b1006000004000004000300000000000400050b10060000080
|
||||
001000015cb353abecb00120001deadbeef0003000000040004000100010002
|
||||
0001000300000000000400050b10060000020003e0e100020006fcff0002000
|
||||
200000000000400050b100600000c0001001106100c0f0a0b08020103000300
|
||||
0200000000000400050b10060000030001000301
|
||||
]] )
|
||||
end,
|
||||
end,
|
||||
}
|
||||
|
||||
-- Packet containing protocol negotiation
|
||||
@@ -631,21 +631,21 @@ Packet.ProtoNeg = {
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0,
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
local pfx = bin.pack(">SH", self.flags, "0106050403020100")
|
||||
return pfx .. "Linuxi386/Linux-2.0.34-8.1.0\0"
|
||||
end,
|
||||
return pfx .. "Linuxi386/Linux-2.0.34-8.1.0\0"
|
||||
end,
|
||||
|
||||
--- Parses and verifies the server response
|
||||
--
|
||||
@@ -655,7 +655,7 @@ Packet.ProtoNeg = {
|
||||
if ( neg ~= 1 ) then
|
||||
return false, "Error protocol negotiation failed"
|
||||
end
|
||||
|
||||
|
||||
if ( ver ~= 6 ) then
|
||||
return false, ("Error protocol version (%d) not supported"):format(ver)
|
||||
end
|
||||
@@ -668,7 +668,7 @@ Packet.Unknown1 = {
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0,
|
||||
|
||||
|
||||
--- Creates a new Packet.Unknown1
|
||||
--
|
||||
-- @param version containing the version of the packet to send
|
||||
@@ -679,10 +679,10 @@ Packet.Unknown1 = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
|
||||
if ( self.os:match("IBMPC/WIN_NT[64]*[-]%d%.%d%.%d") ) then
|
||||
@@ -823,8 +823,8 @@ Packet.Unknown1 = {
|
||||
return bin.pack(">SH", self.flags, "02b200b2004225060101010d010105010101010101017fff0309030301007f011" ..
|
||||
"fff010301013f01010500010702010000180001800000003c3c3c80000000d007")
|
||||
end
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -833,17 +833,17 @@ Packet.Unknown2 = {
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0,
|
||||
|
||||
|
||||
new = function(self, os)
|
||||
local o = { os = os }
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
if ( "x86_64/Linux 2.4.xx" == self.os ) then
|
||||
return bin.pack(">SH", self.flags, [[
|
||||
@@ -881,8 +881,8 @@ Packet.Unknown2 = {
|
||||
00000e900e90001000000f1006d0001000002030203000100000000
|
||||
]])
|
||||
end
|
||||
end,
|
||||
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
-- Signals that we're about to close the connection
|
||||
@@ -890,27 +890,27 @@ Packet.EOF = {
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0x0040,
|
||||
|
||||
|
||||
new = function(self)
|
||||
local o = {}
|
||||
setmetatable(o, self)
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
return bin.pack(">S", self.flags )
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
Packet.PostLogin = {
|
||||
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0x0000,
|
||||
|
||||
|
||||
-- Creates a new PostLogin instance
|
||||
--
|
||||
-- @param sessid number containing session id
|
||||
@@ -921,26 +921,26 @@ Packet.PostLogin = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
local unknown1 = "116b04"
|
||||
local unknown2 = "0000002200000001000000033b05fefffffff4010000fefffffffeffffff"
|
||||
return bin.pack(">SHCH", self.flags, unknown1, tonumber(self.sessid), unknown2 )
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
-- Class responsible for sending queries to the server and handling the first
|
||||
-- row returned by the server. This class is 100% based on packet captures and
|
||||
-- guesswork.
|
||||
Packet.Query = {
|
||||
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0x0000,
|
||||
|
||||
|
||||
--- Creates a new instance of Query
|
||||
-- @param query string containing the SQL query
|
||||
-- @return instance of Query
|
||||
@@ -950,19 +950,19 @@ Packet.Query = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Gets the current counter value
|
||||
-- @return counter number containing the current counter value
|
||||
getCounter = function(self) return self.counter end,
|
||||
|
||||
|
||||
--- Sets the current counter value
|
||||
-- This function is called from sendTNSPacket
|
||||
-- @param counter number containing the counter value to set
|
||||
setCounter = function(self, counter) self.counter = counter end,
|
||||
|
||||
|
||||
--- Converts the DATA packet to string
|
||||
--
|
||||
-- @return string containing the packet
|
||||
-- @return string containing the packet
|
||||
__tostring = function( self )
|
||||
local unknown1 = "035e"
|
||||
local unknown2 = "6180000000000000feffffff"
|
||||
@@ -970,7 +970,7 @@ Packet.Query = {
|
||||
local unknown4 = "01000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000"
|
||||
return bin.pack(">SHCHCHCAH", self.flags, unknown1, self.counter, unknown2, #self.query, unknown3, #self.query, self.query, unknown4 )
|
||||
end,
|
||||
|
||||
|
||||
--- Parses the Query response from the server
|
||||
-- @param tns response as received from the <code>Comm.recvTNSPacket</code>
|
||||
-- function.
|
||||
@@ -983,9 +983,9 @@ Packet.Query = {
|
||||
parseResponse = function( self, tns )
|
||||
local data = tns.data
|
||||
local result = {}
|
||||
|
||||
|
||||
local pos, columns = bin.unpack("C", tns.data, 35)
|
||||
|
||||
|
||||
pos = 40
|
||||
for i=1, columns do
|
||||
local sql_type
|
||||
@@ -1000,9 +1000,9 @@ Packet.Query = {
|
||||
table.insert(result.types, sql_type)
|
||||
pos = pos + 10
|
||||
end
|
||||
|
||||
|
||||
pos = pos + 55
|
||||
|
||||
|
||||
result.rows = {}
|
||||
local row = {}
|
||||
for i=1, columns do
|
||||
@@ -1023,7 +1023,7 @@ Packet.Query = {
|
||||
table.insert(row, val)
|
||||
end
|
||||
table.insert(result.rows, row)
|
||||
|
||||
|
||||
local moredata = true
|
||||
-- check if we've got any more data?
|
||||
if ( #data > pos + 97 ) then
|
||||
@@ -1034,7 +1034,7 @@ Packet.Query = {
|
||||
moredata = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return true, { data = result, moredata = moredata }
|
||||
end,
|
||||
}
|
||||
@@ -1046,7 +1046,7 @@ Packet.QueryResponseAck = {
|
||||
|
||||
tns_type = Packet.TNS.Type.DATA,
|
||||
flags = 0x0000,
|
||||
|
||||
|
||||
--- Creates a new QueryResponseAck instance
|
||||
-- @param result table containing the results as received from the
|
||||
-- <code>Query.parseResponse</code> function.
|
||||
@@ -1061,19 +1061,19 @@ Packet.QueryResponseAck = {
|
||||
--- Gets the current counter value
|
||||
-- @return counter number containing the current counter value
|
||||
getCounter = function(self) return self.counter end,
|
||||
|
||||
|
||||
--- Sets the current counter value
|
||||
-- This function is called from sendTNSPacket
|
||||
-- @param counter number containing the counter value to set
|
||||
setCounter = function(self, counter) self.counter = counter end,
|
||||
|
||||
|
||||
--- Serializes the packet into a string suitable to be sent to the DB
|
||||
-- server.
|
||||
-- @return str string containing the serialized packet
|
||||
__tostring = function(self)
|
||||
return bin.pack(">SHCH", self.flags, "0305", self.counter, "030000000f000000")
|
||||
end,
|
||||
|
||||
|
||||
--
|
||||
-- This is how I (Patrik Karlsson) think this is supposed to work
|
||||
-- At this point we have the 2nd row (the query response has the first)
|
||||
@@ -1093,7 +1093,7 @@ Packet.QueryResponseAck = {
|
||||
-- data it is run through a decoder, that decodes the *real* value from
|
||||
-- the encoded data.
|
||||
--
|
||||
parseResponse = function( self, tns )
|
||||
parseResponse = function( self, tns )
|
||||
local data = tns.data
|
||||
local pos, len = bin.unpack("C", data, 21)
|
||||
local mask = ""
|
||||
@@ -1108,7 +1108,7 @@ Packet.QueryResponseAck = {
|
||||
end
|
||||
pos = pos + 4
|
||||
else
|
||||
pos = pos +3
|
||||
pos = pos +3
|
||||
end
|
||||
|
||||
while(true) do
|
||||
@@ -1149,7 +1149,7 @@ Packet.QueryResponseAck = {
|
||||
else
|
||||
pos, len = bin.unpack("C", data, pos)
|
||||
pos, val = bin.unpack("A" .. len, data, pos)
|
||||
|
||||
|
||||
local sql_type = result.types[col]
|
||||
if ( DataTypeDecoders[sql_type] ) then
|
||||
val = DataTypeDecoders[sql_type](val)
|
||||
@@ -1164,7 +1164,7 @@ Packet.QueryResponseAck = {
|
||||
|
||||
return true, tns.data
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Marshaller = {
|
||||
@@ -1175,11 +1175,11 @@ Marshaller = {
|
||||
-- @param flags The flags
|
||||
-- @return A binary packed string representing the KVP structure
|
||||
marshalKvp = function( key, value, flags )
|
||||
return Marshaller.marshalKvpComponent( key ) ..
|
||||
return Marshaller.marshalKvpComponent( key ) ..
|
||||
Marshaller.marshalKvpComponent( value ) ..
|
||||
bin.pack( "<I", ( flags or 0 ) )
|
||||
end,
|
||||
|
||||
|
||||
--- Parses a TNS key-value pair data structure.
|
||||
--
|
||||
-- @param data Packed string to parse
|
||||
@@ -1187,14 +1187,14 @@ Marshaller = {
|
||||
-- @return table containing the last position read, the key, the value, and the KVP flags
|
||||
unmarshalKvp = function( data, pos )
|
||||
local key, value, flags
|
||||
|
||||
|
||||
pos, key = Marshaller.unmarshalKvpComponent( data, pos )
|
||||
pos, value = Marshaller.unmarshalKvpComponent( data, pos )
|
||||
pos, flags = bin.unpack("<I", data, pos )
|
||||
|
||||
|
||||
return pos, key, value, flags
|
||||
end,
|
||||
|
||||
|
||||
--- Marshals a key or value element from a TNS key-value pair data structure
|
||||
--
|
||||
-- @param value The key or value
|
||||
@@ -1241,7 +1241,7 @@ Marshaller = {
|
||||
|
||||
return result
|
||||
end,
|
||||
|
||||
|
||||
--- Parses a key or value element from a TNS key-value pair data structure.
|
||||
--
|
||||
-- @param data Packed string to parse
|
||||
@@ -1284,14 +1284,14 @@ Marshaller = {
|
||||
|
||||
-- The TNS communication class uses the TNSSocket to transmit data
|
||||
Comm = {
|
||||
|
||||
|
||||
--- Creates a new instance of the Comm class
|
||||
--
|
||||
-- @param socket containing a TNSSocket
|
||||
-- @return new instance of Comm
|
||||
new = function(self, socket)
|
||||
local o = {
|
||||
socket = socket,
|
||||
local o = {
|
||||
socket = socket,
|
||||
data_counter = 06
|
||||
}
|
||||
setmetatable(o, self)
|
||||
@@ -1351,9 +1351,9 @@ Comm = {
|
||||
local msg
|
||||
pos, msg = bin.unpack("p", tns.data, pos )
|
||||
|
||||
return false, msg
|
||||
return false, msg
|
||||
end,
|
||||
|
||||
|
||||
--- Recieves a TNS packet and handles TNS-resends
|
||||
--
|
||||
-- @return status true on success, false on failure
|
||||
@@ -1390,7 +1390,7 @@ Comm = {
|
||||
|
||||
return true, tns
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a TNS packet and recieves (and handles) the response
|
||||
--
|
||||
-- @param pkt containingt the Packet.* to send to the server
|
||||
@@ -1400,7 +1400,7 @@ Comm = {
|
||||
exchTNSPacket = function( self, pkt )
|
||||
local status = self:sendTNSPacket( pkt )
|
||||
local tns, response
|
||||
|
||||
|
||||
if ( not(status) ) then
|
||||
return false, "sendTNSPacket failed"
|
||||
end
|
||||
@@ -1421,7 +1421,7 @@ Comm = {
|
||||
|
||||
return status, response
|
||||
end
|
||||
|
||||
|
||||
}
|
||||
|
||||
--- Class that handles all Oracle encryption
|
||||
@@ -1432,7 +1432,7 @@ Crypt = {
|
||||
local combined_sesskey = ""
|
||||
local sha1 = openssl.sha1(pass .. salt) .. "\0\0\0\0"
|
||||
local auth_sesskey = s_sesskey
|
||||
local auth_sesskey_c = c_sesskey
|
||||
local auth_sesskey_c = c_sesskey
|
||||
local server_sesskey = openssl.decrypt( "aes-192-cbc", sha1, nil, auth_sesskey )
|
||||
local client_sesskey = openssl.decrypt( "aes-192-cbc", sha1, nil, auth_sesskey_c )
|
||||
|
||||
@@ -1475,7 +1475,7 @@ Crypt = {
|
||||
local cli_sesskey = openssl.decrypt( "AES-128-CBC", pwhash, nil, cli_sesskey_enc )
|
||||
local auth_pass = bin.pack("H", "4C5E28E66B6382117F9D41B08957A3B9E363B42760C33B44CA5D53EA90204ABE" )
|
||||
local combined_sesskey = ""
|
||||
local pass
|
||||
local pass
|
||||
|
||||
for i=17, 32 do
|
||||
combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) )
|
||||
@@ -1489,7 +1489,7 @@ Crypt = {
|
||||
print( select(2, bin.unpack("H" .. #combined_sesskey, combined_sesskey )))
|
||||
print( "pass=" .. pass )
|
||||
end,
|
||||
|
||||
|
||||
--- Performs the relevant encryption needed for the Oracle 10g response
|
||||
--
|
||||
-- @param user containing the Oracle user name
|
||||
@@ -1510,7 +1510,7 @@ Crypt = {
|
||||
local rnd = bin.pack("H", "4C31AFE05F3B012C0AE9AB0CDFF0C508")
|
||||
local combined_sesskey = ""
|
||||
local auth_pass
|
||||
|
||||
|
||||
for i=17, 32 do
|
||||
combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) )
|
||||
end
|
||||
@@ -1529,7 +1529,7 @@ Crypt = {
|
||||
-- @param auth_vrfy_data containing the password salt as recieved from the
|
||||
-- PreAuth packet
|
||||
-- @return cli_sesskey_enc the encrypted client session key
|
||||
-- @return auth_pass the encrypted Oracle password
|
||||
-- @return auth_pass the encrypted Oracle password
|
||||
Encrypt11g = function( self, pass, srv_sesskey_enc, auth_vrfy_data )
|
||||
|
||||
-- This value should really be random, not this static cruft
|
||||
@@ -1541,7 +1541,7 @@ Crypt = {
|
||||
local cli_sesskey_enc
|
||||
local combined_sesskey = ""
|
||||
local data = ""
|
||||
|
||||
|
||||
for i=17, 40 do
|
||||
combined_sesskey = combined_sesskey .. string.char( bit.bxor( string.byte(srv_sesskey, i), string.byte(cli_sesskey, i) ) )
|
||||
end
|
||||
@@ -1555,19 +1555,19 @@ Crypt = {
|
||||
|
||||
return cli_sesskey_enc, auth_password
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
Helper = {
|
||||
|
||||
|
||||
--- Creates a new Helper instance
|
||||
--
|
||||
-- @param host table containing the host table as received by action
|
||||
-- @param port table containing the port table as received by action
|
||||
-- @param instance string containing the instance name
|
||||
-- @return o new instance of Helper
|
||||
-- @return o new instance of Helper
|
||||
new = function(self, host, port, instance )
|
||||
local o = {
|
||||
local o = {
|
||||
host = host,
|
||||
port = port,
|
||||
socket = nmap.new_socket(),
|
||||
@@ -1578,7 +1578,7 @@ Helper = {
|
||||
self.__index = self
|
||||
return o
|
||||
end,
|
||||
|
||||
|
||||
--- Connects and performs protocol negotiation with the Oracle server
|
||||
--
|
||||
-- @return true on success, false on failure
|
||||
@@ -1594,7 +1594,7 @@ Helper = {
|
||||
local conn, packet, tns
|
||||
|
||||
if( not(status) ) then return status, data end
|
||||
|
||||
|
||||
self.comm = Comm:new( self.socket )
|
||||
|
||||
status, self.version = self.comm:exchTNSPacket( Packet.Connect:new( self.host.ip, self.port.number, self.dbinstance ) )
|
||||
@@ -1632,12 +1632,12 @@ Helper = {
|
||||
status = self.comm:sendTNSPacket( Packet.Unknown1:new( self.os ) )
|
||||
if ( not(status) ) then
|
||||
return false, "ERROR: Helper.Connect failed"
|
||||
end
|
||||
end
|
||||
status, data = self.comm:sendTNSPacket( Packet.Unknown2:new( self.os ) )
|
||||
if ( not(status) ) then return false, data end
|
||||
if ( not(status) ) then return false, data end
|
||||
|
||||
status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) )
|
||||
if ( not(status) ) then return false, data end
|
||||
if ( not(status) ) then return false, data end
|
||||
-- Oracle 10g under Windows needs this additional read, there's
|
||||
-- probably a better way to detect this by analysing the packets
|
||||
-- further.
|
||||
@@ -1659,7 +1659,7 @@ Helper = {
|
||||
status, data = self.comm:recvTNSPacket( Packet.Unknown2:new( ) )
|
||||
if ( not(status) ) then
|
||||
return false, data
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
status = self.comm:exchTNSPacket( Packet.Unknown1:new( self.os ) )
|
||||
@@ -1667,10 +1667,10 @@ Helper = {
|
||||
return false, "ERROR: Helper.Connect failed"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end,
|
||||
|
||||
|
||||
--- Sends a command to the TNS lsnr
|
||||
-- It currently accepts and tries to send all commands recieved
|
||||
--
|
||||
@@ -1706,7 +1706,7 @@ Helper = {
|
||||
|
||||
return true, data
|
||||
end,
|
||||
|
||||
|
||||
--- Authenticates to the database
|
||||
--
|
||||
-- @param user containing the Oracle user name
|
||||
@@ -1757,7 +1757,7 @@ Helper = {
|
||||
return false
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
--- Queries the database
|
||||
--
|
||||
-- @param query string containing the SQL query
|
||||
@@ -1812,7 +1812,7 @@ Helper = {
|
||||
local status = self.comm:sendTNSPacket( Packet.EOF:new( ) )
|
||||
self.socket:close()
|
||||
end,
|
||||
|
||||
|
||||
}
|
||||
|
||||
return _ENV;
|
||||
|
||||
@@ -64,7 +64,7 @@ end
|
||||
--
|
||||
-- Holds and runs tests.
|
||||
TestSuite = {
|
||||
|
||||
|
||||
--- Creates a new TestSuite object
|
||||
--
|
||||
-- @return TestSuite object
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user