This page looks best with JavaScript enabled

Flare-On 8 2021 Challenge 3 Solution - 03_antioch

Hosted by FireEye's FLARE team from 10 September - 22 October

 ·  ☕ 10 min read  ·  🌚 drome

Thanks drome for sharing his knowledge and skills! He completed all 10 challenges and this series of writeups is done by him :)

Details Links
Official Challenge Site https://flare-on.com/
Official Challenge Announcement https://www.fireeye.com/blog/threat-research/2021/08/announcing-the-eighth-annual-flare-on-challenge.html
Official Solutions https://www.mandiant.com/resources/flare-on-8-challenge-solutions
Official Challenge Binaries http://flare-on.com/files/Flare-On8_Challenges.zip

03_antioch

To solve this challenge, you’ll need to …AAARGH
7-zip password: flare

Docker File System Extraction

After unzipping the given 7z file, we get a tar file called antioch.tar. In the tar file we have a bunch of folders titled with hashes and a manifest.json and repositories files

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$ tar -tf antioch.tar 
09e6fff53d6496d170aaa9bc88bd39e17c8e5c13ee9066935b089ab0312635ef/
09e6fff53d6496d170aaa9bc88bd39e17c8e5c13ee9066935b089ab0312635ef/VERSION
09e6fff53d6496d170aaa9bc88bd39e17c8e5c13ee9066935b089ab0312635ef/layer.tar
...
fd8bf3c084c5dd42159f9654475f5861add943905d0ad1d3672f39e014757470/VERSION
fd8bf3c084c5dd42159f9654475f5861add943905d0ad1d3672f39e014757470/layer.tar
fd8bf3c084c5dd42159f9654475f5861add943905d0ad1d3672f39e014757470/json
manifest.json
repositories

We have to run the following commands to extract the file system out of this image

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
$ cat antioch.tar | docker load
...
Loaded image: antioch:latest
$ docker image ls
REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
antioch      latest    a13ffcf46cf4   2 months ago   13kB
$ docker run antioch:latest
AntiochOS, version 1.32 (build 1975)
Type help for help
$ docker container ls --all
CONTAINER ID   IMAGE            ...   PORTS     NAMES
f174b3802ce5   antioch:latest   ...             compassionate_wescoff
$ docker export compassionate_wescoff > fs.tar

After that we untar the file and find that it has the following structure

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
$ tar -tf fs.tar
.dockerenv
AntiochOS
dev/
dev/console
dev/pts/
dev/shm/
etc/
etc/hostname
etc/hosts
etc/mtab
etc/resolv.conf
proc/
sys/

AntiochOS is an ELF file, so we put it into IDA and start analysis.

Analysis

In the start function, the program prints some version info, AntiochOS, version 1.32 (build 1975), then reads a command from the user and calls a function accordingly. The available commands are quit, help, approach, and consult. The first two functions are self explanatory, while the other two require closer reversing.

approach

The function for approach is in sub_401640, where it would ask for a name from the user, then calculate the CRC32 checksum of that name with a trailing newline (sub_401B50), then compare the name against an array of checksums at 0x402000. If it matches the checksum of one of the elements, it would ask for the user’s quest (this is ignored), then ask for a favorite color from the user, check the CRC32 checksum of the color (also with a trailing newline) against the array, and if it matches, it would return a number from the table between 1 and 30 inclusive.

The table is an array of 30 structs, which we name as the hash_struct. Each hash_struct has 3 DWORDs inside, representing the name checksum, color checksum, and corresponding number returned if successful.

Administrator Verification Form Webpage

We checked the original Docker tar file, and found that there are 31 subdirectories inside, 30 of them containing layers with .dat files, and 1 containing the ELF file (7016b6). All the folders with the .dat files have author names in their json file which match the CRC32 values in the hash table, and we were able to match all 30 hash_structs to their author names.

To find the color names, we compiled all the colors from Wikipedia, and calculated their CRC32 checksums, but even then there were a few missing, namely 593028265, 3136983525, 4008569559, 3035233584, and 3194411288.

We saw more success when we used the wordlist from /usr/share/dict/words instead, which got us the colors of all 30 color CRC32 checksums, which looks like this:

[b'Bridge Keeper', b'Indigo', 14],
[b'Sir Lancelot', b'Blue', 18],
[b'Sir Bors', b'Coral', 2],
[b'Black Knight', b'Black', 29],
[b'Chicken of Bristol', b'Mint', 12],
[b'Roger the Shrubber', b'Tomato', 13],
[b'Rabbit of Caerbannog', b'Salmon', 20],
[b'Trojan Rabbit', b'Beige', 11],
[b'Dinky', b'Turquoise', 28],
[b'Sir Not-Appearing-in-this-Film', b'Transparent', 21],
[b'Brother Maynard', b'Crimson', 5],
[b'Inspector End Of Film', b'Gray', 24],
[b'Sir Ector', b'Bisque', 25],
[b'Sir Robin', b'Red', 7],
[b'Green Knight', b'Green', 10],
[b'Miss Islington', b'Brown', 1],
[b'Lady of the Lake', b'Gold', 19],
[b'Tim the Enchanter', b'Orange', 3],
[b'Dragon of Angnor', b'Khaki', 4],
[b'A Famous Historian', b'Pink', 17],
[b'Squire Concorde', b'Periwinkle', 9],
[b'Zoot', b'Tan', 8],
[b'Dennis the Peasant', b'Orchid', 27],
[b'Legendary Black Beast of Argh', b'Silver', 16],
[b'Prince Herbert', b'Wheat', 22],
[b'Sir Gawain', b'Azure', 15],
[b'Sir Gallahad', b'Yellow', 30],
[b'King Arthur', b'Purple', 23],
[b'Squire Patsy', b'Chartreuse', 26],
[b'Sir Bedevere', b'Teal', 6],

However, the approach function didn’t do anything else other than giving us the corresponding number, so we looked into the other function consult for more clues.

consult

The function for consult is in sub_401460. This function reads in all files from a.dat to z.dat, which are all 0x1000 bytes long, then xors their contents together, then use the result as indices in a character array at 0x404100 to generate some sort of ASCII art that is printed out.

We tried extracting the .dat files in one of the Docker layers, then placing the ELF file in the same folder and running consult, which prints out some string that looks like ASCII art but is unintelligible.

Back to Docker

The approach function clearly gives us some order of the layer folders according to their authors' order numbers in the array, and the consult function suggests that we should get some ASCII art after retrieving and processing the correct .dat files. To find out how the layer folders are supposed to work together to produce the correct .dat files, we went to read up about how Docker image layers work.

Docker image layers (the 31 subdirectories in the tar file) are intermediate images used by Docker to cache intermediate stages so that it can rebuild images faster when small changes are made. The layers store the filesystem diffs made by the instructions in the Dockerfile, and the entire file is in each layer whenever there’s an add or change.

We initially tried to fix the saved docker image tar file to make it include the last layer, but found it very difficult as we didn’t understand how the docker save function works in depth.

Instead, we used the order of layers provided by the approach functions, and took the .dat file in the layer with a higher ordinal number as the more updated one, then used the processing method which we reversed from consult on all the updated .dat files, which gave us the flag in ASCII art.

Solution Script

  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
import os
import json
import tarfile
import zlib

ascii_chars = [
  0x56, 0x27, 0x2C, 0x60, 0x29, 0x28, 0x2F, 0x2F, 0x5C, 0x5C, 0x5C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C,
  0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F,
  0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
  0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E, 0x2E,
]

crc32_list = [
  [0x0B59395A9, 0x1BB5AB29, 0x0E],
  [0x5EFDD04B, 0x3F8468C8, 0x12],
  [0x0ECED85D0, 0x82D23D48, 0x2],
  [0x0D8549214, 0x472EE5, 0x1D],
  [0x2C2F024D, 0x0C9A060AA, 0x0C],
  [0x18A5232, 0x24D235, 0x0D],
  [0x72B88A33, 0x81576613, 0x14],
  [0x674404E2, 0x5169E129, 0x0B],
  [0x307A73B5, 0x0E560E13E, 0x1C],
  [0x13468704, 0x2358E4A9, 0x15],
  [0x94F6471B, 0x0D6341A53, 0x5],
  [0x0EDA1CF75, 0x0BAFA91E5, 0x18],
  [0x0BBAC124D, 0x0A697641D, 0x19],
  [0x0F707E4C3, 0x0EF185643, 0x7],
  [0x0D702596F, 0x79C28915, 0x0A],
  [0x86A10848, 0x59108FDC, 0x1],
  [0x0D640531C, 0x0EF3DE1E8, 0x13],
  [0x7B665DB3, 0x0A3A903B0, 0x3],
  [0x0AB1321CC, 0x0EEEDEAD7, 0x4],
  [0x4F6066D8, 0x9C8A3D07, 0x11],
  [0x256047CA, 0x4085BE9E, 0x9],
  [0x3FC91ED3, 0x379549C9, 0x8],
  [0x0A424AFE4, 0x0EF871347, 0x1B],
  [0x550901DA, 0x1FCEC6B, 0x10],
  [0x10A29E2D, 0x0E76056AA, 0x16],
  [0x56CBC85F, 0x356F1A68, 0x0F],
  [0x80DFE3A6, 0x9D0AB536, 0x1E],
  [0x0E657D4E1, 0x0B4E9FD30, 0x17],
  [0x2BA1E1D4, 0x0BE66D918, 0x1A],
  [0x7D33089B, 0x67C1F585, 0x6],
]

# Color identification: not strictly necessary

with open('/usr/share/dict/words', 'r') as fp:
  colors = fp.read().split('\n')

