6

I'm trying to log the parameter of connect, so I added one rule with auditctl.

Now in audit.log I get lines like this:

type=SOCKADDR msg=audit(1385638181.866:89758729): saddr=hex string

So how should I interpret the target address from the hex string (I'm not sure what is stored in that hex string)?

3
  • 1
    If the string doesn't change then e.g: echo "type=SOCKADDR msg=audit(1385638181.866:89758729): saddr=hex string" | awk -F= '/saddr/{print $NF}' would get the desired result. Commented Nov 28, 2013 at 11:41
  • @val0x00ff Then how do I know the address it connects to ..?
    – daisy
    Commented Nov 28, 2013 at 14:29
  • @warl0ck - can you show what that string looks like?
    – slm
    Commented Nov 28, 2013 at 19:47

5 Answers 5

10

I found this Perl script, parse-audit-log.pl, that shows a function that can parse that string as follows:

sub parse_saddr
{
    my $sockfd = $_[0];
    my $saddr = $_[1];
    # 0 - sys_bind(), 1 - sys_connect(), 2 - sys_accept()
    my $action = $_[2];

    ($f1, $f2, $p1, $p2, @addr) = unpack("A2A2A2A2A2A2A2A2", $saddr);
    $family = hex2dec($f1) + 256 * hex2dec($f2);
    $port = 256 * hex2dec($p1) + hex2dec($p2);
    $ip1 = hex2dec($addr[0]);
    $ip2 = hex2dec($addr[1]);
    $ip3 = hex2dec($addr[2]);
    $ip4 = hex2dec($addr[3]);
    #print "$saddr\n";
    if ($family eq 2) { #&& $ip1 ne 0) {
        my $dst_addr = "$ip1.$ip2.$ip3.$ip4:$port";
#       print "family=$family $dst_addr\n\n";
        # todo: avoid code duplication
        if ($action eq 0) {
            $sockfd_hash{ $sockfd } = $dst_addr;
        } elsif ($action eq 1) {
            my $src_addr;
            if (exists $sockfd_hash{ $sockfd }) {
                $src_addr = $sockfd_hash{ $sockfd };
            } else {
                $src_addr = "x.x.x.x:x";
            }
            print "$src_addr -> $dst_addr\n";
        } elsif ($action eq 2) {
            my $src_addr;
            if (exists $sockfd_hash{ $sockfd }) {
                $src_addr = $sockfd_hash{ $sockfd };
            } else {
                $src_addr = "x.x.x.x:x";
            }
            print "$dst_addr <- $src_addr\n";
        } else {
            print "unknown action\n";
        }
    } elsif ($family eq 1) {
        $tmp1 = 0;
        ($tmp1, $tmp2) = unpack("A4A*", $saddr);
        my $file = pack("H*", $tmp2);
#       print "family=$family file=$file\n";
    } else {
#       print "$saddr\n";
    }
}

This script was part of this TWiki page on the CERN website, under LinuxSupport. The page titled: IDSNetConnectionLogger contains 2 files of interest. One the script I mentioned above, parse-audit-log.pl, and the other is a sample audit.log file.

Running the script

If you download those 2 files you'll notice this is what you're asking about.

Examples

$ ./parse-audit-log.pl -l audit.log 
x.x.x.x:x -> 0.0.0.0:22
x.x.x.x:x -> 137.138.32.52:22
137.138.32.52:22 <- x.x.x.x:x
x.x.x.x:x -> 0.0.0.0:22
x.x.x.x:x -> 137.138.32.52:0
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.128.158:88
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.128.148:750
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.128.158:88
x.x.x.x:x -> 137.138.32.52:0
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.16.5:53
x.x.x.x:x -> 137.138.128.158:88
x.x.x.x:x -> 127.0.0.1:6010

Pulling out the parser logic

We can kind of condense the above so that it's a saddr parser only. Here's my stripped down version.

$ cat parse_saddr.pl 
#!/usr/bin/perl -w

# Getopt::Std module from the perl package
use Getopt::Std;

my %Options;
getopt('s', \%Options);

if (defined($Options{'s'})) {
    $saddr = $Options{'s'};
} else {
    print "saddr not given\n";
    exit(-1);
}

sub hex2dec($) { return hex $_[0] }

sub parse_saddr
{
    my $saddr = $_[0];

    ($f1, $f2, $p1, $p2, @addr) = unpack("A2A2A2A2A2A2A2A2", $saddr);
    $family = hex2dec($f1) + 256 * hex2dec($f2);
    $port = 256 * hex2dec($p1) + hex2dec($p2);
    $ip1 = hex2dec($addr[0]);
    $ip2 = hex2dec($addr[1]);
    $ip3 = hex2dec($addr[2]);
    $ip4 = hex2dec($addr[3]);
    #print "$saddr\n";
    if ($family eq 2) { #&& $ip1 ne 0) {
        my $dst_addr = "$ip1.$ip2.$ip3.$ip4:$port";
        print "family=$family $dst_addr\n\n";
    } elsif ($family eq 1) {
        $tmp1 = 0;
        ($tmp1, $tmp2) = unpack("A4A*", $saddr);
        my $file = pack("H*", $tmp2);
        print "family=$family file=$file\n";
    } else {
        print "$saddr\n";
    }
}

&parse_saddr($saddr);

Sample run of saddr parser scrip

We can run it like so:

$ ./parse_saddr.pl -s 02000035898A1005000000000000000030BED20858D83A0010000000
family=2 137.138.16.5:53

You could then use a command like this to parse all the saddr=.. lines from the audit.log file mentioned above:

$ for i in $(grep saddr audit.log | cut -d"=" -f4);do echo $i; \
    ./parse_saddr.pl -s $i;done | less

The above is hacked together so it doesn't handle the family=1 types of saddr. You'd have to dig in more, but this gives you a rough start as to how to deal with all this.

Example output

$ for i in $(grep saddr audit.log | cut -d"=" -f4);do echo $i; \
    ./parse_saddr.pl -s $i;done | less
...
01002F6465762F6C6F67000000000000
family=1 file=/dev/log^@^@^@^@^@^@
...

02000035898A10050000000000000000726E2E6368009A0900000000
family=2 137.138.16.5:53

...
02000058898A809E0000000000000000
family=2 137.138.128.158:88

...
020002EE898A80940000000000000000
family=2 137.138.128.148:750

...
0200177A7F0000010000000000000000
family=2 127.0.0.1:6010

...

Perl's pack/unpack functions

These are very powerful functions once you understand how they work. If you've never used them before then I'd take a look at the tutorial, perlpacktut.

The idea behind these functions is that they take data in and use a template to return that data using the the template as a structure of how the data should be organized.

Again here's a simple Perl script that shows the unpacking of the saddr.

$ cat unpack.pl
#!/usr/bin/perl

$saddr = "02000035898A1005000000000000000030BED20858D83A0010000000";
($f1, $f2, $p1, $p2, @addr) = unpack("A2A2A2A2A2A2A2A2", $saddr);

printf "org string: $saddr\n";
printf "org values==> f1: %s f2: %s p1: %s p2: %s addr: %s\n",
    $f1,$f2,$p1,$p2,join("",@addr);
printf "new values==> f1: %2s f2: %2s p1: %2s p2: %2s addr: %s.%s.%s.%s\n\n", 
    hex($f1),hex($f2),hex($p1),hex($p2),hex($addr[0]),hex($addr[1]),hex($addr[2]),hex($addr[3]);

Which produces this:

$ ./unpack.pl 
org string: 02000035898A1005000000000000000030BED20858D83A0010000000
org values==> f1: 02 f2: 00 p1: 00 p2: 35 addr: 898A1005
new values==> f1:  2 f2:  0 p1:  0 p2: 53 addr: 137.138.16.5

Here we're taking the data that's contained in $saddr and calling unpack() telling the function to take the data 2 bytes at a time (A2). Do this 10 times. The first 4 A2 blocks, which are really just 2 characters apiece, are stored in the variables: $f1, $f2, $p1, $p2. The remaining characters are stored in the array @addr.

5

Use the -i option to ausearch

-i, --interpret
Interpret numeric entities into text. For example, uid is converted to account name. The conversion is done using the current resources of the machine where the search is being run. If you have renamed the accounts, or don't have the same accounts on your machine, you could get misleading results.

That also decodes the saddrs in type=SOCKADDR entries.

1

There is a new logging option since auditd 2.6 to enabled enriched logs.

In /etc/audit/auditd.conf change log_format to ENRICHED:

log_format = ENRICHED

This will output fields with meaningful names for various fields including the users and groups and system calls. The capitalized fields are the enriched fields. SOCKADDR events have the decoded IP, port or filename of the socket.

Examples:

type=SYSCALL msg=audit(1583352267.747:333333): arch=c000003e syscall=59 success=yes exit=0 a0=19eed30 a1=19e7360 a2=19e6ff0 a3=7ffce315f1e0 items=2 ppid=28317 pid=30555 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1681 comm="tail" exe="/usr/bin/tail" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="EXECVE" ARCH=x86_64 SYSCALL=execve AUID="root" UID="root" GID="root" EUID="root" SUID="root" FSUID="root" EGID="root" SGID="root" FSGID="root"

type=SOCKADDR msg=audit(1583352273.046:333334): saddr=0200028F000000000000000000000000SADDR={ fam=inet laddr=0.0.0.0 lport=655 }

1
0

In case anyone found this and was looking for a python saddr parsing function...

def parse_saddr(saddr):
    if len(saddr) >= 16 and saddr.startswith('0200'):
        port, ipaddr = struct.unpack('>HL', binascii.unhexlify(saddr[4:16]))
        ip_addr = socket.inet_ntoa(struct.pack('>L', ipaddr))

        print('{}:{}'.format(ip_addr, str(port)))
0

I've used the following to achieve this in powershell

function parseSocketAddr {
    [CmdletBinding()]
    param(
        [parameter(Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [string]$Saddr
    )
    begin {
        $re = [regex]"^(?<socket>(?:02|0A)00)(?<port>[a-fA-F0-9]{4})(?<ip>[a-fA-F0-9]{8})(?<remainder>[a-fA-F0-9]+)$"
    }
    process {
        $socket, $port, $ip, $remainder = $re.Match($saddr).groups.Where({$_.Name -ne '0'}).value
        $ipRev = ($ip -split "(\w{2})").Where({$_ -ne ""})[4..0] -join ""
        return [PSCustomObject]@{
            type = $socket
            IPAddress = ([IPAddress][convert]::ToInt64($ip,16)).IPAddressToString
            Port = [convert]::ToInt32($port,16)
            Remainder = $remainder
        }
    }
}

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .