WMCTF 2020 SU Write-Up
感谢 WM 的师傅们精心准备的比赛!本次比赛我们 SU 取得了 2nd 🥈的成绩,感谢队里师傅们的辛苦付出!同时我们也在持续招人,只要你拥有一颗热爱 CTF 的心,都可以加入我们!欢迎发送个人简介至:suers_xctf@126.com
以下是我们 SU 本次 WMCTF 2020 的 writeup。
[TOC]
Web
web_checkin
Third Blood
http://web_checkin.wmctf.wetolink.com/?content=/flag
Make PHP Great Again
session 包含
1 | import io |
Make PHP Great Again And Again
Second Blood
因为 require_once 读不了 flag.php, 根据上一题 flag 的提示是 php 自己的问题, 找了半天发现
https://github.com/php/php-src/blob/master/Zend/zend_virtual_cwd.c
超过 32 层会停止读符号链接, 所以用 /proc/self/root 套 33 层娃即可
1 | http://v2222.no_body_knows_php_better_than_me.glzjin.wmctf.wetolink.com/?file=php://filter/read=convert.base64-encode/resource=compress.zlib://file:///proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php |
webweb
反序列化 gadgets 挖掘, 没啥好说的, 需要注意下清空一些类的构造函数, 不然东西太多会 413
1 | lib/cli/ws.php __destruct 入口, fuction 任意, 参数不可控, 是一个对象 |
1 | <?php |
gogogo
First Blood
三个点, math/rand 是伪随机, 而且不播种的话结果都是一样的, 本地用同样环境搭一个就可以拿到 admin session
1 | MTU5NjM2NzAwNnxEdi1CQkFFQ180SUFBUkFCRUFBQUpQLUNBQUVHYzNSeWFXNW5EQWNBQlhWdVlXMWxCbk4wY21sdVp3d0hBQVZoWkcxcGJnPT182OYB7Y3m7o504Bjnh5dnTgHrQ8H5hNSyzaYxDB0R0Po= |
然后老版本 go 能 crlf, https://github.com/golang/go/issues/30794
1 | import requests |
最后 plugin 必须和编译主程序同一个版本才能被加载, 试了半天发现读 /proc/self/environ 可以读到是 1.9.7
之后编译一个传上去等应用重启就能 rce 了
1 | package main |
Pwn
mengyedekending
C# PWN
玄学啊,讲道理偏移指定为220是可以覆盖到num来触发后门的,但是我输入220之后偏移被赋值成了50?
没事了,\r可以无限地向后推index,推到num写个0就ok了
1 | from pwn import * |
csgo
过了100关之后发现一个栈溢出,本地调试通过覆盖栈上的参数泄露pie地址,部分覆盖返回地址跳回backdoor(这里要爆破1/16)。然后泄露链式栈地址用于定位/bin/sh
参数,最后ROP获得shell。
1 | from pwn import * |
其中hhh的源码:
1 |
|
https://github.com/sibears/IDAGolangHelper/tree/b6e7907755bd57f756a157b3cd7565e7ef7a5dec
roshambo
Second Blood
2a51—sha-256
最后有个堆溢出,malloc 0的时候会read 0xfffffffff
1 | #coding=utf-8 |
Misc
Dalabengba
先用EnigmaVBUnpacker.exe将游戏解包
然后开启rpgmaker开一个新项目,把www里面文件拷过去打开
在这个网站把素材解包https://petschko.org/tools/mv_decrypter/,把完整的放回文件夹里,这里要修改data/system.json的配置
“hasEncryptedImages”: false,
“hasEncryptedAudio”: false,
运行正常使用了
查找了国王最后成功的话
调试发现还是加密了,用了decText.js里面代码
参考RPGMaker防破解百度快照
去分析rpg_core.js,推出systemkey:”f74592328a168cf858e727078d4f6ab”,然后丢回包里看国王说的话,其实可以把所有发现加密的都跑出来看看有哪些东西
emm好像没flag
第一部分:
直接跳到最后的天空城关卡
发现移动路线组成单词Pr1nCe5s
第二部分:
文件part2.jpg
前面有一个do you know java ,查了java和图片隐写的东西
找打java盲水印工具:https://github.com/ww23/BlindWatermark,
需要拼接一部分,扫描出第二部分
W@rR1or
第三部分:
前面导入systemkey后使用加密的素材
看到国王的话逆序hex->ascii得到Y0u_@re_5o_bRaVE得到一个文件
搜文件名s3cr3t找到了个解密的https://gist.github.com/aanoaa/1408846
出了WhrRrrr~
XMAN_Happy_birthday!
翻转
Music_game
声控游戏,走到终点就出flag
Rev
Wmware
Second Blood
一个bochs,磁盘文件,010打开后看到了熟悉的0x55AA,拖入ida,选择16位.扇区是512字节为一个单位,这样可以找到一些代码,简单分析了一下发现是在输入,循环等待不停等待端口状态变化,然后读入,这里获得的是键盘的扫描码,转成了ascii,写到了显存的地址
用bochs自带的调试器调一下,在输入的时候中断,输入的跳出条件是读到回车的扫描码,继续调试,结合ida,发现他在进行一个6*6的循环,每次拿出来一个扫描码.加0x55后放到另外一个地方,有点像栅栏密码,后面切换模式到32位,运行保护模式的代码,这里可以开一个32位的ida,直接F5,发现循环xor了几个很臭的常数,选择臭常数的条件是循环的次数取余9,全部都是xor,写逆运算计算回来就行了
Welcome to CTF
一个windows逆向,一开始踩坑到了虚假的执行流,虚假的base64,虚假的xor,在init的时候,运行了好多函数,关于异常反调试等等,还有三处调用了ntdll里面的一个可以对抗调试器的函数.
既然这样,那就直接上trace了,手头有一个写好的工具,配合qemu,trace出了他所有的指令流
发现到这里了,跟进去看了一下,和假的那个函数差不多,直接逆了一下,发现有一个改了表的base64函数,后面是几个明显的大数计算,这里直接patch了他的ntdll那个函数的调用,可以调试了,在进入虚假的check函数时,修改eip到这个函数,手动跳过来,继续调试
整理出来大概的流程:
base64decode -> rsa -> 高60bit和低56bit分别做x y,计算x^3 + y^3 + z^3 = 43
这里的一个常数值是立方和=42的解,这里却还是43,先带进去42的结果生成一下flag,然后跑trace.
算了一下发现是错的,继续边trace边打印内存,发现base64的结果就不对了,但是log里还是有那个函数的痕迹的,继续查log,发现在初始化码表后,decode之前,有一个函数跳走了.那就是这里的问题了,读了一下码表,发现和直接dump出来的不一样,修了一下码表,最后又一次trace,发现已经输出GOOD了,估计那个43最后还是被改过,不过已经出了就没继续trace了.
easy_re
Second Blood
调试得到perl源码
1 | $flag = "WMCTF{I_WAnt_dynam1c_F1ag}" |
Meet_in_July
First Blood
check格式flag{}长度70
数字和大写ABCDEF
字符串转16进制后进sub_401909进行加密
主要是乘法减法mod三个操作
因为sub_401111操作结果都是32bytes,猜测是一个取模操作。
用了两组数据算出了模数:
1 | from Crypto.Util.number import GCD |
逻辑如下
1 | 输入flag{1234567812345678123456781234567812345678123456781234567812345678} |
总结为:
$$
\begin{aligned}
y &\equiv x^2 - 2 \
z &\equiv y \cdot x - x \equiv x^3 - 3x \pmod{n} \
a &\equiv z \cdot x - y \equiv (x^3 - 3x )x - (x^2 - 2) \equiv x^4 - 4x^2 + 2 \pmod{n} \
b &\equiv a \cdot z - x \equiv (x^4-4x^2 + 2) (x^3 - 3x)
-x \equiv x^7 -7x^5 + 14x^3 - 7x \pmod{n}
\end{aligned}
$$
试了一下,模数n可以被分解为两个素数:
p = 320265757102059730318470218759311257989
q = 361550014853497117429835520396253724753
通过Mathematica可以分别在mod p和mod q上解出来x
然后再用CRT即可得到在mod n下的解:17608204545242378720348793798058123425575979093234353645947732994798163637792
程序输入flag{26EDE3FE048B6BFA04F647259A3F00505FD9C9CCB87298CD631FD91F17CCB620}
提交需要将”flag”改成”WMCTF”
easy_apk
一个安卓逆向,字符串都被加密了,看了一下调用了native的check函数,在ida里看了一下check函数,判断长度是不是32,然后取出偶数位,使用AES加密,然后用加密的结果,作为祖冲之算法的密钥加密全部的flag.
后来改题了AES生成的那个祖冲之算法的key,是已知的了,然后因为那个是流加密,所以直接把加密后的flag带回去就拿到flag了.
Crypto
babySum
Random BKZ Blocksize 24
1 | from json import load |
piece_of_cake
二维格可以用高斯格基规约,多试几组数据就可以跑出来了
1 | from gmpy2 import iroot, sqrt |
Game
1 | from pwn import remote |
idiot box
Second Blood
改过的DES 6轮差分攻击
现学:
- https://medium.com/lotus-fruit/breaking-des-using-differential-cryptanalysis-958e8118ff41
- http://www.cs.technion.ac.il/~biham/Reports/differential-cryptanalysis-of-the-data-encryption-standard-biham-shamir-authors-latex-version.pdf
现学材料里一个可能的疑惑点:第4轮的F函数中,有5个sbox的input(6bit)的差分值都是0,所以这5个sbox的output(4bit)的差分值也都是0,经过P置换后,得到的D’中有4*5=20bit是已知的,所以后面第6轮的F函数的output的差分值:$F’ = c’ \oplus D’ \oplus T_L’$中,有20bit是确定的;经过逆P置换后,得到第6轮8个sbox的outputs的差分值,其中有5个对应的sbox的output的差分值是已知的,所以能用medium里的那个方法把这5个sbox的key求出来。
c’为第3个F函数input的差分值,D’为第4个F函数input的差分值,F’为第6个F函数output的差分值,$T_L’$是密文左半部分的差分值。
攻击方法
DES里面就sbox比较难搞,其他的部分就是一些线性置换,可以通过一些差分特性去操作一下这个sbox,然后就能得到key。
简单来说就是,找到一个差分特征后,可以用这个特征推4轮,然后计算$F’ = c’ \oplus D’ \oplus T_L’$(第6轮F函数output的差分值),逆P置换,得到8个sbox的outputs的差分值out_xors,这个差分值的概率是最大的;接着,将2个已知的第6轮F函数的input(密文的右半部分)去做e扩展,得到$I_1, I_2$,分别分成8组${i_{11}, i_{12}, …, i_{18}}, {i_{21}, i_{22}, …, i_{28}}$,对应着8个sbox。
每一个sbox,对所有可能的64种key(6bit)作判断sbox($i_{1j}$ ^ key) ^ sbox($i_{2j}$ ^ key) == out_xors[j],如果等于,则将该key计数加1。尝试很多次后,必然有一个key出现的次数最多,且远超其他的key,该key即为正确的key。
这8个6bit的key合起来就是第6轮的subkey,又由于密钥扩展就是一个置换,可以反推出前面5轮的key。
把key反过来加密就能getflag。
手动寻找差分特征
1 | from collections import Counter |
发现了好几组非常优秀的差分特征。
以前我么得选择,现在我随便选。
就选这个0x00000040 -> 0x00000000 0.2534
画图分析
在线画图:https://draw.io
可以推出来$F’ = 0x00000040 \oplus T_L’$
获取数据
1 | import re |
差分攻击
1 | from collections import Counter |
WMCTF{D1ff3r3nti@1_w1th_1di0t_B0X3s}
WMCTF 2020 SU Write-Up