NFLabs. エンジニアブログ

セキュリティやソフトウェア開発に関する情報を発信する技術者向けのブログです。

DEF CON CTF Quals 2023 の新形式!LiveCTFのご紹介

はじめに

こんにちは、研究開発担当の市岡です。

このたび、大きいCTF大会であるDEF CON CTF Quals 2023*1にTeam Enu(※)として参加してきました!

※NTTグループ有志によるCTFチーム*2

今年のDEF CON CTF QualsではLiveCTFという新しい形式の問題が出ていました。 今年のDEF CON CTF Qualsはどうだったのか気になる方に向けて、LiveCTFについて紹介します。

DEF CON CTF Quals 2023で出たLiveCTFとは

DEF CON CTF Quals 2023では「LiveCTF」が実施されました。

問題に回答したら得点がもらえる点は普通のDEF CON CTF Qualsと同じですが、いくつか独特な点がありました。

私が考える独特さは以下の3点です。

  • スケジュール
  • 回答の提出方法と制約
  • スコアの付け方

この3点について詳しく説明します。

スケジュール

LiveCTFでは6問が出題されました。DEF CON CTF Quals 2023は48時間ですが、LiveCTFの制限時間は各問題4時間でした。

具体的なスケジュールは以下の通りです。

問題番号 予定での解答可能時間
live-1 5/27 21:00~5/28 01:00
live-2 5/28 01:00~5/28 05:00
live-3 5/28 05:00~5/28 09:00
live-4 5/28 09:00~5/28 13:00
live-5 5/28 13:00~5/28 17:00
live-6 5/28 17:00~5/28 21:00

実際はlive-4でトラブルがあったらしく、予定時間に開始できなかったため、live-4は5/29 01:00までとなりました。

競技参加者が提出したコードはApache 2.0 licenseとされていて、各問題の終了直後にYouTube Liveで紹介されていました。

回答の提出方法と制約

LiveCTFでは、Dockerfileおよびビルドに必要なファイルをtar.gzにしてアップロードします。

競技参加者は、攻略対象のサーバに必要な入出力を行って、シェルを取り、./submitterを実行することが目標です。 このため、自動で攻略するスクリプトを作成し、スクリプトを実行できるようにDockerfileを作成します。

提出したtar.gzをもとにdocker imageが作成され、実行されます。実行時間制限は、docker buildが2分以内、実行は1分以内です。

.
├── exploit
│   ├── Dockerfile
│   └── solve-template.py
├── handout
│   ├── Dockerfile
│   ├── challenge
│   ├── (各問題特有のファイルやディレクトリ)
│   └── config.toml
└── test-solution.sh

スコアの計算方法

スコアは次のように計算されます。

  • 最初に解いた人:50点
  • 最初に解いた人が提出してから6分経過するごとに-1点(6分後に正解した場合は49点加算)
  • 4時間経過し、時間切れになると提出できなくなるため0点

DEFCON CTF Qualsの他の問題は他のチームが正解すると、正解チーム数に応じて点数が減っていくため、LiveCTFのように正解すれば、最後まで50点のまま維持されるのはとても大きいです。

また、live-4のみ、トラブルがあったため、解答時刻によって異なる点数が付くわけでは無く、50点で固定になりました。live-4は解ければとても有利になる問題でした。

私が考えるLiveCTFの特徴

LiveCTFによってDEF CON CTF Qualsへの取り組み方が少し変わると思いました。具体的には寝られなくなったことと、競技中に問題の解説が入ることです。

「寝られなくなった」について、今までのように昼だけに問題を解くということができなくなりました。 今までであれば、とても実力が高いチームや人数が多いチームの場合、夜にみんなで寝ても、昼に取り返すことができました。 しかし、LiveCTFでは日本時間で午前1時から午前5時の間しか解けない問題が出るため、気になって眠れなくなります。

次に「競技中に問題の解説が入ること」について、LiveCTFは他の問題と違って競技時間中に回答時間が終わります。それぞれの問題の終了直後にYouTube Liveが行われ、問題の解説と上位チームが提出したコードの解説が行われました。 競技時間中に他のチームの回答を見られることで、他のチームの試行錯誤を感じることができました。他のチームの試行錯誤の様子を見ると、私も次は取りたいという気持ちになって、いろいろ試してみようという気持ちになりました。

