TSALVIA技術メモ

CTFのWriteupや気になったツールについて書いていきます。また、このサイトはGoogle Analyticsを利用しています。

DEF CON CTF Qualifier 2019 Writeup

DEF CON CTF Qualifier 2019 について

毎年恒例のDEF CON予選が開催されました。
2019年5月11日(土)午前9時~5月13日(月)午前9時まで(48時間)

www.oooverflow.io

私は3問しか解くことができませんでした。結果は、153位で310点でした。 ほとんどがPWN系の問題でかなり難しいといった印象でした。

f:id:tsalvia:20190514051219p:plain

DEF CON CTF Qualifier 2019 Writeup(3問)

[FIRST CONTACT] WELLCOME_TO_THE_GAME

問題

Welcome to the 2019 DEF CON CTF quals! Let's start it out with a free flag :)
If you can't submit it and get points there must be something very wrong, and we hope it's on your end :D

添付ファイル(flag)

OOO{Game on!}

解答例

flagファイルをcatコマンドで表示するだけ

$ cat flag
OOO{Game on!}

FLAG

OOO{Game on!}

[FIRST CONTACT] KNOW_YOUR_MEM

問題

Find the flag page in memory, 64-bit edition. Timeouts are strict, please test locally first! There's a simplified version to help with that.
know_your_mem.quals2019.oooverflow.io 4669

添付ファイル

解答例

とりあえず、README.mdやshellcode.cなどを参考にビルドしてみました。 私の環境だとgetrandomシステムコールがなかったので、少しソースコードを調整しました。 また、alarm関数があり、中断されるのでそこもコメントアウトしました。

know_your_mem.cとsimplified.cの修正箇所

// #include <sys/random.h>
  • 40行目のコメントを戻す
int fd = open("/dev/urandom", O_RDONLY); if (read(fd, &ret, sizeof(ret)) != sizeof(ret)) { err(47, "urandom"); } close(fd);
// if (getrandom(&ret, sizeof(ret), GRND_NONBLOCK) != sizeof(ret)) err(47, "getrandom");
// alarm(10);

必要なパッケージのインストール、権限設定を行います。

$ sudo apt install libseccomp-dev libseccomp2
$ git clone https://chromium.googlesource.com/linux-syscall-support
$ chmod +x topkt.py

README.mdにある通りに make check をすると以下の通りになります。

$ make check
./simplified
[ ] This challenge may be slightly easier in Linux 4.17+. Here, we're running on Linux 4.9.0-8-amd64 (x86_64)
Loading your simplified solution from ./simplified_shellcode.so
[ ] Putting the flag somewhere in memory...
Secret loaded (header + 107 bytes)
[H] The flag is at 0x1d8cfbca6000
[ ] Putting red herrings in memory...
[H] Red herring at 0x1c6adb0bc000
[H] Red herring at 0x1ede9e616000
[H] Red herring at 0x1492e76a5000
[H] Red herring at 0x1b9afff9d000
[H] Red herring at 0x15bea09ed000
[H] Red herring at 0x15bd1d2dc000
[H] Red herring at 0x1ec8383dd000
[H] Red herring at 0x1d90cf8cd000
[H] Red herring at 0x10e19c9bd000
[H] Red herring at 0x176bcd88d000
[H] Red herring at 0x165d121fb000
[H] Red herring at 0x1f26fc306000
[H] Red herring at 0x183b81dff000
[H] Red herring at 0x1e47eccb1000
[H] Red herring at 0x1afc15a34000
[H] Red herring at 0x15ad9b443000
[H] Red herring at 0x16e1dec4d000
[H] Red herring at 0x1e45e4bbd000
[H] Red herring at 0x149657cdf000
[H] Red herring at 0x19e7a56c9000
[H] Red herring at 0x1a6c4a3b5000
[H] Red herring at 0x1eb70b0c6000
[H] Red herring at 0x1aee559f9000
[H] Red herring at 0x1e334063d000
[H] Red herring at 0x189b2c68a000
[H] Red herring at 0x1bc581407000
[H] Red herring at 0x1b408858a000
[H] Red herring at 0x1a78fd20f000
[H] Red herring at 0x16db44b80000
[H] Red herring at 0x1f26e0d3f000
[*] seccomp filter now active!
Hi! Soon I'll be your shellcode!
[*] Your shellcode returned 0x123456
[!] Sorry, you didn't find the secret address.
Makefile:27: ターゲット 'check' のレシピで失敗しました
make: *** [check] エラー 1

