misc总结(隐写篇)

图片隐写

0x01 Exif信息隐藏

可交换图像文件格式简称exif,可以记录图片的属性信息和拍摄数据。Exif信息是可以被任意编辑的

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
root@lewiserii-pc:~/桌面# exiftool 1.png
ExifTool Version Number : 11.88
File Name : 1.png
Directory : .
File Size : 5.4 kB
File Modification Date/Time : 2023:02:21 13:47:49+08:00
File Access Date/Time : 2023:02:21 13:54:17+08:00
File Inode Change Date/Time : 2023:02:21 13:53:15+08:00
File Permissions : rwxrw-rw-
File Type : PNG
File Type Extension : png
MIME Type : image/png
Image Width : 438
Image Height : 121
Bit Depth : 8
Color Type : RGB
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
Pixels Per Unit X : 5669
Pixels Per Unit Y : 5669
Pixel Units : meters
Software : Snipaste
Image Size : 438x121
Megapixels : 0.053

提取缩略图
exiftool -b -ThumbnailImage attachment.jpg >flag.jpg

0x02 文件修复

文件头(尾)被修改或去除,需要修复文件格式

0x03 文件附加

在正常图片中插入额外的文件数据

使用工具binwalk,foremost,dd提取出来

1
2
3
4
5
6
7
8
9
10
root@lewiserii-pc:~/桌面# binwalk -e 1.png

DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 438 x 121, 8-bit/color RGB, non-interlaced
91 0x5B Zlib compressed data, default compression

root@lewiserii-pc:~/桌面# foremost 1.png
Processing: 1.png
|*|

如果检测到附加的数据,binwalk会在当前目录下生成一个_[文件名].extracted目录,foremost会生成一个output目录

在复杂一点的数据插入需要手动提取或使用脚本

0x04 cloacked-pixel(需要passwd)

lsb隐写的一种
支持PNG,JPG,BMP等图片格式

隐写后会转为PNG

1
2
#lsb.py 98 line
steg_img.save(imgFile + "-stego.png", "PNG")

加密

1
2
3
4
5
6
$ python lsb.py hide samples/orig.jpg samples/secret.zip p@$5w0rD
[*] Input image size: 640x425 pixels.
[*] Usable payload size: 99.61 KB.
[+] Payload size: 74.636 KB
[+] Encrypted payload size: 74.676 KB
[+] samples/secret.zip embedded successfully!

解密

1
2
3
4
5
6
7
$ python lsb.py
LSB steganogprahy. Hide files within least significant bits of images.

Usage:
lsb.py hide <img_file> <payload_file> <password>
lsb.py extract <stego_file> <out_file> <password>
lsb.py analyse <stego_file>

0x05 steghide(可需要passwd)

steghide可以将数据隐藏在图像和音频文件中

加密

1
2
//将secret.txt文件隐藏到text.jpg中
steghide embed -cf test.jpg -ef secret.txt -p 123456

解密

1
steghide extract -sf out.jpg -p 123456

有些题目需要爆破steghide的密码,可以使用pyhton脚本或stegseek工具

1
2
//不指定wordlist.txt时自动使用`rockyou.txt`(如果系统中有,没有会报错)
stegseek [stegofile.jpg] [wordlist.txt]

0x06 F5-steganography(需要passwd)

F5隐写可以将文件嵌入到BMP、GIF或JPEG图像中,需要java环境

加密

1
java Embed 原图.jpg 生成图.jpg -e 隐藏的文件.txt -p '密码'

解密

1
java Extract out.jpg -p '密码'

特征

0x07 zsteg

lsb隐写,与steghide类似,可以检测PNG和BMP中的隐藏数据隐藏数据,可以快速提取隐藏信息。

1
2
zsteg -a <文件名>
zsteg -e b8,a,lsb,xy 文件.png -> out.png

0x07 outguess(可需要passwd)

加密

1
2
outguess -k '密码' -d <需要隐藏的内容> 1.jpg 2.jpg
//1.jpg会覆盖2.jpg

解密

1
outguess -k '密码' -r <加密的图片>  -t <输出保存的文件>

0x08 盲水印

水印又分为单图水印和双图水印

单图水印是指只需要一张图片就能提取出隐藏的信息,双图需要两张图片(一般是原图和加密后的图片,通常两张图片看上去是一样的)

1
2
3
4
5
//blindwatermark双图盲水印
python2 bwm.py encode 原图.png 水印图.png 有盲水印的图.png
python2 bwm.py decode 原图.png 有盲水印的图.png 反解出来的水印图.png
python3 bwmforpy3.py decode 原图.png 有盲水印的图.png 反解出来的水印图.png
python3 bwmforpy3.py decode 原图.png 有盲水印的图.png 反解出来的水印图.png --oldseed

还有一种特殊的盲水印:频域盲水印

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
41
42
43
44
45
46
47
48
49
50
import cv2
import numpy as np
import random
import os
from argparse import ArgumentParser

ALPHA = 5

def build_parser():
parser = ArgumentParser()
parser.add_argument('--original', dest='ori', required=True)
parser.add_argument('--image', dest='img', required=True)
parser.add_argument('--result', dest='res', required=True)
parser.add_argument('--alpha', dest='alpha', default=ALPHA)
return parser

def main():
parser = build_parser()
options = parser.parse_args()
ori = options.ori
img = options.img
res = options.res
alpha = options.alpha
if not os.path.isfile(ori):
parser.error("original image %s does not exist." % ori)
if not os.path.isfile(img):
parser.error("image %s does not exist." % img)
decode(ori, img, res, alpha)

def decode(ori_path, img_path, res_path, alpha):
ori = cv2.imread(ori_path)
img = cv2.imread(img_path)
ori_f = np.fft.fft2(ori)
img_f = np.fft.fft2(img)
height, width = ori.shape[0], ori.shape[1]
watermark = (ori_f - img_f) / alpha
watermark = np.real(watermark)
res = np.zeros(watermark.shape)
random.seed(height + width)
x = range(height / 2)
y = range(width)
random.shuffle(x)
random.shuffle(y)
for i in range(height / 2):
for j in range(width):
res[x[i]][y[j]] = watermark[i][j]
cv2.imwrite(res_path, res, [int(cv2.IMWRITE_JPEG_QUALITY), 100])

if __name__ == '__main__':
main()

使用
python pinyubwm.py --original 1.png --image 2.png --result out.png

0x09 gnuplot

gnuplot可以把坐标画出来

坐标格式:number number

1
plot "1.txt"

0x10 拼图

montage+gaps可以将小图片还原成大图片

先用montage组合图片

1
montage input_file -tile 8X6 -geometry +0+0 output_file

input_file:可以一张一张指定,也能使用正则匹配(*.png)
-geometry +0+0:使图片之间没有间隙
-tile 8X6:以8行6列排列
output_file:输出文件

gaps用来还原montage命令创建的乱序图像

1
2
3
4
5
6
7
8
gaps --image=out.jpg --generations=50 --population=120 --size=50

--image 指向拼图的路径
--size 拼图块的像素尺寸(如果不能明确提供--size的参数,拼图块尺寸将自适应调整)
--generations 遗传算法的代的数量
--population 个体数量
--verbose 每一代训练结束后展示最佳结果
--save 将拼图还原为图像

最近gaps更新了
命令变成了
gaps run 目标图片 生成图片 参数

例如:gaps run 1.png 2.png –generations=60 –population=100

montage+gaps适用于大小相同,数量小的拼图。复杂的需要注意图片名或其他地方是否有顺序提示,还可以注意图片中的冗余位是否可能包含坐标数据

0x11 pngcheck

windows工具,用来检查IDAT块

0x12 webp

webp图片是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式。webp最初在2010年发布,目标是减少文件大小,但达到和JPEG格式相同的图片质量,希望能够减少图片在网络上的发送时间。

webp工具官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cwebp - 将图片文件压缩为 WebP 文件
cwebp 1.png -o 2.webp

dwebp - 将 WebP 文件解压缩到图片文件
dwebp 1.webp -o 2.png

gif2webp - 将 GIF 图片转换为 WebP 格式
gif2webp 1.gif -o 2.webp

vwebp - 解压缩 WebP 文件,然后在窗口中显示该文件
vwebp 1.webp

webpinfo - 输出 WebP 文件的区块级结构以及基本完整性检查
webpinfo 1.webp

0x13 stegpy(可需要passwd)

加密

1
2
stegpy 'hello_world' image.png
stegpy "hello_world" image.png -p //需要密码

解密

1
2
stegpy _image.png
stegpy _image.png -p //需要密码

0x14 二维码

二维码类的题目需要对二维码的生成原理有深入了解

CTF中二维码题目及答题技巧总结(一)

可能有以下考点

1:修复二维码

可能给了一张常规的二维码,但是没有定位符,就需要自己画上去,或者一个大二维码打乱成数个小的二维码,需要还原

2:与数织结合

例如祥云杯的shuffle_code

3:奇奇怪怪的变种二维码

推荐cortexScan和中国编码识别

0x14 GIF

1:可能在某一帧上有字符

2:帧间隔的秒数

