DASCTF2025上半年赛
PWN
mini
-
考点:
only-read
-
这题与
ACTF
的那题是一样的题型都是only-read
的题型,只不过这题溢出的字节数非常小,只有0x20
字节,而ACTF
那题溢出的字节数非常大。 -
先来查看一下程序保护机制。没有开启
PIE
和canary found
。
- 然后查看一下这个程序的主要代码,发现只能溢出到
ret
这个部分。所以这题必须栈迁移。
- 此时还发现了
pop rax ret
这个gadget
,那这题的思路还是比较明显的,思路为ret2syscall
利用SROP
从而getshell
,返回到的是__libc_start_call_main
这个函数中的一个syscall
。
- 首先我们先来栈迁移,将栈内存迁移到
bss
段中,但是由于bss
段上还没有libc
地址,所以此时迁移之后还需要返回到_start
将程序从头开始运行,这时bss
段上就有相关的数据。如下图:
- 但是由于先要通过溢出才能构造
SROP链
,并且溢出必然会覆盖__libc_Start_call_main
这个函数的地址。但是注意到__libc_start_main+133
这边是在__libc_start_call_main
函数的附件,这样其实就选择使用爆破1
个字节来得到返回到__libc_start_call_main
那边的syscall
。 - 但是在执行之前,需要先在
__libc_start_main+133
后面的地址处布置好srop
所需要的数据。但是由于溢出字节不够,我们只每次0x10
字节将SROP
链写入到bss段中。 - 这里要解释一下为什么写完要跳到
bss_addr-0x250
这边,其实是为了让rsi
跑远一点不能与rsp
小0x10
字节(这个地方后面还需要用到),如果rsi=rsp-0x10
,程序会在read
调用之后,从rsi
所指地址中写入数据,此时就会覆盖call read
时压入栈上的返回地址,导致程序出现问题。
1 | def my_srop(i): |
- 布置srop的结构如下,并且布置完srop后的栈图如下:
1 | frame = SigreturnFrame() |
- 布置完后,我们就需要步骤
rop
链,将系统调用号15
给rax
,但是我们要通过正常溢出是不可能的,此时我们就需要通过前面read
的那个小技巧这样才能同时构造rop
链,并且还能溢出修改0x2
个字节将__libc_start_main+133
修改为__libc_start_call_main
处的那个syscall
。 - 所以最后一次返回的时候,我们要满足
rsp=rbp
并且rsi=rsp-0x10
,在没有调用read
的时候栈布局是这样的。
- 在调用read之后写入数据,还没返回时,栈布局是这样的。
- 所以调用
read
从rsi
开始写数据的时候,就会先将ret_addr
给覆盖了,我们就可以提前布置rop
链将ret_addr
布置成pop_rax ret
,之后再将ccc
修改成15
,最后修改ret_addr
的最后两个字节,其中我们需要爆破4位
。构造之后的栈布局如下:
- 这样我们其实就能getshell了,这里在打本地的时候关闭了
ASLR
- exp如下:
1 | from pwn import * |
- 远程爆破的话还是比较好爆破的。
1 | DASCTF{5738e34c-70e8-45cf-abc7-ff4eb2265229} |
NSUSServer
Crypto
Excessive Security(复现)
-
考点:
ecdsa
-
参考博客:Triode Field
-
题目如下:
1 | from hashlib import sha256 |
- 这题主要考察的就是
ECDSA
,首先生成了一个椭圆曲线,这个椭圆曲线的各个参数都已知:
1 | curve = SECP256k1 # 使用的是特殊的椭圆曲线,其中G其实已知、p也其实已知、n也就是阶也已知 |
- 这题最主要的一个破解点就是重复使用
k1
和k2
对数据进行签名
1 | def sign(h, priv, k): # 进行签名操作 |
- 接下来对这个过程进行推导
- 由于式子中的这个都是一次的,所以其实我们可以将这个式子理解成为一个模
n
下的四元一次方程组,接下来先化简一下这个式子。这样我们就可以更容易看出来这个四元一次线性方程组。
- 这样其实我们就可以构造一个模
n
意义下的矩阵,解出这个矩阵对应的线性方程组就可以得到了:
- 解出之后就可以来看下一个加密步骤,这个比较简单考察的是
Franklin-Reiter攻击
,而且还是线性的,只要用gcd
(普通多项式gcd算法不行,需要使用H-gcd
)即可求得最大公因式,就可以求出明文m了:
1 | m = bytes_to_long(plaintext) |
- 最终
exp
如下:
1 | from Crypto.Util.number import * |
Strange RSA
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 iyheart的博客!