文件nmap-services
作用:预定义服务和端口映射表,该文件原则上不允许修改
nmap-services未定义33890端口映射,扫描结果:
PORT STATE SERVICE REASON
33890/tcp open unknown syn-ack
nmap-services定义33890端口映射,扫描结果:
PORT STATE SERVICE REASON
33890/tcp open ms-term-serv syn-ack
文件nmap-service-probes
作用:定义了服务版本探测的probe,其中预定义了probe和端口的映射,如果有映射,在NULL probe之后就会使用该probe探测,否则会自上而下的尝试每个probe,直到成功。
自上而下尝试probe的范围,由--version-intensity参数决定,rarity <= intensity的probe均会被尝试。NULL probe不受这个范围影响,添加端口映射关系的probe也不受该值影响。
--version-intensity 默认7级(0-9总共10个级别),0级的含义是除了NULL probe和注册了该端口的probe外,其他级别的probe的均不尝试。目前nmap的probe总共有142个,NULL probe没有rarity值。
--version-light 等效于--version-intensity 2
--version-all 等效于--version-intensity 9
由上面的原理得出结论:
- 知名端口容易匹配到合适probe进行版本探测;
- 自定义端口有可能匹配不到合适的probe;
- 自定义端口扫描时间大于知名端口;
PORT STATE SERVICE REASON VERSION
3389/tcp open ssl/ms-wbt-server? syn-ack ttl 127
PORT STATE SERVICE REASON VERSION
3389/tcp open ms-wbt-server syn-ack ttl 64 Microsoft Terminal Services
服务探测流程
# nmap -sV -Pn -n -p50000 -d9 -vv --version-intensity 0 192.168.186.246
上面命令只执行NULL和映射端口的Probe,通过 -d9 -vv可以从输出总查看执行过程。
执行过程:
1、Nmap先做端口扫描,然后把状态为open或者是open|filtered的TCP或UDP端口传递给服务识别模块,最后这些端口会并行的做服务探测。
Initiating Service scan at 21:10
Starting probes against new service: 192.168.186.246:50000 (tcp)
event_new(): event_new (IOD #1) (EID #8)
初始化服务探测,创建IO Daemon 编号#1,创建Event编号EID #8。
2、Nmap检查端口是否是需要排除的端口(见下文的Exclude Directive),如果是需要排除的端口那么Nmap不会对这个端口做服务探测。这里主要是避免对一些打印机的服务端口发包。在nmap-service-probes文件中定义:
# The Exclude directive takes a comma separated list of ports.
# The format is exactly the same as the -p switch.
Exclude T:9100-9107
3、如果端口是TCP,Nmap会先和端口建立一个连接,如果连接成功并且这个端口的状态原来是open|filtered的,那么Nmap把这个端口的状态改成open。这样做对那些为了隐秘扫描(例如FIN scan)而识别端口为open|filtered非常有用,这样能进一步确认端口的状态。
定义EID 8事件为创建连接会话
nsock_connect_tcp(): TCP connection requested to 192.168.186.246:50000 (IOD #1) EID 8
sock连接池加载事件EID 8
nsock_pool_add_event(): NSE #8: Adding event (timeout in 5000ms)
将EID 8事件关联到IOD 1,以便于IOD监听回包。
process_iod_events(): Processing events on IOD 1 (ev=2)
执行事件
process_event(): Processing event 8
process_event(): NSE #8: Sending event
监听返回信息:创建连接成功
nsock_trace_handler_callback(): Callback: CONNECT SUCCESS for EID 8
[192.168.186.246:50000]
4、一旦上面的连接建立,Nmap尝试等待5s。一些常见的服务,包括大部分的FTP、SSH、SMTP、Telnet、POP3、IMAP服务,为了标示自己会对建立的连接发送一些Welcome banner信息,这个过程称为NULL Probe。
NULL Probe仅仅是Read request from IOD #1(因为前面已经成功创建连接,这里是获取服务端发送的Banner信息,通过新事件执行:event_new (IOD #1) (EID #18)),没有发送任何的数据,也没有创建新的连接,在等待的时间(timeout: 6000ms,这个值是可以修改的,这个值是totalwaitms指令在Probe下设置的)内如果收到了数据,Nmap会将收到的banner信息和NULL Probe的将近3000个服务签名特征进行匹配(下文中的match Directive)。假如服务完全识别了(个人理解就是服务和版本信息都识别出来了),那么这个端口的服务识别就结束了。有时候Nmap只匹配到了服务类型,没有匹配到版本信息(下文softmatch Directive),那么Nmap会继续进行扫描,因为已经识别出来了服务那么Nmap会有针对的发送Probe。
Nmap尝试等待,调整该值可以规避一些响应比较慢的情况:
totalwaitms 6000
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探测。
Service scan sending probe GetRequest to 192.168.186.246:50000 (tcp)
创建HTTP请求事件
event_new (IOD #1) (EID #27)
Write request for 18 bytes to IOD #1 EID 27 [192.168.186.246:50000]: GET / HTTP/1.0....
创建HTTP请求监听事件
event_new (IOD #1) (EID #34)
Read request from IOD #1 [192.168.186.246:50000] (timeout: 5000ms) EID 34
将请求和读取返回事件分别注册并执行(先执行监听,后执行请求)
Processing event 34 (timeout in 5000ms, done=0)
Processing event 27 (timeout in 5000ms, done=0)
请求你成功
Callback: WRITE SUCCESS for EID 27 [192.168.186.246:50000]
读取超时(此时可以指定超时等待事件,GetRequest默认是5s)
Callback: READ TIMEOUT for EID 34 [192.168.186.246:50000]
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 probes(probable port probe:我理解就是ports Directive指令中包含这个端口)就能够识别出来服务。NULL probe和probable port probes共用同一个连接,所以在大部分情况下只要简单的建立一个连接就可以识别出来服务。
如果NULL probe和probable probe没有识别出来服务,Nmap会继续按序尝试其他的probe。那么对于TCP来说不得不重新建立一个连接(对于UDP来说就只要一个包就可以了),因为为了避免不同的probe之间相互影响,有可能前面发送的probe在超时以后还会收到目标端口的回应数据,如果没有重新建立连接的话,那么Nmap就会分不清楚回应的数据对应的是哪个probe。这个重新建立连接可能会要花费点时间了,如果再算上Nmap必须设置6s的超时来等待数据的返回,那么这就更加糟糕了。为了应对这种糟糕的情况,Nmap利用了几种技术来加快扫描的速度:Probe TCP TLSSessionReq
- Nmap尽可能使每一个probe匹配多个服务。例如GenericLines probe发送两个换行符("\r\n\r\n")给目标端口,这样就可以匹配许多的服务,包括FTP、ident、POP3、UUCP、Postgres和whois。GetRequest probe匹配了更多的服务,类似的还有HELP probe("help\r\n")和RPC、MS SMB probe。
- 如果一个服务是softmatch,那么Nmap只会去尝试可能匹配服务的probe(也就是说Nmap会有目的和针对性的进行probe)。
- 所有的probe都是不等价的!有一些匹配更多的服务,基于此Nmap用rarity度量值(rarity Directive)来避免尝试那些可能匹配失败的probe。我们可以用--version-intensity, --version-all, and --version-light选项来选择rarity值。
8、有一些(并不是一个)probes会探测目标端口是否正在运行于SSL之上,如果探测成功Nmap会通过SSL连接重新探测这个服务(会从NULL probe重新来过:Service scan sending probe NULL to 61.135.169.125:443 (tcp)),识别出运行于SSL加密后的真正服务。例如,Nmap会对443端口进行SSL probe,如果探测成功则尝试GetRequest probe,因为一般有一个web服务运行于SSL加密之后。
做SSL探测的Probe有很多,比如:
Probe TCP SSLSessionReq:SSLv3 ClientHello probe
Probe TCP TLSSessionReq:TLSv1.2 ClientHello probe.
Probe TCP SSLv23SessionReq:SSLv2-compatible ClientHello
9、另外一个通用的probe识别会去基于RPC的一些服务,一旦探测成功Nmap RPC grinder就会初始化,开始暴力(翻译有点不准)探测RPC程序的版本、名称。
Probe TCP RPCCheck
10、至少有一个probe会导致目标回应数据,如果Nmap没有成功识别出来服务,那么目标回应的数据会作为特征值(fingerprint)输出,如果你知道目标运行的服务那么你可以把fingerprint提交给Nmap,集成到nmap-probe-services中。
Nmap-service-probes中的一些指令
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”),接下来是一个分隔符(分隔符虽然可以是不同的字符但是强烈建议用斜线/),再接着就是选项的值,下面是所有可能的选项格式:
上面所列的选项都可能为空。在这些选项中可能经常会遇到$1、$2这样的数字,这些其实就是我们正则表达式中的分组引用(引用pattern中的分组)。
4. softmatch Directive
语法:softmatch <service> <pattern>
示例:softmatch ftp m/^220 [-.\w ]+ftp.*\r\n$/i
softmatch指令和上文的match(上文的match其实可以理解为hard match)指令差不多,主要的一个不同就是softmatch以后会要继续执行其他的probe,但是后面执行的probe是有选择性的,因为softmatch已经识别出来了服务,之所以在识别出来服务以后还要继续执行是为了进一步识别版本号。比如我们识别出来目标端口开放了http服务,但是没有能识别出来app是Apache还是Nginx或者是IIS。
在nmap官网中提到softmatch指令不会有<versioninfo>部分,但是我在较新的nmap 6.47 的nmap-service-probes文件中却看到softmatch也会有versioninfo部分,这个是暂时未搞清楚的。
5. Ports and sslports Directive
语法:ports <端口列表>
示例:ports 111,4045,32750-32810,38978
Ports(sslports)指令告诉Nmap该probe识别的服务一般都在哪些端口上面监听(运行),每一个probe只能有一个ports(sslports)指令。
6. Totalwaitms Directive
语法:totalwaitms milliseconds
示例:totalwaitms 6000
Totalwaitms指令表示本次probe的超时等待时间。这个指令一般不太常用,probe在发送了数据会在指定的时间等待目标数据返回。在Nmap中这个值默认为6000(虽然book上面说是5000,但是看了一下最新的nmap-service-probes中是6000,且不同的probe的totalwaittms可能不一样)。
7. Rarity Directive
语法:rarity <0-9>
示例:rarity 7
rarity指令表示改probe被调用的优先级,值越高表示被调用的优先级越低,也就代表该probe能够正确识别服务的概率越低(也可以理解为该probe识别的服务不常见)。
8. fallback Directive:回退指令,如果当前定义的特征值没有匹配到(match Directive failed),并且指定了fallback指令那么回退到指定的probe再进行匹配。
fallback语法:fallback <comma separated list of probes>
示例:fallback GetRequest,GenericLines
fallback是可选的指令。对TCP probe如果没有定义fallback指令,Nmap会首先和当前定义的Probe进行匹配,并且会隐式的回退(fallback)到NULL Probe进行匹配(当然是当前的probe匹配失败)。如果定义了fallback指令,那么在当前的probe匹配失败后会回退到fallback定义的probe去进行匹配,如果继续匹配失败则回退到NULL probe进行匹配。
UDP probe流程相同,除了隐式的回退到NULL probe。
至于为什么要有fallback指令可以参考上文的"Nmap的服务探测流程"。