码迷,mamicode.com
首页 > 其他好文 > 详细

Automatic Risk Listening Port Detection And C&C Controller Identification

时间:2015-08-03 16:17:18      阅读:290      评论:0      收藏:0      [点我收藏+]

标签:

catalog

1. 风险端口覆盖的攻击面
2. IRC协议
3. C&C僵尸网络、中控程序、中控机
4. P2P组网方式
5. 端口监听隐藏技术
6. 风险端口监听自动化检测方案

 

1. 风险端口覆盖的攻击面

从入侵攻防的角度来说,风险端口主要存在以下攻击向量

1. C&C中控端
服务器遭到黑客入侵后,黑客为了隐藏自己的踪迹,将这台被入侵的机器作为跳板,上传并运行C&C中控端程序,通过监听指定端口来接收来自网络上其他肉鸡的上线连接

2. 恶意木马程序/部署后门
服务器遭到黑客入侵后,黑客在服务器上部署后门(例如nc),通过监听指定端口,以此留下一个Backdoor,方便后续的二次入侵、横向渗透

恶意程序、僵尸网络是一个很大的范畴,本文并不打算全部涵盖,本文的重点在于检测当前主机的异常状态,即自动化地检测出当前主机可能导致被入侵、或者已经被入侵的"异常特征状态",而并不是直接和恶意程序、僵尸网络进行对抗,从入侵的角度来看,如果遭到了黑客入侵,黑客往往会在服务器上部署后门程序,这些程序的特点就是监听(listening)某个端口,为攻击者后续二次入侵提供方便

0x1: 对抗的难点

1. 对于僵尸网络C&C中控端的异常检测,我们的切入点是检查当前主机是否有可疑的Listening端口,但是C&C中控端中有很多是P2P类型的组网模式,并不会表现出一个明显的Listening特性,对检测工作带来了挑战。但是这个问题也并不是绝对的,即使在P2P架构的僵尸网络组网中,依然也需要有一个中心服务器,担任Listening的角色(即引导服务器),它负责将"新成员"引导进当前管理下的僵尸网络中
2. 在对异常监听端口的检测中,常常会设置一个白名单,例如HTTP 80端口、FTP 21端口等,对这类端口直接放行,但是僵尸网络的C&C中控端同样可以采用标准化的HTTP协议进行组网和通信,即直接监听在80端口,并将控制指令和结果回显封装在HTTP数据包中,以此达到躲避检查的目的,这就对端口异常检测模型提出了更高的要求,安全研究员需要从更多维度去综合判断当前监听端口是否可疑
3. 对于端口检测技术,僵尸网络C&C中控端可以采取端口隐藏等对抗性技术,例如采用ICMP隐蔽通道进行数据通信,这种情况下C&C机并没有Listening端口
4. 对于使用UDP协议进行组网和通信的C&C程序,这种情况下C&C机并没有Listening端口

Relevant Link:

http://www.0553114.com/news/detail-704088.html
http://www.programgo.com/article/24605262708/

 

2. IRC协议

0x1: 简介

IRC是一种分布式的主从式架构(即服务端对客户端进行服务的网络即时通讯协议,或者说方式)。IRC是一种公开的协议,采用TCP和SSL协议。通常用户连接到大量IRC服务器扩展的IRC网路。通过连接到一个IRC服务器,可以访问这个服务器以及它所连接的其他服务器上的频道,要使用IRC,必须先登陆到一个IRC服务器上,中文用户最多的IRC服务提供者为: irc.freenode.net
频道存在于一个IRC服务器上。一个频道类似于一个聊天室,频道名称必须以#符号开始,例如#irchelp

0x2: IRC中转

我们来做一个比较说明

1. 假设: A 与 B 要交谈
2. 如果不采用中转,那么 A 直接建立一条到达 B 的通信隧道,二者通过这条通信隧道进行信息交流,信息流的方向为:A->B 和 B->A
3. 如果采用中转,则需要有一个第三方来担任中转角色,设为 C
4. A 建立一条到达 C 的通信隧道,B 也建立一条到达C 的通信隧道,然后 A 与 B 通过 C 来间接进行通信,信息流的方向为:
    1) A->C->B
    2) B->C->A
C 就起着 A 与 B 间的中转站的作用
5. 中转的最大优点是使"群聊"能够方便地进行. 恰当地说,中转模式为"信息广播"提供了方便. 我们来举例子. 假设 A,B 和 D 三者要一起聊天. 如果没有 C 的中转,那么 A 要将所说的每句话分别发给 B 和D; 如果有 C 做中转,那么 A 将所说的话发给 C,然后 C 将 A 的话分别发给 B 和 D. 可见,当没有中转时,每个参与聊天的计算机都要执行信息广播的任务,当存在中转时,信息广播的任务全由中转者来执行.中转站 C 的存在使得信息交流过程中的工作任务发生分离,可以把网络环境好、机器配置高的计算机作为中转站来提供服务功能. 这就形成了IRC 的服务器-客户端 模型,聊天者作为客户端,连接到中转站服务器上,这就是IRC的原理和存在的意义

