polyglot4b (SECCON Beginners CTF 2023) WriteUp

# はじめに
この記事は [1日1CTF Advent Calendar 2024](https://qiita.com/advent-calendar/2024/1-day-1-ctf) の 5 日目の記事です。

# 問題
polyglot4b (問題出典: SECCON Beginners CTF 2023)

> polyglotってなに？
> たぶんpolyglotを作れるエディタを開発したよ！

リポジトリ: https://github.com/SECCON/SECCON_Beginners_CTF_2023/tree/main/misc/polyglot4b

## 問題概要

```polyglot4b.py
import os
import sys
import uuid
import shutil
import subprocess

print(
    f"""\033[36m\
 ____       _             _       _     _____    _ _ _
|  _ \ ___ | |_   _  __ _| | ___ | |_  | ____|__| (_) |_ ___  _ __
| |_) / _ \| | | | |/ _` | |/ _ \| __| |  _| / _` | | __/ _ \| '__|
|  __/ (_) | | |_| | (_| | | (_) | |_  | |__| (_| | | || (_) | |
|_|   \___/|_|\__, |\__, |_|\___/ \__| |_____\__,_|_|\__\___/|_|
              |___/ |___/
{"-" * 68}
>> """,
    end="",
)

file = b""
for _ in range(10):
    text = sys.stdin.buffer.readline()
    if b"QUIT" in text:
        break
    file += text

print(f"{'-' * 68}\033[0m")

if len(file) >= 50000:
    print("ERROR: File size too large. (len < 50000)")
    sys.exit(0)

f_id = uuid.uuid4()
os.makedirs(f"tmp/{f_id}", exist_ok=True)
with open(f"tmp/{f_id}/{f_id}", mode="wb") as f:
    f.write(file)
try:
    f_type = subprocess.run(
        ["file", "-bkr", f"tmp/{f_id}/{f_id}"], capture_output=True
    ).stdout.decode()
except:
    print("ERROR: Failed to execute command.")
finally:
    shutil.rmtree(f"tmp/{f_id}")

types = {"JPG": False, "PNG": False, "GIF": False, "TXT": False}
if "JPEG" in f_type:
    types["JPG"] = True
if "PNG" in f_type:
    types["PNG"] = True
if "GIF" in f_type:
    types["GIF"] = True
if "ASCII" in f_type:
    types["TXT"] = True

for k, v in types.items():
    v = "🟩" if v else "🟥"
    print(f"| {k}: {v} ", end="")
print("|")

if all(types.values()):
    print("FLAG: ctf4b{****REDACTED****}")
else:
    print("FLAG: No! File mashimashi!!")
```

以下のコマンドを実行した結果に、`JPEG`, `PNG`, `GIF`, `ASCII` がすべて含まれているようなファイルを作れればクリア。
```sh
file -bkr ファイル名
```

## 考察

とりあえず file の man を読んでコマンドについているオプションを調べる。
```
-b, --brief
        Do not prepend filenames to output lines (brief mode).

-k, --keep-going
        Don't stop at the first match, keep going.  Subsequent matches will be have the string '\012- '
        prepended.  (If you want a newline, see the -r option.)

-r, --raw
        Don't translate unprintable characters to \ooo.  Normally file translates unprintable characters to
        their octal representation.
```

あまり本質ではなさそう。とりあえずもう少し man を眺める。

> The magic tests are used to check for files with data in particular fixed formats.  The canonical example of this is a binary executable (compiled program) a.out file, whose format is defined in <elf.h>, <a.out.h> and possibly <exec.h> in the standard include directory.  These files have a "magic number" stored in a particular place near the beginning of the file that tells the UNIX operating system that the file is a binary executable, and which of several types thereof.  The concept of a “magic number” has been applied by extension to data files.  Any file with some invariant identifier at a small fixed offset into the file can usually be described in this way.  The information identifying these files is read from /etc/magic and the compiled magic file /usr/share/misc/magic.mgc, or the files in the directory /usr/share/misc/magic if the compiled file does not exist. In addition, if \\$HOME/.magic.mgc or \\$HOME/.magic exists, it will be used in preference to the system magic files.

とりあえず `/usr/share/misc/magic.mgc` にどうやって `file` コマンドがファイルタイプを判別しているのか書いてありそうなので読んでみた。が、コンパイルされているようで何が書いてあるか全くわからない。少し調べてみると、[GitHub](https://github.com/file/file/tree/master/magic/Magdir) にコンパイル前のがあった。

今回関係しそうなのはファイル `images` と `jpeg` っぽい。長過ぎるので必要な部分だけ掲載する。

```
...
# Standard PNG image.
0	string		\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0DIHDR	PNG image data
...
# GIF
# Strength set up to beat 0x55AA DOS/MBR signature word lookups (+65)
0	string		GIF8		GIF image data
```

```jpeg
0	belong&0xffffff00	0xffd8ff00	JPEG image data
```

いくつか他にもマッチするパターンがあったが、ほとんど最初の数バイトで判別していて、複数の判定を踏むようにするのは難しそう。


ここで、`PNG` の判定を観察していると `%d` という表記がなされていることに気づいた。`printf` みたいな記法が採用されているのだろうか。

```
# Standard PNG image.
0	string		\x89PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0DIHDR	PNG image data
!:mime	image/png
!:ext   png
!:strength +10
>16	use		png-ihdr
>33	string		\x00\x00\x00\x08acTL	\b, animated
>>41	ubelong		1			(%d frame
>>41	ubelong		>1			(%d frames
>>45	ubelong		0			\b, infinite repetitions)
>>45	ubelong		1			\b, %d repetition)
>>45	ubelong		>1			\b, %d repetitions)
```

magic ファイルのフォーマットを調べてみるため、magic の man を読んでみる。

> Each line of the file specifies a test to be performed. A test compares the data starting at a particular offset in the file with a byte value, a string or a numeric value. If the test succeeds, a message is printed. The line consists of the following fields:
>  (中略)
> message  The message to be printed if the comparison succeeds.  **If the string contains a printf(3) format specification, the value from the file (with any specified masking performed) is printed using the message as the format string.**  If the string begins with "\b", the message printed is the remainder of the string with no whitespace added before it: multiple matches are normally separated by a single space.

太字部分が該当箇所。じゃあ、`%s` を含むようなやつを 1 個みつければいい。

`sgi` ファイルにこんなのがあった。

```
# 32bit core file
0	belong	0xdeadadb0	IRIX core dump
>4	belong	1		of
>16	string	>\0		'%s'
```

これを使ってみよう。

```console
$ echo -e "\xde\xad\xad\xb0\x00\x00\x00\x01________hogehoge" > img.txt 
$ file -bkr ./img.txt 
IRIX core dump of 'hogehoge'
- data
```

ビンゴ。

```console
$ echo -e "\xde\xad\xad\xb0\x00\x00\x00\x01________JPEG_PNG_GIF_ASCII\nQUIT" | nc localhost 31416
 ____       _             _       _     _____    _ _ _
|  _ \ ___ | |_   _  __ _| | ___ | |_  | ____|__| (_) |_ ___  _ __
| |_) / _ \| | | | |/ _` | |/ _ \| __| |  _| / _` | | __/ _ \| '__|
|  __/ (_) | | |_| | (_| | | (_) | |_  | |__| (_| | | || (_) | |
|_|   \___/|_|\__, |\__, |_|\___/ \__| |_____\__,_|_|\__\___/|_|
              |___/ |___/
--------------------------------------------------------------------
>> --------------------------------------------------------------------
| JPG: 🟩 | PNG: 🟩 | GIF: 🟩 | TXT: 🟩 |
FLAG: ctf4b{y0u_h4v3_fully_und3r5700d_7h15_p0ly6l07}
```