如何解释审核日志的“saddr”字段?

如何解释审核日志的“saddr”字段?

我正在尝试记录 的参数connect,因此我添加了一条带有 的规则auditctl

现在在audit.log中我得到这样的行:

类型=SOCKADDR msg=audit(1385638181.866:89758729):saddr=十六进制字符串

那么我应该如何解释十六进制字符串中的目标地址(我不确定该十六进制字符串中存储了什么)?

答案1

我找到了这个 Perl 脚本,解析审计日志.pl,它显示了一个可以解析该字符串的函数,如下所示:

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";
    }
}

这个脚本是这个的一部分CERN 网站上的 TWiki 页面, 在下面Linux支持。该页面标题为:IDSNet连接记录器包含 2 个感兴趣的文件。我上面提到的脚本之一,解析审计日志.pl,另一个是样本审计日志文件。

运行脚本

如果您下载这两个文件,您会发现这就是您所询问的内容。

例子

$ ./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

提取解析器逻辑

我们可以对上面的内容进行压缩,使其只是一个saddr解析器。这是我的精简版本。

$ 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);

Saddr 解析器脚本的示例运行

我们可以像这样运行它:

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

然后,您可以使用这样的命令来解析上述文件saddr=..中的所有行:audit.log

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

上面的代码被拼凑在一起,因此它不能处理 family=1 类型的saddr.您必须深入研究,但这为您提供了如何处理所有这些问题的一个粗略的开始。

输出示例

$ 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 的打包/解包函数

一旦您了解了它们的工作原理,这些功能就会非常强大。如果您以前从未使用过它们,那么我会看一下教程,佩尔帕克图特

这些函数背后的想法是,它们接收数据并使用模板返回该数据,并使用模板作为数据组织结构。

这里又是一个简单的 Perl 脚本,它显示了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]);

产生这个:

$ ./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

在这里,我们获取其中包含的数据$saddr并调用unpack()告诉函数一次获取 2 个字节的数据 (A2)。这样做 10 次。前 4 个A2块实际上每个块只有 2 个字符,存储在变量中:$f1, $f2, $p1, $p2。剩余的字符存储在数组中@addr

答案2

使用该-i选项ausearch

-i,--解释
将数字实体解释为文本。例如,uid 转换为帐户名。转换是使用运行搜索的计算机的当前资源完成的。如果您已重命名帐户,或者计算机上没有相同的帐户,则可能会得到误导性的结果。

这也解码了条目saddr中的 s type=SOCKADDR

答案3

从 2.6 开始,有一个新的日志记录选项auditd可以启用丰富的日志。

在 /etc/audit/auditd.conf 中将 log_format 更改为ENRICHED

log_format = ENRICHED

这将为各个字段输出具有有意义名称的字段,包括用户和组以及系统调用。资本化的领域是丰富的领域。SOCKADDR事件具有解码后的套接字 IP、端口或文件名。

例子:

类型=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 u编号= 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 =“根” SGID=“根” FSGID=“根”

类型 = SOCKADDR msg = 审核(1583352273.046:333334):saddr = 0200028F000000000000000000000000SADDR = {fam = inet laddr = 0.0.0.0 lport = 655 }

答案4

我使用以下方法在 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
        }
    }
}

相关内容