ciscn2025
Crypto
rasnd(复现)
- 这题当时没有写出来,但是之后复现了一下极客大挑战的一题密码题之后就会写了,这题与其他题目相比简直就是一个送分题目。可惜当时没有复现出来。现在复现一下
- 由于这题的密文等都是要与靶机交互,由靶机给我们的,所以我就自己设置了个flag,来解密。
- 题目如下:
1 | from Crypto.Util.number import getPrime, bytes_to_long |
- 这题的加密过程就是将flag分成两部分来加密,这两部分都是使用RSA进行加密,但是泄露的值是不一样的,所以一些做法是不一样的。
crypto1
- 先来解决
crypto1的部分。 crypto1的部分泄露的值是
- 所以我们要根据这两个hint求出p和q的其中一个然后分解n进行后续的RSA解密。
- 可以注意到两个x和两个y的生成方式
- 其中
y1和y2在比较大的范围内产生随机数,如果对y1和y2进行爆破就会有时间问题,而爆破不出来 - 而
x1和x2在随机数比较小的范围内取值,所以我们对x1和x2进行爆破,从而得到一些信息
- 其中
1 | x1=randint(0,2**11) |
- 如果我们将
x1和x2爆破出来后,通过如下推导就可以得到q的倍数
- 这样q求出来后就是常规的RSA解密了
crypto2
- 第二个加密部分仍然是RSA加密,但是提示给的不是随机数了。所以要根据提示进行推导
- 第二部分加密给的提示如下
- 接下来对该式的变形推导如下,推导的时候应用了如下定理
定理
定理1:欧拉定理 $$ \begin{array}{l} 若n,a为正整数,且n,a互素即(gcd(a,n)=1),则有\\ a^{\phi(n)}\equiv 1 ~mod(n) \end{array} $$- 推导的过程如下:
- exp如下:
1 | import libnum |
PWN
anote
novel1(复现)
这题从10号的凌晨2点到凌晨6点,下午4点到晚上10点多才复现完,太牢了QAQ
这题是一到hash碰撞的题目,先在做这题之前先要来介绍一下C++STL中
std::unordered_map的底层原理。- 这个
std::unordered_map有点类似Python中的字典,都是存在键值对即<key,value>,一个key对应着一个value,接下来来说明一下std::unordered_map的底层原理。利用hash碰撞,就可以构造出栈溢出,从而可以构造ROP链泄露libc的地址,然后再getshell - 在这题中,对于使用
gadget自己构造rop链比较难构造,但是使用one_gadget打是比较简单的。
附件分析
- 拿到附件后先检查一下,保护机制。发现没有开启Canary保护,也没有开起PIE保护机制。

- 之后将该附件拖入IDA进行逆向分析,同时运行一下程序,查看程序的运行逻辑。首先查看main函数。main函数这边实现的是菜单的功能。

- 之后查看
prologue()这个函数- 这个函数中定义了一个
unsigned int,unsigned long的键值对,这个哈希桶数组为bloodstains,然后调用了clear方法,将该哈希桶数组中的元素清空。 - 之后让我们输入这个作者的名字,这里没有栈溢出点
- 这个函数中定义了一个

- 之后查看
chapter()函数:该函数提示我们选择章节1和章节2,并读取我们输入的数字,并检查我们输入的数字是否为1和2

- 之后我们看到
part1这边,这边会对让我们输入Blood和Evidence,并将其存储到bloodstains这个unordered中。这边还有一下几个检查- 检查
bloodstains中的长度(即数组元素)是否有超过0x1F即(31) - 检查之后输入的键和值,是否有和之前输入的键和值一样,如果一样则会退出程序
- 键和值不一样后就可以插入键值对到
bloodstains中。
- 检查

- 这里再来查看
part2这边的具体操作,这里会发现变量v3在copy的时候会发生溢出。- 该程序会让我们输入一个
Blood,查找该与该桶索引相同的键值对,如果没有对应的键值对,则程序会退出 - 如果查找到了对应的键值对,则会将对应的所有键值对都复制到
v3这个里面,v3在栈上。所以这个过程会发生栈溢出。
- 该程序会让我们输入一个

- 在逆向分析附件的时候还发现了这样的一串
gadget

构造hash
- 所以我们要发生溢出,就必须构造特殊的hash值,构造的这个hash值必须满足如下条件:
- 对于一开始分配的桶数,键的hash值模上对应的桶数结果应该相同。
- 而之后hash存储桶数变大,变大之后的键的hash值模上对应的桶数结果应该也要相同。
- 所以通过如下脚本可以生成30个具有如上条件的hash值的数。
1 | // g++ collisions.cpp -o collisions |
动态调试
- 通过构造hash值,并输入到程序里面,我们就可以进行进一步的调试,从而找到溢出点,构造rop链。
- 编写如下脚本:
1 | from pwn import * |
- 然后我们从
part2分析,在没有复制数据之前,栈上的数据是这样的。

- 当我们运行到断点
0x402bf3时继续查看栈上的地址就会发现,在复制键值对到栈上的时候。这时我们再使用stack 48查看栈上的地址。我们就会发现,在复制的过程中发生了栈溢出,并且通过查看栈上的数据,键0x26768099所对应的值干好能覆盖为返回地址。

- 同时这里再溢出的时候还要注意一个地方,在溢出的时候,我们会溢出到变量
i和v11的值,这时就要控制一下这两个变量的值,以免造成死循环。

- 这时我们就可以通过栈溢出,利用
pop rsp这个gadget进行栈迁移,将栈迁移到bss段上的author,构造rop链,先泄露一下fgets的got表,从而得到libc的基地址,所以我们在输入author的时候就要先构造一次rop链

- 泄露这个地址后,还需要再次输入,继续rop链的再一次构造。这样才能getshell,所以我们使用
puts输出fgets的地址后,就要返回到输入Author:值之前,由于我们把栈迁移到了author这个到我们就可以直接在authonr的可以继续布栈

- 在这里由于该
.bss段距离不可写段太近,所以这里直接使用one_gadget去getshell,这里我们使用命令one_gadget libc.so.6,查找one_gadget

- 我们再通过查找发现,
r12、r13的的寄存器,这样就满足了one_gadget的寄存器要求。

- 所以就能构造rop链,利用
one_gadget去getshell
exp
- exp如下:
1 | from pwn import * |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 iyheart的博客!

