ハニーポットを作ってみる

「ハニーポット」というのは、クラッカーに対して脆弱に見せかけて好き勝手やらせ、ログをとったりして笑う、ではなく資料にしたり、悪事の証拠にしたりするためのものである。狭義ではそういった鯖プログラムを指すと考えてもよいだろう。

こういったもんはきちっとしたものも多少は作られているが、あまりない。まあ、あったってそうそう使われないのだろう。本気でクラッカーと戦おうという場合以外は(それが仕事なら別だ)。それに既成のものはマジメすぎて面白くない。だいたいネットワークセキュリティーについて考えるなんてのは大変疲れるので、マジメ1本でやってたら気が狂う。そこで、楽しいものを作ってみよー!ということなのだ。

というわけで作ってみた。自分は C言語では多少まともな鯖など作ったことがあるが、大変だったので、今回は Perl で書いてみた。 Perl は慣れているつもりだったし、ソケットプログラミングはなんとなく分かるから素早く書けるだろう、と思っていたのだが、素早く書けなかった(笑)。そこで、手元にあった Perl の本を参考にほとんど丸写しした(泣)。「Perl for expert (秀和システム)」というこの本は安いのに内容が充実していて素晴らしいので、持っていて損はない。しかし、Perl だと楽に書けるもんだ。


