一些话 因为这道题还没有结束,而且网上也没有能看的WP,所以我就不细说是哪一题了
在这篇文章,你可能会了解到:
一点点追踪线索的方法
分离隐藏在图片中文件
查找在图片中隐藏的信息
编写Python程序来解密移位密码
一点点正则表达式
编写Python程序来穷举SHA256
Hashcat的使用
可以听着朴树的一曲老歌,也可以静静地阅读
题目信息 PWNHUB公开赛 某题MISC
题目介绍 详情 获取FLAG需要耐心(英文)
文件 可于 本站网址/static/post/hard-misc-815/ext/chute.jpg 获取
正文 信息收集 给了一张图
了解文件名 先看看chute是什么意思
查看文件属性 没有特别的东西
打开图片看看 提示查看底部
使用010Editor查看 注意到了两段信息
第一段是password,同时看到了PK字样,说明可能藏了一个zip
还看到了code.py
第二段是一段英文
1 The Answer to the Ultimate Question of Life, the Universe, and Everything
搜了一下,答案是42
1、
2、
从图片分离文件 一般常用的有两个工具:binwalk和foremost
下面是使用方法
记得把chute.jpg放在同一目录下
如果使用binwalk
如果使用foremost
可以看到分离出一个zip文件
此zip需要密码
填上上面发现的password,成功获取code.py
可以看出这是出题人加密flag 的时候用的程序改造而来
1 2 3 4 5 from secret import flagfrom hashlib import sha256assert hex (int .from_bytes(sha256(bytes (flag.lower(), 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' assert hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'cbf28477ce64bf9e1bd268fd77ee32bc4196800e6a2ab29fa43c1cc173b14251'
获取的程序的解读 意思是从secret.py这个文件获取flag这个字符串,这道题的做法绝对不是获取secret.py
Python中,字符串.lower()表示把字符串中的大写字母全部转换成小写字母
下面程序的意思是对flag这个字符串转换成小写,然后求sha256,转换成16进制,
再和’b9d893e313a35。。。373275e15992f8’这个sha256比较,不成立就报错。
可以看出’b9d893e313a35。。。373275e15992f8’就是全小写的flag求出来的sha256
1 assert hex (int .from_bytes(sha256(bytes (flag.lower(), 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8'
同理,下面程序对flag求sha256,转换成16进制,然后和’cbf28477。。。c1cc173b14251’这个sha256比较,不成立就报错
可以看出’cbf28477。。。c1cc173b14251’就是flag求出来的sha256
1 assert hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'cbf28477ce64bf9e1bd268fd77ee32bc4196800e6a2ab29fa43c1cc173b14251'
这个flag当然要靠我们自己去获取,因为sha256不可逆,到这里仿佛没有线索了
关于SHA256 Hash(哈希)算法的一种,属于SHA-2【安全散列算法2(Secure Hash Algorithm 2)】
类似MD5,就算输入两个差异很小的值,得出来得结果差别也很大
SHA256的十六进制结果的长度为64位,加密出来的结果是概要,一般用于确定两个文件是否相同
不像RSA,此种算法的结果不可逆,要破解SHA256目前还只能靠穷举暴力破解
上段文字是自己总结的,有错误欢迎指出
尝试穷举SHA256 因为没有找到其它线索,只好试试穷举
穷举 拿从所有字母为小写字母求出来的SHA256作为目标,排除掉26个大写字母,能节省不少时间
找pwnhub的flag格式 找到格式似乎是pwnhub{flag:xxx}
编写密码生成器 参考此处 关于itertools包的用法
使用itertools包编写密码生成器
程序为python3
1 2 3 4 5 6 7 8 def gen_passwd (min_len, max_len, words ): while True : pwds = its.product(words, repeat=min_len) for pwd in pwds: yield '' .join(pwd) min_len += 1 if min_len > max_len: break
完整程序
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 36 37 import itertools as itsfrom hashlib import sha256words = '0123456789abcdefghijklmnopqrstuvwxyz~!#$%^&*' can_stop = False def gen_passwd (min_len, max_len, words ): while True : pwds = its.product(words, repeat=min_len) for pwd in pwds: yield '' .join(pwd) min_len += 1 if min_len > max_len: break def cmp_sha (password ): global can_stop count_ = 0 while not can_stop: p = next (password) flag = "pwnhub{flag:" + p + "}" count_ += 1 if count_ % 1000000 == 0 : print("At :" , count_, ", len =" , len (p), ", test:" , flag) if hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print(flag) can_stop = True if __name__ == '__main__' : password = gen_passwd(1 , 8 , words) cmp_sha(password)
跑了一晚上跑了在’0123456789abcdefghijklmnopqrstuvwxyz~!#$%^&*’中生成的1位到7位
然后觉得方向可能不太对,于是停掉了
再找线索 这次仔细研究了一下图,因为前面得到的42还没有用上
斜槽,底下,42
感觉这个图有点像保险箱的那个密码旋钮,于是数了一下
上面顺时针旋转了41个。。。似乎有点线索
让我想到了凯撒密码,一种移位密码,于是我尝试对之前获得的zip解压密码password那段字符进行位移
因为这是唯一可疑的一段字符
移位密码 程序 程序如下,依然是python3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from hashlib import sha256while True : st = input ("Please input\n" ) if st[0 :3 ] == "add" : st = st[3 :] len_ = int (st[:2 ]) st = st[2 :] for i in st: ord_ = ord (i) ch = '' if ord_ + len_ > 126 : ord_ = ord_ + len_ - 126 + 32 ch = chr (ord_) else : ch = chr (ord_+len_) print(ch, end="" ) print()
用法 输入【add】【往后移位的数量】【字符】即可
例如运行程序后输入
1 add42\<B7=]2Q{;*5O%KH52e5<(dC517aPua03QgS517aPua03QiS52:QgS2S
表示对<B7=]2Q{;*5O%KH52e5<(dC517aPua03QgS517aPua03QiS52:QgS2S进行往后移位42的操作
得到线索 发现这个确实是线索(有坑,请继续看下去)
得到线索前的尝试 (此部分可跳过,不阅读)
上面的线索得出来之前其实做了好几次尝试,包括如何对ascii进行位移
一开始add41
之后试add42发现有flag字样
程序最终的126和+32都是摸索出来的
一开始是+33和126或者+33 122或者+33 126或+48 126或+48 122
尝试1
尝试2
尝试3、4
感觉这时候已经差不多了
后来找到一张图,试了126和+32才得到上面的线索
1 (flag)\{GeT_yOur_\1_fR0m_[a-zA-Z]{3}_[a-zA-Z]{5}_\d{3}\}
再次穷举 初次看,猜想应该是pwnhub{flag:X} X为11位密码,前8位为大小写字母,后三位为数字
1 (flag)\{GeT_yOur_\1_fR0m_[a-zA-Z]{3}_[a-zA-Z]{5}_\d{3}\}
然后手动验证猜想,这时还以为离得到flag不远了
在上面程序的基础上添加验证功能
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 from hashlib import sha256while True : st = input ("Please input\n" ) FLAG = "pwnhub{flag:" + st + "}" if hex (int .from_bytes(sha256(bytes (FLAG.lower(), 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print("R1 Yes" ) if hex (int .from_bytes(sha256(bytes (FLAG, 'utf8' )).digest(), "big" ))[2 :] == 'cbf28477ce64bf9e1bd268fd77ee32bc4196800e6a2ab29fa43c1cc173b14251' : print("R2 Yes" ) if st[0 :3 ] == "add" : st = st[3 :] len_ = int (st[:2 ]) st = st[2 :] for i in st: ord_ = ord (i) ch = '' if ord_ + len_ > 126 : ord_ = ord_ + len_ - 126 + 32 ch = chr (ord_) else : ch = chr (ord_+len_) print(ch, end="" ) print()
还是没有找到flag
又按猜想写了11位的密码生成器
因为穷举的是小写字母生成的sha256,所以前8位可以直接跑小写字母,后三位再跑数字
1 2 3 4 5 6 7 8 9 10 words0 = 'abcdefghijklmnopqrstuvwxyz' words1 = '0123456789' def gen_passwd (words0, words1 ): pwds = its.product(words0, repeat=8 ) pwds1 = its.product(words1, repeat=3 ) for pwd in pwds: for pwd1 in pwds1: yield '' .join(pwd) + '' .join(pwd1) pwds1 = its.product(words1, repeat=3 )
完整程序
去掉了原来写的hex(int.from_bytes(sha256(bytes(flag.lower(), ‘utf8’)).digest(), 。。。75e15992f8’:中的flag.lower()的.lower()以提高速度(其实应该快不了多少,后来发现我坑了我自己)
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 import itertools as itsfrom hashlib import sha256words0 = 'abcdefghijklmnopqrstuvwxyz' words1 = '0123456789' can_stop = False def gen_passwd (words0, words1 ): pwds = its.product(words0, repeat=8 ) pwds1 = its.product(words1, repeat=3 ) for pwd in pwds: for pwd1 in pwds1: yield '' .join(pwd) + '' .join(pwd1) pwds1 = its.product(words1, repeat=3 ) def cmp_sha (password ): global can_stop count_ = 0 while not can_stop: p = next (password) flag = "pwnhub{flag:" + p + "}" count_ += 1 if count_ % 1000000 == 0 : print("At :" , count_, ", len =" , len (p), ", test:" , flag) if hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print(flag) can_stop = True if __name__ == '__main__' : password = gen_passwd(words0, words1) cmp_sha(password)
跑了很久连aaaa都没跑完。。。于是停了
才发现有点不对劲:这是MISC题呀,咋整密码去了
回看线索 仔细观察
1 (flag)\{GeT_yOur_\1_fR0m_[a-zA-Z]{3}_[a-zA-Z]{5}_\d{3}\}
发现是正则表达式
{5}表示匹配5次
[a-zA-Z]{3}代表从大小写字母选三个,[a-zA-Z]{5}同理
\d表示数字
正则测试 使用在线正则测试
正则中的\1不是很懂,先删掉
后来仔细看了
测试了一下,差不多理解了
所以flag大概长这样
改程序 修改手动验证的程序 注释掉了FLAG = “pwnhub{flag:” + st + “}”
改成FLAG = “flag{GeT_yOur_flag_fR0m_” + st + “}”
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 from hashlib import sha256while True : st = input ("Please input\n" ) FLAG = "flag{GeT_yOur_flag_fR0m_" + st + "}" if st[0 :3 ] == "raw" : st = st[3 :] FLAG = st print(FLAG) if hex (int .from_bytes(sha256(bytes (FLAG.lower(), 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print("R1 Yes" ) else : print("SHA256:" , hex (int .from_bytes(sha256(bytes (FLAG.lower(), 'utf8' )).digest(), "big" ))[2 :]) if hex (int .from_bytes(sha256(bytes (FLAG, 'utf8' )).digest(), "big" ))[2 :] == 'cbf28477ce64bf9e1bd268fd77ee32bc4196800e6a2ab29fa43c1cc173b14251' : print("R2 Yes" ) if st[0 :3 ] == "add" : st = st[3 :] len_ = int (st[:2 ]) st = st[2 :] for i in st: ord_ = ord (i) ch = '' if ord_ + len_ > 126 : ord_ = ord_ + len_ - 126 + 32 ch = chr (ord_) else : ch = chr (ord_+len_) print(ch, end="" ) print()
顺便加了原始输入功能和算sha256的功能
举例子
如果是平常,输入
输出
1 2 flag{GeT_yOur_flag_fR0m_aaa_aaaaa_001} SHA256: e89df378decf2e0d1e808d378511641107b1826180134de16d4507809299069b
原始输入功能,则输入
1 rawflag{GeT_yOur_flag_fR0m_aaa_aaaaa_001}
输出
1 2 flag{GeT_yOur_flag_fR0m_aaa_aaaaa_001} SHA256: e89df378decf2e0d1e808d378511641107b1826180134de16d4507809299069b
修改穷举的程序 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 36 37 38 39 40 import itertools as itsfrom hashlib import sha256words0 = 'abcdefghijklmnopqrstuvwxyz' words1 = 'abcdefghijklmnopqrstuvwxyz' words2 = '0123456789' can_stop = False def gen_passwd (words0, words1, words2 ): pwds = its.product(words0, repeat=3 ) pwds1 = its.product(words1, repeat=5 ) pwds2 = its.product(words2, repeat=3 ) for pwd in pwds: for pwd1 in pwds1: for pwd2 in pwds2: yield ["" .join(pwd), "" .join(pwd1), "" .join(pwd2)] pwds2 = its.product(words2, repeat=3 ) pwds1 = its.product(words1, repeat=5 ) def cmp_sha (password ): global can_stop count_ = 0 while not can_stop: p = next (password) flag0 = "GeT_yOur_flag_fR0m_{}_{}_{}" .format (p[0 ], p[1 ], p[2 ]) + "}" flag = "flag{" + flag0 count_ += 1 if count_ % 1000000 == 0 : print("At :" , count_, ", len =" , len (p[0 ])+len (p[1 ])+len (p[2 ]), ", test:" , flag) if hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print(flag) can_stop = True if __name__ == '__main__' : password = gen_passwd(words0, words1, words2) cmp_sha(password)
继续跑
跑了有段时间了,我感觉方向不太对,就停掉了
再次穷举2 这次是靠猜 了
根据英语造句,“from”后面用“the”的可能性比较大
于是把前三位换成the,这样穷举只要8位长度
又猜了下数字为042,这样穷举只要5位长度,很快
还是没猜对
修改了下程序
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 import itertools as itsfrom hashlib import sha256words0 = '0123456789' words1 = 'abcdefghijklmnopqrstuvwxyz' can_stop = False def gen_passwd (words0, words1 ): pwds = its.product(words0, repeat=3 ) pwds1 = its.product(words1, repeat=5 ) for pwd1 in pwds1: for pwd in pwds: yield ["" .join(pwd1), "" .join(pwd)] pwds = its.product(words0, repeat=3 ) def cmp_sha (password ): global can_stop count_ = 0 while not can_stop: p = next (password) flag0 = "GeT_yOur_flag_fR0m_the_{}_{}" .format (p[0 ],p[1 ]) + "}" flag = "flag{" + flag0 count_ += 1 if count_ % 1000000 == 0 : print("At :" , count_, ", len =" , len (p[0 ])+len (p[1 ]), ", test:" , flag) if hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print(flag) can_stop = True if __name__ == '__main__' : password = gen_passwd(words0, words1) cmp_sha(password)
穷举完了,却没出flag,说明前三位不是“the”
猜chute
猜below
找到错误 直觉让我先测试一下我写的程序
于是我生成了一个sha256
填到11位长度的穷举程序里
结果它在我眼皮底下直接过去了
好吧,原来是少了.lower()
之前flag是pwnhub{flag:}的时候没有问题,所因为pwnhub{flag:}字母都为小写
后来换成flag{GeT_yOur_flag_fR0m}里面字母有大写,又少了.lower(),生成的sha256自然对不上
补上就好
再次运行,没问题
又重跑了一次“the”,还是没出flag
再猜chute
修正错误 猜过了很多单词
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 36 import itertools as itsfrom hashlib import sha256words0 = '0123456789' words1 = 'abcdefghijklmnopqrstuvwxyz' can_stop = False def gen_passwd (words0, words1 ): pwds = its.product(words0, repeat=3 ) pwds1 = its.product(words1, repeat=3 ) for pwd1 in pwds1: for pwd in pwds: yield ["" .join(pwd1), "" .join(pwd)] pwds = its.product(words0, repeat=3 ) def cmp_sha (password ): global can_stop count_ = 0 while not can_stop: p = next (password) flag0 = "GeT_yOur_flag_fR0m_{}_guide_{}" .format (p[0 ],p[1 ]) + "}" flag = "flag{" + flag0 count_ += 1 if count_ % 1000000 == 0 : print("At :" , count_, ", len =" , len (p[0 ])+len (p[1 ]), ", test:" , flag) if hex (int .from_bytes(sha256(bytes (flag.lower(), 'utf8' )).digest(), "big" ))[2 :] == 'b9d893e313a35b541bf70bd2ca70e69d7579b79df3d6da7d4b373275e15992f8' : print(flag) can_stop = True if __name__ == '__main__' : password = gen_passwd(words0, words1) cmp_sha(password)
尝试hashcat 因为各种尝试没有结果,我打算直接跑11位
因为python太慢(可能是我菜),听说hashcat能用GPU 跑HASH,快得很
hashcat确实快,python跑6位要差不多3分钟,而hashcat不到10秒 就搞定了(6秒左右)
GPU有点热
使用方法 掩码模式 -a 3是掩码模式
hash.txt是存放sha256的文件
-m 1400代表sha256
-o 1.txt表示如果跑出来了就把结果放在1.txt
?d和?l都是掩码,?l代表一位小写字母,?d代表一位数字
1 .\hashcat.exe hash.txt -a 3 -m 1400 -o 1.txt flag{get_your_flag_fr0m_?l?l?l_?l?l?l?l?l_?d?d?d}
字典+掩码模式 -a 6是字典+掩码模式
1 .\hashcat.exe hash.txt -a 6 -m 1400 -o 1.txt dict.txt ?d?d?d
跑11位 写了个bat
因为跑出来就会有1.txt,所以直接cmd的type命令读txt就行
1 2 3 .\hashcat.exe hash.txt -a 3 -m 1400 -o 1 .txt flag{get_your_flag_fr0m_?l?l?l_?l?l?l?l?l_?d?d?d} type 1 .txtpause
时间有点恐怖,144天,于是放弃了
猜前三位为sha,结果不是
1 2 3 .\hashcat.exe hash.txt -a 3 -m 1400 -o 1 .txt flag{get_your_flag_fr0m_sha_?l?l?l?l?l_?d?d?d} type 1 .txtpause
跑5位单词 想不到其它方法了。。。于是找了一堆长度为5位的单词
可于 本站网址/static/post/hard-misc-815/ext/words.txt 获取
写了个转换小写、读取单词、去除空行的处理程序
(python3程序)
1 2 3 4 5 6 with open ("words.txt" , 'r' , encoding = 'utf-8' ) as fout: with open ("words1.txt" , 'w' , encoding = 'utf-8' ) as fout1: for line in fout.readlines(): line = str (line) if line[1 :2 ] != "" : fout1.write(line[0 :5 ].lower()+"\n" )
处理完成
可于 本站网址/static/post/hard-misc-815/ext/words1.txt 获取
又写了个按行读取单词,配合hashcat的批处理程序(因为懒得写hashcat的规则文件 ,而且hashcat又不支持掩码+字典+掩码模式 )
1 2 3 4 5 6 7 8 9 @echo off set file=./words1.txt for /f %%a in (%file% ) do ( .\hashcat.exe hash.txt -a 3 -m 1400 -o 1 .txt flag{get_your_flag_fr0m_?l?l?l_%%a _?d?d?d} ) type 1 .txtpause
算了下时间(872*7/3600),大概2小时跑完
在跑了
成功获取小写flag 竟然是troop,而且前三位竟然是jpl
因为太艰苦了,有点难以置信,所以手动验证了一下
获取大写flag jpl有2^3=8种组合,直接写了
1 words0 = ["jpl","jpL","jPl","Jpl","jPL","JPl","JpL","JPL"]
改改程序
sha256改成大小写混合的那一串,然后只需要遍历’tropTROP’,数字直接填509
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 36 import itertools as itsfrom hashlib import sha256words0 = ["jpl" ,"jpL" ,"jPl" ,"Jpl" ,"jPL" ,"JPl" ,"JpL" ,"JPL" ] words1 = 'tropTROP' can_stop = False def gen_passwd (words0, words1 ): pwds = its.product(words1, repeat=5 ) for pwd1 in words0: for pwd in pwds: yield ["" .join(pwd1), "" .join(pwd)] pwds = its.product(words1, repeat=5 ) def cmp_sha (password ): global can_stop count_ = 0 while not can_stop: p = next (password) flag0 = "GeT_yOur_flag_fR0m_{}_{}_509" .format (p[0 ],p[1 ]) + "}" flag = "flag{" + flag0 count_ += 1 if count_ % 1000000 == 0 : print("At :" , count_, ", len =" , len (p[0 ])+len (p[1 ]), ", test:" , flag) if hex (int .from_bytes(sha256(bytes (flag, 'utf8' )).digest(), "big" ))[2 :] == 'cbf28477ce64bf9e1bd268fd77ee32bc4196800e6a2ab29fa43c1cc173b14251' : print(flag) can_stop = True if __name__ == '__main__' : password = gen_passwd(words0, words1) cmp_sha(password)
很快就出来了
验证flag
最后 太难了,写wp都写了一晚上
程序共写了9个,批处理写了2个
EOF