可以用identify分析,如果是两种数字重复可以当二进制转字符串

1
2
3
4
5
# 显示序号
identify -format "%s %T \n" 1.gif

# 不显示序号
identify -format "%T\n"

音频隐写

0x01 MP3stego(需要passwd)

针对.mp3文件隐写

.\Decode.exe -X -P [password] [stego_mp3]

会在当前目录下生成一个txt文件

0x02 steghide(可需要passwd)

上面提到过的steghide,也能用于音频隐写

0x03 Audacity

神器

1.摩斯密码

2.频谱图

3.反向音频

4.波形

0x04 minimodem

分析wav文件

1
minimodem --rx -f encoded.wav 1200

--rx:rx或者r均可,为指定读取模式
-f:读取文件
1200:为bell202 bps对应参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//baudmode参考
{any floating point value N} : Bell202-style at N bps −−ascii

1200 : Bell202 1200 bps −−ascii

300 : Bell103 300 bps −−ascii

rtty : RTTY 45.45 bps −−baudot −−stopbits 1.5

tdd : TTY/TDD 45.45 bps −−baudot −−stopbits 2.0

same : SAME 520.83 bps −−startbits 0 −−stopbits 0 −−sync-byte 0xAB NOAA Specific Area Message Encoding (SAME) protocol

callerid : Bell202 1200 bps Caller-ID (MDMF or SDMF) protocol

uic-train : UIC-751-3 600 bps train-to-ground message protocol

uic-ground : UIC-751-3 600 bps ground-to-train message protocol

官方手册

0x05 DeepSound(需要passwd)

windows软件

部分题目的密码需要用deepsound2john.py爆破

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python3
'''
deepsound2john extracts password hashes from audio files containing encrypted
data steganographically embedded by DeepSound (http://jpinsoft.net/deepsound/).

This method is known to work with files created by DeepSound 2.0.

Input files should be in .wav format. Hashes can be recovered from audio files
even after conversion from other formats, e.g.,

ffmpeg -i input output.wav

Usage:

python3 deepsound2john.py carrier.wav > hashes.txt
john hashes.txt

This software is copyright (c) 2018 Ryan Govostes <rgovostes@gmail.com>, and
it is hereby released to the general public under the following terms:
Redistribution and use in source and binary forms, with or without
modification, are permitted.
'''

import logging
import os
import sys
import textwrap


def decode_data_low(buf):
return buf[::2]

def decode_data_normal(buf):
out = bytearray()
for i in range(0, len(buf), 4):
out.append((buf[i] & 15) << 4 | (buf[i + 2] & 15))
return out

def decode_data_high(buf):
out = bytearray()
for i in range(0, len(buf), 8):
out.append((buf[i] & 3) << 6 | (buf[i + 2] & 3) << 4 \
| (buf[i + 4] & 3) << 2 | (buf[i + 6] & 3))
return out


def is_magic(buf):
# This is a more efficient way of testing for the `DSCF` magic header without
# decoding the whole buffer
return (buf[0] & 15) == (68 >> 4) and (buf[2] & 15) == (68 & 15) \
and (buf[4] & 15) == (83 >> 4) and (buf[6] & 15) == (83 & 15) \
and (buf[8] & 15) == (67 >> 4) and (buf[10] & 15) == (67 & 15) \
and (buf[12] & 15) == (70 >> 4) and (buf[14] & 15) == (70 & 15)


def is_wave(buf):
return buf[0:4] == b'RIFF' and buf[8:12] == b'WAVE'


def process_deepsound_file(f):
bname = os.path.basename(f.name)
logger = logging.getLogger(bname)

# Check if it's a .wav file
buf = f.read(12)
if not is_wave(buf):
global convert_warn
logger.error('file not in .wav format')
convert_warn = True
return
f.seek(0, os.SEEK_SET)

# Scan for the marker...
hdrsz = 104
hdr = None

while True:
off = f.tell()
buf = f.read(hdrsz)
if len(buf) < hdrsz: break

if is_magic(buf):
hdr = decode_data_normal(buf)
logger.info('found DeepSound header at offset %i', off)
break

f.seek(-hdrsz + 1, os.SEEK_CUR)

if hdr is None:
logger.warn('does not appear to be a DeepSound file')
return

# Check some header fields
mode = hdr[4]
encrypted = hdr[5]

modes = {2: 'low', 4: 'normal', 8: 'high'}
if mode in modes:
logger.info('data is encoded in %s-quality mode', modes[mode])
else:
logger.error('unexpected data encoding mode %i', modes[mode])
return

if encrypted == 0:
logger.warn('file is not encrypted')
return
elif encrypted != 1:
logger.error('unexpected encryption flag %i', encrypted)
return

sha1 = hdr[6:6+20]
print('%s:$dynamic_1529$%s' % (bname, sha1.hex()))


if __name__ == '__main__':
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('files', nargs='+', metavar='file',
type=argparse.FileType('rb', bufsize=4096))
args = parser.parse_args()

if args.verbose:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARN)

convert_warn = False

for f in args.files:
process_deepsound_file(f)

if convert_warn:
print(textwrap.dedent('''
---------------------------------------------------------------
Some files were not in .wav format. Try converting them to .wav
and try again. You can use: ffmpeg -i input output.wav
---------------------------------------------------------------
'''.rstrip()), file=sys.stderr)

用法

1
2
python3 deepsound2john.py carrier.wav > hashes.txt
john hashes.txt

0x06 dtmf

双音多频信号(Dual-Tone Multi-Frequency, DTMF),电话系统中电话机与交换机之间的一种用户信令,最常用于发拨号时送被叫号码。
双音多频的拨号键盘是4×4的矩阵,每一行代表一个高频,每一列代表一个低频。每按一个键就发送一个高频和低频的正弦信号组合,比如’1’相当于697和1209赫兹(Hz)。交换机可以解码这些频率组合并确定所对应的按键。
(来自维基百科)

命令识别:

1
dtmf2num xxx.wav

0x07 SSTV

慢扫描电视(Slow-scan television)是业余无线电爱好者的一种主要图片传输方法,慢扫描电视通过无线电传输和接收单色或彩色静态图片

可以通过Robot36app或者SSTV Decoder识别

1
sstv -d audio_file.wav -o result.png

0x08 openpuff(需要passwd)

windows软件

需要三个8位密码解密

0x08 lyra

lyra文件特征

安装步骤:

1
2
3
4
5
6
# 安装 Bazel 5.3.2

apt-get install pkg-config zip g++ zlib1g-dev unzip
wget https://github.com/bazelbuild/bazel/releases/download/5.3.2/bazel-5.3.2-installer-linux-x86_64.sh
chmod 777 bazel-5.3.2-installer-linux-x86_64.sh
./bazel-5.3.2-installer-linux-x86_64.sh
1
2
3
4
5
6
7
8
9
10
11
12
# 安装特定版本的clang

apt install ninja-build git cmake clang
git clone https://github.com/llvm/llvm-project.git
cd llvm-project
git checkout 96ef4f307df2
mkdir build_clang
cd build_clang
cmake -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DLLVM_ENABLE_PROJECTS="clang" -DCMAKE_BUILD_TYPE=release ../llvm
ninja
sudo $(which ninja) install
sudo ldconfig
1
2
3
4
5
6
7
# 安装lyra

apt install python3-numpy
git cone https://github.com/google/lyra
cd lyra/
sed -i 's/com_github_gflags_gflags/gflags/g' WORKSPACE
bazel build -c opt lyra/cli_example:decoder_main

用法

bazel-bin/lyra/cli_example/decoder_main --encoded_path=flag.lyra --output_dir=./ --bitrate=3200

文档隐写

0x01 VBA宏密码破解

CMG=替换为CMG.DPB=替换为DPB.GC=替换为GC.

alt+F11打开vba编辑器

0x02 字体隐藏

在字体的效果中勾选隐藏选项即可隐藏字符(或者采用了白色字体)

0x03 图片隐藏

把后缀改为zip解压,在media目录下可以看到文档的所有图片(不排除故意放在其他目录中)

0x04 pdf文件

使用福昕编辑器等即可编辑,或者先转换成docx

0x05 wbstego4

适用于pdf和txt隐写

0x06 密码爆破

可以用passware kit等工具爆破

0x07 odttf转ttf

当复制文档中的内容至notepad后与原内容不一样,多半是修改了字体

在线网站转换

python转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import os

odttf_name = "./1F513ACE-F8AD-4F84-9264-C7B38E1D8CB7.odttf"
odttf_path = os.path.abspath(odttf_name)

with open(odttf_path, "rb") as f:
data = f.read()

# 获取文件名
file_name = os.path.splitext(odttf_path)[0].split("\\")[-1].replace("-", "")

# 获取key
key = []
for i in range(len(file_name), 0, -2):
key.append(int(file_name[i-2:i], 16))

with open("./font2.ttf", "wb") as f:
for i in range(32):
f.write(bytes([data[i] ^ key[i % len(key)]]))
f.write(data[32:])

转换前先修改xxx.odttf文件名为对应的fontKey值(/word/fontTable.xml)

FontDrop或fontforge识别ttf即可

