监控提权命令之audit

监控提权命令之audit
  这个需求源于8月份的系统安全加固的内容,一开始领导希望能找到工具对linux系统日志进行分析,其中一个是提权指令记录及告警。这两天经过我跟ai的努力,还有今天前公司运维朋友的鼎力相助,算是完工了。
  先看看最终效果,我用了两个用户去运行sudo 指令进行测试,然后告警:

    利用了linux自带的audit审计。老实说,我对这个东西挺陌生的,这两天差点把我劝退。。。一开始想用inotifywait去做,发现只能记录root执行的提权命令,然后想到用户家目录下的.bash_history去拿最近日志,感觉还是不够audit靠谱,所以硬着头皮研究吧。

  提下关键点,下面脚本很多都是ai写的(尤其它写的正则,强大),当然我也花了很多功夫帮它纠正代码逻辑,验证它写的内容,算是互相成就吧,哈哈哈~~不然自己写可能不止搞一天哦,本人代码能力比较弱。

 

1、预先设置含关键字的audit规则,然后才能记录到审计日志

  centos 7 编辑这个文件 /etc/audit/rules.d/audit.rules ,规则内容如下(-k 标识记录日志的关键字,下面搜索会用到),标识记录普通用户运行sudo时的审计日志。

-a always,exit -F arch=b64 -S execve -F uid!=0 -F exe=/usr/bin/sudo -k sudo-exec
-a always,exit -F arch=b64 -S execve -F uid!=0 -F exe=/bin/sudo -k sudo-exec

-a always,exit -F arch=b64 -S execve -F uid!=0 -F exe=/bin/su -k sudo-exec
-a always,exit -F arch=b64 -S execve -F uid!=0 -F exe=/usr/bin/su -k sudo-exec

  设置完规则需要重启audit服务,还有如果写的规则有问题,会发现“auditctl -l” 是没有新添加的规则,所以最好写入这个文件前,命令行先验证规则是否正确,例如:

auditctl -a always,exit -F arch=b64 -S execve -C euid=0 -F euid!=0 -k sudo-exec

   另一个问题是ai一直给我写条件规则:euid!=0来排除root用户,这个是错误的,需要用uid去判断

2、提取关于提权命令的日志

  一开始我看/var/log/audit/audit.log,可读性根本不是人类能够适应的。然后找到工具ausearch,利用规则关键字来搜索日志记录,格式类似这样:“ausearch -k 关键字”

然后发现一运行,满屏都是日志,所以要加条件去限制,例如最近x分钟。目前找到的最小粒度是最近10分钟内的日志。写法是:

ausearch --start recent --end now -k 'sudo-exec'

   老实说,用ausearch搜的日志比直接在audit.log可读性强太多,每个日志段用“----”分割,接着就是从每个日志段提取我们需要的字段内容。

 

 3、日志分析

  包括字段提取,和数组存值。每个日志段我们需要用对应数组去存(用户、具体命令、当前所在路径),因为后续要遍历进行钉钉告警。除此我们需要处理以下的点

(1)time->Tue Sep 12 20:44:34 2023,需要利用时间戳来转格式

(2)提取命令在argc{n}这列,需要拼接a0~an-1里面“=”的内容

(3)提取操作用户,根据uid的值,读取/etc/passwd 去匹配

4、钉钉告警用json拼接消息

  这个大家直接看脚本,我以前那种写法报警不了,可能是因为操作命令里有空格导致的,这个有机会再研究

