mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 12:41:29 +00:00
Add afp.lua and afp-showmount.nse from Patrik Karlsson. See
http://seclists.org/nmap-dev/2010/q1/97.
This commit is contained in:
@@ -1,5 +1,9 @@
|
||||
# Nmap Changelog ($Id$); -*-text-*-
|
||||
|
||||
o [NSE] Added a new library, afp.lua, and a script that uses it,
|
||||
afp-showmount. The library is for the Apple Filing Protocol and the
|
||||
script shows shares and their permissions. [Patrik Karlsson]
|
||||
|
||||
o Fixed a bug with the decoding of NMAP OID component values greater
|
||||
than 127. [Patrik Karlsson, David]
|
||||
|
||||
|
||||
549
nselib/afp.lua
Normal file
549
nselib/afp.lua
Normal file
@@ -0,0 +1,549 @@
|
||||
---
|
||||
-- This module was written by Patrik Karlsson and facilitates communication
|
||||
-- with the Apple AFP Service. It is not feature complete and is missing several
|
||||
-- functions and parameters.
|
||||
--
|
||||
-- The library currently has enough functionality to query share names and access controls.
|
||||
-- More functionality will be added once more scripts that depend on it are developed.
|
||||
--
|
||||
--
|
||||
|
||||
-- Version 0.1
|
||||
-- Created 01/03/2010 - v0.1 - created by Patrik Karlsson
|
||||
|
||||
module(... or "afp",package.seeall)
|
||||
|
||||
-- Table of valid REQUESTs
|
||||
local REQUEST = {
|
||||
OpenSession = 0x04,
|
||||
Command = 0x02
|
||||
}
|
||||
|
||||
-- Table of headers flags to be set accordingly in requests and responses
|
||||
local FLAGS = {
|
||||
Request = 0,
|
||||
Response = 1
|
||||
}
|
||||
|
||||
-- Table of possible AFP_COMMANDs
|
||||
COMMAND = {
|
||||
FPCloseVol = 0x02,
|
||||
FPLogin = 0x12,
|
||||
FPGetUserInfo = 0x25,
|
||||
FPGetSrvParms = 0x10,
|
||||
FPOpenVol = 0x18,
|
||||
FPOpenFork = 0x1a,
|
||||
FPGetFileDirParams = 0x22,
|
||||
FPReadExt = 0x3c,
|
||||
FPEnumerateExt2 = 0x44
|
||||
}
|
||||
|
||||
USER_BITMAP = {
|
||||
UserId = 1,
|
||||
PrimaryGroupId = 2,
|
||||
UUID = 4
|
||||
}
|
||||
|
||||
VOL_BITMAP = {
|
||||
Attributes = 1,
|
||||
Signature = 2,
|
||||
CreationDate = 4,
|
||||
ModificationDate = 8,
|
||||
BackupDate = 16,
|
||||
ID = 32,
|
||||
BytesFree = 64,
|
||||
BytesTotal = 128,
|
||||
Name = 256,
|
||||
ExtendedBytesFree = 512,
|
||||
ExtendedBytesTotal = 1024,
|
||||
BlockSize = 2048
|
||||
}
|
||||
|
||||
FILE_BITMAP = {
|
||||
Attributes = 1,
|
||||
DID = 2,
|
||||
CreationDate = 4,
|
||||
ModificationDate = 8,
|
||||
BackupDate = 16,
|
||||
FinderInfo = 32,
|
||||
LongName = 64,
|
||||
ShortName = 128,
|
||||
FileId = 256,
|
||||
DataForkSize = 512,
|
||||
ResourceForkSize = 1024,
|
||||
ExtendedDataForkSize = 2048,
|
||||
LaunchLimit = 4096,
|
||||
UTF8Name = 8192,
|
||||
ExtendedResourceForkSize = 16384,
|
||||
UnixPrivileges = 32768
|
||||
}
|
||||
|
||||
DIR_BITMAP = {
|
||||
Attributes = 1,
|
||||
DID = 2,
|
||||
CreationDate = 4,
|
||||
ModificationDate = 8,
|
||||
BackupDate = 16,
|
||||
FinderInfo = 32,
|
||||
LongName = 64,
|
||||
ShortName = 128,
|
||||
FileId = 256,
|
||||
OffspringCount = 512,
|
||||
OwnerId = 1024,
|
||||
GroupId = 2048,
|
||||
AccessRights = 4096,
|
||||
UTF8Name = 8192,
|
||||
UnixPrivileges = 32768
|
||||
}
|
||||
|
||||
PATH_TYPE = {
|
||||
LongNames = 2,
|
||||
UnicodeNames = 3
|
||||
}
|
||||
|
||||
ACCESS_MODE = {
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
DenyRead = 16,
|
||||
DenyWrite = 32
|
||||
}
|
||||
|
||||
ACLS = {
|
||||
OwnerSearch = 1,
|
||||
OwnerRead = 2,
|
||||
OwnerWrite = 4,
|
||||
|
||||
GroupSearch = 256,
|
||||
GroupRead = 512,
|
||||
GroupWrite = 1024,
|
||||
|
||||
EveryoneSearch = 65536,
|
||||
EveryoneRead = 131072,
|
||||
EveryoneWrite = 262144,
|
||||
|
||||
UserSearch = 1048576,
|
||||
UserRead = 2097152,
|
||||
UserWrite = 4194304,
|
||||
|
||||
BlankAccess = 268435456,
|
||||
UserIsOwner = 2147483648
|
||||
}
|
||||
|
||||
-- Each packet contains a sequential request id
|
||||
-- this number is used within <code>create_fp_packet</code> and increased by one in each call
|
||||
request_id = 1
|
||||
|
||||
|
||||
--- Creates an AFP packet
|
||||
--
|
||||
-- @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
|
||||
function create_fp_packet( command, data_offset, data )
|
||||
|
||||
local reserved = 0
|
||||
local data = data or ""
|
||||
local data_len = data:len()
|
||||
local header = bin.pack("CC>SIII", FLAGS.Request, command, request_id, data_offset, data_len, reserved)
|
||||
local packet = header .. data
|
||||
|
||||
request_id = request_id + 1
|
||||
return packet
|
||||
end
|
||||
|
||||
--- Parses the FP header (first 16-bytes of packet)
|
||||
--
|
||||
-- @param packet string containing the raw packet
|
||||
-- @return table with header data containing <code>flags</code>, <code>command</code>,
|
||||
-- <code>request_id</code>, <code>error_code</code>, <code>length</code> and <code>reserved</code> fields
|
||||
function parse_fp_header( packet )
|
||||
|
||||
local header = {}
|
||||
local pos
|
||||
|
||||
pos, header.flags, header.command, header.request_id = bin.unpack( "CC>S", packet )
|
||||
pos, header.error_code, header.length, header.reserved = bin.unpack( "I>II", packet:sub(5) )
|
||||
header.raw = packet:sub(1,16)
|
||||
|
||||
return header
|
||||
|
||||
end
|
||||
|
||||
--- Sends an OpenSession AFP request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @return status (true or false)
|
||||
-- @return nil (if status is true) or error string (if status is false)
|
||||
function open_session( socket )
|
||||
|
||||
local data_offset = 0
|
||||
local option = 0x01 -- Attention Quantum
|
||||
local option_len = 4
|
||||
local quantum = 1024
|
||||
|
||||
local data = bin.pack( "CCI", option, option_len, quantum )
|
||||
local packet = create_fp_packet( REQUEST.OpenSession, data_offset, data )
|
||||
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("OpenSession error: %d", packet.header.error_code)
|
||||
end
|
||||
|
||||
return true, nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Sends an FPGetUserInfo AFP request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @return status (true or false)
|
||||
-- @return table with user information containing <code>user_bitmap</code> and
|
||||
-- <code>uid</code> fields (if status is true) or error string (if status is false)
|
||||
function fp_get_user_info( socket )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local flags = 1 -- Default User
|
||||
local uid = 0
|
||||
local bitmap = USER_BITMAP.UserId
|
||||
local response = {}
|
||||
local pos
|
||||
|
||||
local data = bin.pack( "CCI>S", COMMAND.FPGetUserInfo, flags, uid, bitmap )
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("OpenSession error: %d", packet.header.error_code)
|
||||
end
|
||||
|
||||
pos, response.user_bitmap, response.uid = bin.unpack(">S>I", packet.data)
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
--- Sends an FPGetSrvrParms AFP request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @return status (true or false)
|
||||
-- @return table with server parameters containing <code>server_time</code>,
|
||||
-- <code>vol_count</code>, <code>volumes</code> fields (if status is true) or error string (if status is false)
|
||||
--
|
||||
function fp_get_srvr_parms(socket)
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local response = {}
|
||||
local pos = 0
|
||||
|
||||
local data = bin.pack("CC", COMMAND.FPGetSrvParms, 0)
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPGetSrvrParms error: %d", packet.header.error_code)
|
||||
end
|
||||
|
||||
data = packet.data
|
||||
pos, response.server_time, response.vol_count = bin.unpack("IC", data)
|
||||
|
||||
-- we should now be at the leading zero preceeding the first volume name
|
||||
-- next is the length of the volume name, move pos there
|
||||
pos = pos + 1
|
||||
|
||||
stdnse.print_debug("Volumes: %d", response.vol_count )
|
||||
response.volumes = {}
|
||||
|
||||
for i=1, response.vol_count do
|
||||
local _, vol_len = bin.unpack("C", data:sub(pos))
|
||||
local volume_name = data:sub(pos + 1, pos + 1 + vol_len)
|
||||
pos = pos + vol_len + 2
|
||||
table.insert(response.volumes, string.format("%s", volume_name) )
|
||||
stdnse.print_debug("Volume name: %s", volume_name)
|
||||
end
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
|
||||
--- Sends an FPLogin request to the server and handles the response
|
||||
--
|
||||
-- This function currently only supports the 3.1 through 3.3 protocol versions
|
||||
-- It does not support authentication so the uam parameter is currently ignored
|
||||
--
|
||||
-- @param socket already connected to the server--
|
||||
-- @param afp_version string (AFP3.3|AFP3.2|AFP3.1)
|
||||
-- @param uam string containing authentication information (currently ignored)
|
||||
-- @return status (true or false)
|
||||
-- @return nil (if status is true) or error string (if status is false)
|
||||
function fp_login( socket, afp_version, uam )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
|
||||
-- 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
|
||||
return
|
||||
end
|
||||
|
||||
uam = "No User Authent"
|
||||
|
||||
local data = bin.pack( "CCACA", COMMAND.FPLogin, afp_version:len(), afp_version, uam:len(), uam )
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPLogin error: %d", packet.header.error_code)
|
||||
end
|
||||
|
||||
return true, nil
|
||||
end
|
||||
|
||||
--- Reads a AFP packet of the socket
|
||||
--
|
||||
-- @param socket socket connected to the server
|
||||
-- @return table containing <code>data</code> and <code>header</code> fields
|
||||
function read_fp_packet( socket )
|
||||
|
||||
local packet = {}
|
||||
local buf = ""
|
||||
|
||||
local catch = function()
|
||||
socket:close()
|
||||
end
|
||||
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
repeat
|
||||
buf = buf .. try( socket:receive(16) )
|
||||
until buf:len() >= 16 -- make sure we have got atleast the header
|
||||
|
||||
packet.header = parse_fp_header( buf )
|
||||
|
||||
-- if we didn't get the whole packet when reading the header, try to read the rest
|
||||
while buf:len() < packet.header.length + packet.header.raw:len() do
|
||||
buf = buf .. try( socket:receive(packet.header.length) )
|
||||
end
|
||||
|
||||
packet.data = buf:len() > 16 and buf:sub( 17 ) or ""
|
||||
|
||||
return packet
|
||||
|
||||
end
|
||||
|
||||
--- Sends an FPOpenVol request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @param bitmap number bitmask of volume information to request
|
||||
-- @param volume_name string containing the volume name to query
|
||||
-- @return status (true or false)
|
||||
-- @return table containing <code>bitmap</code> and <code>volume_id</code> fields
|
||||
-- (if status is true) or error string (if status is false)
|
||||
function fp_open_vol( socket, bitmap, volume_name )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
local response = {}
|
||||
local pos
|
||||
|
||||
local data = bin.pack("CC>SCA", COMMAND.FPOpenVol, pad, bitmap, volume_name:len(), volume_name )
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPOpenVol error: %d", packet.header.error_code )
|
||||
end
|
||||
|
||||
pos, response.bitmap, response.volume_id = bin.unpack(">S>S", packet.data)
|
||||
|
||||
return true, response
|
||||
|
||||
end
|
||||
|
||||
--- Sends an FPGetFileDirParms request to the server and handles the response
|
||||
--
|
||||
-- Currently only handles a request for the Access rights (file_bitmap must be 0 and dir_bitmap must be 0x1000)
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @param volume_id number containing the id of the volume to query
|
||||
-- @param did number containing the id of the directory to query
|
||||
-- @param file_bitmap number bitmask of file information to query
|
||||
-- @param dir_bitmap number bitmask of directory information to query
|
||||
-- @param path string containing the name of the directory to query
|
||||
-- @return status (true or false)
|
||||
-- @return table containing <code>file_bitmap</code>, <code>dir_bitmap</code>,
|
||||
-- <code>file_type</code> and <code>acls</code> fields
|
||||
-- (if status is true) or error string (if status is false)
|
||||
function fp_get_file_dir_parms( socket, volume_id, did, file_bitmap, dir_bitmap, path )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
local response = {}
|
||||
local pos
|
||||
|
||||
if file_bitmap ~= 0 or dir_bitmap ~= DIR_BITMAP.AccessRights then
|
||||
return false, "Only AccessRights querys are supported (file_bitmap=0, dir_bitmap=DIR_BITMAP.AccessRights)"
|
||||
end
|
||||
|
||||
local data = bin.pack("CC>S>I>S>SCCAC", COMMAND.FPGetFileDirParams, pad, volume_id, did, file_bitmap, dir_bitmap, path.type, path.len, path.name, 0)
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPGetFileDirParms error: %d", packet.header.error_code )
|
||||
end
|
||||
|
||||
pos, response.file_bitmap, response.dir_bitmap, response.file_type, pad, response.acls = bin.unpack( ">S>SCC>I", packet.data )
|
||||
|
||||
return true, response
|
||||
end
|
||||
|
||||
--- Sends an FPEnumerateExt2 request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @param volume_id number containing the id of the volume to query
|
||||
-- @param did number containing the id of the directory to query
|
||||
-- @param file_bitmap number bitmask of file information to query
|
||||
-- @param dir_bitmap number bitmask of directory information to query
|
||||
-- @param req_count number
|
||||
-- @param start_index number
|
||||
-- @param reply_size number
|
||||
-- @param path string containing the name of the directory to query
|
||||
-- @return status (true or false)
|
||||
-- @return table containing <code>file_bitmap</code>, <code>dir_bitmap</code>,
|
||||
-- <code>req_count</code> fields
|
||||
-- (if status is true) or error string (if status is false)
|
||||
function fp_enumerate_ext2( socket, volume_id, did, file_bitmap, dir_bitmap, req_count, start_index, reply_size, path )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
local response = {}
|
||||
|
||||
local data = bin.pack( "CC>S>I>S>S", COMMAND.FPEnumerateExt2, pad, volume_id, did, file_bitmap, dir_bitmap )
|
||||
data = data .. bin.pack( ">S>I>IC>SA", req_count, start_index, reply_size, path.type, path.len, path.name )
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPEnumerateExt2 error: %d", packet.header.error_code )
|
||||
end
|
||||
|
||||
_, response.file_bitmap, response.dir_bitmap, response.req_count = bin.unpack(">S>S>S", packet.data)
|
||||
|
||||
return true, response
|
||||
|
||||
end
|
||||
|
||||
--- Sends an FPOpenFork request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @param fork number
|
||||
-- @param volume_id number containing the id of the volume to query
|
||||
-- @param did number containing the id of the directory to query
|
||||
-- @param file_bitmap number bitmask of file information to query
|
||||
-- @param access_mode number containing bitmask of options from <code>ACCESS_MODE</code>
|
||||
-- @param path string containing the name of the directory to query
|
||||
-- @return status (true or false)
|
||||
-- @return table containing <code>file_bitmap</code> and <code>fork</code> fields (if status is true) or
|
||||
-- error string (if status is false)
|
||||
function fp_open_fork( socket, fork, volume_id, did, file_bitmap, access_mode, path )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
local response = {}
|
||||
|
||||
local data = bin.pack( "CC>S>I>S>S", COMMAND.FPOpenFork, fork, volume_id, did, file_bitmap, access_mode )
|
||||
|
||||
if path.type == PATH_TYPE.LongNames then
|
||||
data = data .. bin.pack( "C>SA", path.type, path.len, path.name )
|
||||
end
|
||||
|
||||
if path.type == PATH_TYPE.UnicodeNames then
|
||||
local unicode_hint = 0x08000103
|
||||
data = data .. bin.pack( "C>I>SA", path.type, unicode_hint, path.len, path.name )
|
||||
end
|
||||
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPOpenFork error: %d", packet.header.error_code )
|
||||
end
|
||||
|
||||
_, response.file_bitmap, response.fork = bin.unpack(">S>S", packet.data)
|
||||
|
||||
return true, response
|
||||
|
||||
end
|
||||
|
||||
--- Sends an FPCloseVol request to the server and handles the response
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @param volume_id number containing the id of the volume to close
|
||||
-- @return status (true or false)
|
||||
-- @return nil (if status is true) or error string (if status is false)
|
||||
function fp_close_vol( socket, volume_id )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
local response = {}
|
||||
|
||||
local data = bin.pack( "CC>S>", COMMAND.FPCloseVol, pad, volume_id )
|
||||
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
send_fp_packet( socket, packet )
|
||||
packet = read_fp_packet( socket )
|
||||
|
||||
if packet.header.error_code ~= 0 then
|
||||
return false, string.format("FPCloseVol error: %d", packet.header.error_code )
|
||||
end
|
||||
|
||||
return true, nil
|
||||
|
||||
end
|
||||
|
||||
--- Sends the raw packet over the socket
|
||||
--
|
||||
-- @param socket already connected to the server
|
||||
-- @param packet containing the raw data
|
||||
function send_fp_packet( socket, packet )
|
||||
|
||||
local catch = function()
|
||||
socket:close()
|
||||
end
|
||||
|
||||
local try = nmap.new_try(catch)
|
||||
try( socket:send(packet) )
|
||||
|
||||
end
|
||||
|
||||
|
||||
function fp_read_ext( fork, offset, count )
|
||||
|
||||
local packet
|
||||
local data_offset = 0
|
||||
local pad = 0
|
||||
|
||||
local data = bin.pack( "CC>S>L>L", COMMAND.FPReadExt, pad, fork, offset, count )
|
||||
packet = create_fp_packet( REQUEST.Command, data_offset, data )
|
||||
|
||||
return packet
|
||||
|
||||
end
|
||||
194
scripts/afp-showmount.nse
Normal file
194
scripts/afp-showmount.nse
Normal file
@@ -0,0 +1,194 @@
|
||||
description = [[ Shows AFP shares and ACLs ]]
|
||||
|
||||
---
|
||||
--@output
|
||||
-- PORT STATE SERVICE
|
||||
-- 548/tcp open afp
|
||||
-- | afp-showmount:
|
||||
-- | Yoda's Public Folder
|
||||
-- | Owner: Search,Read,Write
|
||||
-- | Group: Search,Read
|
||||
-- | Everyone: Search,Read
|
||||
-- | User: Search,Read
|
||||
-- | Vader's Public Folder
|
||||
-- | Owner: Search,Read,Write
|
||||
-- | Group: Search,Read
|
||||
-- | Everyone: Search,Read
|
||||
-- | User: Search,Read
|
||||
-- |_ Options: IsOwner
|
||||
|
||||
-- Version 0.1
|
||||
-- Created 01/03/2010 - v0.1 - created by Patrik Karlsson
|
||||
-- Revised 01/13/2010 - v0.2 - Fixed a bug where a single share wouldn't show due to formatting issues
|
||||
|
||||
author = "Patrik Karlsson"
|
||||
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||
categories = {"discovery", "safe"}
|
||||
|
||||
require 'shortport'
|
||||
require 'stdnse'
|
||||
require 'afp'
|
||||
|
||||
portrule = shortport.portnumber(548, "tcp")
|
||||
|
||||
--- Converts a group bitmask of Search, Read and Write to string
|
||||
-- eg. WRS, -RS, W-S, etc ..
|
||||
--
|
||||
-- @param acls number containing bitmasked acls
|
||||
-- @return string of ACLS
|
||||
function acl_group_to_string( acls )
|
||||
|
||||
local acl_string = ""
|
||||
|
||||
if bit.band( acls, afp.ACLS.OwnerSearch ) == afp.ACLS.OwnerSearch then
|
||||
acl_string = "S"
|
||||
else
|
||||
acl_string = "-"
|
||||
end
|
||||
|
||||
if bit.band( acls, afp.ACLS.OwnerRead ) == afp.ACLS.OwnerRead then
|
||||
acl_string = "R" .. acl_string
|
||||
else
|
||||
acl_string = "-" .. acl_string
|
||||
end
|
||||
|
||||
if bit.band( acls, afp.ACLS.OwnerWrite ) == afp.ACLS.OwnerWrite then
|
||||
acl_string = "W" .. acl_string
|
||||
else
|
||||
acl_string = "-" .. acl_string
|
||||
end
|
||||
|
||||
return acl_string
|
||||
end
|
||||
|
||||
--- Converts a group bitmask of Search, Read and Write to table
|
||||
--
|
||||
-- @param acls number containing bitmasked acls
|
||||
-- @return table of ACLs
|
||||
function acl_group_to_long_string(acls)
|
||||
|
||||
local acl_table = {}
|
||||
|
||||
if bit.band( acls, afp.ACLS.OwnerSearch ) == afp.ACLS.OwnerSearch then
|
||||
table.insert( acl_table, "Search")
|
||||
end
|
||||
|
||||
if bit.band( acls, afp.ACLS.OwnerRead ) == afp.ACLS.OwnerRead then
|
||||
table.insert( acl_table, "Read")
|
||||
end
|
||||
|
||||
if bit.band( acls, afp.ACLS.OwnerWrite ) == afp.ACLS.OwnerWrite then
|
||||
table.insert( acl_table, "Write")
|
||||
end
|
||||
|
||||
return acl_table
|
||||
end
|
||||
|
||||
--- Converts a numeric acl to string
|
||||
--
|
||||
-- @param acls number containig acls as recieved from <code>fp_get_file_dir_parms</code>
|
||||
-- @return string of ACLs
|
||||
function acls_to_string( acls )
|
||||
|
||||
local owner = acl_group_to_string( bit.band( acls, 255 ) )
|
||||
local group = acl_group_to_string( bit.band( bit.rshift(acls, 8), 255 ) )
|
||||
local everyone = acl_group_to_string( bit.band( bit.rshift(acls, 16), 255 ) )
|
||||
local user = acl_group_to_string( bit.band( bit.rshift(acls, 24), 255 ) )
|
||||
|
||||
local blank = bit.band( acls, afp.ACLS.BlankAccess ) == afp.ACLS.BlankAccess and "B" or "-"
|
||||
local isowner = bit.band( acls, afp.ACLS.UserIsOwner ) == afp.ACLS.UserIsOwner and "O" or "-"
|
||||
|
||||
return string.format("Owner: %s; Group: %s; Everyone: %s; User: %s; Options: %s%s", owner, group, everyone, user, blank, isowner )
|
||||
|
||||
end
|
||||
|
||||
--- Converts a numeric acl to string
|
||||
--
|
||||
-- @param acls number containig acls as recieved from <code>fp_get_file_dir_parms</code>
|
||||
-- @return table of long ACLs
|
||||
function acls_to_long_string( acls )
|
||||
|
||||
local owner = acl_group_to_long_string( bit.band( acls, 255 ) )
|
||||
local group = acl_group_to_long_string( bit.band( bit.rshift(acls, 8), 255 ) )
|
||||
local everyone = acl_group_to_long_string( bit.band( bit.rshift(acls, 16), 255 ) )
|
||||
local user = acl_group_to_long_string( bit.band( bit.rshift(acls, 24), 255 ) )
|
||||
|
||||
local blank = bit.band( acls, afp.ACLS.BlankAccess ) == afp.ACLS.BlankAccess and "Blank" or nil
|
||||
local isowner = bit.band( acls, afp.ACLS.UserIsOwner ) == afp.ACLS.UserIsOwner and "IsOwner" or nil
|
||||
|
||||
local options = {}
|
||||
|
||||
if blank then
|
||||
table.insert(options, "Blank")
|
||||
end
|
||||
|
||||
if isowner then
|
||||
table.insert(options, "IsOwner")
|
||||
end
|
||||
|
||||
local acls_tbl = {}
|
||||
|
||||
table.insert( acls_tbl, string.format( "Owner: %s", stdnse.strjoin(",", owner) ) )
|
||||
table.insert( acls_tbl, string.format( "Group: %s", stdnse.strjoin(",", group) ) )
|
||||
table.insert( acls_tbl, string.format( "Everyone: %s", stdnse.strjoin(",", everyone) ) )
|
||||
table.insert( acls_tbl, string.format( "User: %s", stdnse.strjoin(",", user) ) )
|
||||
|
||||
if #options > 0 then
|
||||
table.insert( acls_tbl, string.format( "Options: %s", stdnse.strjoin(",", options ) ) )
|
||||
end
|
||||
|
||||
return acls_tbl
|
||||
|
||||
end
|
||||
|
||||
action = function(host, port)
|
||||
|
||||
local socket = nmap.new_socket()
|
||||
local status
|
||||
local result = {}
|
||||
|
||||
-- set a reasonable timeout value
|
||||
socket:set_timeout(5000)
|
||||
|
||||
-- do some exception handling / cleanup
|
||||
local catch = function()
|
||||
socket:close()
|
||||
end
|
||||
|
||||
local try = nmap.new_try(catch)
|
||||
|
||||
try( socket:connect(host.ip, port.number, "tcp") )
|
||||
|
||||
response = try( afp.open_session(socket) )
|
||||
response = try( afp.fp_login( socket, "AFP3.1", "No User Authent") )
|
||||
response = try( afp.fp_get_user_info( socket ) )
|
||||
response = try( afp.fp_get_srvr_parms( socket ) )
|
||||
|
||||
volumes = response.volumes
|
||||
|
||||
for _, vol in pairs(volumes) do
|
||||
table.insert( result, vol )
|
||||
|
||||
status, response = afp.fp_open_vol( socket, afp.VOL_BITMAP.ID, vol )
|
||||
|
||||
if status then
|
||||
local vol_id = response.volume_id
|
||||
stdnse.print_debug(string.format("Vol_id: %d", vol_id))
|
||||
|
||||
local path = {}
|
||||
path.type = afp.PATH_TYPE.LongNames
|
||||
path.name = ""
|
||||
path.len = path.name:len()
|
||||
|
||||
response = try( afp.fp_get_file_dir_parms( socket, vol_id, 2, 0, afp.DIR_BITMAP.AccessRights, path ) )
|
||||
local acls = acls_to_long_string(response.acls)
|
||||
acls.name = nil
|
||||
try( afp.fp_close_vol( socket, vol_id ) )
|
||||
table.insert( result, acls )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return stdnse.format_output(true, result)
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user