LSB隐写

LSB隐写

前言

写这篇文章真的太苦了 +_+,我只是个敲代码的,没打算去学PS,可是得接触各种关于颜色的专业知识,RGBRGBA通道灰度,我还得假装自己学会了


介绍

LSB是最低有效位(Least Significant Bit)的缩写

任何一个像素点的颜色可由三原色RGB确定,每个颜色有8bit,因而RGB最大取值为 (255, 255, 255)——RGB一共包含$256^3=16777216‬$(1600万)种颜色,而人眼可以区分约1000万种不同的颜色,这意味着剩余约600万种颜色人眼无法识别

可以随便找个在线RGB取色网站,测试一下 #00ffff 和修改了红色通道最末位的 #01ffff,对比一下颜色,看是否能发觉有不同

LSB隐写就是将信息藏在各个像素点的某个颜色的最后一位上,因为是最后一位,对颜色的更改是人眼无法识别出来的,起到了隐藏的效果

如果修改的不是最后一位,严格来说不是LSB隐写,但这种情况也经常出现

倘若在一张图片中,我们想隐藏 A 这个信息,那么先将 A 的十六进制Unicode码 0x61 转换为二进制 01100001

我们在三原色中随机选择一种颜色——比如说,因为要隐藏的 A 占8个位,所以在这张图片中任意选择连续的8个像素点,将这8个像素点代表红色的8bit的最后一位,依次修改为 01100001

图源自wooyun,链接在后面
对于这种隐写,想办法将红色通道的最后一位单独提取出来,就能得到隐藏的 `A`

当然单单一个 A 有可能被当成乱码忽略,但有时候将通道的最后一位提取出来,显示成文本会出现 flag{xxx};甚至最后一位的数据其实是一张图片的数据,图片查看器打开就能得到图片


无损压缩

