Land #8293, add initial multi-platform railgun support

bug/bundler_fix
Brent Cook 2017-05-11 22:32:23 -05:00
commit 123462bdca
No known key found for this signature in database
GPG Key ID: 1FFAA0B24B708F96
25 changed files with 539 additions and 173 deletions

View File

@ -7,7 +7,7 @@ module Windows
module Railgun
# Go through each dll and add a corresponding convenience method of the same name
Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun::BUILTIN_DLLS.each do |api|
Rex::Post::Meterpreter::Extensions::Stdapi::Railgun::Railgun::BUILTIN_DLLS['windows'].each do |api|
# We will be interpolating within an eval. We exercise due paranoia.
unless api.to_s =~ /^\w+$/
print_error 'Something is seriously wrong with Railgun.BUILTIN_DLLS list'

View File

@ -23,6 +23,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
require 'thread'
module Rex
module Post
module Meterpreter
@ -31,9 +33,53 @@ module Stdapi
module Railgun
#
# Manages our library of windows constants
# A container holding useful API Constants.
#
class WinConstManager
class ApiConstants
# This will be lazily loaded in self.manager
@manager = nil
# Mutex to ensure we don't add constants more than once via thread races.
@manager_semaphore = Mutex.new
class << self
attr_accessor :manager_semaphore
end
def self.inherited(child_class)
child_class.manager_semaphore = Mutex.new
end
#
# Provides a frozen constant manager for the constants defined in
# self.add_constants
#
def self.manager
# The first check for nil is to potentially skip the need to synchronize
if @manager.nil?
# Looks like we MAY need to load manager
@manager_semaphore.synchronize do
# We check once more. Now our options are synchronized
if @manager.nil?
@manager = ConstManager.new
self.add_constants(@manager)
@manager.freeze
end
end
end
return @manager
end
end
#
# Manages our library of constants
#
class ConstManager
attr_reader :consts
def initialize(initial_consts = {})
@ -72,14 +118,14 @@ class WinConstManager
end
#
# Returns an array of constant names that have a value matching "winconst"
# Returns an array of constant names that have a value matching "const"
# and (optionally) a name that matches "filter_regex"
#
def select_const_names(winconst, filter_regex=nil)
def select_const_names(const, filter_regex=nil)
matches = []
consts.each_pair do |name, value|
matches << name if value == winconst
matches << name if value == const
end
# Filter matches by name if a filter has been provided

View File

@ -0,0 +1,153 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/stdapi/railgun/const_manager'
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Railgun
module Def
#
# A container holding useful Linux API Constants.
#
class DefApiConstants_linux < ApiConstants
#
# Slurp in a giant list of known constants.
#
def self.add_constants(const_mgr)
const_mgr.add_const('MAP_FILE', 0x00)
const_mgr.add_const('MAP_SHARED', 0x01)
const_mgr.add_const('MAP_PRIVATE', 0x02)
const_mgr.add_const('MAP_FIXED', 0x10)
const_mgr.add_const('MAP_ANON', 0x20)
const_mgr.add_const('MAP_ANONYMOUS', 0x20)
const_mgr.add_const('PROT_NONE', 0x00)
const_mgr.add_const('PROT_READ', 0x01)
const_mgr.add_const('PROT_WRITE', 0x02)
const_mgr.add_const('PROT_EXEC', 0x04)
const_mgr.add_const('PROT_GROWSDOWN', 0x01000000)
const_mgr.add_const('PROT_GROWSUP', 0x02000000)
const_mgr.add_const("PF_UNSPEC", 0x00000000)
const_mgr.add_const("PF_LOCAL", 0x00000001)
const_mgr.add_const("PF_UNIX", 0x00000000)
const_mgr.add_const("PF_FILE", 0x00000000)
const_mgr.add_const("PF_INET", 0x00000002)
const_mgr.add_const("PF_AX25", 0x00000003)
const_mgr.add_const("PF_IPX", 0x00000004)
const_mgr.add_const("PF_APPLETALK", 0x00000005)
const_mgr.add_const("PF_NETROM", 0x00000006)
const_mgr.add_const("PF_BRIDGE", 0x00000007)
const_mgr.add_const("PF_ATMPVC", 0x00000008)
const_mgr.add_const("PF_X25", 0x00000009)
const_mgr.add_const("PF_INET6", 0x0000000a)
const_mgr.add_const("PF_ROSE", 0x0000000b)
const_mgr.add_const("PF_DECnet", 0x0000000c)
const_mgr.add_const("PF_NETBEUI", 0x0000000d)
const_mgr.add_const("PF_SECURITY", 0x0000000e)
const_mgr.add_const("PF_KEY", 0x0000000f)
const_mgr.add_const("PF_NETLINK", 0x00000010)
const_mgr.add_const("PF_ROUTE", 0x00000000)
const_mgr.add_const("PF_PACKET", 0x00000011)
const_mgr.add_const("PF_ASH", 0x00000012)
const_mgr.add_const("PF_ECONET", 0x00000013)
const_mgr.add_const("PF_ATMSVC", 0x00000014)
const_mgr.add_const("PF_RDS", 0x00000015)
const_mgr.add_const("PF_SNA", 0x00000016)
const_mgr.add_const("PF_IRDA", 0x00000017)
const_mgr.add_const("PF_PPPOX", 0x00000018)
const_mgr.add_const("PF_WANPIPE", 0x00000019)
const_mgr.add_const("PF_LLC", 0x0000001a)
const_mgr.add_const("PF_IB", 0x0000001b)
const_mgr.add_const("PF_MPLS", 0x0000001c)
const_mgr.add_const("PF_CAN", 0x0000001d)
const_mgr.add_const("PF_TIPC", 0x0000001e)
const_mgr.add_const("PF_BLUETOOTH", 0x0000001f)
const_mgr.add_const("PF_IUCV", 0x00000020)
const_mgr.add_const("PF_RXRPC", 0x00000021)
const_mgr.add_const("PF_ISDN", 0x00000022)
const_mgr.add_const("PF_PHONET", 0x00000023)
const_mgr.add_const("PF_IEEE802154", 0x00000024)
const_mgr.add_const("PF_CAIF", 0x00000025)
const_mgr.add_const("PF_ALG", 0x00000026)
const_mgr.add_const("PF_NFC", 0x00000027)
const_mgr.add_const("PF_VSOCK", 0x00000028)
const_mgr.add_const("PF_KCM", 0x00000029)
const_mgr.add_const("PF_MAX", 0x0000002a)
const_mgr.add_const("AF_UNSPEC", 0x00000000)
const_mgr.add_const("AF_LOCAL", 0x00000001)
const_mgr.add_const("AF_UNIX", 0x00000000)
const_mgr.add_const("AF_FILE", 0x00000000)
const_mgr.add_const("AF_INET", 0x00000002)
const_mgr.add_const("AF_AX25", 0x00000003)
const_mgr.add_const("AF_IPX", 0x00000004)
const_mgr.add_const("AF_APPLETALK", 0x00000005)
const_mgr.add_const("AF_NETROM", 0x00000006)
const_mgr.add_const("AF_BRIDGE", 0x00000007)
const_mgr.add_const("AF_ATMPVC", 0x00000008)
const_mgr.add_const("AF_X25", 0x00000009)
const_mgr.add_const("AF_INET6", 0x0000000a)
const_mgr.add_const("AF_ROSE", 0x0000000b)
const_mgr.add_const("AF_DECnet", 0x0000000c)
const_mgr.add_const("AF_NETBEUI", 0x0000000d)
const_mgr.add_const("AF_SECURITY", 0x0000000e)
const_mgr.add_const("AF_KEY", 0x0000000f)
const_mgr.add_const("AF_NETLINK", 0x00000010)
const_mgr.add_const("AF_ROUTE", 0x00000000)
const_mgr.add_const("AF_PACKET", 0x00000011)
const_mgr.add_const("AF_ASH", 0x00000012)
const_mgr.add_const("AF_ECONET", 0x00000013)
const_mgr.add_const("AF_ATMSVC", 0x00000014)
const_mgr.add_const("AF_RDS", 0x00000015)
const_mgr.add_const("AF_SNA", 0x00000016)
const_mgr.add_const("AF_IRDA", 0x00000017)
const_mgr.add_const("AF_PPPOX", 0x00000018)
const_mgr.add_const("AF_WANPIPE", 0x00000019)
const_mgr.add_const("AF_LLC", 0x0000001a)
const_mgr.add_const("AF_IB", 0x0000001b)
const_mgr.add_const("AF_MPLS", 0x0000001c)
const_mgr.add_const("AF_CAN", 0x0000001d)
const_mgr.add_const("AF_TIPC", 0x0000001e)
const_mgr.add_const("AF_BLUETOOTH", 0x0000001f)
const_mgr.add_const("AF_IUCV", 0x00000020)
const_mgr.add_const("AF_RXRPC", 0x00000021)
const_mgr.add_const("AF_ISDN", 0x00000022)
const_mgr.add_const("AF_PHONET", 0x00000023)
const_mgr.add_const("AF_IEEE802154", 0x00000024)
const_mgr.add_const("AF_CAIF", 0x00000025)
const_mgr.add_const("AF_ALG", 0x00000026)
const_mgr.add_const("AF_NFC", 0x00000027)
const_mgr.add_const("AF_VSOCK", 0x00000028)
const_mgr.add_const("AF_KCM", 0x00000029)
const_mgr.add_const("AF_MAX", 0x0000002a)
const_mgr.add_const("SOL_RAW", 0x000000ff)
const_mgr.add_const("SOL_DECNET", 0x00000105)
const_mgr.add_const("SOL_X25", 0x00000106)
const_mgr.add_const("SOL_PACKET", 0x00000107)
const_mgr.add_const("SOL_ATM", 0x00000108)
const_mgr.add_const("SOL_AAL", 0x00000109)
const_mgr.add_const("SOL_IRDA", 0x0000010a)
const_mgr.add_const("SOL_NETBEUI", 0x0000010b)
const_mgr.add_const("SOL_LLC", 0x0000010c)
const_mgr.add_const("SOL_DCCP", 0x0000010d)
const_mgr.add_const("SOL_NETLINK", 0x0000010e)
const_mgr.add_const("SOL_TIPC", 0x0000010f)
const_mgr.add_const("SOL_RXRPC", 0x00000110)
const_mgr.add_const("SOL_PPPOL2TP", 0x00000111)
const_mgr.add_const("SOL_BLUETOOTH", 0x00000112)
const_mgr.add_const("SOL_PNPIPE", 0x00000113)
const_mgr.add_const("SOL_RDS", 0x00000114)
const_mgr.add_const("SOL_IUCV", 0x00000115)
const_mgr.add_const("SOL_CAIF", 0x00000116)
const_mgr.add_const("SOL_ALG", 0x00000117)
const_mgr.add_const("SOL_NFC", 0x00000118)
const_mgr.add_const("SOL_KCM", 0x00000119)
end
end
end; end; end; end; end; end; end