5、放到定时任务搜索日志内容为空

  ausearch搜关键字日志在命令行执行可以重定向到日志文件,但是放到定时任务竟然为空,全靠前公司运维同事的醍醐灌顶,结合“--input-logs”管道去处理,真是万分感激。

  1 #!/bin/bash
  2 
  3 # 定义空数组来存储事件数据
  4 timestamps=()
  5 users=()
  6 commands=()
  7 cwds=()
  8 
  9 ausearchlog=/root/ausearch.log
 10 
 11 DATE=`date +%F_%T`
 12 >$ausearchlog
 13 # 找到10分钟前到现在的日志并重定向输出到文件
 14 # 写不进去日志: /sbin/ausearch --start recent --end now -k 'sudo-exec' > $ausearchlog 2>&1 
 15 # 搜不到内容,可能跟时间差有关: /sbin/ausearch --start recent --end now -k 'sudo-exec' -f /var/log/audit/audit.log > $ausearchlog
 16 ### 可用写法
 17 /sbin/ausearch --start recent --end now -k 'sudo-exec' --input-logs |tee -a $ausearchlog 
 18 
 19 
 20 # 循环读取日志文件
 21 while IFS= read -r line; do
 22   # 提取时间:
 23   if [[ $line =~ ([a-zA-Z]{3} [a-zA-Z]{3} [0-9]{1,2} [0-9:]{8} [0-9]{4}) ]]; then
 24     timestamp="${BASH_REMATCH[1]}"
 25     # 转换时间格式
 26     formatted_timestamp=$(date -d "$timestamp" +%Y-%m-%d_%H:%M:%S)
 27     timestamps+=("$formatted_timestamp")
 28     echo "时间: $formatted_timestamp" 
 29   fi
 30 
 31   # 提取操作用户所在目录:
 32   if [[ $line == *"type=CWD"* ]]; then
 33     cwd=$(echo "$line" | awk -F"cwd=" '{print $2}' | awk '{print $1}' | tr -d '"' )
 34     cwds+=("$cwd")
 35     echo "用户当前所在目录:$cwd"
 36   fi
 37 
 38   # 提取操作命令:
 39   if [[ $line =~ argc=([0-9]+) ]]; then
 40     argc="${BASH_REMATCH[1]}"
 41     command_args=()
 42 
 43     # 使用循环提取 a0 到 an-1 的内容
 44     for ((i = 0; i < argc; i++)); do
 45       arg_name="a$i"
 46       if [[ $line =~ $arg_name=\"([^\"]+)\" ]]; then
 47         arg_value="${BASH_REMATCH[1]}"
 48         command_args+=("$arg_value")
 49       fi
 50     done
 51 
 52     # 将命令参数拼接,并以空格分隔
 53     command="${command_args[*]}"
 54     echo "运行的命令: $command"
 55 
 56     # 添加命令到数组
 57     commands+=("$command")
 58   fi
 59   
 60   # 提取操作用户:
 61   if [[ $line == *"type=SYSCALL"* ]]; then
 62     #userid=$(echo "$line" | awk -F"uid=" '{print $2}' | awk '{print $1}' )
 63     userid=$(echo "$line" | grep -o '\buid=[0-9]\+\b' | awk -F'=' '{print $2}')
 64     echo "userid = $userid"
 65     user=$(grep "$userid" /etc/passwd | cut -d: -f1)
 66     users+=("$user")
 67     echo "操作用户:$user"
 68   fi
 69   
 70   # 分隔符行,调试每个日志段时为了好看
 71   if [[ $line == "----" ]]; then
 72     echo ""
 73   fi
 74 done < $ausearchlog
 75 
 76 # 统计事件数量
 77 cnt=${#timestamps[@]}
 78 echo "事件数量: $cnt"
 79 
 80 DATE=`date +%F_%T`
 81 TOKEN="钉钉机器人token"
 82 PHONE="报警人电话"
 83 
 84 # 输出事件信息
 85 for ((i = 0; i < $cnt; i++)); do
 86   timestamp="${timestamps[$i]}"
 87   user="${users[$i]}"
 88   command="${commands[$i]}"
 89   cwd="${cwds[$i]}"
 90   echo "(for循环)时间: $timestamp, 用户: $user, 命令: $command, 当前目录:$cwd" 
 91 
 92   
 93   #钉钉报警  --- 不行,以前我经常用这种方式
 94   #curl -H "Content-Type:application/json" -X POST --data '{"msgtype":"text","text":{"content":"服务器:xxx\n当前时间:'$DATE'\n操作时间:'$timestamp'\n操作用户:'$user'\n操作命令:'${command}'\n当前用户所在目录:'$cwd'"} , "at": {"atMobiles": ['${PHONE}'], "isAtAll":false }}' ${TOKEN} > /dev/null 2>&1
 95 
 96 # 构建 JSON 字符串并将结果保存到 json_msg 变量中,需要事先安装jq
 97 json_msg=$(/usr/local/bin/jq -n \
 98   --arg DATE "$DATE" \
 99   --arg command "$command" \
100   --arg PHONE "$PHONE" \
101  '{
102     "msgtype": "text",
103     "text": {
104       "content": "服务器:xxx\n当前时间:\($DATE)\n操作时间:'$timestamp'\n操作用户:'$user'\n操作命令:\($command)\n当前用户所在目录:'$cwd'"
105     },
106     "at": {
107       "atMobiles": [($PHONE | tonumber)],
108       "isAtAll": false
109     }
110   }')
111 
112 ## 使用 curl 发送 JSON 消息
113 curl -H "Content-Type: application/json" -X POST --data "$json_msg" "$TOKEN" > /dev/null 2>&1
114 
115 done
View Code

一些想法 

  最后说下需要完善的地方,例如,如果当前用户在自己家目录上,运行“sudo 自己家目录下的目录或者查看家目录下的文件”,这种我们不应该去告警,所以告警里,为啥我会加个当前用户目录,是想着后续做排除的;其次是这个脚本放定时任务里,每10分钟执行一次,能不能再缩小搜索时间,例如搜索最近5分钟内,感觉10分钟有点久

  另外,监控其他命令或者安全事件其实都可以从这个audit日志筛选,算是挺实时的,一运行完命令,ausearch就能搜到

 

 

posted @ 2023-09-12 23:30  windysai  阅读(103)  评论(0编辑  收藏  举报