CTF問題とセキュリティ(4年実験)
この実験では、セキュリティコンテストのCTF問題(Capture The Flag競技)について、インターネットの仕組みを理解し、その問題の解き方を考え、新しく自分自身でCTF問題を作ってもらいます。
日程
実験は、4週にわたり、以下の日程で行います。
週 | 内容 |
1 | (前半)暗号・ファイル・Web |
2 | |
3 | (後半) プログラム作成・インターネット・OS |
4 |
前半・後半でそれぞれ、問題例(もしくは自分で見つけたCTF問題)の1つの説明と、自作の問題を新しく作り説明をしてください。
提出物
- 実験の目的
- 問題例(or 自分で見つけたCTF問題)を1つ選び
- 前半・後半それぞれについて
- その問題が情報セキュリティにどう関係しているのか
- 問題の解き方のしくみと解説
- 自作問題について
- 前半・後半それぞれ
- その問題が情報セキュリティにどう関係しているのか
- 問題の解き方、問題の作り方
- しくみと解説
- 提出先はこちらのTeams共有フォルダに。
- 自作問題の例
- サブリミナル効果で隠されたGIF
- 電子透かし
- 学校からアクセスする時だけ、答えが見える。(REMOTE_HOST)
- スマホでアクセスした時だけ、答えが見える。(HTTP_USER_AGENT)
- Google 検索した後だけ、答えが見える。(HTTP_REFERER)
コンパイラと関数電卓実験の総括
複雑な字句解析
コンパイラでは、字句解析→構文解析を行うのが一般的である…と説明をしたが、最近のC++では少し話がややこしい。
C++ではテンプレート機能などがあるので、整数型のリストみたいな型は、forward_list<int>といった書き方をする。そして、リスト型のリストを作る場合は、forward_list<forward_list<int>>という型が出てくるかもしれない。しかし、この場合、C言語の単純な字句解析処理が行われると、forward_list, “<” , forward_list , ”<” , int , “>>” というトークンに分解されることになる。しかし、これでは、ただしいC++でのテンプレート表記に構文解析できないので、少し古い C++03 では、”forward_list<forward_list<int> >”と、最後の2つの”>”の間に空白を入れる必要があった。
しかし、これはプログラム記述上問題も多いため、最新の C++11 では、”>>”と書いてもテンプレート記述の”<“との組を判断して、”>”,”>”と2つに分解してくれる。このため、字句解析の処理が lex のようなものでは不十分となっている。
形態素解析
今回の実験では、コンパイラを作るという目的で、字句解析、構文解析 を行う流れを説明し演習を行った。しかし、こういった処理は、自然言語処理でも使われている。
自然言語処理(Natural Language Processing)とは、人間の言語(自然言語)を機械で処理し、内容を抽出することです。
具体的には、言葉や文章といったコミュニケーションで使う「話し言葉」から、論文のような「書き言葉」までの自然言語を対象として、それらの言葉が持つ意味をさまざまな方法で解析する処理技術を指します。(入門編)自然言語処理(NLP)とは[引用]
今回のコンパイラの技術では、最初の処理は字句解析で説明をしていたが、日本語の場合は形態素解析が必要となる。
形態素解析 — 形態素解析とは、文法的な情報の注記の無い自然言語のテキストデータから、対象言語の文法や、辞書と呼ばれる単語の品詞等の情報にもとづき、形態素の列に分割し、それぞれの形態素の品詞等を判別する作業である。(wikipedia引用)
意味解析
自然言語処理では、これに加え構文解析の後に、意味解析の処理が行われる。例えば、「高い」という単語は、金額の意味なのか、高度の意味なのか、判断が必要だが、かかり受けする単語にお金に関するものであれば金額と判断するし、身長という単語があれば高低の意味と判断し、全体の意味を解析する。
コンパイラ処理でも、目的プログラム生成行程プログラミング言語において、コンパイラーがソースコードを解析し目的プログラムを生成する際の処理工程のひとつ。意味解析は、ソースコード内に記述された変数の型や文(ステートメント)が言語の記述仕様に沿っているかどうかをチェックする。
静的型付け・動的型付け・型推論
プログラム言語のコンパイラでも、意味解析が必要な事例として、型推論について紹介する。プログラム言語では、プログラムを記述する際に、値を記憶するために型情報が重要である。C言語では、明確に型を記述する必要がある(静的型付け言語)。これに対し、Perl , Python , PHP , JavaScript といった言語では、変数にどういった型の情報でも代入が可能となる。このため、変数宣言では型を明記する必要がないが、プログラムが動作している時点でインタプリタは型を確認しながら処理が行われる(動的型付け言語)ため、無駄な型判定処理が常に行われ処理効率が悪い。また、動的型付け言語では、型が明記されていないのでプログラムの間違いが見逃されることもある。
Microsot では、JavaScript の動的型付けの問題を解決するために、TypeScript を開発している。TypeScript では JavaScript に、静的型付けとオブジェクト指向のクラスの機能が追加されている。
プログラムを安全に作る視点であれば、データの型のチェックが行われる静的型付けはバグを減らす意味で重要であるが、プログラム記述が複雑になる問題も出てきている。例えば、C++ でのリスト処理は、forward_list のテンプレート機能を使うと、以下のように書ける。
#include <iostream>
#include <forward_list>
int main() {
// std::forward_list<>線形リスト
std::forward_list<int> lst{ 1 , 2 , 3 } ; // 1,2,3のリストで初期化
// for( List* p = lst ; p != NULL ; p = p->next ) {...} に相当
for( std::forward_list<int>::iterator itr = lst.begin() ;
itr != lst.end() ;
itr++ ) {
std::cout << *itr << std::endl ;
}
}
しかし、繰り返し処理のためのデータ型(反復子) itr の宣言は、ただ「リストの要素で繰り返し」とい目的で書くには、型宣言が面倒すぎる。
そこで、最新の C++ では、型推論 とよばれる機能が導入され、型宣言の初期化の右辺式から変数の型を推論してくれる。下記プログラム例では繰り返しのイテレータ itr が auto という曖昧な型で宣言されているけど、初期化の右辺式 lst.begin() の型 std::forward_list<int>::iterator で宣言してくれる。あくまでも型推論は、コンパイル時に型が確定しているので、静的型付け言語の便利な機能の1つである。
#include <iostream> #include <forward_list> int main() { // std::forward_list<>線形リスト std::forward_list<int> lst{ 1 , 2 , 3 } ; // 1,2,3のリストで初期化 for( auto itr = lst.begin() ; // lst.begin() の型からitrの型を推論 itr != lst.end() ; itr++ ) { std::cout << *itr << std::endl ; } }
しかし、変数の型推論をしなくちゃいけないのは、変数を使った副作用を伴う記述方法が間違いのモトという考え方では、関数型プログラミングという話が出てくる。C++のalgorithm = 関数型という意味じゃないけど…
#include <iostream> #include <forward_list> #include <algorithm> int main() { std::forward_list<int> lst{ 1 , 2 , 3 } ; std::for_each( lst.begin() , lst.end() , []( int x ) { // 配列参照のコールバック関数 std::cout << x << std::endl ; } ); return 0 ; }
lexのそれ以外のアクション
flex+bisonを使った関数電卓を作る専攻科の実験で、空白処理の質問が出た。
こちらのような mycalc.l だと、空白の場合の処理とか、それ以外の文字が入力された時の処理は記述していない。
lex の出力する C 言語のソースをざらっと読むと、正規表現で指定しないパターンに対しては、そのまま文字を出力する処理( ECHO という入力文字をそのまま出力する #define マクロ)が生成される。だから、例で示したプログラムだと、式として”1+ab2″ を入力すると”ab>>3″と出力される。
%% 文字1 アクション1 # このような処理を書くと、雑な説明でいうなら、以下のような処理が作られる 文字2 アクション2 %% switch( 入力文字 ) { case '文字1' : アクション1 ; break ; case '文字2' : アクション2 ; break ; default : ECHO ; // 入力文字をそのまま出力するマクロ break ; }
このため空白を読み捨てる、それ以外の文字が入力されたら字句解析段階でのエラーを出力するのであれば、lex の正規表現の処理の部分に以下のようなものを追加する必要がある。
[ ¥t] ; /* 空白やタブ文字の時は何もしない */ . { /* それ以外の文字は、エラーメッセージとエラーとなった文字を出力して中断 */ /* 正規表現の . は、任意の1文字という意味 */ printf("lex:error ") ; ECHO ; exit( 1 ) ; }
batchとnohup
学校の CUDA サーバを自宅より使う学生より、「接続が Broken Pipeで切れるのどうすれば?」との質問があった。
原因は ssh で操作が何もない状態が続くと、セキュリティのために接続が切られるため。んで、.ssh/ssh_config でセッションの最大時間を延ばす設定あたりを説明したけど、「計算どのぐらいかかるの?」と聞くと「20時間かな…」。# さすが CUDA を使う卒研。
ssh のセッション延長の設定で20時間とかしちゃうと、ネットワークトラブルで通信が途中で切れたときに、サーバ側で「まだこの ssh 通信中だよね…」というプロセスが生き残る可能性があるので、あまり勧められる方法ではない。
こういう時には、処理を継続したままで、ログアウトするテクニックが必要。
通常は処理を起動して、ログアウトすると親プロセスにあたる sh が死ぬので、子プロセスが死んでしまう。
# このプロセスの原則があるから、Linux サーバで「訳の変わらんプロセスがあったら、『親を殺せ!!』が基本。」
プロセス確認の基本
((( 通常のプロセス全表示 ))) $ ps ax ((( 指定したコマンドのプロセス情報表示 ))) $ ps ax | grep コマンド名など ((( プロセスの起動引数を全部表示 ))) $ ps axl ((( 負荷の高い順にプロセス一覧を表示 ))) $ top ((( 指定したプロセスを停止 ))) $ kill -KILL プロセス番号
プロセスのバックグラウンド起動
((( プロセスをバックグラウンド起動 ))) $ コマンド... & # &がバックグラウンド起動の意味 ((( プロセスをバックグラウンドに変更 ))) $ コマンド... # フォアグランドでプロセスが動き出す ^Z (Ctrl-Z) # プロセスを一時停止 $ fg # 停止中のプロセスを再びフォアグラウンドで再起動 $ bg # 停止中のプロセスをバックグラウンドで再起動 $ jobs # 起動中の job を表示
上記で説明したコマンドは、login した shell の子プロセスになるため、たとえバックグラウンドで起動して「裏で動いている…プロセス」といえども、logout すると親プロセスが死ぬので、一緒に子プロセスも死んでしまう。
このため、起動するプロセスの親をシステムに代替わりしてもらって起動する nohup コマンドが使われる。
((( プロセスを No Hugup コマンドで起動 ))) $ nohup コマンド & # 出力はファイル保存される。 $ nohup コマンド > file.out & # 明示的にリダイレクトで表示保存先を指定 $ tail file.out # 保存しているファイルの末尾を表示
ただし nohup コマンドだと、プロセスが終了したかどうかわからない。こういう時は、batch コマンドが便利。ただし、batch コマンドはインストールされていない処理系が多い。(メールの設定が必要だから)
((( at パッケージのインストール ))) $ sudo apt-get install at # at(指定時間コマンド起動) # batch(バッチ処理起動) ((( batchの使い方 ))) $ batch at> コマンド at> ^D $ echo "コマンド" | batch
ただし batch は PATH が /bin/sh だけで起動するので、”python ほげ.py” とかで起動しても動かない場合あり。”/usr/bin/python ほげ.py” とかPATHを明記して起動するか、PATH=/usr/local/bin:/usr/bin:/bin なりの設定する処理を明記しないと動かない場合があるので要注意。
batch は、処理の出力結果はメールで送られてくる。結果を残すのなら、出力リダイレクトをしておく方がいい。
さくらクラウドの実験サーバのアップグレード
学生実験で、Webプログラムのセキュリティ問題の対応をテーマに実験しているけど、Ubuntu 18系を20系にアップグレードを行った。基本的な実験だけなので、Apache+PHP程度なので、移行も手間取らないだろうと踏んでいたけど、sulinux でちょっと手間取った。
# systemctl enable apache2 # aptitude install update-manager # aptitude update # aptitude dist-upgrade # do-release-upgrade -d
無事に、更新が終わって再起動をかけたら、ブートに失敗。ssh などが繋がらなくなる。
どうしようもなくなったので、さくらのクラウドのコンソールをみると、sssd 関連でエラーが出てブート途中で止まっている。
冷や汗をかきながら、Ubuntu ブート時のリカバリーモードの起動を試みて、ようやく成功。
起動時にESCを押すと、ブートメニューが表示される。インストール済みの kernel とそのリカバリーモードの一覧がでるので、リカバリーモードでログイン。”aptitude remove sssd-common” により一旦、sssd を削除(sulinux関連)すると、無事に起動したので、基本機能の確認をして、再び”aptitude install sssd-common”。
アップグレード作業中に、apache2 が disable されていたり、php の関連パッケージが入っていなかったので、インストール。
# systemctl enable apache2 # systemctl start apache2 # aptitude install php-mbstring php-sqlite3 # a2enmod php7.4
情報処理演習室のルータを交換
EI棟3F演習室で、Aruba の WiFi 環境のための Buffalo ルータを使っていたけど、警告メッセージが出始めたので、確認したらルータの管理画面さえ表示できなくなってきた。古いルータだったので寿命と思われる。
かといって、このルータは、4EI教室,5EI教室,2F実験室,3F演習室,4F創成LABを束ねる Aruba WiFi の上流にあたるため、実験室がことごとく使えなくなる。そこで、手持ちのマニアックな Edgerouter-X に入れ替える。
データの扱いは丁寧に
うーむ、卒研発表のレジメ、有効数字がおかしいので、質疑応答の時にツッコミを入れた。
有効数字を注意すること
私らの恩師のS先生なら、このネタやると卒研発表の会場が凍りついたよなぁ… (^_^;
でも、情報系の人間は、それっぽく見える数字で嘘ついちゃいかん。
卒研の査読のお手伝いしたのでも、この辺の有効桁がいい加減なのがあったので、あえて指摘。
学会の論文とか発表で、こういうところが変な状態だと、変な有効桁というだけで内容を信用してもらえなくなる。
(ちなみに、この学生さんの卒研は内容もあり、問題なく優秀な内容なので、あえて指摘しています。)
グラフの見せ方にも注意
あと、プレゼン資料やレジメ資料だけど、色の使い方にも注意しましょう。
同じ青系でグラフを描いたら、後ろの人なら見て区別できません。
あと、学会などでも印刷資料になるときは、よほどのことがないかぎり白黒印刷。このようなグラフ資料はデータがまるっきり区別できなくなるので、モノクロにしても内容が判別できるように資料を作りましょう。
CTF実験: unix 系開発環境のインストール
CTFでは、様々な unix 系の開発環境のコマンドが用いられる。
これらの環境を構築するためのメモを記載。
Windows の場合
macOS の場合
macOS で unix 系の開発環境を使う場合には、homebrew や MacPorts を用いることが多い。
CTFでよく使われるunix系パッケージ
((ファイル系)) file - ファイルの種別判定 zip - zip,unzip(ファイル圧縮解凍) gzip - gzip,gunzip(ファイル圧縮解凍-GNU) nkf - nkf(日本語漢字フィルタ) hexcurse - hexcurse(バイナリエディタ) ((OS系)) gcc - gcc(コンパイラ-GNU) gdb - gdb(デバッガ-GNU) binutils - objdump(逆アセンブラ) , - nm(オブジェクトファイルのシンボル出力) , - strings(文字部分の出力) ((ネットワーク系)) telnet - telnet(telnetクライアント) netcat-openbsd - nc(TCP/IP 汎用ツール) bind9-dnsutils - nslookup, dig(DNS参照ツール) whois - whois(ディレクトリサービスクライアント) ((ブラウザ系)) w3m - w3m(テキストブラウザ) wget - wget(テキストブラウザ+ダウンローダ) curl - curl(ダウンローダ) ((使用上要注意)) nmap - nmap(ネットワーク調査ツール) wireshark - wireshark(パケットキャプチャ) - windows用とかmac用のバイナリの方が便利
各unix系のインストールコマンド
((WSL2の場合)) $ sudo apt-get install gcc binutils ((Homebrew の場合)) $ sudo brew install gcc binutils ((MacPorts の場合)) $ sudo port install gcc binutils