NSCTF2017-SteinsGate wp

0x00 前言

挺不错的一道题,思路值得学习,所以简单记录下。

0x01 恢复文件

查看文件头为PK,改后缀名为zip,解压得到三个文件,分别是

  • flag.enc:加密后的flag
  • encryption:加密算法脚本
  • 一段base64:解码后转二进制文件,查看文件头JFIF,为图片(DivergenceMeter.jpg)

0x02 读加密算法逻辑

算法逻辑主要可分为两部分:

1.对t0的求解。

其中t0的值和操作系统平台(Windows/Linux/Darwin),字长位数(32/64),当前用户uid,随机小数r(范围0-2,且最多6位小数),m的md5值有关。

t0 OS 字长位数 uid r 32位md5(m)
m 字长位数 ‘bit’ uid r1
r 个位(0或1) 小数位(最多6位)
r1 个位(0或1) 小数位(最多2位)

这里还可以知道t0的位数应该在48左右(5+2+2+7+32)

2.对t1的求解及使用

t1的值由flag的值和t0异或得到(位数也在48左右),之后算法使用了给的图片文件(DivergenceMeter.jpg),在文件中找和t1各个字符(t1[i])相同的值img[k],并提取索引值k,转换为字符(k与255取模保证在ascii码范围内),遍历t1生成表l,l的结构大致如下:

t1[i] 索引 列表(data)
t1[0] 0 chr(k1),chr(k2),….,chr(kn)(其中k1,..kn为图片中和t1[0]相同的字符所在位置)
t1[n] n chr(k1),chr(k2),….,chr(kn)(其中k1,..kn为图片中和t1[n]相同的字符所在位置)

得到表l后,纵向遍历该表,并把读取数据写入result,最终得到加密后的flag文件flag.enc。

0x03 恢复t1

由上述逻辑可知,突破点在flag.enc,假设t1取48位,则flag.enc的前48位应该是t1[i] (i=0,1,…,47)的第一个元素,即chr(k1),因此可以通过遍历图片文件,将t1的值扩大到包含所有字符(256个)的类表l的索引表table:

ascii码(索引) 列表(data)
0 chr(k1),chr(k2),….,chr(kn)(其中k1,..kn为图片中和chr(0)相同的字符所在位置)
255 chr(k1),chr(k2),….,chr(kn)(其中k1,..kn为图片中和chr(255)相同的字符所在位置)

得到表table后,遍历该表的table[i][0]查找和flag.enc的前48位(记为head)分别相同的索引值转ascii并记录,得到t1

这里有个问题,存在多个table[i][0]和head[j]相同的情况,因此还要判断table[i][1]的情况。打印出几个值判断下:

由于t1位数限制关系,因此table[i][1]应该和table[i][0]距离不远,在文件中查找,基本可以确定A9是t1构成的表l[0][1],因此确定了i,从这里也可以得到t1的实际位数应该是45位,之后的写个代码取flag.enc的46位开始和table[i][1]判断下就可以,具体看最后的代码实现部分。

0x04 恢复t0

上一步中得到了t1,因此这一步主要是通过以下几方面确定t0的值:

t0 OS 字长位数 uid r 32位md5(m)
m 字长位数 ‘bit’ uid r1
r 个位(0或1) 小数位(最多6位)
r1 个位(0或1) 小数位(最多2位)

1.确定操作系统及字长位数

根据题目提示,flag.txt中含完整flag,也就是说前几位为’flag{‘,将已知的flag与t1异或得到t0的头几位为’Darwi’,基本确定操作系统为’Darwin’,又因为python文档中写明若为该类型时机器为64位,故得到t0的前8位为’Darwin64’。

2.确定32位md5值

根据题目提示,flag的最后一位应该是’}’,因此md5的最后一位与t1的最后一位异或得到的值应该是’}’,运算得知为’e’,即得到的md5最后一位应该是’e’

m的前几位已知为’64bit’,开始爆破uid和r1,r1由已知应该是1到3位,uid的位数可以通过t0确定(由于t0为45位,r最少为1位,可以算出uid位数应该在1-4之间[1,45-8-32-1])

写脚本爆破并对求得的符合要求的md5值(末位为’e’)与t1的末32位异或,找到符合要求的部分flag(只含字母、数字、’_’、’}’)

得到部分flag:’ce_0f_st3ints_G4t3_1p048596_233}

对应md5值为afe46f1497072540f60d793e1e5a2a6e,uid为520,r1为125

3.确定r值

由上述uid的值可以确定r的位数应该为2(45-8-32-3),注意到r的第一位不是0就是1,写脚本爆破并组合成最终的t0和t1进行异或,打印出符合要求的flag(只含字母、数字、’_’、’{‘,’}’)。
这里有个问题,uid为520时打印出的flag为flag{to7^im91ce_0f_st3ints_G4t3_1p048596_233},改了uid为420才输出成功。

最终t0:Darwin6442000afe46f1497072540f60d793e1e5a2a6e

最终flag:flag{to7_im91ce_0f_st3ints_G4t3_1p048596_233}

0x05 解密脚本




你可能感兴趣的:(CTF)