Syslog event generator with Net::RawIP (perl)

Recently I have been asked to write a Syslog event generator, but not just a normal syslog generator, it had to be able to generate events coming from different hosts.

The normal ‘logger’ command sends Syslog messages using the machine’s IP address, so logger wasn’t very useful. The only thing useful seem to be, to generate my own Syslog packets in which I spoof the source address. After writing this handy little script, I realized that I’ve actually created a monster. A very evil scary kinda looking mean monster! I will show an example for creating Syslog event generator (and later on one for creating SNMP events), but the code can be used for much more. Please keep in mind I post this code just for educational and debugging reasons. If you want to use it for other reasons… well that’s up to you! 🙂

The example shown overhere is a bit more worked out and put in an object oriented structure. It could have also been a simple quick and dirty script.

The module we will use is called Net::RawIP and it will allow us to create our TCP or UDP packets. We just need to figure out how packets should be created in order for the Syslog daemon to accept them.

At first I’ve created a Packet.pm class. This is the main base class, which creates the constructor and defines the class interface.

package Packet;
# $Id: Packet.pm 828 2011-06-28 13:05:03Z insaniac $
use strict; use warnings;
use Net::RawIP;

sub new {
    my($class, $opts) = @_;

    my $self = bless {}, $class || ref $class;
    $self->{rawip} = Net::RawIP->new($opts);

    return $self;
}

sub pkt_payload {
}

sub pkt_size {
}

sub pkt_debug {
}

sub pkt_send {
    my($self, $delay, $amount) = @_;

    $self->pkt_debug();
    $self->{rawip}->send($delay, $amount);
}

1;

Secondly I’ll need an UDP base class, from which the Syslog module will inherit.

package Packet::UDP;
# $Id: UDP.pm 837 2011-06-28 15:05:10Z insaniac $
use strict; use warnings;
use base qw/Packet/;
use POSIX qw{strftime};

#
# src => IP:PORT
# dst => IP:PORT
#
sub new {
    my($class, $src, $dst) = @_;
    my($saddr, $sport) = split /:/, $src;
    my($daddr, $dport) = split /:/, $dst;

    my $self = $class->SUPER::new({
            ip => {
                saddr    => $saddr,
                daddr    => $daddr,
                frag_off => 0,
                tos      => 0,
                id       => $$ + strftime('%s', localtime()),
            },
            udp => {
                source => $sport,
                dest   => $dport,
            },
            });
}

sub pkt_size {
    my($self)= @_;

    my($src, $dst, $data) = $self->{rawip}->get( {
                udp => [qw/source dest data/]
            },
    );
    my $size = length($src) + length($dst) + length($data);

    # set 'check' to 0 to recalculate the UDP checksum
    $self->{rawip}->set( {
            udp => {
                len   => $size,
                check => 0,
            }
    });

    return $size;
}

sub pkt_payload {
}

sub pkt_debug {
    my($self) = @_;

    my(@udp_fields) = qw/source dest len data/;
    my(@ip_fields)  = qw/version ihl tos id frag_off ttl protocolsaddr daddr/;
    my(@udp_data)   = $self->{rawip}->get({ udp => @udp_fields });
    my(@ip_data)    = $self->{rawip}->get({ ip  => @ip_fields });

    print "IP FIELDSn";
    print "=========n";
    print "- $ip_fields[$_]: $ip_data[$_]n" foreach 0 ..  $#ip_data;
    print "UDP FIELDSn";
    print "=========n";
    print "- $udp_fields[$_]: $udp_data[$_]n" foreach 0 ..  $#udp_data;

}

1;

Having all the base classes in place, we can finally focus on creating our Syslog class, a module which will allow us to send Syslog messages, allowing us to change the source IP address.

package Packet::UDP::Syslog;
# $Id: Syslog.pm 838 2011-06-28 15:08:07Z insaniac $
use strict; use warnings;
use base qw/Packet::UDP/;

sub new {
    my ($class, $src, $dst) = @_;
    my $self = $class->SUPER::new("$src:1666", "$dst:514");
    return $self;
}

