FLARE-ON 2021 writeup

Writer:b1uef0x / Webページ建造途中

概要

個人参加で5問目まで解くことができた。6問目は歯が立たず。

目次

01 - credchecker (1 point)

Welcome to Flare-On 8! This challenge surves as your tutorial mission for the epic quest you are about to emark upon. Reverse engineer the Javascript code to determine the correct username and password the web page is looking for and it will show you the flag. Enter that flag here to advance to the next stage. All flags will be in the format of valid email addresses and all end with "@flare-on.com".

admin.htmlが配布される。UsernameとPasswordを入力して正しければflagが表示されるようだ。該当するスクリプト部分を確認する。

var form = document.getElementById("credform");
var username = document.getElementById("usrname");
var password = document.getElementById("psw");
var info = document.getElementById("infolabel");
var checkbtn = document.getElementById("checkbtn");
var encoded_key = "P1xNFigYIh0BGAofD1o5RSlXeRU2JiQQSSgCRAJdOw=="

function dataEntered() {
        if (username.value.length > 0 && password.value.length > 0) {
                checkbtn.disabled = false;
        } else {
                checkbtn.disabled = true;
        }
}

function checkCreds() {
        if (username.value == "Admin" && atob(password.value) == "goldenticket") 
        {
                var key = atob(encoded_key);
                var flag = "";
                for (let i = 0; i < key.length; i++)
                {
                        flag += String.fromCharCode(key.charCodeAt(i) ^ password.value.charCodeAt(i % password.value.length))
                }
                document.getElementById("banner").style.display = "none";
                document.getElementById("formdiv").style.display = "none";
                document.getElementById("message").style.display = "none";
                document.getElementById("final_flag").innerText = flag;
                document.getElementById("winner").style.display = "block";
        }
        else
        {
                document.getElementById("message").style.display = "block";
        }
}

Usernameは「Admin」、Passwordはgoldenticketをbase64エンコードしたものであることがわかるので、「Z29sZGVudGlja2V0」になる。UsernameとPasswordを入力するとflagが得られる。

enter_the_funhouse@flare-on.com

02 - known (1 point)

We need your help with a ransomware infection that tied up some of our critical files. Good luck.

ランサムウェアを題材とする問題。暗号化されたファイル群とファイルを復号化する実行ファイル「UnlockYourFiles.exe」が配布される。

UnlockYourFiles.exeの実行

「UnlockYourFiles.exe」を実行するとキーの入力が求められるので、適当に入力するとファイルの復号化が実行されるが、正しく復号されない。正しいキーを判別しているわけではなく、入力されたキーで復号化を行うだけのプログラムだ。

実行ファイルをGhidraに読ませて逆アセンブル及び逆コンパイルを行い、Entry部分を確認する。

undefined4 entry(void)

{
  uint uExitCode;
  undefined4 local_14;
  undefined4 local_10;
  DWORD local_c;
  HANDLE local_8;
  
  local_14 = 0;
  local_10 = 0;
  local_8 = GetStdHandle(0xfffffff6);
  DAT_00403844 = GetStdHandle(0xfffffff5);
  SetConsoleTextAttribute(DAT_00403844,0xce);
  WriteConsoleA(DAT_00403844,s_**********_Attention!_**********_00403000,0x70a,(LPDWORD)0x0,
                (LPVOID)0x0);
  ReadConsoleA(local_8,&local_14,8,&local_c,(PCONSOLE_READCONSOLE_CONTROL)0x0);
  uExitCode = FUN_00401370((int)&local_14);
  ExitProcess(uExitCode);
  return 0;
}

ReadConsoleA関数の部分から、キーの長さは8文字であることがわかる。続いて復号部分を確認する。

void __cdecl FUN_004011f0(int param_1,int param_2)

{
  byte bVar1;
  byte bVar2;
  uint uVar3;
  
  uVar3 = 0;
  while (bVar2 = (byte)uVar3, (char)bVar2 < '\b') {
    bVar1 = *(byte *)(uVar3 + param_1) ^ *(byte *)(uVar3 + param_2);
    *(char *)(uVar3 + param_1) = (bVar1 << (bVar2 & 7) | bVar1 >> 8 - (bVar2 & 7)) - bVar2;
    uVar3 = (uint)(byte)(bVar2 + 1);
  }
  return;
}

復号する関数がわかったので、暗号化されたファイル一覧を確認すると、PNGファイルを暗号化した「capa.png.encrypted」がある。

「capa.png.encrypted」の先頭8文字はC7 C7 25 1D 63 0D F3 56に対して、PNGファイルのシグネチャは先頭8文字が89 50 4E 47 0D 0A 1A 0Aとなるので、ここからキーを逆算できる。

JavaScriptで次の逆算プログラムを作成。

decrypt = function(key) {
        var enc= [0xc7,0xc7,0x25,0x1d,0x63,0xd,0xf3,0x56];
        var dec = []
        var i = 0;
        while (i<8) {
          xor = enc[i] ^ key[i];
          dec[i] = 255 & ((xor << (i & 7) | xor >> 8 - (i & 7)) - i);
          i++;
        }
        return dec;
}

key = [0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20];
png = [0x89,0x50,0x4e,0x47,0xd,0xa,0x1a,0xa];


for(i=0; i<8; i++) {
        for(j=0x20; j<0x7e; j++) {
                key[i] = j;
                decode = decrypt(key);
                if(decode[i]===png[i])break;
        }
}
i = 0;
for(i in key)document.write(String.fromCharCode(key[i]));

実行するとキーは「No1Trust」であることがわかる。正しいキーを入力してファイルを復号すると、critical_data.txtからflag入手。

You_Have_Awakened_Me_Too_Soon_EXE@flare-on.com

03 - antioch (1point)

To solve this challenge, you'll need to ...AAARGH

Dockerのコンテナが配布される。antioch.tarの中身を確認するとたくさんのフォルダとjsonが含まれる。

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021/09/11     13:26                09e6fff53d6496d170aaa9bc88bd39e17c8e5c13ee9066935b089ab0312635ef
d-----        2021/09/11     13:26                1c5d28d6564aed0316526e8bb2d79a436b45530d2493967c8083fea2b2e518ce
d-----        2021/09/11     13:26                25e171d6ac47c26159b26cd192a90d5d37e733eb16e68d3579df364908db30f2
d-----        2021/09/11     13:26                2b363180ec5d5862b2a348db3069b51d79d4e7a277d5cf5e4afe2a54fc04730e
d-----        2021/09/11     13:26                303dfd1f7447a80322cc8a8677941da7116fbf0cea56e7d36a4f563c6f22e867
d-----        2021/09/11     13:26                49fb821d2bf6d6841ac7cf5005a6f18c4c76f417ac8a53d9e6b48154b5aa1e76
d-----        2021/09/11     13:26                4c33f90f25ea2ab1352efb77794ecc424883181cf8e6644946255738ac9f5dbd
d-----        2021/09/11     13:26                58da659c7d1c5a0c3447cb97cd6ffb12027c734bfba32de8b9b362475fe92fae
d-----        2021/09/11     13:26                6b4e128697aa0459a6caba2088f6f77efaaf29d407ec6b58939c9bc7814688ad
d-----        2021/09/11     13:26                7016b68f19aed3bb67ac4bf310defd3f7e0f7dd3ce544177c506d795f0b2acf3
d-----        2021/09/11     13:26                754ee87063ee108c1f939cd3a28980a03b700f3c3967df8058831edad2743fd7
d-----        2021/09/11     13:26                76531a907cdecf03c8ac404d91cbcabd438a226161e621fab103a920600372a8
d-----        2021/09/11     13:26                7d643931f34d73776e9169551798e1c4ca3b4c37b730143e88171292dbe99264
d-----        2021/09/11     13:26                81f28623cca429f9914e21790722d0351737f8ad3e823619a4f7019be72e2195
d-----        2021/09/11     13:26                8e11477e79016a17e5cde00abc06523856a7db9104c0234803d30a81c50d2b71
d-----        2021/09/11     13:26                9a31bad171ad7e8009fba41193d339271fc51f992b8d574c501cae1bfa6c3fe2
d-----        2021/09/11     13:26                a2de31788db95838a986271665b958ac888d78559aa07e55d2a98fc3baecf6e6
d-----        2021/09/11     13:26                a435765bcd8745561460979b270878a3e7c729fae46d9e878f4c2d42e5096a44
d-----        2021/09/11     13:26                b5f502d32c018d6b2ee6a61f30306f9b46dad823ba503eea5b403951209fd59b
d-----        2021/09/11     13:26                b75ea3e81881c5d36261f64d467c7eb87cd694c85dd15df946601330f36763a4
d-----        2021/09/11     13:26                bfefc1bdf8b980a525f58da1550b56daa67bae66b56e49b993fff139faa1472c
d-----        2021/09/11     13:26                cd27ad9a438a7eef05f5b5d99e2454225693e63aba29ce8553800fed23575040
d-----        2021/09/11     13:26                cfd7ddb31ce44bb24b373645876ac7ea372da1f3f31758f2321cc8f5b29884fb
d-----        2021/09/11     13:26                e1a9333f9eccfeae42acec6ac459b9025fe6097c065ffeefe5210867e1e2317d
d-----        2021/09/11     13:26                e5254dec4c7d10c15e16b41994ca3cf0c5e2b2a56c9d4dc2ef053eeff24333ff
d-----        2021/09/11     13:26                e6c2557dc0ff4173baee856cbc5641d5b19706ddb4368556fcdb046f36efd2e2
d-----        2021/09/11     13:26                ea12384be264c32ec1db0986247a8d4b2231bf017742313c01b05a7e431d9c26
d-----        2021/09/11     13:26                f2ebdc667cbafc2725421d3c02babc957da2370fbd019a9e1993d8b0409f86dd
d-----        2021/09/11     13:26                f9621328166de01de73b4044edb9030b3ad3d5dbc61c0b79e26f177e9123d184
d-----        2021/09/11     13:26                fadf53f0ae11908b89dffc3123e662d31176b0bb047182bfec51845d1e81beb9
d-----        2021/09/11     13:26                fd8bf3c084c5dd42159f9654475f5861add943905d0ad1d3672f39e014757470
-a----        2021/07/23     12:21           1480 a13ffcf46cf41480e7f15c7f3c6b862b799bbe61e7d5909150d8a43bd3b6c039.json
-a----        1980/01/01      0:01            203 manifest.json
-a----        1980/01/01      0:01             90 repositories

manifest.jsonを見ると7016b68f19aed3bb67ac4bf310defd3f7e0f7dd3ce544177c506d795f0b2acf3/layer.tar"がレイヤーとして読まれているので、このlayer.tarの中身を見るとAntiochOSのLinux用実行ファイルが見つかる。

AntiochOSの実行

AntiochOSを実行するとコマンドラインで入出力を行うプログラムになっている。動作を調べるためにこれをGhidraで逆アセンブル及び逆コンパイルしてコードを調べると、入力文字列を分岐させるentryから呼び出されている次の4つの関数(ASCIIコードで文字列を作っている)から、quit、help、consult、approachのコマンドが確認できる。

void FUN_00401340(undefined *param_1)

{
  *param_1 = 0x71;
  param_1[1] = 0x75;
  param_1[2] = 0x69;
  param_1[3] = 0x74;
  param_1[4] = 10;
  param_1[5] = 0;
  return;
}

void FUN_00401360(undefined *param_1)

{
  *param_1 = 0x68;
  param_1[1] = 0x65;
  param_1[2] = 0x6c;
  param_1[3] = 0x70;
  param_1[4] = 10;
  param_1[5] = 0;
  return;
}

void FUN_00401380(undefined *param_1)

{
  *param_1 = 99;
  param_1[1] = 0x6f;
  param_1[2] = 0x6e;
  param_1[3] = 0x73;
  param_1[4] = 0x75;
  param_1[5] = 0x6c;
  param_1[6] = 0x74;
  param_1[7] = 10;
  param_1[8] = 0;
  return;
}

void FUN_004013b0(undefined *param_1)

{
  *param_1 = 0x61;
  param_1[1] = 0x70;
  param_1[2] = 0x70;
  param_1[3] = 0x72;
  param_1[4] = 0x6f;
  param_1[5] = 0x61;
  param_1[6] = 99;
  param_1[7] = 0x68;
  param_1[8] = 10;
  param_1[9] = 0;
  return;
}

helpは文字列を表示するだけ、quitはプログラムを終了するだけなので省略する。

consultを実行すると次の文字列を表示する。

> consult
Consult the Book of Armaments!
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
VVVVVVVVVVVVVVV
> 

現時点でconsultの正体はよくわからないので保留し、最後のapproachを実行してみる。

> approach
Approach the Gorge of Eternal Peril!
What is your name? test
...AAARGH

> 

名前を聞かれて誤ると終了するようだ。Ghidraでapproachのコマンドで実行される関数を確認すると次のものになっている。

undefined  [16] FUN_00401640(void)

{
  uint *puVar1;
  uint uVar2;
  byte bVar1;
  undefined1 *puVar3;
  ulong uVar4;
  ulong uVar3;
  undefined1 *puVar4;
  int iVar5;
  int *piVar6;
  uint *puVar7;
  uint *puVar8;
  ulong uVar9;
  bool bVar10;
  undefined auVar11 [16];
  undefined local_b9;
  undefined local_b8 [32];
  uint local_98 [32];
  
  uVar9 = 0;
  local_b9 = 10;
  puVar3 = FUN_00401260();
  FUN_004019f0(1,puVar3,0x25);
  FUN_00401a50();
  FUN_00401120(local_b8);
  FUN_004019f0(1,local_b8,0x13);
  auVar11 = FUN_00401a10(0,local_98,0x80);
  uVar4 = FUN_00401b50((byte *)local_98,SUB164(auVar11,0));
  piVar6 = &DAT_0040200c;
  iVar5 = -0x4a6c6a57;
  while (iVar5 != (int)uVar4) {
    _bVar1 = (int)uVar9 + 1;
    uVar9 = (ulong)_bVar1;
    if (_bVar1 == 0x1e) goto LAB_00401800;
    iVar5 = *piVar6;
    piVar6 = piVar6 + 3;
  }
  FUN_00401180(local_b8);
  FUN_004019f0(1,local_b8,0x14);
  auVar11 = FUN_00401a10(0,local_98,0x80);
  if (1 < SUB168(auVar11,0)) {
    FUN_004011e0(local_b8);
    FUN_004019f0(1,local_b8,0x1d);
    auVar11 = FUN_00401a10(0,local_98,0x80);
    uVar3 = FUN_00401b50((byte *)local_98,SUB164(auVar11,0));
    if (((&DAT_00402004)[uVar9 * 3] == (int)uVar3) && ('\0' < (char)(&DAT_00402008)[uVar9 * 0xc])) {
      FUN_00401af0((ulong)(uint)(int)(char)(&DAT_00402008)[uVar9 * 0xc],(char *)local_98);
      puVar4 = FUN_004012a0();
      FUN_004019f0(1,puVar4,0x14);
      puVar1 = local_98;
      do {
        puVar7 = puVar1;
        uVar2 = *puVar7 + 0xfefefeff & ~*puVar7;
        _bVar1 = uVar2 & 0x80808080;
        bVar1 = (byte)_bVar1;
        puVar1 = puVar7 + 1;
      } while (_bVar1 == 0);
      bVar10 = (uVar2 & 0x8080) == 0;
      if (bVar10) {
        bVar1 = (byte)(_bVar1 >> 0x10);
      }
      puVar8 = puVar7 + 1;
      if (bVar10) {
        puVar8 = (uint *)((long)puVar7 + 6);
      }
      FUN_004019f0(1,local_98,(long)puVar8 + ((-3 - (ulong)CARRY1(bVar1,bVar1)) - (long)local_98));
      auVar11 = FUN_004019f0(1,&local_b9,1);
      return auVar11;
    }
  }
LAB_00401800:
  auVar11 = FUN_004019f0(1,"...AAARGH\n\n",0xb);
  return auVar11;
}

