MapboxでSSHの不正アクセスを可視化してみる

オープンソースの地図プラットフォームであるMapboxを使ってSSHブルートフォースアタックを可視化してみます。

Mapboxとは

www.mapbox.com

MapboxはWeb上で簡単に地図を作成、公開できるサービスです。OpenstreetMapをベースとして作られています。某G**gle Mapも似たようなサービスを提供していますが、Mapboxであればデザインや表示する情報などを自分好みにカスタムした地図を使えます。

きっかけ

ある日Mapboxからこんな感じのメールが送られてきました。

Happy Friday!

It's #weekendmaphack time. Show us your take on our latest designer maps styles. Build something with one (or more) of the four below, and I’ll send you our new stickers.

Share screenshots and videos of what you’re creating with @mapbox on Twitter, using the hashtag #weekendmaphack. I’ll be retweeting and liking your tweets all weekend!

Happy hacking.

ということでMapboxを使って何か作ったらそのスクリーンショットとかを#weekendmaphackタグをつけて呟くとステッカーをくれるらしいです。当時ハニーポットに興味を持っていたので(ステッカー欲しかったので)週末を使って不正アクセスがきた場所を地図上に表示するというものを作ってみることにしました。

準備

実装する前にまず実際の不正アクセスのデータを用意する必要があります。今回はSSHに対するブルートフォースアタックのデータを集めます。本来はセキュリティを考慮してハニーポットによって不正アクセスのデータを集めますが、週末の限られた時間しかないので直接ログを収集することにしました。自宅のPC(Linux)でSSHサーバーを動作させ、22番ポートが開いた状態で一定時間放置しておきます(こんなログの集め方でいいのだろうか・・・)。ポートを開けてから15分ぐらいで最初の攻撃が来始め、数時間ぐらい経つと可視化するのに十分な量のログを収集できました。

ログ解析

次に収集したログを解析していきます。Linuxでは認証関係のログは/var/log/auth.logに記録されます。SSHサーバーに対してブルートフォースアタックが来た場合、例えば以下のようなログが見られます。

この攻撃を地図上に可視化するとき最も大事な情報は攻撃元の位置情報を得るためのIPアドレスです。後は攻撃が来た時間や攻撃が来た回数も重要でしょう。上のログのように、auth.logには、"Failed password"という文字列が含まれている行に攻撃元のIPアドレス情報も乗っています。というわけで、まずauth.logから攻撃元のIPアドレスと攻撃が来た時間を抜き出してみます。

>>> import re
>>> f = open('/var/log/auth.log')
>>> lines = f.read().splitlines()
>>> lines = [x for x in lines if 'Failed password' in x]
>>>
>>> ip_ptrn = re.compile(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}')
>>> time_ptrn = re.compile(r'^\S+\s\d{1,2}\s\d{1,2}:\d{1,2}:\d{1,2}')
>>>
>>> access_times = [time_ptrn.findall(x)[0] for x in lines]
>>> ips = [ip_ptrn.findall(x)[0] for x in lines]

まず"Failed password"という文字列が含まれる行のみを抽出したあと正規表現を使って攻撃元IPアドレスの一覧を得ます。攻撃が来た時間についても同様に正規表現を用います。

ただし、ブルートフォースアタックの性質上同一IPアドレスから何度も攻撃が来るので以下のようにIPアドレスが重複する箇所があります。

>>> ips[:5]
['xxx.xxx.xxx.xxx', 'yyy.yyy.yyy.yyy', 'yyy.yyy.yyy.yyy', 'zzz.zzz.zzz.zzz', 'zzz.zzz.zzz.zzz']
>>>

そこでさらに以下のような処理を行って重複を取り除きつつIPアドレスごとに攻撃を受けた回数を調べます。

>>> hosts = {x: ips.count(x) for x in ips}.items()
>>> hosts[:3]
[('xxx.xxx.xxx.xxx', 681), ('yyy.yyy.yyy.yyy', 5), ('zzz.zzz.zzz.zzz', 1)]
>>>

最後にIPアドレス、時刻、攻撃回数の情報を統合します。

>>> atks = [{'date':access_times[ips.index(hosts[x][0])], 'ip':hosts[x][0], 'num':hosts[x][1]} for x in xrange(len(hosts))]
>>> atks[:2]
[{'date': 'Jul 23 20:48:58', 'ip': 'xxx.xxx.xxx.xxx', 'num': 681}, {'date': 'Jul 23 20:32:54', 'ip': 'yyy.yyy.yyy.yyy', 'num': 5}]
>>>

GeoIP

ログを解析したら、freegeoip.netを使ってIPアドレスから位置情報を取得します。freegeoip.netではAPIが提供されており、以下のフォーマットに従ってHTTPリクエストを送ることでIPアドレスから攻撃元の位置情報を得ることができます。

freegeoip.net/{format}/{IP_or_hostname}

{format}にはjsoncsvなど自分が欲しいデータの形式を入れます。今回はjsonフォーマットで返して欲しいので、リクエストを送るURLは例えば以下のようになります。

http://freegeoip.net/json/xxx.xxx.xxx.xxx

では先ほど解析したログの情報から位置情報を取得してみます。

>>> import requests
>>> import json
>>>
>>> geoips = [json.loads(requests.get('http://www.freegeoip.net/json/{0}'.format(x['ip'])).text) for x in atks]

この処理は各IPアドレスごとに上記URLに対してHTTPリクエストを行なっているため、終了するまで少し時間がかかります。

また、後ほど説明しますがMapboxで位置情報のデータを扱う際にgeojson形式である必要があるのでgeojson形式に直しておきます。

>>> import copy
>>> point = {"type":"Feature", "properties":{"ip":"ip", "date":"date", "num":"num"}, "geometry":{"type":"Point","coordinates":[10,20]}}
>>> points = []
>>> for x in xrange(len(geoips)):                                               
...     tmp = copy.deepcopy(point)
...     tmp['geometry']["coordinates"] = [geoips[x]["longitude"], geoips[x]["latitude"]]
...     tmp["properties"]["ip"] = geoips[x]["ip"]
...     tmp["properties"]["date"] = atks[x]["date"]
...     tmp["properties"]["num"] = atks[x]["num"]
...     points.append(tmp)
... 
>>> geojson = {"type": "FeatureCollection", "features": points}
>>> json.dump(geojson, open('attacks.geojson', "w"))
>>>

実装(可視化)

攻撃元の位置情報が得られたら、いよいよMapboxを使って攻撃を可視化します。Mapboxを使う際にMapboxアカウントが必要になるので持っていない人は作る必要があります。実装するにあたってはMapbox GL JS API | Mapboxにサンプルコードがたくさんあるのでここから適当にコードを引っ張り出して使います。

アップロードしたgeojsonファイルにある座標を地図上に表示するスクリプトです。38行目の'your access token'の所に自分のAPIアクセストークンを入れ、46行目のvar geojson = の所に自分が作ったgeojsonファイルの内容を貼り付けます。また、マーカー用画像を上のHTMLファイルと同じディレクトリにmarker.pngという名前で置いておきます。出来たHTMLファイルをブラウザで表示すればこんな風に攻撃元の位置情報が表示されます。

おまけ(本題)

出来たものを公開してから1 ヶ月ぐらい経った後にMapboxからステッカーが送られてきました。

f:id:tuz358:20170903185803j:plain

OSS関連のノベルティが貰えるとやっぱり嬉しいです。ちなみに今回作ったものはここに置いてあります。 github.com