其他

0x01 零宽字符隐写

零宽度字符隐写术(Zero-Width Space Steganography),将隐藏消息编码和解码为不可打印/可读字符。

1
2
3
4
5
6
字符包括:
零宽度空格(\u200b)
零宽度非连接符(\u200c)
零宽度连接符(\u200d)
从左至右书写标记(\u200e)
从右至左书写标记(\u200f)

在线解密工具
https://www.mzy0.com/ctftools/zerowidth1/
http://330k.github.io/misc_tools/unicode_steganography.html
https://offdev.net/demos/zwsp-steg-js
https://yuanfux.github.io/zero-width-web/
http://www.atoolbox.net/Tool.php?Id=829

python解密
https://github.com/enodari/zwsp-steg-py

js解密

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
/**
* Zero-Width Unicode Character Steganography
* Copyright (c) 2015-2016 Kei Misawa
* This software is released under the MIT License.
* http://opensource.org/licenses/mit-license.php
*/
(function(exports){
'use strict';
var chars = [];
var radix = 0;
var codelengthText = 0;
var codelengthBinary = 0;
/**
Set characters of coded hidden text(zero width characters)
args: string of zero width characters
return: null
*/
var setUseChars = function(newchars){
if(newchars.length >= 2){
chars = newchars.split('');
radix = chars.length;
codelengthText = Math.ceil(Math.log(65536) / Math.log(radix));
codelengthBinary = Math.ceil(Math.log(256) / Math.log(radix));
}
return null;
};
/**
Text Encoder
args:
text: original text to be embedded (String)
data: text to be hidden (String)
return: unicode stego text
*/
var encodeText = function(text1, text2){
return combine_shuffle_string(text1, encode_to_zerowidth_characters_text(text2), codelengthText);
};
/**
Binary Encoder
args:
text: original text to be embedded (String)
data: data to be hidden (Uint8Array)
return: unicode stego text
*/
var encodeBinary = function(text, data){
return combine_shuffle_string(text, encode_to_zerowidth_characters_binary(data), codelengthBinary);
};

/**
Text Decoder
args: unicode text with steganography (String)
return: JavaScript Object {
originalText: original text (String),
hiddenText: hidden data (String)
}
*/
var decodeText = function(text){
var splitted = split_zerowidth_characters(text);

return {
'originalText': splitted.originalText,
'hiddenText': decode_from_zero_width_characters_text(splitted.hiddenText, codelengthText)
};
};
/**
Binary Decoder
args: unicode text with steganography (String)
return: JavaScript Object {
originalText: original text (String),
hiddenData: hidden data (Uint8Array)
}
*/
var decodeBinary = function(text){
var splitted = split_zerowidth_characters(text);

return {
'originalText': splitted.originalText,
'hiddenData': decode_from_zero_width_characters_binary(splitted.hiddenText)
};
};

setUseChars('\u200c\u200d\u202c\ufeff');

exports.unicodeSteganographer = {
encodeText: encodeText,
decodeText: decodeText,
encodeBinary: encodeBinary,
decodeBinary: decodeBinary,
setUseChars: setUseChars
};

/**
Internal Functions
*/
var encode_to_zerowidth_characters_text = function(str1){
var result = new Array(str1.length);
var base = '';
var i;
var c;
var d;
var r;

//var base = '0'.repeat(codelength); // IE not support this method
for(i = 0; i < codelengthText; i++){
base += '0';
}

for(i = 0; i < str1.length; i++){
c = str1.charCodeAt(i);
d = c.toString(radix);

result[i] = (base + d).substr(-codelengthText);
}

r = result.join('');

for(i = 0; i < radix; i++){
r = r.replace(new RegExp(i, 'g'), chars[i]);
}

return r;
};
var encode_to_zerowidth_characters_binary = function(u8ary){
var result = new Array(u8ary.length);
var base = '';
var i;
var c;
var d;
var r;

for(i = 0; i < codelengthBinary; i++){
base += '0';
}

for(i = 0; i < u8ary.length; i++){
d = u8ary[i].toString(radix);
result[i] = (base + d).substr(-codelengthBinary);
}

r = result.join('');

for(i = 0; i < radix; i++){
r = r.replace(new RegExp(i, 'g'), chars[i]);
}

return r;
};
var combine_shuffle_string = function(str1, str2, codelength){
var result = [];
var c0 = str1.split(/([\u0000-\u002F\u003A-\u0040\u005b-\u0060\u007b-\u007f])|([\u0030-\u0039]+)|([\u0041-\u005a\u0061-\u007a]+)|([\u0080-\u00FF]+)|([\u0100-\u017F]+)|([\u0180-\u024F]+)|([\u0250-\u02AF]+)|([\u02B0-\u02FF]+)|([\u0300-\u036F]+)|([\u0370-\u03FF]+)|([\u0400-\u04FF]+)|([\u0500-\u052F]+)|([\u0530-\u058F]+)|([\u0590-\u05FF]+)|([\u0600-\u06FF]+)|([\u0700-\u074F]+)|([\u0750-\u077F]+)|([\u0780-\u07BF]+)|([\u07C0-\u07FF]+)|([\u0800-\u083F]+)|([\u0840-\u085F]+)|([\u08A0-\u08FF]+)|([\u0900-\u097F]+)|([\u0980-\u09FF]+)|([\u0A00-\u0A7F]+)|([\u0A80-\u0AFF]+)|([\u0B00-\u0B7F]+)|([\u0B80-\u0BFF]+)|([\u0C00-\u0C7F]+)|([\u0C80-\u0CFF]+)|([\u0D00-\u0D7F]+)|([\u0D80-\u0DFF]+)|([\u0E00-\u0E7F]+)|([\u0E80-\u0EFF]+)|([\u0F00-\u0FFF]+)|([\u1000-\u109F]+)|([\u10A0-\u10FF]+)|([\u1100-\u11FF]+)|([\u1200-\u137F]+)|([\u1380-\u139F]+)|([\u13A0-\u13FF]+)|([\u1400-\u167F]+)|([\u1680-\u169F]+)|([\u16A0-\u16FF]+)|([\u1700-\u171F]+)|([\u1720-\u173F]+)|([\u1740-\u175F]+)|([\u1760-\u177F]+)|([\u1780-\u17FF]+)|([\u1800-\u18AF]+)|([\u18B0-\u18FF]+)|([\u1900-\u194F]+)|([\u1950-\u197F]+)|([\u1980-\u19DF]+)|([\u19E0-\u19FF]+)|([\u1A00-\u1A1F]+)|([\u1A20-\u1AAF]+)|([\u1AB0-\u1AFF]+)|([\u1B00-\u1B7F]+)|([\u1B80-\u1BBF]+)|([\u1BC0-\u1BFF]+)|([\u1C00-\u1C4F]+)|([\u1C50-\u1C7F]+)|([\u1CC0-\u1CCF]+)|([\u1CD0-\u1CFF]+)|([\u1D00-\u1D7F]+)|([\u1D80-\u1DBF]+)|([\u1DC0-\u1DFF]+)|([\u1E00-\u1EFF]+)|([\u1F00-\u1FFF]+)|([\u2000-\u206F]+)|([\u2070-\u209F]+)|([\u20A0-\u20CF]+)|([\u20D0-\u20FF]+)|([\u2100-\u214F]+)|([\u2150-\u218F]+)|([\u2190-\u21FF]+)|([\u2200-\u22FF]+)|([\u2300-\u23FF]+)|([\u2400-\u243F]+)|([\u2440-\u245F]+)|([\u2460-\u24FF]+)|([\u2500-\u257F]+)|([\u2580-\u259F]+)|([\u25A0-\u25FF]+)|([\u2600-\u26FF]+)|([\u2700-\u27BF]+)|([\u27C0-\u27EF]+)|([\u27F0-\u27FF]+)|([\u2800-\u28FF]+)|([\u2900-\u297F]+)|([\u2980-\u29FF]+)|([\u2A00-\u2AFF]+)|([\u2B00-\u2BFF]+)|([\u2C00-\u2C5F]+)|([\u2C60-\u2C7F]+)|([\u2C80-\u2CFF]+)|([\u2D00-\u2D2F]+)|([\u2D30-\u2D7F]+)|([\u2D80-\u2DDF]+)|([\u2DE0-\u2DFF]+)|([\u2E00-\u2E7F]+)|([\u2E80-\u2EFF]+)|([\u2F00-\u2FDF]+)|([\u2FF0-\u2FFF]+)|([\u3000-\u303F]+)|([\u3040-\u309F]+)|([\u30A0-\u30FF]+)|([\u3100-\u312F]+)|([\u3130-\u318F]+)|([\u3190-\u319F]+)|([\u31A0-\u31BF]+)|([\u31C0-\u31EF]+)|([\u31F0-\u31FF]+)|([\u3200-\u32FF]+)|([\u3300-\u33FF]+)|([\u3400-\u4DBF]+)|([\u4DC0-\u4DFF]+)|([\u4E00-\u9FFF]+)|([\uA000-\uA48F]+)|([\uA490-\uA4CF]+)|([\uA4D0-\uA4FF]+)|([\uA500-\uA63F]+)|([\uA640-\uA69F]+)|([\uA6A0-\uA6FF]+)|([\uA700-\uA71F]+)|([\uA720-\uA7FF]+)|([\uA800-\uA82F]+)|([\uA830-\uA83F]+)|([\uA840-\uA87F]+)|([\uA880-\uA8DF]+)|([\uA8E0-\uA8FF]+)|([\uA900-\uA92F]+)|([\uA930-\uA95F]+)|([\uA960-\uA97F]+)|([\uA980-\uA9DF]+)|([\uA9E0-\uA9FF]+)|([\uAA00-\uAA5F]+)|([\uAA60-\uAA7F]+)|([\uAA80-\uAADF]+)|([\uAAE0-\uAAFF]+)|([\uAB00-\uAB2F]+)|([\uAB30-\uAB6F]+)|([\uAB70-\uABBF]+)|([\uABC0-\uABFF]+)|([\uAC00-\uD7AF]+)|([\uD7B0-\uD7FF]+)|([\uD800-\uDFFF]+)|([\uE000-\uF8FF]+)|([\uF900-\uFAFF]+)|([\uFB00-\uFB4F]+)|([\uFB50-\uFDFF]+)|([\uFE00-\uFE0F]+)|([\uFE10-\uFE1F]+)|([\uFE20-\uFE2F]+)|([\uFE30-\uFE4F]+)|([\uFE50-\uFE6F]+)|([\uFE70-\uFEFF]+)|([\uFF00-\uFFEF]+)|([\uFFF0-\uFFFF]+)/g);
var c1 = [];
var i;
var j;
for(i = 0; i < c0.length; i++){
if((typeof c0[i] !== 'undefined') && (c0[i] !== '')){
c1.push(c0[i]);
}
}
var c2 = str2.split(new RegExp('(.{' + codelength + '})', 'g'));
var ratio = c1.length / (c1.length + c2.length);

/* slow
while((c1.length > 0) && (c2.length > 0)){
if(Math.random() <= ratio){
result.push(c1.shift());
}else{
result.push(c2.shift());
}
}*/
i = 0;
j = 0;
while((i < c1.length) && (j < c2.length)){
if(Math.random() <= ratio){
result.push(c1[i]);
i++;
}else{
result.push(c2[j]);
j++;
}
}
c1 = c1.slice(i);
c2 = c2.slice(j);

result = result.concat(c1).concat(c2);

return result.join('');
};
var split_zerowidth_characters = function(str1){
var result = {};
result.originalText = str1.replace(new RegExp('[' + chars.join('') + ']', 'g'), '');
result.hiddenText = str1.replace(new RegExp('[^' + chars.join('') + ']', 'g'), '');

return result;
};
var decode_from_zero_width_characters_text = function(str1){
var r = str1;
var i;
var result = [];
for(i = 0; i < radix; i++){
r = r.replace(new RegExp(chars[i], 'g'), i);
}
for(i = 0; i < r.length; i += codelengthText){
result.push(String.fromCharCode(parseInt(r.substr(i, codelengthText), radix)));
}

return result.join('');
};
var decode_from_zero_width_characters_binary = function(str1){
var r = str1;
var i;
var j;
var result = new Uint8Array(Math.ceil(str1.length / codelengthBinary));

for(i = 0; i < radix; i++){
r = r.replace(new RegExp(chars[i], 'g'), i);
}
for(i = 0, j = 0; i < r.length; i += codelengthBinary, j++){
result[j] = parseInt(r.substr(i, codelengthBinary), radix);
}

return result;
};

return null;
})(this);


var s = unicodeSteganographer
s.setUseChars('\u200b\u200c\u200d\u200e\u200f');
s.decodeText("我已经看见了,​​​​‎‏​​​​​‏​‍​​​​‏‍‌​​​​‏‍‏​​​​‎‏​​​​​‏‏‌​​​​‏‍‌​​​​‏‎‍​​​​‌‌‎​​​​‎‏‏​​​​‏‍‌​​​​‏‍​​​​​‏​‎​​​​‏‍‏​​​​‎‏‍​​​​‏‎‌​​​​‏‎‍​​​​‏‌‎​​​​‎‏‍​​​​‏‎‌​​​​‏​‌​​​‌​​​你呢?")

/**
* 复制并使用代码请注明引用出处哦~
* Lazzaro @ https://lazzzaro.github.io
*/

0x02 ntfs数据流隐写

NTFS交换数据流(alternate data streams,简称ADS)是NTFS磁盘格式的一个特性,在NTFS文件系统下,每个文件都可以存在多个数据流,就是说除了主文件流之外还可以有许多非主文件流寄宿在主文件流中。它使用资源派生来维持与文件相关的信息。————百度百科

创建一个NTFS数据流
echo "test" > 1.txt:2.txt

查看数据流
使用NtfsStreamsEditor(windows)工具或者notepad命令

0x03 pyc文件反编译

一般使用uncompyle6

uncompyle6 test.pyc > test.py

0x04 pyc隐写

Stegosaurus 是一款隐写工具,它允许我们在 Python 字节码文件( pyc 或 pyo )中嵌入任意 Payload

github项目说是Python 3.6 or later,但是实际使用发现只有Python3.6可以运行

1
python3 stegosaurus.py -x [pyc_file]

有时候需要注意magic number的问题
python3.6以下magic number是12个字节;python3.7以上是16个字节

0x05 Tupper自我指涉公式

塔珀自指公式是杰夫·塔珀(Jeff Tupper)发现的自指公式:此公式的二维图像与公式本身外观一样。
该公式是一种对存储在常量k 中的位图信息进行解码的通用方法 ,它实际上可以用来绘制任何其他图像。

该不等式的定义为:

其中 ⌊ ⌋ 表示楼层函数, mod 是模运算

根据k值不同,可以做出”任意”图像

Tupper’s Formula Tools

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 functools import reduce


def Tuppers_Self_Referential_Formula():
k = 4858450636189713423582095962494202044581400587983244549483093085061934704708809928450644769865524364849997247024915119110411605739177407856919754326571855442057210445735883681829823754139634338225199452191651284348332905131193199953502413758765239264874613394906870130562295813219481113685339535565290850023875092856892694555974281546386510730049106723058933586052544096664351265349363643957125565695936815184334857605266940161251266951421550539554519153785457525756590740540157929001765967965480064427829131488548259914721248506352686630476300
# 这里替换为你自己的K值

def f(x, y):
d = ((-17 * x) - (y % 17))
e = reduce(lambda x, y: x * y, [2 for x in range(-d)]) if d else 1
g = ((y // 17) // e) % 2
return 0.5 < g

for y in range(k + 16, k - 1, -1):
line = ""
for x in range(0, 107):
if f(x, y):
line += " ■"
else:
line += " "
print(line)


if __name__ == '__main__':
Tuppers_Self_Referential_Formula()

0x06 snow隐写

snow 隐写是在html嵌入隐写信息,它的原理是通过在文本文件的末尾嵌入空格和制表位的方式嵌入隐藏信息,不同空格与制表位的组合代表不同的嵌入信息。

snow加密的特征非常明显,它会生成大量的2009,在预览的时候像雪花一样

snow在线

1
snow.exe -C -p password filename

0x07 whitespace

white_space是一种编程语言
由”空格”,”回车”,”tab”组成

whitespace ide

0x08 PGP

PGP 加密系统是采用公开密钥加密与传统密钥加密相结合的一种加密技术。它使用一对数学上相关的钥匙,其中一个(公钥)用来加密信息,另一个(私钥)用来解密信息。

一般需要私钥文件xxx.keyxxx.asc,加密文件xxx.pgp和一个密码

PGPTool: https://pgptool.github.io/

例如[BSidesSF2019]bWF0cnlvc2hrYQ==

Key ring->Import PGP Key->导入xxx.key->然后输入密码

0x09 otp

有点偏向密码学,wiki

一次性密码(One Time Password,简称 OTP),又称 “一次性口令”,是指只能使用一次的密码。

OTP 的密钥是以 base32 的方式存储的

python中有一个pyotp库可以处理它

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
#Time-based OTPs
import pyotp
import time

totp = pyotp.TOTP('base32secret3232')
totp.now() # => '492039'


totp.verify('492039') # => True
time.sleep(30)
totp.verify('492039') # => False


#Counter-based OTPs
import pyotp

hotp = pyotp.HOTP('base32secret3232')
hotp.at(0) # => '260182'
hotp.at(1) # => '055283'
hotp.at(1401) # => '316439'

hotp.verify('316439', 1401) # => True
hotp.verify('316439', 1402) # => False



#Generating a Secret Key
pyotp.random_base32()
pyotp.random_hex()

0x10 数控打印

遇到*.gcode文件或者3D打印gcode的命令可以放到下面这两个网站解析

ncviewer
gCodeViewer

0x11 Dicom图像

x光图像

后缀为.dcm的文件可以使用MicroDicom打开


misc总结(隐写篇)
https://www.dr0n.top/posts/b9152a17/
作者
dr0n
发布于
2022年7月3日
更新于
2024年11月3日
许可协议