2024 XCTF 联赛 Final 部分题解
丢。
Web
ezthink
Thinkphp 8 反序列化链构造
路由访问的规则是 POST ``http://127.0.0.1:8888/xctf/hecker
https://xz.aliyun.com/t/12630#toc-3 tp6.0.12 的后半段在 tp8 中依然存在
只需要找到从 __destruct
到 __toString
即可
1 |
|
Crypto
Cu2ve
比赛时用的是题目的非预期解,所以先说一下非预期解
非预期解
题目给定 E1 曲线上的点 P、xP、yP 和 zP,以及曲线 E2 上的点 Q 和 yQ,需要解一个曲线 E1 或 E2 上的 DDH 问题
首先 DDH 问题可以规约到 CDH 问题,或者说 DLP,即如果可以解 CDH 或 DLP 的话就可以解 DDH
然后理论上 CDH 和 DLP 都是难解的,但对曲线 E1 的解 n 进行分解后发现有一个小因子 500
于是 E1 就有一个阶为 500(或者 500 的因子)的小阶群,如果把 P、xP、yP 和 zP 都转换到这个小阶群中,就可以通过枚举解决 DLP 问题,如果熟悉 Pohlig-Hellman 算法的话应该对这个不陌生
最后经过以上操作后理论上可以解出 x、y 和 z(mod 500),然后在模 500 的情况下检测是否 z = xy 即可恢复 prp 的 state
以下为参考代码:
1 | p = 176857581480948244867604802349863732783663856225560523858099969581030268141874483416875345636657439749959951621 |
在以上代码中,由于那四个点可能会处于阶更小(500 的因子)的子群中,所以可能会出现多个解,我的做法是选择把得到的所有 xy 和 z 都放到集合中,然后检查两个集合是否有交集,如果没交集的话说明这个 z 与 xy 不相等
但是如果有交集的话,则不一定是 z = xy,也有可能是点所处的子群阶太小了,导致出现误差,幸好题目给的数据有冗余,所以可以用冗余的数据消除这个误差
我的做法是,首先根据题目的 twist 函数写出逆函数 unTwisit,然后把每一组的数据(100 个一组)都 unTwisit 到同一个状态点,即开始的状态
然后对比此时的每组数据是否相同,如果有某个位置中,一组数据中为 1 而另一组数据为 0 的话,那么这个位置应该为 0,因为根据上面说的,0 保真,而 1 不保真
除杂完后还要做一次 unTwisit,因为 prp 初始化时就做了一次 twist,最后因为加密函数是一个 OTP,所以用题目给的 encrypt 函数解密即可
以下为参考代码:
1 | from hashlib import shake_128 |
(应该是)预期解
下面说一下可能的预期解,其实题目给的提示还是挺充足的
首先在 ECC 中,一种解 DDH 的方法是使用双线性配对(Billinear Pairing),教科书的内容,这里就不细说了
比如给 P、xP、yP 和 zP 的话,可以通过比较 e(P, zP) 和 e(xP, yP) 是否相同来解 DDH 问题,但在这里显然是不行的,一个原因是这四个点都在一个群里所以实测 Pairing 后结果都是 1,另一个原因是这样的话点 Q 和 yQ 就没用了
在继续下面的内容之前,先看一下著名的 Tate Pairing 的定义
即如果要做 Tate Pairing 的话,就要 n 的因子里面有一个大素数 r,和一个嵌入度 k,满足 r 整除 p^k-1
所以可以测试一下题目的曲线是否满足,怼个代码测试一下
假设能成的话,Pairing 需要在 Fpk 上做,所以这个 k 肯定不能太大,于是可以通过枚举,然后检查 n 和 pk-1 是否有大的公因子来找到这个 k 和 r
1 | for k in range(100): |
枚举到 k=8 的时候发现有大因子,然后顺便可以把 n 分解了(分解结果可见下面代码)
到这里的话可以发现题目已经提醒了用 Tate Pairing,因为这样的 k 和 r 的出现其实是个小概率事件,所以一定是出题人特意构造的
如果直接用 P、xP、yP 和 zP 的话,即使 Tate Pairing 可能也不行,猜测可能是因为这四个点都处于同一个子群,于是就可以引入(大概率)与 P 不相关的点 Q,然后比较 e(zP, Q) 与 e(xP, yQ) 是否相同来解 DDH
但在做 Pairing 之前还需要解决一个问题,即点 P 和点 Q 不在同一条曲线上,就不能做 Pairing
一种解决方法是,可以把 P 映射到 E2 中,或者 Q 映射到 E1 中再做,根据这个理论,赛中的时候找到一种 Twist 的映射
然后题目中的两条曲线刚好满足其中的 d=4 的情况
那么理论上只要把这个映射复现出来就好,但是比赛的时候一直没搞出来,就止步于此(反而搞出了非预期)
赛后问出题人要了 WP,发现这个映射可以直接使用 SageMath 的 isomorphism_to 函数实现,不知道是不是同一个映射,直接用就对了。。。
首先需要把曲线 E1(F1) 和 E2(F2) 都扩展到 E1(Fk) 和 E2(Fk) 中,然后会发现此时 E1(Fk) 和 E2(Fk) 的阶相同(目前不清楚原理,盲猜和上面的 Twist 有关),阶相同就可以直接用 isomorphism_to 构造一个同态,把点都映射到 E1(Fk) 中
在这一步中,E1(F1) -> E1(Fk) 是简单的,而 E2(F2) -> E2(Fk) 会相对麻烦一点(由于两个有限域模的不可约多项式不一样吧),但可以直接用 SageMath 的 Hom 方法做映射
最后在 E1(Fk) 中发现已经有一部分点可以做 Tate Pairing 了,按照上面的思路解 DDH 即可
至于其他点为什么 Tate Pairing 的结果依然为 1,目前原理未明(盲猜点还是在同一个子群),按出题人 WP 的说法是需要 r 整除 n2 且 n 不整除 n2 才可以
以下为参考代码:
1 | p = 176857581480948244867604802349863732783663856225560523858099969581030268141874483416875345636657439749959951621 |
解出来把所有数据组合后发现依然有 16 个比特是未知的,但 2^16 并不大,爆破即可
参考代码:
1 | from hashlib import shake_128 |
参考文献:Guide to pairing-based cryptography[M]. CRC Press, 2017.
Pwn
0ob
1 | let dogc_flag = false; |
bytEc0DeVM
核心: VirtualMachine::Interpret patch 0X124CA -> 90
支持的指令:
1 | CMD OP |
load 和 store 要加 vm 的偏移,没有限制。VM 的结构体和 libc 对齐,所以似乎有 libc 任意读写
逆完发现是 RW,void 坏 https://github.com/Illation/BytecodeVM
然而所有操作都是 signed int32,程序是 64 位 (libc2.35 0ubuntu3.8)
拿 apple 打:
1 | from pwn import * |
Misc
Pretender
套娃题,题目大意是从返回包里解析变量做计算,后台随机概率爆到符合条件的包就进下一轮
跑通一次之后,题目说 flag 就是“来时的路”(每次计算结果 %256)
1 | from pwn import * |
Cain's Dog
one pixel attack
https://adversarial-attacks-pytorch.readthedocs.io/en/latest/attacks.html#module-torchattacks.attacks.onepixel
需要把 torchattack/attacks/onepixel.py
的 L115 和 L117 的条件改改成
1 | def _attack_success(self, image, label, delta): |
step 和 popsize 要调大,尤其是 step 要调大
1 | import numpy as np |
2024 XCTF 联赛 Final 部分题解