2019 HDCTF WriteUp
前言
主办方:海南大学
Misc
你能发现什么蛛丝马迹吗(Windows pslist 排查进程)

下载附件是一个 .img,使用 vol2 进行取证,先查看操作系统
1 | python2 vol.py -f memory.img imageinfo |
发现是 Windows 2003,给出了三个具体的版本

查看进程,Server 2003 生命周期中 SP2 是最普遍的版本,所以选择它
1 | python2 vol.py -f memory.img --profile=Win2003SP2x86 pslist |

dump 出 explorer.exe,它的内存里主要是与桌面、任务栏、文件夹浏览相关的代码和数据
1 | python2 vol.py -f memory.img --profile=Win2003SP2x86 memdump -p 1992 -D ../ |
检查字符串发现有一个 flag.png

foremost 提取出两个图片
1 | key:Th1s_1s_K3y00000 |
解密拿到 flag
1 | flag(FOuNd_s0m3th1ng_1n_M3mory} |
信号分析(无线电固定码信号分析)

固定码遥控信号的构成
钥匙信号(PT224X) = 同步引导码(8bit) + 地址位(20bit) + 数据位(4bit) + 停止码(1bit)
钥匙信号(PT226X) = 同步引导码(4bit) + 地址位(8bit) + 数据位(4bit) + 停止码(1bit)


PT2262 是一种常用的无线遥控编码芯片(与 PT2272 配对),广泛应用在车库门、报警器等 315/433MHz 设备上
其编码特点:每个数据位可以有三种状态——0、1、F(浮空)
1 | 一长一短 → F |
位宽表示:每位由两个脉冲周期组成
用 Audacity 打开文件

1 | 地址位 + 数据位 = FFFFFFFF0001 |
Crypto
bbbbbbrsa(爆破 e)
题目源代码如下:
1 | from base64 import b64encode as b32encode |
已知条件:
已知
p和n,可以直接求出q = n // pe为 50000~70000 之间的随机数,并与φ(n)互素
反转字符串,得到正常 Base64 编码结果
1 | c_rev = "==gMzYDNzIjMxUTNyIzNzIjMyYTM4MDM0gTMwEjNzgTM2UTN4cjNwIjN2QzM5ADMwIDNyMTO4UzM2cTM5kDN2MTOyUTO5YDM0czM3MjM" |
Base64 解码得到 c 的十进制字符串,再转整数
1 | from base64 import b64decode |
计算基本参数
1 | p = 177077389675257695042507998165006460849 |
爆破 e 并解密
1 | import gmpy2 |
得到 flag{rs4_1s_s1mpl3!}
basic rsa(RSA)

1 | import gmpy2 |
加密流程
flag 字符串 →
b2a_hex(flag)转换为十六进制字符串再
int(..., 16)转为大整数 m计算
c = m^e mod n
所以解密只需逆回去
1 | import gmpy2 |
得到 flag{B4by_Rs4_1s_3asy}
together(共模攻击)

拿到题目后,解压附件会得到 4 个文件:
pubkey1.pem和pubkey2.pem公钥文件myflag1和myflag2密文文件
解题的关键突破口在于对比两个公钥文件
使用 openssl 命令分别提取两个公钥的模数 N 和指数 e
1 | openssl rsa -pubin -text -modulus -in pubkey1.pem |
经过对比,得到两条核心信息:
模数
N完全相同公钥指数
e不同(例如e1=2333,e2=23333)
两条不同的消息使用了相同的模数 N 进行加密,这是典型的共模攻击场景
密文其实是 Base64 字符串,进行解码才能得到真正的密文数值 c1 和 c2
1 | import base64 |
得到 flag{23re_SDxF_y78hu_5rFgS}
Reverse
Maze(花指令绕过 + 迷宫)

使用 Exeinfo PE 发现有 UPX 壳

脱壳后用 IDA 分析,有花指令

关键点:jnz +1 的跳转目标
jnz short指令本身长 2 字节(75 操作码 + 01 偏移量)偏移量是相对下一指令地址的位移。当前
jnz位于0x40102C,下一指令地址是0x40102E加上
+1,目标地址 =0x40102E + 1=0x40102F因此,无论标志位如何,CPU 下一步必然从
0x40102F开始执行
所以,call 0EC85D78Bh 这条 5 字节指令的唯一目的就是干扰 IDA 反编译
CPU 实际会执行的部分是从 0x40102F 开始的每一个字节,所以这些字节一个都不能动
唯一需要填掉的是 IDA 会顺序解析,但 CPU 永不执行的部分,即:
75 01这 2 字节的jnz(它虽然会执行,但它的存在让 IDA 沿着0x40102E往下分析。如果我们NOP掉它,IDA 就不会进入那条错误的分支了)E8这 1 字节的call操作码
因此,我们只需把 0x40102C 开始的 3 个字节 改为 90:
1 | 原始: 75 01 E8 58 C7 45 EC ... |
保存后再次反编译可以看到伪代码,接受用户输入的 awsd 推测为迷宫类题目

Shift + F12 打开字符串列表

正好是 70 个字符,对应一个 7 行 × 10 列 的迷宫
1 | *******+** |
s s a a a s a a s s d d d w → 刚好能走到 F 的位置,所以 flag 就是 flag{ssaaasaassdddw}
MFC(MFC)

使用 Exeinfo PE 发现有 VMP 壳

这道题是一个 MFC,你可以把 Windows 的图形界面程序想象成一个“事件驱动”的机器,按钮点击、文本输入等所有操作都会被转化为系统消息
而 MFC 框架就像一个黑箱,把这些底层的消息处理过程层层封装,让你很难在 IDA 等静态工具里,通过简单的关键API 或者搜索字符串定位到核心逻辑
再加上程序还加了 VMProtect 壳,雪上加霜
所以这里我要利用到另一个工具——xspy
xspy 是一款专门为逆向分析 MFC 程序设计的辅助工具,它的核心能力是监控目标程序的消息和控件信息
下面开始逆向,运行程序

用 xspy 的 “放大镜” 拖拽到程序窗口

现在我们来解析下回显内容
窗口身份确认
窗口类型:主窗口是一个
CDialog(对话框),继承自CWnd→CCmdTarget→CObject句柄值:当前该对话框的窗口句柄(HWND)是
0x000708FE。这个值后面可以直接用来发消息
1 | class:001AFE60(CDialog,size=0x98) |
框架与链接方式
MFC 版本:120,对应 VS2013 的 MFC 库
静态链接:程序把 MFC 库静态编译进了自身,因此看不到
mfc120.dll,所有逻辑都在同个 exe 里
1 | mfc version:120, static linked?: true, debug?: false |
消息映射表
标准消息:
WM_SYSCOMMAND、WM_PAINT、WM_QUERYDRAGICON都是系统预设消息,对应普通的功能自定义消息
0x0464:OnMsg:0464是程序作者自己定义的,对应的处理函数地址是0x00402170。这意味着,只要向这个窗口发送0x0464号消息,程序就会自动调用0x00402170处的函数
1 | message map=0x00588CF4(mfc.exe+ 0x188cf4 ) |
编写代码编译成 exe 发送消息
1 |
|
可以看到控件更新了

再次用 xspy 的放大镜拖到窗口上可以看到密文

DES 解密得到 flag{thIs_Is_real_kEy_hahaaa}