flagが書き込まれたメモリ領域を引き当てることができれば、フラグを取得できそうだということが分かりました。 shellcodeを記述できるような環境も揃っているようなので、そちらを利用して検証してみます。

まずは、ヒントとして用意されているsimplified.cとsimplified_shellcode.so.cを使って問題を解いてみます。 simplified_shellcode.so.cを編集して以下のプログラムを書きました。

  1. メモリ領域(0x00001ffffffff000~0x0000100000000000)を1ページ(4096byte)ずつ探索する。
  2. 見つけた領域をmprotectシステムコールを使って、READ権限の付与を行う。
    • mprotectシステムコールは、確保されたメモリ領域でなければ、ENOMEMエラーを返す。 エラーが返ってこなければ、すでに確保された領域だと判定できる。
    • 今回は適当にREAD権限の付与を行った。
  3. 確保されたメモリ領域を見つけたら、printf関数で表示する。
  4. OOO」の文字列を見つけたら、探索を終了する。
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <inttypes.h>
#include <string.h>

#define ADDR_MIN   0x0000100000000000UL
#define ADDR_MASK  0x00000ffffffff000UL

#define hint(x, ...) fprintf(stderr, "[H] " x, __VA_ARGS__)

void *shellcode()
{
        uintptr_t offset;
        void *addr = 0;

        printf("Hi! Soon I'll be your shellcode!\n");

        for (offset = 0xffffffff; offset >= 0; offset--) {
                addr = (void *)(((offset << 12) & ADDR_MASK) + ADDR_MIN);

                if (mprotect(addr, 4096, PROT_READ) != 0)
                        continue;

                hint("%p: Allocated Memory Found\n", addr);
                hint("%.100s\n", (char *)addr);
                if (strncmp(addr, "OOO", 3) == 0)
                        break;
        }
        return addr;
}

実行結果は、以下の通りになります。

$ ./simplified
[ ] This challenge may be slightly easier in Linux 4.17+. Here, we're running on Linux 4.9.0-8-amd64 (x86_64)
Loading your simplified solution from ./simplified_shellcode.so
[ ] Putting the flag somewhere in memory...
Secret loaded (header + 107 bytes)
[H] The flag is at 0x1d384bdf8000
[ ] Putting red herrings in memory...
[H] Red herring at 0x11c0a1626000
[H] Red herring at 0x15335a290000
[H] Red herring at 0x107d3964d000
[H] Red herring at 0x144355ee4000
[H] Red herring at 0x160aa56e7000
[H] Red herring at 0x175715a7c000
[H] Red herring at 0x1400c3e53000
[H] Red herring at 0x1f44346ca000
[H] Red herring at 0x195005f03000
[H] Red herring at 0x1ec52dce4000
[H] Red herring at 0x151ddf5cb000
[H] Red herring at 0x15aadcfbd000
[H] Red herring at 0x16f8cfca7000
[H] Red herring at 0x151605b97000
[H] Red herring at 0x13d2c86e9000
[H] Red herring at 0x10e899e43000
[H] Red herring at 0x10039b2ee000
[H] Red herring at 0x1d2e14bbf000
[H] Red herring at 0x1ce7b022d000
[H] Red herring at 0x184fd3ee9000
[H] Red herring at 0x1a27962ff000
[H] Red herring at 0x1ca467878000
[H] Red herring at 0x11118208c000
[H] Red herring at 0x185c01d03000
[H] Red herring at 0x1c5835743000
[H] Red herring at 0x114620a2e000
[H] Red herring at 0x10a01139a000
[H] Red herring at 0x12fae2514000
[H] Red herring at 0x120eba409000
[H] Red herring at 0x131e1400a000
[*] seccomp filter now active!
Hi! Soon I'll be your shellcode!
[H] 0x1f44346ca000: Allocated Memory Found
[H] Sorry, this is just a red herring page. Keep looking!
[H] 0x1ec52dce4000: Allocated Memory Found
[H] Sorry, this is just a red herring page. Keep looking!
[H] 0x1d384bdf8000: Allocated Memory Found
[H] OOO: You found it, congrats! The flag is: OOO{theflagwillbehere} Make sure you print it to stdout, s
[*] Your shellcode returned 0x1d384bdf8000
[^] Success! Make sure you're also printing the flag, and that it's not taking too long. Next: convert your solution to raw shellcode -- you can start with C code, BTW! shellcode.c shows one way to do it.

