1. Nmap先做端口扫描,然后把状态为open或者是open|filtered的TCP或UDP端口传递给服务识别模块,最后这些端口会并行的做服务探测。
2. Nmap检查端口是否是需要排除的端口(见下文的Exclude Directive),如果是需要排除的端口那么Nmap不会对这个端口做服务探测。这里主要是避免对一些打印机的服务端口发包。
3. 如果端口是TCP,Nmap会先和端口建立一个连接,如果连接成功并且这个端口的状态原来是open|filtered的,那么Nmap把这个端口的状态改成open。这样做对那些为了隐秘扫描(例如FIN scan)而识别端口为open|filtered非常有用,这样能进一步确认端口的状态。
4. 一旦上面的连接建立,Nmap尝试等待6s(以前是5s)。一些常见的服务,包括大部分的FTP、SSH、SMTP、Telnet、POP3、IMAP服务,为了标示自己会对建立的连接发送一些Welcome banner信息,这个过程称为NULL Probe
。NULL Probe
仅仅是和目标端口建立连接,没有发送任何的数据(也就是只经过了TCP的三次握手),在等待的时间内如果收到了数据,Nmap会将收到的banner信息和NULL Probe
的将近3000个服务签名特征进行匹配(下文中的match Directive)。假如服务完全识别了(个人理解就是服务和版本信息都识别出来了),那么这个端口的服务识别就结束了。有时候Nmap只匹配到了服务类型,没有匹配到版本信息(下文softmatch Directive),那么Nmap会继续进行扫描,因为已经识别出来了服务那么Nmap会有针对的发送Probe。
5. UDP Probe
或者前面的NULL Probe
匹配失败再或者是NULL Probe
有soft match,那么就会接着顺序进行nmap-service-probes文件中其他的Probe。
每一个服务都注册到了一个常见的端口中(通过nmap-services文件),每一个service probe都包含了一个端口的列表,表示在这些端口上面最有可能开放这个服务。例如 GetRequest是一个识别web servers的Probe,包含的端口为80-85、8000-8010(不限于这些端口),那么Nmap在扫描的时候会顺序对这些开放的端口进行GetRequest probe探测。
6. 每一个probe包含了一个probe string,在服务探测的时候发送给目标端口,目标返回的数据会和这个对应的probe的一系列特征字符串(signature,下文中的match Directive)进行匹配。如果匹配成功则服务识别结束,如果是一个softmatch那么Nmap有选择的进行下一步probe,要是都匹配失败则看是否有fallback Directive,如果有则执行fallback Directive,更具体的参考fallback Directive和Cheats and Fallback部分。
在服务探测期间如果收到了来自UDP端口的数据,并且端口的UDP状态为open|filtered那么Nmap把这个端口的UDP状态改成open。在UDP扫描的时候如果因为有防火墙的干扰导致所有的端口状态为open|filtered,那么结合服务探测对端口的状态做进一步的探测,这是非常有用的。
7. 在大部分的情况下,NULL probe
和接下来的一个probable port probe
(probable port probe
:我理解就是ports Directive指令中包含这个端口)就能够识别出来服务。NULL probe
和probable port probe
公用同一个连接,所以在大部分情况下只要简单的建立一个连接就可以识别出来服务。
如果NULL probe
和probable probe
没有识别出来服务,Nmap会继续按序尝试其他的probe。那么对于TCP来说不得不重新建立一个连接(对于UDP来说就只要一个包就可以了),因为为了避免不同的probe之间相互影响,有可能前面发送的probe在超时以后还会收到目标端口的回应数据,没有重新建立连接那么Nmap就会分不清楚回应的数据对应的是哪个probe。这个重新建立连接可能会要花费点时间了,如果再算上Nmap必须设置6s的超时来等待数据的返回,那么这就更加糟糕了。为了应对这种糟糕的情况,Nmap利用了几种技术来加快扫描的速度:
"\r\n\r\n"
)给目标端口,这样就可以匹配许多的服务,包括FTP、ident、POP3、UUCP、Postgres和whois。GetRequest probe匹配了更多的服务,类似的还有HELP probe("help\r\n"
)和RPC、MS SMB probe。--version-intensity
, --version-all
, and --version-light
选项来选择rarity值。8. 有一个probe会探测目标端口是否正在运行于SSL之上,如果探测成功Nmap会通过SSL连接重新探测这个服务,识别出运行于SSL加密后的真正服务。例如,Nmap会对443端口进行SSL probe,如果探测成功则尝试GetRequest probe,因为一般有一个web服务运行于SSL加密之后。
9. 另外一个通用的probe识别会去基于RPC的一些服务,一旦探测成功Nmap RPC grinder就会初始化,开始暴力(翻译有点不准)探测RPC程序的版本、名称。
10. 至少有一个probe会导致目标回应数据,如果Nmap没有成功识别出来服务,那么目标回应的数据会作为特征值(fingerprint)输出,如果你知道目标运行的服务那么你可以把fingerprint提交给Nmap,集成到nmap-probe-services中。
尽管Nmap会等待几秒时间等待目标服务返回数据,有时候一些应用对NULL Probe
的回应会比较慢。可能是多重原因导致的这种慢,比如一个慢的DNS反向查询。基于这个原因,Nmap有时候会在后续的Probe中接受到NULL Probe
的返回数据。
例如,我们扫描端口25(SMTP),只要我们一建立连接,SMTP服务会要去一大堆的DNS黑名单中查询我们是否是垃圾邮件发送者,如果是就拒绝服务。这个过程可能比较耗时间,可能会超过Nmap的NULL Probe
等待时间,如果超时那么Nmap会接着顺序执行后面的其他probe,对于25端口接下来是HELP Probe
。当SMTP做完anti-spam的检查发现我们不是垃圾邮件者,它会回送greeting banner如下。在接受到NULL Probe
返回的banner信息时Nmap因为超时已经到了HELP Probe
这个步骤了,将收到的banner和HELP Probe
的特征字符串进行匹配,匹配失败。
220 hcsw.org ESMTP Sendmail 8.12.3/8.12.3/Debian-7.1; Tue, [cut]
214-2.0.0 This is sendmail version 8.12.3
214-2.0.0 Topics:
214-2.0.0 HELO EHLO MAIL RCPT DATA
214-2.0.0 RSET NOOP QUIT HELP VRFY
214-2.0.0 EXPN VERB ETRN DSN AUTH
214-2.0.0 STARTTLS
214-2.0.0 For more info use “HELP ”.
214-2.0.0 To report bugs in the implementation send email to
214-2.0.0 [email protected].
214-2.0.0 For local information send email to Postmaster at your site.
214 2.0.0 End of HELP info
因为这是一个比较普遍的问题,Nmap会有一个"欺骗(cheats)"动作,如果当前的probe匹配失败则会尝试去匹配NULL Probe
。在上面的SMTP例子中,我们会在NULL Probe
中匹配成功,识别服务为SMTP,程序是Sendmail,版本是8.12.3/8.12.3/Debian-7.1
,并且主机名是hcsw.org
。
上面提到的NULL Probe
的cheat过程其实是Nmap的一个通用的特征:fallbacks。fallback directive指令在下文中会详细描述了,基本上每一个可能会在当前的匹配过程中遇到其他的probe的回应数据的probe,都会定义一些fallback directive。
例如,有一些Apache服务器的设置,Apache不会回应GetRequest("GET / HTTP/1.0\r\n\r\n")
probe的请求,但是Nmap还是能够正确的识别出来这个服务,因为这些服务会回应HTTPOptions probe
,这个HTTPOptions probe
有一个fallback directive指向GetRequest。在GetRequest probe中有足够的特征值(signature)可以识别出来服务(这里其实应该是假设HTTPOptions没有成功识别)。
Nmap用rarity(优先级)来决定选用哪些probe进行服务探测,rarity值高表示这个probe是不常用的而且probe返回的数据不太有用。Nmap的使用者可以通过如下所示的服务扫描强度(version-intensity
)参数来决定使用哪个probe进行服务探测。Nmap选择probe的完整算法如下:
NULL Probe
总是第一个使用的。ports Directive
和sslports Directive
包含了该端口的probe都将按照在nmap-service-probes出现的顺序被使用。例如对于6000端口来说,probe的选择顺序为:NULL probe
、HTTPOptions probe
、X11Probe probe
、ibm-db2 probe
。version-intensity
值的probe被调用,这些probe也是按照在nmap-service-probes出现的顺序被使用。除了NULL probe
以外的probe都会有一个rarity,在Nmap进行服务识别的时候我们可以灵活的控制使用哪些probe,在每次扫描的时候选用一个合适的version-intensity
值即可。version-intensity
值越高,使用的probe越多,所以一个全面的扫描就需要选择一个高强度的version-intensity
值,当然version-intensity
值越高需要的扫描时间越长。Nmap默认的version-intensity
值为7,Nmap提供如下的选项:
–version-intensity
设置扫描的服务探测强度,如果为0则只有NULL probe和端口在ports Directive列表中的probe会被调用
–version-light
等价于–version-intensity 2。
–version-all
等价于–version-intensity 9,所有的probe都被使用。
1. Exclude Directive
:排除指令是指在做服务识别时需要排除的端口,这个命令只能使用一次,一般是放在了文件的头部(也就是任何探测命令之前)。在nmap-service-probes文件中默认包含了tcp端口的9100到9107之间的端口(Exclude T:9100-9107
),这是因为这些端口一般都是用于打印机的,这些监听的端口会打印数据只要你给这个端口发送了任何的数据。你可以使用—allports
选项,这样服务探测会去探测所有的端口。
2. Probe Directive
:探测指令告诉Nmap发送何种字符串去识别各种服务,也就是定义了一个探测包。
Probe的语法为:Probe protocol probename probestring
示例:Probe TCP X11Probe q|\x6C\0\x0B\0\0\0\0\0\0\0\0\0|
protocol:
这个只能是TCP或者UDP。Nmap只会对这两种协议的服务做probe
probename:
这是给本次的probe取的名称,个人猜测在Nmap中会有很多的地方用这个probename来代表这个服务probe。在后面的fallback Directive中我们会遇到使用这个probename,还有在Nmap指定--version-trace
选项后会打印出来对这个端口做了哪些服务probe,在这里就会使用这个probe name,如:Service scan sending probe Help to 192.168.1.1:3389 (tcp)
probestring:
Nmap为了服务探测发送的字符串。Probestring必须以"q"字母开头,然后以分隔符"|"为字符串的开头和结尾。在分隔符之间是真正发送的字符,发送的字符格式跟C or Perl字符格式类似,允许这些转义字符:\\ \0, \a, \b, \f, \n, \r, \t, \v, and \xHH
(H是十六进制数字)。有一个特殊的probestring,叫做TCP NULL probe
,分隔符之间的字符串为空(Probe TCP NULL q||
),这个特殊的probe的作用见下面的章节。如果你想在probestring中包含分隔符那么你可能需要选择除|以外的字符。
3. match Directive
:匹配指令告诉Nmap如何从目标机返回的字符串中识别出来服务。
match的语法为:match service pattern [versioninfo]
示例:match networkaudio m|^\0\x19\x02\0\x02\0\x07\0Protocol version mismatch\0| p/Network Audio System/
service:
这是和pattern相匹配的服务名称.例如是ssh,smtp,http.可以在服务名前增加ssl标志,表示这个服务是通过SSL隧道加密的.
pattern:
这是用于识别返回的字符串是否匹配对应的服务。pattern的格式跟Perl类似,语法是m/[regex]/[opts]
,"m"表示字符串的开始,接着的/是分隔符,这个分隔符可以是任何其他可打印的字符只要和接下来的分隔符相匹配即可。[regex]部分是一个Perl风格的表达式,目前仅支持i、s这两个选项,分别表示大小写不敏感和’.’元字符包括换行符。这两个选项和我们一般的正则表达式中的意义一致。[regex]还支持分组。
versioninfo:
versioninfo部分包含了几个可选的子选项。每一个选项都由一个特定的标示符开始(例如h代表”hostname”),接下来是一个分隔符(分隔符虽然可以是不同的字符但是强烈建议用斜线/),再接着就是选项的值,下面是所有可能的选项格式:
softmatch
softmatch ftp m/^220 [-.\w ]+ftp.*\r\n$/i
ports <端口列表>
ports 111,4045,32750-32810,38978
totalwaitms milliseconds
totalwaitms 6000
rarity <0-9>
rarity 7
fallback
fallback GetRequest,GenericLines
实践是检验真理的唯一标准,那么下面就用一些示例来近一步了解Nmap的服务探测过程。我们可以用—version-trace或者-d(debugging)来查看Nmap的服务探测整个过程,还可以结合网络抓包来查看网络发包情况。
下面是Nmap中提到的一个示例,扫描参数为:nmap -sSV -T4 -F -d --version-trace insecure.org
。
Service scan sending probe NULL to 173.255.243.189:22 (tcp)
NSOCK INFO [3.6630s] nsock_trace_handler_callback(): Callback: READ SUCCESS for EID 50 [173.255.243.189:22] (21 bytes): SSH-2.0-OpenSSH_5.3..
Service scan match (Probe NULL matched with NULL line 2951): 173.255.243.189:22 is ssh. Version: |OpenSSH|5.3|protocol 2.0|
结合上面两个输出来看:
Service scan sending probe NULL to 173.255.243.189:80 (tcp)
NSOCK INFO [0.6230s] nsock_read(): Read request from IOD #1 [173.255.243.189:80] (timeout: 6000ms) EID 18
NSOCK INFO [6.6300s] nsock_trace_handler_callback(): Callback: READ TIMEOUT for EID 18 [173.255.243.189:80]
Service scan sending probe GetRequest to 173.255.243.189:80 (tcp)
NSOCK INFO [6.7820s] nsock_trace_handler_callback(): Callback: READ SUCCESS for EID 34 [173.255.243.189:80] (1448 bytes)
Service scan match (Probe GetRequest matched with GetRequest line 5555): 173.255.243.189:80 is http. Version: |Apache httpd|2.2.15|(CentOS)|
NULL probe
中没有识别出来80端口的服务,在NULL probe
中Nmap会等待6000ms的时间。在6000ms超时以后继续执行了GetRequest probe
,并且在GetRequest probe
中完整识别出来了80端口的服务。NULL probe
和接下来的第一个probe中会共用同一个tcp连接,除此之外的probe都需要单独重新建立连接(在这个示例中没有体现出来,在下面的示例中有体现)。 下面这个是我模拟了一个14000端口的开放,上面没有运行任何的服务,用Nmap对这个端口进行了扫描,扫描参数为:nmap -Pn -sT -sV --version-intensity 6 -p 14000 10.16.60.106 -e eth1 -n --open --version-trace
Service scan sending probe NULL to 10.16.60.106:14000 (tcp)
NSOCK INFO [6.1410s] nsock_trace_handler_callback(): Callback: READ TIMEOUT for EID 18 [10.16.60.106:14000]
Service scan sending probe GenericLines to 10.16.60.106:14000 (tcp)
NSOCK INFO [11.1480s] nsock_trace_handler_callback(): Callback: READ TIMEOUT for EID 34 [10.16.60.106:14000]
Service scan sending probe GetRequest to 10.16.60.106:14000 (tcp)
NSOCK INFO [16.1610s] nsock_trace_handler_callback(): Callback: READ TIMEOUT for EID 58 [10.16.60.106:14000]
Service scan sending probe HTTPOptions to 10.16.60.106:14000 (tcp)
NSOCK INFO [21.1740s] nsock_trace_handler_callback(): Callback: READ TIMEOUT for EID 82 [10.16.60.106:14000]
Service scan sending probe RTSPRequest to 10.16.60.106:14000 (tcp)
NSOCK INFO [26.1910s] nsock_trace_handler_callback(): Callback: READ TIMEOUT for EID 106 [10.16.60.106:14000]
NULL probe
和GenericLines probe
是共用一个tcp连接,但是接下来的GetRequest probe
是重新建立的tcp连接。--version-intensity 6
,所以Nmap把rarity < 6的probe都执行了。