# TAG               CODE    DESCRIPTION
# =====================================
# kernel            0       kernel messages
# user              1       user-level messages
# mail              2       mail system
# system            3       system daemons
# security          4       security/authorization messages (note 1)
# internal          5       messages generated internally by syslogd
# print             6       line printer subsystem
# news              7       network news subsystem
# uucp              8       UUCP subsystem
# clock             9       clock daemon (note 2)
# security2        10       security/authorization messages (note 1)
# ftp              11       FTP daemon
# ntp              12       NTP subsystem
# logaudit         13       log audit (note 1)
# logalert         14       log alert (note 1)
# clock2           15       clock daemon (note 2)
# local0           16       local use 0  (local0)
# local1           17       local use 1  (local1)
# local2           18       local use 2  (local2)
# local3           19       local use 3  (local3)
# local4           20       local use 4  (local4)
# local5           21       local use 5  (local5)
# local6           22       local use 6  (local6)
# local7           23       local use 7  (local7)
my $i = 0;
my %fac_map = map {$_ => $i++ } qw/kernel user mail system security internal print news uucp clock security2 ftp ntp logaudit logalert clock2 local0 local1 local2 local3 local4 local5 local6 local7/;

# TAG             CODE    DESCRIPTION
# =====================================
# emerg           0       Emergency: system is unusable
# alert           1       Alert: action must be taken immediately
# crit            2       Critical: critical conditions
# err             3       Error: error conditions
# warn            4       Warning: warning conditions
# notice          5       Notice: normal but significant condition
# info            6       Informational: informational messages
# debug           7       Debug: debug-level messages
$i = 0;
my %sev_map = map {$_ => $i++} qw/emerg alert crit err warn notice info debug/;

sub pkt_payload {
    my($self, $facility, $severity, $msg) = @_;

    my ($fac_sev) = ($fac_map{$facility} << 3) + $sev_map{$severity};
    $self->{rawip}->set({
            udp => { 
                data => "<$fac_sev>$msg�" 
                } 
            }
    );

    # call and set the size
    $self->pkt_size();
}

1;

Our OO structure is now ready, all we need is a script which will call all this code.

#!/usr/bin/perl
use strict; use warnings;
use lib './';
use Packet::UDP::Syslog;
use POSIX qw/strftime/;

my @sources = qw{
    166.59.83.200 166.59.83.1 166.59.83.206 192.168.0.99
    192.168.1.99
};

my @messages = (
        {severity => 'info',  payload => 'the planet is going down'},
        {severity => 'err',   payload => 'BOOM BOOM BOOM'},
        {severity => 'emerg', payload => 'The house is on fire!'},
        {severity => 'alert', payload => "Do NOT forget to feed the monkeysnAnd the bearsnand the cows!"},
        {severity => 'debug', payload => 'We should have a look into this'},
);

foreach my $src (@sources){
    my $syslog = Packet::UDP::Syslog->new($src, "192.168.1.3");
    foreach my $msg (@messages) {
        my $datetime = strftime("%b %d %H:%M:%S", localtime());
        $syslog->pkt_payload('local7', $msg->{severity}, $datetime." $src: ".$msg->{payload});
        $syslog->pkt_send(1,1);
    }
}

Running the script:

root@yamamoto:/home/insaniac/dev/Net# perl raw_pkt.pl
IP FIELDS
=========
- version: 4
- ihl: 5
- tos: 0
- id: 1309286460 
- frag_off: 0
- ttl: 64
- protocolsaddr: 3232235779
UDP FIELDS
=========
- source: 1666   
- dest: 514
- len: 68
- data: <190>Jun 28 17:12:13 166.59.83.200: the planet is going down
IP FIELDS
=========
- version: 4
- ihl: 5
- tos: 0
- id: 1309286460 
- frag_off: 0
- ttl: 64
- protocolsaddr: 3232235779
UDP FIELDS
=========
- source: 1666   
- dest: 514
- len: 58
- data: <187>Jun 28 17:12:14 166.59.83.200: BOOM BOOM BOOM
*** snip output ***

And that’s it! If we would check our receiving network interface (on IP address 192.168.1.3) with tcpdump, we would see our packets arriving:

