Skip to content
This repository was archived by the owner on Apr 3, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions lib/audit_log_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@
class AuditLogParser
class Error < StandardError; end

def self.parse(src, flatten: false)

# @param unhex_keys [Array<String>] with * meaning all
def self.parse(src, flatten: false, unhex: false, unhex_keys: ['*'], unhex_min_length: 8)
# audit always uses uppercase hex digits. Fortunately addresses are generally lower-case.
src.each_line.map do |line|
parse_line(line, flatten: flatten)
parse_line(line, flatten: flatten, unhex: unhex, unhex_keys: unhex_keys, unhex_min_length: unhex_min_length)
end
end

def self.parse_line(line, flatten: false)
def self.parse_line(line, flatten: false, unhex: false, unhex_keys: ['*'], unhex_min_length: 8)
unhex_re = /^[A-F0-9]{#{unhex_min_length},}$/
line = line.strip

if line !~ /type=\w+ msg=audit\([\d.:]+\): */
Expand All @@ -22,10 +26,27 @@ def self.parse_line(line, flatten: false)
header.sub!(/: *\z/, '')
header = parse_header(header)
body = parse_body(body.strip)

if unhex
unhex_keys = unhex_keys.include?('*') ? :all : unhex_keys
unhex_hash!(header, unhex_keys, unhex_re)
unhex_hash!(body, unhex_keys, unhex_re)
end

result = {'header' => header, 'body' => body}
flatten ? flatten_hash(result) : result
end

def self.unhex_hash!(hash, unhex_keys, unhex_re)
hash.each do |key, value|
if value.kind_of?(Hash)
unhex_hash!(value, unhex_keys, unhex_re)
elsif (unhex_keys == :all || unhex_keys.include?(key)) && (value.length % 2) == 0 && value =~ unhex_re
value[0..-1] = [value].pack("H*")
end
end
end

def self.parse_header(header)
result = {}

Expand Down
62 changes: 61 additions & 1 deletion spec/audit_log_parser_spec.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
RSpec.describe AuditLogParser do
let(:audit_log) do
{
%q{type=SYSCALL msg=audit(1364481363.243:24287): arch=c000003e syscall=2 success=no exit=-13 a0=7fffd19c5592 a1=0 a2=7fffd19c4b50 a3=a items=1 ppid=2686 pid=3538 auid=500 uid=500 gid=500 euid=500 suid=500 fsuid=500 egid=500 sgid=500 fsgid=500 tty=pts0 ses=1 comm="cat" exe="/bin/cat" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="sshd_config"} =>
%q{type=SYSCALL msg=audit(1364481363.243:24287): arch=c000003e syscall=2 success=no exit=-13 a0=7fffd19c5592 a1=0 a2=7fffd19c4b50 a3=a items=1 ppid=2686 pid=3538 auid=500 uid=500 gid=500 euid=500 suid=500 fsuid=500 egid=500 sgid=500 fsgid=500 tty=pts0 ses=1 comm="cat" exe="/bin/cat" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key="sshd_config"} =>
{"header"=>{"type"=>"SYSCALL", "msg"=>"audit(1364481363.243:24287)"},
"body"=>
{"arch"=>"c000003e",
Expand Down Expand Up @@ -95,6 +95,12 @@
expect(AuditLogParser.parse(lines)).to eq audit_log.values
end

specify '#parse unhex does not affect unhexable' do
lines = audit_log.keys.join("\n")
expect(AuditLogParser.parse(lines, unhex: true)).to eq audit_log.values
end


context 'when flatten' do
specify '#parse can be parsed flatly' do
lines = audit_log.keys.join("\n")
Expand All @@ -103,6 +109,60 @@
end
end

context 'when unhex log' do
let(:unhex_audit_log) do
{
%q{type=PROCTITLE msg=audit(1585655101.154:27786): proctitle=2F62696E2F7368002D6300636F6D6D616E64202D762064656269616E2D736131203E202F6465762F6E756C6C2026262064656269616E2D73613120312031} =>
{"header"=>{"type"=>"PROCTITLE", "msg"=>"audit(1585655101.154:27786)"},
"body"=>
{
"proctitle" => "/bin/sh\u0000-c\u0000command -v debian-sa1 > /dev/null && debian-sa1 1 1",
}
}
}
end

let(:unhex_specific_audit_log) do
{
%q{type=PROCTITLE msg=audit(1585655101.154:27786): proctitle=2F62696E2F7368002D6300636F6D6D616E64202D762064656269616E2D736131203E202F6465762F6E756C6C2026262064656269616E2D73613120312031 proctitle2=2F62696E2F7368002D6300636F6D6D616E64202D762064656269616E2D736131203E202F6465762F6E756C6C2026262064656269616E2D73613120312031 } =>
{"header"=>{"type"=>"PROCTITLE", "msg"=>"audit(1585655101.154:27786)"},
"body"=>
{
"proctitle" => "2F62696E2F7368002D6300636F6D6D616E64202D762064656269616E2D736131203E202F6465762F6E756C6C2026262064656269616E2D73613120312031",
"proctitle2" => "/bin/sh\u0000-c\u0000command -v debian-sa1 > /dev/null && debian-sa1 1 1",
}
}
}
end

let(:unhex_length_audit_log) do
{
%q{type=PROCTITLE msg=audit(1585655101.154:27786): proctitle=2F62696E2F7368002D6300636F6D6D616E64202D762064656269616E2D736131203E202F6465762F6E756C6C2026262064656269616E2D73613120312031 } =>
{"header"=>{"type"=>"PROCTITLE", "msg"=>"audit(1585655101.154:27786)"},
"body"=>
{
"proctitle" => "2F62696E2F7368002D6300636F6D6D616E64202D762064656269616E2D736131203E202F6465762F6E756C6C2026262064656269616E2D73613120312031",
}
}
}
end

specify '#parse correctly unhex proctitle' do
lines = unhex_audit_log.keys.join("\n")
expect(AuditLogParser.parse(lines, unhex: true)).to eq unhex_audit_log.values
end

specify '#parse correctly unhex specific keys' do
lines = unhex_specific_audit_log.keys.join("\n")
expect(AuditLogParser.parse(lines, unhex: true, unhex_keys: ['proctitle2'])).to eq unhex_specific_audit_log.values
end

specify '#parse does not unhex short keys' do
lines = unhex_length_audit_log.keys.join("\n")
expect(AuditLogParser.parse(lines, unhex: true, unhex_keys: ['proctitle'], unhex_min_length: 10000)).to eq unhex_length_audit_log.values
end
end

context 'when invalid log' do
let(:invalid_log) do
{
Expand Down