这题我做法比较笨,完全是人工看文件片段拼图拼出来的。拼图过程中逐步推断出 stripe size 和盘的顺序。
用 string
, xxd
先扫一下文件内容。盘里有一些小段文本文件,以及三个 PDF 文件。PDF 文件开头和结尾有明确的 magic string(%PDF
和 %%EOF
),然后文件中有一些部分(可能类似 PostScript 中的 command)是 ASCII 文本,特征很明显。
观察几个短的文本文件可以推断出文件系统 block size 是 4096 byte。比如看 1GHGGrmaMM0.img
的 00c09000-00c09fff
,最开始是个 Dockerfile,后面补 0 填满 4096 byte,然后开始下一个文件。不过这个信息没啥直接用途,只是帮助理解一下文件存储方式。
注意力还是回到那三个 PDF 文件。这里列出镜像里的三组 PDF 文件头和结尾的位置:
- 文件开头
%PDF
- BOF1:
ID7sM2RWkyI.img:008ae000
- BOF2:
jCC60mutgoE.img:008c4000
- BOF3:
RApjvIxRlu0.img:008d2000
- BOF1:
- 文件结尾
%%EOF
- EOF1:
jCC60mutgoE.img:008c36ad
- EOF2:
RApjvIxRlu0.img:008d1d35
- EOF3:
wlOUASom2fI.img:008e426d
- EOF1:
这几个文件看起来挺大,显然超出了一个 fs block。试着用文件内容不连续的位置来判断 stripe size。ID7sM2RWkyI.img:008ae000
这里开始有个 PDF 文件,然后一直到 008dffff
断了,没有 EOF,后面开始是 0 字节。可以推断 008e0000
应该是新 stripe 开始的位置:
008ae000: 2550 4446 2d31 2e33 0a25 c4e5 f2e5 eba7 %PDF-1.3.%......
......
008dfff0: 46a1 4f02 d9fe 5c69 e7df ec6f e647 95a8 F.O...\i...o.G..
008e0000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
另一方面,jCC60mutgoE.img:008c0000
突然从字节0变成疑似 PDF 中间部分的内容,这里应该也是新 stripe 的起点:
008bfff0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
008c0000: 3030 3020 6e20 0a30 3030 3030 3736 3336 000 n .000007636
以上是两个例子,实际上每个位置我都看了好几个文件相互验证。总之最后得到这两个 stripe 起点位置,相减得到 stripe size 最多是 128K,也可能是 64K、32K…… 然后,依然通过文件连续性来排除其他可能。比如 1GHGGrmaMM0.img:008d0000
这里文件是连续的,就可以排除 64K:
008cfff0: 2031 3239 3420 3020 5220 3132 3935 2030 1294 0 R 1295 0
008d0000: 2052 0a31 3239 3620 3020 5220 3132 3937 R.1296 0 R 1297
类似方法排除其他 stripe size,最终确定 RAID stripe size 是128K。
接下来是判断盘的顺序。观察每个盘的 stripe #70(008c0000~008dffff
) 的开始和末尾的内容:
for f in *.img; do echo $f; xxd -s 0x008c0000 -l 131072 $f | sed -e 1b -e '$!d'; done
1GHGGrmaMM0.img
008c0000: 324a b3e3 6006 edee 4844 8ee8 e8dc 7f6b 2J..`...HD.....k
008dfff0: 5375 6274 7970 6520 2f4c 696e 6b20 2f52 Subtype /Link /R
5qiSQnlrA4Y.img
008c0000: 6563 7420 5b31 3138 2e33 3632 3520 3233 ect [118.3625 23
008dfff0: 675f 7379 7374 656d 5f72 6573 706f 6e73 g_system_respons
d3Be7V0EVKo.img
008c0000: 6976 656e 6573 735f 756e 6465 725f 6c6f iveness_under_lo
008dfff0: b2e8 f53a cec3 c6d8 b2d6 62e5 3cd6 644c ...:......b.<.dL
eRL2MQSdOjo.img
008c0000: b171 2c10 5b04 51ab b342 ce16 b301 59a6 .q,.[.Q..B....Y.
008dfff0: 3034 3730 3739 3620 3030 3030 3020 6e20 0470796 00000 n
ID7sM2RWkyI.img
008c0000: fbf8 67fd 8162 b363 d044 5cd7 1afb 4707 ..g..b.c.D\...G.
008dfff0: 46a1 4f02 d9fe 5c69 e7df ec6f e647 95a8 F.O...\i...o.G..
jCC60mutgoE.img
008c0000: 3030 3020 6e20 0a30 3030 3030 3736 3336 000 n .000007636
008dfff0: 4dfe 74a7 70e4 8380 562b 912c 3daa 32a4 M.t.p...V+.,=.2.
RApjvIxRlu0.img
008c0000: 0a30 3030 3034 3730 3530 3520 3030 3030 .0000470505 0000
008dfff0: 34a7 831e b625 e8d4 b9e6 ecec 2c95 6949 4....%......,.iI
wlOUASom2fI.img
008c0000: 3020 5220 2f42 6f72 6465 7220 5b20 3020 0 R /Border [ 0
008dfff0: 6e20 0a30 3030 3030 3736 3631 3020 3030 n .0000076610 00
已经很容易看出有几个是连着的了(此处建议自己找一些 PDF 文件找相似的内容比对):
wlOUASom2fI.img -> jCC60mutgoE.img
1GHGGrmaMM0.img -> 5qiSQnlrA4Y.img -> d3Be7V0EVKo.img
eRL2MQSdOjo.img -> RApjvIxRlu0.img
同样方法看上一个 stripe #69(008a0000~008bffff
),只有 ID7sM2RWkyI.img
是有内容的,其余几个都是全 0:
ID7sM2RWkyI.img
008a0000: 6174 7461 6368 2031 3635 350a 0000 0000 attach 1655.....
008bfff0: 3020 6f62 6a0a 3c3c 202f 4120 3438 3920 0 obj.<< /A 489
并且会发现,这里接的下一个 stripe 很可能是 wlOUASom2fI.img:008c0000
(/A 489 0 R
),所以 ID7sM2RWkyI.img
排最后一个,wlOUASom2fI.img
排第一个。
以上信息足够拼出第一个 PDF 文件了。三个 PDF 头只有 BOF1 是在 stripe #69 并且正是在 ID7sM2RWkyI.img
。如果文件连续存储的话,它对应的文件尾只可能是 EOF1,在 jCC60mutgoE.img
。这里可以恢复出来 PDF1:
$ dd if=ID7sM2RWkyI.img ibs=1 skip=9101312 count=73728 > 1.pdf
$ dd if=wlOUASom2fI.img ibs=1 skip=9175040 count=131072 >> 1.pdf
$ dd if=jCC60mutgoE.img ibs=1 skip=9175040 count=13998 >> 1.pdf
另外两组 BOF/EOF 如何配对也非常明显了:PDF2 是 BOF2 - EOF2,PDF3 是 BOF3 - EOF3。这里看 PDF3,文件头在 RApjvIxRlu0.img
,下一个盘还不确定,但不妨猜一下是 ID7sM2RWkyI.img
(然后进入 wlOUASom2fI.img
的下一个 stripe 到 EOF)。试着恢复文件,然后用任意 PDF 阅读器打开,验证的确是个有效的 PDF 文件:
$ dd if=RApjvIxRlu0.img ibs=1 skip=9248768 count=57344 > 2.pdf
$ dd if=ID7sM2RWkyI.img ibs=1 skip=9175040 count=131072 >> 2.pdf
$ dd if=wlOUASom2fI.img ibs=1 skip=9175040 count=17006 >> 2.pdf
至此已经推断出了盘顺序:
wlOUASom2fI.img
jCC60mutgoE.img
1GHGGrmaMM0.img
5qiSQnlrA4Y.img
d3Be7V0EVKo.img
eRL2MQSdOjo.img
RApjvIxRlu0.img
ID7sM2RWkyI.img
以下脚本重组 RAID:
images = ["wlOUASom2fI.img", "jCC60mutgoE.img", "1GHGGrmaMM0.img", "5qiSQnlrA4Y.img", "d3Be7V0EVKo.img", "eRL2MQSdOjo.img", "RApjvIxRlu0.img", "ID7sM2RWkyI.img"]
block_size = 128 * 1024
pos = 0
with open('output.img', 'wb') as fout:
while True:
for img in images:
with open(img, 'rb') as fin:
fin.seek(pos)
data = fin.read(block_size)
if len(data) == 0:
exit(0)
fout.write(data)
pos += block_size
挂载重组的磁盘镜像,拿 flag:
$ losetup -P loop0 output.img
$ mount /dev/loop0p1 /mnt/temp
$ cd /mnt/temp && python3 getflag.py
这题其实更容易。扫一下内容就会发现有很多文本文件(比如 GRUB 代码)。这样看哪个盘乱码就很容易识别出来哪块盘在做 parity。
比如我们看 byte 00400000 前后 16 字节(之所以选这个是因为这么整的地址基本肯定是个新 stripe 的起点):
$ for f in *.img; do echo $f; xxd -s $((0x00400000-16)) -l 32 $f; done
3D8qN9DH91Q.img
003ffff0: 4453 3b20 6a2b 2b29 0a20 2020 206f 7265 DS; j++). ore
00400000: 635f 6374 5f46 5245 4554 5950 453d 2461 c_ct_FREETYPE=$a
3RlmViivyG8.img
003ffff0: 735f 6563 686f 5f6e 2022 6368 6563 6b69 s_echo_n "checki
00400000: 594a 565e 393c 6a72 3760 3a2c 620a 4726 YJV^9<jr7`:,b.G&
60kE0MQisyY.img
003ffff0: 7564 6520 3c69 6f2e 683e 0a23 2065 6e64 ude <io.h>.# end
00400000: 745f 666c 6f61 743d 2224 6361 6e64 225d t_float="$cand"]
IrYp6co7Gos.img
003ffff0: 6c4c 5a00 6148 631d 4813 634b 0007 1348 lLZ.aHc.H.cK...H
00400000: 6e67 2066 6f72 206f 7074 696f 6e73 2074 ng for options t
output.img
003ffff0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00400000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
QjTgmgmwXAM.img
003ffff0: 2e24 6163 5f65 7874 0a2f 2a20 656e 6420 .$ac_ext./* end
00400000: 202d 7320 6669 6c65 2064 6972 2720 616e -s file dir' an
test.img
003ffff0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00400000: 0000 0000 0000 0000 0000 0000 0000 0000 ................
这里很明显看出,在前一个 stripe IrYp6co7Gos.img
做 parity,然后新的 stripe 轮到 3RlmViivyG8.img
做 parity。
接下来试试 +32K、+64K、+128K…… 看看哪里是下一个 stripe 起点,就可以得到 stripe size 了(64K)。
后面就找规律了,一个 stripe 一个 stripe 往下看,结合文件连续性,很快就能得到盘的顺序,这里不再赘述过程。重组脚本如下:
import itertools
images = ["3RlmViivyG8.img", "IrYp6co7Gos.img", "3D8qN9DH91Q.img", "QjTgmgmwXAM.img", "60kE0MQisyY.img"]
block_size = 64 * 1024
pos = 0
with open('output.img', 'wb') as fout:
while True:
images.insert(0, images.pop())
for j, img in enumerate(images):
with open(img, 'rb') as fin:
fin.seek(pos)
data = fin.read(block_size)
if len(data) == 0:
exit(0)
if j != 0:
fout.write(data)
pos += block_size