刚好看到就做了下题,真的感叹国外赛事的高质量,每次都能学点东西。比赛地址:https://2022.imaginaryctf.org/Challenges
Web
button
页面看起来是空白的,审计前端代码,发现有非常多的白色小按钮(所以看不见),在源码搜索ctf可以找到嵌入式js代码,可以发现有几个button对应执行的是motSusfunclion(),所以我们只要去控制台输入此函数就能弹出flag:ictf{y0u_f0und_7h3_f1ag!}
rooCookie
根据题目描述,出题人把密码藏在了cookie里,审计会看到cookie以及生成cookie的js代码,于是把算法逆一下就可以恢复password,即flag。
1 | function createToken(text) { |
1 | cookie = '101100000111011000000110101110011101100000001010111110010101101111101011110111010111001110101001011101001100001011000000010101111101101011111011010011000010100101110101001101001010010111010101111110101011011111011000000110110000001101100001011010111110110110000000101011100101010100101110100110000101011101111010111000110110000010101011101001011000100110101110110101001111101010111111010101000001101011011011010100010110101110110101011011111010100010110101101101101100001011010110111110101000011101011111001010100010110101101101101100000101010011111010100111110101011011011010111000010101000010101011100101011000101110100110000' |
SSTI Golf
这种白盒ssti应该算是最简单的,也没什么过滤,基本就只有payload长度限制;首先看一下源码:
1 | #!/usr/bin/env python3 |
试一下模板解析,可以成功:
所以接下来尝试执行命令,首先用config看看全局变量,发现有os,那么可以利用lipsum访问os进行命令执行(popen有回显)
1 | lipsum flask的一个方法,可以用于得到__builtins__,而且lipsum.__globals__含有os模块:{{lipsum.__globals__['os'].popen('ls').read()}} |
这是payload:
其实这题还有其他利用方法,我们可以用脚本去遍历一下object的子类,利用其他子类的特性进行rce,作为拓展,给出链接:
http://www.hackdig.com/06/hack-677892.htm
https://blog.csdn.net/zbbjya/article/details/124185476
Misc
Sponsors
给了赞助商的主页,访问其中一个,有个视频,视频的结尾处有flag。
pyprison
python沙盒逃逸,以前经常遇到但不会做,这次的比较简单,学习了一下。
首先是给了我们python jail的源码:
1 | #!/usr/bin/env python3 |
服务器会过滤除了字母和()之外的所有符号,其余的部分用exec当作命令执行,在操作之前,先了解一下python动态加载模块。逃逸过程分析:
1.eval(input())
这里绕过了过滤,并且可以把我们输入的字符当作命令执行,这里之所以要加上eval,是因为出题人故意用的exec,它是无回显的,所以我们才需要自己加一层eval(有回显),或者用print(exec())
2.—import—(‘os’).system(‘ls’)
这里不多说了,动态加载os模块rce
两种利用方法:
Crypto
emojis
序列有两种符号,分别转成0和1,再转成字符串即可:ictf{enc0ding_is_n0t_encrypti0n_1b2e0d43}
smoll
1 | from Crypto.Util.number import * |
huge
n由很多个不大的素数相乘,多因子,高次数n求phi即可。
1 | from Crypto.Util.number import * |
cbc
自己实现了不安全的cbc分组模式,直接把前一组的密文当作key给下一组加密,相当于知道key了,第一组分组解不出来没关系,题目加密的是flag*3。
1 | from Crypto.Cipher import AES |
Secure Encoding: Hex
这题以前没做过类似的,重点记一下;题目是把16进制的字符表置换了:
1 | #!/usr/bin/env python3 |
由排列组合的知识可知一共有16!种置换方案,这很大无法直接爆破。但观察到把ictf{和}转成16进制和密文的hex的字符频率一致,猜测前后格式就是ictf{};那么根据这一部分明文密文对照可以得到置换群的部分映射关系,剩下的爆破即可,剩下需要爆破a96种组合,过滤一下字符得到最终flag。
1 | import itertools |
Lorge
p-1光滑,但因子不超过25bit这一限制导致因子还是可能较大;Pollard’s p − 1 算法可以做但是需要改进一下。
这是通常的算法写法:
1 | def Pollards_p_1(N): |
算法这样写是为了保证质数因子次数大于1的时候也能分解出来,并且每次对n加1都进行一次gcd判定,当素因子比特稍微大点效率就很低,所以普通的写法无法做出本题。想一想如何优化?
1.减少gcd判定 2.改变每次n加1的流程
减少gcd判定很好理解,只要前边依次乘了素数,隔一段时间进行一次判定即可;而n每次加1是因为可能出现质数因子次数大于1的情况,由于这题的特殊性可以大胆猜想因子次数都是1,否则几乎无法优化。综上所述可以做出优化如下:
1.由于比特最大为25,所以可以把这个范围内的质数先全部找到,遍历的时候只需要按索引取值,python库找素数的算法肯定比我们自己后续遍历的时候取素数效率高。
2.先把大部分素数乘起来,这个过程不去gcd判定,以节约时间(猜想出题人肯定会放几个大素数进去的);在乘最后一部分大素数的时候n每增加1000左右做一次判定。
最后的exp:
1 | from Crypto.Util.number import * |
四五分钟能跑出结果。
hash
这道题hash师傅用矩阵解的,我不太能理解,于是找到了暴力破解的办法;因为password长度在15-20,分布区间很小,所以考虑遍历,用z3求解,一是因为z3对于xor求解运算很快,二是由于password是string.printable,所以限制条件足以得到正确解。exp:
1 | from pwn import * |
otp
这题出的挺好,考查选手用数理统计的知识解决密码学问题;出题人生成了一个jumbler列表,然后利用jumbler实现了一个看似随机的序列生成器;
1 | def secureRand(bits, seed): |
这样的白盒生成器让我们有机会去直接对0和1的生成结果进行统计,可以发现每次生成1和0的概率大约为2:1;因为加密过程就是简单的异或操作,所以只要是0,明文和密文该位不变,若为1,则该位取反;因此加密有大概70%的概率是把该位翻转。
1 | jumbler = [] |
因此我们可以多次请求flag的密文,得到多组密文;统计各组密文每一位的0和1个数比例,方法是把各组对应位的数字(1 or 0)加起来,与组数的0.5倍进行比较,看该位1和0哪个更多;cipher中某一位0出现多说明其原本为1,1出现的多则原本为0。exp参考大佬的,很简洁
1 | from pwn import * |