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

4 min read

こんにちは、無能です。表向きとしてVultrのFreeBSDサーバーを公開しているわけですが、SSHでの試行をしようとしている人たちが一体どんな人たちなのか気になったのでみてます。ちなみに、FreeBSDの場合は"security run output"として自ドメイン宛にお届けしてくれてどんな試行があったのかを教えてくださります。 image
こんな感じ。というわけで、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  
  

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

image

デデドン!どうやら米国からが一番多いみたい。

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')  
  

とすると。。。。 image
なぜわざわざありえなさそうな30,000以上の値のポート番号での試行が多いのか・・・。というわけで今日はここまで。またよろしくお願いします。