View File

@ -0,0 +1,121 @@
# -*- coding: binary -*-
module Rex
module Post
module Meterpreter
module Extensions
module Stdapi
module Railgun
module Def
class Def_libc
def self.create_dll(constant_manager, dll_path = 'libc.so.6')
dll = DLL.new(dll_path, constant_manager)
dll.add_function(
'calloc',
'LPVOID',
[
['SIZE_T', 'nmemb', 'in'],
['SIZE_T', 'size', 'in']
],
nil,
'cdecl'
)
dll.add_function(
'free',
'VOID',
[
['LPVOID', 'ptr', 'in']
],
nil,
'cdecl',
)
dll.add_function(
'getpid',
'DWORD',
[],
nil,
'cdecl'
)
dll.add_function(
'inet_ntop',
'LPVOID',
[
['DWORD', 'af', 'in'],
['PBLOB', 'src', 'in'],
['PBLOB', 'dst', 'out'],
['DWORD', 'size', 'in']
],
nil,
'cdecl'
)
dll.add_function(
'inet_pton',
'DWORD',
[
['DWORD', 'af', 'in'],
['PBLOB', 'src', 'in'],
['PBLOB', 'dst', 'out']
],
nil,
'cdecl'
)
dll.add_function(
'malloc',
'LPVOID',
[['SIZE_T', 'size', 'in']],
nil,
'cdecl'
)
dll.add_function(
'memfrob',
'LPVOID',
[
['PBLOB', 'mem', 'inout'],
['SIZE_T', 'length', 'in']
],
nil,
'cdecl'
)
dll.add_function(
'mmap',
'LPVOID',
[
['LPVOID', 'addr', 'in'],
['SIZE_T', 'length', 'in'],
['DWORD', 'prot', 'in'],
['DWORD', 'flags', 'in'],
['DWORD', 'fd', 'in'],
['SIZE_T', 'offset', 'in']
],
nil,
'cdecl'
)
dll.add_function(
'mprotect',
'DWORD',
[
['LPVOID', 'addr', 'in'],
['SIZE_T', 'length', 'in'],
['DWORD', 'prot', 'in']
],
nil,
'cdecl'
)
dll.add_function(
'munmap',
'DWORD',
[
['LPVOID', 'addr', 'in'],
['SIZE_T', 'length', 'in']
],
nil,
'cdecl'
)
return dll
end
end
end; end; end; end; end; end; end

View File

@ -1,6 +1,5 @@
# -*- coding: binary -*-
require 'rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager'
require 'thread'
require 'rex/post/meterpreter/extensions/stdapi/railgun/const_manager'
module Rex
module Post
@ -8,41 +7,12 @@ module Meterpreter
module Extensions
module Stdapi
module Railgun
module Def
#
# A container holding useful Windows API Constants.
#
class ApiConstants
# This will be lazily loaded in self.manager
@@manager = nil
# Mutex to ensure we don't add constants more than once via thread races.
@@manager_semaphore = Mutex.new
#
# Provides a frozen constant manager for the constants defined in
# self.add_constants
#
def self.manager
# The first check for nil is to potentially skip the need to synchronize
if @@manager.nil?
# Looks like we MAY need to load manager
@@manager_semaphore.synchronize do
# We check once more. Now our options are synchronized
if @@manager.nil?
@@manager = WinConstManager.new
self.add_constants(@@manager)
@@manager.freeze
end
end
end
return @@manager
end
class DefApiConstants_windows < ApiConstants
#
# Slurp in a giant list of known constants.
@ -2356,31 +2326,31 @@ class ApiConstants
win_const_mgr.add_const('RTM_VIEW_MASK_UCAST',0x00000001)
win_const_mgr.add_const('CERT_ALT_NAME_VALUE_ERR_INDEX_MASK',0x0000FFFF)
win_const_mgr.add_const('ERROR_NO_SUCH_GROUP',0x00000527)
# Generic Access Rights
win_const_mgr.add_const('GENERIC_ALL',0x10000000)
win_const_mgr.add_const('GENERIC_EXECUTE',0x20000000)
win_const_mgr.add_const('GENERIC_WRITE',0x40000000)
win_const_mgr.add_const('GENERIC_READ',0x80000000)
# Standard Access Rights
win_const_mgr.add_const('DELETE',0x00010000)
win_const_mgr.add_const('READ_CONTROL',0x00020000)
win_const_mgr.add_const('WRITE_DAC',0x00040000)
win_const_mgr.add_const('WRITE_OWNER',0x00080000)
win_const_mgr.add_const('ACCESS_SYSTEM_SECURITY',0x01000000)
# Services
win_const_mgr.add_const('SERVICE_NO_CHANGE',0xFFFFFFFF)
# Service Start Types
win_const_mgr.add_const('START_TYPE_BOOT',0x00000000)
win_const_mgr.add_const('START_TYPE_SYSTEM',0x00000001)
win_const_mgr.add_const('START_TYPE_AUTO',0x00000002)
win_const_mgr.add_const('START_TYPE_MANUAL',0x00000003)
win_const_mgr.add_const('START_TYPE_DISABLED',0x00000004)
# Service States
win_const_mgr.add_const('SERVICE_STOPPED',0x00000001)
win_const_mgr.add_const('SERVICE_START_PENDING',0x00000002)
@ -2389,7 +2359,7 @@ class ApiConstants
win_const_mgr.add_const('SERVICE_CONTINUE_PENDING',0x00000005)
win_const_mgr.add_const('SERVICE_PAUSE_PENDING',0x00000006)
win_const_mgr.add_const('SERVICE_PAUSED',0x00000007)
# Service Types
win_const_mgr.add_const('SERVICE_KERNEL_DRIVER',0x00000001)
win_const_mgr.add_const('SERVICE_FILE_SYSTEM_DRIVER',0x00000002)
@ -2397,7 +2367,7 @@ class ApiConstants
win_const_mgr.add_const('SERVICE_RECOGNIZER_DRIVER',0x00000008)
win_const_mgr.add_const('SERVICE_WIN32_OWN_PROCESS',0x00000010)
win_const_mgr.add_const('SERVICE_WIN32_SHARE_PROCESS',0x00000020)
# Service Manager Permissions
win_const_mgr.add_const('SC_MANAGER_CONNECT',0x00000001)
win_const_mgr.add_const('SC_MANAGER_CREATE_SERVICE',0x00000002)
@ -2407,7 +2377,7 @@ class ApiConstants
win_const_mgr.add_const('SC_MANAGER_MODIFY_BOOT_CONFIG',0x00000020)
win_const_mgr.add_const('SC_MANAGER_USER_DEFINED_CONTROL',0x00000100)
win_const_mgr.add_const('SC_MANAGER_ALL_ACCESS',0x000F003F)
# Service Permissions
win_const_mgr.add_const('SERVICE_QUERY_CONFIG',0x00000001)
win_const_mgr.add_const('SERVICE_CHANGE_CONFIG',0x00000002)
@ -2419,7 +2389,7 @@ class ApiConstants
win_const_mgr.add_const('SERVICE_INTERROGATE',0x00000080)
win_const_mgr.add_const('SERVICE_USER_DEFINED_CONTROL',0x00000100)
win_const_mgr.add_const('SERVICE_ALL_ACCESS',0x000F01FF)
win_const_mgr.add_const('LINEINITIALIZEEXOPTION_USECOMPLETIONPORT',0x00000003)
win_const_mgr.add_const('AVIIF_TWOCC',0x00000002)
win_const_mgr.add_const('TBTS_LEFT',0x00000001)
@ -38170,4 +38140,4 @@ class ApiConstants
end
end; end; end; end; end; end
end; end; end; end; end; end; end

View File

@ -24,8 +24,8 @@ class Def_advapi32
[:UserName, :LPTSTR]
]
def self.create_dll(dll_path = 'advapi32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'advapi32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('QueryServiceStatus', 'DWORD', [
['LPVOID', 'hService', 'in'],

View File

@ -9,8 +9,8 @@ module Def
class Def_crypt32
def self.create_dll(dll_path = 'crypt32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'crypt32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('CryptUnprotectData', 'BOOL', [
['PBLOB','pDataIn', 'in'],

View File

@ -9,8 +9,8 @@ module Def
class Def_iphlpapi
def self.create_dll(dll_path = 'iphlpapi')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'iphlpapi')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('CancelIPChangeNotify', 'BOOL',[
["PBLOB","notifyOverlapped","in"],

View File

@ -9,8 +9,8 @@ module Def
class Def_kernel32
def self.create_dll(dll_path = 'kernel32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'kernel32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function( 'GetConsoleWindow', 'LPVOID',[])

View File

@ -9,8 +9,8 @@ module Def
class Def_netapi32
def self.create_dll(dll_path = 'netapi32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'netapi32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('NetApiBufferFree','DWORD',[
["LPVOID","Buffer","in"]

View File

@ -9,8 +9,8 @@ module Def
class Def_ntdll
def self.create_dll(dll_path = 'ntdll')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'ntdll')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('NtAllocateVirtualMemory', 'DWORD',[
["DWORD","ProcessHandle","in"],

View File

@ -9,8 +9,8 @@ module Def
class Def_psapi
def self.create_dll(dll_path = 'psapi')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'psapi')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('EnumDeviceDrivers', 'BOOL',[
%w(PBLOB lpImageBase out),

View File

@ -9,8 +9,8 @@ module Def
class Def_shell32
def self.create_dll(dll_path = 'shell32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'shell32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('IsUserAnAdmin', 'BOOL', [
])

View File

@ -9,8 +9,8 @@ module Def
class Def_user32
def self.create_dll(dll_path = 'user32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'user32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('ActivateKeyboardLayout', 'DWORD',[
["DWORD","hkl","in"],

View File

@ -9,8 +9,8 @@ module Def
class Def_version
def self.create_dll(dll_path = 'version')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'version')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('GetFileVersionInfoA', 'BOOL',[
["PCHAR","lptstrFilename","in"],

View File

@ -9,8 +9,8 @@ module Def
class Def_wlanapi
def self.create_dll(dll_path = 'wlanapi')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'wlanapi')
dll = DLL.new(dll_path, constant_manager)
dll.add_function( 'WlanOpenHandle', 'DWORD',[

View File

@ -9,8 +9,8 @@ module Def
class Def_wldap32
def self.create_dll(dll_path = 'wldap32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'wldap32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('ldap_sslinitA', 'DWORD',[
['PCHAR', 'HostName', 'in'],

View File

@ -9,8 +9,8 @@ module Def
class Def_ws2_32
def self.create_dll(dll_path = 'ws2_32')
dll = DLL.new(dll_path, ApiConstants.manager)
def self.create_dll(constant_manager, dll_path = 'ws2_32')
dll = DLL.new(dll_path, constant_manager)
dll.add_function('getaddrinfo', 'DWORD',[
["PCHAR","pNodeName","in"],

View File

@ -46,11 +46,11 @@ class DLL
attr_accessor :functions
attr_reader :dll_path
def initialize(dll_path, win_consts)
def initialize(dll_path, consts_mgr)
@dll_path = dll_path
# needed by DLLHelper
@win_consts = win_consts
@consts_mgr = consts_mgr
self.functions = {}
end
@ -99,7 +99,7 @@ class DLL
# ["DWORD","uType","in"],
# ])
#
# Use +windows_name+ when the actual windows name is different from the
# Use +remote_name+ when the actual library name is different from the
# ruby variable. You might need to do this for example when the actual
# func name is myFunc@4 or when you want to create an alternative version
# of an existing function.
@ -107,11 +107,11 @@ class DLL
# When the new function is called it will return a list containing the
# return value and all inout params. See #call_function.
#
def add_function(name, return_type, params, windows_name=nil, calling_conv="stdcall")
if windows_name == nil
windows_name = name
def add_function(name, return_type, params, remote_name=nil, calling_conv="stdcall")
if remote_name == nil
remote_name = name
end
@functions[name] = DLLFunction.new(return_type, params, windows_name, calling_conv)
@functions[name] = DLLFunction.new(return_type, params, remote_name, calling_conv)
end
private
@ -125,8 +125,6 @@ class DLL
native = 'V'
end
#puts "process_function_call(function.windows_name,#{PP.pp(args, "")})"
# We transmit the immediate stack and three heap-buffers:
# in, inout and out. The reason behind the separation is bandwidth.
# We don't want to transmit uninitialized data in or no-longer-needed data out.
@ -221,7 +219,7 @@ class DLL
# it's not a pointer (LPVOID is a pointer but is not backed by railgun memory, ala PBLOB)
buffer = [0].pack(native)
case param_desc[0]
when "LPVOID", "HANDLE"
when "LPVOID", "HANDLE", "SIZE_T"
num = param_to_number(args[param_idx])
buffer += [num].pack(native)
when "DWORD"
@ -261,8 +259,8 @@ class DLL
request.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_IN, in_only_buffer)
request.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT, inout_buffer)
request.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, @dll_path )
request.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.windows_name)
request.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, @dll_path)
request.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.remote_name)
request.add_tlv(TLV_TYPE_RAILGUN_CALLCONV, function.calling_conv)
response = client.send_request(request)
@ -368,7 +366,7 @@ class DLL
#=== START of proccess_function_call snapshot ===
# {
# :platform => '#{native == 'Q' ? 'x64/windows' : 'x86/windows'}',
# :name => '#{function.windows_name}',
# :name => '#{function.remote_name}',
# :params => #{function.params},
# :return_type => '#{function.return_type}',
# :dll_name => '#{@dll_path}',
@ -379,7 +377,7 @@ class DLL
# TLV_TYPE_RAILGUN_BUFFERBLOB_IN => #{in_only_buffer.inspect},
# TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT => #{inout_buffer.inspect},
# TLV_TYPE_RAILGUN_DLLNAME => '#{@dll_path}',
# TLV_TYPE_RAILGUN_FUNCNAME => '#{function.windows_name}',
# TLV_TYPE_RAILGUN_FUNCNAME => '#{function.remote_name}',
# },
# :response_from_client => {
# TLV_TYPE_RAILGUN_BACK_BUFFERBLOB_INOUT => #{rec_inout_buffers.inspect},

View File

@ -40,8 +40,9 @@ class DLLFunction
"DWORD" => ["in", "return"],
"WORD" => ["in", "return"],
"BYTE" => ["in", "return"],
"LPVOID" => ["in", "return"], # sf: for specifying a memory address (e.g. VirtualAlloc/HeapAlloc/...) where we dont want ot back it up with actuall mem ala PBLOB
"LPVOID" => ["in", "return"], # sf: for specifying a memory address (e.g. VirtualAlloc/HeapAlloc/...) where we don't want to back it up with actual mem ala PBLOB
"HANDLE" => ["in", "return"],
"SIZE_T" => ["in", "return"],
"PDWORD" => ["in", "out", "inout"], # todo: support for functions that return pointers to strings
"PWCHAR" => ["in", "out", "inout"],
"PCHAR" => ["in", "out", "inout"],
@ -52,15 +53,15 @@ class DLLFunction
@@directions = ["in", "out", "inout", "return"].freeze
attr_reader :return_type, :params, :windows_name, :calling_conv
attr_reader :return_type, :params, :remote_name, :calling_conv
def initialize(return_type, params, windows_name, calling_conv="stdcall")
def initialize(return_type, params, remote_name, calling_conv="stdcall")
check_return_type(return_type) # we do error checking as early as possible so the library is easier to use
check_params(params)
check_calling_conv(calling_conv)
@return_type = return_type
@params = params
@windows_name = windows_name
@remote_name = remote_name
@calling_conv = calling_conv
end

View File

@ -38,7 +38,7 @@ module DLLHelper
# converts ruby string to zero-terminated ASCII string
def str_to_ascii_z(str)
return str+"\x00"
return str + "\x00"
end
# converts 0-terminated ASCII string to ruby string
@ -72,14 +72,14 @@ module DLLHelper
# "SOME_CONSTANT | OTHER_CONSTANT" => 17
# "tuna" => !!!!!!!!!!Exception
#
# Parameter "win_consts" is a WinConstantManager
def param_to_number(v, win_consts = @win_consts)
# Parameter "consts_mgr" is a ConstantManager
def param_to_number(v, consts_mgr = @consts_mgr)
if v.class == NilClass then
return 0
elsif v.kind_of? Integer then
return v # ok, it's already a number
elsif v.kind_of? String then
dw = win_consts.parse(v) # might raise an exception
dw = consts_mgr.parse(v) # might raise an exception
if dw != nil
return dw
else

View File

@ -25,7 +25,6 @@
require 'pp'
require 'enumerator'
require 'rex/post/meterpreter/extensions/stdapi/railgun/api_constants'
require 'rex/post/meterpreter/extensions/stdapi/railgun/tlv'
require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_helper'
require 'rex/post/meterpreter/extensions/stdapi/railgun/buffer_item'
@ -42,12 +41,12 @@ class MultiCaller
include DLLHelper
def initialize( client, parent, win_consts )
def initialize(client, parent, consts_mgr)
@parent = parent
@client = client
# needed by DLL helper
@win_consts = win_consts
@consts_mgr = consts_mgr
if @client.native_arch == ARCH_X64
@native = 'Q<'
@ -75,7 +74,6 @@ class MultiCaller
end
raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length
#puts "process_function_call(function.windows_name,#{PP.pp(args, "")})"
# We transmit the immediate stack and three heap-buffers:
# in, inout and out. The reason behind the separation is bandwidth.
@ -170,7 +168,7 @@ class MultiCaller
# it's not a pointer
buffer = [0].pack(@native)
case param_desc[0]
when "LPVOID", "HANDLE"
when "LPVOID", "HANDLE", "SIZE_T"
num = param_to_number(args[param_idx])
buffer += [num].pack(@native)
when "DWORD"
@ -209,8 +207,8 @@ class MultiCaller
group.add_tlv(TLV_TYPE_RAILGUN_STACKBLOB, literal_pairs_blob)
group.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_IN, in_only_buffer)
group.add_tlv(TLV_TYPE_RAILGUN_BUFFERBLOB_INOUT, inout_buffer)
group.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, dll_name )
group.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.windows_name)
group.add_tlv(TLV_TYPE_RAILGUN_DLLNAME, dll_host.dll_path)
group.add_tlv(TLV_TYPE_RAILGUN_FUNCNAME, function.remote_name)
request.tlvs << group
layouts << [inout_layout, out_only_layout]

View File

@ -31,13 +31,16 @@
# chao - June 2011 - major overhaul of dll lazy loading, caching, and bit of everything
#
#
# zeroSteiner - April 2017 - added support for non-windows platforms
#
require 'pp'
require 'enumerator'
require 'rex/post/meterpreter/extensions/stdapi/railgun/api_constants'
require 'rex/post/meterpreter/extensions/stdapi/railgun/tlv'
require 'rex/post/meterpreter/extensions/stdapi/railgun/util'
require 'rex/post/meterpreter/extensions/stdapi/railgun/win_const_manager'
require 'rex/post/meterpreter/extensions/stdapi/railgun/const_manager'
require 'rex/post/meterpreter/extensions/stdapi/railgun/multicall'
require 'rex/post/meterpreter/extensions/stdapi/railgun/dll'
require 'rex/post/meterpreter/extensions/stdapi/railgun/dll_wrapper'
@ -59,28 +62,33 @@ class Railgun
# Railgun::DLL's that have builtin definitions.
#
# If you want to add additional DLL definitions to be preloaded create a
# definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/'.
# definition class 'rex/post/meterpreter/extensions/stdapi/railgun/def/$platform/'.
# Naming is important and should follow convention. For example, if your
# dll's name was "my_dll"
# file name: def_my_dll.rb
# class name: Def_my_dll
# entry below: 'my_dll'
#
BUILTIN_DLLS = [
'kernel32',
'ntdll',
'user32',
'ws2_32',
'iphlpapi',
'advapi32',
'shell32',
'netapi32',
'crypt32',
'wlanapi',
'wldap32',
'version',
'psapi'
].freeze
BUILTIN_DLLS = {
'linux' => [
'libc'
].freeze,
'windows' => [
'kernel32',
'ntdll',
'user32',
'ws2_32',
'iphlpapi',
'advapi32',
'shell32',
'netapi32',
'crypt32',
'wlanapi',
'wldap32',
'version',
'psapi'
].freeze
}.freeze
##
# Returns a Hash containing DLLs added to this instance with #add_dll
@ -109,7 +117,7 @@ class Railgun
end
def self.builtin_dlls
BUILTIN_DLLS
BUILTIN_DLLS[client.platform]
end
#
@ -124,12 +132,24 @@ class Railgun
end
#
# Return this Railgun's WinConstManager instance, initially populated with
# Return this Railgun's platform specific ApiConstants class.
#
def api_constants
if @api_constants.nil?
require "rex/post/meterpreter/extensions/stdapi/railgun/def/#{client.platform}/api_constants"
@api_constants = Def.const_get('DefApiConstants_' << client.platform)
end
return @api_constants
end
#
# Return this Railgun's ConstManager instance, initially populated with
# constants defined in ApiConstants.
#
def constant_manager
# Loads lazily
return ApiConstants.manager
return api_constants.manager
end
#
@ -157,22 +177,18 @@ class Railgun
# Write data to a memory address on the host (useful for working with
# LPVOID parameters)
#
def memwrite(address, data, length)
def memwrite(address, data, length=nil)
length = data.length if length.nil?
raise "Invalid parameters." if(not address or not data or not length)
request = Packet.create_request('stdapi_railgun_memwrite')
request.add_tlv(TLV_TYPE_RAILGUN_MEM_ADDRESS, address)
request.add_tlv(TLV_TYPE_RAILGUN_MEM_DATA, data)
request.add_tlv(TLV_TYPE_RAILGUN_MEM_LENGTH, length)
response = client.send_request(request)
if(response.result == 0)
return true
end
return false
return response.result == 0
end
#
@ -182,7 +198,7 @@ class Railgun
# cached dlls) an unfrozen copy is created and used henceforth for this
# instance.
#
def add_function(dll_name, function_name, return_type, params, windows_name=nil, calling_conv="stdcall")
def add_function(dll_name, function_name, return_type, params, remote_name=nil, calling_conv="stdcall")
unless known_dll_names.include?(dll_name)
raise "DLL #{dll_name} not found. Known DLLs: #{PP.pp(known_dll_names, "")}"
@ -199,31 +215,31 @@ class Railgun
dlls[dll_name] = dll
end
dll.add_function(function_name, return_type, params, windows_name, calling_conv)
dll.add_function(function_name, return_type, params, remote_name, calling_conv)
end
#
# Adds a DLL to this Railgun.
#
# The +windows_name+ is the name used on the remote system and should be
# The +remote_name+ is the name used on the remote system and should be
# set appropriately if you want to include a path or the DLL name contains
# non-ruby-approved characters.
#
# Raises an exception if a dll with the given name has already been
# defined.
#
def add_dll(dll_name, windows_name=dll_name)
def add_dll(dll_name, remote_name=dll_name)
if dlls.has_key? dll_name
raise "A DLL of name #{dll_name} has already been loaded."
end
dlls[dll_name] = DLL.new(windows_name, constant_manager)
dlls[dll_name] = DLL.new(remote_name, constant_manager)
end
def known_dll_names
return BUILTIN_DLLS | dlls.keys
return BUILTIN_DLLS[client.platform] | dlls.keys
end
#
@ -232,7 +248,6 @@ class Railgun
# exist, returns nil
#
def get_dll(dll_name)
# If the DLL is not local, we now either load it from cache or load it lazily.
# In either case, a reference to the dll is stored in the collection "dlls"
# If the DLL can not be found/created, no actions are taken
@ -241,14 +256,14 @@ class Railgun
@@cache_semaphore.synchronize do
if @@cached_dlls.has_key? dll_name
dlls[dll_name] = @@cached_dlls[dll_name]
elsif BUILTIN_DLLS.include? dll_name
elsif BUILTIN_DLLS[client.platform].include? dll_name
# I highly doubt this case will ever occur, but I am paranoid
if dll_name !~ /^\w+$/
raise "DLL name #{dll_name} is bad. Correct Railgun::BUILTIN_DLLS"
end
require 'rex/post/meterpreter/extensions/stdapi/railgun/def/def_' << dll_name
dll = Def.const_get('Def_' << dll_name).create_dll.freeze
require "rex/post/meterpreter/extensions/stdapi/railgun/def/#{client.platform}/def_#{dll_name}"
dll = Def.const_get('Def_' << dll_name).create_dll(constant_manager).freeze
@@cached_dlls[dll_name] = dll
dlls[dll_name] = dll
@ -280,7 +295,7 @@ class Railgun
end
#
# Return a Windows constant matching +str+.
# Return a constant matching +str+.
#
def const(str)
return constant_manager.parse(str)
@ -291,7 +306,7 @@ class Railgun
#
def multi(functions)
if @multicaller.nil?
@multicaller = MultiCaller.new(client, self, ApiConstants.manager)
@multicaller = MultiCaller.new(client, self, constant_manager)
end
return @multicaller.call(functions)

View File

@ -16,7 +16,7 @@ module MetasploitModule
def initialize(info = {})
super(update_info(info,
'Name' => 'Python Meterpreter',
'Description' => 'Run a meterpreter server in Python (2.5-2.7 & 3.1-3.5)',
'Description' => 'Run a meterpreter server in Python (2.5-2.7 & 3.1-3.6)',
'Author' => 'Spencer McIntyre',
'Platform' => 'python',
'Arch' => ARCH_PYTHON,

View File

@ -11,16 +11,84 @@ class MetasploitModule < Msf::Post
def initialize(info={})
super( update_info( info,
'Name' => 'Railgun API Tests',
'Description' => %q{ This module will test railgun api functions},
'License' => MSF_LICENSE,
'Author' => [ 'Spencer McIntyre'],
'Platform' => [ 'windows' ]
))
'Name' => 'Railgun API Tests',
'Description' => %q{ This module will test railgun api functions },
'License' => MSF_LICENSE,
'Author' => [ 'Spencer McIntyre' ],
'Platform' => [ 'linux', 'windows' ]
))
end
def test_api_function_calls
def test_api_function_calls_linux
return unless session.platform == 'linux'
buffer = nil
buffer_size = 128
buffer_value = nil
it "Should include error information in the results" do
ret = true
result = session.railgun.libc.malloc(128)
ret &&= result['GetLastError'] == 0
ret &&= result['ErrorMessage'].is_a? String
end
it "Should support functions with no parameters" do
ret = true
result = session.railgun.libc.getpid()
ret &&= result['GetLastError'] == 0
ret &&= result['return'] == session.sys.process.getpid
end
it "Should support functions with literal parameters" do
ret = true
result = session.railgun.libc.calloc(buffer_size, 1)
ret &&= result['GetLastError'] == 0
buffer = result['return']
ret &&= buffer != 0
end
it "Should support functions with in/out/inout parameter types" do
ret = true
# first test in/out parameter types
result = session.railgun.libc.inet_ntop('AF_INET', "\x0a\x00\x00\x01", 128, 128)
ret &&= result['GetLastError'] == 0
ret &&= result['return'] != 0
ret &&= result['dst'][0...8] == '10.0.0.1'
# then test the inout parameter type
result = session.railgun.libc.memfrob('metasploit', 10)
ret &&= result['GetLastError'] == 0
ret &&= result['return'] != 0
ret &&= result['mem'] == 'GO^KYZFEC^'
end
it "Should support calling multiple functions at once" do
ret = true
multi_rail = [
['libc', 'getpid', []],
['libc', 'memfrob', ['metasploit', 10]]
]
results = session.railgun.multi(multi_rail)
ret &&= results.length == multi_rail.length
ret &&= results[0]['return'] == session.sys.process.getpid
ret &&= results[1]['mem'] == 'GO^KYZFEC^'
end
it "Should support writing memory" do
ret = true
buffer_value = Rex::Text.rand_text_alphanumeric(buffer_size)
ret &&= session.railgun.memwrite(buffer, buffer_value)
end
it "Should support reading memory" do
ret = true
ret &&= session.railgun.memread(buffer, buffer_size) == buffer_value
end
session.railgun.libc.free(buffer)
end
def test_api_function_calls_windows
return unless session.platform == 'windows'
it "Should include error information in the results" do
ret = true
result = session.railgun.kernel32.GetCurrentProcess()
@ -70,43 +138,39 @@ class MetasploitModule < Msf::Post
ret &&= results[2]['return'] == session.sys.process.getpid
end
it "Should support reading memory" do
ret = true
result = client.railgun.kernel32.GetModuleHandleA('kernel32')
ret &&= result['GetLastError'] == 0
ret &&= result['return'] != 0
return false unless ret
handle = result['return']
mz_header = client.railgun.memread(handle, 4)
ret &&= mz_header == "MZ\x90\x00"
end
it "Should support writing memory" do
ret = true
result = client.railgun.kernel32.GetProcessHeap()
result = session.railgun.kernel32.GetProcessHeap()
ret &&= result['GetLastError'] == 0
ret &&= result['return'] != 0
return false unless ret
buffer_size = 32
handle = result['return']
result = client.railgun.kernel32.HeapAlloc(handle, 0, buffer_size)
result = session.railgun.kernel32.HeapAlloc(handle, 0, buffer_size)
ret &&= result['GetLastError'] == 0
ret &&= result['return'] != 0
return false unless ret
buffer_value = Rex::Text.rand_text_alphanumeric(buffer_size)
buffer = result['return']
ret &&= client.railgun.memwrite(buffer, buffer_value, buffer_size)
ret &&= client.railgun.memread(buffer, buffer_size) == buffer_value
ret &&= session.railgun.memwrite(buffer, buffer_value)
ret &&= session.railgun.memread(buffer, buffer_size) == buffer_value
client.railgun.kernel32.HeapFree(handle, 0, buffer)
session.railgun.kernel32.HeapFree(handle, 0, buffer)
ret
end
it "Should support reading memory" do
ret = true
result = session.railgun.kernel32.GetModuleHandleA('kernel32')
ret &&= result['GetLastError'] == 0
ret &&= result['return'] != 0
return false unless ret
handle = result['return']
mz_header = session.railgun.memread(handle, 4)
ret &&= mz_header == "MZ\x90\x00"
end
end
end