■ 概要 ■

  • デフォルトではログインまでは telnetd のような動き、表示をする鯖。
  • パスワードなしでログインできたように見せかける。
  • 最終ログインもいかにもそれらしく表示する
  • コマンドは何を打とうが、決まった返事(デフォルトは I love you.)と返すだけ。
  • exit を打つか、コマンドを規定数(デフォルトは3)より多く打つと、鯖がコネクションを切る。そのときに捨てゼリフを吐く(デフォルトは Thieves start as liars.:-( (うそつきは泥棒の始まり))。
  • 最終ログインも以下にもそれらしく表示する
  • ログインしたクライアントの打ったコマンドをログに記録する。
  • telnetd 風以外の動きを簡単に自作できるように配慮してある。scenarioX の部分に記述を追加し、変数 $scn の値を変えればよい。
■ 遊び方 ■
変数 $log の値を変更して、ログファイルの出力先を決めたら、
keronet_server.pl を置いたディレクトリに入って

hoge.fuga$ perl ./keronet_server.pl &

でバックグラウンドで起動します(フォアでもいいけど)。
他の端末から telnet でポートを指定してアクセスします。
keronet_server の走っているホストを hoge とすると

uhyo.fuga$ telnet
> open hoge 54321
> ...あとは telnet ログインの要領

で、遊び方が分かったら、ソースをイジって(# as you like !! や # scenario2 の部分など)自分好みの動きを記述して、
$port_server 変数を 23(telnet) とか、22(ssh) とかにして走らせましょう。
そういった特権ポートで動かすには root になってください。それから当然そのポートを使っているほかのデーモンを止めてからです。
そのうちバカなクラッカーが引っかかってきますぜ、ひひ。
ログを見て大笑いしましょう。

というのはウソです。

ぜひ「自分だけ」で遊んでください。下手なことをすると恐ろしい逆襲に遭うでしょう。

■ ご注意 ■
  • このスクリプトの実行に関するいかなるトラブルにも当方は責任を負いませんので、ご注意下さい。
  • あくまで遊びのレベルで作っています。細かい所は全く実装していませんので、 使い方によっては不具合が出る可能性があります。実用に用いるのは自殺行為です。 現に作者はこの鯖を攻撃する方法をいくつか知っています。
  • 遊びのレベルでは充分問題なく動きます。カスタマイズすればさらに面白く、 しっかりしたものが作れます。個人の責任で存分にお楽しみ下さい。
  • 拡張、バグ、アイデアなど掲示板等で意見交換できるのも楽しいのですが。
  • ###################################################################
    
    #!/usr/bin/perl
    #
    # keronet_server.pl
    # shimakero
    # 2001.11.06
    #
    
    use sigtrap;
    use Socket;
    
    my %CHILD;
    
    #
    # dynamic configuration
    #
    my $proto           = 'tcp';
    my $port_server     = 54321;
    my $welcome_msg     = "Ultra Linux 2.4 (Flog)\n"
                         ."Kernel 2.4.13-4649 on an i686\n";
    my $log             = "$ENV{HOME}/dvlp/perl/keronet_server.log";
    my $scn             = 1;
    my $waiting_clients = 3;
    
    # as you like !!
    if($scn == 1){
        $span_lastlogin = 80000;
        $host_lastlogin = 'smtp2';
        $login_msg = "\n\n>> Wellcome to kero's Honey Pot <<\n\n";
        $repeated_msg = "I love you.";
        $quit_msg = "Thieves start as liars. :-( ";
        $prompt = 'honeypot.kero$';
        $command_max = 5;
    }
    
    &main;
    
    #
    # handler
    #
    sub sigchld()
    {
        my $pid = wait;
        $SIG{CHLD} = \&sigchld;
        print "CLOSED{$pid} "
    	, scalar localtime(), ' : '
    	, time - $CHILD{$pid} , "sec.\n";
        delete $CHILD{$pid};
    }
    
    sub main
    {
        my $protonum;
        {# get protocol number
    	 $protonum = getprotobyname($proto)
    	     or die("ERROR: getprotobyname(): $!");
        }
        {# create socket
    	 socket(SH
    		, Socket::AF_INET
    		, Socket::SOCK_STREAM
    		, $protonum
    		)
    	     or die("ERROR: socket(): $!");
         }
        {# set socket options
    	 setsockopt(SH
    		    , Socket::SOL_SOCKET
    		    , Socket::SO_REUSEADDR
    		    , pack('l', 1)
    		    )
    	     or die("ERROR: setsockopt(): $!");
        }
        {# bind
    	 bind(SH
    	      , sockaddr_in($port_server, Socket::INADDR_ANY)
    	      )
    	     or die("ERROR: bind(): $!");
    
         }
        {# listen
    	 listen(SH, $waiting_clients) or die("ERROR: listen(): $!");
    
        }
        {# handler SIGCHLD
    	 $SIG{CHLD} = \&sigchld;
        }
        {# accept...
    	 my($peeraddr, $now, $pid, $born);
    	 my($port_client, $inetaddr, $peername);
    	 while($peeraddr = accept(CH, SH)){
    	     {# get client information
    		  ($port_client, $inetaddr) = Socket::sockaddr_in($peeraddr);
    		  $peername = gethostbyaddr($inetaddr, Socket::AF_INET);
    		  $inetaddr = Socket::inet_ntoa($inetaddr);
    	      }
    #
    # create child process
    #
    	     my $pid = fork;
    	     if($pid){
    #
    # primary 
    #
    		 print "ACCEPT[$pid] "
    		     , scalar localtime() , ' : '
    			 , $peername || $inetaddr
    			     , ":$port_client\n";
    		 $CHILD{$pid} = time;
    	     }elsif(defined $pid){
    #
    # child
    #
    		 open(STDIN, '<&CH')
    		     or die("ERROR: open(STDIN, '<&CH'): $!");
    		 open(STDOUT, '>&CH')
    		     or die("ERROR: open(STDOUT, '>&CH'): $!");
    		 open(STDERR, '>&CH')
    		     or die("ERROR: open(STDERR, '>&CH'): $!");
    		 select((select(STDIN), $|=1)[0]);
    		 select((select(STDOUT), $|=1)[0]);
    		 select((select(STDERR), $|=1)[0]);
    
    #
    # like telnetd
    #
    		 print "\n"
    		     , "$welcome_msg\n"
    			 ,"login: ";
    
    		 my $lop = 0;
    		 my $acount;
    		 my $command;
    
    	       INPUT_LOOP:
    		 while(<>){
    		     $_ =~ s/\x0a//;
    		     $_ =~ s/\x0d//;
    		     if($lop == 0){
    			 $acount = $_;
    		     }
    		     $command = $_;
    		     local(*LOG);
    		     {# error abort
    			  if(open (LOG, ">>$log")){
    			      flock(LOG, 2);
    			      my $temp
    				  = $peername eq '' ? 'unknown' : $peername;
    			      if($lop == 0){
    				  print LOG substr(scalar localtime(), 4)
    				      ,"$pid{$$} $temp "
    					  , "$inetaddr:$port_client "
    					      , "$acount [ LOGIN ]\n";
    			      }else{
    				  print LOG substr(scalar localtime(), 4)
    				      ,"$pid{$$} $temp "
    					  , "$inetaddr:$port_client "
    					      , "$acount $command\n";
    			      }
    			      flock(LOG, 8);
    			      close(LOG);
    			  }
    		      }
    
    		   SWITCH:
    		     {
    			 $scn == 1 and do{
    #
    # scenario 1
    # accept any password
    # But, The server always says "I love you."
    # finally, says "Thieves start as liars"
    # 
    			     if($lop == 0){
    #
    # ignore password(pretends as not being set a password)
    #				 print "Password: ";
    				 print "Last login: "
    				 , scalar localtime(time()
    						    - $span_lastlogin)
    				     , " from $host_lastlogin\n"
    					 , "$login_msg\n"
    					     , "$prompt ";
    			     }elsif($command eq 'exit' or $lop > $command_max){
    				 print "\n\n$quit_msg\n\n";
    				 last INPUT_LOOP;
    			     }else{
    				 print "$repeated_msg\n"
    				     , "$prompt ";
    			     }
    			     last SWITCH;
    			 };
    #			 $scn == 2 and do{
    #
    # scenario 2 .....
    # ( place holder )
    #			     last SWITCH;
    #			 };
    			 print "\nbye\n\n";
    			 last INPUT_LOOP;
    		     }
    		     $lop++;
    		 }
    		 exit 0;
    	     }
    	 }
    	 die("ERROR: accept(): $!");
        }
    }
    


トップページ