0x3: IRC协议结构

IRCP是一种具有很多命令的文本协议,其主要命令是

1. 用户 (用户名) (主机名) (服务器名) (真实名): 在连接初使用,详细说明新用户的用户名、主机名、服务器名及真实名
2. 通过 (口令):在设置"连接口令"时使用
3. 昵称 (昵称) (跳转): 给用户一个昵称或更换以前的昵称
4. 服务器 (服务器名) (跳转) (信息): 告诉服务器连接的另一个终端是服务器
5. 进入 (用户) (口令): 请求获得操作权
6. 停止 (停止消息): 用户会话以停止消息结束
7. 服务器停止 (服务器) (注释): 停止和终结服务器提示
8. 连接 (频道): 客户机开始收听特别频道
9. 主题 (频道): 改换或检查频道
10. 名字(频道): 列出所有的昵称使其他任何信道上的用户都可以看到
11. 目录 (信道): 列出信道及其主题
12. 删除 (信道) (用户) (注释): 强制性地从信道上删除用户

Relevant Link:

https://zh.wikibooks.org/zh-cn/IRC
http://baike.baidu.com/subview/10525/5909292.htm
http://net.anquan365.com/protocol/other/200701/28072.html
http://blog.shuo1.com/?p=1910
http://www.ietf.org/rfc/rfc1459.txt

 

3. C&C僵尸网络、中控程序、中控机

0x1: 僵尸网络

僵尸网络Botnet是指采用一种或多种传播手段,将大量主机感染bot程序(僵尸程序)病毒,从而在控制者和被感染主机之间所形成的一个可一对多控制的网络。 攻击者通过各种途径传播僵尸程序感染互联网上的大量主机,而被感染的主机将通过一个控制信道接收攻击者的指令,组成一个僵尸网络
僵尸网络是一种由引擎驱动的恶意因特网行为(它的组网、中控行为取决于Botnet的通信方式): DDoS攻击是利用服务请求来耗尽被攻击网络的系统资源,从而使被攻击网络无法处理合法用户的请求。DDoS攻击有多种形式,但是能看到的最典型的就是流量溢出,它可以消耗大量的带宽,却不消耗应用程序资源。DDoS攻击并不是新鲜事物。在过去十年中,随着僵尸网络的兴起,它得到了迅速的壮大和普遍的应用。僵尸网络为DDoS攻击提供了所需的"火力"带宽和计算机以及管理攻击所需的基础架构

技术分享

0x2: 网络特点

1. 是一个可控制的网络,这个网络并不是指物理意义上具有拓扑结构的网络,它具有一定的分布性,随着bot程序的不断传播而不断有新位置的僵尸计算机添加到这个网络中来。僵尸病毒被人放到计算机时会自动尝试和C&C控制端进行连接,连接成功后每隔一定时间进行心跳通知
2. 这个网络是采用了一定的恶意传播手段形成的,例如主动漏洞攻击,邮件病毒等各种病毒与蠕虫的传播手段,都可以用来进行Botnet的传播,从这个意义上讲,恶意程序bot也是一种病毒或蠕虫
3. 可以一对多地执行相同的恶意行为,比如可以同时对某目标网站进行分布式拒绝服务(DDos)攻击,同时发送大量的垃圾邮件等,而正是这种一对多的控制关系,使得攻击者能够以极低的代价高效地控制大量的资源为其服务,这也是Botnet攻击模式近年来受到黑客青睐的根本原因。在执行恶意行为的时候,Botnet充当了一个攻击平台的角色,这也就使得Botnet不同于简单的病毒和蠕虫 

技术分享

0x3: 中控程序分析

1. TFN(Tribe Flood Network)

TFN is made up of client and daemon programs, which implement a distributed network denial of service tool capable of waging ICMP flood, SYN flood, UDP flood, and Smurf style attacks, as well as providing an "on demand" root shell bound to a TCP port.
TFN daemons were originally found in binary form on a number of Solaris 2.x systems, which were identified as having been compromised by exploitation of buffer overrun bugs in the RPC services "statd", "cmsd" and "ttdbserverd".

https://staff.washington.edu/dittrich/misc/tfn.analysis

2. TFN2K/3K

https://packetstormsecurity.com/files/10537/TFN2k_Analysis.htm.html
https://packetstormsecurity.com/files/10525/tfn3k.txt.html
https://packetstormsecurity.com/distributed/page3/

3. Trinoo

https://staff.washington.edu/dittrich/misc/trinoo.analysis.txt
http://wenku.baidu.com/view/c44f9d68b84ae45c3b358c5a

4. SubSeven 2.1

http://www.megasecurity.org/trojans/s/subseven/Subseven2.1.5.html

5. IRC Botnet

An IRC bot is a set of scripts or an independent program that connects to Internet Relay Chat as a client, and so appears to other IRC users as another user. An IRC bot differs from a regular client in that instead of providing interactive access to IRC for a human user, it performs automated functions.

https://en.wikipedia.org/wiki/IRC_bot

6. Agobot/Gaobot

Agobot, also frequently known as Gaobot, is a family of computer worms. Axel "Ago" Gembe, a German programmer, was responsible for writing the first version. [1][2][3] The Agobot source code describes it as: “a modular IRC bot for Win32 / Linux”. Agobot was released under version 2 of the GNU General Public License. Agobot is a multi-threaded and mostly object oriented program written in C++ as well as a small amount of assembly. Agobot is an example of a Botnet that requires little or no programming knowledge to use.
Most Agobots have the following features:

1. Password Protected IRC Client control interface
2. Remotely update and remove the installed bot
3. Execute programs and commands
4. Port scanner used to find and infect other hosts
5. DDoS attacks used to takedown networks
6. Packet sniffer
7. Keylogger
8. Polymorphic code
9. Rootkit installer
10. Information harvest
11. Email Addresses
12. Software Product Keys
13. Passwords
14. SMTP Client
15. Spam
16. Spreading copies of itself
17. HTTP client
18. Click Fraud
19. DDoS Attacks

Spreading(传播方式)

The following propagation methods are sub-modules to the port scanning engine

1. MS03-026: RPC DCOM Remote Buffer Overflow
2. MS04-011: LSASS Remote Buffer Overflow
3. MS05-039: Plug and Play Remote Buffer Overflow
4. Attempts to hijack common Trojan horses that accept incoming connections via an open port.
5. The ability to spread to systems by brute forcing a login. A good example is Telnet or Microsofts Server Message Block
Generally, it has been observed that every custom modified variant of Agobot features a selection of the above methods as well as some "homebrew" modules, which essentially are released exploits ported to its code.

Relevant Link:

https://en.wikipedia.org/wiki/Agobot

7. P2P Botnet

http://cs.ucf.edu/~czou/research/P2PBotnets-bookChapter.pdf
https://www.usenix.org/legacy/event/hotbots07/tech/full_papers/grizzard/grizzard_html/index.html
http://network.51cto.com/art/201006/207932.htm
http://www.2cto.com/net/201306/221922.html

8. Agobot/Phatbot/Forbot/XtremBot

这可能是最出名的僵尸工具。防病毒厂商Spphos 列出了超过500种已知的不同版本的Agobot(Sophos 病毒分析),这个数目也在稳步增长。僵尸工具本身使用跨平台的C++写成。Agobot 最新可获得的版本代码清晰并且有很好的抽象设计,以模块化的方式组合,添加命令或者其他漏洞的扫描器及攻击功能非常简单,并提供像文件和进程隐藏的Rootkit 能力在攻陷主机中隐藏自己。在获取该样本后对它进行逆向工程是比较困难的,因为它包含了监测调试器(Softice 和O11Dbg)和虚拟机(VMware 和Virtual PC)的功能

9. GT-Bots

GT-Bots是基于当前比较流行的IRC客户端程序mIRC编写的,GT是(Global Threat)的缩写。这类僵尸工具用脚本和其他二进制文件开启一个mIRC聊天客户端, 但会隐藏原mIRC窗口。通过执行mIRC脚本连接到指定的服务器频道上,等待恶意命令。这类bot程序由于捆绑了mIRC程序,所以体积会比较大,往往会大于1MB

0x4: 工作过程

Botnet的工作过程包括三个阶段

1. 传播
一个Botnet首先需要的是具有一定规模的被控计算机,而这个规模是逐渐地随着采用某种或某几种传播手段的bot程序的扩散而形成的,在这个传播过程中有如下几种手段
    1) 主动攻击漏洞: 其原理是通过攻击系统所存在的漏洞获得访问权,并在Shellcode执行bot程序注入代码,将被攻击系统感染成为僵尸主机。属于此类的最基本的感染途径是攻击者手动地利用一系列黑客工具和脚本进行攻击,获得权限后下载bot程序执行。攻击者还会将僵尸程序和蠕虫技术进行结合,从而使bot程序能够进行自动传播,著名的bot样本AgoBot,就是实现了将bot程序的自动传播 
    2) 邮件病毒: bot程序还会通过发送大量的邮件病毒传播自身,通常表现为在邮件附件中携带僵尸程序以及在邮件内容中包含下载执行bot程序的链接,并通过一系列社会工程学的技巧诱使接收者执行附件或点击链接,或是通过利用邮件客户端的漏洞自动执行,从而使得接收者主机被感染成为僵尸主机 
    3) 即时通信软件: 利用即时通信软件向好友列表中的好友发送执行僵尸程序的链接,并通过社会工程学技巧诱骗其点击,从而进行感染,如2005年年初爆发的MSN性感鸡(Worm.MSNLoveme)采用的就是这种方式 
    4) 恶意网站脚本: 攻击者在提供Web服务的网站中在HTML页面上绑定恶意的脚本,当访问者访问这些网站时就会执行恶意脚本,使得bot程序下载到主机上,并被自动执行 
    5) 特洛伊木马: 伪装成有用的软件,在网站、FTP服务器、P2P网络中提供,诱骗用户下载并执行
2. 加入
在加入阶段,每一个被感染主机都会随着隐藏在自身上的bot程序的发作而加入到Botnet中去,加入的方式根据控制方式和通信协议的不同而有所不同
    1) 在基于IRC协议的Botnet中,感染bot程序的主机会登录到指定的服务器和频道中去,在登录成功后,在频道中等待控制者发来的恶意指令
    2) 还有另一种是基于P2P协议的僵尸网络加入方式
3. 控制 
在控制阶段,攻击者通过中心服务器发送预先定义好的控制指令,让被感染主机执行恶意行为,如发起DDos攻击、窃取主机敏感信息、更新升级恶意程序等 

0x5: Botnet控制方式

1. IRC Botnet
控制和通信方式为利用IRC协议的Botnet,形成这类Botnet的主要bot程序有
    1) spybot
    2) GTbot
    3) SDbot
目前绝大多数Botnet属于这一类别

2. AOL Botnet
与IRC Bot类似,AOL为美国在线提供的一种即时通信服务,这类Botnet是依托这种即时通信服务形成的网络而建立的,被感染主机登录到固定的服务器上接收控制命令
    1) AIM-Canbot
    2) Fizzer
就采用了AOL Instant Messager实现对Bot的控制 

3. P2P Botnet
这类Botnet中使用的bot程序本身包含了P2P的客户端,可以连入采用了Gnutella技术(一种开放源码的文件共享技术)的服务器,利用WASTE文件共享协议进行相互通信。由于这种协议分布式地进行连接,就使得每一个僵尸主机可以很方便地找到其他的僵尸主机并进行通信,而当有一些bot被查杀时,并不会影响到Botnet的生存,所以这类的Botnet具有不存在单点失效但实现相对复杂的特点
    1) Agobot
    2) Phatbot
采用了P2P的方式

4. SSL/加密SOCKET
客户端采取SSL Socket方式向中控端发起加密连接,并定时发送心跳表表示存活

5. HTTP协议组网
为了规避IPS对异常端口通信的检测,僵尸网络可以采取HTTP这类标准协议作为肉鸡和C&C中控端的通信方式

技术分享

0x6: 僵尸网络对抗技术

无论使用HTTP、IRC或是其他协议,传统僵尸网络的构成都是相同的。所有的僵尸主机(bot)都通过一个或多个域名连接到一个或多个服务器。尽管网络结构可能千差万别,或一目了然,或错综复杂,通过各服务器间良好的协作,都可以轻易摧毁僵尸网络
恢复被入侵的僵尸主机正常工作最常见的办法,就是夺取它所使用的域名或服务器的控制权。然而有些时候由于某些原因我们无法向僵尸主机发送指令,就需要另一种手段。当然尝试关闭命令与控制服务器(C&C Server)一般情况下都是浪费时间的做法,因为主控者(botmaster)不需要花多少时间,就可以找到另一个服务器,并将域名重定向到它上面(肉鸡的上线连接基本是通过域名访问的)。于是我们就只有在域名上做文章来应对了,僵尸网络通常会使用多个域名,当其中一个被关闭时,僵尸主机会连接到另一个。想要彻底破坏这样的僵尸网络,有两个办法

1. 暂时关闭所有和这个僵尸网络相关的域名(需要在短时间内完成,以防主控者操作僵尸主机定向到新的域名)
2. 把僵尸网络使用的域名定向到一个有特殊功能的服务器(如Sinkhole路由器),使僵尸主机远离正常的控制服务器,从而切断主控者对它们的控制 

Relevant Link:

http://baike.baidu.com/view/297306.htm 
http://security.tencent.com/index.php/blog/msg/32
http://www.mcafee.com/cn/resources/white-papers/wp-new-era-of-botnets.pdf

 

4. P2P组网方式

不同于传统僵尸网络在域名或服务器被夺取控制权时表现出的脆弱,P2P僵尸网络会建立一个相对分散的网络环境。思路就是所有的僵尸主机都互相连接并且进行通信,从而不再需要一个中心服务器

0x1: 指令

如果所有的僵尸主机都能互相通信,那么主控者需要确保只有他能向这些主机发送指令,通常的解决办法是数字签名。这个签名是由不对称加密算法生成的,需要两个密钥(公钥和私钥)。如果其中一个密钥用于加密消息,那么必须使用另一个来解密。于是主控者可以自己持有一个密钥(私钥),将另一个(公钥)发放到僵尸主机上。他使用私钥对指令进行加密,然后僵尸主机使用公钥解密。没有私钥,任何人都无法正确加密指令

0x2: 网络架构

那些使用NAT、防火墙,或者代理访问网络的计算机,无法接受连入的请求,它们只能向外发送消息,这会导致大多数僵尸主机无法被另一个直接连接。在传统僵尸网络中,这根本不是问题,因为僵尸主机都是连接到服务器的。因此在P2P网络中,我们仍然需要服务器,只不过形式不同罢了

技术分享

把那些可以接受连入请求的僵尸主机(没有使用代理、NAT或防火墙)作为服务器(通常称作节点),同时那些不能连入的主机(通常称作worker,暂译工作机)将向一个或多个节点发起连接,从而获取指令。尽管这些节点仅仅从技术上可以作为服务器,但是可以利用它们防止僵尸网络被摧毁。办法就是:所有的工作机在节点间是分布式连接的,这使它们在某个节点失效时,能够轻易转移到另一个节点上。有时关闭所有的节点是一件不切实际的事情,于是P2P僵尸网络得以持续工作。不幸的是,这些节点都是合法的普通计算机,它们可不能像服务器一样随意被我们控制
每个节点都维护着一个IP列表,它包含所有和自身有相同工作机的节点。然后工作机再获取这个列表,于是它们就能够在其中某个节点失效时转向另一个。在这个阶段,数个僵尸主机成为一组,并且连接到许多不同的节点,但整个僵尸网络还无法接受指令。为了使指令能够抵达网络中各台主机,需要僵尸主机连接到多个节点并把接受到的指令发送到其他节点,或者节点之间相互连接同时相互传递指令,亦或者把两者结合起来

技术分享

0x3: 引导增殖

要将一台主机拥入僵尸网络的怀抱,它至少需要获取一个节点的IP地址,这时引导增殖的过程便开始了。僵尸程序里硬编码了一系列可提供自身繁衍所需信息的服务器(引导服务器),当程序第一次在被感染的计算机上运行时,它就会连接到这些服务器。引导服务器的工作就是维护一个海量的节点地址,将其中一部分发放到僵尸主机上(把主机引入僵尸网络)。通常来说,引导服务器使用某种签名,防止被安全人员劫持,也避免提供无效的节点地址
显然引导服务器是中心要点,就像传统僵尸网络一样,他们可以被破坏,然而这也不算什么大不了的事。如果所有的引导服务器在一瞬间被全部拿下,这也不会影响到已经存在的僵尸主机,只是阻止了新僵尸的加入。主控者可以从容地暂停感染新机器,直到他设置好新的引导服务器。因此这个办法只能算是一个临时措施,去攻击僵尸网络的引导增殖系统意义并不大

Relevant Link:

http://blog.jobbole.com/60716/

 

5. 端口监听隐藏技术

0x1: ICMP Reverse Shell

在一个严格限制的网络环境中,受感染的肉鸡想要和C&C中控机发起"上线连接"常常是很困难的,因为企业会对内部环境的所有"外连请求"进行严格的监控(通过Hook技术),但是ICMP协议却可以"击穿"所有的网络边界审核限制,因为ICMP的设计目标就是要能够不受限制的横穿所有异构网络环境,为通信双方提供"是否可达"的判断
The idea of encapsulating data and commands in ICMP traffic to create a stealthy remote control channel was first popularized by the tool Loki, which was described in Phrack Magazine in 1996. The Tribe Flood Network (TFN) botnet, analyzed by David Dittrich in 1999, used a similar ICMP-based scheme for remotely controlling infected systems. Among the more recent tools for implementing a simple ICMP-based backdoor is "icmpsh"

1. ICMP隐蔽通信的核心在于ICMP Socket可以类似UDP那样无端口Listening而直接recv接收数据包,而且ICMP标准协议的数据包在大多数情况下,都不会遭到防火墙的拦截
2. 被部署了后门的肉鸡在启动"ICMP监听"后,会收到包含C&C主机的IP、端口的ICMP数据包(ICMP requst/replay模式,这些从IPS角度看都是正常数据包)
3. 肉鸡通过解析ICMP数据包,获得远程IP、端口,向C&c主动发起TCP连接,上线
4. 甚至来说,肉鸡还可以继续使用ICMP和C&C之间进行无连接的ICMP通信,接收指令、回显执行结果都通过ICMP完成

Relevant Link:

