Commit 5b619e2e authored by liming6's avatar liming6
Browse files

feature 添加文件监控部分功能

parent 095e09e6
This diff is collapsed.
# 监控scp创建文件动作
-a always,exit -F arch=b64 -S openat -F exe=/usr/bin/scp -F 'a2&0x40' -F key=scp_create_file
-a always,exit -F arch=b64 -S open -F exe=/usr/bin/scp -F 'a1&0x40' -F key=scp_create_file
# 监控scp关闭文件动作,通常表示文件已经写入完成
-a always,exit -F arch=b64 -S openat -F exe=/usr/bin/scp -F a2&0x40 -F key=scp_create_file
-a always,exit -F arch=b64 -S open -F exe=/usr/bin/scp -F a1&0x40 -F key=scp_create_file
# 监控scp关闭文件动作
-a always,exit -F arch=b64 -S close -F exe=/usr/bin/scp -F key=scp_close_file
# 监控sftp创建文件动作
-a always,exit -F arch=b64 -S openat -F exe=/usr/libexec/openssh/sftp-server -F 'a2&0x40' -F key=sftp_create_file
-a always,exit -F arch=b64 -S open -F exe=/usr/libexec/openssh/sftp-server -F 'a1&0x40' -F key=sftp_create_file
# 监控sftp关闭文件动作,通常表示文件已经写入完成
-a always,exit -F arch=b64 -S openat -F exe=/usr/libexec/openssh/sftp-server -F a2&0x40 -F key=sftp_create_file
-a always,exit -F arch=b64 -S open -F exe=/usr/libexec/openssh/sftp-server -F a1&0x40 -F key=sftp_create_file
# 监控sftp关闭文件动作
-a always,exit -F arch=b64 -S close -F exe=/usr/libexec/openssh/sftp-server -F key=sftp_close_file
# 监控sftp修改文件名动作
......
package logic
import (
"errors"
"fmt"
"os"
"os/exec"
"regexp"
"sshd-tool/utils"
"strconv"
"strings"
)
var (
// 需要的二进制文件
NeedExec = []string{"chattr", "lsattr", "clamscan", "clamdscan"}
ReInfectedFile = regexp.MustCompile(`(?i)^infected\s+files:\s+([0-9]+)\s*$`)
ErrNoLineMatch = errors.New("no line match")
)
func CheckExec() error {
for _, v := range NeedExec {
p := utils.FindCmd(v)
if p == nil {
return fmt.Errorf("%s not found", v)
}
}
return nil
}
// ScanFile 扫描文件,返回是否有毒,如果有毒会删除
func ScanFile(f string) (bool, error) {
finfo, err := os.Stat(f)
if err != nil {
return false, err
}
if finfo.IsDir() {
return false, errors.New("error: dest is directory")
}
sf := fmt.Sprintf("%s.scanning", f)
err = os.Rename(f, sf)
if err != nil {
return false, err
}
cmd := exec.Command("clamdscan", "-i", sf)
output, err := cmd.CombinedOutput()
if err != nil {
if cmd.ProcessState.ExitCode() != 1 {
return false, fmt.Errorf("run clamdscan failed, output: %s", string(output))
}
}
if cmd.ProcessState.ExitCode() == 1 {
// 返回值不为0,有异常
str := string(output)
num, err := parseClamOutput(str)
if err != nil {
return false, fmt.Errorf("parse clamdscan output error, output: %s", str)
}
if num > 0 {
// todo:可以在这里插入日志
os.Remove(sf)
}
return true, nil
} else {
// 无异常,直接返回
err = os.Rename(sf, f)
return false, err
}
}
// parseClamOutput 解析clamdscan输出
func parseClamOutput(str string) (int, error) {
for _, line := range strings.Split(strings.Trim(str, "\n"), "\n") {
if ReInfectedFile.MatchString(line) {
f := ReInfectedFile.FindStringSubmatch(line)
return strconv.Atoi(f[1])
}
}
return 0, ErrNoLineMatch
}
package logic
import (
"strings"
"testing"
"time"
)
const (
Output = `----------- SCAN SUMMARY -----------
Infected files: 0
Time: 0.981 sec (0 m 0 s)
Start Date: 2026:02:14 09:39:02
End Date: 2026:02:14 09:39:03`
)
func TestRegext(t *testing.T) {
lines := strings.Split(Output, "\n")
for _, i := range lines {
if ReInfectedFile.MatchString(i) {
f := ReInfectedFile.FindStringSubmatch(i)
for _, ff := range f {
t.Log(ff)
}
}
}
}
func TestScanFile(t *testing.T) {
target := "./hycusmutool"
start := time.Now()
have, err := ScanFile(target)
dua := time.Since(start)
if have {
t.Logf("%s is bd", target)
}
t.Logf("used time: %dms", dua.Milliseconds())
if err != nil {
t.Error(err)
}
}
func TestParseInt(t *testing.T) {
i, err := ParseInt("abc")
if err != nil {
t.Error(err)
}
t.Logf("%d", i)
}
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
}
}
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"sshd-tool/cmd/file-monitor/logic"
"time"
"github.com/elastic/go-libaudit/v2"
......@@ -19,10 +19,7 @@ func (h *EventHandler) ReassemblyComplete(msgs []*auparse.AuditMessage) {
if err != nil {
fmt.Printf("coalesce messages error: %v", err)
}
fmt.Println("---------------------------")
j, _ := json.MarshalIndent(event, "", " ")
fmt.Printf("%s\n", string(j))
fmt.Println("---------------------------")
logic.EventChan <- event
}
func (h *EventHandler) EventsLost(count int) {
......@@ -37,7 +34,7 @@ func main() {
defer cli.Close()
handler := &EventHandler{}
rea, err := libaudit.NewReassembler(256, time.Second*2, handler)
rea, err := libaudit.NewReassembler(1024, time.Second*60, handler)
if err != nil {
log.Printf("%v", err)
return
......@@ -45,7 +42,7 @@ func main() {
defer rea.Close()
go func() {
ticker := time.NewTicker(time.Second)
ticker := time.NewTicker(time.Second * 15)
defer ticker.Stop()
for range ticker.C {
if rea.Maintain() != nil {
......@@ -61,4 +58,6 @@ func main() {
}
_ = rea.Push(rawMsg.Type, rawMsg.Data)
}
close(logic.EventChan)
}
# readme
sftp:
- 对于tabby,openat开始,close,unlink、link、unlink,unlink表示写入完成
- 普通sftp,openat开始,close表示写入完成
scp:
-
对于sftp,重命名文件:
-
<86>Jan 27 16:00:36 admin02 sftp-server[3761842]: opendir "/tmp"
<86>Jan 27 16:00:36 admin02 sftp-server[3761842]: closedir "/tmp"
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: open "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" flags WRITE,CREATE mode 0666
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: close "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" bytes read 0 written 1412082
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: remove name "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar"
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: sent status No such file
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: rename old "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" new "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar"
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: opendir "/tmp"
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: closedir "/tmp"
<86>Jan 27 16:48:22 admin02 sftp-server[3965704]: open "/tmp/id_ed25519" flags WRITE,CREATE,TRUNCATE mode 0666
<86>Jan 27 16:48:22 admin02 sftp-server[3965704]: close "/tmp/id_ed25519" bytes read 0 written 419
<86>Jan 27 16:48:22 admin02 sftp-server[3965052]: opendir "/tmp"
<86>Jan 27 16:48:23 admin02 sftp-server[3965052]: closedir "/tmp"
<86>Jan 27 16:48:53 admin02 sftp-server[3965052]: remove name "/tmp/id_ed25519"
<86>Jan 27 16:48:53 admin02 sftp-server[3965052]: opendir "/tmp"
<86>Jan 27 16:48:53 admin02 sftp-server[3965052]: closedir "/tmp"
\ No newline at end of file
package logic
import (
"fmt"
"os"
"sshd-tool/utils"
)
var (
NeedExec = []string{"chattr", "lsattr", "clamscan", "clamdscan"}
)
// CheckExec 检查必要的命令是否存在
func CheckExec() error {
for _, v := range NeedExec {
if p := utils.FindCmd(v); p == nil {
return fmt.Errorf("command %s not found", v)
}
}
return nil
}
// CheckRoot 检查是否以root的身份执行
func CheckRoot() bool {
return os.Getuid() == 0
}
package logic
import (
"testing"
"time"
)
func TestReg1(t *testing.T) {
itmes := []string{
`<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: rename old "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" new "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar"`,
}
for _, v := range itmes {
if ReMatch.MatchString(v) {
t.Logf("match: %s", v)
fields := ReMatch.FindStringSubmatch(v)
tt, err := time.Parse("Jan _2 15:04:05", fields[1])
if err != nil {
t.Log("parse time error!")
} else {
tn := time.Date(time.Now().Year(), tt.Month(), tt.Day(), tt.Hour(), tt.Minute(), tt.Second(), 0, time.Local)
t.Logf("time is %v", tn)
}
for ki, vi := range fields {
t.Logf(" %d: %v", ki, vi)
}
} else {
t.Logf("not match: %s", v)
}
}
}
func TestParseItem(t *testing.T) {
itmes := []string{
`<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: rename old "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" new "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar"`,
`<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: open "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" flags WRITE,CREATE mode 0666`,
`<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: close "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" bytes read 0 written 1412082`,
}
for _, v := range itmes {
i, err := ParseItem(v)
if err != nil {
t.Error(err)
}
if i != nil {
t.Logf("%+v", i)
} else {
t.Logf("not match: %s", v)
}
}
}
package logic
import (
"fmt"
"log"
"os"
"os/exec"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/shirou/gopsutil/v4/process"
)
/*
思路:由于tabby比较特殊,所以分两类:普通sftp客户端日志和tabby客户端日志
普通:
<86>Jan 27 16:48:22 admin02 sftp-server[3965704]: open "/tmp/id_ed25519" flags WRITE,CREATE,TRUNCATE mode 0666
<86>Jan 27 16:48:22 admin02 sftp-server[3965704]: close "/tmp/id_ed25519" bytes read 0 written 419
解析到open动作以及CREATE标志,记录文件位置;等到解析到同文件的close动作表示写文件完成,立即扫描该文件
tabby:
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: open "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" flags WRITE,CREATE mode 0666
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: close "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" bytes read 0 written 1412082
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: remove name "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar"
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: sent status No such file
<86>Jan 27 16:00:53 admin02 sftp-server[3761842]: rename old "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar.tabby-upload" new "/tmp/X680G55_9800195108886297_20251230-012816_bmcblackinfo.tar"
解析到open动作以及CREATE标志,记录文件位置;等到解析到同文件的rename动作表示写文件完成,立即扫描该文件
*/
var (
ReMatch = regexp.MustCompile(`<\d+>([A-Z][a-z]+\s+\d+\s+\d+:\d+:\d+)\s+[a-zA-Z0-9]+\s+sftp-server\[(\d+)\]:\s+([a-z]+)\s+(.*)$`)
LogItemMap = make(map[string][]*SftpLogItem) // key为文件名,value为两个对象指针,一个记录文件上传开始,一个记录文件上传结束
LogItemMapLock = sync.RWMutex{}
)
type Action int
const (
ActionOpenDir Action = iota
ActionCloseDir
ActionOpenFile
ActionCloseFile
ActionRemove
ActionRename
)
const (
TabbySuffix = ".tabby-upload"
ScanSuffix = ".scanning"
)
// SftpLogItem sftp日志类型,Flag的key可以为:name(remove) old(rename) new(rename) flags(open) mode(open) read(close) written(close)
type SftpLogItem struct {
Pid int
UserInfo string // uid或用户名
Time time.Time
Act Action
Target string // 动作的目标,针对open、close,是文件路径,针对rename,是文件原名
Flag map[string]any // 动作的参数,针对open、close、rename才有
IsTabby bool // 针对tabby需要特殊处理
}
// FinalFile 获取最终文件路径
func (sli *SftpLogItem) FinalFile() (string, error) {
if !sli.IsTabby {
return sli.Target, nil
}
switch sli.Act {
case ActionOpenFile, ActionCloseFile:
return strings.TrimSuffix(sli.Target, TabbySuffix), nil
case ActionRename:
if sli.Flag == nil {
return strings.TrimSuffix(sli.Target, TabbySuffix), nil
}
nname, have := sli.Flag["new"]
if !have {
return strings.TrimSuffix(sli.Target, TabbySuffix), nil
}
return nname.(string), nil
default:
return "", fmt.Errorf("unexcept action: %d", sli.Act)
}
}
func ParseItem(str string) (*SftpLogItem, error) {
str = strings.Trim(strings.Trim(str, "\n"), " ")
fields := ReMatch.FindStringSubmatch(str)
if len(fields) == 0 {
return nil, nil
}
var act Action
switch fields[3] {
case "open":
act = ActionOpenFile
case "close":
act = ActionCloseFile
case "rename":
act = ActionRename
default:
return nil, nil
}
time_raw, err := time.Parse("Jan _2 15:04:05", fields[1])
if err != nil {
return nil, err
}
time_raw = time_raw.AddDate(time.Now().Year()-time_raw.Year(), 0, 0)
pid, err := strconv.Atoi(fields[2])
if err != nil {
return nil, err
}
proc, err := process.NewProcess(int32(pid))
if err != nil {
return nil, err
}
uinfo, err := proc.Username()
if err != nil {
uinfo = fmt.Sprintf("%d",proc.Uids())
} else {
}
result := SftpLogItem{Act: act, Time: time_raw, Pid: pid}
switch result.Act {
case ActionOpenFile:
err = parseOpen(fields[4], &result)
case ActionCloseFile:
err = parseClose(fields[4], &result)
case ActionRename:
err = parseRename(fields[4], &result)
}
return &result, err
}
// parseClose
func parseClose(str string, target *SftpLogItem) error {
str = strings.Trim(strings.Trim(str, "\n"), " ")
fields := strings.Fields(str)
last := ""
for _, v := range fields {
if strings.HasPrefix(v, `"`) && strings.HasSuffix(v, `"`) {
target.Target = strings.Trim(v, `"`)
target.IsTabby = strings.HasSuffix(target.Target, TabbySuffix)
last = v
continue
}
switch v {
case "bytes", "read", "written":
last = v
default:
i, err := strconv.Atoi(v)
if err != nil {
last = v
break
}
if last != "" {
if target.Flag == nil {
target.Flag = make(map[string]any)
}
target.Flag[last] = i
last = ""
} else {
return fmt.Errorf("unknown parse close log: %s", str)
}
}
}
return nil
}
func parseOpen(str string, target *SftpLogItem) error {
str = strings.Trim(strings.Trim(str, "\n"), " ")
fields := strings.Fields(str)
last := ""
for _, v := range fields {
if strings.HasPrefix(v, `"`) && strings.HasSuffix(v, `"`) {
target.Target = strings.Trim(v, `"`)
target.IsTabby = strings.HasSuffix(target.Target, TabbySuffix)
last = v
continue
}
switch v {
case "flags", "mode":
last = v
default:
if last != "" {
if target.Flag == nil {
target.Flag = make(map[string]any)
}
target.Flag[last] = v
last = ""
} else {
return fmt.Errorf("unknown parse open log: %s", str)
}
}
}
return nil
}
func parseRename(str string, target *SftpLogItem) error {
str = strings.Trim(strings.Trim(str, "\n"), " ")
fields := strings.Fields(str)
last := ""
for _, v := range fields {
switch v {
case "old", "new":
last = v
default:
if strings.HasPrefix(v, `"`) && strings.HasSuffix(v, `"`) {
if last != "" {
if target.Flag == nil {
target.Flag = make(map[string]any)
}
target.Flag[last] = strings.Trim(v, `"`)
if last == "old" {
target.Target = strings.Trim(v, `"`)
target.IsTabby = strings.HasSuffix(target.Target, TabbySuffix)
}
last = ""
} else {
return fmt.Errorf("unknown parse rename log: %s", str)
}
last = v
} else {
return fmt.Errorf("unknown parse rename log: %s", str)
}
}
}
return nil
}
// HandleSftpLog 处理sftp日志
func HandleSftpLog(str string) {
item, err := ParseItem(str)
if err != nil {
log.Printf("parse sftp log error,log: %s, err: %v", str, err)
}
if item == nil {
return
}
switch item.Act {
case ActionOpenFile:
if item.Flag == nil {
return
}
flag, have := item.Flag["flags"]
if !have {
return
}
f := flag.(string)
if strings.Contains(f, "CREATE") {
LogItemMapLock.Lock()
ii := make([]*SftpLogItem, 0, 2)
ii = append(ii, item)
LogItemMap[item.Target] = ii
LogItemMapLock.Unlock()
}
case ActionCloseFile:
if item.IsTabby {
return
}
rl := LogItemMapLock.RLocker()
rl.Lock()
i, have := LogItemMap[item.Target]
rl.Unlock()
if !have {
log.Printf("close file but not found open: %s", item.Target)
return
}
i[1] = item
scanFile(i)
case ActionRename:
if !item.IsTabby {
return
}
rl := LogItemMapLock.RLocker()
rl.Lock()
i, have := LogItemMap[item.Target]
rl.Unlock()
if !have {
log.Printf("rename file but not found open: %s", item.Target)
return
}
i[1] = item
scanFile(i)
}
}
func scanFile(logItems []*SftpLogItem) {
if len(logItems) < 2 {
return
}
// // 记录权限
filePath, err := logItems[0].FinalFile()
if err != nil {
log.Printf("get final file path error: %v", err)
return
}
fileStat, err := os.Stat(filePath)
fileMode := fileStat.Mode()
os.Chmod(filePath, 0000)
// 修改名称
reFilePath := fmt.Sprintf("%s%s", filePath, ScanSuffix)
os.Rename(filePath, reFilePath)
// 扫描文件
cmd := exec.Command("clamdscan", "-i", "--no-summary", reFilePath)
output, err := cmd.CombinedOutput()
if cmd.ProcessState.ExitCode() != 0 {
// 有问题
content := string(output)
log.Printf("find virus file: %s", content)
} else {
// 没问题
os.Rename(reFilePath, filePath)
os.Chmod(filePath, fileMode)
}
}
package main
import (
"fmt"
"log"
"net"
"os/exec"
"sshd-tool/cmd/sftp-monitor/logic"
"strings"
"github.com/gofrs/flock"
)
const (
SocketPath = "/var/run/authpriv.sock"
FileLockPath = "/tmp/sftp-monitor.pid"
)
func main() {
fileLock := flock.New(FileLockPath, flock.SetPermissions(0644))
l, err := fileLock.TryLock()
if err != nil {
log.Fatalf("error lock file %s, %v", fileLock, err)
}
if !l {
log.Fatalf("can't lock %s, Perhaps an instance is already running.", FileLockPath)
}
// 删除旧
_ = exec.Command("rm", "-f", SocketPath).Run()
conn, err := net.ListenPacket("unixgram", SocketPath)
if err != nil {
log.Fatalf("can't listen unix socket: %v", err)
}
buff := make([]byte, 16384)
for {
n, _, err := conn.ReadFrom(buff)
if err != nil {
log.Fatalf("read error: %v", err)
break
}
content := string(buff[:n])
if strings.Contains(content, "sftp-server") {
fmt.Println(content)
go logic.HandleSftpLog(content)
}
}
}
......@@ -8,6 +8,7 @@ require (
)
require (
github.com/alphadose/haxmap v1.4.1 // indirect
github.com/ebitengine/purego v0.9.1 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/kr/text v0.2.0 // indirect
......@@ -17,6 +18,7 @@ require (
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
golang.org/x/exp v0.0.0-20221031165847-c99f073a8326 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
......
package utils
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment