|
原文链接:浅谈LSB隐写解题与出题
前言:LSB隐写在CTF中属于出现得比较多的类型。这篇文章对LSB隐写的原理,解题方法,出题脚本,以及LSB隐写特性进行研究。
LSB隐写原理LSB即为最低有效位(Least Significant Bit,lsb),我们知道,图片中的图像像素一般是由RGB三原色(红绿蓝)组成,每一种颜色占用8位,取值范围为0x00~0xFF,即有256种颜色,一共包含了256的3次方的颜色,即16777216种颜色。而人类的眼睛可以区分约1000万种不同的颜色,这就意味着人类的眼睛无法区分余下的颜色大约有6777216种。
 
LSB隐写就是修改RGB颜色分量的最低二进制位也就是最低有效位(LSB),而人类的眼睛不会注意到这前后的变化,每个像数可以携带3比特的信息。
 
上图我们可以看到,十进制的235表示的是绿色,我们修改了在二进制中的最低位,但是颜色看起来依旧没有变化。我们就可以修改最低位中的信息,实现信息的隐写。
StegSolve工具这里推荐使用一款功能很强大的lsb隐写分析工具---StegSolve图片通道查看器。
下载地址:
http://www.caesum.com/handbook/Stegsolve.jar
使用stegsolve打开图片,按右方向键查看各通道显示的图像。
图像处理主要是analyse这个模块,主要有这四个功能:
File Format: 文件格式,查看图片的具体信息
Data Extract: 数据抽取,提取图片中隐藏数据
Frame Browser: 帧浏览器,主要是对GIF之类的动图进行分解,动图变成一张张图片
Image Combiner: 拼图,图片拼接
对于LSB隐写的图片,我们用StegSolve打开模块,由于是RGB三原色的最低位隐写,所以在Data Extract模,提取Red,Green,和Blue的0通道信息,在这三个颜色的0通道上打勾,并按下Preview键,当隐写的内容为文本文件时如下所示:
 
当隐写的内容为图片时如下所示:
 
