./c0de/perl/botsniff.txt

0.
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
212.
213.
214.
215.
216.
217.
218.
219.
220.
221.
222.
223.
224.
225.
226.
227.
228.
229.
230.
231.
232.
233.
234.
235.
236.
237.
238.
239.
240.
241.
242.
243.
244.
245.
246.
247.
248.
249.
250.
251.
252.
253.
254.
255.
256.
257.
258.
259.
260.
261.
262.
263.
264.
# BNSC v0.0.4d by Shadowserver SSD Team
#
# usage: bnsc.pl /path/to/config.ini
 
# sample config.ini (remove all comments):
#
# [target]
# server = "1.1.1.1" ; server ip or hostname
# port = "6667" ; server port
# nick = "foo" ; nick
# user = "foo 0 0 :foo" ; user string
# join = "#bar zoo" ; join string
# password = "secret" ; server password (optional)
#
# [output]
# text = "[MyIRC]" ; text to be prepended to output line
# log = "myirc.log" ; path to log file (append mode)
# alive = "15" ; number of minutes between connection still established messages
#
# [mirror]
# server = "2.2.2.2" ; mirror server ip or hostname
# port = "6667" ; mirror server port
# user = "foo 0 0 :foo: ; user string for mirror
# nick = "me" ; nick
# join = "#mirror" ; join string
# throttle = "2" ; seconds between messages
# password = "secret" ; server password (optional)
#
# Release 'd' fixes a known bug with linefeeds.
 
use strict;
use IO::Socket;
use IO::Poll 0.04 qw(POLLIN POLLOUT POLLERR POLLHUP);
use Errno qw(EWOULDBLOCK);
use Config::Simple;
use constant MAXBUF => 8192;
use Data::Dumper;
$SIG{PIPE} = 'IGNORE';
my ($to_stdout,$to_socket,$to_log,$to_mirror,$from_mirror,$last_to_mirror,
$stdin_done,$sock_done,$log_done, $mirror_done);
 
# load config file
my $ini = shift or die "BNSC v0.0.4d - Shadowserver.org Botnet Hunters\nUsage: $0 /path/to/config.ini\n";
tie my %conf, "Config::Simple", $ini;
 
# connect to target server
my $socket = IO::Socket::INET->new($conf{'target.server'} . ':'
. $conf{'target.port'}) or die $@;
 
# open log file in append mode
open(LOG, ">>" . $conf{'output.log'}) or
die "Can't write to logfile " . $conf{'output.log'};
 
my $poll = IO::Poll->new() or die "Can't create IO::Poll object";
my $alive_tell = $conf{'output.alive'} * 60;
my $alive_last = time();
my $stdout_last = "\n";
$poll->mask(\*STDIN => POLLIN);
$poll->mask($socket => POLLIN);
$socket->blocking(0); #turn off blocking on the socket
STDOUT->blocking(0); #and on stdout
LOG->blocking(0); #and the log
 
# connect to mirror server
my $mirror;
if ($conf{'mirror.server'})
{
$mirror = IO::Socket::INET->new($conf{'mirror.server'} . ':'
. $conf{'mirror.port'}) or die $@;
$mirror->blocking(0);
$poll->mask($mirror => POLLIN);
if ($conf{'mirror.password'})
{
$to_mirror = "PASS " . $conf{'mirror.password'} . "\r\n";
}
$to_mirror .= "NICK " . $conf{'mirror.nick'} . "\r\n" .
"USER " . $conf{'mirror.user'} . "\r\n" .
"JOIN " . $conf{'mirror.join'} . "\r\n";
$conf{'mirror.throttle'} = 1 unless ($conf{'mirror.throttle'} > 1);
}
 
# text to prepend to each output line
my $text = $conf{'output.text'};
$text .= " " if (length($text));
$text .= "(" . $socket->peerhost() . ':' . $conf{'target.port'} . ") -> ";
 
# login to server
if ($conf{'target.password'})
{
$to_socket = "PASS " . $conf{'target.password'} . "\r\n";
}
 
