这篇文章发表于 839 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

如何找到网站上的木马?当然可以使用关键字匹配这种方法,比如使用D盾之类的,但这里推荐对流量简单检查来查出木马文件,因为要制作一个静态免杀马还是比较容易的。

本文讨论的前提假设是木马文件已经实现静态免杀并且对流量进行了非常好的加密混淆这样的情况。下面的部分侧重于对连接时产生的流量的分析,至于返回流量其实亦是同理。

木马流量大赏

先来看看各种马子连接时会产生的流量(我用的是antsword连接的,之前已经修改过部分明显特征)。

  • 连编码都没的

    捕获

  • 单层base64(自动脑补其他chr和rot之类的流量)

    捕获2

  • RSA加密

    捕获3

  • 本人使用的加密

    捕获4

如何通过流量反木马

可见,前两种是非常憨憨的,你能直接一眼看出来有奇奇怪怪的命令语句混在其中,还是挺好防的;而后面两者你已经完全不知道这里的流量是啥了,只能够看见一开始有一个入参(也就是木马所谓的连接密码,像这里的ant和eat,但是这个入参并不属于木马的特征)——更有甚者不用get或是post方法,而是直接通过cookie头或是header里面的其他部分来传入命令,堂而皇之地实现绕过(还有一种直接逃逸打进底层的,这里不予讨论)。。

在源码层面已经实现了免杀,而且加密这样羞耻,那么这种东西该怎么防御呢?

思路简单粗暴,直接通过“一定时间内大量流量+异常文件+异常请求地址”来实现定位。

wireshark

捕获5

就像这样,你看到了HTTP请求,然后对应了一个莫名其妙的PHP文件,而且流量内容奇怪,并且发送请求的地址非常不友好(这里显然是实验条件所限,假设那是个不友好的地址。。),然后就可以好好看看那个PHP文件的内容,很可能就不对劲。

ps:Linux上可以使用tshark或者tcpdump。

scapy

如果有scapy+tcpdump,也能够通过

1
sniff(filter='tcp and port 80',lfilter=lambda p:'POST' in str(p),prn=lambda x:x.show())

捕获6

找到了命令的来源IP地址,不过可以直接找得更准确一点。

1
sniff(filter='tcp and port 80',lfilter=lambda p:'POST' in str(p),prn=lambda x:hexdump(x.lastlayer()))

捕获7

当开启嗅探之后如果存在木马上线或是活动的情况就会直接暴露出相关的数据。对应的木马文件路径也就在上面的第一个白圈。。。以上的检测方式都是针对POST方法的检测,至于GET或是直接用cookie控制的方法,也完全同理。

于是乎就拿下了,要感兴趣的甚至可以直接写一个Python脚本来实现部分自动化了,人工检查的部分必然是需要的,不然可能会有一点几率误报2333,虽然几率非常非常非常小(对于一般的站来说)。

利用Python脚本实现检测

思路明确,实现起来就不困难。下面以对抗流量为POST类的马来进行讨论——

我的环境是python3+tcpdump+scapy[complete]。(假设我方网站的有POST方法的页面都存在已久)

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
43
44
import re
import time
import os
import threading
from scapy.all import *
captures=[]
threats=[]
#pcaps=[]

def deal(x): #这里将POST流量全部记录下来
if (x!=None) and (x!='None'):
captures.append(str(x))
#pcaps.append(str(x))
def catch(): #这里抓获POST流量
packets=sniff(filter='tcp and port 80',lfilter=lambda p:'POST' in str(p),prn=deal)

#首先找到24小时内创建的文件(我默认新文件可疑),其中/app/public/为网站的根目录
lines=os.popen('grep -rl "<?" /app/public/ | find /app/public/ -ctime 0').readlines()
for line in lines:
a=line[line.rfind('/'):line.rfind('\\')]
if a!='/':
threats.append(a) #这里将所有新文件名格式化处理后放入可疑列表

#开启抓流量的线程
c=threading.Thread(target=catch,args=())
c.setDaemon(True)
c.start()

with open('/root/threatens.txt','w') as f: #建一个记录威胁的文件
while True:
t=False
for capture in captures:
for threat in threats:
if threat in capture: #POST特征流量与新文件相匹配后将该文件写入威胁文件中
f.write(threat) #每次POST流量请求,都会被记录一次,之前的不计
f.write('\n')
print(threat)
t=True
#if t: #这里灵活性较大,你可以将对方的流量保存下来
#for pcap in pcaps: #也可以将电脑关机以保护(推荐2333)
# f.write(pcap) #当然还可以移除威胁文件之类的,或者ban了对方IP
# f.write('\n') #下一部分番外的操作主要就改动这里了
#os.popen('shutdown now')
captures=[] #防止流量包过大,检查完成后直接丢弃

可见这里的操作响应还算及时,对方上传了这种类型的马并且迫不及待地使用后,就会被发现,并且我方相关的保护操作就会及时跟进——如果我方不选择关机的话,可以尝试直接ban对方IP,并移除相关文件。注意:我并没有按照文件后缀(像.php这样)来进行排除,因为要防其他后缀的马。并且不难发现,这个脚本并没有让木马彻底失效,只是让其成为了“一次性”木马。