colors = [color.capitalize().encode() for color in colors]

crc32_to_color = {zlib.crc32(color + b'\n'):color for color in colors}

# Layer folder identification from name
# Extract the docker tar file into a folder called 'antioch' first

names = []
names_to_folder = {}

for subdir, dirs, files in os.walk(r'antioch'):
  for filename in files:
    if filename == 'json':
      with open(os.path.join(subdir, filename)) as fp:
        obj = json.load(fp)
      if 'author' in obj:
        names.append(obj['author'].encode())
        names_to_folder[obj['author'].encode()] = subdir

crc32_to_folder = {zlib.crc32(name + b'\n'):names_to_folder[name] for name in names}

# Replacing CRC32 hashes with folder name and color

for li in crc32_list:
  li[0] = crc32_to_folder[li[0]]
  li[1] = crc32_to_color[li[1]]

# Getting latest .dat files

crc32_list.sort(key=lambda li:li[2])

dat_file_contents = {}

for li in crc32_list:
  path = li[0]
  tar = tarfile.open(os.path.join(path, 'layer.tar'))
  for member in tar.getmembers():
    f = tar.extractfile(member)
    dat_file_contents[member.name] = f.read()

# Getting ascii art from .dat files

ascii_data = [0] * 0x1000
for data in dat_file_contents.values():
  for i, b in enumerate(data):
    ascii_data[i] ^= b
ascii_data = [ascii_chars[b & 0xFF] for b in ascii_data]

s = bytes(ascii_data)
ans = b''
for i in range(0, 0x1000, 0x10):
  ans += (s[i:i+0xf])
  ans += b'\n'

print(ans.decode('utf-8'))

which gives the following output:

...............
...............
...............
...............
...............
...............
...............
...............
...............
....______.....
...|..____|....
...|.|__.......
...|..__|......
...|.|.........
...|_|.........
...............
...............
...._..........
...(_).........
...._..........
...|.|.........
...|.|.........
...|_|.........
...............
...............
...............
...............
...__...__.....
...\.\././.....
....\.V./......
.....\_/.......
...............
...............
...............
...............
.....___.......
..../._.\......
...|..__/......
....\___|......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
...._____......
...|_..._|.....
.....|.|.......
.....|.|.......
...._|.|_......
...|_____|.....
...............
...............
...............
...............
....___........
.../.__|.......
...\__.\.......
...|___/.......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
...._____......
...|..__.\.....
...|.|__).|....
...|.._../.....
...|.|.\.\.....
...|_|..\_\....
...............
...............
...._..........
...(_).........
...._..........
...|.|.........
...|.|.........
...|_|.........
...............
...............
...............
...............
.....__._......
..../._`.|.....
...|.(_|.|.....
....\__,.|.....
.....__/.|.....
....|___/......
...._..........
...|.|.........
...|.|__.......
...|.'_.\......
...|.|.|.|.....
...|_|.|_|.....
...............
...............
...._..........
...|.|.........
...|.|_........
...|.__|.......
...|.|_........
....\__|.......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
.....____......
..../.__.\.....
...|.|..|.|....
...|.|..|.|....
...|.|__|.|....
....\____/.....
...............
...............
...............
...............
...._..._......
...|.|.|.|.....
...|.|_|.|.....
....\__,_|.....
...............
...............
...._..........
...|.|.........
...|.|_........
...|.__|.......
...|.|_........
....\__|.......
...............
...............
...............
......____.....
...../.__.\....
...././._`.|...
...|.|.(_|.|...
....\.\__,_|...
.....\____/....
...............
.....__........
..../._|.......
...|.|_........
...|.._|.......
...|.|.........
...|_|.........
...............
...............
...._..........
...|.|.........
...|.|.........
...|.|.........
...|.|.........
...|_|.........
...............
...............
...............
...............
.....__._......
..../._`.|.....
...|.(_|.|.....
....\__,_|.....
...............
...............
...............
...............
...._.__.......
...|.'__|......
...|.|.........
...|_|.........
...............
...............
...............
...............
.....___.......
..../._.\......
...|..__/......
....\___|......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
...............
...............
.....___.......
..../._.\......
...|.(_).|.....
....\___/......
...............
...............
...............
...............
...._.__.......
...|.'_.\......
...|.|.|.|.....
...|_|.|_|.....
...............
...............
...............
...............
...............
...............
...._..........
...(_).........
...............
...............
...............
...............
.....___.......
..../.__|......
...|.(__.......
....\___|......
...............
...............
...............
...............
.....___.......
..../._.\......
...|.(_).|.....
....\___/......
...............
...............
...............
...............
...._.__.___...
...|.'_.`._.\..
...|.|.|.|.|.|.
...|_|.|_|.|_|.
...............
...............
...............
...............
...............
...............
...............
...............
...............

Flag

Five-Is-Right-Out@flare-on.com
Share on