由PNG文件头可以看出隐写内容为PNG文件,按save Bin键保存为PNG文件
LSB隐写脚本我在github上参考了这位大佬的脚本,
https://github.com/librauee/Steganalysis/tree/master/LSB
但感觉用起来不是很方便,稍微修改了一下,如下所示:
- from PIL import Image
- import sys
- def toasc(strr):
- return int(strr, 2)
-
- #str1为所要提取的信息的长度(根据需要修改),str2为加密载体图片的路径,str3为提取文件的保存路径
- def decode(str1,str2,str3):
- b=""
- im = Image.open(str2)
- lenth = int(str1)*8
- width,height = im.size[0],im.size[1]
- count = 0
- for h in range(height):
- for w in range(width):
- #获得(w,h)点像素的值
- pixel = im.getpixel((w, h))
- #此处余3,依次从R、G、B三个颜色通道获得最低位的隐藏信息
- if count%3==0:
- count+=1
- b=b+str((mod(int(pixel[0]),2)))
- if count ==lenth:
- break
- if count%3==1:
- count+=1
- b=b+str((mod(int(pixel[1]),2)))
- if count ==lenth:
- break
- if count%3==2:
- count+=1
- b=b+str((mod(int(pixel[2]),2)))
- if count ==lenth:
- break
- if count == lenth:
- break
-
- with open(str3,"w",encoding='utf-8') as f:
- for i in range(0,len(b),8):
- #以每8位为一组二进制,转换为十进制
- stra = toasc(b[i:i+8])
- #将转换后的十进制数视为ascii码,再转换为字符串写入到文件中
- #print((stra))
- f.write(chr(stra))
- print("sussess")
- def plus(string):
- #Python zfill() 方法返回指定长度的字符串,原字符串右对齐,前面填充0。
- return string.zfill(8)
-
- def get_key(strr):
- #获取要隐藏的文件内容
- with open(strr,"rb") as f:
- s = f.read()
- string=""
- for i in range(len(s)):
- #逐个字节将要隐藏的文件内容转换为二进制,并拼接起来
- #1.先用ord()函数将s的内容逐个转换为ascii码
- #2.使用bin()函数将十进制的ascii码转换为二进制
- #3.由于bin()函数转换二进制后,二进制字符串的前面会有"0b"来表示这个字符串是二进制形式,所以用replace()替换为空
- #4.又由于ascii码转换二进制后是七位,而正常情况下每个字符由8位二进制组成,所以使用自定义函数plus将其填充为8位
- string=string+""+plus(bin(s[i]).replace('0b',''))
- #print(string)
- return string
- def mod(x,y):
- return x%y
- #str1为载体图片路径,str2为隐写文件,str3为加密图片保存的路径
- def encode(str1,str2,str3):
- im = Image.open(str1)
- #获取图片的宽和高
- width,height= im.size[0],im.size[1]
- print("width:"+str(width))
- print("height:"+str(height))
- count = 0
- #获取需要隐藏的信息
- key = get_key(str2)
- keylen = len(key)
- for h in range(height):
- for w in range(width):
- pixel = im.getpixel((w,h))
- a=pixel[0]
- b=pixel[1]
- c=pixel[2]
- if count == keylen:
- break
- #下面的操作是将信息隐藏进去
- #分别将每个像素点的RGB值余2,这样可以去掉最低位的值
- #再从需要隐藏的信息中取出一位,转换为整型
- #两值相加,就把信息隐藏起来了
- a= a-mod(a,2)+int(key[count])
- count+=1
- if count == keylen:
- im.putpixel((w,h),(a,b,c))
- break
- b =b-mod(b,2)+int(key[count])
- count+=1
- if count == keylen:
- im.putpixel((w,h),(a,b,c))
- break
- c= c-mod(c,2)+int(key[count])
- count+=1
- if count == keylen:
- im.putpixel((w,h),(a,b,c))
- break
- if count % 3 == 0:
- im.putpixel((w,h),(a,b,c))
- im.save(str3)
- if __name__ == '__main__':
- if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) < 2:
- print ('Usage: python test.py <cmd> [arg...] [opts...]')
- print (' cmds:')
- print (' encode image + flag -> image(encoded)')
- print (' decode length + image(encoded) -> flag')
- sys.exit(1)
- cmd = sys.argv[1]
- if cmd != 'encode' and cmd != 'decode':
- print('wrong input')
- sys.exit(1)
- str1 = sys.argv[2]
- str2 = sys.argv[3]
- str3 = sys.argv[4]
- if cmd != 'encode' and cmd != 'decode':
- print ('Wrong cmd %s' % cmd)
- sys.exit(1)
- elif cmd=='encode':
- encode(str1,str2,str3)
- elif cmd=='decode':
- decode(str1,str2,str3)
复制代码
LSB隐写出题这里以合天网安实验室的图标为例子,对其进行lsb隐写,以文件和图片两种方式。
 
如图所示
 
得到两张lsb隐写的图片
 
我们可以用脚本或者使用StegSolve工具获取flag,工具如前文所示,这里演示用脚本获取flag。这里我们先查看flag.txt的大小,当然在不知道大小的情况下也是可以的,要得到完整的flag可以把文件大小设置大一些。
 
测试结果如下:
 
如果把文件大小设置太小得到的flag不完整,设置太大会产生一些额外的字符,为得到完整的flag,可以把大小设置稍大一些。
不同于文本文件的大小可以任意调整,不影响文件的阅读,图片调整大小对图片的影响较大,而且在一般情况下,我们是不知道被隐写的文件的大小,所以并不推荐用脚本来获取flag,推荐使用工具来解题,脚本可以使用在出题中。
LSB隐写特性加密性由于LSB隐写在只知道加密图片的情况下就可以知道隐写的内容,可见加密性是比较差的。
鲁棒性我们通过测试来检测LSB隐写的鲁棒性,首先我们对合天_txt.png图片进行以下操作。
 
因为文本隐写隐藏在图片最开始的地方,所以我们对图片最开始的部分进行攻击,结果如下:
 
可以看到内容完全被破坏。当然如果不在图片最开始的地方攻击图片,则对内容恢复完全没有问题。
 
再测试一下图片隐写的鲁棒性,对合天网安实验室_jpg.png图片进行处理,结果如图所示:
 
可以看出,图片隐写的鲁棒性更差,这是因为图片的大小比文本大得多,对图片的完整性要求就更高。总的来说,lsb隐写的鲁棒性是很差的。
参考文章LSB图片隐写 https://segmentfault.com/a/1190000016223897隐写脚本 https://github.com/librauee/Steganalysis/tree/master/LSB
实验推荐大家也可以多在练习中,积累,复制下方链接实操起来吧
https://www.hetianlab.com/expc.d ... =weixin-wemedia#stu
|
|