次に上記の結果を基にshellcode.cを編集してRAWシェルコードを作成しました。

static int my_errno = 0;
#define SYS_ERRNO my_errno
#include "linux-syscall-support/linux_syscall_support.h"

#define PAGE_SIZE  4096
#define ADDR_MIN   0x0000100000000000UL  // Low-ish
#define ADDR_MASK  0x00000ffffffff000UL  // Page-aligns
#define ADDR_MAX   (ADDR_MASK + ADDR_MIN + PAGE_SIZE)
#define N_FAKES    30

void _start()
{
        void *addr = (void *)ADDR_MAX;
        int i;

        for (i = 0; i < N_FAKES + 1; i++) {
                do {
                        addr -= PAGE_SIZE;
                } while (sys_mprotect(addr, PAGE_SIZE, 1) != 0);
                sys_write(1, addr, PAGE_SIZE);
        }
        sys_exit_group(2);
}

flagが書き込まれたメモリ領域は、ランダムで決まります。 10秒以内でflagが書き込まれたメモリ領域が見つかることを祈って、何回か実行させました。 200回程度の試行回数で発見することができました。

#!/bin/bash

echo "pid: $$"

count=0
echo -en "try: $count"

while :
do
        nc know_your_mem.quals2019.oooverflow.io 4669 < shellcode.bin.pkt >> $$.log

        FLAG=`grep -a OOO $$.log`
        if [ "$FLAG" ]; then
                echo -e "\n$FLAG"
                break
        fi

        let count++
        echo -en "\rtry: $count"
done
$ make shellcode.bin.pkt
$ chmod +x ./solve.bash
$ ./solve.bash
pid: 7626
try: 200
OOO: You found it, congrats! The flag is: OOO{so many bits, so many syscalls}

FLAG

OOO{so many bits, so many syscalls}

[FIRST CONTACT] CANT_EVEN_UNPLUG_IT

問題

You know, we had this up and everything. Prepped nice HTML5, started deploying on a military-grade-secrets.dev subdomain, got the certificate, the whole shabang. Boss-man got moody and wanted another name, we set up the new names and all. Finally he got scared and unplugged the server. Can you believe it? Unplugged. Like that can keep it secret…

添付ファイル(HINT)

Hint: these are HTTPS sites. Who is publicly and transparently logging the info you need?
Just in case: all info is freely accessible, no subscriptions are necessary. The names cannot really be guessed. 

解答例

問題文を要約すると、以下の通りになります。

  1. HTML5で書かれたWebサイトを作成した。
  2. military-grade-secrets.devのサブドメインで証明書を作成した。
  3. 名前が気に入らなかったので、新しいドメイン名を取得した。
  4. Webサイトの公開を停止した。

military-grade-secrets.devのサブドメインで証明書を作成しているということなので、証明書の登録を確認しました。 今回は、Check website securityというサービスを利用して検索しました。

ssltools.digicert.com

「military-grade-secrets.dev」と入力し、「Include subdomains」にチェックを入れて検索します。 以下の2つのサブドメインで登録されていることが確認できました。

  • secret-storage.military-grade-secrets.dev
  • now.under.even-more-militarygrade.pw.military-grade-secrets.dev

f:id:tsalvia:20190514021828p:plain

どちらか片方にcurlで接続してみます。

$ curl https://secret-storage.military-grade-secrets.dev
<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>302 Moved</TITLE></HEAD><BODY>
<H1>302 Moved</H1>
The document has moved
<A HREF="https://forget-me-not.even-more-militarygrade.pw">here</A>.
</BODY></HTML>

https://forget-me-not.even-more-militarygrade.pw にリダイレクトされるようになっていました。 curlで接続してみましたが、今度は接続できませんでした。 問題文にあるようにサーバは既に切断されているようです。

Finally he got scared and unplugged the server.

$ curl https://forget-me-not.even-more-militarygrade.pw
curl: (7) Failed to connect to forget-me-not.even-more-militarygrade.pw port 443: Connection refused

ウェブアーカイブに残っているかもしれないと、アクセスしてみるとフラグの書かれたページを表示することができました。

web.archive.org

http://web.archive.org/web/20190309234647/http://forget-me-not.even-more-militarygrade.pw/

f:id:tsalvia:20190514023113p:plain

FLAG

OOO{DAMNATIO_MEMORIAE}