填坑——脚本大坑

题目资源下载(github)

题目资源下载(static)

首先审下题目资源中所给的脚本,我们可以看到脚本中只给了实现功能的函数,其中完成密文加密功能的函数为makepack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def makepack(uid):
header=headmaker(uid)
while len(header)<50:
header=header+'8'
result=''
j=0
for i in readfile("key.txt"):
if j<50:
result=result+str(ord(i)^ord(header[j]))
else:
j=0
result=result+str(ord(i)^ord(header[j]))
j=j+1

img=readfile("C:\\Users\\Public\\Pictures\\Sample Pictures\\Desert.jpg")
data2send=''
for k in result:
for p in range(len(img)):
if ord(k)==ord(img[p]):
data2send=data2send+chr(p)
break
fw=open("data.css","wb")
fw.write(data2send)
fw.close()

这个函数先获得header,然后读取key.txt的内容,将key.txt中字符的ascii值与header中字符的ascii值异或,将异或结果转成字符串后组装成result(这是个超级大坑…)

随后读取了一张图片的数据,这张图片的路径似乎是windows系统的示例图片,搜索了下,果然是win7系统中的示例图片,这里也就确定了题目所给的密文文件是在win7系统中生成的。

再往下看,居然是依次将图片数据中第一次出现result中各个字符的位置给chr后组装起来,生成密文文件。

综上所述,根据脚本,我们可以很轻松的获得result字符串的内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def readfile(filename):
fobject=open(filename,"rb")
totalfile=""
while 1:
line = fobject.readline()
if not line:
fobject.close()
return totalfile
totalfile=totalfile+line

data = readfile("data.css")
img = readfile("Desert.jpg")
result = ''
for x in data:
result += img[ord(x)]
print result

还记得上面所说的坑点么,那就是组装生成的result字符串是异或结果直接转成字符串后拼接起来的,所以我们不知道组装之前的异或结果到底是几位数字。

想得到原文的话必须确定下来header和result,但是现在result没法确定,想不到什么好办法,只能继续看程序尝试确定header的内容。

header是由headmaker这个函数生成的,而headmaker又调用了之前的一些函数。header前面几位分别为当前脚本运行系统环境的版本号和系统位数。之前通过图片确定为win7系统,搜索了下,win7主要的版本号有如下15个。

win7.png

我觉得以这出题人的尿性他可能会选择个很少见的版本号,但是win7所有版本的前两位是一样的,都为61;而在result中,因为异或的运算双方一个为明文数据,肯定是可打印的字符,另一方为header,尤其是前几位(系统版本号)可以确认为数字字符,所以异或的结果也只有可能在0-128之间,因此result前两位可能的组合有7组。将猜测的header和result异或,得出的结果中只有一组为纯英文(直觉告诉我就是这组…)。

1
2
3
4
5
6
7
header = [ord('6'), ord('1')]
res = [[1, 1], [1, 14], [1, 148],
[11, 4], [11, 48],
[114, 8], [114, 84]]

for r in res:
print str(r[0])+','+str(r[1])+':'+chr(r[0]^header[0])+chr(r[1]^header[1])

这样就确定(疑似)了result的前两位为114和84,header的前两位’6’和’4’,明文的前两位为’De’。

作为一只web狗实在懒得想(也想不出)什么更好的方法去简化这个过程,所以我就选择了最笨的方法,一位位输出然后人工判断…

比如header第三位可能为’6’和’7’,而result的第三位可能为7和71,将他们异或输出…

1
2
3
4
5
6
7
header = [ord('6'), ord('7')]
res = [7, 71]


for r in res:
for h in header:
print 'h -> '+chr(h)+' r -> '+str(r)+':'+'De'+chr(h^r)

根据结果,我觉得只有最后一组像正确的。所以得出header前三位为’6’、’1’和’7’,result前三位为114、84和71,原文前三位为’Dep’。

经过多次尝试,我搞定了header的前六位,也就是’617601’,result前六位为’114 84 71 68 85 66’,原文前六位为’Depres’。这个时候我已经确定我没搞错,毕竟得到的原文很明显就是个正常的单词。
header中接下来两位是系统的位数,只有32和64两个选择,很快就能得出结果。

1
2
3
header = '61760164'
result = '114 84 71 68 85 66 69 93'
key = 'Depressi'

header中再往下是个由getuid函数生成的值,getuid函数可能会生成四个值,但这四个值只有最后一位不同,因此后面五位即uid的值也比较容易得到。(继续一位位尝试…)

1
2
3
header = '6176016410088'
result = '114 84 71 68 85 66 69 93 94 94 16 81 75'
key = 'Depression is'

继续往下看是getpid函数,其实也就是os.getpid()的值,这个值是每次运行脚本时进程的pid,根本不确定,不过在对os.getpid()测试后我发现大部分情况下pid为四位数,五位数很少出现且小于11000。观察已经得到的明文结合英语语法,可以确定再往下的一位明文应该是空格,然并卵还是要猜…

经过艰苦卓绝的测试我确定了pid值为4416,随后的header部分为uid+pid的md5值,加起来总共49位,根据程序还要补上一位’8’。

1
2
3
header = '61760164100884416913e7540852dbcd5f3fcbcb9344d19ae8'
result = '114 84 71 68 85 66 69 93 94 94 16 81 75 20 88 88 93 92 17 82 69 71 89 85 87 77 80 18 16 10 2 16 21 14 90 18 16 66 6 20 92 65 77 20 11 95 92 65 10 94'
key = 'Depression is like a plague that hits every one of'

将已经解出的明文搜索下,发现是一篇英语文章…

有了对应原文,header也已经确定了,接下来只要继续推测出result就可以了…又经过一段艰苦卓绝的测试后,终于得到了flag

1
2
3
header = '61760164100884416913e7540852dbcd5f3fcbcb9344d19ae8'
result = '114 84 71 68 85 66 69 93 94 94 16 81 75 20 88 88 93 92 17 82 69 71 89 85 87 77 80 18 16 10 2 16 21 14 90 18 16 66 6 20 92 65 77 20 11 95 92 65 10 94 22 68 68 22 81 69 22 71 94 93 85 24 72 91 93 95 66 25 88 93 69 91 92 82 85 22 21 119 18 7 17 29 90 8 86 70 0 13 19 7 74 19 67 93 16 89 25 8 17 24 95 95 23 66 88 84 95 70 17 95 71 86 24 67 85 72 26 25 83 70 17 23 90 82 68 93 91 70 13 15 6 23 21 15 71 65 16 66 11 3 75 87 20 64 11 17 76 15 1 93 68 66 67 87 94 85 22 67 89 81 68 24 93 76 85 82 66 85 72 19 28 88 64 20 86 93 80 94 68 3 13 0 21 17 91 31 67 27 12 23 25 85 81 81 8 17 85 8 14 93 22 69 95 87 68 31 22 114 84 85 92 81 86 83 20 85 83 73 67 86 22 68 80 80 16 94 90 64 68 3 67 2 80 17 19 11 10 12 22 22 92 64 20 85 10 85 25 9 10 77 68 66 23 95 67 17 87 20 89 85 81 84 76 92 77 17 68 92 80 80 17 94 90 90 16 76 90 18 5 66 11 5 71 2 19 21 10 22 22 3 77 90 91 90 68 94 75 65 0 78 83 95 67 24 16 25 80 88 80 87 75 123 12 68 64 5 68 10 110 103 13 4 106 124 4 91 94 1 22 67 66 69 72 79'
key = "Depression is like a plague that hits every one of us at some point in life. Everyone copes with it in their own way, but oftentimes it's hard to understand what exactly you feel and why you feel like that. Feeling depressed for a few minutes and hours is a healthy reaction to a hard situation or event. (flag{C4pt4r3_Th3_H4ck3r!!!})"

附上验证脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
header='61760164100884416913e7540852dbcd5f3fcbcb9344d19ae8'
result='114 84 71 68 85 66 69 93 94 94 16 81 75 20 88 88 93 92 17 82 69 71 89 85 87 77 80 18 16 10 2 16 21 14 90 18 16 66 6 20 92 65 77 20 11 95 92 65 10 94 22 68 68 22 81 69 22 71 94 93 85 24 72 91 93 95 66 25 88 93 69 91 92 82 85 22 21 119 18 7 17 29 90 8 86 70 0 13 19 7 74 19 67 93 16 89 25 8 17 24 95 95 23 66 88 84 95 70 17 95 71 86 24 67 85 72 26 25 83 70 17 23 90 82 68 93 91 70 13 15 6 23 21 15 71 65 16 66 11 3 75 87 20 64 11 17 76 15 1 93 68 66 67 87 94 85 22 67 89 81 68 24 93 76 85 82 66 85 72 19 28 88 64 20 86 93 80 94 68 3 13 0 21 17 91 31 67 27 12 23 25 85 81 81 8 17 85 8 14 93 22 69 95 87 68 31 22 114 84 85 92 81 86 83 20 85 83 73 67 86 22 68 80 80 16 94 90 64 68 3 67 2 80 17 19 11 10 12 22 22 92 64 20 85 10 85 25 9 10 77 68 66 23 95 67 17 87 20 89 85 81 84 76 92 77 17 68 92 80 80 17 94 90 90 16 76 90 18 5 66 11 5 71 2 19 21 10 22 22 3 77 90 91 90 68 94 75 65 0 78 83 95 67 24 16 25 80 88 80 87 75 123 12 68 64 5 68 10 110 103 13 4 106 124 4 91 94 1 22 67 66 69 72 79'
result=result.split()
key=''
n=0
for res in result:
if n<50:
key = key + chr(int(res)^ord(header[n]))
else:
n=0
key = key + chr(int(res)^ord(header[n]))
n=n+1

print key

image.jpg