-
Notifications
You must be signed in to change notification settings - Fork 60
/
type-nmap.go
383 lines (334 loc) · 9.22 KB
/
type-nmap.go
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
package gonmap
import (
"context"
"fmt"
"github.com/miekg/dns"
"strings"
"time"
)
type Nmap struct {
exclude PortList
portProbeMap map[int]ProbeList
probeNameMap map[string]*probe
probeSort ProbeList
probeUsed ProbeList
filter int
//检测端口存活的超时时间
timeout time.Duration
bypassAllProbePort PortList
sslSecondProbeMap ProbeList
allProbeMap ProbeList
sslProbeMap ProbeList
}
//扫描类
func (n *Nmap) ScanTimeout(ip string, port int, timeout time.Duration) (status Status, response *Response) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
var resChan = make(chan bool)
defer func() {
close(resChan)
cancel()
}()
go func() {
defer func() {
if r := recover(); r != nil {
if fmt.Sprint(r) != "send on closed channel" {
panic(r)
}
}
}()
status, response = n.Scan(ip, port)
resChan <- true
}()
select {
case <-ctx.Done():
return Closed, nil
case <-resChan:
return status, response
}
}
func (n *Nmap) Scan(ip string, port int) (status Status, response *Response) {
var probeNames ProbeList
if n.bypassAllProbePort.exist(port) == true {
probeNames = append(n.portProbeMap[port], n.allProbeMap...)
} else {
probeNames = append(n.allProbeMap, n.portProbeMap[port]...)
}
probeNames = append(probeNames, n.sslProbeMap...)
//探针去重
probeNames = probeNames.removeDuplicate()
firstProbe := probeNames[0]
status, response = n.getRealResponse(ip, port, n.timeout, firstProbe)
if status == Closed || status == Matched {
return status, response
}
otherProbes := probeNames[1:]
return n.getRealResponse(ip, port, 2*time.Second, otherProbes...)
}
func (n *Nmap) getRealResponse(host string, port int, timeout time.Duration, probes ...string) (status Status, response *Response) {
status, response = n.getResponseByProbes(host, port, timeout, probes...)
if status != Matched {
return status, response
}
if response.FingerPrint.Service == "ssl" {
status, response := n.getResponseBySSLSecondProbes(host, port, timeout)
if status == Matched {
return Matched, response
}
}
return status, response
}
func (n *Nmap) getResponseBySSLSecondProbes(host string, port int, timeout time.Duration) (status Status, response *Response) {
status, response = n.getResponseByProbes(host, port, timeout, n.sslSecondProbeMap...)
if status != Matched || response.FingerPrint.Service == "ssl" {
status, response = n.getResponseByHTTPS(host, port, timeout)
}
if status == Matched && response.FingerPrint.Service != "ssl" {
if response.FingerPrint.Service == "http" {
response.FingerPrint.Service = "https"
}
return Matched, response
}
return NotMatched, response
}
func (n *Nmap) getResponseByHTTPS(host string, port int, timeout time.Duration) (status Status, response *Response) {
var httpRequest = n.probeNameMap["TCP_GetRequest"]
return n.getResponse(host, port, true, timeout, httpRequest)
}
func (n *Nmap) getResponseByProbes(host string, port int, timeout time.Duration, probes ...string) (status Status, response *Response) {
var responseNotMatch *Response
for _, requestName := range probes {
if n.probeUsed.exist(requestName) {
continue
}
n.probeUsed = append(n.probeUsed, requestName)
p := n.probeNameMap[requestName]
status, response = n.getResponse(host, port, p.sslports.exist(port), timeout, p)
//如果端口未开放,则等待10s后重新连接
//if b.status == Closed {
// time.Sleep(time.Second * 10)
// b.Load(n.getResponse(host, port, n.probeNameMap[requestName]))
//}
//logger.Printf("Target:%s:%d,Probe:%s,Status:%v", host, port, requestName, status)
if status == Closed || status == Matched {
responseNotMatch = nil
break
}
if status == NotMatched {
responseNotMatch = response
}
}
if responseNotMatch != nil {
response = responseNotMatch
}
return status, response
}
func (n *Nmap) getResponse(host string, port int, tls bool, timeout time.Duration, p *probe) (Status, *Response) {
if port == 53 {
if DnsScan(host, port) {
return Matched, &dnsResponse
} else {
return Closed, nil
}
}
text, tls, err := p.scan(host, port, tls, timeout, 10240)
if err != nil {
if strings.Contains(err.Error(), "STEP1") {
return Closed, nil
}
if strings.Contains(err.Error(), "STEP2") {
return Closed, nil
}
if p.protocol == "UDP" && strings.Contains(err.Error(), "refused") {
return Closed, nil
}
return Open, nil
}
response := &Response{
Raw: text,
TLS: tls,
FingerPrint: &FingerPrint{},
}
//若存在返回包,则开始捕获指纹
fingerPrint := n.getFinger(text, tls, p.name)
response.FingerPrint = fingerPrint
if fingerPrint.Service == "" {
return NotMatched, response
} else {
return Matched, response
}
//如果成功匹配指纹,则直接返回指纹
}
func (n *Nmap) getFinger(responseRaw string, tls bool, requestName string) *FingerPrint {
data := n.convResponse(responseRaw)
probe := n.probeNameMap[requestName]
finger := probe.match(data)
if tls == true {
if finger.Service == "http" {
finger.Service = "https"
}
}
if finger.Service != "" || n.probeNameMap[requestName].fallback == "" {
//标记当前探针名称
finger.ProbeName = requestName
return finger
}
fallback := n.probeNameMap[requestName].fallback
fallbackProbe := n.probeNameMap[fallback]
for fallback != "" {
logger.Println(requestName, " fallback is :", fallback)
finger = fallbackProbe.match(data)
fallback = n.probeNameMap[fallback].fallback
if finger.Service != "" {
break
}
}
//标记当前探针名称
finger.ProbeName = requestName
return finger
}
func (n *Nmap) convResponse(s1 string) string {
//为了适配go语言的沙雕正则,只能讲二进制强行转换成UTF-8
b1 := []byte(s1)
var r1 []rune
for _, i := range b1 {
r1 = append(r1, rune(i))
}
s2 := string(r1)
return s2
}
//配置类
func (n *Nmap) SetTimeout(timeout time.Duration) {
n.timeout = timeout
}
func (n *Nmap) OpenDeepIdentify() {
//-sV参数深度解析
n.allProbeMap = n.probeSort
}
func (n *Nmap) AddMatch(probeName string, expr string) {
var probe = n.probeNameMap[probeName]
probe.loadMatch(expr, false)
}
//初始化类
func (n *Nmap) loads(s string) {
lines := strings.Split(s, "\n")
var probeGroups [][]string
var probeLines []string
for _, line := range lines {
if !n.isCommand(line) {
continue
}
commandName := line[:strings.Index(line, " ")]
if commandName == "Exclude" {
n.loadExclude(line)
continue
}
if commandName == "Probe" {
if len(probeLines) != 0 {
probeGroups = append(probeGroups, probeLines)
probeLines = []string{}
}
}
probeLines = append(probeLines, line)
}
probeGroups = append(probeGroups, probeLines)
for _, lines := range probeGroups {
p := parseProbe(lines)
n.pushProbe(*p)
}
}
func (n *Nmap) loadExclude(expr string) {
n.exclude = parsePortList(expr)
}
func (n *Nmap) pushProbe(p probe) {
n.probeSort = append(n.probeSort, p.name)
n.probeNameMap[p.name] = &p
//建立端口扫描对应表,将根据端口号决定使用何种请求包
//如果端口列表为空,则为全端口
if p.rarity > n.filter {
return
}
//0记录所有使用的探针
n.portProbeMap[0] = append(n.portProbeMap[0], p.name)
//分别压入sslports,ports
for _, i := range p.ports {
n.portProbeMap[i] = append(n.portProbeMap[i], p.name)
}
for _, i := range p.sslports {
n.portProbeMap[i] = append(n.portProbeMap[i], p.name)
}
}
func (n *Nmap) fixFallback() {
for probeName, probeType := range n.probeNameMap {
fallback := probeType.fallback
if fallback == "" {
continue
}
if _, ok := n.probeNameMap["TCP_"+fallback]; ok {
n.probeNameMap[probeName].fallback = "TCP_" + fallback
} else {
n.probeNameMap[probeName].fallback = "UDP_" + fallback
}
}
}
func (n *Nmap) isCommand(line string) bool {
//删除注释行和空行
if len(line) < 2 {
return false
}
if line[:1] == "#" {
return false
}
//删除异常命令
commandName := line[:strings.Index(line, " ")]
commandArr := []string{
"Exclude", "Probe", "match", "softmatch", "ports", "sslports", "totalwaitms", "tcpwrappedms", "rarity", "fallback",
}
for _, item := range commandArr {
if item == commandName {
return true
}
}
return false
}
func (n *Nmap) sortOfRarity(list ProbeList) ProbeList {
if len(list) == 0 {
return list
}
var raritySplice []int
for _, probeName := range list {
rarity := n.probeNameMap[probeName].rarity
raritySplice = append(raritySplice, rarity)
}
for i := 0; i < len(raritySplice)-1; i++ {
for j := 0; j < len(raritySplice)-i-1; j++ {
if raritySplice[j] > raritySplice[j+1] {
m := raritySplice[j+1]
raritySplice[j+1] = raritySplice[j]
raritySplice[j] = m
mp := list[j+1]
list[j+1] = list[j]
list[j] = mp
}
}
}
for _, probeName := range list {
rarity := n.probeNameMap[probeName].rarity
raritySplice = append(raritySplice, rarity)
}
return list
}
//工具函数
func DnsScan(host string, port int) bool {
domainServer := fmt.Sprintf("%s:%d", host, port)
c := dns.Client{
Timeout: 2 * time.Second,
}
m := dns.Msg{}
// 最终都会指向一个ip 也就是typeA, 这样就可以返回所有层的cname.
m.SetQuestion("www.baidu.com.", dns.TypeA)
_, _, err := c.Exchange(&m, domainServer)
if err != nil {
return false
}
return true
}