一道姿势较多的隐写题

发布 : 2019-10-27

​ 在隐写的解题中,对不同类型隐写文件的解题,一般都有一定的步骤,下面是最近巅峰极客的一道隐写题目的比较详细的解题步骤,其中一些隐写方式也是遇到后再百度了才知道的,其实也都是一些基本的知识串到了一起,只要能耐心把隐写的基础都看完,看到这些文件一般都会知道应该怎么解题,所以基础还是比较重要的。

题目

解题步骤

原题解压后就是这张图片,没有其他提示,因为是杂项,所以首先想到的就是图片隐写,先使用一些基本命令查看一下文件的情况

1
2
strings leaf.png			
#打印文件中可打印的字符,经常用来发现文件中的一些提示信息或是一些特殊的编码信息,常常用来发现题目的突破口。

1
2
binwalk leaf.png
# 本是一个固件的分析工具,比赛中常用来发现多个文件粘合在一起的情况。根据文件头去识别一个文件中夹杂的其他文件

可以看到两个命令得到的信息都差不多,其中有个flag.xml非常明显,所以那里面可能就是题解或者是提示。另外第二个命令上看到里面有一个存在leaf.pyc的压缩文件,所以下面使用foremost命令分解一下

1
foremost leaf.png	#自动化切割文件

切割后得到一个png文件和一个zip文件,zip文件存在解压密码,一般这种情况可以考虑是不是zip伪加密,不是的话再考虑破解密码或者找密码,但是之前看到了一个flag.xml所以还是先处理那个,foremost并没有把那些有xml的压缩包分离出来,看那些xml文件感觉是某种文件里的东西,百度了一下

image-20191027131706268

那看来是里面有个docx文件,这里使用手动分离,看起来比较形象,自己创建一个docx文件,使用winhex这个工具打开

看到文件头是下面这个

image-20191027134323788

到leaf.png里面去找一下,找到后从那个位置拖到最后,然后复制到新文件,命名为123.docx

image-20191027134750355

生成文件后将后缀改成zip,解压后看到了flag.xml文件内容image-20191027141016563

里面什么字都没有,但是细心点可以发现每个空是由tab键或者四个空格构成的,所以将tab转为0,空格的转为1,下面是脚本,当然也可以手动转换,但是有点慢

1
2
3
4
5
6
7
file=open('flag.xml','rb')
data = file.read()
for i in data:
if i==32:
print(0,end="")
elif i==9:
print(1,end="")

image-20191027141341563

再使用工具将二进制转换为字符串得到前一段的flag

image-20191027141452032

然后要获得后面一段flag,想到了先打开这个文档看看

打开是一堆base64加密,解密后就是一篇普通文章,但是注意到最后发现了隐藏的字,就是下面标出的红字,说是在这个文档里还有东西。

学习了base64之后就可以很快推断出这个base64隐写,隐写真是无处不在,这里只写一下base64隐写的原理,不介绍base64了。

table3

解码过程将上面的示例从下向上看即可,即先丢弃编码后面的‘=’,然后将每个base64字符对应索引转为6bit的二 进制数,再8个一组转为ASCII码字符完成解码,最后若剩下不足8位的,则全部丢弃。 所以上图紫色方框中的bit位在解码时会被丢弃,换句话说,紫色方框中的bit值不会对解码结果产生影响。一个简 单直观的例子就是QUJDRA==和QUJDRC==解码后都是ABCD。由此我们便可以将隐藏信息插入这些bit位中实现隐写。

用网上搜到的解密脚本

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
"""
base64隐写解密
"""
def get_base64_diff_value(s1, s2):
base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
res = 0
for i in xrange(len(s2)):
if s1[i] != s2[i]:
return abs(base64chars.index(s1[i]) - base64chars.index(s2[i]))
return res


def solve_stego():
with open('1.txt', 'rb') as f:
file_lines = f.readlines()
bin_str = ''
for line in file_lines:
steg_line = line.replace('\n', '')
norm_line = line.replace('\n', '').decode('base64').encode('base64').replace('\n', '')
diff = get_base64_diff_value(steg_line, norm_line)
print diff
pads_num = steg_line.count('=')
if diff:
bin_str += bin(diff)[2:].zfill(pads_num * 2)
else:
bin_str += '0' * pads_num * 2
print goflag(bin_str)


def goflag(bin_str):
res_str = ''
for i in xrange(0, len(bin_str), 8):
res_str += chr(int(bin_str[i:i + 8], 2))
return res_str


if __name__ == '__main__':
solve_stego()

下面是运行结果,最后解密的内容为 I4mtHek3y@ 用这个可以解压那个压缩包

331-53-35

压缩包中是一个pyc文件

这是python运行时编译的中间文件,然后pyc文件也是有隐写的,就叫做pyc文件隐写,这个在CTF-wiki中有写,只需要使用stegosaurus这个工具就可以得到隐写的内容。

image-20191027140809411

最后得到flag{2806105f-ec43-57f3-8cb4-1add2793f508}

这道题中使用的隐写方式比较多,比如说通过图片隐写将压缩包和docx文件藏到图片中,又在docx文件中使用白色字来隐藏提示,还有其中的base64隐写,以及在docx文件中的flag.xml的tab和空格形成的二进制转换字符串的隐写,最后还有一个pyc文件隐写,可以说这是一道很好的考察基础的题目,用到的姿势非常多。

Reference

https://cltheorem.github.io/2018/10/base64%E9%9A%90%E5%86%99/

https://wiki.x10sec.org/misc/other/pyc/

本文作者 : W4rnIn9
原文链接 : http://joner11234.github.io/article/e9839e6f.html
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!