HIDS入侵检测相关实现内容记录 相关技术整理

  1. 用户类
import (
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"strings"
	"time"

	"github.com/fsnotify/fsnotify"
)

const (
	userInfoEvent        = "user_info"
	userInfoMonitorEvent = "UserInfo"
	bashPidContentRegexp = "(useradd|password|usermod)[[0-9]{2,8}]"
	bashPidRegexp        = "[0-9]{2,8}"
)

var (
	userInfoRepository       = repository.NewUserInfoRepository()
	UserInfoMonitorProcessor = newUserInfoProcessor()
)

type UserInfoProcessor struct {
	fileName       string
	passwdFileName string
}

func newUserInfoProcessor() *UserInfoProcessor {
	return &UserInfoProcessor{
		conf.GlobalMonitorConfig.RootPath + "/etc/passwd",
		conf.GlobalMonitorConfig.RootPath + "/etc/shadow",
	}
}

func (fpp *UserInfoProcessor) HandleUserInfoEvent(notifyEvent fsnotify.Event) {
	monitorFileName := notifyEvent.Name
	if strings.ToLower(notifyEvent.Op.String()) == "remove" {
		fpp.restartMonitorUserInfo(monitorFileName)
		runtime.Goexit()
		return
	}
	//获取数据库数据
	userInfoList := userInfoRepository.SelectUserInfo()
	var userInfoDetailArr []event.Detail
	//获取变化后的数据map
	newUserInfoMap := util.GetCurUserInfoData(fpp.fileName, fpp.passwdFileName)
	for _, oldUserInfo := range userInfoList {
		value, isFind := newUserInfoMap[oldUserInfo.UserName]
		if isFind {
			var newUserInfo userInfo.UserInfo
			util.MapToStruct(value, &newUserInfo)
			if !reflect.DeepEqual(oldUserInfo, newUserInfo) {
				userInfoRepository.UpdateUserInfo(newUserInfo)
				isHiddenModCommandOpt, _ := fpp.isCommandOpt(newUserInfo, "usermod")
				isHiddenPasswdCommandOpt, currentBashUserName := fpp.isCommandOpt(newUserInfo, "passwd")
				if currentBashUserName != "" {
					newUserInfo.CurrentBashUserName = currentBashUserName
				}
				newUserInfo.IsHiddenUser = !isHiddenModCommandOpt && !isHiddenPasswdCommandOpt
				if newUserInfo.PassWdMd5 != oldUserInfo.PassWdMd5 {
					newUserInfo.IsPasswdChange = true
				}
				userInfoDetailArr = append(userInfoDetailArr, event.NewDetail(oldUserInfo, newUserInfo))
				fpp.updateUserStatus(fpp.generateUserStatusInfo(newUserInfo))
			}
			delete(newUserInfoMap, oldUserInfo.UserName)
		} else {
			userInfoRepository.DeleteUserInfo(oldUserInfo.UserName)
			userInfoDetailArr = append(userInfoDetailArr, event.NewDetail(oldUserInfo, struct{}{}))
		}
	}

	for _, addItem := range newUserInfoMap {
		var newUserInfo userInfo.UserInfo
		util.MapToStruct(addItem, &newUserInfo)
		userInfoRepository.InsertUserInfo(newUserInfo)
		isHiddenCommandOpt, currentBashUserName := fpp.isCommandOpt(newUserInfo, "useradd")
		if currentBashUserName != "" {
			newUserInfo.CurrentBashUserName = currentBashUserName
		}
		newUserInfo.IsHiddenUser = !isHiddenCommandOpt
		var oldUserInfo userInfo.UserInfo
		userInfoDetailArr = append(userInfoDetailArr, event.NewDetail(oldUserInfo, newUserInfo))
		fpp.insertUserStatus(fpp.generateUserStatusInfo(newUserInfo))
	}

	if len(userInfoDetailArr) > 0 {
		EventReporter.HandleEventReporter(userInfoEvent, userInfoDetailArr)
	}

	if notifyEvent.Name == fpp.passwdFileName || notifyEvent.Name == fpp.fileName {
		fpp.restartMonitorUserInfo(monitorFileName)
		runtime.Goexit()
		return
	}

}

func (fpp *UserInfoProcessor) generateUserStatusInfo(userInfo userInfo.UserInfo) userLoginInfo.UserStatusInfo {
	var userStatusInfo userLoginInfo.UserStatusInfo
	userStatusInfo.UserName = userInfo.UserName
	userStatusInfo.IsLocked = userInfo.IsLockUser
	userStatusInfo.IsUnauthorized = util.GetAuthorizedStatus(userInfo.Shell)
	return userStatusInfo
}

func (fpp *UserInfoProcessor) updateUserStatus(newUserStatusInfo userLoginInfo.UserStatusInfo) {
	beforeUserStatusInfo := newUserStatusRepository.GetUserStatusByUser(newUserStatusInfo.UserName)
	if newUserStatusInfo.IsLocked == true && beforeUserStatusInfo.IsLocked == false {
		newUserStatusInfo.IsUnauthorized = beforeUserStatusInfo.IsUnauthorized
		newUserStatusRepository.UpdateUserStatus(newUserStatusInfo)
	}
	if newUserStatusInfo.IsUnauthorized == true && beforeUserStatusInfo.IsUnauthorized == false {
		newUserStatusInfo.IsLocked = beforeUserStatusInfo.IsLocked
		newUserStatusRepository.UpdateUserStatus(newUserStatusInfo)
	}
}

func (fpp *UserInfoProcessor) insertUserStatus(newUserStatusInfo userLoginInfo.UserStatusInfo) {
	if newUserStatusInfo.IsLocked == true || newUserStatusInfo.IsUnauthorized == true {
		repository.NewUserStatusRepository().InsertUserStatus(newUserStatusInfo)
	}
}

func (fpp *UserInfoProcessor) InitUserInfoData() {
	userInfoList := userInfoRepository.SelectUserInfo()
	listLen := len(userInfoList)
	if listLen == 0 {
		fpp.saveUserInfoToDB()
	}
}

func (fpp *UserInfoProcessor) saveUserInfoToDB() {
	newUserInfoMap := util.GetCurUserInfoData(fpp.fileName, fpp.passwdFileName)
	for _, newUserInfoMap := range newUserInfoMap {
		var newUserInfo userInfo.UserInfo
		util.MapToStruct(newUserInfoMap, &newUserInfo)
		userInfoRepository.InsertUserInfo(newUserInfo)
	}
}

//可能的操作类型 useradd  usermod  userdel
func (fpp *UserInfoProcessor) isCommandOpt(changedUserInfo userInfo.UserInfo, optType string) (bool, string) {
	time.Sleep(500 * time.Millisecond)
	matchString := "new user: name=" + changedUserInfo.UserName
	if optType == "usermod" {
		matchString = "change user '" + changedUserInfo.UserName + "'"
	}
	if optType == "userdel" {
		matchString = "delete user '" + changedUserInfo.UserName + "'"
	}
	if optType == "passwd" {
		matchString = "password changed for " + changedUserInfo.UserName
	}
	commandString := "grep -a \"" + matchString + "\" " + conf.GlobalMonitorConfig.RootPath + "/var/log/secure"
	if conf.GlobalMonitorConfig.RootName == "itran" {
		commandString = "grep -a \"" + matchString + "\" " + conf.GlobalMonitorConfig.RootPath + conf.RanSecureFilePath
	}
	outPut, _, _ := util.ExecShellCommand(commandString)
	if outPut != "" && fpp.checkTriggeringTime(outPut) {
		if optType == "passwd" || optType == "useradd" {
			currentBashUserName := fpp.getBashNameByPidThreeTimes(outPut, optType)
			return true, currentBashUserName
		} else {
			return true, ""
		}
	}
	return false, ""
}

func (fpp *UserInfoProcessor) restartMonitorUserInfo(monitorFileName string) {
	policy.CurrentMonitor.Notify(monitorFileName)
}

func (fpp *UserInfoProcessor) checkTriggeringTime(outPut string) bool {
	triggeringTime := time.Now().Format("15:04:05")
	triggeringTimeLast5 := time.Now().Add(-7 * time.Second).Format("15:04:05")
	recordTime := ""
	arr := strings.Split(outPut, "\n")
	if conf.GlobalMonitorConfig.RootName == "itran" {
		subArr := strings.Fields(arr[len(arr)-1])
		itranTimeDate := util.FormatRanTime(subArr[0])
		itranTime := strings.Fields(itranTimeDate)
		recordTime = itranTime[1]
	} else {
		subArr := strings.Fields(arr[len(arr)-1])
		recordTime = subArr[2]
	}
	if recordTime >= triggeringTimeLast5 && recordTime <= triggeringTime {
		return true
	} else {
		return false
	}
}

func (fpp *UserInfoProcessor) getBashNameByPidThreeTimes(outPut string, optType string) string {
	bashPid := fpp.getCurrentPid(outPut)
	for count := 0; count < 3; count++ {
		bashName := fpp.getBashNameByPid(bashPid, optType)
		if bashName != "" {
			return bashName
		}
		time.Sleep(2 * time.Second)
	}
	return ""
}

func (fpp *UserInfoProcessor) getBashNameByPid(bashPid int, optType string) string {
	if optType == "useradd" {
		_, isFind := monitor.BashUserNameMap.Load(int32(bashPid))
		if isFind {
			value, isOK := monitor.BashUserNameMap.LoadAndDelete(int32(bashPid))
			if isOK {
				return value.(string)
			} else {
				return ""
			}
		}
	} else if optType == "passwd" {
		_, isFind := monitor.BashUserNameMap.Load("passwd")
		if isFind {
			value, isOK := monitor.BashUserNameMap.LoadAndDelete("passwd")
			if isOK {
				return value.(string)
			} else {
				return ""
			}
		}
	}
	return ""
}

func (fpp *UserInfoProcessor) getCurrentPid(outPut string) int {
	bashPidRegexpContentPattern, _ := regexp.Compile(bashPidContentRegexp)
	bashPidList := bashPidRegexpContentPattern.FindAllString(outPut, -1)
	if len(bashPidList) >= 1 {
		bashPidContent := bashPidList[len(bashPidList)-1]
		bashPidRegexpPattern, _ := regexp.Compile(bashPidRegexp)
		bashPidString := bashPidRegexpPattern.FindString(bashPidContent)
		bashPid, _ := strconv.Atoi(bashPidString)
		return bashPid
	}
	return -1
}

webshell检测规则

func (fpp *WebShellProcessor) matchWebShellRegx(fileContent string) bool {
	regx1 := "((eval|assert)[\\s|\n]{0,30}\\([\\s|\n]{0,30}(\\\\{0,1}\\$((_(GET|POST|REQUEST|SESSION|SERVER)(\\[[\\'\"]{0,1})[\\w\\(\\)]{0,15}([\\'\"]{0,1}\\]))|\\w{1,10}))\\s{0,5}\\))"
	regx2 := "((eval|assert)[\\s|\n]{0,30}\\((gzuncompress|gzinflate\\(){0,1}[\\s|\n]{0,30}base64_decode.{0,100})"
	regx3 := "\\s{0,10}=\\s{0,10}([{@]{0,2}\\\\{0,1}\\$_(GET|POST|REQUEST)|file_get_contents|[\"\\']a[\"\\']\\.[\"\\']s[\"\\']\\.|[\"\\']e[\"\\']\\.[\"\\']v[\"\\']\\.|[\"\\']ass[\"\\']\\.).{0,20}"
	var regxPhpEvalAssertLis = []string{regx1, regx2, regx3}
	regx4 := "(\\$_(GET|POST|REQUEST)\\[.{0,15}\\]\\s{0,10}\\(\\s{0,10}\\$_(GET|POST|REQUEST).{0,15})"
	regx5 := "((\\$(_(GET|POST|REQUEST|SESSION|SERVER)(\\[[\\'\"]{0,1})\\w{1,12}([\\'\"]{0,1}\\])|\\w{1,10}))[\\s\n]{0,20}\\([\\s\n]{0,20}(@{0,1}\\$(_(GET|POST|REQUEST|SESSION|SERVER)(\\[[\\'\"]{0,1})\\w{1,12}([\\'\"]{0,1}\\])|\\w{1,10}))[\\s\n]{0,5}\\))"
	regx6 := "\\s{0,10}=\\s{0,10}[{@]{0,2}(\\$_(GET|POST|REQUEST)|file_get_contents|str_replace|[\"\\']a[\"\\']\\.[\"\\']s[\"\\']\\.|[\"\\']e[\"\\']\\.[\"\\']v[\"\\']\\.|[\"\\']ass[\"\\']\\.).{0,10}"
	var regxPhpDynamicFunction = []string{regx4, regx5, regx6}
	phpDdosCcKeywords := []string{"启动自动攻击", "xxddos", "phpddos", "fsockopen(\"udp:\"", "fsockopen(\"tcp:", "$_get[\"moshi\"]==\"udp\""}
	regx7 := "([^\\'\"](include|require)(_once){0,1}\\s{0,5}(\\s{0,5}|\\(\\s{0,5})[\"\\']([\\.\\w\\,/\\\\+-_]{1,60})[\"\\']\\s*\\){0,1})"
	regx8 := "((include|require)(_once){0,1}(\\s{0,5}|\\s{0,5}\\(\\s{0,5})[\\'\"]{0,1}(\\$(_(GET|POST|REQUEST|SERVER)(\\[[\\'\"]{0,1})\\w{0,8}([\\'\"]{0,1}\\])|[\\w]{1,15}))[\\'\"]{0,1})"
	regx9 := "\\s{0,10}=\\s{0,10}([{@]{0,2}\\$_(GET|POST|REQUEST)|[\\'\"]{0,1}php:\\/\\/input[\\'\"]{0,1}|file_get_contents).{0,20}"
	var regxPhpIncludeFile = []string{regx7, regx8, regx9}
	var regxPhpPackShell = []string{"gzdeflate|gzcompress|gzencode"}
	regx10 := "(array_map[\\s\n]{0,20}\\(.{1,5}(eval|assert|ass\\x65rt).{1,20}\\$_(GET|POST|REQUEST).{0,15})"
	regx11 := "(call_user_func[\\s\n]{0,25}\\(.{0,25}\\$_(GET|POST|REQUEST).{0,15})"
	regx12 := "(\\$_(GET|POST|REQUEST)\\[.{0,15}\\]\\s{0,10}\\(\\s{0,10}\\$_(GET|POST|REQUEST).{0,15})"
	regx13 := "(include|require)(_once){0,1}[\\s*]+[\"|\\']+[0-9A-Za-z_]*\\://"
	regx14 := "(preg_replace[\\s\n]{0,10}\\([\\s\n]{0,10}(([\"\\'].{0,15}[/@\\'][is]{0,2}e[is]{0,2}[\"\\'])|\\$[a-zA-Z_][\\w\"\\'\\[\\]]{0,15})\\s{0,5},\\s{0,5}.{0,40}(\\$_(GET|POST|REQUEST|SESSION|SERVER)|str_rot13|urldecode).{0,30})"
	var regxWebShell = []string{regx10, regx11, regx12, regx13, regx14}
	phpDpJmKeywords := []string{"www.phpdp.org", "www.phpjm.net", "www.phpdp.com", "Zend"}
	if fpp.matchRegxLis(fileContent, regxPhpEvalAssertLis, "regx") ||
		fpp.matchRegxLis(fileContent, regxPhpDynamicFunction, "regx") ||
		fpp.matchRegxLis(fileContent, phpDdosCcKeywords, "keyword") ||
		fpp.matchRegxLis(fileContent, phpDpJmKeywords, "keyword") ||
		fpp.matchRegxLis(fileContent, regxPhpIncludeFile, "regx") ||
		fpp.matchRegxLis(fileContent, regxPhpPackShell, "regx") || fpp.matchRegxLis(fileContent, regxWebShell, "regx") {
		return true
	}
	return false
}

流式读取文件内容 防止读取大文件 CPU冲高

func (fpp *WebShellProcessor) DetectFileContentByFlow(fileName string) bool {
	txtFile, err := os.OpenFile(filepath.Clean(fileName), os.O_RDONLY, 0600)
	if err != nil {
		util.HandleErr(err)
		return false
	}
	defer func() {
		if err := txtFile.Close(); err != nil {
			util.HandleErr(err)
		}
	}()
	bufReader := bufio.NewReader(txtFile)
	var fileContent strings.Builder
	step := 0
	for {
		data, _, err := bufReader.ReadLine() // 读一行日志
		if err == io.EOF {                   // 如果列表读完了,退出
			if fpp.matchWebShellRegx(fileContent.String()){
				return true
			}
			return false
		}
		fileContent.WriteString(string(data))
		step = step + 1
		if step == 50 {
			if fpp.matchWebShellRegx(fileContent.String()){
				return true
			}
			time.Sleep(500*time.Millisecond)
			step = 0
			fileContent.Reset()
		}
	}
}

爆破测试方法

hydra工具爆破命令:      hydra -l 用户名 -P passlist.txt ssh://ip

性能测试命令

测试命令
ps -ef |grep "proName"         查看名称还有proName的进程
top -p pid                     查看对应pid进程的cpu
cat /proc/20705/status         查看对应pid进程占用的内存

进入容器命令

docker exec -it 容器ID /bin/bash

linux系统相关:

查看登录记录   /var/log/secure
查看网络接口状态  每次网络接口状态变化都会在 /var/log/messages 中记录
记录sftp登陆信息
Subsystem       sftp    /usr/libexec/openssh/sftp-server -l VERBOSE
Subsystem       sftp    internal-sftp -l VERBOSE
重启服务:systemctl restart sshd.service

复制宿主机文件到虚拟机

scp /home/admin/main admin@ip:/home/admin/main
cp /home/admin/ls admin@ip:/home/admin/ls

查看二进制文件的可打印字符串

"strings " + filePath + " | grep -w " + feature

容器目录
/var/lib/docker/containers

流量检测引擎原理
HIDS入侵检测相关实现内容记录 相关技术整理_第1张图片

你可能感兴趣的:(web安全,安全威胁分析)