headless_horseman

以下のファイルが与えられた。

These files were given:

headless_horseman |-- __MACOSX/ | `-- distributed_files/ | `-- ._README.txt `-- distributed_files/ |-- README.txt |-- body_bag/ | |-- bloated_body | |-- decomposing_body | `-- rotting_body `-- headless_horseman

distributed_files/headless_horsemanGhidraで逆コンパイルすると、 main関数から以下の関数が呼び出されていることがわかった。

first_count関数は、入力の上位16ビットが 0xdead かをチェックしている。
second_count関数は、入力の下位16ビットが 0xface かをチェックしている。
(second_count関数は逆コンパイル結果では入力全体をチェックしているように見えるが、アセンブリコードを見ると下位16ビットしか見ていないことがわかる)
合わせると、0xdeadface、すなわち -559023410 を入力すればいいことがわかる。

distributed_files/headless_horsemanCS50 IDEで実行し、-559023410 を入力すると、以下の6個のファイルが出力された。

Decompiling distributed_files/headless_horseman using Ghidra, I found the function main calling these functions:

The function first_count checks if the upper 16 bits of the input is 0xdead.
The function second_count checks if the lower 16 bits of the input is 0xface.
(The function second_count looks as if it is checking the whole input, but reading the assembly code reveals that it is checking only the lower 16 bits.)
Combining these, the value to input is 0xdeadface, which means -559023410.

I executed distributed_files/headless_horseman on CS50 IDE and entered -559023410. As a result, it produced these 6 files:

各ファイルの内容を確認すると、それぞれELFヘッダーのようだった。
そこで、これらのファイルを distributed_files/body_bag ディレクトリ内のファイルと総当りで合体させる以下のプログラムを作成し、実行した。

Checking the contents of each files, they looked ELF headers.
I created this program to concatenate the files with the files in the directory distributed_files/body_bag using all combinations, and executed that.

gattai.pl

できたファイル群を readelf -lS でチェックすると、以下の3個のファイルのみエラーが出なかった。

I checked the resulting files using the command readelf -lS. As a result, these 3 files didn't produce any errors while the other files did:

そこで、これらのファイルをGhidraで逆コンパイルした。

dessicated_head_decomposing_body では、main関数からkatrina関数が呼ばれていた。
katrina関数では、メッセージを出力した後文字列を読み込み、それをencrpyted_words (21 09 04 09 1C 00 7F 24 00 1A 09 1C 28 00 00) とxorして出力していた。
ここで出力されるメッセージの中に、以下の部分があった。

Seeing this, I decompiled these files using Ghidra.

In the file dessicated_head_decomposing_body, the function main calls the function katrina.
The function katrina prints some messages, reads a string, and print the string with exclusive-ored with encrpyted_words (21 09 04 09 1C 00 7F 24 00 1A 09 1C 28 00 00).
The message printed here contained this part:

'Drat! it looks like I encrypted my portion but I cant seem to remember what I used!'
'can you help me out? I was never very creative with these things, maybe try the street I grew up on? or my Home Town?'

distributed_files/README.txt を読むと、「the village of Sleepy Hollow」についてのお話が書かれていた。
そこで、CyberChefを用い、Sleepy Hollow とのXORをとった。

Reading distributed_files/README.txt, I found a story about "the village of Sleepy Hollow".
Seeing this, I calculated XOR with Sleepy Hollow on CyberChef.

XOR - CyberChef

結果は really_loves_ となった。

moldy_head_bloated_body では、main関数からichabod関数が呼ばれていた。
ichabod関数では、check_surroundings関数を呼び出し、その返り値が0ならprint_incantation関数を呼び出していた。
print_incantation関数では、"ZmxhZ3t0aGVfaG9yc2VtYW5fanVzdF8="b_decode関数に渡した結果を出力していた。
そこで、このデータにCyberChefの From Base64 を適用してみた。

The result was really_loves_.

In the file moldy_head_bloated_body, the function main calls the function ichabod.
The function ichabod calls the function check_surroundings, and when its return values is 0, it calls the function print_incantation.
The function print_incantation prints the result of the function b_decode with "ZmxhZ3t0aGVfaG9yc2VtYW5fanVzdF8=" passed as an argument.
Seeing this, I tried applying "From Base64" to the data on CyberChef.

From Base64 - CyberChef

結果は flag{the_horseman_just_ となった。

shrunken_head_rotting_body では、main関数からbrom関数が呼ばれていた。
brom関数は、local_10の値が指定のものだったらprint_flag関数を呼ぶ処理をしていた。
print_flag関数では、lettersto_replacebuff の値を用いて何かを出力していた。
そこで、print_flag関数の処理を再現する以下のプログラムを書き、実行した。

The result was flag{the_horseman_just_.

In the file shrunken_head_rotting_body, the function main calls the function brom.
The function brom calls the function print_flag if the value of local_10 is what is expected.
The function print_flag prints something based on the values of letters, to_replace, and buff.
Seeing this, I created this program to mimic what is done in the function print_flag, and executed that.

rotting_print_flag.pl

結果は pumpkin_pie} となった。

これらの3個のファイルの解析結果を繋げることで、flagが得られた。

The result was pumpkin_pie}.

I obtained the flag by concatenating what are obtained from these 3 files.

flag{the_horseman_just_really_loves_pumpkin_pie}

BuckeyeCTF 2021