Skip to content

Instantly share code, notes, and snippets.

@cstayyab
Created December 5, 2018 08:31
Show Gist options
  • Save cstayyab/4ca0ac623d38de0d1527c749cbbf1d1d to your computer and use it in GitHub Desktop.
Save cstayyab/4ca0ac623d38de0d1527c749cbbf1d1d to your computer and use it in GitHub Desktop.
A Sysdig Chisel written in lua to capture all the activites done in SSH sessions separately on a linux machine.
-- Chisel description
description = "Track SSH Session activity";
short_description = "Track SSH SESSIONS activity";
category = "Security";
-- Chisel argument list
args =
{
{
name = "max_depth",
description = "the maximum depth to show in the hierarchy of processes",
argtype = "int",
optional = true
},
{
name = "disable_color",
description = "Set to 'disable_colors' if you want to disable color output",
argtype = "string",
optional = true
},
}
require "common"
terminal = require "ansiterminal"
terminal.enable_color(true)
MAX_ANCESTOR_NAVIGATION = 16
max_depth = -1
-- Argument notification callback
function on_set_arg(name, val)
if name == "max_depth" then
max_depth = parse_numeric_input(val, name)
elseif name == "disable_color" and val == "disable_color" then
terminal.enable_color(false)
end
return true
end
-- Initialize Callback
function on_init()
sysdig.set_snaplen(2000)
-- Request the fields needed for this chisel
fetype = chisel.request_field("evt.type")
fexe = chisel.request_field("proc.exe")
fproc = chisel.request_field("proc.name")
fargs = chisel.request_field("proc.args")
fdir = chisel.request_field("evt.arg.path")
fuser = chisel.request_field("user.name")
fdtime = chisel.request_field("evt.time.s")
fpid = chisel.request_field("proc.pid")
fppid = chisel.request_field("proc.ppid")
fppname = chisel.request_field("proc.pname");
fsid = chisel.request_field("proc.sid")
ffname = chisel.request_field("fd.name")
ffilename = chisel.request_field("fd.filename")
fargdata = chisel.request_field("evt.arg.data")
fapid = chisel.request_field("proc.apid[2]")
fanames = {}
fapids = {}
sfilter = "(fd.name startswith /run/systemd/sessions/ and not fd.filename startswith \".#\" and not fd.filename startswith c and evt.arg.data contains \"REMOTE=1\")"
bfilter = "(evt.type=execve)"
cfilter = "(((evt.type=execve and evt.dir=<) or (evt.type=chdir and evt.dir=< and proc.name contains sh and not proc.name contains sshd)) and evt.failed=false)"
chisel.set_filter(sfilter .. " or " .. bfilter .. " or " .. cfilter)
for j = 0, MAX_ANCESTOR_NAVIGATION do
fanames[j] = chisel.request_field("proc.aname[" .. j .. "]")
fapids[j] = chisel.request_field("proc.apid[" .. j .. "]")
end
return true
end
process_tree = {}
sessions = {}
files = {}
function table.print(t)
print("{\n")
for k,v in pairs(t) do
print(k .. " : " .. v .. "\n")
end
print("}\n")
end
-- Event parsing callback
function on_event()
local color = ""
local etype = evt.field(fetype)
local user = evt.field(fuser)
local dtime = evt.field(fdtime)
local pid = evt.field(fpid)
local ppid = evt.field(fppid)
local ischdir = evt.field(fetype) == "chdir"
local containername = evt.field(fcontainername)
local containerid = evt.field(fcontainerid)
local sid=evt.field(fsid)
local dir=evt.field(fdir)
local proc = evt.field(fproc)
local fname = evt.field(ffname)
local filename = evt.field(ffilename)
local argdata = evt.field(fargdata)
local exe = evt.field(fexe)
local apid = evt.field(fapid)
local ppname = evt.field(fppname);
local aname
local icorr = 1
if(etype=="execve" and exe == "-bash" and ppname == "sshd") then
for k,v in pairs(sessions) do
--print(v["LEADER"] .. " " .. apid)
if(v["LEADER"]==tonumber(apid)) then
sessions[k]["BASH"] = tonumber(pid)
--print("Session " .. k .. " User=" .. v["USER"] .. " IP=" .. v["REMOTE_HOST"])
print(terminal.red .. "New session " .. k .. " started for user (".. sessions[k]["USER"].. ") from IP " .. sessions[k]["REMOTE_HOST"] .. " SHELL ID: " .. sessions[k]["BASH"] .. terminal.reset)
break
end
end
else
if (sessions[filename] == nil and tonumber(filename) ~= nil and io.open(fname,"r") ~= nil) then
local session = get_session_attr(fname)
sessions[filename] = session
--[[elseif (sessions[filename] ~= nil and tonumber(filename) ~= nil and io.open(fname,"r") ~= nil) then
local update = get_session_attr(fname)
if(update["STATE"]=="CLOSING") then
sessions[filename]=nil
print("Session " .. filename .. " closed by user(".. update["USER"] ..") from IP " .. update["REMOTE_HOST"] .. " SHELL ID: " .. sessions[k]["BASH"])
end--]]
end
end
-- Traking user sessions activity
if ischdir then
ppid = pid
table.insert(fanames, 0, 0)
table.insert(fapids, 0, 0)
icorr = 0
end
local belongsto
for k,v in pairs(sessions) do
if v["BASH"]~=nil and tonumber(sid)==tonumber(v["BASH"]) then
belongsto = v
break
end
end
local remoteip
if belongsto~=nil then
user=belongsto["USER"]
remoteip=belongsto["REMOTE_HOST"]
if not process_tree[ppid] then
-- No parent pid in the table yet.
-- Add one and make sure that there's a shell among the ancestors
process_tree[ppid] = {-1}
for j = 1, MAX_ANCESTOR_NAVIGATION do
aname = evt.field(fanames[j])
if aname == nil then
if evt.field(fapids[j]) == nil then
-- no shell in the ancestor list, hide this command
break
end
elseif string.len(aname) >= 2 and aname:sub(-2) == "sh" then
apid = evt.field(fapids[j])
if process_tree[apid] then
process_tree[ppid] = {j - 1, apid}
else
process_tree[ppid] = {0, apid}
end
end
end
end
if process_tree[ppid][1] == -1 then
-- the parent process has already been detected as NOT having a shell ancestor
return true
end
if not process_tree[pid] then
process_tree[pid] = {1 + process_tree[ppid][1], process_tree[ppid][2]}
end
if ischdir then
if max_depth ~= -1 then
if process_tree[pid][1] - icorr > max_depth then
return true
end
end
-- The -pc or -pcontainer options was supplied on the cmd line
if print_container then
-- No support of containers added yet
else
print(color ..
extend_string("", 4 * (process_tree[pid][1] - icorr)) .. process_tree[pid][2] .. " " ..
dtime .. " " .. remoteip .. "(" ..
user .. ") cd " ..
evt.field(fdir))
end
else
if max_depth ~= -1 then
if process_tree[pid][1] - 1 > max_depth then
return true
end
end
-- The -pc or -pcontainer options was supplied on the cmd line
if print_container then
-- No support for container added yet
else
print(color ..
extend_string("", 3 * (process_tree[pid][1] - 1)) .. process_tree[pid][2] .. " " .. dtime .. " " .. remoteip .. " (" .. user ..") " .. evt.field(fexe) .. " " .. evt.field(fargs))
end
end
end
-- Tracking activity ends here
return true
end
-- Get an attribute of ssh session
function get_session_attr(filename)
attrs = {}
local f = io.open(filename)
while true do
line = f:read()
if line == nil then break end
if not line:match "^#" then
local attr = split(line,"=")
if(tonumber(attr[2])~=nil) then
attrs[attr[1]]=tonumber(attr[2])
else
attrs[attr[1]]=attr[2]
end
end
end
return attrs
end
-- Called by the engine at the end of the capture (Ctrl-C)
function on_capture_end()
print(terminal.reset)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment