Python.jp を Python で守る
関西 Python Workshop 01 (2007.3.16)
自己紹介
- Python 的には Mailman やってます
- OSS 的には Apache ユーザ会やってます
- Web 的には 気象情報頁 やってます
- 本職的には 理学部(数理情報科|応用理)学科情報科学コースで Python 教えてます
- 「菊地時夫」といいます。
高知です
Python.jp に関わるようになって
- Python.jp の Mailman 管理を引き継いだのですが
- 近頃 ssh brute force attack 多いよね
SBFA 放題だったころの security report
Sep 12 16:01:44 parrot sshd[5539]: Failed password for root from 84.16.234.101 port 38928 ssh2 Sep 12 16:01:47 parrot sshd[5542]: Failed password for root from 84.16.234.101 port 38994 ssh2 ... ... Sep 12 22:04:56 parrot sshd[42080]: Failed password for root from 84.16.234.101 port 47232 ssh2 Sep 12 22:05:50 parrot sshd[42187]: Failed password for root from 84.16.234.101 port 48931 ssh2
- 2119 回
やわなパスワードではないと信じているが
- そもそも default が PermitRootLogin no だし
- でも、延々と attack かけられるのも面白くない
- そういうの止めるツールあるやろ
こんなツール
- maxlogins.pl ... syslog を Perl script にパイプで渡す。/etc/hosts.allow (tcpwrappers)を利用
- sshdfilter ... sshd を wrap. iptables で遮断
- Linux なら iptables に「短時間に繰り返しアクセスがあったら遮断」という機能もあるらしい
Python.jp は、ちょっと古めなので
- 便利なツールは無い(ことにする)
- Python.jp なんだから Python 使ってなんとかならない?
- 遮断だけなら ipfw でできるし
まずは、だるまさんが cron だ
- cron で「最近の」auth.log を読む
- 規定回数以上
Failed password
|Illegal user
があったら - os.system で ipfw を起動
cron 間に合わない
- 最初 10分ごと
- 10 分の間に切り上げて帰っていく
- 面白くない(帰っていったんだからいいじゃない?)
- 1分ごと ... cron 忙しい
要は
- "tail -f" 相当のことをやる script を daemon化すればよいのだが
Python "tail -f"
で、ぐぐる- ASPN がヒット
Python daemonize
で、ぐぐる- ASPN がヒット
tail -f 相当のこと
while 1: where = file.tell() line = file.readline() if not line: time.sleep(1) file.seek(where) else: print line, # already has newline
ただし
- logrotate で新しいファイルに移ったりするので、そのあたりを(かなりたくさん)追加
daemon化
def createDaemon(): try: pid = os.fork() except OSError, e: raise Exception, '%s [%d]' % (e.strerror, e.errno) if (pid == 0): os.setsid() os.chdir(WORKDIR) else: os._exit(0) # Exit parent return 0
ただし
処理の流れ
- daemon 化する - t = Tail(AUTHLOG) ... ここだけ objective - 無限ループ - t から 1行読んで SBFA パターンなら - ip アドレスのカウントアップ - カウントが閾値を超えたら - ipfw で遮断
網にかかったよ!
- 一応、ログをとる
- 時刻 ユーザ名 attack元IPaddress
- ipfw のメッセージ
Mar 5 00:52:40 test 218.1.73.216 Mar 5 00:52:43 test 218.1.73.216 Mar 5 00:52:47 test 218.1.73.216 29996 deny log tcp from 218.1.73.216 to any 22 in setup
どこから来たのかな?
- host コマンド相当 ... socket.gethostbyaddr(addr)[0]
- whois ... output 長い
- 国だけでいい
- whois xxx.xxx.xxx | grep -i '^country:' | tail -1
- 例外: ブラジル, 合衆国一部 etc
あら、日本からも
- JPCERT に報告する?
- インシデント報告format
3-1 アクセス元に関する情報をご記入下さい。 IP アドレス、ホスト名など: 3-3 インシデントが発生したシステムについてご記入下さい。 発生日時: 該当するログ情報をメールに添付するかこちらに挿入して下さい。
インシデント報告
- 必要項目は3箇所だけ
- ディクショナリ挿入で作成
- メールでまずは自分へ
- チェックして JPCERT へ転送
日本語メールを送るコード
m = email.Message.Message() m['From'] = sender m['To'] = ', '.join(recips) m['Subject'] = email.Header.Header( unicode(title, 'utf-8').encode('iso-2022-jp'), charset='iso-2022-jp', header_name='Subject') m.set_payload(unicode(body, 'utf-8').encode('iso-2022-jp'), charset='iso-2022-jp') s = smtplib.SMTP(server) s.sendmail(sender, recips, m.as_string())
報告のその後
- 2/23 -> 2/26 「特定アカウントがクラッキングを受けたことにより、不正なプログ ラムを設置されておりました。弊社にて緊急ログインし、該当アカウ ントおよび該当プロセスを停止、現在では不正アクセス等はございま せんので、ご報告いたします。」
- 2/28 -> 3/8 「該当ユーザーの特定調査を実施しております。」
- 3/5 -> 3/14 「人為的なタイプミスであると判別できました。万が一、次に発生した 場合は、社内にてしかるべき処置を取りますので、早々にご連絡ください。」
ソース公開?
- うぅーん
- 汚いし、コメント無いし
- そもそも、あまりお勧めできる方法でもないし
- JP-CERT にレポート殺到するようになってもかわいそうだし
- どうしてもソース見たい場合は、
- 「PyJUG サーバ管理」に立候補して、菊地を蹴落とす
お粗末でした。
- m(__)m