俺のサーバーにSSHで不正アクセスをしようとしているしようとしているやつはどこの国だ!

4 min read

こんにちは、無能です。

表向きとしてVultrのFreeBSDサーバーを公開しているわけですが、SSHでの試行をしようとしている人たちが一体どんな人たちなのか気になったのでみてます。

ちなみに、FreeBSDの場合は"security run output"として自ドメイン宛にお届けしてくれてどんな試行があったのかを教えてくださります。

こんな感じ。

というわけで、geoipを使いながらこれで試行しようとしている人たちがどこの国のグローバルIPアドレスか覗いてみることにしましょう。

ちなみに、geoiplookupコマンドは以下のようにしてインストールが可能です。

sudo pacman -S geoip

コマンドはこれ

$ geoiplookup IPアドレス
GeoIP Country Edition: RU, Russian Federation

bashで文字の整形

それではまずは元データ"security run output"の文字列を自ThinkPadになんでもいいのでもってきてから整形をしていきます。

cat vultr | grep -oP "from.*port" | awk '{print $(NF-1)}' | sort | uniq > grepip 

上記の場合はvultrという"security run output"の記述からgrepのoでマッチングした値とPでPerlの正規表現を使いfromという文字列からportまでがある記述を抜き出します。
以下のような文字列になるかんじ。

from IPアドレス port

この時点でawkを使わなかった理由は、スペースを区切りとするとパスワードを間違えた場合とそもそも自分のサーバーに対象ユーザが存在しなかった場合でエラー出力が変わるので全て拾うためにfrom~portまでの文字列を抜き出しました。

次にawkで末尾の1番目のレコードにあるIPアドレスだけ抽出し、sortとuniqで重複IPを削除、そしてgrepipというファイルに書き出しています。

そして、bashでこのIPに対してgeoiplookupをした後に国と軽く集計を取ってもらうようにしましょう。

$ cat country.sh
#!/bin/bash

# IPアドレスのファイル
INPUT="grepip"

# 出力ファイル
OUTPUT="countries.log"

# 初期化
> $OUTPUT

# 各IPアドレスに対してgeoiplookupを実行し、国情報を取得
while IFS= read -r ip; do
    geoiplookup "$ip" | awk -F: '{print $2}' >> $OUTPUT
done < "$INPUT"

# 国情報を集計して表示
sort $OUTPUT | uniq -c | sort -tr

これで実行権限を与えます。

chmod +x ./country.sh

これで実行するだけ

$ ./country.sh
      1  BE, Belgium
      1  BG, Bulgaria
      1  BR, Brazil
      1  CH, Switzerland
      1  HK, Hong Kong
      1  HU, Hungary
      1  IR, Iran, Islamic Republic of
      1  IT, Italy
      1  LT, Lithuania
      1  MT, Malta
      1  MX, Mexico
      1  MY, Malaysia
      1  NO, Norway
      1  PA, Panama
      1  PE, Peru
      1  SE, Sweden
      1  SK, Slovakia
      1  TH, Thailand
      1  TM, Turkmenistan
      1  TZ, Tanzania, United Republic of
      1  UZ, Uzbekistan
      1  ZA, South Africa
      2  RO, Romania
      2  UA, Ukraine
      3  PL, Poland
      6  GB, United Kingdom
      7  VN, Vietnam
      8  CA, Canada
      8  ID, Indonesia
      9  RU, Russian Federation
      9  SG, Singapore
     10  FR, France
     10  KR, Korea, Republic of
     11  DE, Germany
     11  NL, Netherlands
     16  CN, China
     18  IN, India
     43  US, United States

ほうほう。。。

わかりやすくMatplotlibでチャート表示

さて、文字列上ではわかりますがこれでは数が多くなった場合に見にくいのでチャート表示させたいですね?
また、スクリプト化しておけばcronジョブとして生成してくれるようにもできます。

ということで便利なPythonのライブラリであるMatplotlibでチャート表示を行います。

インストールとコード

pip install matplotlib
cat AreYouFrom.py 
#!/usr/bin/python3

import matplotlib.pyplot as plt
import collections

# ファイルを読み込み、国ごとにアクセス数を集計
country_counts = collections.Counter()

with open('countries.txt', 'r') as file:
    for line in file:
        # 国情報を取得
        country = line.strip().split(",")[1]
        country_counts[country] += 1

# データをソート
countries, counts = zip(*country_counts.most_common())

# グラフを作成
plt.figure(figsize=(10, 6))
plt.bar(countries, counts, color='skyblue')
plt.xlabel('Country')
plt.ylabel('Accesses')
plt.title('Accesses by Country')
plt.xticks(rotation=90)
plt.tight_layout()

# pngファイルとして画像出力
plt.savefig('accesses.png')

これでチャート出力と画像への出力ができます。

chmod +x AreYouFrom.py

(中学生英語みたいなファイル名やな!)

デデドン!

どうやら米国からが一番多いみたい。

SSHしようとしてるポート別に集計

ではアクセスしようとしているポート別に調べてみます。
これでポートだけgrepできました。

cat vultr | grep -oP "port [123456789].*ssh2$" | grep -oP "[123456789].* "

これをports.txtとして出力。
しかしですが、全てポート番号が違うのでplot出力時にはそれぞれブロックごとに別けていきましょう。

$ cat AreYouPort.py 
#!/usr/bin/python3
import matplotlib.pyplot as plt
from collections import Counter

# ポート番号をファイルから読み込む
with open('ports.txt', 'r') as file:
    ports = [int(line.strip()) for line in file]

# ポート番号の頻度分布を計算
counter = Counter(ports)

blocks = {}
for port, count in counter.items():
    block = (port // 10000) * 10000
    if block not in blocks:
        blocks[block] = 0
    blocks[block] += count

# グラフを作成
x = list(blocks.keys())
y = list(blocks.values())

plt.figure(figsize=(12, 6))
plt.bar(x, y, width=6000, color='skyblue', edgecolor='black')
plt.xlabel('Port Number')
plt.ylabel('Frequency')
plt.title('Port Numbers by Frequency')
plt.xticks(x, [f'{i}-{i+9999}' for i in x], rotation=45)
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
plt.tight_layout()

# PNGファイルに保存
plt.savefig('port_numbers.png')

とすると。。。。

なぜわざわざありえなさそうな30,000以上の値のポート番号での試行が多いのか・・・。

というわけで今日はここまで。

またよろしくお願いします。