Misay's blog

Back

🫡官方repo wp https://github.com/team-s2/ACTF-2026 ↗

inverse_pow#

I’m tired of watching people insta-gib the proof-of-work with their 128-core machines. So why not inverse it?

赛题提供了一个go写的ELF 逆向后是一个PoW 协议基本如下

生成一个 1 到 99999999 之间的随机数 mm 要求计算一个 nn 满足 在60秒内计算 2n2^n 要求 2n2^n的高len(m)位恰好是 mm 即

str(2**n)[:len(str(m))] == str(m)
py

题目情景可以理解为找nn 满足

m⋅10t≤2n<(m+1)⋅10tm \cdot 10^t \le 2^n < (m+1) \cdot 10^t

其中tt是根据数量级来确定的整数 取对数

log⁡10m+t≤nlog⁡102<log⁡10(m+1)+t\log_{10} m + t \le n\log_{10}2 < \log_{10}(m+1) + t

我们不难理解

对于一个整数 如果把它写成科学计数法的形式

m=c×10k−1m=c\times 10^{k-1}

它的cc部分的”小数”会决定它的前缀

对于LHS/RHS的 大数 我们全部集中于它的小数部分 设十进制总长度为 kk 那么可以把问题全部限制在 [  0,1  )[\;0,1\;) 的区间小数

m10k−1≤{2n}<m+110k−1\frac{m}{10^{k-1}} \le \{2^n\}< \frac{m+1}{10^{k-1}}

回代取对数

log⁡10(m10k−1)≤log⁡10(c)<log⁡10(m+110k−1)\log_{10}\left(\frac{m}{10^{k-1}}\right) \le \log_{10}(c) < \log_{10}\left(\frac{m+1}{10^{k-1}}\right)

记为

L≤{nθ}<UL\le \{n\theta\}\lt U

其中 θ=log⁡102\theta=\log_{10}2 作为每轮的步长


为什么要转化到小数?

就前缀而言 我们其实不是很关注其他无关的后续尾部位数 化为小数的视角理解 可以认为是存在 1后的”截断”

可以认为 nn作为轮转步数 而θ\theta 作为无理数 nn的增加会导致 nθn\theta 不重复的分布在 [0,1)[0,1) 之内 我们简记

{nθ}=xθ−y\{n\theta\}=x\theta-y

xx就是要找的值 yy表示多”转”了多少圈

从y=0y=0开始 先讨论是否存在整数xx

Lθ≤x<Uθ\frac{L}{\theta} \le x < \frac{U}{\theta}

如果存在 直接返回xx

否则 需要找到xx满足

y+Lθ≤x<y+Uθ\frac{y + L}{\theta} \le x < \frac{y + U}{\theta}

求xx和求yy没有本质区别 以yy为主元

xθ−U<y≤xθ−Lx\theta - U < y \le x\theta - L

注意我们前面的操作手法 都是 只讨论必要的小数 给无关的整数截断

两侧除去θ\theta 给共同的整数xx截断 即化归为等价问题

x−Uθ<yθ≤x−Lθx - \frac{U}{\theta} < \frac{y}{\theta} \le x - \frac{L}{\theta}

截断掉xx 只需分析

L′≤{y⋅{1θ}}<U′L' \le \left\{y \cdot \left\{\frac{1}{\theta}\right\}\right\} < U'

后续按照一样的思路 定义新的x0x_0 y0y_0 来递归求解即可

Pandora#

These mathematical structures are noisy and chaotic enough, so hiding my numbers here must be safe, right?

先看给了什么

ζ = list(h.reduced_form())[:2]; ζ[0] >>= 360
py

ζ\zeta涉及hh 而hh 由下列代码定义

h = Oq.ideal([A, (-B + q*K.gen())/2]).quadratic_form()
python

我们需要分析三个变量 A B K

  • A由a决定 而

    a=x2+py2a = x^2 + py^2
  • B的定义为

    B≡bq(mod2a)B \equiv bq \pmod{2a}
  • K.gen() 由前文对K的定义 严格为

    K=Q(−p)K = \mathbb{Q}(\sqrt{-p})

随后在定义的Oq环上由A B 定义一个二次型 并且约化后告诉你系数 ara_r的高位 和完整的 brb_r

λ = f"{R(-Δq).sqrt():.470f}".split('.')[1]
python

相当于提供

λ=qp\lambda=q\sqrt{p}

的高精度小数位数

因为它的高位被精准的截断了 我们可以写D=T+λ\sqrt{D} = T + \lambda 等式两边平方

D=(T+λ)2=T2+2Tλ+λ2D = (T + \lambda)^2 = T^2 + 2T\lambda + \lambda^2 D−T2=2Tλ+λ2D-T^2=2T\lambda+\lambda^2

也就是 2Tλ+λ2≈某个整数2T\lambda + \lambda^2 \approx \text{某个整数}

这是个高精度的Integral Diophantine approximation ↗


令 Q=10470Q = 10^{470} 把小数转化为整数方便做格

D=(T+LQ)2=T2+2TLQ+L2Q2D = \left(T + \frac{L}{Q}\right)^2 = T^2 + \frac{2TL}{Q} + \frac{L^2}{Q^2}

即

D⋅Q2=T2Q2+2TLQ+L2D \cdot Q^2 = T^2 Q^2 + 2TLQ + L^2

所以说 2TLQ+L22TLQ + L^2 必须得是Q2Q^2的整数倍 也就是一定存在线性关系

n⋅Q2+T⋅(2LQ)+1⋅L2=0n \cdot Q^2 + T \cdot (2LQ) + 1 \cdot L^2 = 0

做

B=(Q2002LQQ0L20Q⋅X)B = \begin{pmatrix} Q^2 & 0 & 0 \\ 2LQ & Q & 0 \\ L^2 & 0 & Q \cdot X \end{pmatrix}

有

(n,T,1)⋅B=(nQ2+2TLQ+L2,T⋅Q,QX)\begin{pmatrix}n,T,1\end{pmatrix} \cdot B = \Big( nQ^2 + 2TLQ + L^2, \quad T \cdot Q, \quad QX \Big)

XX取很大的量来固定目标向量第三位只能取1 提取第一位接近0的向量即可恢复TT 从而恢复DD


解出DD之后拷打LLM 给出了如下性质

在类群密码学中,任何一个二元二次型 h=[a,b,c]h = [a, b, c] 必须满足其判别式恒等于系统参数 Δq\Delta_q

这里ζ\zeta给出的虽然是约化后的二次型 但是仍然满足 br2−4arcr=−Db_r^2 - 4a_r c_r = -D 把已知的都移到一边 我们有 arcr=br2+D4=Ka_r c_r = \frac{b_r^2 + D}{4} = K 这里给我们 ara_r的高位 可以写 ar=Ah⋅2360+ua_r = A_h \cdot 2^{360} + u 问题转化为经典的已知因子的高位 要进行分解

赛中尝试了朴素的small_roots会直接OOM 于是进行了如下优化

  • a≤⌊K⌋a \le \lfloor\sqrt{K}\rfloor
  • Abase=Ahigh⋅2360+centerA_{base} = A_{high} \cdot 2^{360} + \text{center} 把上界从 23602^{360}压到 23592^{359}

然后根据 论文中的Howgrave-Graham ↗ 的构造搓了一个49×4949\times49格来恢复 ara_r 再恢复 crc_r


恢复 [ar,br,cr][a_r,b_r,c_r]后 目标是恢复 高斯约化前的 [a,b,c][a,b,c] 但是赛中发现 a=x2+py2≈2512⋅(2120)2=2752a = x^2 + py^2 \approx 2^{512} \cdot (2^{120})^2 = 2^{752}

D=pq2≈2512⋅(2512)2=21536D = pq^2 \approx 2^{512} \cdot (2^{512})^2 = 2^{1536} C≈D4a≈215362754≈2782C \approx \frac{D}{4a} \approx \frac{2^{1536}}{2^{754}} \approx 2^{782}

而 if B >= a: B -= 2*a 已经保证了 ∣B∣≤a≤C|B| \le a \le C 也就是说这里的 reduced_form() 实际上没有对系数进行处理


现在梳理一下 我们已知 D=pq2D=pq^2 a=x2+py2a = x^2 + py^2 B≡bqmod  2aB\equiv bq\mod 2a 需要解x  yx\;y

接着拷打LLM得到了在当前整环 OK\mathcal{O}_K 下的代数结构

二次型判别式的关系满足

b2≡−pmod  ab^2\equiv -p \mod a

同乘

b2y2≡−py2≡x2−a≡x2mod  ab^2y^2\equiv -py^2 \equiv x^2-a\equiv x^2\mod a

故而

(by−x)(by+x)≡0mod  a(by-x)(by+x)\equiv 0 \mod a

取

by+x=ma(0)by+x=ma\tag{0}

而由于B≡bqmod  2aB\equiv bq\mod 2a 故而 B = bq - 2an\tag{1}

因为我们是已知 BB的 关于bb的系数只差一个qq来对齐 我们构造

qby+qx=qmaqby+qx=qma y(B+2na)+qx=qma→yB+qx=(2ny−qm)a=kay(B+2na)+qx=qma\rightarrow yB+qx=(2ny-qm)a=ka

这一步最大的帮助就是帮我们给 aa BB之间建立了 从模转化到线性的代数关系


回看二次型 hh 有

h(X,Y)=aX2+BXY+CY2h(X,Y)=aX^2+BXY+CY^2

而我们一定有 B2−4aC=−DB^2-4aC=-D 给CC消掉 即 C=B2+D4aC = \frac{B^2+D}{4a}

代回去

h=4a2X2+4aBXY+B2Y2+DY24ah=\frac{4a^2X^2+4aBXY+B^2Y^2+DY^2}{4a}

而我们又有 yB+qx=ka\tag{2} 平方一下k2a2=y2B2+2xyqB+q2x2k^2a^2=y^2B^2+2xyqB+q^2x^2

这个二次型其实是存在这样的构造的 我们先令 X=kX=k 看看会怎么样

h(k,Y0)=4a2k2+4akBY0+B2Y02+DY024ah(k,Y_0)=\frac{4a^2k^2+4akBY_0+B^2Y_0^2+DY_0^2}{4a}

后两项关注 B02Y02B_0^2Y_0^2 这个4也不难配平 取 Y0=−2yY_0=-2y

h(k,−2y)=4a2k2−8axykB+4B2y2+4Dy24a=(ak−By)2+Dy2ah(k,-2y)= \frac{4a^2k^2-8axykB+4B^2y^2+4Dy^2}{4a}=\frac{(ak-By)^2+Dy^2}{a}

3式让我们可以消去 ak−By=qxak-By=qx 而D=pq2D=pq^2 所以说

h(k,−2y)=q2x2+pq2y2a=q2(x2+py2)a=q2h(k,-2y)=\frac{q^2x^2+pq^2y^2}{a}=\frac{q^2(x^2+py^2)}{a}=q^2

在我们已知二次多项式关系 h(k,−2y)=q2h(k,-2y)=q^2 后 由于我们还知道 q2q^2的倍数 DD 在shift中加入 DD 即按照 g(X,Y)=h(X,Y)i⋅Dm−ig(X,Y) = h(X,Y)^i \cdot D^{m-i} 的shift思路造格 可以成功规约出满足 h0(k,−2y)=0h_0(k,-2y)=0的系数多项式 它长这样 h0(X,Y)=c0Y12+c1XY11+c2X2Y10+⋯+c12X12=0h_0(X,Y) = c_0 Y^{12} + c_1 X Y^{11} + c_2 X^2 Y^{10} + \dots + c_{12} X^{12} = 0 变形成 c0+c1(XY)+c2(XY)2+⋯+c12(XY)12=0c_0 + c_1 \left(\frac{X}{Y}\right) + c_2 \left(\frac{X}{Y}\right)^2 + \dots + c_{12} \left(\frac{X}{Y}\right)^{12} = 0 解出有理根 k−2y\frac{k}{-2y} 展开成分数 解出y  ky\;k 可以算qx=yB−kaqx=yB-ka 然后gcd解qq 算xx

ArrAnge in Asceding#

From Bubble Sort to Quick Sort, the pursuit of order has always defined humanity.

chal

setup

https://arxiv.org/pdf/2412.15126 ↗

远端生成 Base=[x0,x1,…,x127]Base = [x_0,x_1,\dots,x_{127}] 并给出CKKS加密后的密文 要求得到这128个项的大小索引 加密后重新发回去得到flag 附件给了CKKS的公钥和Galois旋转密钥

CKKS属于FHE的一种 能够支持不解密的情况下对于密文进行计算 等价于直接计算明文

所以初步策略是直接对于密文进行计算 让明文解密后的结果直截等于需要的rank数组

这里引入一个指示函数I(xi,xj)=sgn⁡(xi−xj)+12I(x_i, x_j) = \frac{\operatorname{sgn}(x_i - x_j) + 1}{2}

有 Rank[a]=∑I(xa,xi)Rank[a]=\sum I(x_a,x_i)

但是CKKS只支持加法乘法 关于 sgn这种跃迁函数需要用多项式方法进行拟合 拷打LLM后给出了一个能够使用的思路

参考 https://ieeexplore.ieee.org/document/9517029 ↗ 先用chebshev进行拟合 再在7~8层加入minimax函数

进行复合多项式的拟合 把符号函数的结果拓展到1与-1

这一步能解决排序精度问题 但是开销非常大 相当于每次比较都需要shift 127次 远端会断连

再参考 https://eprint.iacr.org/2025/198 ↗ 利用已有的galois密钥 给base向量拓展到 行列矩阵

直观上 我们简写一下这两个矩阵长什么样

VR=(x0x1⋯x127x0x1⋯x127⋮⋮⋮x0x1⋯x127)VR=\begin{pmatrix}x_0&x_1&\cdots&x_{127}\\x_0&x_1&\cdots&x_{127}\\\vdots &\vdots&&\vdots\\x_0&x_1&\cdots&x_{127}\\\end{pmatrix}

VC=(x0x0⋯x0x1x1⋯x1⋮⋮⋮x127x127⋯x127)VC=\begin{pmatrix}x_0&x_0&\cdots&x_{0}\\x_1&x_1&\cdots&x_{1}\\\vdots &\vdots&&\vdots\\x_{127}&x_{127}&\cdots&x_{127}\\\end{pmatrix}

做差 复杂度是O(1)

D=(0x1−x0⋯x127−x0x0−x10⋯x127−x1⋮⋮⋮x0−x127x1−x127⋯0)D=\begin{pmatrix}0&x_1-x_0&\cdots&x_{127}-x_0\\x_0-x_1&0&\cdots&x_{127}-x_1\\\vdots&\vdots&&\vdots\\x_0-x_{127}&x_1-x_{127}&\cdots&0\end{pmatrix}

然后调用多项式工具 拟合符号函数 再直接进行计算即可

*OhMyCaptcha#

I’ve designed a group verification mechanism, let’s try.

task

先提一下预期解

SandBoxService提供了一个沙箱来运行template的模板 而其中的 eval运行执行 message 再把沙箱的输出进行RSA加密 这里的指数比较小


sandbox的构造#

template中的eval允许读已有的信息 可以构造message分别为 key dir()[-2][-16:] 给AES的key 与nonce 读出来 这里二者大概是128字节 而5次幂下截断大概在124字节 稍微爆破一下模数的倍数就可以给二者成功恢复

此时还剩两次机会

随后传eval(dir()[-2]) 可以得到 cipher RSA加密的结果 不难想到有相关明文攻击的机会 即 Franklin-Riether-Attack 那么接下来只需要构造一个payload 能够让沙箱输出 f(cipher)的值即可 先 dir()[-2] 获取cipher的变量名 再 globals()[dir()[-2]] 得到取值 最后字符串拼接一下 globals()[dir()[-2]] + 'a'

后续搜索的时候 key实在太短 导致m太小 换成了 dir()[-2][16:32]

最后使用的是

  • key — dir()[-2][16:32]
  • nonce — dir()[-2][-16:]
  • cipher — eval(dir()[-2])
  • f(cipher) — eval(dir()[-2])*2

pn的搜索构造#

上述payload在末尾增加 # 后再在末尾增加各种乱码也不会引起问题 同时注意到虽然pn是公共的 但是题目给了我们code 和时间 我们完全可以

  • 搜索找10~16组可用的 pn 这一步非常简单 素数 长度OK即可
  • 得到code后 再针对key nonce cipher f(cihper) 当场搜索suffix即可

这里就可以转化为一个背包格问题 来求解

我们的message可以看成

mi=knowni+∑255i×charim_i=known_i+\sum255^i\times char_i

只需满足

mi≡codeSubStrmod  pnim_i \equiv \text{codeSubStr} \mod pn_i

这里应该是存在一个背包格的转化的 毕竟suffix 转化回来只会影响低位 但当前还没发现一个稳定的规约成功suffix的思路 如果想要赛中优化 可以参照

  • 字串从 01⋯90\quad1\quad\cdots\quad9 拓展到2位 比如 1234⋯8912\quad34\quad\cdots\quad89 这样 空间会更足

后续对着官方wp分析还是发现比较大的思路差距了

这里我们搜索的实际上是后缀的字节 本质还是几十的相对大数字,而赛题的关键是

x≡rimod  pix\equiv r_i\mod p_i

其中rir_i是10以内的小量 这也就意味着

如果我们能先确定pip_i 再把这个细微的偏移给求出来 那也一样的可以恢复我们的xx

所有放在模数上的问题我们都不太喜欢 但是如果先确定pp 那就转化为了去规约一个0∼90\sim9的量 我们还可以中心化 令

ui=ri−5∈{−5∼4}u_i=r_i-5\in\{-5\sim4\}

规约这里的uiu_i并不难

再说回命令本身 当我们先确定了n=64n=64组pip_i后 格的规模和维度基本就被固定在64附近 此时前缀相对而言长度没有那么敏感,规约效率也高了很多,同时当payload的长度上升 反而能给我们的xx提供更大的宽度容错区间

处理数据与格#

构造四次message 前两次拿下 key nonce 后续拿下 cipher f(cipher)

前2者是16字节 稍微爆破即可完全恢复 而cipher是94字节 再知道 RSA加密结果后 相当于

m5≡c1mod  nm^5\equiv c_1\mod n (2m)5≡c2mod  n(2m)^5\equiv c_2\mod n

我们很容易计算出来 mmod  nm\mod n 接下来就需要分析 因为AES加密的结果实际上是全部为hex的字串 考虑继续使用背包格求解 可以针对 a-z0-9 来进行稍微的移位和缩放 规约到待求小量均是 [−4,4][-4,4] 的格中 但是这个格的复杂度还是相当大的 目前没有调出来 如果想参考类似的求法可以看看 AliyunCTF2026-Griffin-Rec师傅的手法 ↗ 这道题是uuid格 但是对于hex的处理还是值得参照的

个人的相关经验和积累还是太少了… 面对这道题赛中折腾了很长一段时间的eval短命令读取…


嗯感谢wuy4n师傅提供的相关脚本 这里贴一个格的构造

这里预期使用的是256来进行balance

f = sum([(a * xs[i] + ys[i] + b) * 256**(k - 1 - i) for i in range(k)]) - c
py

结合仿射 对如下式子做背包格

C=∑i=0k−1(52+48xi+yi) 256k−1−iC=\sum_{i=0}^{k-1}(52+48x_i+y_i)\,256^{k-1-i}

亦可尝试改为1616 来进行balance 取得相对更加可控的格规模

对于hex的字符 令

ord⁡(sj)=16Hj+Lj\operatorname{ord}(s_j) = 16H_j + L_j
  • sis_i若是数字: Hj=3,Lj∈0,…,9H_j=3,\quad L_j\in{0,\dots,9}
  • sis_i若是字母: Hj=6,Lj∈1,…,6H_j=6,\quad L_j\in{1,\dots,6}

可以转化为

ord⁡(sj)=16⋅3uj+128+(4+vj)\operatorname{ord}(s_j) =16\cdot 3\frac{u_j+12}{8} + (4+v_j) ord⁡(sj)=6uj+vj+76\operatorname{ord}(s_j)=6u_j + v_j + 76

其中

uj∈{−4,4}u_j \in \{-4,4\}

对应的

Lj=4+vjvj≈[−4,5]L_j = 4 + v_j\quad v_j \approx [-4,5]

背包格相对应转化为

C=∑i=0k−1(12+3ui) 162k−1−2i+∑i=0k−1(4+vi) 162k−2−2iC=\sum_{i=0}^{k-1}(12+3u_i)\,16^{2k-1-2i} + \sum_{i=0}^{k-1}(4+v_i)\,16^{2k-2-2i}

demo


再简要说一下非预期 可以直接通过

help('secret')
bash

来读取FLAG 虽然flag的长度相当大 但是可以多次重连 获得多组 nn 结合已有的 FLAG5mod  niFLAG^5\mod n_i 合并起来打CRT直接恢复 FLAG 😶‍🌫️

ACTF2026
https://www.zt2misay2.cn/blog/actf2026
Author Misay
Published at 2026年5月12日