出題された問題のご紹介

出題された問題の技術スタックを紹介するため、私が解こうとした問題を紹介します。

より詳しい解説や他の問題の解説は公式にYouTube Live*3で公開されています。詳しく知りたい方はYouTube Liveをご覧ください。

1問目(5/27 21:00~5/28 01:00)

問題名:What a maze meant

問題文:It's the day of the High Sun Games and you and your intrepid band of thieves are just trying to make it out alive. Can you solve the maze and beat the beast at the exit?

最初に答えた人の回答時間:28分

内容

私が感じたこの問題に必要な知識は以下の2つです。

  • バイナリ解析(rev)
  • プログラミング(探索)

簡単に問題の概要を説明します。

配布されたファイルを展開すると以下のようなディレクトリになっていました。handout/challengeが実行可能形式でした。サーバ側では、この実行可能形式のファイルからフラグが出力されるようにsolverを作ってアップロードする必要があります。

$ tree .
.
├── exploit
│   ├── Dockerfile         <-- tar.gzに入れて提出(solver動かす用のDockerfile)
│   └── solve-template.py  <-- 編集してtar.gzに入れて提出
├── handout  <-- 問題サーバの起動に必要なファイルが入っている
│   ├── Dockerfile
│   ├── challenge
│   ├── config.toml
│   ├── ld-linux-x86-64.so.2
│   └── libc.so.6
└── test-solution.sh  <-- exploitとhandoutそれぞれのdockerをビルドし実行するスクリプトが入っている

handout/challengeはELFファイルでした。まずは実行してみると以下のようになりました。revにより、aを入力すると、何かが出力されることがわかります。そこで、aを入力すると迷路が出力されました。

$ ./challenge 
Reticulating splines... 
/

Welcome to the maze!
You are in room (1, 1)
As you step into the room, you find yourself standing in a medium-sized space. The walls are adorned with statues and a two large chests dominate the center of the room. You see 12 flowers in a vase, and through a window you stop to count 53324 stars. The room appears well designed in the Minimalist style.
Which would you like to do?
go (s)outh, or (q) end the torment: a

You cast arcane eye and send your summoned magical eye above the maze.
############################# 
#@#...#...#.................# 
#.#.#.#.#.###.#######.#####.# 
#...#.#.#...#.#.....#.#...#.# 
#####.#.###.#.#.#####.#.#.#.# 
#...#...#.#.#.#.....#...#.#.# 
#.#.#####.#.#.###.#.#####.#.# 
#.#...#.....#...#.#.#.....#.# 
#.#.#.#.#######.###.#.#####.# 
#.#.#.#.#.....#.....#.#.....# 
#.#.###.###.#.###.###.#.##### 
#.#...#.....#...#.#...#.....# 
#.###.#########.###.#######.# 
#...#.......#...#...#.....#.# 
###.###.#####.###.###.###.#.# 
#...#...#.....#...#...#...#.# 
#.###.#.#.#####.###.###.###.# 
#...#.#.#...#...#...#.#.#.#.# 
#.###.#.###.###.###.#.#.#.#.# 
#.#...#...#.....#...#.#.#.#.# 
#.#.#############.###.#.#.#.# 
#.#...........#...#...#...#.# 
#.#########.#.#.#####.###.#.# 
#.#...#...#.#...#...#.....#.# 
#.###.#.#.#.#####.#.#######.# 
#...#.#.#...#...#.#.#.....#.# 
###.#.#.#####.#.#.#.#.###.#.# 
#.....#.......#...#.....#...# 
#########################*### 
                              
You are in room (1, 1)
Which would you like to do?
go (s)outh, or (q) end the torment: 

この迷路を自動で解くスクリプトを作成する必要があります。

また、以下のように、ゴールにつくと、なぜか失敗してしまいます。

