GSoC 2017にmetasploitで採択された。
GSoCとは
GSoCというのはGoogle Summer of Codeの略で、アメリカでは学校が休みである夏の3ヶ月(5/30〜8/22)の間、Googleからお金💰を貰ってOSSに貢献しようというイベントである。日本では一部の期間、学校がある期間とかぶり、一見むずかしいように見えるが、今のところ問題なく行えている。作業開始前にメンターに夏休みかどうか聞かれたので考慮してくれているかんじはあるが、この辺はOSSによると思う。参加しているOSSの組織は多く、今年は201個の組織が参加している。
Google Developers Japan: Google Summer of Code のおしらせ
Google Summer of Code 2017 statistics part 1 によると、今年は日本から13人採択された模様。インド人569人マジか…
GSoCの流れとしては、まずGoogleが学生を受け入れることができるOSSの団体を募集し、承認された団体は学生に取り組んでほしいタスクのリストを用意しておく。学生はそのリストからタスクを選ぶ、もしくは自分でやりたいテーマを提案することでテーマを決める。 学生はその内容をProposalに書いて応募する。そして採択されるとメンターの指導のもと実装に入る。
このProposalを書くのが結構大変でもちろん英語だし、OSSによっては1週間毎のスケジュールを書く必要があったりする。書いたら終わりじゃなくて、メンターとなりそうな人と連絡を取りレビューしてもらいながら完成させる。Proposalに何を書くかというと、Abstract, Vitals, Skill, Projectの詳細等である。ProposalのテンプレートがOSSによってはあるので気になる人は見てみるといい。このProposalの内容によって採択の可否が決まる。2つのOSSに応募したのでほんとに大変だった。
採択テーマ
採択されたテーマはImproving stager payloads especially on non-Windows platformsでmetasploitというOSSでやっていくこととなった。 ProposalはGistに上げたので興味のある各位はみてくれ。 最初、適当にテーマを選んでProposal書き、レビューしてもらうために送りつけてみたところ、今までの開発経験を評価してもらえたらしく、メンターにこっちのほうがいいよーというかんじで別のテーマを提案してもらえて幸運だった。たしかに面白そうだったので提案してもらったテーマで応募した。
何をやるのか
テーマを直訳すると非windows環境でのstager payloadが、貧弱なので強化していくというかんじになる。 stager payloadというのはstage payloadと連携して動作し、 攻撃者とリモートホストとの通信を確立し、リモートホスト上でstage payloadを読み込み実行するというものである。stageというのは、stagersモジュールによってダウンロードされるpayload componentである。
具体的には、以下のようなことをやる。
windowsと違って、linux環境には汎用的なHTTP libraryがないので、reverse_http/https stagerを作成する。
Windows環境でしか動かない reverse_tcp_rc4 stagerをlinux環境で動くようにする。payloadのfingerprintingを回避するためにrc4で暗号化するというstagerである。
完走できるようやっていくぞ!!!!
RSpecで画像をアップロードするAPIのテストを書く
Rack::Test::Methods
に含まれるRack::Test::UploadedFile.new
を使う。Gemfile.lockを見れば分かるようにrack-test
はデフォルトでRailsアプリケーションにインストールされている。
以下、grapeで作成した画像をアップロードするAPIに対する正常系のテストの例である。
describe Hoge::Image::CreateAPI, type: :request do describe 'POST /Image' do let(:path) { '/api/v1/image/' } let!(:user) { create(:user) } let!(:access_token) { user.activate.access_token } let(:valid_headers) { { 'Access-Token' => access_token } } context 'when param is valid' do let(:valid_params) do { image: { image: Rack::Test::UploadedFile.new('path/forTest.png', 'image/png') } } end it_behaves_like 'require_login', :post describe 'response' do before do post path, params: valid_params, headers: valid_headers @json = JSON.parse(response.body) end it 'returns 201' do expect(response.status).to eq(201) end it 'contains success message' do expect(@json['message']).to eq('Success') end end end end end
GOT、PLTとIAT
動的リンクされたライブラリのアドレス解決の際、ELFではGOT、PLTが、PEではIATがそれぞれ用いられる。たまにどっちがどっちでどうだったのか分からなくなるのでメモしておく。
GOTとPLT
GOT(Global Offset Table)はシンボルへのポインタの配列で、 プログラムが使用するすべてのライブラリ関数はGOTにエントリを持ち、そこで実際の関数のアドレスを管理する。 これにより、ライブラリをプロセスメモリ空間の中で容易に再配置できるようになる。
遅延リンク
ELFではPLT(Procedure Linkage Table)を用いて遅延リンクできるようにしている。 遅延リンクとは、共有ライブラリのロード時にシンボル解決を行うのではなく、関数が実際に呼び出されるときにシンボル解決を行うことである。 関数が最初に呼ばれるとき、飛び先は関数の本体ではなく、PLT上の対応するコードとなり、 そのPLTには、GOTの対応するスロットから飛び先アドレスを取得し、そこに飛ぶようなコードが格納されている。 呼びだされたときには、PLT経由でRTL(RunTime Linker: 実行時リンカ)のシンボル解決のコードが呼ばれ、 GOTに設定してある自分自身の飛び先を、本来の関数に設定しなおす。 関数が次に呼び出されたときには、今度はRTLは関与せずにGOTのエントリを通じて直接実行される。
確認する
以下のコードを実行し、puts()を呼ぶ前と呼んだ後でGOTのエントリを確認する。
コード
#include <stdio.h> int main() { puts("test"); }
gdbにより確認
$ gdb ./a.out Reading symbols from ./a.out...(no debugging symbols found)...done. gdb-peda$ start gdb-peda$ disas main Dump of assembler code for function main: 0x0000000000400526 <+0>: push rbp 0x0000000000400527 <+1>: mov rbp,rsp => 0x000000000040052a <+4>: mov edi,0x4005c4 0x000000000040052f <+9>: call 0x400400 <puts@plt> 0x0000000000400534 <+14>: mov eax,0x0 0x0000000000400539 <+19>: pop rbp 0x000000000040053a <+20>: ret End of assembler dump. gdb-peda$ disas 0x400400 Dump of assembler code for function puts@plt: 0x0000000000400400 <+0>: jmp QWORD PTR [rip+0x200c12] # 0x601018 0x0000000000400406 <+6>: push 0x0 0x000000000040040b <+11>: jmp 0x4003f0 End of assembler dump. gdb-peda$ x/x 0x601018 // GOTを確認 0x601018: 0x0000000000400406 // 書き換え前 gdb-peda$ break 0x400534 Function "0x400534" not defined. gdb-peda$ break *0x400534 Breakpoint 2 at 0x400534 gdb-peda$ c Continuing. test Breakpoint 2, 0x0000000000400534 in main () gdb-peda$ x/x 0x601018 // GOTを確認 0x601018: 0x00007ffff7a7d690 // 書き換え後
こうして遅延リンクを用いたアドレス解決は位置独立性と共有性を損なわずに行われる。
遅延リンクを用いるときGOTを動的に書き換えるため、GOTは書き込み可能である必要がある。 そのため、format string bugやROPを用いてGOT overwriteするといった形で攻撃に用いられることがある。
この攻撃に対するセキュリティ機構としてFull RELRO(RELocation ReadOnly)というものがある。 これは、遅延リンクを行わずに、ローダがプログラム実行開始時に実際の関数アドレスをGOTのエントリに挿入することで、 GOTを書き込み禁止するというものである。
IAT
Windows PEにもGOTと似たIAT(Import Address Table)というのがある。IATはプログラム中で使用するAPIのコードの開始位置のアドレスを格納したテーブルである。
IATのエントリにはローダがメモリ空間上に各DLLが読み込まれたアドレスをベースにして、実際の関数開始アドレスを書き込む。ローダが書き込むので実行前にIATのエントリは書き換えられる。これによって、プログラムはIATを参照するだけでAPIを呼び出せるようになる。IATのエントリは実行時には書き換える必要性がないので、デフォルトで書き込み禁止にすることができ、GOTより実行中に書き換えられるリスクは軽減されている。
IATが書き込み禁止になっているからといって、書き換えられない訳ではなく、Windows APIのVirtualProtect()を用いて書き込めるようにできる。 これはAPIのIATエントリを書き換え、任意の関数を指すようにするAPIフックに利用される。
参考資料
- C/C++ セキュアコーディング
- リンカ・ローダ実践開発テクニック
- Practical Malware Analysis
- ld -z relro で GOT overwrite attack から身を守る - memologue
Pythonスクリプトに対してユニットテストするときのディレクトリ構成
フレームワークを使って開発しているとユニットテスト用にディレクトリが用意されていることもあるが、単体のPythonスクリプトに対してユニットテストを書くときはディレクトリ構成を考える必要がある。やはりユニットテストとそれ以外は違うディレクトリに配置したい。この記事ではディレクトリ構成を紹介する。
ディレクトリ構成
new_project ├── project_name │ ├── __init__.py │ └── hoge.py └── tests ├── __init__.py └── test_hoge.py
空の __init__.py
をディレクトリに置きパッケージ化することで、ディレクトリの下のファイルを読めるようにしている。
これによって、new_project以下からtests以下のテストを実行すると、project_name以下のファイルをimport文で読み込むことができる。
tkmru/nao: No-meaning Assembly Omitter for IDA proでもこのようなディレクトリ構成にしている。
なお、unittestモジュールを使ったテストの実行は以下のコマンドでできる。
テストすべてを実行
python -m unittest discover
テストケースを指定
python -m unittest tests.ファイル名.クラス名
メソッドを指定
python -m unittest tests.ファイル名.クラス名.メソッド名
参考資料
近況
nao: No-meaning Assembly Omitter for IDA proを公開したところ、リポジトリにスターがいっぱいついたり、unicornの作者にコメントされたり、unicornのshowcaseに載せてもらえたりして高まった。
ツイートされた!
w0w, finally someone used Unicorn emulator to built an IDA plugin for binary deobfuscation \O/https://t.co/9KSWtH1XR0 pic.twitter.com/wAlmOKVSKa
— Unicorn Engine (@unicorn_engine) 2017年1月13日
リポジトリのissueでほめられた!!
わいわい!! pic.twitter.com/8vfOg3SJcJ
— たけまる (@tkmru) 2017年1月19日
cool project! · Issue #23 · tkmru/nao
showcaseに載せてもらった!!!
Showcases – Unicorn – The ultimate CPU emulator
やっていきましょう。
Shuffler: Fast and Deployable Continuous Code Re-Randomization の紹介
はじめに
情報セキュリティ系論文紹介 Advent Calendar 2016 - Adventarの23日目の記事として、Shuffler: Fast and Deployable Continuous Code Re-Randomization | USENIXの紹介をやっていく。今日は12月23日であり、明日はクリスマス・イブである。
これはJIT-ROPに代表される、memory disclosure vulnerabilityを利用した攻撃に対する防衛機構の話である。 JIT-ROPに関してはx64でDynamic ROPによるASLR+DEP+RELRO回避をやってみる - ももいろテクノロジーを見てほしい。
- 論文: https://www.usenix.org/system/files/conference/osdi16/osdi16-williams-king.pdf
- スライド: https://www.usenix.org/sites/default/files/conference/protected-files/osdi16_slides_williams-king.pdf
この記事で扱う図はすべて上の2つのリンクから借用した。
概要
現代のシステムでは、NX bitやASLRによりコードインジェクション攻撃は実質的に排除されているが、今日のプログラムはROPのようなコードを再利用する攻撃に対して脆弱である。また、Anti-ROP: A Moving Target Defense によると、ソフトウェアに対する攻撃の90%でROPが用いられている。
特に悪意のあるものはJust-In-Time ROP(JIT-ROP)であり、攻撃者はmemory disclosure vulnerabilityを利用して実行時にgadgetを発見する。これに対する対抗策として、攻撃者がgadgetを見つけるよりはやく、コードを再ランダム化しようというアイデアのもと、Shufflerは開発された。Shufflerは、コード位置を20-50ミリ秒単位で連続的に再ランダム化し、攻撃者に時間の制限をかけることで対処する。 この制限はネットワーク経由での攻撃に特に有用で、攻撃者マシンから数十ミリ秒離れた場所にあるサーバープログラムに対して、攻撃を最後まで行うことは困難となる。
また、本研究の貢献は以下の4点だと述べられている。
Deployability: Shufflerによる再ランダム化は、ソースコード、コンパイラ、リンカを変更とせず、ローダーへのミニマムな変更だけで行うことができる。
Speed: レイテンシ、オーバーヘッドが低い、非同期の再ランダム化によって、disclosure-based attackに対しミリ秒のオーダーのリアルタイムデッドラインを導入する。
Egalitarianism: 我々は防衛機構をどのようにセルフホストし、trusted computing base (TCB)の拡大を回避するかについて説明する。 Trusted computing base - Wikipedia
Augmented binary analysis: コンパイラから得られる情報(シンボルや再配置)を活用することで、バイナリ上で完全で正確な分析が可能であることを示す。
Implementation
Shufflerは x86-64 Linuxのユーザー空間で動作し、バイナリが依存するすべての共有ライブラリと、バイナリ自体をシャッフルする。 シャッフルするプロセスは、シャッフルされるプログラムのスレッドの実行をさまたげることなく、スレッド内で非同期に実行される。
以下の図は、シャッフルされたコードのスナップショットである。 code pointerはcode pointer tableを介して指され、リターンアドレスはXORで暗号化され、スタックに積まれる。
Shufflerはシャッフルする各期間で、コードの新しいコピーを作成し、code pointer tableを更新、すべてのスレッド(自身を含む)にシグナルを送る。 各スレッドはそのスタックをunwindsし修正する。
Shufflerはすべてのスレッドが unwindingを終えるまでバリアーで待機し、以前のコピーしたコードを消去する。Shufflerの実装は、共有ライブラリ、複数のスレッド、フォーク(それぞれの子は独自のシャッフラースレッドを取得する)、{set、long} jmp、システムコールの再エントリ、シグナルなど、多くのシステムレベルの機能をサポートしている。
Transformations to Support Shuffling
元のコードの前後に以下のように命令を挿入することで、return addressをxorしている。
func: mov %fs:0x28,%r11 xor %r11,(%rsp) ; original code mov %fs:0x28,%r11 xor %r11,(%rsp) ret
関数を呼び出す際のアドレス解決は以下のように行われる。
call命令はそのとき以下のように書き換えられる。
callq *%rax => callq *%gs:(%rax)
また上のときのraxは以下のように書き換えられ、データセグメントのindexとして扱われる。
mov $0x40054d, %rax => mov $0x20, %rax
Bootstrapping
2つのライブラリ(stage1, stage2)を使って、シャッフルされたコードを起動するので、 システムは現在実行中のモジュールのコードポインタを上書きしません。 これらのライブラリは、LD_PRELOADを使用してターゲットにinjectionされる。 ローダー機能を再実装するのではなく、システムローダーに任せて有効なプロセスを作成し、 プログラム(またはそのコンストラクター)が実行を開始する前に引き継ぐ。
stage1のコンストラクタは、リンカメカニズム-z initfirst
を介して他のもののより早く呼び出される。
次に、ローダ自体にブレークポイントを設定することによって、stage1では、他のすべてのコンストラクタがシャッフルされたコードで実行されるようになる。
最後に呼び出されるコンストラクタ(LD_PRELOADの副作用)はstage2自身のコンストラクタである。
stage2は専用のShufflerスレッドを作成し、他のすべてのコードの元のコピーを消去し、シャッフルされたELFエントリポイントで実行を再開する。
評価
ROP、direct JIT-ROP、indirect JIT-ROP、およびBlind ROPを含む、コードを再利用するすべての既知の形態の攻撃に対し、Shufflerが防御できることを示した。 50 msごとにシャッフルするとSPEC CPUのオーバーヘッドは14.9%となり、Nginxなどの現実のアプリケーションの上でShufflerを実行できることを確認した。シャッフルされたNginxは12コアで最大24のワーカープロセスをスケーリングできた。
また、CVE-2013-2028によってBlind-ROPが可能なNginx 1.4.0を用いて、攻撃が完了するまでに7分かかることを測定した。 このBlind-ROPをシャッフルされたNginxで試したとき、攻撃者はPLT、stack canaryを見つけられなかった。 親プロセスと子プロセスが独立してランダム化されるため、攻撃は成立しなかった。
感想
gadgetが発見される前にコードを再ランダム化するというアイデアだけではなく、再ランダム化することに対する攻撃手法も述べられており、面白い論文だった。是非コードを読みたい。
Railsを5.0にupdateした時のメモ
方法
Gemfileを編集する。Railsのversionを最新版に指定してbundle update
。
gem 'rails', '5.0.0.1' # そのときの最新版を指定してくれ!
Rails5に対応していないversionを指定されているgemがあったりするのでエラーメッセージに応じてversion指定を抜く必要がある。
bin/rails app:update
で設定ファイルをRails5向けに書き換えてくれるが、config/application.rb
や config/route.rb
も容赦なく書き換えようとしてくるので、diffを見たりして注意する必要がある。
コードを変更した点
skip_before_action
同一ファイル内にcallback関数がない場合、raiseオプションにfalseを設定しないと、ArgumentErrorが出るように変更された。sourceryというgemのcallbackを使うところで変更が必要になった。
skip_before_filter :require_login, raise: false
本体のコードの該当箇所
- rails/callbacks.rb at 6dfab475ca230dfcad7a603483431c8e7a8f908e · rails/rails
- rails/callbacks.rb at 6dfab475ca230dfcad7a603483431c8e7a8f908e · rails/rails
時間の扱い
いままでは、Active Recordが時間をUTCで保存するため、JSTで時間を扱いたいとき、Time.zone.utc_to_local
でいちいち変換する必要があった。
Rails5ではconfig/application.rb
に以下のように書くことで、DBにUTCで入っている時間をJSTとして扱えるようになり、Time型の扱いが楽になった。
config.time_zone = 'Tokyo' config.active_record.time_zone_aware_types = [:datetime, :time]
しかし、DBのTime型には日にちが入らないため、UTCからJSTに変換する際、+9時間すると日をまたぐ場合がある。そのため、ActiveSupportのTime.change(day:n)
を用いて日にちを変更する必要は残った。
参考
Rails で DB の Time 型を扱う - おもしろwebサービス開発日記
雑感
rakeコマンドをつかわなくても、railsコマンドでdb:migrate等をできるようになっててよい。
あと、 config.active_record.time_zone_aware_types
のおかげで時間をJSTとして扱えるのがほんとに最高。
神っぽい。