$to_socket .= "NICK " . $conf{'target.nick'} . "\r\n" .
"USER " . $conf{'target.user'} . "\r\n" .
"JOIN " . $conf{'target.join'} . "\r\n" .
"NAMES " . $conf {'target.join'} . "\r\n";
 
 
while ($poll->handles) {
 
$poll->poll;
 
for my $handle ($poll->handles(POLLIN|POLLHUP|POLLERR)) {
if ($handle eq \*STDIN) {
&stdin_done++ unless sysread(STDIN,$to_socket,2048,length $to_socket);
}
 
elsif ($handle eq $socket) {
$sock_done++ unless sysread($socket,$to_stdout,2048,length $to_stdout);
}
elsif ($handle eq $mirror) {
$mirror_done++ unless sysread($mirror,$from_mirror,2048,length $from_mirror);
}
}
 
# handle writers
for my $handle ($poll->handles(POLLOUT|POLLERR))
{
if ($handle eq \*STDOUT)
{
foreach my $line (split /(\n)/, $to_stdout)
{
if ($line =~ /^PING :(.*)$/i)
{
my $id = $1;
my $bytes = syswrite($socket,"PONG :$id\r\n");
unless ($bytes)
{
next if $! == EWOULDBLOCK;
die "write to socket failed: $!";
}
substr($to_stdout,0,length($line)) = '';
$stdout_last = '';
 
if (time() - $alive_last > $alive_tell)
{
 
my $timestamp = gmtime time;
$to_stdout .= "* Connection still established " . $timestamp . " *\n";
$alive_last = time();
}
}
else
{
if ($stdout_last =~ /\n/)
{
syswrite (STDOUT, $text);
$to_log .= $text;
if ($mirror)
{
$conf{'mirror.join'} =~ /([^\s,]+)/;
$to_mirror .= "PRIVMSG $1 :" . $text;
}
}
 
my $bytes = syswrite(STDOUT, $line);
unless ($bytes)
{
next if $! == EWOULDBLOCK;
die "write to stdout failed: $!";
}
 
if ($mirror)
{
my $tmp = $line;
$tmp =~ s/[^[:print:]\n]//g;
if (length $tmp)
{
$to_mirror .= $tmp;
}
}
 
$to_log .= $line;
substr($to_stdout,0,$bytes) = '';
$stdout_last = substr($line, length($line) - 1, 1);
}
}
}
elsif ($handle eq $socket)
{
my $bytes = syswrite($socket,$to_socket);
unless ($bytes)
{
next if $! == EWOULDBLOCK;
die "write to socket failed: $!";
}
substr($to_socket,0,$bytes) = '';
}
elsif ($handle eq $mirror)
{
foreach my $line (split /(\n)/, $from_mirror)
{
if ($line =~ /^PING :(.*)$/i)
{
my $id = $1;
my $bytes = syswrite($mirror,"PONG :$id\r\n");
unless ($bytes)
{
next if $! == EWOULDBLOCK;
die "write to socket failed: $!";
}
}
substr($from_mirror,0,length($line)) = '';
}
 
if (time() - $last_to_mirror > $conf{'mirror.throttle'})
{
if ($to_mirror =~ /^([^\r\n]+[\r\n]+)/)
{
my $line = $1;
my $bytes = syswrite($mirror,$line);
unless ($bytes)
{
next if $! == EWOULDBLOCK;
die "write to socket failed: $!";
}
substr($to_mirror,0,$bytes) = '';
$last_to_mirror = time();
}
else
{
$to_mirror =~ s/^[\r\n]+//;
}
}
}
elsif ($handle eq \*LOG)
{
my $bytes = syswrite(LOG,$to_log);
unless ($bytes)
{
next if $! == EWOULDBLOCK;
die "write to socket failed: $!";
}
substr($to_log,0,$bytes) = '';
}
}
 
} continue {
my ($outmask,$inmask,$sockmask,$logmask,$mirrormask) = (0,0,0,0,0);
 
$outmask = POLLOUT if length $to_stdout > 0;
$inmask = POLLIN unless length $to_socket >= MAXBUF
or ($sock_done || $stdin_done);
 
$sockmask = POLLOUT unless length $to_socket == 0 or $sock_done;
$sockmask |= POLLIN unless length $to_stdout >= MAXBUF or $sock_done;
 
$logmask = POLLOUT unless length $to_log == 0 or $log_done;
 
$poll->mask(\*STDIN => $inmask);
$poll->mask(\*STDOUT => $outmask);
$poll->mask($socket => $sockmask);
$poll->mask(\*LOG => $logmask);
 
if ($conf{'mirror.server'})
{
$mirrormask = POLLOUT unless length $to_mirror == 0 or $mirror_done;
$mirrormask |= POLLIN unless length $from_mirror >= MAXBUF or $mirror_done;
 
$poll->mask($mirror => $mirrormask);
}
 
$socket->shutdown(1) if $stdin_done and !length($to_socket);
} 

Back