Write-Up: Misc -
Appnote.txt Google CTF 2022
题目
You are given an archive file that contains the flag somehow, but
conventionally unpacking it does not lead to anything useful.
直接用二进制编辑器打开文件,发现:
hello.txt 里面是 “There’s more to it than meets the
eye…”
`hi.txt 写着 “Find a needle in the haystack…”
(大海捞针)然后是一些形如 flagXX 的文件。XX 在 00 和 18
之间,每个文件重复36次。提取全部txt内部的字符后,得到字符串:“abcdefghijklmnopqrstuvwxyz{CTF0137}_”
.
这个字符串跟flag并无太大关联,但给了我们一些线索。我们注意到ZIP文件中还存在着其他信息。
EOCDs 的 hexdump:
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 0000eecc : 504 b 0506 0000 0000 0100 0100 00 ee 0000 PK..............0000eedc : cc00 0000 b801 504 b 0506 0000 0000 0100 ......PK........0000eeec : 0100 5 ae4 0000 880 a 0000 a201 504 b 0506 ..Z.........PK..0000eefc : 0000 0000 0100 0100 93 d7 0000 6517 0000 ............e...0000ef0c : 8 c01 504 b 0506 0000 0000 0100 0100 ccca ..PK............0000ef1c : 0000 4224 0000 7601 504 b 0506 0000 0000 ..B$..v.PK......0000ef2c : 0100 0100 69 bf 0000 bb2f 0000 6001 504 b ....i..../..`.PK0000ef3c : 0506 0000 0000 0100 0100 ceb6 0000 6 c38 ..............l80000ef4c : 0000 4 a01 504 b 0506 0000 0000 0100 0100 ..J.PK..........0000ef5c : 29 a5 0000 274 a 0000 3401 504 b 0506 0000 )...'J..4 .PK....0000ef6c : 0000 0100 0100 e79c 0000 7 f52 0000 1 e01 ...........R....0000ef7c : 504 b 0506 0000 0000 0100 0100 428 b 0000 PK..........B...0000ef8c : 3 a64 0000 0801 504 b 0506 0000 0000 0100 :d....PK........0000ef9c : 0100 2186 0000 7169 0000 f200 504 b 0506 ..!...qi....PK..0000efac : 0000 0000 0100 0100 7173 0000 377 c 0000 ........qs..7 |..0000efbc : dc00 504 b 0506 0000 0000 0100 0100 6670 ..PK..........fp0000efcc : 0000 587 f 0000 c600 504 b 0506 0000 0000 ..X.....PK......0000efdc : 0100 0100 e359 0000 f195 0000 b000 504 b .....Y........PK0000efec : 0506 0000 0000 0100 0100 ac52 0000 3 e9d ...........R..>.0000effc : 0000 9 a00 504 b 0506 0000 0000 0100 0100 ....PK..........0000f00c : a247 0000 5 ea8 0000 8400 504 b 0506 0000 .G..^.....PK....0000f01c : 0000 0100 0100 8 e33 0000 88 bc 0000 6 e00 .......3 ......n.0000f02c : 504 b 0506 0000 0000 0100 0100 9 a2a 0000 PK...........*..0000f03c : 92 c5 0000 5800 504 b 0506 0000 0000 0100 ....X.PK........0000f04c : 0100 161 c 0000 2 cd4 0000 4200 504 b 0506 ......,...B.PK..0000f05c : 0000 0000 0100 0100 3815 0000 20 db 0000 ........8 ... ...0000f06c : 2 c00 504 b 0506 0000 0000 0100 0100 2 f02 ,.PK........../.0000f07c : 0000 3 fee 0000 1600 504 b 0506 0000 0000 ..?.....PK......0000f08c : 0100 0100 34 f0 0000 5000 0000 0000 ....4 ...P.....
每个分区都以 “PK”
开始,然后是8个无用的字节,4个字节表示大小,4字节表示偏移(offset)。
然后,将其“合法编码”为适当的 ZIP文件的方式是,它声明所有的 EOCD
都在前一个文件的注释字段中。
除其他字段外,ECDR
包含中央目录的大小(以字节为单位)和从文件开头开始的偏移字节,两者都是 4
字节的小端整数。所以从偏移字节开始会找到对应的CDH。CDH有很多字段,包括本地头的相对偏移量,它再次给出了从文件开头到相应LFH的偏移量。此外,CDH
和 LFH 都将压缩方法字段设置为“”,即不压缩。LFH 之后是我们的数据。
中央目录记录结束 => 中央目录头 => 本地文件头 => 数据
如果使用我们找到的大小和偏移量,并从文件中的“分段”中提取文本,得到了一系列字符串:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 abcdefghijklmnopqrstuvwxyz{CTF0137}_ TF0137}_ F0137}_ 0137}_ abcdefghijklmnopqrstuvwxyz{CTF0137}_ CTF0137}_ qrstuvwxyz{CTF0137}_ 137}_ tuvwxyz{CTF0137}_ }_ nopqrstuvwxyz{CTF0137}_ 137}_ efghijklmnopqrstuvwxyz{CTF0137}_ 7}_ stuvwxyz{CTF0137}_ opqrstuvwxyz{CTF0137}_ {CTF0137}_ 37}_ qrstuvwxyz{CTF0137}_ _
现在我们可以肯定这就是flag了。 如果假设flag格式是
CTF{****} 的话,那么就有如下结论:
第一行是某种类似“字典”的转换规则
所有后面的行都代表标志的一个字符,这里是截断位置前的那一个字符
编写脚本来提取flag:
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 import re rex = b"flag[0-9][0-9](.)PK\x01\x02" rexf = b"PK\x05\x06[\x00-\xFF]{8}([\x00-\xFF]{4})([\x00-\xFF]{4})" rexm = b"(.)" patt = b"" first = True def collect (data ): global first, patt if first: for datum in re.findall(rex, data): patt += datum patt = bytes (patt[-1 ]) + patt first = False return "" datum = re.findall(rex, data)[0 ] return patt[patt.rfind(datum)-1 :patt.rfind(datum)].decode("ASCII" )with open ("dump.zip" , "rb" ) as dump: data = dump.read() for size_b, offset_b in re.findall(rexf, data): size = int .from_bytes(size_b, byteorder="little" ) offset = int .from_bytes(offset_b, byteorder="little" ) c = collect(data[offset:offset+size]) print (c, end="" ) if c == "}" : break print ()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 import re content = open ('dump.zip' ,'rb' ).read()def parse_int (b ): return int .from_bytes(b,'little' ) for m in re.finditer(b"PK\5\6" , content): i = m.start() cent_dir_offset= parse_int(content[i+16 :i+20 ]) print (hex (i)) local_header_offset= parse_int(content[cent_dir_offset+42 :cent_dir_offset+42 +4 ]) compressed_size = parse_int(content[local_header_offset+18 :local_header_offset+22 ]) file_name_length = parse_int(content[local_header_offset+26 :local_header_offset+28 ]) content_start = local_header_offset+30 +file_name_length comp_content = content[content_start:content_start+compressed_size] print (comp_content.decode("ascii" ),end="" )
最终的Flag是: CTF{p0s7m0d3rn_z1p}