由于LSB隐写是在最低位隐藏数据,也就是在比较无关紧要的地方隐藏,因此只有在无损压缩(png无压缩(bmp图片上实现

这部分涉及图片文件的格式,以后应该会专门写一篇针对CTF图片隐写的图片格式文章

jpg 属于有损压缩格式,我们修改的信息可能会在压缩的过程中被破坏;而 png 虽然也有压缩,但却是无损压缩,我们修改的信息不会丢失

bmp 图片把所有的像素都按原样储存,没有进行压缩,因此一般会特别的大


StegSolve

当遇到LSB隐写的图片时,通常借助的是工具StegSolve,它的下载地址是http://www.caesum.com/handbook/Stegsolve.jar,运行它需要Java环境

StegSolve是Steganography(隐写术) Solver的缩写

在StegSolve中随便打开一张图片,左上角首先显示的是Normal Image,它表示正常地查看图片;点击下方的<>,能够切换到不同模式:

  • Colour Inversion (Xor)

    颜色反转,将RGB所表示的二进制 0 1互换(可能是8位二进制数据与 11111111 进行了异或)

    测试

    (255, 255, 0) 的纯黄色图片保存为 png,放入StegSolve,切换到 Xor,显示的是纯蓝色 (0, 0, 255)

    (11111111, 11111111, 00000000)(00000000, 00000000, 11111111)

    在CTF解题过程中,某一步可能得到一张二维码,但二维码的黑白是相反的,要想扫码得到信息,就需要进行颜色反转(二维码通常由黑白组成,反转对立)

    反转前,无法识别   反转后,可以识别
    当然,有时候Ctrl+a选中图片,就可以实现颜色反转
  • 4通道

    • Alpha plane

      Alpha通道是除RGB三原色通道外的第一个通道,与RGB一同组成RGBA,它指一张图片的透明和半透明度

      Alpha通道是为保存选择区域而专门设计的通道,RGB代表的所有颜色信息共需要24位存储,在此基础上,Alpha通道也使用8位二进制数,表示256级灰度(透明度)

      RGBA转RGB的公式是:将Alpha映射到 [0, 1] 的区域上,然后执行

      1
      2
      3
      r = r1 * Alpha
      g = g1 * Alpha
      b = b1 * Alpha
    • Red plane:红色通道

    • Green plane:绿色通道

    • Blue plane:蓝色通道

    4个通道,每个通道各需要8bit,StegSolve将 4 × 8 = 32 个bit位置的数据都提取出来,单独形成图片显示出来。这恰好对应了上面的LSB隐写

    正如上面的 A 隐藏在红色通道的最低位上,StegSolve在Red plane 0上就将它显示出来了

    因此,有些CTF题目修改了整张图片的某通道的位,通过StegSolve查看各通道就能发现

    一道图片隐写的题目,尝试找到flag:

    链接:https://pan.baidu.com/s/1rQsp7FbLy_ZIaljPoRNMqw
    提取码:6cr4

  • Full red

    将Alpha通道、绿色通道、蓝色通道关闭,本质是将这三个通道的值全部修改为 0

  • Full greenFull blueFull alpha同理

  • Random colour map 1~3

    完全随机打乱整张图片4个通道8bits上的数值

    实时打乱,你来回在 12 之间切换,会发现每次得到的图片都是不一样的

  • Gray bits

    灰度图像比特平面

    检查每个像素点的RGB 3个点的值是否相等,如果相等,就修改为白色 (255, 255, 255);否则修改为黑色 (0, 0, 0)

以上就是StegSolve提供的查看图片的不同模式,强烈建议有Matlab语言基础的同学前往https://www.tr0y.wang/2017/06/07/CtfMiscStega/index.html,阅读别人用Matlab对StegSolve部分功能的实现


StegSolve除了提供下方的<>切换不同查看模式,更主要的功能汇集在左上角的Analyse

File选项卡提供的OpenSave AsExit以及Help选项卡提供的About不必多说,下面主要来分析一下Analyse选项卡

  • File Format —— 文件格式

    查看图片的具体信息,有时候flag会隐藏在这里面

    它与直接点击图片右键"属性",查看"详细信息"相辅相成,两者提供的信息有重叠、也有互不相同

  • Stereogram Solver —— 立体试图

    图片与图片自身进行异或运算,因此初始的 offet: 0 时的图像永远是黑屏(对于任意 a,都有a ^ a = 0

    可以控制offset(偏移量)对其中一张进行移动,进行的仍然是异或运算;offset的取值范围是 [0, 图片的宽度-1]

    用一张图片试试就知道怎么回事了

  • Frame Browser —— 帧浏览器

    该功能只针对 gif 动图

    有时候CTF会将flag隐藏在 gif 动图中,会在 gif 中急速闪过,这时就需要把动图一帧一帧地放;有时候又会把 gif 放映得非常慢,需要手动查看下一帧

    Frame Browser分析出所有的帧,让你能够逐帧查看

    攻防世界中,新手练习区Misc的题目的第4道give_you_flag适合练手

    gif 图可前往攻防世界获得,或者下载:

    链接:https://pan.baidu.com/s/1Xa5KZk8WiA_3bTkoG3MEXA
    提取码:v4b2

    答案

    gif 图放入StegSolve中,打开Frame Browser,在第50帧发现一个残缺的二维码:

    二维码缺少了三个角的定位符▣,会PS的同学可以把正常的二维码的定位符扣下来,叠图上去,就能够得到完整的二维码了(不会PS的自己想办法吧)

    二维码也是CTF中的一类题目,以后会写一篇文章专门介绍

    说个题外话

    现在打CTF的要求越来越高了,十八般武艺样样精通,光怎么补全这张二维码就浪费了我不少时间

    鄙人不会PS,那怎么办,右键点击图片 ->打开方式 ->画图,用Windows自带的画图,歪歪扭扭地把二维码补全了:

    我把这张图扔到各大在线解码网站上,统统识别失败......然后用自己的手机扫了扫,竟然识别成功,出flag了......

  • Image Combiner —— 图像合并

    使用StegSolve打开 1.png,再在Image Combiner中打开 2.png,能够查看两张图片的数据进行多种运算呈现出来的结果

    Image Combiner支持许多运算:XOR、OR、AND、ADD、SUB、MUL、Lightest、Darkest、Horizontal Interlace、Vertical Interlace

    CTF中,flag信息有可能隐藏在两张图片的合并中,这被称之为双图问题

    练习一:格式为ctf{xxx}

    链接:https://pan.baidu.com/s/1Sa0r8REMtBvxjVGmweHsGQ
    提取码:s70y

    练习二:格式为flag{xxx}

    链接:https://pan.baidu.com/s/115fS1pswXRNyJNXDgYfn3A
    提取码:s7uu

    解答一

    下载 男神.zip,解压得到两张图片 first.pngsecond.png,两张图片看上去一模一样,明显的双图问题

    first.png 放入StegSolve中,在Image Combiner中打开 second.png,发现在 SUB (R,G,B separate) 的图像呈现二维码的形状:

    然而它并不能直接被扫码

    将上图另存为 colorfulQR.bmp,再在StegSolve中打开,左右查看,发现这同时也是一道LSB隐写题,在RGB三色通道的最低位,分别存储了一张二维码

    将Red plane 0、Green plane 0、Blue plane 0对应的图像分别导出,得到:

    观察发现,这三张二维码都是反色的,需要进行颜色反转处理

    再次将三张图片导入StegSolve,进行Colour Inversion处理;把三张处理后的二维码保存,扔到在线二维码解码中,会分别得到 DES6XaMMbM7 和 一长串被加密的字符串

    按照提示是DES加密,密钥为 6XaMMbM7,随便找个在线的DES解密网站,解码字符串得到flag

    解答二

    用StegSolve打开 flag_enc.png,发现其它都是乱七八糟的、唯独Red plane 0为全黑,猜测是LSB隐写

    分别将Alpha plane 0、Red plane 0、Green plane 0、Blue plane 0另存为 a.pngr.pngg.pngb.png

    发现用StegSolve打开 a.png,Image Combiner打开 g.png,两者进行的异或运算能得到隐藏的flag

    别问我为什么是 a.pngg.png 的合成,出题人就是将flag隐藏在这两张图片中

    往往做这种题目也是挺无奈的,得凭借你做CTF题的经验直觉,不断地去尝试。兴许下次出题人把flag藏在 b.pngr.png 中,你除了一个个尝试,没有其它办法

  • Data Extract —— 数据提取

    在StegSolve的<>切换界面中,StegSolve会自动读取RGBA的各个通道,将各个通道的数据结合打开文件的类型(猜测是加上对应文件的一些格式数据,如 jpg 就添加文件头标识 FF D8、文件尾标识 FF D9),重新生成一张图片,显示在上面

    但有时候,flag并不以图片的形式存在,它有可能就以数据的形式藏匿在某个通道的数据中,这时就要通过Data Extract单独浏览数据了

    Data Extract 主界面
    + **Bit Planes** —— 位通道

    这里罗列了RGBA四条通道的各8位,如果点击Blue 0,StegSolve将会提取整张图片各个像素点中,代表蓝色深度的8位二进制的最后一位,提取出来的 010101... 可以通过Preview查看

    注意这里强调提取出来的是 010101...二进制数据,因为Blue的取值有$2^8$种,相当于一个8位二进制数值。假设Blue通道各位上的数据是 01010101,初始时8个▢都未勾上,因此读取不到数据;如果勾选了后五个▢,则读取了数据 10101

    • Preview—— 预览

      正如上面举例所示,如果点击的是Blue 0,StegSolve自动提取出来的 10101,由于二进制数需要转十六进制数,因此需要凑齐8位;对于提取出来的数据不满8位,将会在后面自动填充 0

      因此这时候的数据变成了 10101000,它会显示在窗口的左端

    我们以某次的数据截图来分析:

窗口的数据由两部分组成:左侧是提取出来的数据转十六进制的显示,右侧是这些十六进制对应的Unicode字符

比如第二行第一个的 C,它对应的Unicode码是 67,十进制数 67 的十六进制恰好就是 43

窗口右侧只显示ASCII字符,非ASCII字符都会被 . 代替

Bit Planes下方有个Preview Settings,有个选项框Include Hex Dump In Preview,翻译过来就是在窗口中显示对应的十六进制数值;默认勾选

  • Bit Plane Order —— 位通道读取顺序

    首先确定,Alpha通道的数据永远是优先访问的,因此这里只有6种选择(由 RGB 三者排列组合)

    默认采用RGB,这里决定对通道数据的提取顺序

    我们假设一张图只由一个像素点组成,其RGBA的情况为 (00000000, 00000000, 00000000, 11111111)

    在这种情况下,我们罗列几个例子来更好地理解StegSolve对各通道数据的提取

    1. 由于Blue通道的值为 (11111111),这里提取出 1,又因为假设没有其它像素点了,所以StegSolve将提取出来的数据补全为8位,即 10000000;二进制 100000000 对应十六进制 80,因此在窗口的左侧会显示 80

    2. 按照默认的RGB顺序,该像素点提取出 011,补全为 01100000,对应十六进制数 60

      如果是BRG顺序,就应该是 110,补全为 11000000,对应十六进制数 C0

  • Bit Order —— 位读取顺序

    MSB是"Most Significant Bit"的缩写,即最高有效位LSB则是最低有效位

    对于一条通道,默认是MSB First,即从高位往低位读取

    假设某像素点Green通道的最后三位数据分别是 011,那么按照MSB First,将提取出 011;相反,按照LSB First则提取出 110

    对于LSB隐写题目,flag信息通常只隐藏在最低位,没有涉及到其它位,这时MSB FirstLSB First都是一样的,该选项不产生影响

  • Extra By —— 额外

    这里提供两个选项:row(行)column(列)

    一张图片是二维的,里面的每一个像素点都可以看作是图片的基本单位,那么图片其实就是一个二维矩阵;对于二维矩阵,有"优先访问行"和"优先访问列"

  • Save TextSave Bin

    两者是保存文本保存二进制代码的区别

    Save Text会原原本本地将Preview窗口中的文本保存下来,它保存的格式只能是 txtdoc 之类的处理文档

    Save Bin只会将窗口左边的十六进制数值保存,而忽略右边的对应Unicode字符;当需要将数据保存为 jpgpng 之类的非可编辑文档时,必须选择Save Bin

    链接:https://pan.baidu.com/s/18Vt7w0M6SHmaqK8CHRnICA
    提取码:7102

    下载图片进行练习,格式:cumtctf{xxx}

    解答

    将图片在StegSolve中打开,左右切换,发现在RGB的最低位图像的上方出现异常,猜测是LSB隐写

    打开Data Extract,同时勾选RGB的最低位通道,其余保持默认,Preview

    发现最前面的4个字符是 .PNG,估计是 png 图片的数据,点击Save Bin,保存为 png 文件

    发现保存的 png 文件是一张二维码,扫码可得flag

    (可以再点击Save Text,用十六进制编辑器打开导出的两份文件,体会Save TextSave Bin的不同)

    为了凸显Data Extract的重要性、以及之前提到过的"在LSB隐写中,flag有可能不以图片的形式、而是以数据的形式存在",再做一题:

    链接:https://pan.baidu.com/s/1FNAsUKYIW7olrwk_TvWztQ
    提取码:ijqm

    解答

    StegSolve打开图片,左右浏览,发现RBG的plane 0都显示为全黑,猜测为LSB隐写

    Data Extract打开,勾选RGB三色的最低位,其余保留默认,Preview,直接在窗口中出现了flag:

以上就是StegSolve的大致用法

【本部分参考】

安全客 abyss文章 https://www.anquanke.com/post/id/189154#h3-6

cnblogs 0cat文章 https://www.cnblogs.com/cat47/p/11483478.html

wooyun文章 https://wooyun.js.org/drops/隐写术总结.html


zsteg

相比于StegSolve对隐写信息检索的略微缓慢和复杂,由俄罗斯黑客开发的开源工具zsteg是一款快速的图片隐写信息检测工具

zsteg用Ruby语言开发,专用于检测 pngbmp 格式图片中的隐写信息,源代码在Github上有


安装

在Ubuntu系统中安装zsteg,需提前安装Gem

Gem

又称为RubyGems,一个管理基于Ruby语言的程序的程序

终端窗口输入

1
$ sudo apt-get install ruby-full rubygems

安装后输入 $ gem,显示RubyGems is a sophisticated package manager for Ruby. This is a basic help message containing pointers to more information.则表明安装成功

之后执行命令:

1
$ sudo gem install zsteg

安装成功后,输入 $ zsteg 可以查看用法


使用

事实上zsteg的具体用法还是相当复杂的,就其示范样例来说:

1
$ zsteg fname.png 2b,b,lsb,xy  ==> --bits 2 --channel b --lsb --order xy
  • 2b,即 --bits 2:每次只提取颜色通道中的第2个比特

  • b,即 --channel b:只提取蓝色通道的比特位

  • lsb,即 --lsb:按最低有效位优先的顺序进行提取;对应StegSolve的MSB FirstLSB First

  • xy,即 --order xy:按照从左到右、从上到下的顺序对图像像素点进行提取;对应StegSolve的RowColumn

    注意StegSolve只有两种次序——行遍历和列遍历,对应zsteg的xyyx;而zsteg有8种组合:xy、xY、Xy、yx、Yx、YX;xy表示从左到右、从上到下,有任何的大写都表示倒序)

真要像对StegSolve那样对zsteg分析,相当麻烦,这里只讲懒人选项 --all

1
$ zsteg temp.png --all

它的作用是:对给定的图片,尝试所有方法提取数据,"try all known methods"

注意,倘若不加任何参数,将会是简化版的 --all,这时只会在较小的区域内尝试检索隐写信息

对于 pngbmp 图片隐写的题目,个人建议的顺序是:

  1. zsteg不带参数的检索
  2. zsteg --all 自动检索
  3. StegSolve

以之前的题目举例,首先是那道"放大镜"图片的题目:

一、flag以数据形式隐藏在图片中
>链接:https://pan.baidu.com/s/1FNAsUKYIW7olrwk_TvWztQ >提取码:ijqm

这题的flag隐藏在三色通道的最低位的数据Preview上:

直接用zsteg检索,结果显示:

b1,rgb,lsb,xy 一行中,检索到了隐写的信息,对应我们在StegSolve中的:每次读取1比特、在RGB三通道上、最低有效位优先、Row

然后是有校徽图片的那道题目:

二、flag以图片数据的形式隐藏在图片中
>链接:https://pan.baidu.com/s/18Vt7w0M6SHmaqK8CHRnICA >提取码:7102

这题在StegSolve中的做法是:在RGB三色通道的最低位的Preview上发现 .PNG,猜测隐藏了 png 图片,Save Bin保存为 png 图片后打开,得到包含flag的二维码

zsteg检索这张图,显示:

还是显示在 b1,rgb,lsb,xy 一行,标识为file,并且是 png 图片文件

三、flag以图片的形式隐藏在图片中
**zsteg**是对数据的检索,对于这种问题只能通过**StegSolve**切换各个模式来查看

注意,zsteg有个 -l 参数,即 --limit,它限制每个方法提取出来的隐藏信息的最大字节数(默认为256bytes),当隐藏信息较多时,zsteg只会显示前面部分(但这也足以让我们意识到隐藏信息在哪了),后面的隐藏信息要么通过StegSolve打开对应通道的Data Extract,要么使用zsteg提取出来

练习一:i春秋王鼎杯,格式为flag{xxx}

链接:https://pan.baidu.com/s/1Z3neHC6ZA3_14QOPqYDbkg
提取码:axe6

练习二:格式为flag{xxx}

链接:https://pan.baidu.com/s/16pR7S0ar4rlqRxaFt45F8A
提取码:2455

解答一

解压 zip,发现共有6张 png 图片

对6张图片依次进行zsteg检索,发现:

6.png 存在LSB隐写,当提取数据的通道次序为bgr时,成功找到隐藏的flag

当隐藏信息的地方较隐蔽,或者隐藏的信息较多时,zsteg相对于StegSolve有优势

解答二

我们直接使用zsteg对这种 bmp 图片进行检索

不带参数的 $ zsteg warmup.bmp 没有检索出什么有用的数据,改成 $ zsteg warmup.bmp --all

在检索出的一堆文本中,我们将目标锁定在:

(这里需要对一些特殊编码有认识,但如果不知道这些特殊编码,对于如此诡异的 Ook,不妨上网查查就可以知道)

我们看到,这里的前3个红色text分别对应short Ook!编码、Ook!编码、Brainfuck编码

这三种特殊编码是Andreas Gohr的开源项目,可以在其博客上的Brainfuck/Ook! Obfuscation/Encoding上在线编码/解码

对于特殊编码,我们只要能够辨别出是哪种编码就行了,解码的工作交给工具去做

不过我们发现,这里检索出来的文本明显是被zsteg --limit 默认的256bytes截断了,以最前面的short Ook!举例,可以根据最前面的 b1,r,lsb,xy,在StegSolve中查看

更推荐的方法是直接用zsteg有条件地检索,方法很简单:照搬前面的通道位置信息,指定 --limit 参数就可以了:

1
>$ zsteg warmup.bmp b1,r,lsb,xy -l 2048

把分别检索出来的信息对应解码即可得到flag

以上便是对zsteg的学习


对于LSB隐写,除了StegSolvezsteg,当然还有其它有用的工具

比如Github上的项目LSB-Steganography、Steghide等

但这篇文章已经很长了,就点到为止


制作

我们可以使用Github上的项目cloacked-pixel来制作一张LSB隐写图片

(该项目共有3个使用方法:hide制作隐写图(需要密码)、extract分离隐写图(需要密码)、analyse分析检索数据,它也可与StegSolvezsteg一样作为分析LSB隐写的工具,但限制较多)

该方法有两个缺点:

  • 使用Python 2
  • 生成LSB隐写图必须要设置密码(密码用来进行AES加密)

我们暂时不需要加密,因此选择http://blog.eonew.cn/archives/812这篇文章提到的代码

这篇文章的代码也是来源自Github的cloacked-pixel项目,但博主对其进行了改进:

  1. 使用Python 3
  2. 无需密码

除了这两点外,其它的都与cloacked-pixel相同


使用

将文章中的代码复制保存到本地,命名为 lsb.py

代码中使用到 sysstructnumpymatplotlibPIL

PIL 库只支持Python 2的各个版本,Python 3无法正常安装;不过有一个可以替代 PIL 的包,并且能在Python 3上正常安装——pillow

直接执行命令安装:

1
pip install pillow

我们尝试在一张 png 格式图片中通过LSB隐写隐藏一张 png 格式的二维码—— temp.pngqrcode.png

执行命令:

1
python lsb.py hide temp.png qrcode.png

执行成功会显示 qrcode.png embedded successfully!,然后会生成一张 temp.png-stego.png 的图片

(原本Github代码需要在最后添加参数密码

测试

我们将 temp.png-stego.pngStegSolve打开,左右切换浏览可以发现在Red plane 0、Green plane 0、Blue plane 0处图像出现异常

在Data Extract勾选RGB三色通道的最低位,Preview,能够发现 .PNG 的信息,说明隐藏了一张 png 图片

zsteg也能检测出这张图片

使用binwalk,无法发现隐藏的 png,这也符合LSB隐写的特征


总结

LSB隐写适用于无损压缩(png)无压缩(bmp)图片

遇到LSB隐写题目,先通过zsteg无参数地检索一遍数据,然后添加 --all 参数再检索一遍,最后使用StegSolve

flag以数据的形式存在,zsteg能快速解决;flag以图片的形式存在,只能通过StegSolve逐一查看

StegSolve提供"反色"、"数据提取"、"文件格式"、"帧浏览"、"立体试图"、"图像合成"(双图问题)功能

可以使用cloacked-pixel制作简单的LSB隐写图

新工具:

  • StegSolve
  • zeteg —— 快速检索信息
  • cloacked-pixel —— 简单制作

end