この関数に加えてgdb pedaでAntiochOSをデバッグして入力値の誤りを無視して処理を進めてみると、What is your name?What is your quest?What is your favorite color?の3回の入力があるとわかる。

入力した文字列はFUN_00401b50で32bitのハッシュ値に変換されており、さらに関数FUN_00401b50が読み込んでいるDAT_00402260以下のデータは次のようになっている。

DAT_00402260

このバイナリ列をGoogle検索等すれば、CRC32のハッシュテーブルがヒットする。つまり、入力された文字列はCRC32でハッシュ値をとって比較されていることがわかる。なお注意点として、プログラムでは入力した文字列を改行コード(\n)までハッシュ値の計算に使用している。

比較しているデータはDAT_00402000付近で次のようになっている。

DAT_00402000

データを調べると、12バイトで一つの塊になっており、30個記録されている。12バイトのブロックのうち、最初の4バイトがWhat is your name?、次の4バイトがWhat is your favorite color?に対応しており、What is your quest?は任意の入力値でパスできる。最後の4バイトは入力を全てパスしたあとに表示される文字列に使用される。

colorのCRC32ハッシュ値については、例えばBlue\nやRed\nのCRC32をとれば該当するものが見つかる。nameのCRC32は簡単な名前ではヒットしてくれない。30個という数字について考えると、配布されたDockerコンテナのlayerを格納するフォルダ数が31個あり、うち1個にはAntiochOSが入っており、残り30個あることが思い出される。

30個のフォルダを確認してみると、例えば1c5d28d6564aed0316526e8bb2d79a436b45530d2493967c8083fea2b2e518ceのフォルダ内のjsonには名前として"author":"Roger the Shrubber"の記述がある。このRoger the Shrubberを入力するとWhat is your name?をパスできる。

> approach
Approach the Gorge of Eternal Peril!
What is your name? Roger the Shrubber
What is your quest?

したがって30個のフォルダ内のauthorが30個のCRC32ハッシュ値に対応しているとして、CRC32の元の文字列を求める次のPythonコードを作成した。colorについてはネット上からかき集めた色名を使って辞書攻撃を行う(機械的に生成したので多少無駄な色名が含まれている)。

import binascii

binstrs = ("A9 95 93 B5 29 AB B5 1B 0E 00 00 00 4B D0 FD 5E C8 68 84 3F 12 00 00 00 D0 85 ED EC 48 3D D2 82 02 00 00 00 14 92 54 D8 E5 2E 47 00 1D 00 00 00 4D 02 2F 2C AA 60 A0 C9 0C 00 00 00 32 52 8A 01 35 D2 24 00 0D 00 00 00 33 8A B8 72 13 66 57 81 14 00 00 00 E2 04 44 67 29 E1 69 51 0B 00 00 00 B5 73 7A 30 3E E1 60 E5 1C 00 00 00 04 87 46 13 A9 E4 58 23 15 00 00 00 1B 47 F6 94 53 1A 34 D6 05 00 00 00 75 CF A1 ED E5 91 FA BA 18 00 00 00 4D 12 AC BB 1D 64 97 A6 19 00 00 00 C3 E4 07 F7 43 56 18 EF 07 00 00 00 6F 59 02 D7 15 89 C2 79 0A 00 00 00 48 08 A1 86 DC 8F 10 59 01 00 00 00 1C 53 40 D6 E8 E1 3D EF 13 00 00 00 B3 5D 66 7B B0 03 A9 A3 03 00 00 00 CC 21 13 AB D7 EA ED EE 04 00 00 00 D8 66 60 4F 07 3D 8A 9C 11 00 00 00 CA 47 60 25 9E BE 85 40 09 00 00 00 D3 1E C9 3F C9 49 95 37 08 00 00 00 E4 AF 24 A4 47 13 87 EF 1B 00 00 00 DA 01 09 55 6B EC FC 01 10 00 00 00 2D 9E A2 10 AA 56 60 E7 16 00 00 00 5F C8 CB 56 68 1A 6F 35 0F 00 00 00 A6 E3 DF 80 36 B5 0A 9D 1E 00 00 00 E1 D4 57 E6 30 FD E9 B4 17 00 00 00 D4 E1 A1 2B 18 D9 66 BE 1A 00 00 00 9B 08 33 7D 85 F5 C1 67 06").split(" ")

users = []

class User:
        name = "--"
        name_hash = 0
        color = "--"
        color_hash = 0
        quest = 0

for i in range(0x1e):
        users += [User()]
        tmp = ""
        for j in range(4):
                tmp = binstrs[i*12+j] + tmp
        users[i].name_hash = int(tmp,16)
        tmp = ""
        for j in range(4):
                tmp = binstrs[i*12+j+4] + tmp
        users[i].color_hash = int(tmp,16)
        users[i].quest = int(binstrs[i*12+8],16)


input_name = ["Dragon of Angnor","Roger the Shrubber","Dinky","Dennis the Peasant","Sir Ector","A Famous Historian","Tim the Enchanter","Sir Gawain","Trojan Rabbit","Sir Robin","Green Knight","Sir Bedevere","Squire Concorde","Sir Not-Appearing-in-this-Film","Legendary Black Beast of Argh","Sir Gallahad","Lady of the Lake","Zoot","Miss Islington","Chicken of Bristol","Rabbit of Caerbannog","Black Knight","Prince Herbert","Brother Maynard","King Arthur","Sir Bors","Squire Patsy","Bridge Keeper","Inspector End Of Film","Sir Lancelot"]

for i in input_name:
        nameh = binascii.crc32(i.encode()+b"\n")
        for j in range(0x1e):
                if nameh == users[j].name_hash:
                        users[j].name = i
                        break

input_color = ["Ivory","Aqua","Aquamarine","Azalea","Apricot","Amethyst","Amber","Yellow","Indigo","Vermilion","Vermillion","Wistaria","Wisteria","Ultramarine","Ecru","Evergreen","Ebony","Ocher","Orchid","Olive","Orange","Khaki","Cardinal","Garnet","Carmine","Karmijn","Camel","Green","Crimson","Gray","Grey","Crocus","Gold","Cork","Saxeblue","Sand","Cyan","Cinnabar","Cinnamon","Champagne","Silver","Scarlet","Strawberry","Spruce","Smalt","Sepia","Celadon","Solferino","Tan","Chestnut","Cherry","Chartreuse","Chocolate","Terracotta","Taupe","Topaz","Navy","Navyblue","Burgundy","Parchment","Purple","Violet","Honey","Buff","Pansy","Bamboo","Pumpkin","Peach","Biscuit","Hyacinth","Viridian","Pink","Bougainvillaea","Forget-me-not","Fawn","Fuchsia","Brown","Black","Flamingo","Blue","Flesh","Bronze","Blond","Blonde","Beige","Begonia","Peche","Heliotrope","Bellflower","Henna","Bordeaux","White","Mustard","Magenta","Madonna","Mahogany","Marigold","Maroon","Maize","Mauve","Lime","Lilac","Raspberry","Russet","Raphael","Lavender","Leghorn","Red","Rose","Snow","Tuberose","Cornsilk","Seashell","Highlighter","Bisque","Sarcoline","Coral","Tomato","Linen","Salmon","Pilsener","Camellia","Wheat","Azure","Chiffon","Citrine","Sakura","Burlywood","Plum","Gainsboro","Goldenrod","Thistle","Umber","Sienna","Lotus","Skyblue","Hollyhock","Rubber","Limeade","Turquoise","Seagreen","Emerald","Darkcyan","Darkblue","A","Aero","Alabaster","Alizarin","Almond","Amaranth","Amazon","Ao","Artichoke","Asparagus","Auburn","Aureolin","Avocado","B","Beaver","Bistre","Bittersweet","Blue-gray","Blue-green","Blue-violet","Blueberry","Blush","Bole","Bone","Byzantine","Byzantium","C","Cadet","Calypso","Carnelian","Catawba","Celeste","Cerise","Cerulean","Charcoal","Cinereous","Citron","Claret","Coconut","Coffee","Copper","Coquelicot","Cordovan","Cream","Cultured","Cyclamen","D","Dandelion","Denim","Desert","Diamond","Dirt","E","Eggplant","Eggshell","Eminence","Eucalyptus","F","Fallow","Fandango","Feldgrau","Firebrick","Flame","Flax","Flirt","Frostbite","Fulvous","Gamboge","Glaucous","Green-blue","Green-cyan","Green-yellow","Grullo","Gunmetal","Harlequin","Honeydew","Iceberg","Icterine","Inchworm","Independence","Iris","Irresistible","Isabelline","Jade","Jasmine","Jet","Jonquil","Keppel","Kobe","Kobi","Kobicha","L","Lanzones","Lava","Lemon","Lenurple","Liberty","Licorice","Limerick","Lion","Liver","Livid","Lotion","Lumber","Lust","M","Malachite","Manatee","Mandarin","Mango","Mantis","Mauvelous","Melon","Menthol","Midnight","Milk","Mindaro","Ming","Mint","Moccasin","Mocha","Moonstone","Mud","Mulberry","Mystic","Nickel","Nyanza","Ochre","Olivine","Onyx","Opal","Orange-red","Orange-yellow","Oxblood","Oxley","Patriarch","Peach-orange","Peach-yellow","Pear","Pearl","Peridot","Periwinkle","Persimmon","Peru","Petal","Phlox","Pineapple","Pink-orange","Pistachio","Platinum","Popstar","Prune","Puce","Purpureus","Quartz","Quincy","R","Rackley","Rajah","Razzmatazz","Red-brown","Red-orange","Red-purple","Red-violet","Redwood","Rhythm","Rosewood","Ruber","Ruby","Rufous","Rum","Rust","S","Saffron","Sage","Salem","Sandstorm","Sapphire","Shadow","Shampoo","Shandy","Sinopia","Skobeloff","Smitten","Smoke","Soap","Straw","Sunglow","Sunny","Sunray","Sunset","T","Taffy","Tangelo","Tangerine","Teal","Telemagenta","Temptress","Tenne","Timberwolf","Titanium","Toolbox","Tooth","Tulip","Tumbleweed","Tuscan","Tuscany","Ube","Urobilin","Vanilla","Verdigris","Veronica","Violet-blue","Violet-red","Water","Watermelon","Waterspout","Wenge","Wine","Xanadu","Yellow-green","Zaffre","Zinnwaldite","Zomp","Iron","Ice","Ivy","Ash","Apple","Alice","Antwerp","Ink","Indian","Van","Dyck","Elm","Oyster","Old","Drab","Oriental","Cadmium","Canary","Carrot","Grass","Chrome","Golden","Cocoa","Cochineal","Cobalt","Saxony","Sulfur","Sea","Shell","Signal","Shrimp","Jaune","Brillant","Sky","Steel","Spray","Slate","Geranium","Dark","Turnbull's","Duck","Dove","China","Chinese","Tyrian","Diesbach","Deep","Dawn","Nile","Naples","Nail","Burnt","Bice","Paris","Hunter","Hunting","Vandyke","Pea","Peacock","Blossom","Pistacho","Billiard","Fir","Fire","Fountain","Vermeer","Fog","Forest","Prussian","Hazel","Pale","Baby","Berlin","Bottle","Poppy","Horizon","Pompeian","Marine","Maya","Mikado","Milky","Milori","Moss","Light","Saxe","Lapis","Lazuli","Lamp","Leaf","River","Royal","Raw","Madder","Color","Papaya","Whip","Blanched","Misty","Sunflower","Hot","Antique","Sandy","Sweet","Winter","Mist","Snowshoe","Hare","Moon","Lily","Silent","Delight","Lightest","Ripple","Medium","Rosy","Powder","Shade","Of","Lawn","Dim","Cornflower","Dodger","Spring","Absolute","Zero","Acid","African","Air","Superiority","Alloy","Android","Brass","Arctic","Army","Arylide","Atomic","Baker-Miller","Banana","Mania","Barbie","Barn","Battleship","Beau","B'dazzled","Big","Dip","Foot","Bitter","Shimmer","Bean","Leather","Shadows","Blast-off","Bleu","De","Blizzard","Blood","Bell","Jeans","Yonder","Booger","Buster","Brick","Bright","Brilliant","Brink","British","Racing","Sugar","Brunswick","Bud","Burnished","Cafe","Au","Noir","Cambridge","Cameo","Candy","Caput","Mortuum","Caribbean","Carnation","Carolina","Castleton","Cedar","Chest","Frost","Charleston","Charm","Satin","Columbia","Congo","Cool","Penny","Cornell","Cosmic","Latte","Coyote","Cotton","Cyber","Grape","Electric","Jungle","Pastel","Dartmouth","Davy's","Space","Dingy","Dungeon","Dogwood","Duke","Dutch","Earth","Eerie","Egyptian","English","Eton","Falu","Fashion","Fern","Field","Fiery","Engine","Floral","Fluorescent","French","Fuzzy","Wuzzy","Generic","Ghost","Glossy","GO","Fusion","Granite","Granny","Smith","Lizard","Sheen","Han","Hansa","Harvest","Heat","Wave","Hollywood","Honolulu","Hooker's","Illuminating","Imperial","India","Dye","International","Italian","Japanese","Jazzberry","Jam","June","Kelly","Key","Kombu","KSU","La","Salle","Languid","Laser","Laurel","Curry","Glacier","Iced","Meringue","Grayish","Thulian","Luster","Lincoln","Liseran","Little","Boy","Girl","Maastricht","Macaroni","And","Lake","Majorelle","Tango","Mardi","Gras","Maximum","May","Meat","Persian","Mellow","Metallic","Seaweed","Sunburst","Mexican","Microsoft","Edge","Middle","Mimi","Minion","Mode","Mordant","Morning","Mountain","Meadow","Mountbatten","MSU","Mughal","Mummy's","Tomb","Myrtle","Nadeshiko","Napier","Navajo","Neon","New","Car","York","Nintendo","Non-photo","Ocean","Boat","Office","Ogre","Odor","Lace","Opera","Peel","Soda","Orioles","Otter","Outer","Outrageous","Oxford","OU","Pacific","Pakistan","Palatinate","Magenta-pink","Robin","Palm","Paolo","Veronese","Paradise","Parrot","Payne's","Puff","Pearly","Permanent","Pewter","Philippine","Phthalo","Picton","Pictorial","Piggy","Pine","Tree","Sherbet","Pixie","Plump","Poison","Police","Polished","Pomp","Portland","Prilly","Princess","Perfume","Princeton","Psychedelic","Pullman","Heart","Pizzazz","Queen","Quick","Quinacridone","Radical","Raisin","Glace","Razzle","Dazzle","Razzmic","Berry","Rebecca","Cola","Devil","Salsa","Registration","Resolution","Rich","Rifle","Ripe","Roast","Egg","Rocket","Roman","Root","Beer","Bonbon","Dust","Vale","Rubine","Russian","Rusty","Sacramento","State","Saddle","Safety","St.","Patrick's","Samsung","Dune","Sap","Sasquatch","Socks","Schauss","School","Bus","Screamin'","Foam","Serpent","Seal","Selective","Shamrock","Shimmering","Shiny","Shocking","Chalice","Foil","Sizzling","Sunrise","Slimy","Smashed","Smokey","Smoky","Solid","Sonic","Spartan","Spanish","Spicy","Mix","Spiro","Disco","Star","Command","Stil","Stop","Sunburnt","Cyclops","Super","Tart","Tea","Deer","Terra","Cotta","Tickle","Me","Tiffany","Tiger's","Eye","Sauce","Tractor","Trolley","Tropical","Rain","True","Tufts","Turkish","Surf","Turtle","Twilight","Twitter","Ultra","Unbleached","Silk","United","Nations","Unmellow","UP","Upsdell","Vampire","Dyke","Vegas","Venetian","Verse","Very","Vine","Violin","Vista","Vivid","Red-tangelo","Weldon","Wild","Willpower","Windsor","Dregs","Wizard","Wintergreen","Dream","Wood","Sunshine","YInMn","Zebra","See","Also"]
input_color += ["Clear","Ochre","Papaya","Floral","Mango","Honeydew","Manatee","Citron","Pistachio","Avocado","Lapis","Transparent","Opaque","Fluorescent","Iridescent","Tone","Bright","Simple","None","Ash"]

off_color = []

for i in input_color:
        flag = True
        colorh = binascii.crc32(i.encode()+b"\n")
        for j in range(0x1e):
                if colorh == users[j].color_hash:
                        users[j].color = i
                        flag = False
                        break
        if flag==True:
                off_color += [i]

for k in range(0x1e):
        for i in users:
                if i.quest == k+1:
                        print('%s, %s, %s, %s, %s' % (i.name, hex(i.name_hash), i.color, hex(i.color_hash), i.quest))
                        break

すべてのCRC32ハッシュ値を解くことができ、結果は以下の通り。

name, name_hash, color, color_hash, number
Miss Islington, 0x86a10848, Brown, 0x59108fdc, 1
Sir Bors, 0xeced85d0, Coral, 0x82d23d48, 2
Tim the Enchanter, 0x7b665db3, Orange, 0xa3a903b0, 3
Dragon of Angnor, 0xab1321cc, Khaki, 0xeeedead7, 4
Brother Maynard, 0x94f6471b, Crimson, 0xd6341a53, 5
Sir Bedevere, 0x7d33089b, Teal, 0x67c1f585, 6
Sir Robin, 0xf707e4c3, Red, 0xef185643, 7
Zoot, 0x3fc91ed3, Tan, 0x379549c9, 8
Squire Concorde, 0x256047ca, Periwinkle, 0x4085be9e, 9
Green Knight, 0xd702596f, Green, 0x79c28915, 10
Trojan Rabbit, 0x674404e2, Beige, 0x5169e129, 11
Chicken of Bristol, 0x2c2f024d, Mint, 0xc9a060aa, 12
Roger the Shrubber, 0x18a5232, Tomato, 0x24d235, 13
Bridge Keeper, 0xb59395a9, Indigo, 0x1bb5ab29, 14
Sir Gawain, 0x56cbc85f, Azure, 0x356f1a68, 15
Legendary Black Beast of Argh, 0x550901da, Silver, 0x1fcec6b, 16
A Famous Historian, 0x4f6066d8, Pink, 0x9c8a3d07, 17
Sir Lancelot, 0x5efdd04b, Blue, 0x3f8468c8, 18
Lady of the Lake, 0xd640531c, Gold, 0xef3de1e8, 19
Rabbit of Caerbannog, 0x72b88a33, Salmon, 0x81576613, 20
Sir Not-Appearing-in-this-Film, 0x13468704, Transparent, 0x2358e4a9, 21
Prince Herbert, 0x10a29e2d, Wheat, 0xe76056aa, 22
King Arthur, 0xe657d4e1, Purple, 0xb4e9fd30, 23
Inspector End Of Film, 0xeda1cf75, Gray, 0xbafa91e5, 24
Sir Ector, 0xbbac124d, Bisque, 0xa697641d, 25
Squire Patsy, 0x2ba1e1d4, Chartreuse, 0xbe66d918, 26
Dennis the Peasant, 0xa424afe4, Orchid, 0xef871347, 27
Dinky, 0x307a73b5, Turquoise, 0xe560e13e, 28
Black Knight, 0xd8549214, Black, 0x472ee5, 29
Sir Gallahad, 0x80dfe3a6, Yellow, 0x9d0ab536, 30

これらのデータをapproachに入力してみたが、特にflagが出てくるわけでもなく、Right. Off you go. #1Right. Off you go. #30までの文字列が出力されるのみだった。

かなり悩んだ末、Dockerコンテナの各種設定ファイルを見ると、レイヤーはAntiochOSの実行ファイルが入っている一つしか読み込まれていないことに気づく。

repositories{

    "antioch":{
        "latest":"7016b68f19aed3bb67ac4bf310defd3f7e0f7dd3ce544177c506d795f0b2acf3"
    }

}
manifest.json[

    {
        "Config":"a13ffcf46cf41480e7f15c7f3c6b862b799bbe61e7d5909150d8a43bd3b6c039.json",
        "RepoTags":[
            "antioch:latest"
        ],
        "Layers":[
            "7016b68f19aed3bb67ac4bf310defd3f7e0f7dd3ce544177c506d795f0b2acf3/layer.tar"
        ]
    }

]
a13ffcf46cf41480e7f15c7f3c6b862b799bbe61e7d5909150d8a43bd3b6c039.json{

    "architecture":"amd64",
    "config":{
        "Hostname":"",
        "Domainname":"",
        "User":"",
        "AttachStdin":false,
        "AttachStdout":false,
        "AttachStderr":false,
        "Tty":false,
        "OpenStdin":false,
        "StdinOnce":false,
        "Env":[
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd":[
            "/AntiochOS"
        ],
        "Image":"sha256:72081c09b8504bda08787ba6ea0c5059e74464398cb92685b3c86a26230b8a1f",
        "Volumes":null,
        "WorkingDir":"",
        "Entrypoint":null,
        "OnBuild":null,
        "Labels":null
    },
    "container":"5a7d890eaf80df63166dedb6c0f0afaa26894ba10dd647671da887cfe2ce4349",
    "container_config":{
        "Hostname":"5a7d890eaf80",
        "Domainname":"",
        "User":"",
        "AttachStdin":false,
        "AttachStdout":false,
        "AttachStderr":false,
        "Tty":false,
        "OpenStdin":false,
        "StdinOnce":false,
        "Env":[
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd":[
            "/bin/sh",
            "-c",
            "#(nop) ",
            "CMD [\"/AntiochOS\"]"
        ],
        "Image":"sha256:72081c09b8504bda08787ba6ea0c5059e74464398cb92685b3c86a26230b8a1f",
        "Volumes":null,
        "WorkingDir":"",
        "Entrypoint":null,
        "OnBuild":null,
        "Labels":{
        }
    },
    "created":"2021-07-23T03:21:55.959771124Z",
    "docker_version":"20.10.2",
    "history":[
        {
            "created":"2021-07-23T03:21:55.793483339Z",
            "created_by":"/bin/sh -c #(nop) ADD file:fae1674275a5cc9b0c04ef177df65aebeaf796b0ba7c94ac2bd35120306411d4 in / "
        },
        {
            "created":"2021-07-23T03:21:55.959771124Z",
            "created_by":"/bin/sh -c #(nop) CMD [\"/AntiochOS\"]",
            "empty_layer":true
        }
    ],
    "os":"linux",
    "rootfs":{
        "type":"layers",
        "diff_ids":[
            "sha256:d26c760acd6e75540d4ab7a33245a75a5506daa7998819f97918a39632a15497"
        ]
    }

}

antioch.tar直下のrepositories、manifest.json、a13ffcf46cf41480e7f15c7f3c6b862b799bbe61e7d5909150d8a43bd3b6c039.jsonの3つのファイルを編集して残り30個のレイヤーを適用すれば変化があるのではないか?

試しに適当にレイヤーを追加してDockerにロードして実行してみると、consultコマンドの実行結果が次のように変化することがわかった。

