Personal tools
You are here: Home メンバー 菊地時夫 講演 関西 Python Workshop 01
Document Actions

Python.jp を Python で守る

by 菊地時夫 last modified 2007-03-19 15:15

関西 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
    

ただし

  • もっと書いてあったのを省略しているので、ASPN を参照のこと
  • zdaemon というのもあるらしい

処理の流れ

  • - 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

Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: