Shodanは脆弱性などを探す検索エンジン。一般的にはサイトに検索キーを入力して使うが、APIも提供されているので、Jupyter(Azure Notebook)から試してみた。
公式ドキュメントはこちら。
まずJupyterでshodan-pythonをインストール。
!pip install shodan
APIキーはshodanにユーザ登録して発行されたものを入力する。うっかりキーを公開してしまわないよう、getpass()を使って入力している。
import shodan
import pprint
from getpass import getpass
API_KEY = getpass()
api = shodan.Shodan(API_KEY)
例えば公開されているRaspberry Piの一覧を取得してみる。
query = 'Raspbian'
limit = 10
try:
result = api.search(query)
for index, service in enumerate(result['matches']):
for header in service['data'].splitlines():
if query in header:
print('{0} {1}'.format(header,service['location']['country_name']))
for key in service:
if 'vulns' in key:
print(service[key].keys())
if index == limit:
break
print(index)
except Exception as e:
print('Error: {0}'.format(e))
生々しいのでサンプルでは国名を出力してるが、当然ながらIPアドレスやホスト名なども出せる。
出力は、こんな感じ。Shodanのユニークな点は、脆弱性が残っているバージョンの場合、そのCVEまで出力できるところだろう。
SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u4 Netherlands
SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u4 United Kingdom
SSH-2.0-OpenSSH_6.7p1 Raspbian-5+deb8u3 Finland
Server: Apache/2.4.10 (Raspbian) United States
dict_keys(['CVE-2017-15710', 'CVE-2016-8612', 'CVE-2017-7679', 'CVE-2017-9788', 'CVE-2016-0736', 'CVE-2014-3583', 'CVE-2014-8109', 'CVE-2018-1283', 'CVE-2015-3185', 'CVE-2015-3184', 'CVE-2017-3167', 'CVE-2017-9798', 'CVE-2016-8743', 'CVE-2017-7668', 'CVE-2017-3169', 'CVE-2018-1312', 'CVE-2016-2161', 'CVE-2017-15715'])
Server: Apache/2.4.10 (Raspbian) France
dict_keys(['CVE-2017-15710', 'CVE-2016-8612', 'CVE-2017-7679', 'CVE-2017-9788', 'CVE-2016-0736', 'CVE-2014-3583', 'CVE-2014-8109', 'CVE-2018-1283', 'CVE-2015-3185', 'CVE-2015-3184', 'CVE-2017-3167', 'CVE-2017-9798', 'CVE-2016-8743', 'CVE-2017-7668', 'CVE-2017-3169', 'CVE-2018-1312', 'CVE-2016-2161', 'CVE-2017-15715'])
また、query='Raspbian'
と指定しているところを、query = 'net:"1.1.1.0/24"'
とすれば該当するアドレスレンジに脆弱性が残っていないかをチェックすることができる。どちらかというと、こういう使い方がShodanならではだろう。(余談だが1.1.1.0をサンプルに指定したのは、shodanの公式ドキュメントがそう書かれているからであって、別にcloudflareを調べたかったわけではない)
統計データを出力することもできる。ここではproduct,port,vulnの3つをFACETとして指定している。
query = 'Raspbian'
FACETS = [
'product',
'port',
'vuln',
]
try:
result = api.count(query, facets=FACETS)
print('Total Results: {0}'.format(result['total']))
for facet in result['facets']:
print(facet)
for term in result['facets'][facet]:
print('{0}: {1}'.format(term['value'], term['count']))
print()
except Exception as e:
print('Error: {0}'.format(e))
こちらの出力は、こんな感じ。22番ポートが誰でもアクセスできてしまうあたり、ドキドキしてしまう。ちなみにfacetにcountryとかorgを指定することもでき、もっとドキドキすることも可能。
Total Results: 171625
vuln
cve-2017-9798: 52888
cve-2017-7679: 52861
cve-2017-7668: 52859
cve-2017-3169: 52859
cve-2017-9788: 47496
product
Apache httpd: 65763
Postfix smtpd: 2589
nginx: 45
ProFTPD: 3
Exim smtpd: 3
port
22: 73015
80: 39929
443: 20166
2222: 10704
8080: 3757
当然ながら、Shodanの検索結果に出なかったからリスクがないというわけではない。検索結果の扱いは自己責任で。