当企业被攻击者入侵,系统被挂暗链、内容遭到恶意篡改,服务器出现异常链接、卡顿等情况时,需要进行紧急处理,使系统在最短时间内恢复正常。由于应急处理往往时间紧,所以尝试将应急中常见处理方法整合到脚本中,可自动化实现部分应急工作。应急脚本采用python2.0完成,由于所有需要执行的命令都是依靠ssh进行远程链接,所以在运行脚本之前,需要输入正确的主机ip地址、ssh远程连接端口、ssh远程登录账户、ssh远程登录密码。
一、脚本实现的主要功能
1、获取主机信息
获取的主机信息包括:主机ip地址、主机名、当前系统内核版本、当前系统版本、系统当前时间;
2、获取异常进程
获取异常进程主要是采用两种方式,第一种,通过执行netstat -antp获取当前主机存在哪些链接,并通过判断外部链接地址归属地,如果归属地不是中国,则会提取相关pid,并根据pid定位出文件所在位置。第二种,通过cpu占有率,一旦发现cpu占用率高于%15时,会提取对应程序的pid,根据pid定位异常文件位置。
3、判断常见命令是否被篡改
在之前的应急响应中出现过常见命令被非法篡改情况,如ps、netstat命令被恶意替换,利用stat查看文件详细信息,通过比对时间的方式判断命令是否被篡改。
4、查看系统启动项
很多恶意程序会修改系统启动项,这样即使系统进行重启时,恶意程序也能自动启动,查看init.d目录下的启动文件,根据修改时间提取最近被修改的启动文件,并根据时间排序列出前5个。
5、查看历史命令
查看.bash_history历史命令,通过匹配关键字,如wget、cur等,来查看系统在被入侵后是否被执行了恶意操作。
6、判断非系统默认账户
恶意程序可能会在系统中新建账户,通过查看login.defs文件获取最小uid,从而根据uid查看passwd文件,获取之后新建的系统用户。
7、获取当前登录用户
通过调用who,查看当前登录用户(tty为本地登录,pts为远程登录),判断是否存在异常用户登录情况。
8、查看系统当前用户
通过查看etc/passwd,查看相关用户信息,确定是否存在异常用户。
9、查看crontab定时任务
查看/etc/crontab定时任务,并将输出结果保存到log中
10、查看、保存最近三天系统文件修改情况
通过find命令,查找最近三天修改过的文件,由于修改的系统文件较多,所以修改文件被单独保存在本地file_edit文件中
11、查找特权用户。
查看passwd文件,查找用户id为0的特权用户
12、secure日志分析
日志分析是应急的重头工作,尤其是在应急后期的溯源阶段,日志分析更显得尤为重要,由于日志种类包括服务器日志、应用日志,此处只是分析了secure服务器日志,提取日志的ip地址进行判断,并对ip归属地进行判断,查看的secure日志单独保存在本地secure中。
脚本整体的思路比较简单,就是远程登录到linux执行常见的应急命令,脚本中的命令在centos下都是可正常运行的,可以在根据实际环境自行在对命令做调整。上面的部分功能如果有好的实现方法也可灵活调整,如判断常见命令是否被篡改,脚本中是根据时间进行判断,在实际应用中也可根据文件大小进行判断,代码整体写的比较渣,有意见欢迎大家多多指出,希望通过改进能让功能更加完善。
#!/usr/bin/env python
# -*- coding:utf-8 -*-
'''
#作者:tide_应急小组
# 版本:v1.0
应急自动化分析脚本,采用python2.0,主要是通过ssh远程登录实现常见命令查看,所以在使用之前需要输入正确的服务器ip、ssh连接端口、ssh登录用户名、ssh登录密码,
主要实现如下功能:
1、获取系统基本信息,ip地址,主机名称,版本;
2、根据netstat、cpu占用率,获取异常程序pid,并定位异常所在路径;
3、常见系统命令可能会被恶意文件替换修改,识别常见系统命令是否被修改;
4、查看系统启动项目,根据时间排序,列出最近修改的前5个启动项
5、查看历史命令,列出处存在可疑参数的命令;
6、查看非系统用户;
7、查看当前登录用户(tty 本地登陆 pts 远程登录);
8、通过查看passwd文件,确定系统当前用户
9、查看crontab定时任务
10、查看、保存最近三天系统文件修改情况
11、查看passwd,存在哪些用户id为0的特权用户
12、分析secure日志,判断其中是否存在异常ip地址
上述所有操作均输出保存在log文件中
'''
import requests
import paramiko
import re
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # 跳过了远程连接中选择‘是’的环节,
ssh.connect('192.168.157.139', 22, 'root', 'abc-123')
# 通过netstat、top确定异常程序pid,通过pid定位异常程序位置
class netstat_Analysis:
def __init__(self):
self.pid=' '
# 获取网络链接,并查看是否存在异常
def check_netstat(self):
try:
ip=set()
command="netstat -antp | grep 'ESTABLISHED \| SYN_SENT \|SYN_RECEIVED' | awk '{print $1,$5,$7}' "
command1="netstat -antp"
stdin, stdout, stderr = ssh.exec_command(command)
info= stdout.read().splitlines()
stdin, stdout1, stderr = ssh.exec_command(command1)
info1 = stdout1.read()
file2.write(info1)
return info
except Exception, e:
print str(e)
# 判断ip地址归属地,看是否是异常连接
def check_yichangip(self):
yichang=list()
remot_ip=set()
result=self.check_netstat()
try:
for net in result:
# print net
ip = str(net).split(' ')[1].split(':')[0]
pid = str(net).split(' ')[2].split('/')[0]
application = str(net).split(' ')[2].split('/')[1]
remot_ip.add(ip)
for i in remot_ip:
URL = 'http://ip.taobao.com/service/getIpInfo.php?ip='+i
try:
r = requests.get(URL, timeout=3)
except requests.RequestException as e:
print (e)
else:
json_data = r.json()
if json_data[u'code'] == 0:
country= json_data[u'data'][u'country'].encode('utf-8')
# print country
keyword=['中国','局域网','共享地址','本机地址','本地链路','保留地址','XX']
if country not in keyword:
wrong_ip=i
info= "异常链接ip:"+wrong_ip+',ip归属地:'+country
file.write(info)
# print info
command1="netstat -antp | grep "+wrong_ip+" | awk '{print $1,$5,$7}' "
stdin, stdout1, stderr = ssh.exec_command(command1)
info1= stdout1.read().splitlines()
# print type(info1)
yichang=yichang+info1
return yichang
else:
file.write('\n'+"查看netstat未发现国外ip地址链接"+'\n')
else:
print '查询失败,请稍后再试!'
except:
print "暂时未发现异常链接"
# 对异常pid进行检测,定位文件位置
def jiance_pid(self):
try:
# pid=1
command1 = "ls -l /proc/"+self.pid
stdin, stdout, stderr = ssh.exec_command(command1)
info = stdout.read().splitlines()
mulu=str(info[9]).split(' ')[10:]
kezhixingwenjian=str(info[11]).split(' ')[10:]
yichangtixing= "pid 为"+self.pid+"的异常程序,存在位置为:"+mulu[0]+",可执行文件为"+kezhixingwenjian[0]+",请手工进行验证。"
file.write(yichangtixing+'\n')
print yichangtixing
except:
print "未发现异常行为"
# 检测cpu高于15%的程序,并获取其pid,查看是否为异常进程
def check_cpu(self):
command_cpu = "ps -aux | sort -nr -k 3 | head -5 | awk '{if($3>=15) print $2,$3}'"
stdin, stdout, stderr = ssh.exec_command(command_cpu)
yichang_cpu = stdout.read().splitlines()
try:
for n in range(len(yichang_cpu)):
self.pid=yichang_cpu[n].split(' ')[0]
# print self.pid
info= "pid 为"+self.pid+"的程序,cpu占用率为"+yichang_cpu[n].split(' ')[1]
file.write(info+'\n')
print info
self.jiance_pid()
except Exception, e:
print str(e)
# 通过netstat查看链接,获取异常pid的程序位置
def ps_aux(self):
a = self.check_yichangip()
if a!=None:
try:
for i in a:
# print i
self.pid = str(i).split(' ')[2].split('/')[0]
self.jiance_pid()
except Exception, e:
print str(e)
else:
print "查看netstat未发现异常ip地址"
# 有些恶意程序会替换ps,netstat命令,此处用来判断系统命令是否被替换(此处判断bin目录下命令)
class check_mingling:
def __init__(self):
stdin, stdout, stderr = ssh.exec_command("stat /bin/mkdir")
info = stdout.read().splitlines()
if __name__ == '__main__':
self.time=info[5].split(' ')[1].split('-')[0]
def mingling(self):
command=['ps','netstat']
for i in range(len(command)):
try:
command1 = "stat /bin/"+command[i]
stdin, stdout, stderr = ssh.exec_command(command1)
info1 = stdout.read().splitlines()
time=info1[5].split(' ')[1].split('-')[0]
except:
print "所查询命令不在bin目录下"
# print time,self.time
# print int(self.time),int(time)
try:
if int(self.time)<int(time):
info1= command[i]+' 命令存在异常,修改时间为:'+info1[5].split(' ')[1]
file.write(info1+'\n')
print info1
else:
print '未发现'+command[i]+'命令存在异常'
except:
print '暂无异常命令'
# 检查系统启动项目,查看init.d文件
class check_init():
def get_init(self):
try:
command1 = "ls -lth /etc/rc.d/init.d | sed -n '2,6p'"
stdin, stdout, stderr = ssh.exec_command(command1)
info1 = stdout.read()
tishi="根据最近修改时间,系统启动项前5名如下:"
if len(info1) !=0:
info=(tishi+'\n'+info1)
file.write(info+'\n')
else:
print "查看默认启动项时,查看路径与实际不符合,请根据情况自行修改commad1中路径"
except Exception, e:
print str(e)
#历史命令查看
class check_history():
def get_history(self):
try:
command1 = "cat /root/.bash_history | grep -a 'wget\|curl\|rpm\|install\|tar\|zip\|chmod\|rm\|mv'"
stdin, stdout, stderr = ssh.exec_command(command1)
info1 = stdout.read()
print '通过查看历史命令发现以下可疑命令,需要进行手动确认'+'\n'+info1
file.write('通过查看历史命令发现以下可疑命令,需要进行手动确认'+'\n'+info1)
except Exception, e:
print str(e)
# 获取主机信息
class Host_Info:
def __init__(self):
self.ip = ""
# 主机名
self.hostname = ""
# 主机ip
self.ip = ""
# 主机内核版本
self.version = ""
#主机系统版本
self.version1=""
# 主机时间
self.time = ""
def get_ip(self):
try:
command1 = "ifconfig -a | grep -w inet | grep -v 127.0.0.1 | awk 'NR==1{print $2}'&&hostname&&uname -a &&date&&cat " \
"/etc/issue| awk 'NR==1' "
stdin, stdout, stderr = ssh.exec_command(command1)
info=stdout.read().splitlines()
# print info
self.ip=info[0]
self.hostname=info[1]
self.version=info[2]
self.time=info[3]
self.version1=info[4]
info_write='当前系统IP地址为:'+self.ip+'\n'+'当前系统主机名称为:'+self.hostname+'\n'+'当前系统内核版本为:'+self.version+'\n'+"当前系统版本:"+self.version1+'\n'+'当前系统时间为:'+self.time+'\n'
file.write(info_write)
print info_write
except Exception, e:
print str(e)
# 获取当前用户情况
class check_user:
def __init__(self):
self.user=""
self.nosystemuser=""
self.current_yonghu=""
def get_user(self):
command1 = ["uid=$(grep UID_MIN /etc/login.defs | awk '{print $2}') &&gawk -F: '{if ($3>='$uid' && $3!=65534) {print $1}}' /etc/passwd","who","cat /etc/passwd"]
try:
stdin, stdout, stderr = ssh.exec_command(command1[0])
if len(self.nosystemuser) ==0:
self.nosystemuser='无'
else:
self.nosystemuser = stdout.read()
except:
print "查看非系统用户时出错"
try:
stdin, stdout, stderr = ssh.exec_command(command1[1])
self.current_yonghu=stdout.read()
except:
print "查看当前登录用户出错"
try:
stdin, stdout, stderr = ssh.exec_command(command1[2])
self.user=stdout.read()
except:
print "查看用户信息出错"
# print self.user
text='\n'+'存在以下非系统用户:'+'\n'+self.nosystemuser+'\n'+"当前登录用户如下:"+'\n'+self.current_yonghu+'\n'+'系统当前用户信''息如下:'+'\n'+self.user
print text
file.write(text)
class find_filerevise():
def get_filerevise(self):
try:
command2 = "find ./ -mtime 0 -o -mtime 1 -o -mtime 2 -o -mtime 3"
stdin, stdout, stderr = ssh.exec_command(command2)
info2 = stdout.read()
file1.write('近三天修改文件如下:'+'\n'+info2)
except Exception, e:
print str(e)
#查看、保存当前定时任务
class find_crontab():
def get_crontab(self):
try:
command3 = "cat /etc/crontab"
stdin, stdout, stderr = ssh.exec_command(command3)
info3 = str(stdout.read()).strip()
print 'crontab当前定时任务如下:'+'\n'+info3
file.write('crontab当前定时任务如下:'+'\n'+info3)
except Exception, e:
print str(e)
#排查提权账户
class find_gid():
def get_gid(self):
user_list=set()
try:
command4 = "cat /etc/passwd"
stdin, stdout, stderr = ssh.exec_command(command4)
info3 = stdout.read().splitlines()
# print(info3)
for i in info3:
# print(i)
gid = str(i).split(':')[3]
# print(type(gid))
if gid == str(0):
user = str(i).split(':')[0]
user_list.add(user)
if len(user_list) !=0:
print 'gid为0账户如下:'+str(user_list)
file.write('\n' + 'gid为0账户如下:')
for i in user_list:
file.write('\n' +i)
else:
print "不存在uid为0的特权用户"
except Exception, e:
print str(e)
#查看日志文件,判断secure日志是否存在异常,由于日志查看的内容比较多,此处只是对secure日志中ip地址进行分析,判断是否存在国外异常ip归属
class check_log():
try:
def cat_log(self):
command="cat /var/log/secure"
stdin, stdout, stderr = ssh.exec_command(command)
info3 = stdout.read()
if len(info3)!=0:
file4.write(info3)
file4.close()
ip_list=set()
for i in open("secure.txt",'r').readlines():
ip = re.findall(r'(?<![\.\d])(?:\d{1,3}\.){3}\d{1,3}(?![\.\d])', str(i), re.S)
if len(ip) !=0:
ip_list.add(ip[0])
else:
pass
for i in ip_list:
URL = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + i
try:
r = requests.get(URL, timeout=3)
except requests.RequestException as e:
file.write('在secure日志中未发现异常ip地址' + '\n')
print(e)
else:
json_data = r.json()
if json_data[u'code'] == 0:
country = json_data[u'data'][u'country'].encode('utf-8')
keyword = ['中国', '共享地址','局域网', '本机地址', '本地链路', '保留地址','XX']
if country not in keyword:
print '在secure日志中发现如下异常ip地址:'+'\n'+i+" 地址归属地"+country+'\n'
file.write('在secure日志中发现如下异常ip地址:'+'\n'+i+" 地址归属地"+country+'\n')
else:
pass
else:
print "系统未发现存在secure日志,请根据实际情况修改所需要查看的日志路径"
except:
print "系统未发现存在secure日志"
if __name__=='__main__':
'''
输出结果默认保存在log.txt中,查看最近3天文件的修改情况默认保存在file_edit.txt中,
通过netstat查看链接情况默认保存在netstat.txt中,secure日志的查看情况保存在secure.txt中
'''
file = open("log.txt", 'a')
file1=open("file_edit.txt",'w')
file2=open("netstat.txt",'w')
file4 = open("secure.txt", 'w')
e = Host_Info()
e.get_ip()
a=netstat_Analysis()
a.ps_aux()
a.check_cpu()
b=check_mingling()
b.mingling()
c=check_init()
c.get_init()
d=check_history()
d.get_history()
f=check_user()
f.get_user()
g = find_filerevise()
g.get_filerevise()
h = find_crontab()
h.get_crontab()
i = find_gid()
i.get_gid()
j=check_log()
j.cat_log()
声明:本文为原创文章,版权归lsh4ck's Blog所有,欢迎分享本文,转载请保留出处!