https://zeltser.com/reverse-icmp-shell/
http://phrack.org/issues/49/6.html#article
http://blog.csdn.net/itcastcpp/article/details/4034302
https://github.com/andreafabrizi/prism
http://www.cnblogs.com/lingerhk/p/4009909.html
http://read.pudn.com/downloads37/sourcecode/windows/shell/118101/ICMP%E5%90%8E%E9%97%A8/client/icmpsend.cpp__.htm
http://297020555.blog.51cto.com/1396304/584800

 

6. 风险端口监听自动化检测方案

1. 枚举当前机器上处于Listening状态的端口列表
2. 遍历Listening状态端口,检查端口的"被连接"情况,即有多少IP和该端口建立了established关系
    1) 被连接的IP列表
    2) 被连接的IP数量
    3) Listening监听程序路径 

0x1: Netstat端口查看

Netstat    
-n 显示IP地址和端口的数目。  
-S 显示每个协议的连接状态。  
-a 显示所有的连接和监听端口(服务所有连接通常不显示的)  
-e 显示太网的连接状态。最好与-s合用。  
-P PROTO 显示特定的协议连接状态   

每个端口都有一个对应的状态(state),它们分别代表TCP连接生命周期中的不同状态

1. LISTEN: 侦听来自远方的TCp端口的连接请求
2. SYN-SENT: 再发送连接请求后等待匹配的连接请求
3. SYN-RECEIVED: 再收到和发送一个连接请求后等待对方对连接请求的确认
4. ESTAbLISHED: 代表一个打开的连接,即已经完整建立连接的TCP连接
5. FIN-WAIT-1: 等待远程TCp连接中断请求,或先前的连接中断请求的确认
6. FIN-WAIT-2: 从远程TCp等待连接中断请求
7. CLOSE-WAIT: 等待从本地用户发来的连接中断请求
8. CLOSING: 等待远程TCp对连接中断的确认
9. LAST-ACK: 等待原来的发向远程TCp的连接中断请求的确认
10. TIME-WAIT: 等待足够的时间以确保远程TCp接收到连接中断请求的确认
11. CLOSED: 没有任何连接状态

0x2: 获取端口对应的监听程序

我们需要获取Listening状态端口对应的监听进程

netstat -ano
//获得pid
tasklist | findstr "pid"

用C++ API实现

1. PMIB_TCPTABLE_OWNER_PID: 一个结构体,含有:源地址及端口,目的地址及端口,Pid,进程所在目录
2. GetExtendedTcpTable: 获得TCP列表
3. OpenProcess: 与下面的函数配合,通过Pid获得进程所在目录 
4. GetModuleFileNameEx: 同上 

0x3: MIB_TCPTABLE_OWNER_PID structure

//The MIB_TCPTABLE_OWNER_PID structure contains a table of process IDs (PIDs) and the IPv4 TCP links that are context bound to these PIDs.
typedef struct 
{
    //dwNumEntries: The number of MIB_TCPROW_OWNER_PID elements in the table. 
    DWORD                dwNumEntries;
    
    //table: Array of MIB_TCPROW_OWNER_PID structures returned by a call to GetExtendedTcpTable.
    MIB_TCPROW_OWNER_PID table[ANY_SIZE];
} MIB_TCPTABLE_OWNER_PID, *PMIB_TCPTABLE_OWNER_PID;


//The MIB_TCPROW_OWNER_PID structure contains information that describes an IPv4 TCP connection with IPv4 addresses, ports used by the TCP connection, and the specific process ID (PID) associated with connection.
typedef struct _MIB_TCPROW_OWNER_PID {
    DWORD dwState;
    DWORD dwLocalAddr;
    DWORD dwLocalPort;
    DWORD dwRemoteAddr;
    DWORD dwRemotePort;
    DWORD dwOwningPid;
} MIB_TCPROW_OWNER_PID, *PMIB_TCPROW_OWNER_PID;

0x4: MIB_TCPTABLE2 structure

//The MIB_TCPTABLE2 structure contains a table of IPv4 TCP connections on the local computer.
typedef struct _MIB_TCPTABLE2 {
    //dwNumEntries: The number of entries in the table. 
    DWORD       dwNumEntries;

    //table: A pointer to a table of TCP connections implemented as an array of MIB_TCPROW2 structures.
    MIB_TCPROW2 table[ANY_SIZE];
} MIB_TCPTABLE2, *PMIB_TCPTABLE2;


//The MIB_TCPROW2 structure contains information that describes an IPv4 TCP connection.
MIB_TCPROW2 structure
typedef struct _MIB_TCPROW2 {
  DWORD                        dwState;
  DWORD                        dwLocalAddr;
  DWORD                        dwLocalPort;
  DWORD                        dwRemoteAddr;
  DWORD                        dwRemotePort;
  DWORD                        dwOwningPid;
  TCP_CONNECTION_OFFLOAD_STATE dwOffloadState;
} MIB_TCPROW2, *PMIB_TCPROW2;

