Files
d-eyes/configcheck/check/sshlog.go
2023-11-08 15:31:09 +08:00

224 lines
5.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package check
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/gookit/color"
"github.com/toolkits/slice"
)
type BaopoInfo struct {
User string
Ip string
Time string
}
// 爆破成功
var correct_baopo_info []BaopoInfo
// 所有爆破的信息(超过阈值)
var baopo_info []BaopoInfo
//所有C段爆破信息超过阈值
//var c_baopo_info []BaopoInfo
// ip_failed_count: 单IP错误的次数超过此错误代表发生了爆破行为
// ips_failed_count: IP C段错误的次数超过此错误代表发生了爆破行为
// correct_baopo_info: 记录爆破成功的信息
func SSH_Analysis(log string, log_dir string, ip_failed_count uint, ips_failed_count uint) {
if log == "" {
dir_file_detect(log_dir, ip_failed_count, ips_failed_count)
} else {
attack_detect(log, ip_failed_count, ips_failed_count)
}
}
func dir_file_detect(log_dir string, ip_failed_count uint, ips_failed_count uint) {
var files []string
dirs, _ := ioutil.ReadDir(log_dir)
for _, dir := range dirs {
if dir.IsDir() {
continue
}
if strings.Contains(dir.Name(), "secure") {
files = append(files, log_dir+dir.Name())
}
}
for _, file := range files {
attack_detect(file, ip_failed_count, ips_failed_count)
}
}
func reRepeat(old []BaopoInfo) []string {
var ip_sum []string
// 遍历数组
for _, item := range old {
if !slice.ContainsString(ip_sum, item.Ip) {
ip_sum = append(ip_sum, item.Ip)
}
}
return ip_sum
}
func filter(old map[string]uint, count uint) map[string]uint {
var new_dict map[string]uint
new_dict = make(map[string]uint)
for key := range old {
if old[key] > count {
new_dict[key] = old[key]
}
}
return new_dict
}
func counter(old []string) map[string]uint {
var count_dict map[string]uint
count_dict = make(map[string]uint)
for _, item := range old {
_, ok := count_dict[item]
if ok {
count_dict[item] += 1
} else {
count_dict[item] = 1
}
}
return count_dict
}
func attack_detect(log string, ip_failed_count uint, ips_failed_count uint) {
// 账户错误特征
username_error := "Invalid user"
// 账户正确密码错误特征
username_correct := "Failed password for"
// 成功登陆
username_password_correct := "Accepted password for"
// 所有错误登陆日志ip
var failed_ips []string
// 登陆成功日志
var correct_infos []BaopoInfo
// 登录日志
var login_infos []BaopoInfo
// C段ip登陆错误日志
//var failed_c_ips []string
_, filename := filepath.Split(log)
year := ""
if strings.Contains(filename, "secure-") && len(filename) == 15 {
year = filename[7:11]
}
dat, err := ioutil.ReadFile(log)
if err != nil {
fmt.Println(err.Error())
}
lines := strings.Split(string(dat), "\n")
for _, line := range lines {
if strings.Contains(line, username_error) && strings.Contains(line, "from") && strings.Contains(line, "sshd") {
ip := strings.Split(strings.Split(line, ": ")[1], " ")[4]
failed_ips = append(failed_ips, ip)
time := strings.Join(strings.Split(strings.Replace(line, " ", " ", -1), " ")[:3], " ") + " " + year
login_infos = append(login_infos, BaopoInfo{User: "", Ip: ip, Time: time})
} else if strings.Contains(line, username_correct) && strings.Contains(line, "from") && strings.Contains(line, "sshd") {
strs := strings.Split(strings.TrimSpace(strings.Split(line, ": ")[1]), " ")
ip := strs[len(strs)-4]
failed_ips = append(failed_ips, ip)
time := strings.Join(strings.Split(strings.Replace(line, " ", " ", -1), " ")[:3], " ") + " " + year
login_infos = append(login_infos, BaopoInfo{User: "", Ip: ip, Time: time})
} else if strings.Contains(line, username_password_correct) && strings.Contains(line, "sshd") {
ip := strings.Split(strings.Split(line, ": ")[1], " ")[5]
user := strings.Split(strings.Split(line, ": ")[1], " ")[3]
time := strings.Join(strings.Split(strings.Replace(line, " ", " ", -1), " ")[:3], " ") + " " + year
correct_infos = append(correct_infos, BaopoInfo{User: user, Ip: ip, Time: time})
}
}
// 记录登陆失败攻击源IP地址和尝试次数
failed_ip_dict := filter(counter(failed_ips), ip_failed_count)
// 获取所有未爆破成功的ip信息
for _, login_info := range login_infos {
for failed := range failed_ip_dict {
if login_info.Ip == failed {
baopo_info = append(baopo_info, login_info)
}
}
}
// 判断爆破行为是否成功:
for _, correct_info := range correct_infos {
for failed := range failed_ip_dict {
if correct_info.Ip == failed {
correct_baopo_info = append(correct_baopo_info, correct_info)
}
}
}
}
func SSHLog() {
_, err := os.Lstat("/var/log/auth.log")
if os.IsNotExist(err) {
_, err := os.Lstat("/var/log/secure")
if os.IsNotExist(err) {
fmt.Println("Could't find SSH log !!!")
} else {
if !sshLogNotSafe("/var/log/secure", "") {
color.Infoln("主机SSH登录爆破检测: [safe]")
}
}
} else {
if !sshLogNotSafe("/var/log/auth.log", "") {
color.Infoln("主机SSH登录爆破检测: [safe]")
}
}
}
func sshLogNotSafe(log string, log_dir string) bool {
malice := false
SSH_Analysis(log, log_dir, 10, 10)
for _, info := range correct_baopo_info {
user := info.User
time := info.Time
ip := info.Ip
fmt.Printf("主机SSH被外部爆破且成功登录时间: %s, ip: %s, 用户: %s\n", time, ip, user)
malice = true
}
ip_sum := reRepeat(baopo_info)
if len(ip_sum) != 0 {
malice = true
fmt.Println("以下", len(ip_sum), "个IP尝试SSH登录爆破建议封禁:")
for _, ip := range ip_sum {
fmt.Print(ip + "、")
}
if len(ip_sum) != 0 {
fmt.Println("")
}
}
return malice
}