package logic import ( "encoding/json" "fmt" "strconv" "strings" "sync" "github.com/alphadose/haxmap" "github.com/elastic/go-libaudit/v2/aucoalesce" "github.com/elastic/go-libaudit/v2/auparse" ) var ( // 接收审计事件的管道 EventChan = make(chan *aucoalesce.Event, 1024) // 记录有效事件的map EventMap = haxmap.New[string, []*TaggedEvent]() // 记录需要注意的可执行文件路径 ExecPath = map[string]bool{ "/usr/bin/scp": true, "/usr/libexec/openssh/sftp-server": true, } // 记录需要注意的系统调用 Syscalls = map[string]FileAction{ "open": FA_Open, "openat": FA_Open, "close": FA_Close, "rename": FA_Rename, "renameat": FA_Rename, "renameat2": FA_Rename, "link": FA_Rename, "unlink": FA_Rename, "linkat": FA_Rename, "unlinkat": FA_Rename, } SCP = "/usr/bin/scp" SFTP = "/usr/libexec/openssh/sftp-server" ) // FileTransformExec 传输文件的程序类型 type FileTransformExec uint8 const ( FTE_UNKNOWN FileTransformExec = iota FTE_SFTP // sftp FTE_SCP // scp ) type FileAction uint8 const ( FA_Open FileAction = iota FA_Close FA_Rename FA_Delete ) type TaggedEvent struct { Event *aucoalesce.Event Exec FileTransformExec IsTabby bool // 标记是否为tabby客户端 Fd *int // 记录文件描述符 } func FromEvent(event *aucoalesce.Event) *TaggedEvent { if event == nil { return nil } result := TaggedEvent{ Event: event, } switch event.Process.Exe { case SCP: result.Exec = FTE_SCP case SFTP: result.Exec = FTE_SFTP default: result.Exec = FTE_UNKNOWN } // 如果是sftp,检查是否为 if result.Exec == FTE_SFTP { } // 如果是open、openat、close,会有文件描述符 return &result } // EventSet 事件集,一个事件集记录了同一个进程的审计事件 type EventSet struct { Pid, PPID int // pid和ppid Events []*aucoalesce.Event // 记录所有事件 Fds map[int64][]*aucoalesce.Event // 记录可以获取文件描述符的事件 IsTabby *bool // 记录是否为tabby客户端 Exec FileTransformExec // 记录文件传输工具 Lock sync.RWMutex // 锁,用于保护Fds和Events的读写 } func NewEventSet() *EventSet { return &EventSet{ Events: make([]*aucoalesce.Event, 0, 16), Fds: make(map[int64][]*aucoalesce.Event), IsTabby: nil, Exec: FTE_UNKNOWN, Lock: sync.RWMutex{}, } } // func (es *EventSet) AddEvent(e *aucoalesce.Event) error { // if e == nil { // return nil // } // es.Events = append(es.Events, e) // sc := e.Data["syscall"] // switch sc { // case "close": // case "openat": // case "": // } // if sc == "openat" { // i, err := ParseInt(e.Data["exit"]) // if err != nil { // return nil // } // } // return nil // } // GenKey 为事件生成字符串id,格式为 pid-ppid func GenKey(event *aucoalesce.Event) string { return fmt.Sprintf("%s-%s", event.Process.PID, event.Process.PPID) } // FiltMsg 过滤出有用的事件,存放到map中,并触发处理程序 func FiltMsg() { for i := range EventChan { v, have := ExecPath[i.Process.Exe] if !(v && have) { // 不是指定的exe跳过 continue } if i.Category != aucoalesce.EventTypeAuditRule { // 不是由审计规则触发的事件跳过 continue } if i.Type != auparse.AUDIT_SYSCALL { // 不是系统调用,跳过 continue } if i.Data == nil { // Data里没有数据的跳过 continue } sc, have := i.Data["syscall"] if !have { continue } // 检查是不是需要关注的系统调用 _, have = Syscalls[sc] if !have { continue } // 失败 if i.Result != "success" { continue } printEvent(i) } } // HandleEvent 处理事件 func HaneldEvent(events []*TaggedEvent) { // todo panic("unimplemented") } // needHandle 判断是否需要进入处理流程 func needHandle(events []*TaggedEvent) bool { // todo panic("unimplemented") } func printEvent(e *aucoalesce.Event) { // pid,ppid,syscall, // open/openat:file,fd // close:fd if e == nil { return } switch e.Data["syscall"] { case "open", "openat": fmt.Printf("open(at) pid: %s, ppid: %s, open %s, fd: %s \n", e.Process.PID, e.Process.PPID, e.File.Path, e.Data["exit"]) case "close": fmt.Printf("close pid: %s, ppid: %s, fd: %s \n", e.Process.PID, e.Process.PPID, e.Data["a0"]) case "rename", "renameat", "renameat2": fmt.Println(json.MarshalIndent(e, "", " ")) case "link", "linkat": fmt.Printf("link(at): paths: %v\n", e.Paths) case "unlink", "unlinkat": fmt.Printf("unlink(at): paths: %v\n", e.Paths) case "dup2", "dup3": fmt.Println("dup") default: return } } func ParseInt(str string) (int64, error) { if strings.ContainsAny(str, "abcdefABCDEFxX") { // 是16进制数 s := strings.ToLower(str) if strings.HasPrefix(s, "0x") { return strconv.ParseInt(s, 0, 64) } else { return strconv.ParseInt(fmt.Sprintf("0x%s", s), 0, 64) } } else { // 不是16进制数 n, err := strconv.Atoi(str) if err != nil { return 0, err } return int64(n), err } }