有一道 CTF 题是这样的:

Crypto/加密的压缩包
加密的压缩包,提交flag格式。flag{xxx}
flag.zip

其中 flag.zip 大小为 620 字节,可以用以下方法生成:

$ cat a.sh
base64 -d >flag.zip <<!
UEsDBAoAAQgAAOmabk663hBLEgAAAAYAAAAFAAAAMS50eHToxH9inXBNs6uDcY3VXJWmq9tQSwME
CgABCAAAXZtuTnqg2B8SAAAABgAAAAUAAAAyLnR4dCLGT5/ZylqRHEyimeT9qmEXrFBLAwQKAAEI
AABgm25OjOH35xIAAAAGAAAABQAAADMudHh0SBuBnBBBbVfzy62glPY08azuUEsDBAoAAQgAAL2b
bk7eD5SIMgAAACYAAAAIAAAAZmxhZy50eHRpwGS+iMMmvZMLGkAQVnsZy/iJiDsbim54zK75keik
4DBi8muo+/j0YFWpBMeSHQS7llBLAQI/AAoAAQgAAOmabk663hBLEgAAAAYAAAAFACQAAAAAAAAA
IAgAAAAAAAAxLnR4dAoAIAAAAAAAAQAYAEfdCFJY2tQBrFDmt1fa1AGsUOa3V9rUAVBLAQI/AAoA
AQgAAF2bbk56oNgfEgAAAAYAAAAFACQAAAAAAAAAIAgAADUAAAAyLnR4dAoAIAAAAAAAAQAYADe7
nNVY2tQBENzuVlja1AEQ3O5WWNrUAVBLAQI/AAoAAQgAAGCbbk6M4ffnEgAAAAYAAAAFACQAAAAA
AAAAIAgAAGoAAAAzLnR4dAoAIAAAAAAAAQAYALna8dZY2tQBBaWPe1ja1AEFpY97WNrUAVBLAQI/
AAoAAQgAAL2bbk7eD5SIMgAAACYAAAAIACQAAAAAAAAAIAgAAJ8AAABmbGFnLnR4dAoAIAAAAAAA
AQAYAFSPDEBZ2tQB0cEh41ja1AEFpY97WNrUAVBLBQYAAAAABAAEAF8BAAD3AAAAAAA=
!
$ sh a.sh

这个 flag.zip 的内容是:

Path = flag.zip
Type = zip
Physical Size = 620

   Date      Time    Attr   Size   Compressed  Name
------------------- ----- ------ ------------  --------
2019-03-14 19:23:16 ....A      6           18  1.txt
2019-03-14 19:26:57 ....A      6           18  2.txt
2019-03-14 19:26:59 ....A      6           18  3.txt
2019-03-14 19:29:56 ....A     38           50  flag.txt
------------------- ----- ------ ------------  --------
2019-03-14 19:29:56           56          104  4 files

我试了各种方法,都没有解出这道题。

有谁能够告诉我这道题的解题思路?

感谢卢涛老师的评论。

解题过程

如前所述,flag.zip 包含以下 4 个文件:

  • 1.txt, 6 bytes
  • 2.txt, 6 bytes
  • 3.txt, 6 bytes
  • flag.txt, 38 bytes

我们使用以下命令得到它们的 CRC32:

$ 7z l -slt flag.zip | grep CRC
CRC = 4B10DEBA
CRC = 1FD8A07A
CRC = E7F7E18C
CRC = 88940FDE

注意,这些 CRC32 是原始的明文文件的 CRC32。我写了一个 C# 程序来暴力破解前 3 个文本文件。

$ csc crackcrc32.cs ~/src/apps/lib/Crc32.cs
$ date; time mono crackcrc32.exe 4B10DEBA
$ date; time mono crackcrc32.exe 1FD8A07A
$ date; time mono crackcrc32.exe E7F7E18C
$ 7z x -pWe1c0meT0CTF flag.zip
$ cat flag.txt
flag{592b7e16bb42d046e1e85fecb9c9e6e5}

这里的难点是选择明文的能够包含哪些字符:纯数字、大小写英文字母、空格及其它符号、回车换行? 包含的范围小了,得不到结果,包含的范围大子,得到太多结果。 最终,我选择了 base64。此时:

  • CRC32 总数(4位):2564 = 4,294,967,296
  • base64 总数(6位):656 = 75,418,890,625

如果 CRC32 是均匀分布的话,平均结果数是 17.6。

运行结果表明文本文件的范围不是 base64,而是大小写英文字母加数字,不过这是 base64 的子集,所以没有问题。并且也多耗费不了多少时间,因为只多出 3 个字符。

运行结果

其中标有 * 号的是我们需要的结果,标有 # 号的是更早的尝试结果,不是本次程序的运行结果。

1.txt: 4B10DEBA 6hour6min
67-67-72-76-0D-0A ggrv   #
6C-4D-6F-66-42-32 lMofB2
53-31-44-71-2F-33 S1Dq/3
42-4D-43-6F-45-39 BMCoE9
78-35-34-48-32-42 x54H2B
6A-38-5A-44-77-44 j8ZDwD
69-49-33-56-58-48 iI3VXH
64-37-45-74-4B-4A d7EtKJ
37-38-43-67-62-4B 78CgbK
39-7A-71-6A-33-4D 9zqj3M
44-38-76-4D-70-4F D8vMpO
68-49-72-67-43-51 hIrgCQ
5A-4B-2F-52-3D-52 ZK/R=R
79-78-58-44-44-53 yxXDDS
57-78-74-4D-43-58 WxtMCX
49-5A-4F-33-62-59 IZO3bY
6E-50-34-68-72-63 nP4hrc
6D-6C-70-47-30-67 mlpG0g
51-61-32-42-72-6A Qa2Brj
31-3D-4A-34-52-6D 1=J4Rm
41-50-59-50-6E-71 APYPnq
50-61-73-73-69-73 Passis *
52-41-39-31-31-7A RA911z
6F-50-75-59-69-7A oPuYiz
31-4D-76-45-57-3D 1MvEW=

2.txt: 1FD8A07A 5hour33min
6D-4C-24-25-2B-0A mL$%+  #
3C-2E-45-49-37-0A <.EI7  #
50-5D-68-4D-73-0A P]hMs  #
68-38-44-64-34-20 h8Dd4  #
66-66-39-35-64-32 ff95d2
55-35-47-50-6D-34 U5GPm4
38-5A-64-39-33-39 8Zd939
6D-6D-7A-35-42-46 mmz5BF
73-4F-41-4B-63-47 sOAKcG
6C-71-74-58-58-4B lqtXXK
6D-71-35-69-43-52 mq5iCR
51-31-5A-51-6C-57 Q1ZQlW
7A-59-59-45-75-62 zYYEub
68-54-37-49-30-64 hT7I0d
54-59-75-4C-72-69 TYuLri
74-56-46-75-49-6C tVFuIl
57-65-31-63-30-6D We1c0m *
65-67-6C-56-4E-6E eglVNn
36-68-6A-45-67-6F 6hjEgo
79-79-52-36-36-72 yyR66r
56-65-70-52-2B-74 VepR+t
6A-68-32-57-69-79 jh2Wiy
46-38-68-6D-33-2B F8hm3+
59-4B-70-43-65-2F YKpCe/

3.txt: E7F7E18C 5hour2min
79-3A-32-52-29-0A y:2R)  #
28-58-53-3E-35-0A (XS>5  #
65-75-6E-53-3D-0A eunS=  #
58-64-22-3B-65-0A Xd";e  #
44-2B-7E-3A-71-0A D+~:q  #
31-63-6F-7E-3E-20 1co~>  #
6D-63-37-6C-30-36 mc7l06
34-36-51-2F-48-46 46Q/HF
65-54-30-43-54-46 eT0CTF *
37-47-38-3D-67-4A 7G8=gJ
5A-65-36-69-54-4F Ze6iTO
39-54-68-51-5A-50 9ThQZP
55-6A-68-68-73-58 UjhhsX
6E-2F-4F-32-77-62 n/O2wb
41-33-6D-56-6A-64 A3mVjd
31-42-31-6E-57-6C 1B1nWl
52-73-6F-56-59-73 RsoVYs
63-4D-76-4C-65-74 cMvLet
4D-3D-66-34-67-2F M=f4g/

源程序

crackcrc32.cs:

using System;
using System.Text;
using System.Collections.Generic;
using Skyiv.Utils;

static class CrackCrc32
{
  static uint z;
  static byte[] a;
  static byte[] bytes;
  static Encoding ascii;

  static CrackCrc32()
  {
    var s = new List<byte>();
    for (var i = '0'; i <= '9'; i++) s.Add((byte)i);
    for (var i = 'A'; i <= 'Z'; i++) s.Add((byte)i);
    for (var i = 'a'; i <= 'z'; i++) s.Add((byte)i);
    s.Add((byte)'+');
    s.Add((byte)'/');
    s.Add((byte)'=');
    a = s.ToArray();
    bytes = new byte[6];
    ascii = new ASCIIEncoding();
  }

  static void Make(int i)
  {
    if (i < 0) {
      if (z == bytes.GetCrc32())
        Console.WriteLine("{0} {1}",
          BitConverter.ToString(bytes),
          ascii.GetString(bytes));
      return;
    }
    foreach (var j in a) {
      bytes[i] = (byte)j;
      Make(i - 1);
    }
  }

  static void Main(string[] args)
  {
    z = Convert.ToUInt32(args[0], 16);
    Console.WriteLine("Crack Crc32 (Base64): {0:X}", z);
    Make(bytes.Length - 1);
  }
}

Crc32.cs

// https://rosettacode.org/wiki/CRC-32#C.23
using System;
using System.Linq;
using System.Collections.Generic;

namespace Skyiv.Utils
{
  public static class Crc32
  {
    const uint s = 0xEDB88320;
    static readonly uint[] t;

    static Crc32()
    {
      t = Enumerable.Range(0, 256).Select(i => {
        var z = (uint)i;
        for (var j = 0; j < 8; ++j)
          z = ((z & 1) != 0) ? (s ^ (z >> 1)) : (z >> 1);
        return z;
      }).ToArray();
    }

    public static uint GetCrc32<T>(this IEnumerable<T> bytes)
    {
      return ~bytes.Aggregate(0xFFFFFFFF, (r, b) =>
        (t[(r & 0xFF) ^ Convert.ToByte(b)] ^ (r >> 8)));
    }
  }
}