############################# 
#.#...#...#.................# 
#.#.#.#.#.###.#######.#####.# 
#...#.#.#...#.#.....#.#...#.# 
#####.#.###.#.#.#####.#.#.#.# 
#...#...#.#.#.#.....#...#.#.# 
#.#.#####.#.#.###.#.#####.#.# 
#.#...#.....#...#.#.#.....#.# 
#.#.#.#.#######.###.#.#####.# 
#.#.#.#.#.....#.....#.#.....# 
#.#.###.###.#.###.###.#.##### 
#.#...#.....#...#.#...#.....# 
#.###.#########.###.#######.# 
#...#.......#...#...#.....#.# 
###.###.#####.###.###.###.#.# 
#...#...#.....#...#...#...#.# 
#.###.#.#.#####.###.###.###.# 
#...#.#.#...#...#...#.#.#.#.# 
#.###.#.###.###.###.#.#.#.#.# 
#.#...#...#.....#...#.#.#.#.# 
#.#.#############.###.#.#.#.# 
#.#...........#...#...#...#.# 
#.#########.#.#.#####.###.#.# 
#.#...#...#.#...#...#.....#.# 
#.###.#.#.#.#####.#.#######.# 
#...#.#.#...#...#.#.#.....#.# 
###.#.#.#####.#.#.#.#.###.#.# 
#.....#.......#...#.....#@..# 
#########################*### 
                              
You are in room (27, 25)
As you step into the room, you find yourself standing in a massive space. The walls are adorned with beds and a two large mirrors dominate the center of the room. You see 10 flowers in a vase, and through a window you stop to count 70570 stars. The room appears well designed in the Contemporary style.
Which would you like to do?
go (n)orth, go (s)outh, go (e)ast, or (q) end the torment: s

Just as you are about to exit, a displacer beast captures you. You die.

このときrevで、以下のことが分かります。

  • 乱数が生成されていることと
  • You are in room (x,y)の後ろからgo (n)orth,...までの文章が、生成された乱数ごとに変わっていること (文章から乱数を特定できること)
  • 生成された乱数が1213で割ったあまりが1212のときのみゴールできること
  • ゴールすると、/bin/shが実行されること

この文章から乱数を特定し、狙った乱数になるまで0を入力し続けた後にゴールすることで、正解できる問題でした。

5問目(5/28 13:00~5/28 17:00)

問題名:Don't bLink

問題文:Compiler challenges are so passé. Now we have linker challenges! Don't bLink! This one is probably going fast...

配布ファイル

├── exploit
│   ├── solve-template.py  <-- 編集してtar.gzに入れて提出
│   └── Dockerfile         <-- tar.gzに入れて提出(solver動かす用のDockerfile)
├── handout
│   ├── Dockerfile
│   ├── config.toml  <-- flagが記述されている
│   ├── challenge  <-- server.pyを起動する
│   ├── server.py <-- 標準入力をlink.ldとして保存する
│   └── src
│       ├── main.c  <-- "Hello World!"を出力する
│       └── CMakeLists.txt <-- "link.ld"を使ってリンクするよう指定する 
└── test-solution.sh

内容

私が感じたこの問題に必要な知識

  • リンカコマンド言語

配布されたファイルでは、標準入力をlink.ldとして保存するスクリプトがありました。

def main():
    src_dir = Path(__file__).parent / "src"
    with tempfile.TemporaryDirectory() as build_dir:
        with open(Path(build_dir) / "link.ld", "wb") as f:
            while True:
                conts = sys.stdin.buffer.readline()
                if conts.strip() == b"":
                    break
                f.write(conts)

標準入力されたリンカが実行ファイル生成時に使われることが分かりました。

リンカーのスクリプトとして、./submitterを実行するシェルコードを送るスクリプトを作成すると、答えが得られるそうです。

YouTube Liveの解説によると、リンカコマンド言語で以下のようにBYTE(0x**);という形でシェルコードを送るようでした。

OUTPUT_FORMAT(・・・)
OUTPUT_ARCH(・・・)
ENTRY(_start)
SECTIONS
{
. = 0x400000 + SIZEOF_HEADERS;
.text = {
_start = .;
BYTE(0x31);
BYTE(0xd2);
BYTE(0x52);
・・・
}

おわりに

Team EnuではLiveCTFで得点を取ることはできませんでした。 LiveCTFは解説を見るとできる気がしてくる内容も多かったです。ギリギリ手が届きそうなのが悔しかったです。

来年もLiveCTFが出るようであれば、1問くらいは取れるようにまた1年頑張りたいと思います。 本記事がDEF CON CTF Qualsへの参加を検討されている方の参考になれば幸いです。