LoginSignup
12
7

More than 1 year has passed since last update.

SECCON Beginners CTF 2021 作問者writeup

Last updated at Posted at 2021-05-23

はじめに

5月22日14:00から24時間で初心者向けのSECCON Beginners CTF 2021を開催しました。私はMisc1問とReversing1問を担当したので、そちらのwriteupを公開します。

[Misc 58pts] git-leak(410 solves)

Gitでコミット上書きされる前の情報を読み出す問題です。Gitのコミット上書きで証跡を消せたと思いこむのが危険なので、この問題を作問しました。

解法はいくつかあると思いますが、想定解法に利用するコマンド類は配布したREADME.mdに全て記載してあります。正答率がかなり高かったので、きっとこのヒントを利用した方もいたのではないでしょうか?

Gitのオブジェクトには、ファイルの内容のみが格納されているblobblob等をツリー構造で保持するtreeオブジェクトがあります。これらのオブジェクトをコミットハッシュ(SHA-1)と紐づけることで管理しています。今回は、flagを含むblobを特定して中身を見れればflagが得られます。

さて、本題の解法に移ります。問題文から察するに、コミットを上書きしたことが分かります。そのため、コミット履歴を見るためにreflogを使います。

$ git reflog
e0b545f (HEAD -> master) HEAD@{0}: commit (amend): feat: めもを追加
80f3044 HEAD@{1}: commit (amend): feat: めもを追加
b3bfb5c HEAD@{2}: rebase -i (finish): returning to refs/heads/master
b3bfb5c HEAD@{3}: commit (amend): feat: めもを追加
7387982 HEAD@{4}: rebase -i: fast-forward
36a4809 HEAD@{5}: rebase -i (start): checkout HEAD~2
7387982 HEAD@{6}: reset: moving to HEAD
7387982 HEAD@{7}: commit: feat: めもを追加

# (snip)

結果から、コミットハッシュ7387982に対して上書きが行われていることが分かります。

次に、コミットハッシュから中身のデータを確認するために、cat-fileを使います。

$ git cat-file -p 7387
tree a5b6b52f47aba96730ab61471ddcdff864e5dd8c
parent 36a4809f1ae8013432eb52cfd2f9f062a3269499
author task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900
committer task4233 <29667656+task4233@users.noreply.github.com> 1620491725 +0900

feat: めもを追加
$ git cat-file -p a5b6b52f47aba96730ab61471ddcdff864e5dd8c
100644 blob 16290835a0f74ccf30cbf30d791c84392a9dcce6    README.md
100644 blob 4cbb035d2ff072127b4e22919485127d2273e88e    flag.txt
100644 blob 62337fdb59ceb048f7da9eaf768923d744930842    note.md
$ git cat-file -p 4cbb035d2ff072127b4e22919485127d2273e88e
ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}

また、checkout, reset --hardを利用しても良いです。

$ git checkout 7387982
$ cat flag.txt

@gamoutatsumi さん、ご指摘いただきありがとうございました!

別解として、全てのblobはzlibで圧縮されているので、全てのblobを解凍することでもflagは得られます。

import os, glob, zlib

objects = glob.glob('./.git/objects/**', recursive=True)
for obj in objects:
    if os.path.isdir(obj):
        continue
    with open(obj, 'rb') as f:
        data = zlib.decompress(f.read())
        if b'ctf4b{' in data:
            print(data.decode())

# Output:
# blob 40ctf4b{0verwr1te_1s_n0t_c0mplete_1n_G1t}

[Reversing 73pts] children(293 solves)

新たに生成される子プロセスの情報を確認する問題です。本問題は、実行ファイルの振る舞いを観察するためのstraceを使う問題を作りたかったので作問しました。

とはいえ、提出されたフラグを見ていると、ゴリゴリにReversingして解いている方もいたようですね。作問時に気づいてはいたのですが、Easy問なので抜け穴として残しておきました。

想定解ではstraceを使ってprocess IDを取得して回答し、最後に生成された子プロセスの数を回答すれば良いです。以下が実行例です。process IDや生成される子プロセスの数は変動するので、各自の環境で確認してください。

また、本問題は$ psコマンドを併用しても解くことが出来ます。
@Takohabakaさん、ご指摘ありがとうございました。

$ strace ./children
execve("./children", ["./children"], 0x7fffe8e7b530 /* 21 vars */) = 0
brk(NULL)                               = 0x557ac1056000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=31525, ...}) = 0
mmap(NULL, 31525, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fcc13353000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fcc13351000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fcc12d43000
mprotect(0x7fcc12f2a000, 2097152, PROT_NONE) = 0
mmap(0x7fcc1312a000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7fcc1312a000
mmap(0x7fcc13130000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fcc13130000
close(3)                                = 0
arch_prctl(ARCH_SET_FS, 0x7fcc13352500) = 0
mprotect(0x7fcc1312a000, 16384, PROT_READ) = 0
mprotect(0x557ac02cb000, 4096, PROT_READ) = 0
mprotect(0x7fcc1335b000, 4096, PROT_READ) = 0
munmap(0x7fcc13353000, 31525)           = 0
fstat(1, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
brk(NULL)                               = 0x557ac1056000
brk(0x557ac1077000)                     = 0x557ac1077000
write(1, "I will generate 10 child process"..., 36I will generate 10 child processes.
) = 36
write(1, "They also might generate additio"..., 51They also might generate additional child process.
) = 51
write(1, "Please tell me each process id i"..., 58Please tell me each process id in order to identify them!
) = 58
write(1, "\n", 1
)                       = 1
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7fcc133527d0) = 1390
write(1, "Please give me my child pid!\n", 29) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=1390, si_uid=0, si_status=1, si_utime=0, si_stime=0} ---
write(1, "Please give me my child pid!\n", 29Please give me my child pid!
) = 29
fstat(0, {st_mode=S_IFCHR|0600, st_rdev=makedev(136, 0), ...}) = 0
read(0, 1390

# (snip)

"1400\n", 1024)                 = 5
write(1, "ok\n", 3ok
)                     = 3
wait4(-1, NULL, 0, NULL)                = 1390
write(1, "How many children were born?\n", 29How many children were born?
) = 29
read(0, 11
"11\n", 1024)                   = 3
write(1, "ctf4b{p0werfu1_tr4sing_t0015_15_"..., 40ctf4b{p0werfu1_tr4sing_t0015_15_usefu1}       ) = 40
lseek(0, -1, SEEK_CUR)                  = -1 ESPIPE (Illegal seek)
exit_group(0)                           = ?
+++ exited with 0 +++
12
7
4

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
12
7