JupyterからShodanのAPIにアクセスする

Shodanは脆弱性などを探す検索エンジン。一般的にはサイトに検索キーを入力して使うが、APIも提供されているので、Jupyter（Azure Notebook)から試してみた。

公式ドキュメントはこちら。

https://shodan.readthedocs.io/en/latest/api.html

まずJupyterでshodan-pythonをインストール。

```py
!pip install shodan
```

APIキーはshodanにユーザ登録して発行されたものを入力する。うっかりキーを公開してしまわないよう、getpass()を使って入力している。

```py
import shodan
import pprint
from getpass import getpass
API_KEY = getpass()
api = shodan.Shodan(API_KEY)
```

例えば公開されているRaspberry Piの一覧を取得してみる。

```py
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アドレスやホスト名なども出せる。

https://developer.shodan.io/api/banner-specification

出力は、こんな感じ。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として指定している。

```py
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の検索結果に出なかったからリスクがないというわけではない。検索結果の扱いは自己責任で。