17:13:24.218196 IP (tos 0x0, ttl 64, id 8359, offset 0, flags [none], proto UDP (17), length 96)
    166.59.83.206.1666 > musashi_vpn.syslog: [udp sum ok] SYSLOG, length: 68
        Facility local7 (23), Severity debug (7)
        Msg: Jun 28 17:13:24 166.59.83.206: We should have a look into this�x00
        0x0000:  3c31 3931 3e4a 756e 2032 3820 3137 3a31
        0x0010:  333a 3234 2031 3636 2e35 392e 3833 2e32
        0x0020:  3036 3a20 5765 2073 686f 756c 6420 6861
        0x0030:  7665 2061 206c 6f6f 6b20 696e 746f 2074
        0x0040:  6869 7300
        0x0000:  4500 0060 20a7 0000 4011 9e31 a63b 53ce
        0x0010:  c0a8 0103 0682 0202 004b cc9e 3c31 3931
        0x0020:  3e4a 756e 2032 3820 3137 3a31 333a 3234
        0x0030:  2031 3636 2e35 392e 3833 2e32 3036 3a20
        0x0040:  5765 2073 686f 756c 6420 6861 7665 2061
        0x0050:  206c 6f6f 6b20 696e 746f 2074 6869 7300
17:13:25.219101 IP (tos 0x0, ttl 64, id 8364, offset 0, flags [none], proto UDP (17), length 88)
    192.168.0.99.1666 > musashi_vpn.syslog: [udp sum ok] SYSLOG, length: 60
        Facility local7 (23), Severity info (6)
        Msg: Jun 28 17:13:25 192.168.0.99: the planet is going down�x00
        0x0000:  3c31 3930 3e4a 756e 2032 3820 3137 3a31
        0x0010:  333a 3235 2031 3932 2e31 3638 2e30 2e39
        0x0020:  393a 2074 6865 2070 6c61 6e65 7420 6973
        0x0030:  2067 6f69 6e67 2064 6f77 6e00
        0x0000:  4500 0058 20ac 0000 4011 d732 c0a8 0063
        0x0010:  c0a8 0103 0682 0202 0043 70bf 3c31 3930
        0x0020:  3e4a 756e 2032 3820 3137 3a31 333a 3235
        0x0030:  2031 3932 2e31 3638 2e30 2e39 393a 2074
        0x0040:  6865 2070 6c61 6e65 7420 6973 2067 6f69
        0x0050:  6e67 2064 6f77 6e00

Comments

Syslog event generator with Net::RawIP (perl) — 10 Comments

  1. Hi Johnny

    wow, this is really cool. I know very little about perl and scripting, but I am faced with a problem whereby I am recieving a sh#tload of syslog messages from Cisco devices. They are all received by a CiscoWorks LMS server , they are then filtered and then forwarded to an another syslog collecting server. Thing is, that this collecting server “sees” all these messages coming from one device – which is the LMS server. I hope to use your script and modify this forwarding script so that it will show the original devices’ IP address.
    Cheers
    P

  2. This is great stuff, however… when I attempt to run the script as presented above, I receive the following error:

    dev@ubuntu:~/Documents/syslog_generator$ ./syslog_gen.pl
    Odd number of elements in anonymous hash at UDP.pm line 59.
    Can't use string ("source") as an ARRAY ref while "strict refs" in use at /usr/local/lib/perl/5.14.2/Net/RawIP.pm line 564.

    I’ve tried turning off strict, but still couldn’t get it working.

    Line 59 in UDP.pm:

    my(@udp_data) = $self->{rawip}->get({ udp => @udp_fields });

    Line 564 in RawIP.pm:

    map { ${$$hash{"$self->{proto}h"}}{$_} = '$' } @{$hash->{$self->{proto}}};

    Perl 5.14.2.

    Any ideas?

    Thanks!

    /b

    PS – running all with elevated permissions, just in case you need root to access Raw IP.

  3. Yep \@udp_fields and \@ip_fields should be in UDP.pm

    However, what on earth is � in Syslog.pm? Without it, it doesnt send. With it you get \xEF\xBF on the end of the message?

    –Chris

  4. Actually, it does work it just seems to be acting as padding, a space does the same job without the strange hex codes.

  5. Hi Johnny,

    I want to change my source IP from where syslog packet is being send e.g. ip address of my machine is 192.168.0.10 which is getting identified as ‘log source’ ip, instead of that I want 10.10.0.12 as a source IP, how to do that, please help.

    Thanks
    Sunil

  6. I’m not quite clear of the filenames and directory structure that the 2nd & 3rd pieces of code should be saved in. Can you elaborate on that, or give more specifics?

    • Packet.pm goes under Packet, and in there have a directory UDP in which udp.pm goes, and in there a directory called syslog in which you put Syslog.pm. Easiest thing to do is have /Packet in the same directory as your perl script unless you end up better packaging your code

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.