你可能会问为什么不直接在电脑上运行删掉新创建文件的脚本呢,因为本文主要是想介绍下抓流量的方法,顺带以这样的场景为例啦2333……

番外:概念型回手掏操作

其实呢,我们可以不选择关掉电脑,,,然后想办法修改甚至直接替换对方上传的木马内部的内容,来实现让对方一直报错却找不到问题所在,或是一直给对方发垃圾数据(要是知道对面的解密方式假数据也行),甚至直接getshell反杀。好刺激哦,嘿嘿嘿。。。

以下操作是针对antsword上古版本的XSS漏洞实现的,原理在这位老哥的文章中讲的很明白,新版里面早就修复了。

虽然已经修复已久,但还是假装这是一个0day,对之前的脚本稍加修改2333。我的想法是通过脚本直接替换那个木马内容——

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
import re
import time
import os
import threading
from scapy.all import *
captures=[]
threats=[]
fightback=[]

def deal(x):
if (x!=None) and (x!='None'):
captures.append(str(x))
def catch():
packets=sniff(filter='tcp and port 80',lfilter=lambda p:'POST' in str(p),prn=deal)

lines=os.popen('grep -rl "<?" /app/public/ | find /app/public/ -ctime 0').readlines()
for line in lines:
a=line[line.rfind('/'):line.rfind('\\')]
if a!='/':
threats.append(a)
c=threading.Thread(target=catch,args=())
c.setDaemon(True)
c.start()

with open('/root/threatens.txt','w') as f:
while True:
t=False
for capture in captures:
for threat in threats:
if threat in capture:
f.write(threat)
f.write('\n')
fightback.append(threat)
t=True
if t:
for x in fightback:
os.popen('cp /root/fightback.php /app/public'+x)
captures=[]

并在/root/fightback.php中添加以下内容:

1
2
<?php
header('HTTP/1.1 500 <img src=# onerror=alert(1)>');

然后第一次用antsword连接木马的时候还行,第二次就被XSS了(漏洞修复后是报错)。。

这样一个概念型的回手掏操作意味着什么?这意味着——无论你一开始属于攻还是受,一旦你能够控制对方的程序输入参数,并且对方没有任何过滤等防护措施,你就能够轻轻松松。

再针对那种直接手打的家伙,就更有意思,将/root/fightback.php中的内容替换为:

1
2
<?php
header(location:phish.php)

phish.php里面的内容自己发挥吧,比如利用反向社会工程让对面下载一些好玩的东西。

番外:对脚本的反思(自问自答)

虽然这个脚本能够完成一大部分任务了,对付大多数杂鱼坏人已经绰绰有余了,但是这里还是有几点地方值得考虑:

1、通过其他传入的参数来触发的木马怎么对付?

​ 很简单,针对各种传入检测各种包即可。

2、这里的脚本只是检测了流量却没有任何拦截的措施,能否加强?

​ 应该是可以的,不过好像有点麻烦,懒得搞了。拦截措施有了的话,我们就能够让木马直接变成“死”木马而不再是所谓的“一次性”木马。

3、有没有可能网站上一些正常的网页误触发脚本的反制措施?

​ 只要那些网页不会进入可疑文件名单,就不会受到干扰,从假设+脚本来看,应该不存在问题。

4、对方直接修改正常的网页加马怎么办?

​ 这种情况我的一个简单粗暴的解决建议是备份之前的网页文件,然后在反制的时候,直接将其备份的拷贝过去。

5、可疑文件的匹配是否会存在遗漏的情况?

​ 会!这里的“<?”存在绕过的可能性,具体要看PHP版本,并且还要针对utf-16be格式的进行检测。。

6、对方能够完成像找文件等这样的操作吗?

​ 可以,因为这个脚本检测的是第一次的命令,而第一次命令并未被拦截。对方只要坚持不懈或是直接一套指令直接就在第一次运行完毕就能够直接完成这种任务。。。

7、对方在知道脚本内容的情况下,是否能在第一次操作时候改文件时间来躲避检测?

​ 不可能,首先要改时间就必须调用Linux或是windows内部像system这样的命令,这一点很可能在php.ini里面被禁了,即便是改了文件时间,木马内容也已经被修改了,没用了。当然对方尝试直接利用这个“一次性”木马去生成一个假时间的文件(我不知道怎么操作,但是这很可能有操作空间)还是可以躲避检测的。

8、对方直接使用了reverse_tcp等直接在tcp层通信的木马该咋办?

​ 不好对付,在ban的时候已经来不及了,但是补救的话个人觉得可以考虑直接封ip增大对方的成本或者直接iptables封禁出流量来恶心攻击者,算是挺有效的方法。

9、这个脚本是否存在比较严重的安全问题?

​ 是,被人知道的情况下比较危险,等于送了个文件上传漏洞。

由此可见这个脚本的不完善之处还有很多,但是思忖一下,稍加修改,上面自问自答中的问题应该都能够逐一解决。具体实现就不给出来了。都是我个人的鄙陋之见,肯定还存在一些未考虑的点或是自答有问题的地方,还请多多包涵。