做CTF题好长一段时间了,真的可以学到很多东西。这次,我们开启 net-force.nl 的 Steganography之旅,所谓的隐写术。
level 801: Training - Can you see me?
点开上面的链接可以查看题目。字的颜色以背景颜色书写,惯用的雕虫小技,Ctrl + A 就能现形,或者查看网页代码,答案显而易见。
<span style="color: white"> Look at the file password.gif and you will find the password for the next challenge page easy!!!...You hope ;-) </span>
可是文件 password.gif 在哪里?搜索了网页代码,没找到引用的地方,尝试在该级目录下追加名字 password.gif 打开
咦,图片坏掉了?图片存到本地,或者直接用 wget 命令拉取下来查看一下。
martin@M2037:~/ctf/net-force.nl$ wget https://net-force.nl/challenge/level801/password.gif
--2015-10-25 23:14:29-- https://net-force.nl/challenge/level801/password.gif
Resolving net-force.nl (net-force.nl)... 83.137.145.137
Connecting to net-force.nl (net-force.nl)|83.137.145.137|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 56 [image/gif]
Saving to: ‘password.gif’
100%[=======================================================>] 56 --.-K/s in 0s
2015-10-25 23:14:31 (196 KB/s) - ‘password.gif’ saved [56/56]
martin@M2037:~/ctf/net-force.nl$ file password.gif
password.gif: ASCII text, with CRLF line terminators
martin@M2037:~/crack/net-force.nl$ vim password.gif
martin@M2037:~/ctf/net-force.nl$ cat password.gif
hehe...nope it's just a text file :)
password = stegano
被欺骗了,原来它就不是什么图片,是ASCII文本。这让我想起了声声叹的第七则笑话,关于PNG改后缀。
然而,这场战役才刚刚开始。隐写就是将秘密藏在公共信息里,让你察觉不出来,达到传输出去的目的;而加密会明确告诉你密文(一般也会有加密方法),让你推算明文消息。三十六计中的第一计瞒天过海:备周则意怠,常见则不疑。阴在阳之内,不在阳之对。太阳,太阴。如果之前没读过,可以抽时间好好读一下了。兵法上说秘计往往隐藏于公开的事物里,而不在公开事物的对立面上,隐写术也是这么玩的,给你"明修栈道"的信息,实际则"暗度陈仓"。
level 802: Go Holland Go!
There is password hidden on this page, find it...that's all (dutch word)
乍一看,又是0/1二进制字符,又是红黑蓝三种颜色的,玩什么把戏?
远远地看,这字母大片的0大片的1,中空的数字0构成四个字母TULP,难道这就是答案了?输入回车,果然是,那这也太简单了。
是否感觉题目不过如此?难度级别在慢慢增大,更有挑战性的还在后面。
如果你对这样的书写感兴趣,可以搜索关键字"图片转ASCII",算法大致的思路是,颜色抖动(dither)。
如果仅用黑白两种颜色来表达一张灰度图/彩色图这样有多种颜色的图片呢?0/1只能表达两种颜色,可是如果每两个像素一看,就有2*2=4种色彩了,每四个一看4*4=16种色彩了,虽然仅仅是灰度值。用字符充当灰度信息也是一个道理,一个字在一个方块里也是两种色,线条多的字比线条少的字看起来更"浓"一些,所以ASCII里可以用@#表示浓,,.表示淡。延伸阅读,有关最终幻想1里面的像素的艺术: http://www.petesqbsite.com/sections/tutorials/tuts/tsugumo/chapter6.htm
level 803: Words, words, words...
Well, this is as easy as it can be... just read the password and fill it in on the challenge page!
The tiny thing, the password, sure it is that what you seek?
You need it fast? Need is one thing, for what you need the password anyway?
The challenge? Look at this page, if the password is what you want.
Again I say, Look!
虽然指明了说密码就在文字里,可一个一个地试答案,就没多大意思了,我们需要知道背后的逻辑。
我在做caesum.com 的题目 Unix finger is not aching 搜索到 http://www.phearless.org/istorija/razno/bsteg.txt ,也许对你以后做类似的题有启发。
文本隐写可以在每个单词的字母位置,每个段落的字母位置和每个段落的单词位置做文章。
很多人玩过文字游戏藏头诗,将要表达的话用每句话的地一个字串起来。例如,帮忙用"武婷婷我爱你",做成藏头诗来表白。
武大郎!
停下!
停下!
我要买烧饼!
爱!好咧!
你要几个!
笑够了,该做题了。段落的最后一句Again I say,是不是像在说"都说了是Again了"?
The tiny thing, the password, sure it is that what you seek?
You need it fast? Need is one thing, for what you need the password anyway?
The challenge? Look at this page, if the password is what you want.
Again I say, Look!
每隔三个单词一摘取:The password that You Need for the challenge page is Again.
不知道答案是否讲究大小写,一试便知。
Level 804: “Nice colors eh?”
Uhm ok, get the password out of this image...it's a dutch word :)
很单调的图像,只有几种颜色,把颜色数值提取出来。
Color 1: 8B8B61 Color 2: 8B8B61 Color 3: 8B8B70
Color 4: 8B8B6A Color 5: 8B8B65 Color 6: 8B8B73
将字符连在一起显示,很容易被发现,于是有了上面的,只采用单一的通道书写,稍微提高了隐蔽性。
前面的都是8B8B,我们把后面的蓝色通道信息提取出来 61 61 70 6A 65 73,粗看,他们就在小写字母(a-z, 0x61-0x7a)的范围内。
printf "\x61\x61\x70\x6A\x65\x73\n"
Level 805: Another picture!
Another dutch word is hidden in this image ;)
给了一张木偶(puppet)的图片,文件名 monster.jpg
关于图片的隐写,第一步就是像上一题一样,查看是否隐藏的信息被当成像素,或者图片元信息写入了图片。
这个时候,strings 命令就是你的朋友了。
martin@M2037:~/ctf/net-force.nl$ strings monster.jpg
JFIF
$.' ",#
(7),01444
'9=82<.342
!22222222222222222222222222222222222222222222222222
...
mNYe%L
j^'6
wvT
@!Fx
)01101011011011110110010101101011011010100011001101110011.
开头的一串2倒是没什么,末尾的0/1字符串行迹特别可疑,为什么呢?首先它看起来就像啊!
martin@M2037:~/ctf/net-force.nl$ echo -n "01101011011011110110010101101011011010100011001101110011" | wc
0 1 56
martin@M2037:~/ctf/net-force.nl$ echo "01101011011011110110010101101011011010100011001101110011" | sed -r "s/([01]{8})/\1 /g"
01101011 01101111 01100101 01101011 01101010 00110011 01110011
用wc一下,长度是56,刚好可以凑成7个字符,用 sed 再分割一下解析文本成ASCII字符,也可以用Python的struct模块,方便!
我之前的文章C++ 的二进制语法与语义第二种方法也给出了如何解析。
level 806: Just a flag...
There's a password inside this icon...
martin@M2037:~/ctf/net-force.nl$ file flag.ico
flag.ico: MS Windows icon resource - 2 icons, 32x32, 16 colors
martin@M2037:~/ctf/net-force.nl$ strings flag.ico
V.qK
file.txt
wgyM
V.qK
file.txtPK
看到了PK,条件反射地想到这个文件可能会有.zip文件,file.txtPK,可以提取出file.txt。
用dd命令,或者Notepad++/wxHexEditor/010 Editor之类的二进制编辑工具提取出来。
提取过程可以参考之前的题解--题目2 Stegano Attachment
0000430: 0000 ffff 0000 504b 0304 0a00 0900 0000 ......PK........
0000440: a784 562e 714b c313 2400 0000 1800 0000 ..V.qK..$.......
0000450: 0800 0000 6669 6c65 2e74 7874 a5c8 d381 ....file.txt....
0000460: cc63 5ab4 1cd1 d623 9677 6779 4dbb 5856 .cZ....#.wgyM.XV
0000470: 03bd 7d63 c3cf 4ee3 dd1f 917c 1662 e6b1 ..}c..N....|.b..
0000480: 504b 0708 714b c313 2400 0000 1800 0000 PK..qK..$.......
0000490: 504b 0102 1400 0a00 0900 0000 a784 562e PK............V.
00004a0: 714b c313 2400 0000 1800 0000 0800 0000 qK..$...........
00004b0: 0000 0000 0100 2000 8081 0000 0000 6669 ...... .......fi
00004c0: 6c65 2e74 7874 504b 0506 0000 0000 0100 le.txtPK........
00004d0: 0100 3600 0000 5a00 0000 0000 ..6...Z.....
martin@M2037:~/ctf/net-force.nl$ dd if=flag.ico of=file.zip skip=`printf "%d" 0x0000436`
dd: ‘flag.ico’: cannot skip to specified offset
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.00348879 s, 0.0 kB/s
martin@M2037:~/ctf/net-force.nl$ dd if=flag.ico of=file.zip skip=`printf "%d" 0x0000436` bs=1
166+0 records in
166+0 records out
166 bytes (166 B) copied, 0.0110782 s, 15.0 kB/s
martin@M2037:~/crack/net-force.nl$ file file.zip
file.zip: Zip archive data, at least v1.0 to extract
martin@M2037:~/ctf/net-force.nl$ unzip file.txt
Archive: file.txt
[file.txt] file.txt password:
解压的时候,提示要输入密码,只能暴力破解了。不过这样的题一般意思到就行了,不会出很复杂的密码。
推荐.zip格式密码破解工具fcrackzip
先benchmark一下,也就是Android手机界里常说的跑分,看哪种方法在本机上快就选哪个来破解。
martin@M2037:~/ctf/net-force.nl$ apt-cache search fcrackzip
fcrackzip - password cracker for zip archives
martin@M2037:~/ctf/net-force.nl$ sudo apt-get install fcrackzip
[sudo] password for martin:
Reading package lists... Done
Building dependency tree
Reading state information... Done
fcrackzip is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 324 not upgraded.
martin@M2037:~/ctf/net-force.nl$ fcrackzip --benchmark
cpmask: (skipped)
zip1: cracks/s = 0451414
*zip2, USE_MULT_TAB: cracks/s = 0442891
martin@M2037:~/crack/net-force.nl$ fcrackzip file.zip -u -v -m zip2 -l1 -c a
found file 'file.txt', (size cp/uc 36/ 24, flags 9, chk 84a7)
PASSWORD FOUND!!!!: pw == a
原来它用了最简单的字符a作密码,fcrackzip参数的意思man一下
-u, --use-unzip: 尝试解压文件,防止误判(false positive)
-v, --verbose: 输出更多信息
-m, --method name: 用的破解方法,我的机器默认的是zip2,所以可以不写。
-l, --length min[-max]: 密码长度,由短至长慢慢破解,奈何一下就发现。
-c, --charset characterset-specification 搜索空间,也就是字符集。a表示在小写字母里搜索。
a include all lowercase characters [a-z]
A include all uppercase characters [A-Z]
1 include the digits [0-9]
! include [!:$%&/()=?{[]}+*~#]
: the following characters upto the end of the specification string are
included in the character set. This way you can include any character
except binary null (at least under unix).
例如,aaaa可以匹配单词flag。a1:$% 表示在小写字母,数字,美元符号和百分号之间搜索。
-p, --init-password string: 初始值,从某个密码开始搜索,或者用字典密码,暂未排上用场。
martin@M2037:~/crack/net-force.nl$ unzip file.zip
Archive: file.zip
[file.zip] file.txt password:
replace file.txt? [y]es, [n]o, [A]ll, [N]one, [r]ename: y
extracting: file.txt
martin@M2037:~/crack/net-force.nl$ cat file.txt
The password is: *******
为了防止泄漏密码,我还是打上马赛克,哈哈~
注意,标点符号可能也是密码的一部分。
level 807: Learn See Become
Get the password from the text below!
Text:
2C7CBi*66iC6C2BBB3i6B36i<;][XJ\D>AQJ>Q7[\C;|Q[M]>917,.E.|G]B>S.2X3YXYXXY./YY.2Y3XY32.X.Yl//lmml.63mm2*l6.+7lml622336*26/
注意到这一题目与其他题目的不同,题目三个连续的动词,从语法上讲是有错误的,它想说明什么?要学会看,学会观察和分析。
首字母均大写,合起来是LSB,想到了什么?在程序中处理过大小端的朋友一定会想到MSB/LSB缩写。
难道他是在提醒Least Significant Bit么?尝试先把这些位提取出来,你如果熟悉C语言,就用C语言拽出来吧。
martin@M2037:~/ctf/net-force.nl$ echo -n '2C7CBi*66iC6C2BBB3i6B36i<;][XJ\D>AQJ>Q7[\C;|Q[M]>917,.E.|G]B>S.2X3YXYXXY./YY.2Y3XYl.63mm2*l6.+7lml622336*26/' | xxd -b -c 1 | awk '{print $2}' | sed -r 's/[01]{7}([01])/\1/g' | tr -d -c '01'
011101000110100001100101011100000110011101101111011100100110010001101001011100110110000101101100011100000110100001100001
xxd -b 表示工作在二进制模式
tr -d -c '01' 过滤掉不是 0/1 的字符
sed -r 's/([01]{8})/\1 /g' 意思是每8个用空格分割一下
sed -r 's/([01]{8})/\1 /g'
01110100 01101000 01100101 01110000 01100111 01101111 01110010 01100100 01101001 01110011 01100001 01101100 01110000 01101000 01100001
然后就是二进制解析了,怎么熟悉就怎么来吧。
#include <stdio.h> #include <string.h> #include <stdlib.h> int main() { // http://en.cppreference.com/w/cpp/language/string_literal // const char raw[] = R"###(2C7CBi*66iC6C2BBB3i6B36i<;][XJ\D>AQJ>Q7[\C;|Q[M]>917,.E.|G]B>S.2X3YXYXXY./YY.2Y3XY32.X.Yl//lmml.63mm2*l6.+7lml622336*26/)###"; // or you have to escape charactors like " \ (diliberately put \ at the end) const char raw[] = "2C7CBi*66iC6C2BBB3i6B36i<;][XJ\\D>AQJ>Q7[\\C;|Q[M]>917,.E.|G]B>S.2X3YXYXXY./YY.2Y3XY32.X.Yl//lmml.63mm2*l6.+7lml622336*26/"; const char binary[] = "01110100 01101000 01100101 01110000 01100111 01101111 01110010 01100100 01101001 01110011 01100001 01101100 01110000 01101000 01100001"; printf("length: %zu\n", strlen(raw)); const char* begin; char* end = const_cast<char*>(binary); do { begin = end; long int byte = strtol(begin, &end, 2/*base*/); putchar(static_cast<int>(byte)); } while(begin != end); putchar('\n'); return 0; }
output:
length: 120
thepgordisalpha
the pgord is alpha
net-force.nl中的nl是荷兰(Netherlands)的字符编码缩写,
pgord 是什么玩意儿,荷兰语单词"密码"?维基字典查了下,好像不是。百度一下有惊喜。
level808: How good are your eyes?
stip是什么意思?好像不是什么单词.只能找到 stipple,可能就是这个了。
stipple 点画,点描;点刻(指雕刻)
检查了下,图没有什么不正常的。
用专业修图工具GIMP打开查看,粗看没什么,放大几倍查看,有好几个红点。位置不规则,连起来也看不出什么,定位一下坐标再看。
martin@M2037:~/ctf/net-force.nl$ gimp stip.bmp
记下红点的坐标和像素值
(100, 0): 1946FA
( 34, 1): 3246FA
(130, 15): 2D46FA
( 20, 17): 2846FA
( 90, 21): 1E46FA
(100, 21): 1446FA
(150, 39): 0F46FA
(180, 59): 2346FA
(171, 100): 0A46FA
像素绿蓝通道都是一样的46FA,红色通道不是字母或数字对应的ASCII字符,好像有什么其他的规律?提取出来单独看。
19 32 2D 28 1E 14 0F 23 0A
0A 0F 14 19 1E 23 28 2D 32 从小到大排列是等差数列,说明什么呢?
坐标值倒是可打印的字母,难道把密文藏在坐标里了?试着把前面的坐标值也排列一下。
GoOdEy\03s!
好像是答案了,不过有一个字符的值是3,根据本题的题目先猜 GoOdEyes!
leet speak 里3和e是一样的,不过这里是数字3,不是ASCII字符3(51)
那就用3再试试,GoOdEy3s!嗯,我眼神还是很好的嘛~
level809: Did you try it 100x?
看到歪歪斜斜的条,我马上意识到图片的尺寸可能有问题。
因为大学做过数字图像处理,BMP文件的读写需要四字节对齐,如果没有的话,会出现这样的锯齿效果。
对BMP文件格式作一番了解后,结构体(struct)需要采用一字节对齐(32位机器默认是四)才能读写正确。
010 editor 是很不错的二进制编辑软件,强烈推荐,因为有很多模板可以使用。Templates > BMP template
对图片的每个字节意思都给予解释,不明白的会转换不同类型显示。
图中尺寸47x50,但是字节数有sizeImage=30000,BitCount是24位,每个像素是RGB形式,占3个字节。
w*h的结果应该是10000,开方一下width=height=100,看到结果 o3ps
如果你改成 width=200,height=50,你会看到左右两个 o3ps,神奇吗?改了两个像素就出现这种效果。
图像其实就是二位数组,这就是MATLAB里对二维矩阵 reshape 后的效果。
其实还有很多可以在图片文件上作文章的stegano想法。
level811: Ancient Voices
Your task speaks for itself, find the hidden password.
The program 'pngcheck' is currently not installed. You can install it by typing:
sudo apt-get install pngcheck
$ pngcheck giza.png
giza.png illegal (unless recently approved) unknown, public chunk cHRm
ERROR: giza.png
PNG 的 chunk 都是四个字符组成的32位int,我记得好像一般首字母小写,后面三个字母大写。
还是上代码吧,摘取 /usr/include/png.h
#define PNG_INFO_gAMA 0x0001 #define PNG_INFO_sBIT 0x0002 #define PNG_INFO_cHRM 0x0004 #define PNG_INFO_PLTE 0x0008 #define PNG_INFO_tRNS 0x0010 #define PNG_INFO_bKGD 0x0020 #define PNG_INFO_hIST 0x0040 #define PNG_INFO_pHYs 0x0080 #define PNG_INFO_oFFs 0x0100 #define PNG_INFO_tIME 0x0200 #define PNG_INFO_pCAL 0x0400 #define PNG_INFO_sRGB 0x0800 /* GR-P, 0.96a */ #define PNG_INFO_iCCP 0x1000 /* ESR, 1.0.6 */ #define PNG_INFO_sPLT 0x2000 /* ESR, 1.0.6 */ #define PNG_INFO_sCAL 0x4000 /* ESR, 1.0.6 */ #define PNG_INFO_IDAT 0x8000 /* ESR, 1.0.6 */
cHRm chunck 有问题,是笔误还是故意写错的呢?
chromaticity是色度的意思,the quality of a color as determined by its dominant wavelength
name value start size color
struct CHUNK chunk[13] cHRm (Ancillary, Public, Safe to Copy) 196h 313Fh Fg: Bg:
uint32 length 12595 196h 4h Fg: Bg:
union CTYPE type cHRm 19Ah 4h Fg: Bg:
ubyte data[12595] 19Eh 3133h Fg: Bg:
uint32 crc D97638D7h 32D1h 4h Fg: Bg:
martin@M2037:~/ctf/net-force.nl$ dd if=giza.png of=giza.cHRm ibs=1 skip=0x19E count=0x3133
dd: invalid number ‘0x19E’
martin@M2037:~/ctf/net-force.nl$ dd if=giza.png of=giza.cHRm ibs=1 skip=`printf "%d" 0x19E` count=`printf "%d" 0x3133`
12595+0 records in
24+1 records out
12595 bytes (13 kB) copied, 0.0244755 s, 515 kB/s
$ file giza.cHRm
giza.cHRm: ISO Media, MPEG v4 system, version 1
$ vlc giza.cHRm
用音乐播放器播放,音节大概听出来是tu tan ga mang搜索一下,中文提示"图坦卡蒙"。
题目中给的图片是金字塔,所以大概就是指这个人了。
图坦卡蒙(前1341-前1323年)是古埃及新王国时期第十八王朝的法老。
Tutankhamun
应该就是这个人了,与埃及金字塔有关,顺便了解了一下这个埃及法老。
图坦卡蒙的诅咒:谁扰乱了这位法老的安宁,‘死神之翼’将在他头上降临。
数十年来,凡是胆敢进入法老墓穴的,几乎一一应了咒语,不是当场毙命,就是不久后染上奇怪的病症而痛苦地死去。
不过怎么输入密码都不对,去论坛看了一下。https://net-force.nl/forum/list_messages/363/Stegano_Ancient_Voices/1/
For everybody who practically solved it but fights with the spelling: the solution is all lowercase without '-' or ' ' and the spelling follows a very well known english online encyclopaedia.
@admins: If you think that this gives to much away, remove this post. But having to guess around with the spelling is just stupid.
原来不是我一个人在战斗,很多人都遇到过这个问题。
level812: SPECtacular
I watched the river one day. It scrolled past in its colors, making a beautiful sound.
- Ivan the Zen Master
wget https://net-force.nl/challenge/level812/nfchallenge_818.mp3
用Audacity软件打开,调到音量或者amplify一下可以听到两种声音混在一起。
SPECtacular 是一个单词,意思"场面壮观的,引人注意的,惊动一时的"
但是前半部分大写,后半部分小写,是想说明由两部分组成?
这是个有关声音隐写的题目,看能不能从 spectrum(光谱; 波谱; 范围; 系列)得到提示。
如果你没有音频相关的职业,或者英语不是很好,可能就想不到 spectrum 了。