0x5: Code

//port scan 
struct portListDetails
{
    char* listeningip;
    char* listenerprogram;
    int listenerprogrampid;
    int connectedipnum;
};
#include <Psapi.h> 
#pragma comment(lib, "psapi.lib")   

if(action == "--portOpenScan")
{
    //json class
    JsonEasy JsonResult = JsonEasy();
    JsonResult.jRoot["isvul"] = 0;

    // Declare and initialize variables
    PMIB_TCPTABLE2 pTcpTable;
    ULONG ulSize = 0;
    DWORD dwRetVal = 0; 
    char szLocalAddr[128];
    char szRemoteAddr[128]; 
    struct in_addr IpAddr; 
    int i, pid = 0;
    //process path
    TCHAR szFilename[MAX_PATH + 1];  
    DWORD dwPathSize = MAX_PATH;
    //result map
    std::map<int, portListDetails> portList;

    pTcpTable = (MIB_TCPTABLE2 *) MALLOC(sizeof (MIB_TCPTABLE2));
    if (pTcpTable == NULL) 
    { 
        return 0;
    }

    ulSize = sizeof (MIB_TCPTABLE);
    // Make an initial call to GetTcpTable2 to
    // get the necessary size into the ulSize variable
    if ((dwRetVal = GetTcpTable2(pTcpTable, &ulSize, TRUE)) == ERROR_INSUFFICIENT_BUFFER) 
    {
        FREE(pTcpTable);
        pTcpTable = (MIB_TCPTABLE2 *) MALLOC(ulSize);
        if (pTcpTable == NULL) 
        { 
            return 0;
        }
    }
    // Make a second call to GetTcpTable2 to get
    // the actual data we require
    if ((dwRetVal = GetTcpTable2(pTcpTable, &ulSize, TRUE)) == NO_ERROR) 
    { 
        //enable the SeDebugPrivilege privilege.
        EnableDebugPriv(); 
        for (i = 0; i < (int) pTcpTable->dwNumEntries; i++) 
        {  
            if(pTcpTable->table[i].dwState == MIB_TCP_STATE_LISTEN)
            {    
                //for LISTEN, LocalAddr means the ip which is listening, LocalPort means the port which is listening 
                //get port
                int listeningport = ntohs((u_short)pTcpTable->table[i].dwLocalPort);
                //get pid
                pid = pTcpTable->table[i].dwOwningPid;  
                portList[listeningport].listenerprogrampid = pid;  
                //get ip
                IpAddr.S_un.S_addr = (u_long) pTcpTable->table[i].dwLocalAddr;
                strcpy_s(szLocalAddr, sizeof (szLocalAddr), inet_ntoa(IpAddr));
                //set connectedipnum、listeningip
                portList[listeningport].connectedipnum = 0;
                portList[listeningport].listeningip = szLocalAddr;  
                if(pid != 0)
                {
                    HANDLE Handle = OpenProcess(  
                        PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,  
                        FALSE,  
                        pid // This is the PID, you can find one from windows task manager  
                    );  
                    if (Handle != NULL )   
                    {  
                        memset(szFilename, 0, sizeof(szFilename));
                        dwPathSize = MAX_PATH;
                        //if (GetModuleFileNameEx(Handle, 0, Buffer, MAX_PATH))  
                        if (QueryFullProcessImageName(Handle, 0, szFilename, &dwPathSize))  
                        {  
                            // At this point, buffer contains the full path to the executable  
                            portList[listeningport].listenerprogram = TCHAR2char(szFilename);    //copy it
                        }  
                        else  
                        {    
                            portList[listeningport].listenerprogram = "QueryFullProcessImageName faild"; 
                        }  
                        CloseHandle(Handle);  
                    }
                    else 
                    {  
                        portList[listeningport].listenerprogram = "OpenProcess faild"; 
                    }
                } 
                else
                {
                    portList[listeningport].listenerprogram = "Get PID faild"; 
                } 
                //have geted program path
            }
            if(pTcpTable->table[i].dwState == MIB_TCP_STATE_ESTAB)
            {
                //for ESTABLISHED, RemoteAddr means the ip which is connected, RemotePort means the port which is connected
                //get ip
                IpAddr.S_un.S_addr = (u_long) pTcpTable->table[i].dwRemoteAddr;
                strcpy_s(szRemoteAddr, sizeof (szRemoteAddr), inet_ntoa(IpAddr));
                //get port
                int connectedport = ntohs((u_short)pTcpTable->table[i].dwRemotePort); 
                //the ESTABLISHED port which is also In LISTEN port
                if( portList.find(connectedport) != portList.end() )
                {
                    //TODO: here we need a check: inHost or outHost
                    //inc the port connected count
                    portList[connectedport].connectedipnum++;
                } 
            }  
        }
        //end for
        
        //show port details   
        for(std::map<int, portListDetails>::iterator portItem = portList.begin(); portItem != portList.end(); ++portItem)
        { 
            JsonResult.execinfoItemPortOpenScan["listeningport"] = portItem->first;
            JsonResult.execinfoItemPortOpenScan["listeningip"] = portItem->second.listeningip;
            JsonResult.execinfoItemPortOpenScan["connectedipnum"] = portItem->second.connectedipnum;
            JsonResult.execinfoItemPortOpenScan["listenerprogram"] = portItem->second.listenerprogram;
            JsonResult.execinfoItemPortOpenScan["listenerprogrampid"] = portItem->second.listenerprogrampid;

            JsonResult.execinfoArrayObj.append(JsonResult.execinfoItemPortOpenScan);
        }   

        //wrap the json
        JsonResult.jRoot["execinfo"] = JsonResult.execinfoArrayObj;
        JsonResult.jRoot["moduleid"] = portOpenScan;
        JsonResult.jRoot["execresult"] = 1; 
        //output finnal result
        std::string out = JsonResult.jRoot.toStyledString();
        std::cout << out << std::endl;  
    } 
    else 
    { 
        FREE(pTcpTable);
        return 0;
    }

    if (pTcpTable != NULL) 
    {
        FREE(pTcpTable);
        pTcpTable = NULL;
    } 
    return 0;    
}