> consult
Consult the Book of Armaments!
....`..........
..|......,\....
.....|.|......'
........\......
.........._....
'.......,...|..
....._.._'...._
..`............
..|.........|..
......(........
..............\
...............
|..........|_..
....._....._...
...............
./........._..|
......_.'...|..
|...\....|.....
........._.....
...............
...V..._.......
.....|..\...._|
....|.._....|..
..........'....
....|.......|..
._\..\.)...._..
.|.............
........._..'.|
./_.._.........
.............._
|..._..........
...............
........|......
......|..|.....
...._..........
|.|._..\..|...|
........._.....
..._........._.
...............
._........._.`.
.(.|....._.....
...|..|..|(....
............._.
...|'_...|...._
.......'._....|
.....)........|
............_/.
........._.._.'
......_.(.`'...
\..........\...
...............
..............\
......((._.....
.....|......._.
../.......'.._.
...............
.............._
.|....|_.......
.....|.\.......
..._.|.........
.../........_..
....(..........
(..............
...............
.......,..|....
..........|....
..._......._...
....../......||
'.|...........V
._........../._
\..........,...
._.V._.........
...............
\.......\|.....
_.'.......__..`
_............./
...........|...
..._...../.....
.|....._/......
...........|||.
._...`..,....._
...............
..............|
..__......|.|..
......._......|
............_..
...,...........
...|........_..
.........|._...
........|.._.._
|...|....._..|.
`..........._..
.|..._......_..
.......(....|..
...............
..._.`....._...
......|...._...
_|....|......_.
............._.
........._.....
..........._..,
_.....).|....|.
.....\..|,.....
......V.....|._
..|...\........
..|...__..._..|
...............
....(..........
......_..|.....
.|.....|....._.
....._...|`.||.
.......|...|...
.._.._....|..._
.......(.`'._.\
...........|...
.......|.......
......_...\|...
..,........|.._
.\._.|.....|...
.....|..._.....
.__...../_..(..
..V\.........||
....)._......._
_|....|_..._|..
....|......'...
...|..........\
/...|_.....|...
_......_._.....
.|........)....
..|............
............_..
.......|.|.....
...............
._..._.........
.._./..........
.............'.
._...._.._.....
.....(|....../`
._.............
..._....._.....
....._.........
.....|._.......
...............
....|__....._..
...'._.._......
./.............
...............
(.._...........
._.............
..._.__........
..........._...
/....'.._.|._..
._.............
..V'.._........
...............
.|.............
....\|..\..._..
,.....\........
..._..|........
.|....._.......
...|...|_......
.V..)......|...
.....\......._.
...._..........
._.....).|.....
...._....._....
..........|.|.|
..|...._......_
.............._
..\...../..._..
........\._`...
...............
../..|.........
...|...........
\......._...\..
.....|_......|.
........._.'...
..............\
.........._.\.|
...._..|.......
.\._...|..._...
....._........_
...|...........
.._......).....
...._....._...,
.._..||..|.....
.............|.
|.._..__..||||_
_....|.....\..|
.|...|_|.|.....
.......'...|...
.\.......`.....
...............
.`.....|.......
._..,..._.|/...
..\|..........|
.V.\..........V
..........._.._
._|......V.....
._.._..._......
|._............
...._..........
.../...V..|..|.
........._...._
...|.........V.
..../......_...
.|..(.....|.|..
.......|....._.
...............
...............
|.|./|..)|.....
_\..\..........
...|....|..\.\.
......./.......
.........\_....
.._....|.......
.............|.
|.._\.\|.......
....|...|.|....
............_.|
...|......../..
|.|............
..V_.......`...
...|..||V......
........._.._..
........'|.._..
..../.......`./
......._._.\...
.....|..._.....
......_.......(
....../........
.....\.._......
/..............
/..._...._.|_..
........||.....
.......|.....`|
.....`.....|...
.............|.
............._.
...\|.|........
....._.........
........,....|.
...............
....._.,|......
\.|......_.....
....'........|.
..|..|.._.._...
...._..|....\..
....._,.|......
......_.(..'|/(
........|_.._..
.|.............
....../..|...|\
./.............
....._...|.._..
....)|_....../.

何が出てきそうな気配がするが、レイヤーを重ねる順番が重要かもしれない。各レイヤーのauthorは先のCRC32ハッシュ値の解析結果から、1~30までの番号が振られている。この番号順にレイヤーを適用してみよう。

Dockerの設定ファイル3つを次のように修正する。

repositories{

    "antioch":{
        "latest":"7016b68f19aed3bb67ac4bf310defd3f7e0f7dd3ce544177c506d795f0b2acf3",
        "a0":"b75ea3e81881c5d36261f64d467c7eb87cd694c85dd15df946601330f36763a4",
        "a1":"ea12384be264c32ec1db0986247a8d4b2231bf017742313c01b05a7e431d9c26",
        "a2":"4c33f90f25ea2ab1352efb77794ecc424883181cf8e6644946255738ac9f5dbd",
        "a3":"09e6fff53d6496d170aaa9bc88bd39e17c8e5c13ee9066935b089ab0312635ef",
        "a4":"e5254dec4c7d10c15e16b41994ca3cf0c5e2b2a56c9d4dc2ef053eeff24333ff",
        "a5":"7d643931f34d73776e9169551798e1c4ca3b4c37b730143e88171292dbe99264",
        "a6":"754ee87063ee108c1f939cd3a28980a03b700f3c3967df8058831edad2743fd7",
        "a7":"b5f502d32c018d6b2ee6a61f30306f9b46dad823ba503eea5b403951209fd59b",
        "a8":"81f28623cca429f9914e21790722d0351737f8ad3e823619a4f7019be72e2195",
        "a9":"76531a907cdecf03c8ac404d91cbcabd438a226161e621fab103a920600372a8",
        "a10":"6b4e128697aa0459a6caba2088f6f77efaaf29d407ec6b58939c9bc7814688ad",
        "a11":"bfefc1bdf8b980a525f58da1550b56daa67bae66b56e49b993fff139faa1472c",
        "a12":"1c5d28d6564aed0316526e8bb2d79a436b45530d2493967c8083fea2b2e518ce",
        "a13":"f9621328166de01de73b4044edb9030b3ad3d5dbc61c0b79e26f177e9123d184",
        "a14":"58da659c7d1c5a0c3447cb97cd6ffb12027c734bfba32de8b9b362475fe92fae",
        "a15":"9a31bad171ad7e8009fba41193d339271fc51f992b8d574c501cae1bfa6c3fe2",
        "a16":"49fb821d2bf6d6841ac7cf5005a6f18c4c76f417ac8a53d9e6b48154b5aa1e76",
        "a17":"fd8bf3c084c5dd42159f9654475f5861add943905d0ad1d3672f39e014757470",
        "a18":"a435765bcd8745561460979b270878a3e7c729fae46d9e878f4c2d42e5096a44",
        "a19":"cd27ad9a438a7eef05f5b5d99e2454225693e63aba29ce8553800fed23575040",
        "a20":"8e11477e79016a17e5cde00abc06523856a7db9104c0234803d30a81c50d2b71",
        "a21":"e1a9333f9eccfeae42acec6ac459b9025fe6097c065ffeefe5210867e1e2317d",
        "a22":"e6c2557dc0ff4173baee856cbc5641d5b19706ddb4368556fcdb046f36efd2e2",
        "a23":"fadf53f0ae11908b89dffc3123e662d31176b0bb047182bfec51845d1e81beb9",
        "a24":"303dfd1f7447a80322cc8a8677941da7116fbf0cea56e7d36a4f563c6f22e867",
        "a25":"f2ebdc667cbafc2725421d3c02babc957da2370fbd019a9e1993d8b0409f86dd",
        "a26":"2b363180ec5d5862b2a348db3069b51d79d4e7a277d5cf5e4afe2a54fc04730e",
        "a27":"25e171d6ac47c26159b26cd192a90d5d37e733eb16e68d3579df364908db30f2",
        "a28":"cfd7ddb31ce44bb24b373645876ac7ea372da1f3f31758f2321cc8f5b29884fb",
        "a29":"a2de31788db95838a986271665b958ac888d78559aa07e55d2a98fc3baecf6e6"
    }

}
manifest.json[

    {
        "Config":"a13ffcf46cf41480e7f15c7f3c6b862b799bbe61e7d5909150d8a43bd3b6c039.json",
        "RepoTags":[
            "antioch:latest"
        ],
        "Layers":[
            "7016b68f19aed3bb67ac4bf310defd3f7e0f7dd3ce544177c506d795f0b2acf3/layer.tar",
            "b75ea3e81881c5d36261f64d467c7eb87cd694c85dd15df946601330f36763a4/layer.tar",
            "ea12384be264c32ec1db0986247a8d4b2231bf017742313c01b05a7e431d9c26/layer.tar",
            "4c33f90f25ea2ab1352efb77794ecc424883181cf8e6644946255738ac9f5dbd/layer.tar",
            "09e6fff53d6496d170aaa9bc88bd39e17c8e5c13ee9066935b089ab0312635ef/layer.tar",
            "e5254dec4c7d10c15e16b41994ca3cf0c5e2b2a56c9d4dc2ef053eeff24333ff/layer.tar",
            "7d643931f34d73776e9169551798e1c4ca3b4c37b730143e88171292dbe99264/layer.tar",
            "754ee87063ee108c1f939cd3a28980a03b700f3c3967df8058831edad2743fd7/layer.tar",
            "b5f502d32c018d6b2ee6a61f30306f9b46dad823ba503eea5b403951209fd59b/layer.tar",
            "81f28623cca429f9914e21790722d0351737f8ad3e823619a4f7019be72e2195/layer.tar",
            "76531a907cdecf03c8ac404d91cbcabd438a226161e621fab103a920600372a8/layer.tar",
            "6b4e128697aa0459a6caba2088f6f77efaaf29d407ec6b58939c9bc7814688ad/layer.tar",
            "bfefc1bdf8b980a525f58da1550b56daa67bae66b56e49b993fff139faa1472c/layer.tar",
            "1c5d28d6564aed0316526e8bb2d79a436b45530d2493967c8083fea2b2e518ce/layer.tar",
            "f9621328166de01de73b4044edb9030b3ad3d5dbc61c0b79e26f177e9123d184/layer.tar",
            "58da659c7d1c5a0c3447cb97cd6ffb12027c734bfba32de8b9b362475fe92fae/layer.tar",
            "9a31bad171ad7e8009fba41193d339271fc51f992b8d574c501cae1bfa6c3fe2/layer.tar",
            "49fb821d2bf6d6841ac7cf5005a6f18c4c76f417ac8a53d9e6b48154b5aa1e76/layer.tar",
            "fd8bf3c084c5dd42159f9654475f5861add943905d0ad1d3672f39e014757470/layer.tar",
            "a435765bcd8745561460979b270878a3e7c729fae46d9e878f4c2d42e5096a44/layer.tar",
            "cd27ad9a438a7eef05f5b5d99e2454225693e63aba29ce8553800fed23575040/layer.tar",
            "8e11477e79016a17e5cde00abc06523856a7db9104c0234803d30a81c50d2b71/layer.tar",
            "e1a9333f9eccfeae42acec6ac459b9025fe6097c065ffeefe5210867e1e2317d/layer.tar",
            "e6c2557dc0ff4173baee856cbc5641d5b19706ddb4368556fcdb046f36efd2e2/layer.tar",
            "fadf53f0ae11908b89dffc3123e662d31176b0bb047182bfec51845d1e81beb9/layer.tar",
            "303dfd1f7447a80322cc8a8677941da7116fbf0cea56e7d36a4f563c6f22e867/layer.tar",
            "f2ebdc667cbafc2725421d3c02babc957da2370fbd019a9e1993d8b0409f86dd/layer.tar",
            "2b363180ec5d5862b2a348db3069b51d79d4e7a277d5cf5e4afe2a54fc04730e/layer.tar",
            "25e171d6ac47c26159b26cd192a90d5d37e733eb16e68d3579df364908db30f2/layer.tar",
            "cfd7ddb31ce44bb24b373645876ac7ea372da1f3f31758f2321cc8f5b29884fb/layer.tar",
            "a2de31788db95838a986271665b958ac888d78559aa07e55d2a98fc3baecf6e6/layer.tar"
        ]
    }

]
a13ffcf46cf41480e7f15c7f3c6b862b799bbe61e7d5909150d8a43bd3b6c039.json{

    "architecture":"amd64",
    "config":{
        "Hostname":"",
        "Domainname":"",
        "User":"",
        "AttachStdin":false,
        "AttachStdout":false,
        "AttachStderr":false,
        "Tty":false,
        "OpenStdin":false,
        "StdinOnce":false,
        "Env":[
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd":[
            "/AntiochOS"
        ],
        "Image":"sha256:72081c09b8504bda08787ba6ea0c5059e74464398cb92685b3c86a26230b8a1f",
        "Volumes":null,
        "WorkingDir":"",
        "Entrypoint":null,
        "OnBuild":null,
        "Labels":null
    },
    "container":"5a7d890eaf80df63166dedb6c0f0afaa26894ba10dd647671da887cfe2ce4349",
    "container_config":{
        "Hostname":"5a7d890eaf80",
        "Domainname":"",
        "User":"",
        "AttachStdin":false,
        "AttachStdout":false,
        "AttachStderr":false,
        "Tty":false,
        "OpenStdin":false,
        "StdinOnce":false,
        "Env":[
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd":[
            "/bin/sh",
            "-c",
            "#(nop) ",
            "CMD [\"/AntiochOS\"]"
        ],
        "Image":"sha256:72081c09b8504bda08787ba6ea0c5059e74464398cb92685b3c86a26230b8a1f",
        "Volumes":null,
        "WorkingDir":"",
        "Entrypoint":null,
        "OnBuild":null,
        "Labels":{
        }
    },
    "created":"2021-07-23T03:21:55.959771124Z",
    "docker_version":"20.10.2",
    "history":[
        {
            "created":"2021-07-23T03:21:55.793483339Z",
            "created_by":"/bin/sh -c #(nop) ADD file:fae1674275a5cc9b0c04ef177df65aebeaf796b0ba7c94ac2bd35120306411d4 in / "
        },
        {
            "created":"2021-07-23T03:21:55.959771124Z",
            "created_by":"/bin/sh -c #(nop) CMD [\"/AntiochOS\"]",
            "empty_layer":true
        }
    ],
    "os":"linux",
    "rootfs":{
        "type":"layers",
        "diff_ids":[
            "sha256:d26c760acd6e75540d4ab7a33245a75a5506daa7998819f97918a39632a15497",
            "sha256:56ad9094e91589a0c4468a7e584f98f95eb0cdb95d77c1ed3567dc0f310079fa",
            "sha256:20614930b565c3c2526921cf31e702c2da876e6a25761e568c3a6475b1964506",
            "sha256:c65f4e1496510df94490300b02159b21f00d742cc8080d53db4f08cd3089ff4d",
            "sha256:9ec175233e561a2ad8bceb988d8a186abd7562222b32a8487d3cf457b9970930",
            "sha256:c68fa7e94d0a47345da92520fd4e27fde95ca5f231db6f435b8c9cbade1b9efb",
            "sha256:f2ebda36a3271ce1edbfda092bc053163b904f7f93c84f907d4791cb0f4b8af9",
            "sha256:8cc63febc37c500f301c624c1c82657900b19a6ea20284a2dddebc00acfc4aab",
            "sha256:f54e81fa7589480b984daf7b11b57a5fe30c6335da59f0e0f0245e858f70aea7",
            "sha256:8a59833db6f59a805955912151a3403e8390b5889608da3073095035afe80221",
            "sha256:4dcdbaa3c03c9d6d127109bc686a6ceddec745836e93fb7727bb1789ef51ea6e",
            "sha256:86125e99d06c9cbb8f25aaf9e70f9729844dc65f32d0b9efba91e61867488a6a",
            "sha256:c28906f9ed037116d6ff53ca7b5d3eb78a4547390e39d8d82e37c26b65d9ce55",
            "sha256:c416773ee5440f99882839de8d06eee55cb8536d1bbb75a81288532a5bc8dd70",
            "sha256:64f92abbf678e56017f1b670b6fc78cf239a949f703fba98883cf758690cf102",
            "sha256:e944a08386b83919946593148a10b6b8275569629641e33c34c030470ffdfc32",
            "sha256:9c55cbc3e9f23e18005595de7f3039a0007d13b327048fdf02e6c9dff6bf8de2",
            "sha256:61c178892d09a1a7e50123811ceb2a2d860ce901a786aa260ddfa09735e88e91",
            "sha256:970169c0464c5aa6c46a82e115a85e7831240c9458f18821f0825c8552052e76",
            "sha256:12cb667eaf12d800ed7ff08f79dc80079f6fb4559cb42bc51b8826daaf6b5a59",
            "sha256:3748143bbc2992faddca264a8aca13125149922bf24496d6feffc11ee3f2639c",
            "sha256:2627f896e6d693720980dd7eec1b4bd3dfa7bc81c16fcd5c5a5f2a07d9044011",
            "sha256:021c7ef7074b770c5aff7315e43ac0cc4c310c27fef2bb36f01ec38ad4c1a7b4",
            "sha256:2fdfbdaf3fcbdd173c30e17b38d600747f1502b833966b3c9b2732e8f86a89de",
            "sha256:ee93546f18b55ff3eb1af34a4e07bdb0a2e80b23d1fa8be14342510de2fbe107",
            "sha256:6a176544c667df500bcb8bacb610bc36d3cdf77d34e72824aaa351331ddc5478",
            "sha256:14df0f30dcb29e09bfdf2b4d0333e41fb706986b0f9c6f7ffa0024771738fd61",
            "sha256:3e89196663df29d2cbf04edd731ab51f7f47aeb285f432e572400bf5cca38dad",
            "sha256:51bb2a6f30c9b9223ce724504ac1bed187f4d19891d7a5371c3e2d1d66618d60",
            "sha256:937f0b798dcf7876f6553c8c8b06eddef39f0f8595597fd649ab3bcce3a3a64e",
            "sha256:308b197bb7085ec2dc8889914b184ffc185469512e36b45e8a61dc59b8b386da"
        ]
    }

}

Dockerコンテナを読み込んでconsultコマンドを実行した結果。

*******************************/CTF/flareon2021/antioch$ sudo docker load < antioch.tar 
56ad9094e915: Loading layer [==================================================>]  88.58kB/88.58kB
20614930b565: Loading layer [==================================================>]  79.36kB/79.36kB
c65f4e149651: Loading layer [==================================================>]  74.75kB/74.75kB
9ec175233e56: Loading layer [==================================================>]  74.75kB/74.75kB
c68fa7e94d0a: Loading layer [==================================================>]  93.18kB/93.18kB
f2ebda36a327: Loading layer [==================================================>]  93.18kB/93.18kB
8cc63febc37c: Loading layer [==================================================>]  93.18kB/93.18kB
f54e81fa7589: Loading layer [==================================================>]  79.36kB/79.36kB
8a59833db6f5: Loading layer [==================================================>]  102.4kB/102.4kB
4dcdbaa3c03c: Loading layer [==================================================>]  97.79kB/97.79kB
86125e99d06c: Loading layer [==================================================>]  70.14kB/70.14kB
c28906f9ed03: Loading layer [==================================================>]  97.79kB/97.79kB
c416773ee544: Loading layer [==================================================>]  83.97kB/83.97kB
64f92abbf678: Loading layer [==================================================>]  83.97kB/83.97kB
e944a08386b8: Loading layer [==================================================>]  79.36kB/79.36kB
9c55cbc3e9f2: Loading layer [==================================================>]  88.58kB/88.58kB
61c178892d09: Loading layer [==================================================>]  74.75kB/74.75kB
970169c0464c: Loading layer [==================================================>]  88.58kB/88.58kB
12cb667eaf12: Loading layer [==================================================>]  93.18kB/93.18kB
3748143bbc29: Loading layer [==================================================>]  88.58kB/88.58kB
2627f896e6d6: Loading layer [==================================================>]  93.18kB/93.18kB
021c7ef7074b: Loading layer [==================================================>]  93.18kB/93.18kB
2fdfbdaf3fcb: Loading layer [==================================================>]  88.58kB/88.58kB
ee93546f18b5: Loading layer [==================================================>]  74.75kB/74.75kB
6a176544c667: Loading layer [==================================================>]  102.4kB/102.4kB
14df0f30dcb2: Loading layer [==================================================>]  79.36kB/79.36kB
3e89196663df: Loading layer [==================================================>]  97.79kB/97.79kB
51bb2a6f30c9: Loading layer [==================================================>]  70.14kB/70.14kB
937f0b798dcf: Loading layer [==================================================>]  88.58kB/88.58kB
308b197bb708: Loading layer [==================================================>]  93.18kB/93.18kB
Loaded image: antioch:latest
*******************************/CTF/flareon2021/antioch$ sudo docker run -it antioch
AntiochOS, version 1.32 (build 1975)
Type help for help
> consult
Consult the Book of Armaments!
...............
...............
...............
...............
...............
...............
...............
...............
...............
....______.....
...|..____|....
...|.|__.......
...|..__|......
...|.|.........
...|_|.........
...............
...............
...._..........
...(_).........
...._..........
...|.|.........
...|.|.........
...|_|.........
...............
...............
...............
...............
...__...__.....
...\.\././.....
....\.V./......
.....\_/.......
...............
...............
...............
...............
.....___.......
..../._.\......
...|..__/......
....\___|......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
...._____......
...|_..._|.....
.....|.|.......
.....|.|.......
...._|.|_......
...|_____|.....
...............
...............
...............
...............
....___........
.../.__|.......
...\__.\.......
...|___/.......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
...._____......
...|..__.\.....
...|.|__).|....
...|.._../.....
...|.|.\.\.....
...|_|..\_\....
...............
...............
...._..........
...(_).........
...._..........
...|.|.........
...|.|.........
...|_|.........
...............
...............
...............
...............
.....__._......
..../._`.|.....
...|.(_|.|.....
....\__,.|.....
.....__/.|.....
....|___/......
...._..........
...|.|.........
...|.|__.......
...|.'_.\......
...|.|.|.|.....
...|_|.|_|.....
...............
...............
...._..........
...|.|.........
...|.|_........
...|.__|.......
...|.|_........
....\__|.......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
.....____......
..../.__.\.....
...|.|..|.|....
...|.|..|.|....
...|.|__|.|....
....\____/.....
...............
...............
...............
...............
...._..._......
...|.|.|.|.....
...|.|_|.|.....
....\__,_|.....
...............
...............
...._..........
...|.|.........
...|.|_........
...|.__|.......
...|.|_........
....\__|.......
...............
...............
...............
......____.....
...../.__.\....
...././._`.|...
...|.|.(_|.|...
....\.\__,_|...
.....\____/....
...............
.....__........
..../._|.......
...|.|_........
...|.._|.......
...|.|.........
...|_|.........
...............
...............
...._..........
...|.|.........
...|.|.........
...|.|.........
...|.|.........
...|_|.........
...............
...............
...............
...............
.....__._......
..../._`.|.....
...|.(_|.|.....
....\__,_|.....
...............
...............
...............
...............
...._.__.......
...|.'__|......
...|.|.........
...|_|.........
...............
...............
...............
...............
.....___.......
..../._.\......
...|..__/......
....\___|......
...............
...............
...............
...............
....______.....
...|______|....
...............
...............
...............
...............
...............
...............
.....___.......
..../._.\......
...|.(_).|.....
....\___/......
...............
...............
...............
...............
...._.__.......
...|.'_.\......
...|.|.|.|.....
...|_|.|_|.....
...............
...............
...............
...............
...............
...............
...._..........
...(_).........
...............
...............
...............
...............
.....___.......
..../.__|......
...|.(__.......
....\___|......
...............
...............
...............
...............
.....___.......
..../._.\......
...|.(_).|.....
....\___/......
...............
...............
...............
...............
...._.__.___...
...|.'_.`._.\..
...|.|.|.|.|.|.
...|_|.|_|.|_|.
...............
...............
...............
...............
...............
...............
...............
...............
...............
> 

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

04 - myaquaticlife (1point)

What would Flare-On do without a healthy amount of nostalgia for the abraisive simplicity of 1990's UI design? Probably do more actual work and less writing fun challenges like this.

実行ファイル「myaquaticlife.exe」が配布される。バイナリの末尾を確認すると Created with Multimedia Builder, version 4.9.8.13の文字列が見つかる。MMBで作られたWindowsアプリケーションのようだ。

実行画面1

実行画面2

実行すると16種類のアイコンが表示され、それぞれクリックできるようだが何も起きない。中央の「What's your favorite aquatic animal?」をクリックすると画面が遷移するが、「you chose.. poorly」と表示される。

とりあえず「myaquaticlife.exe」をGhidraやIDAに読ませて解析しようとするが上手くコードを読んでくれない。バイナリエディタで実行ファイルを読んでみる。

実行ファイルのバイナリ先頭

バイナリ先頭付近にUPXの文字が見つかる。逆アセンブラで解析できないのはUPXでパックされているためだ。したがってupxを使って以下のようにアンパックする。

UPXによるアンパック

これで逆アセンブラを使うことができるようになる。もっとも今回はランタイムエンジンがスクリプトを実行している形式であるため、実行ファイル本体解析の出番はなかった。

バイナリエディタでデータを読んでいくと、後半に画像ファイルなどのデータが格納されていることがわかる。アプリケーションを起動させて出てくる画面は内部的にはInternet Explorerのエンジンで動いており、Tempフォルダに構成ファイルが出力される。

Tempフォルダ内の構成ファイル

index.htmlを開いて最初の画面のソースコードを確認してみる。

<div class="main">
    <img class="img1" src="bg.gif" width=1190 height=100>
    <a href="script:Script17"><p class="txt1">What's your favorite aquatic animal?</p></a>
    <a href="script:Script1"><img class="img2" src="1.gif" width=200></a>
    <a href="script:Script2"><img class="img3" src="2.gif" width=200></a>
    <a href="script:Script3"><img class="img4" src="3.gif" width=200></a>
    <a href="script:Script4"><img class="img5" src="4.gif" width=200></a>
    <a href="script:Script5"><img class="img6" src="5.gif" width=200></a>
    <a href="script:Script6"><img class="img7" src="6.gif" width=200></a>
    <a href="script:Script7"><img class="img8" src="7.gif" width=200></a>
    <a href="script:Script8"><img class="img9" src="8.gif" width=200></a>
    <a href="script:Script9"><img class="img10" src="9.gif" width=140></a>
    <a href="script:Script10"><img class="img11" src="10.gif" width=200></a>
    <a href="script:Script11"><img class="img12" src="11.gif" width=100></a>
    <a href="script:Script12"><img class="img13" src="12.gif" width=60></a>
    <a href="script:Script13"><img class="img14" src="13.gif" width=120></a>
    <a href="script:Script14"><img class="img15" src="14.gif" width=200></a>
    <a href="script:Script15"><img class="img16" src="15.gif" width=200></a>
    <a href="script:Script16"><img class="img17" src="16.gif" width=300></a>
    <img class="img18" src="banner.gif"><img class="img19" src="banner.gif">
    <img class="img20" src="bubbles.gif"><img class="img21" src="bubbles.gif">
</div>

各アイコンをクリックしても何も起きないように見えていたが、実際にはスクリプトが実行されていることがわかる。これらのスクリプトを抽出することはできなかったが、バイナリエディタから概ね何をやっているかを確認することができる。

スクリプト部分のバイナリ

上の画像ではScript15が始まったあと、後半にpart4$='derelict:RTYXAc'という意味のありそうな文字列が見える。各Script番号でどのような文字列が記述されているかを調べた。

Script1  part$1='derelict:MZZWP'
Script2  part$2='lagan:BAJkR'
Script3  part$2='flotsam:DFWEyEW'
Script4  part$1='flotsam:PXopvM'
Script5  part$2='derelict:LDNCVYU'
Script6  part$3='derelict:yXQsGB'
Script7  part$2='jetsam:newaui'
Script8  part$3='lagan:QICMX'
Script9  part$1='lagan:rOPFG'
Script10 part$3='jetsam:HwdwAZ'
Script11 part$1='jetsam:SLdkv'
Script12 part$2='derelict:LSZvYSFHW'
Script13 part$3='flotsam:BGgsuhn'
Script14 part$4='lagan:GTYAKlwER'
Script15 part$4='derelict:RTYXAc'
Script16 part$2='lagan:GTXI'
Script17 PlugIn.PluginFunc19

Script1~16は何らかの文字列を代入しており、Script17ではプラグインのPluginFunc19を実行しているようだ。このプラグインは出力されたファイルに含まれるfathom.dllの可能性が高い。fathom.dllをGhidraで読み込むとPluginFunc19が見つかり、次の逆コンパイル結果を得ることができる。

void PluginFunc19(void)

{
  int *piVar1;
  char cVar2;
  byte bVar3;
  code *pcVar4;
  int iVar5;
  undefined4 *puVar6;
  undefined **ppuVar7;
  char *pcVar8;
  byte *pbVar9;
  undefined8 *puVar10;
  uint uVar11;
  bool bVar12;
  int local_88 [2];
  undefined4 local_80;
  HINSTANCE__ local_60;
  undefined4 uStack92;
  undefined4 uStack88;
  undefined4 uStack84;
  undefined4 local_50;
  undefined4 uStack76;
  undefined4 uStack72;
  undefined4 uStack68;
  HINSTANCE__ local_40;
  undefined4 uStack60;
  undefined4 uStack56;
  undefined4 uStack52;
  undefined4 local_30;
  undefined4 uStack44;
  undefined4 uStack40;
  undefined4 uStack36;
  uint local_14;
  
                    /* 0x2e40  11  PluginFunc19 */
  local_14 = DAT_102ceebc ^ (uint)&stack0xfffffff0;
  local_40 = (HINSTANCE__)0xa9a42596;
  uStack60 = 0x909a96a3;
  uStack56 = 0x38e5af9f;
  uStack52 = 0x169e81f9;
  local_30 = 0xa4e4cbf9;
  uStack44 = 0xba8f8f87;
  uStack40 = 0xd1a79dd2;
  uStack36 = 0xa8a3fc;
  if (*(int *)(DAT_102d577c + -0xc) != 0) {
    local_88[0] = DAT_102d5774;
    if (*(int *)(DAT_102d5774 + -0xc) != 0) {
      if (1 < *(int *)(DAT_102d5774 + -4)) {
        FUN_10002560(&DAT_102d5774,*(int *)(DAT_102d5774 + -0xc));
        local_88[0] = DAT_102d5774;
      }
      DAT_102d5774 = local_88[0];
      if (1 < *(int *)(DAT_102d577c + -4)) {
        FUN_10002560(&DAT_102d577c,*(int *)(DAT_102d577c + -0xc));
      }
      uVar11 = 0;
      do {
        pcVar8 = DAT_102d577c;
        do {
          cVar2 = *pcVar8;
          pcVar8 = pcVar8 + 1;
        } while (cVar2 != '\0');
        *(byte *)((int)&local_40 + uVar11) =
             *(byte *)((int)&local_40 + uVar11) ^
             DAT_102d577c[uVar11 % (uint)(pcVar8 + -(int)(DAT_102d577c + 1))];
        *(char *)((int)&local_40 + uVar11) =
             *(char *)((int)&local_40 + uVar11) - *(char *)(uVar11 % 0x11 + local_88[0]);
        uVar11 = uVar11 + 1;
      } while ((int)uVar11 < 0x1f);
      local_60 = local_40;
      uStack92 = uStack60;
      uStack88 = uStack56;
      uStack84 = uStack52;
      local_50 = local_30;
      uStack76 = uStack44;
      uStack72 = uStack40;
      uStack68 = uStack36;
      iVar5 = FUN_10002bc0((BYTE *)&local_60,(undefined4 *******)&local_80);
      if (iVar5 != 0) goto LAB_10003016;
      pbVar9 = &DAT_1024f360;
      puVar6 = &local_80;
      do {
        bVar3 = *(byte *)puVar6;
        bVar12 = bVar3 < *pbVar9;
        if (bVar3 != *pbVar9) {
LAB_10002f74:
          uVar11 = -(uint)bVar12 | 1;
          goto LAB_10002f79;
        }
        if (bVar3 == 0) break;
        bVar3 = *(byte *)((int)puVar6 + 1);
        bVar12 = bVar3 < pbVar9[1];
        if (bVar3 != pbVar9[1]) goto LAB_10002f74;
        puVar6 = (undefined4 *)((int)puVar6 + 2);
        pbVar9 = pbVar9 + 2;
      } while (bVar3 != 0);
      uVar11 = 0;
LAB_10002f79:
      if (uVar11 == 0) {
        ppuVar7 = FUN_10006ce4();
        if (ppuVar7 == (undefined **)0x0) {
          FUN_100023d0(-0x7fffbffb);
          pcVar4 = (code *)swi(3);
          (*pcVar4)();
          return;
        }
        local_88[0] = (**(code **)(*ppuVar7 + 0xc))();
        local_88[0] = local_88[0] + 0x10;
        uVar11 = FUN_100023f0(local_88,(HMODULE)&local_40);
        if ((char)uVar11 == '\0') {
          puVar10 = &local_40;
          do {
            cVar2 = *(char *)puVar10;
            puVar10 = (undefined8 *)((int)puVar10 + 1);
          } while (cVar2 != '\0');
          FUN_10002800(local_88,&local_40,(uint)((int)puVar10 - ((int)&local_40 + 1)));
        }
        FUN_10002220(&DAT_102d577c,local_88);
        LOCK();
        piVar1 = (int *)(local_88[0] + -4);
        iVar5 = *piVar1;
        *piVar1 = *piVar1 + -1;
        if (iVar5 == 1 || iVar5 + -1 < 0) {
          (**(code **)(**(int **)(local_88[0] + -0x10) + 4))((int **)(local_88[0] + -0x10));
          FUN_101d4266();
          return;
        }
        goto LAB_10003016;
      }
    }
  }
  FUN_10002800(&DAT_102d577c,(undefined8 *)"you chose.. poorly",0x12);
LAB_10003016:
  FUN_101d4266();
  return;
}

IDAで逆アセンブルするとよりわかりすいが、関数中の&DAT_1024f360は32桁の文字列6c5215b12a10e936f8de1e42083ba184を読み込んでおり、何らかの比較を行って処理を分岐させている。

32桁といえばMD5ハッシュ値が思い当たる。処理を調べていくと、先の文字列を読み込む直前のiVar5 = FUN_10002bc0((BYTE *)&local_60,(undefined4 *******)&local_80);で呼ばれているFUN_10002bc0でハッシュ計算を行っている。

void __cdecl FUN_10002bc0(BYTE *param_1,undefined4 *******param_2)

{
  BYTE BVar1;
  BOOL BVar2;
  BYTE *pBVar3;
  uint uVar4;
  DWORD local_38;
  HCRYPTPROV local_34;
  HCRYPTHASH local_30;
  BYTE local_2c [16];
  undefined4 local_1c;
  undefined4 uStack24;
  undefined4 uStack20;
  undefined4 uStack16;
  undefined local_c;
  uint local_8;
  
  local_8 = DAT_102ceebc ^ (uint)&stack0xfffffffc;
  local_c = 0;
  local_34 = 0;
  local_30 = 0;
  local_1c = 0x33323130;
  uStack24 = 0x37363534;
  uStack20 = 0x62613938;
  uStack16 = 0x66656463;
  BVar2 = CryptAcquireContextA(&local_34,(LPCSTR)0x0,(LPCSTR)0x0,1,0xf0000000);
  if (BVar2 == 0) {
    GetLastError();
    FUN_10003480((int)"CryptAcquireContext failed: %d\n");
    FUN_101d4266();
    return;
  }
  BVar2 = CryptCreateHash(local_34,0x8003,0,0,&local_30);
  if (BVar2 != 0) {
    pBVar3 = param_1;
    do {
      BVar1 = *pBVar3;
      pBVar3 = pBVar3 + 1;
    } while (BVar1 != '\0');
    BVar2 = CryptHashData(local_30,param_1,(DWORD)(pBVar3 + -(int)(param_1 + 1)),0);
    if (BVar2 != 0) {
      local_38 = 0x10;
      BVar2 = CryptGetHashParam(local_30,2,local_2c,&local_38,0);
      if (BVar2 == 0) {
        GetLastError();
        FUN_10003480((int)"CryptGetHashParam failed: %d\n");
      }
      else {
        uVar4 = 0;
        if (local_38 != 0) {
          do {
            FUN_100034b0(param_2,0x20,(int)&DAT_1024f2d4);
            uVar4 = uVar4 + 1;
            param_2 = (undefined4 *******)((int)param_2 + 2);
          } while (uVar4 < local_38);
        }
      }
      CryptDestroyHash(local_30);
      CryptReleaseContext(local_34,0);
      FUN_101d4266();
      return;
    }
    GetLastError();
    FUN_10003480((int)"CryptHashData failed: %d\n");
    CryptReleaseContext(local_34,0);
    CryptDestroyHash(local_30);
    FUN_101d4266();
    return;
  }
  GetLastError();
  FUN_10003480((int)"CryptAcquireContext failed: %d\n");
  CryptReleaseContext(local_34,0);
  FUN_101d4266();
  return;
}

ハッシュアルゴリズムの種類はCryptCreateHash(local_34,0x8003,0,0,&local_30);の部分( https://docs.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptcreatehash )で指定されており、2番目の引数のALG_IDについて調べると、0x8003はMD5を指定していることがわかる( https://docs.microsoft.com/ja-jp/windows/win32/seccrypto/alg-id )。文字列の正体はMD5ハッシュ値で間違いない。

x64dbgを使って実際にどのような処理が行われているのかを調べる。今回はdllをデバッグする必要があるので、やり方をメモしておく。x64dbg(今回は32bitアプリケーションのためx32dbg.exe)のoptions→PreferencesからSettingsを開く。

x64dbgのSettings画面

Settingsで、DLL LoadとDLL Entryにチェックを入れれば、それぞれDLLがLoadされたときとDLLのコードを実行したときにbreakpointとして実行を止めることができる。

準備ができたら「myaquaticlife.exe」を読み込み、左下にfathom.dllのLoadが出るまでRunを繰り返す。Loadで止まったらSymbolsタブからfathom.dll内の関数を確認して、PluginFunc19にbreakpointを張ると、ここからデバッグできるようになる。

PluginFunc19にbreakpointを張る

Runを続けるとアプリケーションの画面が立ち上がり、Loop状態になる。「What's your favorite aquatic animal?」をクリックしてScript17を実行させればPluginFunc19が実行されるので、適当にアイコンをクリックしたりしてからPluginFunc19の処理を調べていく。

6c5215b12a10e936f8de1e42083ba184との比較部分のcmp命令に到達する条件を調べると、文字列をクリックしてScript17を実行する前に、動物アイコンをクリックしてflotsam・jetsamが共に1回以上選ばれるようにScript1~Script16を実行させた場合であることがわかった。例えば1,2,3,7番目の動物をクリックして、derelict:MZZWP、lagan:BAJkR、flotsam:DFWEyEW、jetsam:newauiのときにcmp命令に到達すると次のような状態になっている。

MD5ハッシュ値同士の比較部分

EAXが指定するアドレス0019CF40に比較するMD5ハッシュ値が含まれているので、x64dbg上でこれを6c5215b12a10e936f8de1e42083ba184に変更してどのように処理が進むか確かめる。

実行画面3

流石に分岐を強制的に曲げてflagは出てこなかったが、flagが誤った文字列で復号される。つまりMD5ハッシュ値が6c5215b12a10e936f8de1e42083ba184になる文字列を特定できれば正しく復号されたflagが出るはずだ。

MD5計算に入力されるバイト列は、pluginFunc19前半の以下の部分で作られている。

  local_40 = (HINSTANCE__)0xa9a42596;
  uStack60 = 0x909a96a3;
  uStack56 = 0x38e5af9f;
  uStack52 = 0x169e81f9;
  local_30 = 0xa4e4cbf9;
  uStack44 = 0xba8f8f87;
  uStack40 = 0xd1a79dd2;
  uStack36 = 0xa8a3fc;
  if (*(int *)(DAT_102d577c + -0xc) != 0) {
    local_88[0] = DAT_102d5774;
    if (*(int *)(DAT_102d5774 + -0xc) != 0) {
      if (1 < *(int *)(DAT_102d5774 + -4)) {
        FUN_10002560(&DAT_102d5774,*(int *)(DAT_102d5774 + -0xc));
        local_88[0] = DAT_102d5774;
      }
      DAT_102d5774 = local_88[0];
      if (1 < *(int *)(DAT_102d577c + -4)) {
        FUN_10002560(&DAT_102d577c,*(int *)(DAT_102d577c + -0xc));
      }
      uVar11 = 0;
      do {
        pcVar8 = DAT_102d577c;
        do {
          cVar2 = *pcVar8;
          pcVar8 = pcVar8 + 1;
        } while (cVar2 != '\0');
        *(byte *)((int)&local_40 + uVar11) =
             *(byte *)((int)&local_40 + uVar11) ^
             DAT_102d577c[uVar11 % (uint)(pcVar8 + -(int)(DAT_102d577c + 1))];
        *(char *)((int)&local_40 + uVar11) =
             *(char *)((int)&local_40 + uVar11) - *(char *)(uVar11 % 0x11 + local_88[0]);
        uVar11 = uVar11 + 1;
      } while ((int)uVar11 < 0x1f);
      local_60 = local_40;
      uStack92 = uStack60;
      uStack88 = uStack56;
      uStack84 = uStack52;
      local_50 = local_30;
      uStack76 = uStack44;
      uStack72 = uStack40;
      uStack68 = uStack36;
      iVar5 = FUN_10002bc0((BYTE *)&local_60,(undefined4 *******)&local_80);

x64dbgでこの処理を追っていくと、使われている文字列はjetsamとflotsamの2つのみで、他の2つの文字列は飾りであることがわかった。さらにMD5ハッシュ関数に入力されるバイト列は、上記コードの最初に宣言される31バイトのバイト列に対して、jetsamとflotsamで1文字ずつXORとSUBをかけたものであることがわかった。よって目標となるハッシュ値を得る入力文字列を総当りするPythonプログラムを書いた。

import hashlib
import itertools

goal = "6c5215b12a10e936f8de1e42083ba184"

def calcHash(xjetsam,xflotsam):
        table = [0x96,0x25,0xa4,0xa9,0xa3,0x96,0x9a,0x90,0x9f,0xaf,0xe5,0x38,0xf9,0x81,0x9e,0x16,0xf9,0xcb,0xe4,0xa4,0x87,0x8f,0x8f,0xba,0xd2,0x9d,0xa7,0xd1,0xfc,0xa3,0xa8,0x00]
        flotsam = xflotsam
        jetsam = xjetsam + "\x00\xab\xab\xab\xab\xab\xab\xab\xab\xfe\x00\x00"
        str = b""
        for i in range(0x1f):
                table[i] = (table[i] ^ ord(flotsam[i%len(flotsam)]))%0x100
                table[i] = (table[i] - ord(jetsam[i%0x11]))%0x100
                str = str + table[i].to_bytes(1, byteorder="little")
        return hashlib.md5(str).hexdigest()



jetsams = ["SLdkv","newaui","HwdwAZ"]
flotsams = ["PXopvM","DFWEyEW","BGgsuhn"]
for i in range(3):
        for i2 in itertools.permutations(jetsams, i+1):
                for j in range(3):
                        for j2 in itertools.permutations(flotsams, i+1):
                                print(''.join(i2))
                                print(''.join(j2))
                                hh = calcHash(''.join(i2),''.join(j2))
                                print(hh)
                                if goal == hh :
                                        break
                        else:
                                continue
                        break
                else:
                        continue
                break
        else:
                continue
        break

プログラムを実行すると、目標のハッシュ値を得るjetsamとflotsamが判明する。

jetsam:SLdkvnewauiHwdwAZ
flotsam:PXopvMDFWEyEWBGgsuhn
MD5:6c5215b12a10e936f8de1e42083ba184

この文字列を作るには、jetsamがScript11→Script7→Script10、flotsamがScript4→Script3→Script13の順に動物アイコンをクリックして文字列をクリックすればいい。

順番通りクリックして先に進むとflagが得られる。

flag

s1gn_my_gu357_b00k@flare-on.com

05 - FLARE Linux VM (1point)

Because of your superior performance throughout the FLARE-ON 8 Challenge, the FLARE team has invited you to their office to hand you a special prize! Ooh – a special prize from FLARE ? What could it be? You are led by a strong bald man with a strange sense of humor into a very nice conference room with very thick LED dimming glass. As you overhear him mumbling about a party and its shopping list you notice a sleek surveillance camera. The door locks shut!

Excited, you are now waiting in a conference room with an old and odd looking computer on the table. The door is closed with a digital lock with a full keyboard on it.

Now you realise… The prize was a trap! They love escape rooms and have locked you up in the office to make you test out their latest and greatest escape room technology. The only way out is the door – but it locked and it appears you have to enter a special code to get out. You notice the glyph for U+2691 on it. You turn you attention to the Linux computer - it seems to have been infected by some sort of malware that has encrypted everything in the documents directory, including any potential clues.

Escape the FLARE Linux VM to get the flag - hopefully it will be enough to find your way out.

Hints:

FLARE Linux VMのVMファイルが配布されるが、手元の環境では都合上HyperVしか動作しなかったため、StarWind V2V Image Converterを使ってVMDKファイルをVHDXファイルに変換してから問題に着手した。

マルウェアに感染しているということで、自動起動されるプログラムの有無をcrontab -lコマンドで確認すると、/usr/lib/zyppeが登録されている。これがマルウェアのようだ。また/root/Documents/を確認すると以下の暗号化されたファイルが見つかる。

Documentsフォルダ内のファイル一覧

まずはzyppeファイルを取り出してGhidraで解析する。なお、ファイルの取り出しはSSHなどを使うが、今回はFTK Imager(バイナリエディタで良い)でVHDKファイルを読めばファイルシステムがそのまま見えることを利用して、バイナリデータを切り出してフォレンジック的に抽出した(このやり方だと色々なものを見ることができて一部の問題をショートカットできるが、後述する)。

zyppe:mainundefined8 main(void)

{
  bool bVar1;
  allocator *paVar2;
  char *__name;
  basic_ostream *this;
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_518 [32];
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_4f8 [32];
  basic_string *local_4d8 [4];
  basic_ofstream<char,std--char_traits<char>> local_4b8 [512];
  basic_ifstream<char,std--char_traits<char>> local_2b8 [528];
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_a8 [46];
  allocator<char> local_7a;
  allocator<char> local_79;
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_78 [32];
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_58 [40];
  char *local_30;
  dirent *local_28;
  DIR *local_20;
  
  allocator();
  paVar2 = (allocator *)getenv("HOME");
                    /* try { // try from 0040199b to 0040199f has its CatchHandler @ 00401cfe */
  basic_string((char *)local_a8,paVar2);
  ~allocator(&local_7a);
                    /* try { // try from 004019bb to 004019f6 has its CatchHandler @ 00401db2 */
  operator+=(local_a8,"/Documents");
  __name = (char *)c_str();
  local_20 = opendir(__name);
  if (local_20 != (DIR *)0x0) {
    while (local_28 = readdir(local_20), local_28 != (dirent *)0x0) {
      allocator();
                    /* try { // try from 00401a30 to 00401a34 has its CatchHandler @ 00401d18 */
      basic_string((char *)local_518,(allocator *)local_28->d_name);
      ~allocator(&local_79);
      find_last_of((char *)local_518,(ulong)&DAT_00401fb5);
                    /* try { // try from 00401a78 to 00401a7c has its CatchHandler @ 00401d9e */
      substr((ulong)local_4f8,(ulong)local_518);
      bVar1 = operator==<char,std--char_traits<char>,std--allocator<char>>
                        ((basic_string *)local_518,".");
      if (((bVar1 == false) &&
          (bVar1 = operator==<char,std--char_traits<char>,std--allocator<char>>
                             ((basic_string *)local_518,".."), bVar1 == false)) &&
         (bVar1 = operator==<char,std--char_traits<char>,std--allocator<char>>
                            ((basic_string *)local_4f8,"broken"), bVar1 == false)) {
        bVar1 = false;
      }
      else {
        bVar1 = true;
      }
      if (!bVar1) {
                    /* try { // try from 00401af5 to 00401af9 has its CatchHandler @ 00401d8a */
        operator+<char,std--char_traits<char>,std--allocator<char>>
                  ((basic_string *)local_78,(char *)local_a8);
                    /* try { // try from 00401b12 to 00401b16 has its CatchHandler @ 00401d2c */
        operator+<char,std--char_traits<char>,std--allocator<char>>
                  (local_4d8,(basic_string *)local_78);
        ~basic_string(local_78);
                    /* try { // try from 00401b2d to 00401b31 has its CatchHandler @ 00401d76 */
        basic_ifstream();
        operator|(8,4);
                    /* try { // try from 00401b57 to 00401b6a has its CatchHandler @ 00401d62 */
        open((basic_string *)local_2b8,(int)register0x00000020 - 0x4d8);
        basic_ofstream();
        operator|(0x10,4);
                    /* try { // try from 00401b92 to 00401b96 has its CatchHandler @ 00401d4e */
        operator+<char,std--char_traits<char>,std--allocator<char>>
                  ((basic_string *)local_58,(char *)local_4d8);
                    /* try { // try from 00401baa to 00401bae has its CatchHandler @ 00401d3d */
        open((basic_string *)local_4b8,(int)register0x00000020 - 0x58);
        ~basic_string(local_58);
                    /* try { // try from 00401bc0 to 00401c6d has its CatchHandler @ 00401d4e */
        local_30 = (char *)operator.new[](0x400);
        read((char *)local_2b8,(long)local_30);
        encrypt(local_30);
        write((char *)local_4b8,(long)local_30);
        close();
        close();
        __name = (char *)c_str();
        remove(__name);
        this = operator<<<char,std--char_traits<char>,std--allocator<char>>
                         ((basic_ostream *)cout,(basic_string *)local_518);
        this = operator<<<std--char_traits<char>>(this," is now a secret");
        operator<<((basic_ostream<char,std--char_traits<char>> *)this,
                   _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
        ~basic_ofstream(local_4b8);
        ~basic_ifstream(local_2b8);
        ~basic_string((basic_string<char,std--char_traits<char>,std--allocator<char>> *)local_4d8);
      }
      ~basic_string(local_4f8);
      ~basic_string(local_518);
    }
                    /* try { // try from 00401ce0 to 00401ce4 has its CatchHandler @ 00401db2 */
    closedir(local_20);
  }
  ~basic_string(local_a8);
  return 0;
}

main関数を確認すると、予想通り、ユーザーHOMEのDocumentsディレクトリ内のファイルを暗号化する仕組みとなっている。暗号化を行う関数であるencrypt関数を確認する。

zyppe:encryptvoid encrypt(char *param_1)

{
  uint uVar1;
  int iVar2;
  int aiStack1128 [256];
  undefined8 local_68;
  undefined8 local_60;
  undefined8 local_58;
  undefined8 local_50;
  undefined8 local_48;
  undefined8 local_40;
  undefined4 local_38;
  undefined local_34;
  int local_2c;
  int local_28;
  int local_24;
  int local_20;
  int local_1c;
  int local_18;
  int local_14;
  int local_10;
  int local_c;
  
  local_68 = 0x7465726365732041;
  local_60 = 0x6c206f6e20736920;
  local_58 = 0x2061207265676e6f;
  local_50 = 0x6f20746572636573;
  local_48 = 0x656d6f732065636e;
  local_40 = 0x776f6e6b20656e6f;
  local_38 = 0x74692073;
  local_34 = 0;
  local_c = 0;
  while (local_c < 0x100) {
    aiStack1128[local_c] = local_c;
    local_c = local_c + 1;
  }
  local_10 = 0;
  local_14 = 0;
  while (local_14 < 0x100) {
    iVar2 = aiStack1128[local_14] + local_10 +
            (int)*(char *)((long)&local_68 + (long)(local_14 % 0x34));
    uVar1 = (uint)(iVar2 >> 0x1f) >> 0x18;
    local_10 = (iVar2 + uVar1 & 0xff) - uVar1;
    local_24 = aiStack1128[local_14];
    aiStack1128[local_14] = aiStack1128[local_10];
    aiStack1128[local_10] = local_24;
    local_14 = local_14 + 1;
  }
  local_18 = 0;
  local_10 = 0;
  local_1c = 0;
  local_20 = 0;
  while (local_20 < 0x400) {
    uVar1 = (uint)(local_18 + 1 >> 0x1f) >> 0x18;
    local_18 = (local_18 + 1 + uVar1 & 0xff) - uVar1;
    uVar1 = (uint)(aiStack1128[local_18] + local_10 >> 0x1f) >> 0x18;
    local_10 = (aiStack1128[local_18] + local_10 + uVar1 & 0xff) - uVar1;
    local_28 = aiStack1128[local_18];
    aiStack1128[local_18] = aiStack1128[local_10];
    aiStack1128[local_10] = local_28;
    uVar1 = (uint)(aiStack1128[local_18] + aiStack1128[local_10] >> 0x1f) >> 0x18;
    local_2c = aiStack1128
               [(int)((aiStack1128[local_18] + aiStack1128[local_10] + uVar1 & 0xff) - uVar1)];
    param_1[local_20] = param_1[local_20] ^ (byte)local_1c ^ (byte)local_2c;
    local_1c = local_2c;
    local_20 = local_20 + 1;
  }
  return;
}

RC4に近い暗号化アルゴリズムが使われており、暗号鍵はlocal_68以下に予め入力されている。暗号鍵は見るからにASCIIコードなので変換してみると、A secret is no longer asecret once someone knows itというメッセージになる。

コードを読むと暗号データは必ず1024byteになり、元ファイルのデータよりも長い部分は0x00で埋められる。暗号化は生成された乱数とXORを取ることで行われており、param_1[local_20] = param_1[local_20] ^ (byte)local_1c ^ (byte)local_2c;が該当する。復号化手段としてはアルゴリズム全体を書き下す方法もあるが、容量0のファイルを暗号化させればXORさせる乱数がそのまま出力されることを利用して、出力結果と暗号データをもう一度XORして元のデータを得る方法を使う。また、あとで気付いたが暗号化されたファイルをもう一度暗号化すれば元のデータを得ることができる。

容量0のファイルを用意して、zyppeを起動して暗号化させる。ここではzeroファイルを暗号化したzero.brokenファイルを作って、次のPythonコードで復号化を行った。なお暗号化ファイルはdocフォルダに設置し、復号化ファイルはdecフォルダに生成している。

filelist = ["tiramisu.txt.broken","instant_noodles.txt.broken","reeses.txt.broken","shopping_list.txt.broken","nachos.txt.broken","sausages.txt.broken","backberries.txt.broken","natillas.txt.broken","spaghetti.txt.broken","banana_chips.txt.broken","nutella.txt.broken","strawberries.txt.broken","blue_cheese.txt.broken","oats.txt.broken","tacos.txt.broken","donuts.txt.broken","omelettes.txt.broken","tomatoes.txt.broken","dumplings.txt.broken","oranges.txt.broken","udon_noddles.txt.broken","ice_cream.txt.broken","raisins.txt.broken","ugali.txt.broken","iced_coffee.txt.broken","rasberries.txt.broken","unagi.txt.broken"]

f = open('zero.broken', 'rb') 
declist = f.read()

for i in filelist:
        f = open('doc/'+i, 'rb') 
        context = f.read()
        plaintext = b''
        stock = b''
        for j in range(0x400):
                tmp = int(context[j]) ^ int(declist[j])
                if tmp==0:
                        stock += tmp.to_bytes(1,'little')
                else :
                        if len(stock)>0:
                                plaintext += stock
                                stock = b''
                        plaintext += tmp.to_bytes(1,'little')
        print(str(i)+":")
        print(plaintext)
        fw = open('dec/'+i[0:len(i)-7], 'wb') 
        fw.write(plaintext)

一応これで復号化は完了したが、tiramisu.txt.brokenについては、元のファイルの容量が1024byteより大きかったようで、実は完全に復元できていない。これは最終的にバイナリデータで読んだファイルシステム内に暗号化される前のファイルの情報が残っており、抽出することができた。ファイル名でVMのバイナリ内を検索すると暗号化される前のデータを得ることができ、実際にはファイルの復号化作業は必要ではない。

復号化されたDocumentsフォルダ内のデータは以下のとおり。tiramisu.txtはファイルシステムから直接抽出している。

tiramisu.txt:
aa68c1adcfb5bcb550b4d2e44da48bb59b1cc86701cb3df2b2c81bc468e3c710b6e6a07c1bce4a04b2a34192823aa0b64f883783170f6c21d00b20c4a44c6acfffc893f63bc2526a5b7704683c444689d0fb709116503be628b6430a765ba80a9f97627abc0ca6c2c61a8b25411b138cf47550ed48d227cb9a99575a09fa36bacd0e3a6f464eb2ba492608c0644431f4db020cb95065254562d6099cf4c32459f3942335334b54255573e1b689a876123f24e8760c75a60af63454d528e7cae315e6094db7d0d40342751df0cf528f7a67257082f86d47f9cd104f3d98710ccd507c343afdd2ce14fdc3d702488629a96fb21453e608219162a57496f740429fa11266846daff565ebb68b1e03b4835ee64a69a6d5540ba731f5c924ff74e7cb086ae025aa39d392630bbe3efd40ac4d73d512e040659d91be0d05a3a495fa3e4ecd295fd826f0f8a5b2d4b4bc58dd6fd2740c11de5959946caeadbebfdadc8a50375e820527f4c42809adb36202c4b7b447f9fcaff0074a4a4c94fac18d351ef5ed4853d2b7fba196c0a79e19fbbcf927cbbe70065c49826fffd8e5a2a2f14822227f84a6a4e771561d8fbf736eb0cc4730a3cd187fdc07e88010546aea8ac01aa70e436ea0311b77b9f6429bbbe4d166cc86fdb5483716c711ac3c2b80920bcb86ad112340ab7a57b17380ce8f97ef42e4e1ea57367a0a1135e8622d31ec013e00d135cce8dc77000d217a9d8954168f103ff95bdf8e1663bf903962674b334b93071921efdf945063ce08dd544d775b93a1178c4ad4f429345e51f91ebe25ef5ddef43d56af2ba3c5ca74e90c1339

instant_noodles.txt:
Xli$9xl$f}xi$sj$xli$teww{svh$mw>$4|QW\x0e

reeses.txt:
V2UgTE9WRSAiUmVlc2UncyIsIHRoZXkgYXJlIGdyZWF0IGZvciBldmVyeXRoaW5nISBUaGV5IGFyZSBhbWF6aW5nIGluIGljZS1jcmVhbSBhbmQgdGhleSBldmVuIHdvcmsgYXMgYSBrZXkgZm9yIFhPUiBlbmNvZGluZy4K

shopping_list.txt:
/\n[U]don noodles\n[S]trawberries\n[R]eese's\n/\n[B]anana chips\n[I]ce Cream\n[N]atillas\n/\n[D]onuts\n[O]melettes\n[T]acos\n

nachos.txt:
H\xe6\x9d\xba\xdd\xfd\xc5\xc1\xe2\xb5\xef\x8aF\x15\xd6{\xf8\xa8(\t\xearD Q\xe6N\xb6)\x8aj;\x9cd+\xfa\xa7\xf6\xb0\x12\x97\xc7VB\x9de\xeb\x86\xecg^\xccg\\"\xd5\xf2\x14P>\xf4\x18C\xd1\xa4\xbb\xaeu:U\xa31D\x1e"\x19MtL\xf9f\xc6\x0b"\xf2\x02,y\x01\xb5k*A\x8a\x8e\xc5\xe1\x9ch\xa5\x0e9\xad\x10C\xf5\x0c\xa4\xa5e\xc2\x10\xdf\xf3\x04H\xd7P\x12;\xbfE{\xc2\xads\xc2\x97\xc2~Y\xb9\x86\xa4 \xe7\xfb\x06\xcf3\xb0\xcbF\xfe\x13\xd81\xac\x1b\xad\x92\rz\xa1\xbc\xc9\xde\xa9\xa8\tQ\x8dPS\xc8\x9c\x98\xe5r\xd3([\xaf\xb4\x95.\x97V\xb2L

sausages.txt:
*4\xb2\x10\x19\xb9:\x101\xbc:\xb2\x10\xb73\x10:4\xb2\x108\xb0\xb9\xb9\xbb\xb792\x10\xb4\xb9\x10\x18<\x99\x1a\x05

backberries.txt:
\x1b\x03E\n\nRS3\x17\x00S\x0bH\x07r\x02\n\x1c\x01\x07\x1a<E\x08\x12\x11O\x00~E\x11\x1b\x00\x07\x1c<\t\x1cS\x11O\x1a<\x02E\x07\rF\x07r\x06\x04\x1dET\x12$\x00E\n\nRS;\x16E\x07\n\x07\x117E\x04S\x07F\x00:E\x00\x0b\x15B\x01&KE<\x11O\x16 \x12\x0c\x00\x00\x07\n=\x10E\x04\x0cK\x1fr\x07\x00S\tH\x109\x00\x01S\rB\x017E\x03\x1c\x17B\x057\x17E;$\x07;\x13E-2D-

natillas.txt:
E\xe7\x9d\xb7\xda\xed\xc5\xec\xc0\x9b\xca\xef\x08\x00\xc7s\xf9\xe4>\x1f\xf5 h/\x1d\xd9G\xf7,\x8d-~\xc8J\'\xe5\xee\xfa\xf5$\x9f\x8bET\x8fe\xf5\x99\xa93P\x80a\x133\xc9\xf5\x08\\?\xbc\x18T\xc8\xb2\xb0\xe7y7]\xbf1G\x1f3Q\x02w@\xb5z\x8e\x03/\xfe\x02\x04S5\x80M\x16`\xd5\x8e\xc2\xe0\x94!\x99\x00\'\xf4\x06E\xa0\x10\xf0\xbeq\xc5Q\xe8\xe3VP\xceA\x1bu\xebTh\xc9\xbfn\xde\xc4\xcay\x10\xb5\x9a`m\xe3\xb5\x03\x81!\xaf\xcf\\\xad\x10\x84p\x86\x01\xe4\xbe\x0cw\xba\xb8\xc4\xd9\xe8\xf0F\x05\x84V\x1b\xc5\x9c\x82\xe91\xd6"D\xe9\xa5\x89"\x8b\x18\xf2)\xe6I\xe7E\xdf\xf7h\xbe\xd7A\xee\xa3\xe5\x8e\xf8\xb9;\x18\xe3\xd2H1\xd3\xdd\x9c\xce\xe9\xae>\xb5j\x10M%]\xb8G2\xa5\x05f`

spaghetti.txt:
\xa47\x10:4\xb2\x10#&\xa0)\xa2\x106\xb07\xb3\xba\xb0\xb3\xb2\x10\x11\xb98\xb0\xb34\xb2::\xb4\x11\x10\xb4\xb9\x10\x11\xb1\x99!4-\x19462$)8\x11\x17\x05

banana_chips.txt:
\x13\x17\x00S\x1cH\x06r\x02\n\x1c\x01\x07\x12&E\x08\x12\x11O\x00mE2\x16EK\x1c$\x00E\x1e\x04S\x1b!E\x04\x07Ea?\x137 REp\x16r\x10\x16\x16ES\x1b;\x16E\x15\nU\x1e'\t\x04S\x04\x07\x1f=\x11E\x07\n\x07\x177\x06\n\x17\x00\x07\x11+\x11\x00\x00_\x07Q\x17+&<!b7\r'<' \x07XrWRSN\x07=\x07('67\x16SxE+&(e6\x00WE^Ei&\x1f' !V\x05y

nutella.txt:
U\xe0\xd8\xee\x83\xec\x8d\xa7\xcc\x8d\xc9\xaaF\x0e\xd5:\xe1\xe0:L\xbaaR2J\xe5E\xf2e\x8ard\x9c\x126\xa5\xf8\x84

strawberries.txt:
\xa47\x10:4\xb2\x10#&\xa0)\xa2\x10:\xb2\xb0\xb6\x10\xbb\xb2\x106\xb4\xb5\xb2\x10:\xb7\x10\xb98\xb2\xb0\xb5\x10\xb47\x10\xb1\xb72\xb2\x17\x10\xac\xb7\xba\x10\xb94\xb7\xba62\x106\xb2\xb097\x10\xb7\xba9\x106\xb07\xb3\xba\xb0\xb3\xb2\x16\x10\xb7:4\xb29\xbb\xb4\xb9\xb2\x10\xbc\xb7\xba\x10\xbb\xb07:\x101\xb2\x10\xb016\xb2\x10:\xb7\x10\xb98\xb2\xb0\xb5\x10\xbb\xb4:4\x10\xba\xb9\x10\xbb4\xb27\x10\xbc\xb7\xba\x10\xb2\xb9\xb1\xb08\xb2\x10\x14\xb43\x10\xbc\xb7\xba\x10\xb6\xb07\xb0\xb3\xb2\x10:\xb7\x10\xb2\xb9\xb1\xb08\xb2\x90\x94\x17\x10#\xb79\x10\xb2<\xb0\xb686\xb2\x16\x10\xb47\xb9:\xb2\xb02\x10\xb73\x10\x11\xb9:9\xb0\xbb1\xb299\xb4\xb2\xb9\x11\x10\xbb\xb2\x10\xb9\xb0\xbc\x10\x11\xb1\x99)\xbc\xac,2\xb4-,%\xbc\xb0\xab+=\x11\x17\x05

blue_cheese.txt:
\x06\r\x00SQS\x1br\x07\x1c\x07\x00\x07\x1c4E\x11\x1b\x00\x07\x033\x16\x16\x04\nU\x17r\x0c\x16IE\x17\x0baPo

oats.txt:
Kww jvkugh xatnfk phz JDMZG kswm dr Liqvksn. Tciq bwuk o xuigz an keharzwluvi jhqfa efp pcms crzel owpmsnsvxaav qe Hsioxwd!\npvkdo://trmlfmt.tci/aieeyi_06\njkhls://oaafbgi.qkm/HediitvAaccefuk\n

tacos.txt:
27420b6486cfe04e872b09e3bddaa76ef575e5ef36e8306db062e2febc6d6cab46b4a761b2c0c3ce6f405c7add12ce0e5fee7e6c6b7ce1fb0af633e6af548f1d2c3676c6a6e8a074dd216d1c884a577abbae5f45bda386d4d0dfae81fcf5ba02

donuts.txt:
Din moq agos etcp Ememog Lhobeihz Awttivt ytxtv drwvgoswps?\n

omelettes.txt:
Kww jvkugh xatnfk phz JDMZG kswm dr Liqvksn. Oolwdekjs phzc emg ivh wnbvq mvf ecp lzx qac nvore zzwz qh pcq gzx ltm hcoc.\nhoxhe://byzhpem.ggy/ipraia_06\ncxlba://vnwptzv.uau/qjondvv1\nzfbrj://hsioxwd.kqd/AwlrejqUgtvwndg\n

tomatoes.txt:
4892fbcc38c4a6aeeb2435081ec6cd83535b7f4e1ded18ad2a1ba9d5c5f825a6e53b343b0a04d39bf4c1fde228d0d958abea262bc8b4e5ac8eda08908be43e0354cf0bd78a3bbc23149776da6fdb4d4118f8095540113a2719e41308305727ac02831d3018e853d96b0f9d22e2866bfff4c12c874583679c67a6f40309df994c00284a76a514a3f167afb1c0d6e36a7e650505abfbf9a6f5c2b40d0399f565f6011a4e3b42233b361bc8bf1bfd9349349a534559648f5dedf87c0dce23d676f1

dumplings.txt:
Abn lef emadkxp frceqdnhe? Tah gdcktm temyku xxo qo ktyhzn! Zd'k raooua, por uda ztykqh.\n

oranges.txt:
Fpg 8kv xyoi gr bjv dwsnagdl kj: 0l60\n

udon_noddles.txt:
"ugali", "unagi" and "udon noodles" are delicious. What a coincidence that all of them start by "u"!\n

ice_cream.txt:
Mj$xlmw$gleppirki$mw$xss$hmjjmgypx$erh$}sy${erx$xs$kmzi$yt$sv$nywx$mr$gewi$}sy$ksx$lyrkv}0${lex$efsyx$feomrk$wsqi$qyjjmrwC$Xv}$xlmw$vigmti>\x0e4$1$Gmrreqsr\x0e5$1$Fyxxiv$594kv\x0e6$1$Piqsr$536\x0e7$1$Ikkw$7\x0e8$1$Wykev$594kv\x0e9$1$Jpsyv$694kv\x0e:$1$Qmpo$74kv\x0e;$1$Mgmrk$wykev$54kv\x0e<$1$Ettpi$544kv\x0e=$1$Vewtfivvmiw$544kv\x0e\x0eQm|$4$xs$=$erh$feoi$jsv$74$qmryxiw$ex$5<4\xc6\xb4G2\x0e\x0e

raisins.txt:
VGhlIDNyZCBieXRlIG9mIHRoZSBwYXNzd29yZCBpcy4uIGl0IGlzIGEgam9rZSwgd2UgZG9uJ3QgbGlrZSByYWlzaW5zIQo=

ugali.txt:
Ugali with Sausages or Spaghetti is tasty. It doesn\xe2\x80\x99t matter if you rotate it left or right, it is still tasty! You should try to come up with a great recipe using CyberChef.\n

iced_coffee.txt:
Xli$srp}$tvsfpiq${mxl$VG8$mw$xlex$}sy$riih$e$oi}2$Xli$JPEVI$xieq$rsvqepp}$ywiw$xlmw$ryqfiv>$&WVIJFI&$,ew$er$YXJ1<$wxvmrk-2$Mj$}sy$lezi$rs$mhie${lex$xlex$qierw0$}sy$wlsyph$kmzi$yt$erh$feoi$wsqi$qyjjmrw2\x0e

rasberries.txt:
VGhlIDNyZCBieXRlIG9mIHRoZSBwYXNzd29yZCBpczogMHg1MQo=

unagi.txt:
The 1st byte of the password is 0x45\n

すでに復号化されたデータも混ざっているが、まずはshopping_list.txtを見る。

/\n[U]don noodles\n[S]trawberries\n[R]eese's\n/\n[B]anana chips\n[I]ce Cream\n[N]atillas\n/\n[D]onuts\n[O]melettes\n[T]acos\n

カッコで囲まれた部分の字を抜き出すと/usr/bin/dotというファイルパスになる。まあ復号化する前にこのファイルは見つけていたのだが、実行するとパスワードを要求される。

dotファイルの実行

dotファイルを抽出してGhidraで解析してみる。

dot:mainundefined8 main(void)

{
  bool bVar1;
  basic_ostream *this;
  ulong uVar2;
  long lVar3;
  char *pcVar4;
  ulong uVar5;
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_a8 [32];
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_88 [32];
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_68 [32];
  basic_string<char,std--char_traits<char>,std--allocator<char>> local_48 [43];
  allocator<char> local_1d;
  int local_1c;
  
  basic_string();
                    /* try { // try from 00401377 to 0040139f has its CatchHandler @ 0040158b */
  operator<<<std--char_traits<char>>((basic_ostream *)cout,"Password: ");
  operator>><char,std--char_traits<char>,std--allocator<char>>
            ((basic_istream *)cin,(basic_string *)local_88);
  while( true ) {
    basic_string((basic_string *)local_48);
                    /* try { // try from 004013ae to 004013b2 has its CatchHandler @ 00401555 */
    to_sha256((basic_string)0x98);
                    /* try { // try from 004013bf to 004013c3 has its CatchHandler @ 00401544 */
    bVar1 = operator!=<char,std--char_traits<char>,std--allocator<char>>
                      ((basic_string *)local_68,
                       "b3c20caa9a1a82add9503e0eac43f741793d2031eb1c6e830274ed5ea36238bf");
    ~basic_string(local_68);
    ~basic_string(local_48);
    if (bVar1 == false) break;
                    /* try { // try from 004013ec to 0040141b has its CatchHandler @ 0040158b */
    this = operator<<<std--char_traits<char>>((basic_ostream *)cout,"Wrong password!");
    this = (basic_ostream *)
           operator<<((basic_ostream<char,std--char_traits<char>> *)this,
                      _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
    operator<<<std--char_traits<char>>(this,"Password (ASCII):");
    operator>><char,std--char_traits<char>,std--allocator<char>>
              ((basic_istream *)cin,(basic_string *)local_88);
  }
  allocator();
                    /* try { // try from 00401440 to 00401444 has its CatchHandler @ 00401566 */
  basic_string((char *)local_a8,(allocator *)&DAT_00401763);
  ~allocator(&local_1d);
  local_1c = 0;
  while( true ) {
    uVar5 = SEXT48(local_1c);
    uVar2 = length();
    if (uVar2 <= uVar5) break;
    lVar3 = length();
                    /* try { // try from 0040149c to 0040151f has its CatchHandler @ 00401577 */
    pcVar4 = (char *)operator[](local_88,(lVar3 - local_1c) - 1);
    operator+=(local_a8,*pcVar4 + -1);
    local_1c = local_1c + 1;
  }
  operator+=(local_a8,"@flare-on.com");
  this = operator<<<std--char_traits<char>>((basic_ostream *)cout,"Correct password!");
  this = (basic_ostream *)
         operator<<((basic_ostream<char,std--char_traits<char>> *)this,
                    _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
  this = operator<<<std--char_traits<char>>(this,"Flag: ");
  this = operator<<<char,std--char_traits<char>,std--allocator<char>>(this,(basic_string *)local_a8)
  ;
  operator<<((basic_ostream<char,std--char_traits<char>> *)this,
             _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_);
  ~basic_string(local_a8);
  ~basic_string(local_88);
  return 0;
}

挙動としては入力されたパスワードのSHA256ハッシュ値をとってb3c20caa9a1a82add9503e0eac43f741793d2031eb1c6e830274ed5ea36238bfと比較して、一致するとパスワード文字列を材料にflagを作ってくれるようだ。つまり目的はdotに入力するパスワードを見つけることになる。

再び復号化されたファイル群に戻ろう。復号化時点で平文が得られたファイルは以下のものになる。

udon_noddles.txt:
"ugali", "unagi" and "udon noodles" are delicious. What a coincidence that all of them start by "u"!\n

ugali.txt:
Ugali with Sausages or Spaghetti is tasty. It doesn\xe2\x80\x99t matter if you rotate it left or right, it is still tasty! You should try to come up with a great recipe using CyberChef.\n

unagi.txt:
The 1st byte of the password is 0x45\n

unagi.txtからパスワードの1文字目は0x45であることがわかった。ugali.txtには残りのファイルを解く手がかりとして、CyberChefでrotateさせるレシピを作れと書いてある。

この方法で復号できそうなファイルを探していくと、例えばsausages.txtは以下のように復号できる。

CyberChefによるrotate復号

同様に他のファイルも復号し、次の3ファイルの平文が得られる。

strawberries.txt:
In the FLARE team we like to speak in code. You should learn our language, otherwise you want be able to speak with us when you escape (if you manage to escape!). For example, instead of "strawberries" we say "c3RyYXdiZXJyaWVz".

spaghetti.txt:
In the FLARE language "spaghetti" is "c3BhZ2hldHRp".

sausages.txt:
The 2st byte of the password is 0x34

sausages.txtからパスワードの2文字目は0x34であることがわかった。

次のヒントとして、FLAREチームではbase64エンコードを使用しているとほのめかされている(strawberriesとspaghettiのbase64はそれぞれc3RyYXdiZXJyaWVzとc3BhZ2hldHRpになる)ので、次はbase64でデコードできるファイルを探す。すると次の3つのファイルの平文が得られる。

reeses.txt:
We LOVE "Reese's", they are great for everything! They are amazing in ice-cream and they even work as a key for XOR encoding.

rasberries.txt:
The 3rd byte of the password is: 0x51

raisins.txt:
The 3rd byte of the password is.. it is a joke, we don't like raisins!

rasberries.txtからパスワードの3文字目は0x51であることがわかった。

次のヒントとして、Reese'sを鍵としてXORエンコードされていることが書かれている。CyberChefを使ってXORエンコードを試すと、次のようにファイルを復号できる。

CyberChefによるXOR復号

この方法で次の3つのファイルの平文が得られる。

blue_cheese.txt:
The 4th byte of the password is: 0x35

banana_chips.txt:
Are you good at maths? We love maths at FLARE! We use this formula a lot to decode bytes: "ENCODED_BYTE + 27 + NUMBER1 * NUMBER2 - NUMBER3"

backberries.txt.broken:
If you are not good in maths, the only thing that can save you is to be a bash expert. Otherwise you will be locked here forever HA HA HA!

blue_cheese.txtからパスワードの4文字目は0x35であることがわかった。

次のヒントはバイト列のデコード方法である。どうやらデータをバイト毎に;ENCODED_BYTE + 27 + NUMBER1 * NUMBER2 - NUMBER3で演算すれば復号できるようだ。NUMBER1~NUMBER3の正体は配布されたLinux VMの環境変数だ(バイナリデータを検索しても確認できる)。

VMで環境変数を確認すると次のようにNUMBER1~3の値がわかる。

環境変数の表示

これを処理するPythonコードを作成。

filelist = ['instant_noodles.txt','iced_coffee.txt','ice_cream.txt']

for i in filelist:
        f = open('dec/'+i, 'rb')
        inp = f.read()
        out = ''
        print(i+":")
        for j in range(len(inp)):
                tmp = inp[j]+27+2*3-37
                out += chr(tmp)
        print(out)

実行の結果、次の3つのファイルの平文が得られる。

instant_noodles.txt:
The 5th byte of the password is: 0xMS

iced_coffee.txt:
The only problem with RC4 is that you need a key. The FLARE team normally uses this number: "SREFBE" (as an UTF-8 string). If you have no idea what that means, you should give up and bake some muffins.

ice_cream.txt:
If this challenge is too difficult and you want to give up or just in case you got hungry, what about baking some muffins? Try this recipe:
0 - Cinnamon
1 - Butter 150gr
2 - Lemon 1/2
3 - Eggs 3
4 - Sugar 150gr
5 - Flour 250gr
6 - Milk 30gr
7 - Icing sugar 10gr
8 - Apple 100gr
9 - Raspberries 100gr

Mix 0 to 9 and bake for 30 minutes at 180A°C.

instant_noodles.txtからパスワードの5文字目がわかると思いきや、何故か0xMSになっている。何らかの誤りがあった可能性はあるが、一旦保留しておく。

次のヒントとして、RC4の鍵にSREFEBという番号を使用することが書かれているが、残念ながら私には意味がわからず、マフィンを作らざるを得なかった。

しかしここでは終わらない。未解読のファイルをヒントなしで復号できないか試す。というわけでoranges.txtを読むと以下のようになっている。

oranges.txt:
Fpg 8kv xyoi gr bjv dwsnagdl kj: 0l60\n

文字の区切りから、8文字目のパスワードを記述しているものと推測することができ、8文字目はおそらく0x60であることがわかる。

アルファベット以外は暗号化されていないから、換字式暗号だと考えられ、答えがThe 8th byte of the password is: 0x60になると仮定して、ヴィジュネル暗号の暗号鍵を逆に求めると鍵はMICROWAVESというわかりやすい単語になった。多分正解なので、ヴィジュネル暗号で解けるファイルを探すと、3つのファイルの平文を得ることができた。

oranges.txt:
The 8th byte of the password is: 0x60

omelettes.txt:
You should follow the FLARE team in Twitter. Otherwise they may get angry and not let you leave even if you get the flag.
https://twitter.com/anamma_06
https://twitter.com/osardar1
https://twitter.com/MalwareMechanic

oats.txt:
You should follow the FLARE team in Twitter. They post a bunch of interesting stuff and have great conversation on Twitter!
https://twitter.com/anamma_06
https://twitter.com/MalwareMechanic

改めてoranges.txtからパスワードの8文字目は0x60であることがわかった。

一旦逸れるが、同じ理由で.daiquiris.txtから7文字目は0x66だと推測できる。こちらの換字式暗号は残念ながら解読できなかった。

.daiquiris.txt
Qac 7ys hcpe xq cyp typxterl xi: 0m66

話を戻して復号できた他の2ファイルにはFLAREチームのTwitterをフォローしろと書かれているが、ここまでの流れを考えると無駄なテキストであるはずがない。この3つのTwitterアカウントの会話を検索する。

すると意味ありげな以下の会話を発見した。

TwitterでのFLAREチームの会話

会話内容をまとめると、AES暗号の暗号鍵と初期ベクトルIVの値について議論しており、暗号鍵にはSheep should sleep in a shedにFLARE Linux VMのOSバージョン番号を連結した32byteのデータ、初期ベクトルIVには@osardar1の好物に00000000000を連結したデータとしていることが読み取れる。

FLARE Linux VMのOSバージョン番号はVM起動時に15.2と出ている。

FLARE Linux VMの起動画面

@osardar1の好物は本人のプロフィールにPIZZAと書かれている。

@osardar1のプロフィール

以上の情報から、AESの256bitの暗号鍵と128bitの初期ベクトルIVは次のようになる。

AES Key: Sheep should sleep in a shed15.2
AES IV : PIZZA00000000000

AESを復号するPythonコードを作成した。

from Crypto.Cipher import AES
from Crypto.Util.number import bytes_to_long, long_to_bytes

def create_aes(password, iv):
    key = password
    return AES.new(key, AES.MODE_CBC, iv)

def encrypt(data, password,iv):
    return iv + create_aes(password, iv).encrypt(data)

def decrypt(data, password,iv):
    cipher = data
    return create_aes(password, iv).decrypt(cipher)


_password = b'Sheep should sleep in a shed15.2'
_iv = b'PIZZA00000000000'
filelist = ['tomatoes.txt','tiramisu.txt','tacos.txt']

for i in filelist:
        f = open('dec/'+i,'r')
        _data = long_to_bytes(int(f.read(),16))
        dec = decrypt(_data,_password,_iv)
        print(i+":")
        print(dec)

次の3つのファイルの平文を得ることができた。

tomatoes.txt:
It seems you are close to escape... We are preparing the tomatoes to throw at you when you open the door! It is only a joke... The 13th byte of the password is revealed by the FLARE alias.

tiramisu.txt:
The 9th byte of the password is the atomic number of the element moscovium.
The 10th byte of the password is the bell number preceding 203.
The 11th byte of the password is the number of unique words in /etc/Quijote.txt
The 12th byte of the password is the largest known number to be the sum of two primes in exactly two different ways 
The 14th (and last byte) of the password is the sum of the number of participants from Spain, Singapore and Indonesia that finished the FLARE-ON 7, FLARE-ON 6 or FLARE-ON 5.


tacos.txt:
Woow! It seems you are very very close to get the flag! Hurry up before we run out of tacos!

一気に9文字目~14文字目までのパスワードがわかるらしい。更に重要なのは14文字目が最後であるということだ。

まずtomatoes.txtから、VMのaliasに13文字目が入っていることがわかる。実際に表示させてみる(実は最初の方で見つけてはいた)。

FLAREのコマンド表示

続いてtiramisu.txtから、9文字目はmoscoviumの原子番号で、モスコビウムは原子番号115だから0x73だ。

10文字目は203の手前のベル数で52だから0x34だ。

飛んで最後の14番目はFLARE-ON 7, FLARE-ON 6, FLARE-ON 5をクリアしたスペイン,シンガポール,インドネシアの合計人数で、FIREEYEの以下のページに公開されている。

合計人数は73で、0x49だ。

11文字目と12文字目も計算及び調べれば出てくるはずだが、この時点で勝った気分になっており、無視した。ここまでで判明した14文字のパスワードの内訳は以下のようになる。

1 2 3 4 5 6 7 8 9 10 11 12 13 14
0x45 0x34 0x51 0x35 0xMS ???? 0x66 0x60 0x73 0x34 ???? ???? 0x35 0x49
E 4 Q 5 ? ? f ` s 4 ? ? 5 I

ここまでわかればブルートフォース攻撃で解ける。

hashcatをSHA256のブルートフォース攻撃モードで、わかっていない4文字のマスクをかけて次のコマンドで実行する。

.\hashcat.exe -m 1400 -a 3 b3c20caa9a1a82add9503e0eac43f741793d2031eb1c6e830274ed5ea36238bf 'E4Q5?a?af`s4?a?a5I'

hashcatの実行結果

パスワードの全桁がE4Q5d6f`s4lD5Iとわかったので、flagを出力するdotファイルを実行してflagを得ることができる。

# ./dot
Password: E4Q5d6f`s4lD5I
Correct password!
Flag: H4Ck3r_e5c4P3D@flare-on.com

ちなみにpasswordのバイト列を逆さに並び替えて各バイトを1減らすとflagになる。

RC4のパートがわからなくてもなんとかなってよかった。

H4Ck3r_e5c4P3D@flare-on.com