0x6: 遇到的问题

1. 一台服务器,尤其是代理服务器,可能同时存在监听本机80端口、以及连接远程主机的80端口,这导致的结果是我们统计处于ESTABLISHED状态、且目标端口是80的TCP条目的时候,无法区分是本机被连接的,还是本机主动向远程主机发起的80连接。通过判断ip是否属于本机能起到一定作用,但是在多网卡、内外网同时存在的情况下,判断ip是否属于本机的代码逻辑会相当复杂

2. 通过GetExtendedTcpTable获取的MIB_TCPROW_OWNER_PID结构,dwOwningPid成员代表的是和这个TCP连接绑定的进程PID,但在服务进程(services)场景下,这个PID会发生获取错误,导致无法最终获取到进程路径,而且MIB_TCPROW_OWNER_PID/MIB_TCPROW2获取到的PID值并不一样,导致这个问题的原因是apche这类service进程采取了master-slave架构,GetExtendedTcpTable获取到的是slave进程的pid,通过GetTcpTable2获取PMIB_TCPTABLE2可以得到监听指定端口的master进程

3. 对于WEB Server这类Listening应用来说,常常采取master-slave架构,主进程负责监听端口,接收客户端的请求,然后把具体工作转交给slave(worker)进程,所以一个进程列表里常常会看到多个同名的进程(它们之间存在父子关系),我们在进行风险端口检测的时候只要关注master进程就可以了

4. 通过pid获取进程绝对路径的时候,我们采取了: OpenProcess -> GetModuleFileNameEx方式,但是当目标进程为系统服务的时候,OpenProcess会返回"5-拒绝访问",这是权限不够的原因
To open a handle to another local process and obtain full access rights, you must enable the SeDebugPrivilege privilege.

5. 对于一些客户端程序(例如QQ.exe)来说,它会通过驱动进行自我防御,即对请求自身句柄的请求去掉了读权限、或者返回一个重定向后错误的结果

Relevant Link:

http://www.cppblog.com/prayer/archive/2009/01/06/71347.html
http://blog.csdn.net/xywlpo/article/details/6545594
http://www.cnblogs.com/tianfang/archive/2006/12/30/607859.html
http://cpp.ezbty.org/content/science_doc/c%E6%A0%87%E5%87%86%E5%BA%93%EF%BC%9Astd_map%E4%BD%9C%E4%B8%BA%E4%B8%80%E4%B8%AA%E5%85%B3%E8%81%94%E6%95%B0%E7%BB%84
http://003317.blog.51cto.com/2005292/531436
https://msdn.microsoft.com/en-us/library/aa366386.aspx
https://msdn.microsoft.com/en-us/library/aa365928.aspx
https://msdn.microsoft.com/en-us/library/aa366913.aspx
http://www.codeproject.com/Articles/14423/Getting-the-active-TCP-UDP-connections-using-the-G
https://msdn.microsoft.com/en-us/library/windows/desktop/bb485772(v=vs.85).aspx
http://blog.csdn.net/iiprogram/article/details/1829534
http://programming.nullanswer.com/question/26136544
http://blog.csdn.net/zy825316/article/details/13093797
http://www.solutionscore.com/tags/openprocess

 

Copyright (c) 2015 LittleHann All rights reserved

 

Automatic Risk Listening Port Detection And C&C Controller Identification

标签:

原文地址:http://www.cnblogs.com/LittleHann/p/4690670.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!