634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | passive Nmap like scanner built with shodan.io
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | 
24 |
25 | ---
26 |
27 | Smap is a replica of Nmap which uses shodan.io's free API for port scanning. It takes same command line arguments as Nmap and produces the same output which makes it a drop-in replacament for Nmap.
28 |
29 | ## Features
30 | - Scans 200 hosts per second
31 | - Doesn't require any account/api key
32 | - Vulnerability detection
33 | - Supports all nmap's output formats
34 | - Service and version fingerprinting
35 | - Makes no contact to the targets
36 |
37 | ## Installation
38 | ### Manual
39 | `go install -v github.com/s0md3v/smap/cmd/smap@latest`
40 |
41 | Confused? For more detailed instructions, [click here](https://github.com/s0md3v/Smap/wiki/FAQ#how-do-i-install-smap)
42 | ### AUR pacakge
43 | Smap is available on AUR as [smap-git](https://aur.archlinux.org/packages/smap-git).
44 |
45 | ## Usage
46 | Smap takes the same arguments as Nmap but options other than `-p`, `-h`, `-o*`, `-iL` are ignored. If you are unfamiliar with Nmap, here's how to use Smap.
47 |
48 | ### Specifying targets
49 | ```
50 | smap 127.0.0.1 127.0.0.2
51 | ```
52 | You can also use a list of targets, seperated by newlines.
53 | ```
54 | smap -iL targets.txt
55 | ```
56 | **Supported formats**
57 |
58 | ```
59 | 1.1.1.1 // IPv4 address
60 | example.com // hostname
61 | 178.23.56.0/8 // CIDR
62 | ```
63 |
64 | ### Output
65 | Smap supports 6 output formats which can be used with the `-o* ` as follows
66 | ```
67 | smap example.com -oX output.xml
68 | ```
69 | If you want to print the output to terminal, use hyphen (`-`) as filename.
70 |
71 | **Supported formats**
72 | ```
73 | oX // nmap's xml format
74 | oG // nmap's greppable format
75 | oN // nmap's default format
76 | oA // output in all 3 formats above at once
77 | oP // IP:PORT pairs seperated by newlines
78 | oS // custom smap format
79 | oJ // json
80 | ```
81 |
82 | > Note: Since Nmap doesn't scan/display vulnerabilities and tags, that data is not available in nmap's formats. Use `-oS` to view that info.
83 |
84 | ### Specifying ports
85 | Smap scans these [1237 ports](https://api.shodan.io/shodan/ports) by default. If you want to display results for certain ports, use the `-p` option.
86 |
87 | ```
88 | smap -p21-30,80,443 -iL targets.txt
89 | ```
90 |
91 | ## Considerations
92 | Since Smap simply fetches existent port data from shodan.io, it is super fast but there's more to it. You should use Smap if:
93 |
94 | #### You want
95 | - vulnerability detection
96 | - a super fast port scanner
97 | - results for most common ports (top 1237)
98 | - no connections to be made to the targets
99 |
100 | #### You are okay with
101 | - not being able to scan IPv6 addresses
102 | - results being up to 7 days old
103 | - a few false negatives
104 |
--------------------------------------------------------------------------------
/cmd/smap/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/s0md3v/smap/internal/core"
5 | )
6 |
7 | func main() {
8 | core.Init()
9 | }
10 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/s0md3v/smap
2 |
3 | go 1.13
4 |
5 | require (
6 | github.com/weppos/publicsuffix-go v0.15.0
7 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
8 | )
9 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/weppos/publicsuffix-go v0.15.0 h1:2uQCwDczZ8YZe5uD0mM3sXRoZYA74xxPuiKK8LdPcGQ=
2 | github.com/weppos/publicsuffix-go v0.15.0/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
3 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
4 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
5 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
6 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
7 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
8 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
9 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
10 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
11 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
12 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
13 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
14 | golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
15 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
16 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
17 |
--------------------------------------------------------------------------------
/internal/core/argparser.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "os"
5 | "regexp"
6 | "strings"
7 | )
8 |
9 | var reValidPair = regexp.MustCompile(`^([-]{1,2}[A-Za-z-]+)(\d.*)?`)
10 |
11 | var validArgs = map[string]bool{ // name : is_boolean_type
12 | "iL": false,
13 | "iR": false,
14 | "exclude": false,
15 | "excludefile": false,
16 | "sL": true,
17 | "sn": true,
18 | "Pn": true,
19 | "PS": true,
20 | "PA": true,
21 | "PU": true,
22 | "PY": true,
23 | "PE": true,
24 | "PP": true,
25 | "PM": true,
26 | "PO": true,
27 | "n": true,
28 | "R": true,
29 | "dns-servers": false,
30 | "system-dns": true,
31 | "traceroute": true,
32 | "sS": true,
33 | "sT": true,
34 | "sA": true,
35 | "sW": true,
36 | "sM": true,
37 | "sU": true,
38 | "sN": true,
39 | "sF": true,
40 | "sX": true,
41 | "scanflags": false,
42 | "sI": false,
43 | "sY": true,
44 | "sZ": true,
45 | "sO": true,
46 | "b": false,
47 | "p": false,
48 | "exclude-ports": false,
49 | "F": true,
50 | "r": true,
51 | "top-ports": false,
52 | "port-ratio": false,
53 | "sV": true,
54 | "version-intensity": false,
55 | "version-light": true,
56 | "version-all": true,
57 | "version-trace": true,
58 | "sC": true,
59 | "script": true,
60 | "script-args": true,
61 | "script-args-file": true,
62 | "script-trace": true,
63 | "script-updatedb": true,
64 | "script-help": true,
65 | "O": true,
66 | "osscan-limit": true,
67 | "osscan-guess": true,
68 | "T": false,
69 | "min-hostgroup": false,
70 | "max-hostgroup": false,
71 | "min-parallelism": false,
72 | "max-parallelism": false,
73 | "min-rtt-timeout": false,
74 | "max-rtt-timeout": false,
75 | "initial-rtt-timeout": false,
76 | "max-retries": false,
77 | "host-timeout": false,
78 | "scan-delay": false,
79 | "max-scan-delay": false,
80 | "min-rate": false,
81 | "max-rate": false,
82 | "f": true,
83 | "D": false,
84 | "S": false,
85 | "e": false,
86 | "g": false,
87 | "source-port": false,
88 | "proxies": false,
89 | "data": false,
90 | "data-string": false,
91 | "data-length": false,
92 | "ip-options": false,
93 | "ttl": false,
94 | "spoof-mac": false,
95 | "badsum": true,
96 | "oN": false,
97 | "oX": false,
98 | "oS": false,
99 | "oG": false,
100 | "oA": false,
101 | "oJ": false,
102 | "oP": false,
103 | "v": true,
104 | "d": true,
105 | "reason": true,
106 | "open": true,
107 | "packet-trace": true,
108 | "iflist": true,
109 | "append-output": true,
110 | "resume": false,
111 | "stylesheet": false,
112 | "webxml": true,
113 | "no-stylesheet": true,
114 | "6": true,
115 | "A": true,
116 | "datadir": false,
117 | "send-eth": true,
118 | "send-ip": true,
119 | "privileged": true,
120 | "unprivileged": true,
121 | "V": true,
122 | "h": true,
123 | }
124 |
125 | func whatToDo(token string, lastAction int) (string, int) {
126 | /*
127 | -1 = error
128 | 0 = look for next arg
129 | 1 = look for arg's value
130 | 2 = treat as extra data
131 | */
132 | if strings.HasPrefix(token, "-") {
133 | if lastAction == 1 {
134 | if token == "-" {
135 | return token, 0
136 | }
137 | return token, -1
138 | }
139 | newToken := strings.TrimPrefix(strings.TrimPrefix(token, "-"), "-")
140 | if newToken == "6" {
141 | return newToken, 0
142 | }
143 | argName := strings.Replace(newToken, "_", "-", -1)
144 | if boolType, ok := validArgs[argName]; ok {
145 | if boolType {
146 | return argName, 0
147 | }
148 | return argName, 1
149 | }
150 | return argName, -1
151 | } else if lastAction == 1 {
152 | return token, 0
153 | }
154 | return token, 2
155 | }
156 |
157 | func ParseArgs() (map[string]string, []string, bool) {
158 | var lastAction int
159 | var lastArg string
160 | var extra []string
161 | argPair := map[string]string{}
162 | for _, token := range os.Args[1:] {
163 | groups := reValidPair.FindStringSubmatch(token)
164 | if strings.HasPrefix(token, "-") && (strings.Contains(token, "=") || groups != nil) {
165 | if lastAction == 1 {
166 | return argPair, extra, true
167 | }
168 | thisArgName := strings.Split(token, "=")[0]
169 | if groups != nil {
170 | thisArgName = groups[1]
171 | }
172 | cleaned, action := whatToDo(thisArgName, lastAction)
173 | if action == 1 {
174 | if groups != nil {
175 | argPair[cleaned] = groups[2]
176 | } else {
177 | argPair[cleaned] = strings.Replace(token, thisArgName+"=", "", 1)
178 | }
179 | } else if action == 0 {
180 | argPair[cleaned] = ""
181 | } else if action == 2 {
182 | extra = append(extra, cleaned)
183 | } else if action == -1 {
184 | return argPair, extra, true
185 | }
186 | lastArg = cleaned
187 | lastAction = action
188 | continue
189 | }
190 | cleaned, action := whatToDo(token, lastAction)
191 | if action == 2 {
192 | extra = append(extra, cleaned)
193 | } else if action == 1 {
194 | lastArg = cleaned
195 | } else if action == -1 {
196 | return argPair, extra, true
197 | } else if action == 0 && lastAction == 1 {
198 | argPair[lastArg] = cleaned
199 | }
200 | lastAction = action
201 | }
202 | if lastAction == 1 {
203 | return argPair, extra, true
204 | }
205 | return argPair, extra, false
206 | }
207 |
--------------------------------------------------------------------------------
/internal/core/common.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | type Contender struct {
4 | Service string `json:"service"`
5 | Cpes []string `json:"cpes"`
6 | Protocol string `json:"protocol"`
7 | Softmatch bool `json:"softmatch"`
8 | Product string `json:"product,omitempty"`
9 | Heuristic []int `json:"heuristic,omitempty"`
10 | Os string `json:"os,omitempty"`
11 | Devicetype string `json:"devicetype,omitempty"`
12 | Ports []int `json:"ports,omitempty"`
13 | Sslports []int `json:"sslports,omitempty"`
14 | Ssl bool `json:"ssl,omitempty"`
15 | Score int `json:"score,omitempty"`
16 | }
17 |
--------------------------------------------------------------------------------
/internal/core/correlate.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "strconv"
5 | "strings"
6 |
7 | g "github.com/s0md3v/smap/internal/global"
8 | )
9 |
10 | var Probes []g.Contender
11 | var Table map[string]string
12 |
13 | func deleteString(s []string, i int) []string {
14 | return append(s[:i], s[i+1:]...)
15 | }
16 |
17 | func containsInt(array []int, item int) bool {
18 | for _, thisItem := range array {
19 | if thisItem == item {
20 | return true
21 | }
22 | }
23 | return false
24 | }
25 |
26 | func Correlate(ports []int, cpes []string) ([]g.Port, g.OS) {
27 | contenders := map[int]g.Contender{}
28 | used_cpes := map[string]int{}
29 | result := []g.Port{}
30 | var thisOS g.OS
31 | duplicateMap := map[string][]int{} // {joined_cpe: [score, port]}
32 | for _, service := range Probes {
33 | cpeMatched := false
34 | thisContender := service
35 | for _, cpe := range service.Cpes {
36 | minus := len(service.Cpes)
37 | for _, shodanCpe := range cpes {
38 | if strings.HasPrefix(shodanCpe, cpe) {
39 | minus--
40 | if strings.HasPrefix(shodanCpe, "cpe:/a") {
41 | cpeMatched = true
42 | }
43 | if strings.Count(cpe, ":") < 3 {
44 | thisContender.Score += 1
45 | } else {
46 | thisContender.Score += 2
47 | }
48 | }
49 | }
50 | thisContender.Score -= minus
51 | }
52 | if !cpeMatched {
53 | continue
54 | }
55 | if !service.Softmatch {
56 | thisContender.Score--
57 | }
58 | for _, port := range ports {
59 | tempContender := thisContender
60 | if containsInt(service.Heuristic, port) {
61 | tempContender.Score += 3
62 | }
63 | if containsInt(service.Ports, port) {
64 | tempContender.Score += 2
65 | }
66 | if containsInt(service.Sslports, port) {
67 | tempContender.Score += 2
68 | tempContender.Ssl = true
69 | }
70 | if tempContender.Score > contenders[port].Score {
71 | failed := false
72 | for _, cpe := range tempContender.Cpes {
73 | if bestScore, ok := used_cpes[cpe]; ok {
74 | if tempContender.Score < bestScore {
75 | failed = true
76 | }
77 | }
78 | }
79 | if failed {
80 | continue
81 | }
82 | joinedCpes := strings.Join(tempContender.Cpes, "")
83 | if scoreAndPort, ok := duplicateMap[joinedCpes]; ok {
84 | localScore, localPort := scoreAndPort[0], scoreAndPort[1]
85 | if tempContender.Score > localScore {
86 | duplicateMap[joinedCpes] = []int{tempContender.Score, port}
87 | delete(contenders, localPort)
88 | } else {
89 | continue
90 | }
91 | } else {
92 | duplicateMap[joinedCpes] = []int{tempContender.Score, port}
93 | }
94 | if tempContender.OS != "" {
95 | thisOS.Port = port
96 | thisOS.Name = tempContender.OS
97 | thisOS.Cpes = []string{}
98 | for _, cpe := range tempContender.Cpes {
99 | if strings.HasPrefix(cpe, "cpe:/o") {
100 | thisOS.Cpes = append(thisOS.Cpes, cpe)
101 | }
102 | }
103 | }
104 | tempContender.Ports = []int{}
105 | tempContender.Sslports = []int{}
106 | tempContender.Heuristic = []int{}
107 | contenders[port] = tempContender
108 | for _, cpe := range tempContender.Cpes {
109 | used_cpes[cpe] = tempContender.Score
110 | }
111 | }
112 | }
113 | }
114 | orphan_ports := []int{}
115 | for port, contender := range contenders {
116 | thisPort := g.Port{}
117 | thisPort.Port = port
118 | thisPort.Service = contender.Service
119 | thisPort.Protocol = contender.Protocol
120 | thisPort.Product = contender.Product
121 | thisPort.Ssl = contender.Ssl
122 | thisPort.Cpes = []string{}
123 | replaceWith := cpes
124 | for _, cpe := range contender.Cpes {
125 | cpes = replaceWith
126 | for index, shodanCpe := range cpes {
127 | if strings.HasPrefix(shodanCpe, cpe) {
128 | thisPort.Cpes = append(thisPort.Cpes, shodanCpe)
129 | if strings.Count(shodanCpe, ":") > 3 {
130 | thisPort.Version = strings.Split(shodanCpe, ":")[4]
131 | }
132 | replaceWith = deleteString(cpes, index)
133 | break
134 | }
135 | }
136 | }
137 | result = append(result, thisPort)
138 | }
139 | for _, port := range ports {
140 | if _, ok := contenders[port]; !ok {
141 | orphan_ports = append(orphan_ports, port)
142 | }
143 | }
144 | for _, port := range orphan_ports {
145 | dummyPort := g.Port{}
146 | dummyPort.Port = port
147 | if value, ok := Table[strconv.Itoa(port)]; ok {
148 | dummyPort.Service = value + "?"
149 | }
150 | dummyPort.Protocol = "tcp"
151 | result = append(result, dummyPort)
152 | }
153 | return result, thisOS
154 | }
155 |
--------------------------------------------------------------------------------
/internal/core/manager.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "bufio"
5 | "fmt"
6 | "net"
7 | "os"
8 | "regexp"
9 | "strconv"
10 | "strings"
11 | "sync"
12 | "time"
13 |
14 | "encoding/json"
15 |
16 | "github.com/s0md3v/smap/internal/db"
17 | g "github.com/s0md3v/smap/internal/global"
18 | o "github.com/s0md3v/smap/internal/output"
19 | "github.com/weppos/publicsuffix-go/publicsuffix"
20 | )
21 |
22 | var (
23 | activeScans sync.WaitGroup
24 | activeOutputs sync.WaitGroup
25 | activeEnders sync.WaitGroup
26 | activeObjects sync.WaitGroup
27 | targetsChannel = make(chan scanObject, 3)
28 | outputChannel = make(chan g.Output, 1000)
29 | reAddressRange = regexp.MustCompile(`^\d{1,3}(-\d{1,3})?\.\d{1,3}(-\d{1,3})?\.\d{1,3}(-\d{1,3})?\.\d{1,3}(-\d{1,3})?$`)
30 | )
31 |
32 | type scanObject struct {
33 | IP string
34 | Ports []int
35 | Hostname string
36 | }
37 |
38 | type respone struct {
39 | Cpes []string `json:"cpes"`
40 | Hostnames []string `json:"hostnames"`
41 | IP string `json:"ip"`
42 | Ports []int `json:"ports"`
43 | Tags []string `json:"tags"`
44 | Vulns []string `json:"vulns"`
45 | }
46 |
47 | func getPorts() []int {
48 | thesePorts := []int{}
49 | if value, ok := g.Args["p"]; ok {
50 | for _, port := range strings.Split(value, ",") {
51 | intPort, err := strconv.Atoi(port)
52 | if err == nil && intPort >= 0 && intPort <= 65535 {
53 | thesePorts = append(thesePorts, intPort)
54 | } else {
55 | fmt.Fprint(os.Stderr, "' ' is not a valid port number.\nQUITTING!\n")
56 | os.Exit(1)
57 | }
58 | }
59 | }
60 | return thesePorts
61 | }
62 |
63 | func isIPv4(str string) bool {
64 | parsed := net.ParseIP(str)
65 | if parsed == nil {
66 | return false
67 | }
68 | return reAddressRange.MatchString(str)
69 | }
70 |
71 | func isHostname(str string) bool {
72 | _, err := publicsuffix.Domain(str)
73 | return err == nil
74 | }
75 |
76 | func isAddressRange(str string) bool {
77 | if !reAddressRange.MatchString(str) {
78 | return false
79 | }
80 | for _, part := range strings.Split(str, ".") {
81 | for _, each := range strings.Split(part, "-") {
82 | if each[0] == 48 { // 48 is 0 in decimal
83 | return false
84 | }
85 | n, _ := strconv.Atoi(each)
86 | if n > 255 {
87 | return false
88 | }
89 | }
90 | }
91 | return true
92 | }
93 |
94 | func hostnameToIP(hostname string) string {
95 | ips, _ := net.LookupIP(hostname)
96 | if len(ips) > 0 {
97 | return ips[0].String()
98 | }
99 | return ""
100 | }
101 |
102 | func incIP(ip net.IP) {
103 | for j := len(ip) - 1; j >= 0; j-- {
104 | ip[j]++
105 | if ip[j] > 0 {
106 | break
107 | }
108 | }
109 | }
110 |
111 | func handleOutput() {
112 | var (
113 | startOutput []func()
114 | continueOutput []func(g.Output)
115 | endOutput []func()
116 | )
117 |
118 | activeEnders.Add(1)
119 | if value, ok := g.Args["oA"]; ok {
120 | activeEnders.Add(2)
121 | if value == "-" {
122 | fmt.Fprint(os.Stderr, "Cannot display multiple output types to stdout.\nQUITTING!\n")
123 | os.Exit(1)
124 | } else {
125 | g.XmlFilename = value + ".xml"
126 | g.GrepFilename = value + ".gnmap"
127 | g.Args["oN"] = value + ".nmap"
128 | }
129 | startOutput = []func(){o.StartXML, o.StartGrep, o.StartNmap}
130 | continueOutput = []func(g.Output){o.ContinueXML, o.ContinueGrep, o.ContinueNmap}
131 | endOutput = []func(){o.EndXML, o.EndGrep, o.EndNmap}
132 | } else if value, ok := g.Args["oX"]; ok {
133 | startOutput = []func(){o.StartXML}
134 | continueOutput = []func(g.Output){o.ContinueXML}
135 | endOutput = []func(){o.EndXML}
136 | g.XmlFilename = value
137 | } else if value, ok := g.Args["oG"]; ok {
138 | startOutput = []func(){o.StartGrep}
139 | continueOutput = []func(g.Output){o.ContinueGrep}
140 | endOutput = []func(){o.EndGrep}
141 | g.GrepFilename = value
142 | } else if value, ok := g.Args["oJ"]; ok {
143 | startOutput = []func(){o.StartJson}
144 | continueOutput = []func(g.Output){o.ContinueJson}
145 | endOutput = []func(){o.EndJson}
146 | g.JsonFilename = value
147 | } else if value, ok := g.Args["oS"]; ok {
148 | startOutput = []func(){o.StartSmap}
149 | continueOutput = []func(g.Output){o.ContinueSmap}
150 | endOutput = []func(){o.EndSmap}
151 | g.SmapFilename = value
152 | } else if value, ok := g.Args["oP"]; ok {
153 | startOutput = []func(){o.StartPair}
154 | continueOutput = []func(g.Output){o.ContinuePair}
155 | endOutput = []func(){o.EndPair}
156 | g.PairFilename = value
157 | } else {
158 | startOutput = []func(){o.StartNmap}
159 | continueOutput = []func(g.Output){o.ContinueNmap}
160 | endOutput = []func(){o.EndNmap}
161 | }
162 | for _, function := range startOutput {
163 | function()
164 | }
165 | for output := range outputChannel {
166 | for _, function := range continueOutput {
167 | function(output)
168 | }
169 | activeOutputs.Done()
170 | }
171 | for _, function := range endOutput {
172 | function()
173 | activeEnders.Done()
174 | }
175 | }
176 |
177 | func scanner() {
178 | threads := make(chan bool, 3)
179 | for target := range targetsChannel {
180 | threads <- true
181 | go func(target scanObject) {
182 | processScanObject(target)
183 | activeScans.Done()
184 | <-threads
185 | }(target)
186 | }
187 | }
188 |
189 | func createScanObjects(object string) {
190 | activeScans.Add(1)
191 | var oneObject scanObject
192 | oneObject.Ports = g.PortList
193 | if isIPv4(object) {
194 | oneObject.IP = object
195 | targetsChannel <- oneObject
196 | } else if strings.Contains(object, "/") && isIPv4(strings.Split(object, "/")[0]) {
197 | activeScans.Done()
198 | ip, ipnet, err := net.ParseCIDR(object)
199 | if err != nil {
200 | return
201 | }
202 | for ip := ip.Mask(ipnet.Mask); ipnet.Contains(ip); incIP(ip) {
203 | oneObject.IP = ip.String()
204 | activeScans.Add(1)
205 | targetsChannel <- oneObject
206 | }
207 | } else if isHostname(object) {
208 | ip := hostnameToIP(object)
209 | if ip != "" {
210 | oneObject.IP = ip
211 | oneObject.Hostname = object
212 | targetsChannel <- oneObject
213 | } else {
214 | activeScans.Done()
215 | }
216 | } else if isAddressRange(object) {
217 | return
218 | } else {
219 | activeScans.Done()
220 | }
221 | }
222 |
223 | func processScanObject(object scanObject) {
224 | g.Increment(0)
225 | scanStarted := time.Now()
226 | response := Query(object.IP)
227 | var output g.Output
228 | if len(response) < 50 {
229 | return
230 | } else {
231 | activeOutputs.Add(1)
232 | }
233 | var data respone
234 | json.Unmarshal(response, &data)
235 | output.IP = data.IP
236 | output.Tags = data.Tags
237 | output.Vulns = data.Vulns
238 | output.Hostnames = data.Hostnames
239 | output.UHostname = object.Hostname
240 | filteredPorts := []int{}
241 | if len(object.Ports) > 0 {
242 | for _, port := range data.Ports {
243 | if containsInt(object.Ports, port) {
244 | filteredPorts = append(filteredPorts, port)
245 | }
246 | }
247 | if len(filteredPorts) == 0 {
248 | return
249 | }
250 | } else {
251 | filteredPorts = data.Ports
252 | }
253 | output.Ports, output.OS = Correlate(filteredPorts, data.Cpes)
254 | output.Start = scanStarted
255 | output.End = time.Now()
256 | g.Increment(1)
257 | outputChannel <- output
258 | }
259 |
260 | func Init() {
261 | args, extra, invalid := ParseArgs()
262 | if invalid {
263 | fmt.Println("One or more of your arguments are invalid. Refer to docs.\nQUITTING!")
264 | os.Exit(1)
265 | } else if _, ok := args["h"]; ok || len(os.Args) == 1 {
266 | fmt.Print(db.HelpText)
267 | os.Exit(0)
268 | }
269 | g.Args = args
270 | json.Unmarshal(db.NmapSigs, &Probes)
271 | json.Unmarshal(db.NmapTable, &Table)
272 | g.PortList = getPorts()
273 | g.ScanStartTime = time.Now()
274 | go scanner()
275 | go handleOutput()
276 | if value, ok := g.Args["iL"]; ok {
277 | scanner := bufio.NewScanner(os.Stdin)
278 | if value != "-" {
279 | file, err := os.Open(value)
280 | if err != nil {
281 | os.Exit(1)
282 | }
283 | defer file.Close()
284 | scanner = bufio.NewScanner(file)
285 | }
286 | for scanner.Scan() {
287 | createScanObjects(scanner.Text())
288 | }
289 |
290 | if err := scanner.Err(); err != nil {
291 | os.Exit(1)
292 | }
293 | } else if len(extra) != 0 {
294 | threads := make(chan bool, 3)
295 | for _, arg := range extra {
296 | activeObjects.Add(1)
297 | threads <- true
298 | go func(object string) {
299 | createScanObjects(object)
300 | <-threads
301 | activeObjects.Done()
302 | }(arg)
303 | }
304 | activeObjects.Wait()
305 | } else {
306 | fmt.Println("WARNING: No targets were specified, so 0 hosts scanned.")
307 | }
308 | activeScans.Wait()
309 | close(targetsChannel)
310 | g.ScanEndTime = time.Now()
311 | activeOutputs.Wait()
312 | close(outputChannel)
313 | activeEnders.Wait()
314 | }
315 |
--------------------------------------------------------------------------------
/internal/core/shodan.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "net"
5 | "time"
6 |
7 | "io/ioutil"
8 | "net/http"
9 | )
10 |
11 | var client = &http.Client{
12 | Transport: &http.Transport{
13 | Dial: (&net.Dialer{
14 | Timeout: 8 * time.Second,
15 | }).Dial,
16 | TLSHandshakeTimeout: 3 * time.Second,
17 | ResponseHeaderTimeout: 5 * time.Second,
18 | ExpectContinueTimeout: 1 * time.Second,
19 | },
20 | }
21 |
22 | func Query(ip string) []byte {
23 | url := "https://internetdb.shodan.io/" + ip
24 | req, err := http.NewRequest("GET", url, nil)
25 | resp, err := client.Do(req)
26 | if err != nil {
27 | return []byte{}
28 | }
29 | content, err := ioutil.ReadAll(resp.Body)
30 | if err != nil {
31 | return []byte{}
32 | }
33 | req.Close = true
34 | defer resp.Body.Close()
35 | return content
36 | }
37 |
--------------------------------------------------------------------------------
/internal/db/help_text.go:
--------------------------------------------------------------------------------
1 | package db
2 |
3 | import (
4 | "fmt"
5 | g "github.com/s0md3v/smap/internal/global"
6 | )
7 |
8 | var HelpText = fmt.Sprintf(`Smap %s
9 | Usage: smap
10 | TARGET SPECIFICATION:
11 | Valid targets are hostnames, IP addresses, networks, etc.
12 | Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1
13 | -iL : Input from list of hosts/networks. Use - as filename to use stdin input.
14 | OUTPUT:
15 | Specify a file to write the output or use - as filename to write it to stdout (terminal).
16 | Ex: -oX
17 | -oX XML
18 | -oG Greppable
19 | -oN Nmap
20 | -oA All 3 above
21 | -oJ JSON
22 | -oS Smap format
23 | -oP ip:port pairs
24 | `, g.Version)
--------------------------------------------------------------------------------
/internal/global/types.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | type Contender struct {
8 | Service string `json:"service"`
9 | Cpes []string `json:"cpes"`
10 | Protocol string `json:"protocol"`
11 | Softmatch bool `json:"softmatch"`
12 | Product string `json:"product,omitempty"`
13 | Heuristic []int `json:"heuristic,omitempty"`
14 | OS string `json:"os,omitempty"`
15 | Devicetype string `json:"devicetype,omitempty"`
16 | Ports []int `json:"ports,omitempty"`
17 | Sslports []int `json:"sslports,omitempty"`
18 | Ssl bool `json:"ssl,omitempty"`
19 | Score int `json:"score,omitempty"`
20 | }
21 |
22 | type OS struct {
23 | Name string `json:"name"`
24 | Cpes []string `json:"cpes"`
25 | Port int `json:"port"`
26 | }
27 | type Output struct {
28 | IP string `json:"ip"`
29 | Hostnames []string `json:"hostnames"`
30 | UHostname string `json:"user_hostname,omitempty"`
31 | Ports []Port `json:"ports"`
32 | Tags []string `json:"tags,omitempty"`
33 | Vulns []string `json:"vulns,omitempty"`
34 | Start time.Time `json:"start_time"`
35 | End time.Time `json:"end_time"`
36 | OS OS `json:"os,omitempty"`
37 | }
38 |
39 | type Port struct {
40 | Port int `json:"port"`
41 | Service string `json:"service"`
42 | Cpes []string `json:"cpes"`
43 | Protocol string `json:"protocol"`
44 | Product string `json:"product,omitempty"`
45 | Version string `json:"version,omitempty"`
46 | Ssl bool `json:"ssl,omitempty"`
47 | }
48 |
--------------------------------------------------------------------------------
/internal/global/variables.go:
--------------------------------------------------------------------------------
1 | package global
2 |
3 | import (
4 | "sync/atomic"
5 | "time"
6 | )
7 |
8 | type count32 int32
9 |
10 | func (c *count32) inc() int32 {
11 | return atomic.AddInt32((*int32)(c), 1)
12 | }
13 |
14 | func Increment(counterType int) {
15 | if counterType == 0 {
16 | TotalHosts.inc()
17 | } else {
18 | AliveHosts.inc()
19 | }
20 | }
21 |
22 | var (
23 | PortList []int
24 | ScanStartTime time.Time
25 | ScanEndTime time.Time
26 | XmlFilename string
27 | GrepFilename string
28 | NmapFilename string
29 | JsonFilename string
30 | SmapFilename string
31 | PairFilename string
32 | Args map[string]string
33 | TotalHosts count32
34 | AliveHosts count32
35 | Version = "0.1.0-rc"
36 | )
37 |
--------------------------------------------------------------------------------
/internal/output/common.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 | "time"
8 | )
9 |
10 | func GetCommand() string {
11 | return "nmap " + strings.Join(os.Args[1:], " ")
12 | }
13 |
14 | func ConvertTime(unixTime time.Time, format string) string {
15 | if format == "nmap-file" {
16 | parts := strings.Split(strings.Replace(unixTime.Format(time.RFC1123), ",", "", 1), " ")
17 | return fmt.Sprintf("%s %s %s %s %s", parts[0], parts[2], parts[1], parts[4], parts[3])
18 | } else if format == "nmap-stdout" {
19 | rawDate := strings.Split(unixTime.Format(time.RFC3339), "T")[0]
20 | formattedDate := strings.Replace(rawDate, ":", "-", -1)
21 | parts := strings.Split(unixTime.Format(time.RFC822), " ")
22 | return fmt.Sprintf("%s %s %s", formattedDate, parts[3], parts[4])
23 | }
24 | return fmt.Sprintf("%d", unixTime.Unix())
25 | }
26 |
27 | func OpenFile(filepath string) *os.File {
28 | f, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
29 | if err != nil {
30 | fmt.Fprint(os.Stderr, fmt.Sprintf("Failed to open output file %s for writing\n", filepath))
31 | fmt.Fprint(os.Stderr, "QUITTING!\n")
32 | os.Exit(1)
33 | }
34 | return f
35 | }
36 |
37 | func Write(str string, dest string, openedFile *os.File) {
38 | if dest == "-" {
39 | fmt.Print(str)
40 | return
41 | }
42 | openedFile.WriteString(str)
43 | }
44 |
--------------------------------------------------------------------------------
/internal/output/grep.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 | "time"
8 |
9 | g "github.com/s0md3v/smap/internal/global"
10 | )
11 |
12 | var openedGrepFile *os.File
13 |
14 | func StartGrep() {
15 | if g.GrepFilename != "-" {
16 | openedGrepFile = OpenFile(g.GrepFilename)
17 | }
18 | startstr := ConvertTime(g.ScanStartTime, "nmap-file")
19 | Write(fmt.Sprintf("# Nmap 9.99 scan initiated %s as: %s\n", startstr, GetCommand()), g.GrepFilename, openedGrepFile)
20 | }
21 |
22 | func ContinueGrep(result g.Output) {
23 | hostname := ""
24 | if len(result.Hostnames) > 0 {
25 | hostname = result.Hostnames[0]
26 | }
27 | hostPrefix := fmt.Sprintf("Host: %s (%s)", result.IP, hostname)
28 | if hostname == "" {
29 | hostPrefix += " "
30 | }
31 | entireString := fmt.Sprintf("%s Status: Up\n", hostPrefix)
32 | thesePorts := []string{}
33 | for _, port := range result.Ports {
34 | thisPort := fmt.Sprintf("%d/open/%s//%s//%s", port.Port, port.Protocol, port.Service, port.Product)
35 | if port.Version != "" {
36 | thisPort += fmt.Sprintf(" %s/", port.Version)
37 | } else {
38 | thisPort += "/"
39 | }
40 | thesePorts = append(thesePorts, thisPort)
41 | }
42 | entireString += fmt.Sprintf("%s Ports: %s\n", hostPrefix, strings.Join(thesePorts, ", "))
43 | Write(entireString, g.GrepFilename, openedGrepFile)
44 | }
45 |
46 | func EndGrep() {
47 | elapsed := fmt.Sprintf("%.2f", time.Since(g.ScanStartTime).Seconds())
48 | esTotal := ""
49 | if g.TotalHosts > 1 {
50 | esTotal = "es"
51 | }
52 | sAlive := ""
53 | if g.AliveHosts > 1 {
54 | sAlive = "s"
55 | }
56 | Write(fmt.Sprintf("# Nmap done at %s -- %d IP address%s (%d host%s up) scanned in %s seconds\n", ConvertTime(g.ScanEndTime, "nmap-file"), g.TotalHosts, esTotal, g.AliveHosts, sAlive, elapsed), g.GrepFilename, openedGrepFile)
57 | defer openedGrepFile.Close()
58 | }
59 |
--------------------------------------------------------------------------------
/internal/output/json.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "encoding/json"
5 | "os"
6 |
7 | g "github.com/s0md3v/smap/internal/global"
8 | )
9 |
10 | var firstDone = false
11 | var openedJsonFile *os.File
12 |
13 | func StartJson() {
14 | if g.JsonFilename != "-" {
15 | openedGrepFile = OpenFile(g.JsonFilename)
16 | }
17 | Write("[", g.JsonFilename, openedJsonFile)
18 | }
19 |
20 | func ContinueJson(result g.Output) {
21 | prefix := ""
22 | if firstDone {
23 | prefix = ","
24 | }
25 | firstDone = true
26 | jsoned, _ := json.Marshal(&result)
27 | Write(prefix+string(jsoned), g.JsonFilename, openedJsonFile)
28 | }
29 |
30 | func EndJson() {
31 | Write("]", g.JsonFilename, openedJsonFile)
32 | defer openedJsonFile.Close()
33 | }
34 |
--------------------------------------------------------------------------------
/internal/output/nmap.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strconv"
7 | "strings"
8 | "time"
9 |
10 | g "github.com/s0md3v/smap/internal/global"
11 | )
12 |
13 | var openedNmapFile *os.File
14 |
15 | func pad(str string, n int) string {
16 | return strings.Repeat(" ", n) + str
17 | }
18 |
19 | func StartNmap() {
20 | if value, ok := g.Args["oN"]; ok {
21 | openedNmapFile = OpenFile(value)
22 | startstr := ConvertTime(g.ScanStartTime, "nmap-file")
23 | Write(fmt.Sprintf("# Starting Nmap 9.99 ( https://nmap.org ) at %s as: %s\n", startstr, GetCommand()), value, openedNmapFile)
24 | } else {
25 | startstr := ConvertTime(g.ScanStartTime, "nmap-stdout")
26 | Write(fmt.Sprintf("Starting Nmap 9.99 ( https://nmap.org ) at %s\n", startstr), "-", openedNmapFile)
27 | }
28 | }
29 |
30 | func ContinueNmap(result g.Output) {
31 | longestPort := 5
32 | longestService := 7
33 | for _, port := range result.Ports {
34 | strPort := strconv.Itoa(port.Port)
35 | if len(strPort)+4 > longestPort {
36 | longestPort = len(strPort) + 4
37 | }
38 | if len(port.Service) > longestService {
39 | longestService = len(port.Service)
40 | }
41 | }
42 | thisOutput := ""
43 | if result.UHostname != "" {
44 | thisOutput += fmt.Sprintf("Nmap scan report for %s (%s)\nHost is up.\n", result.UHostname, result.IP)
45 | if len(result.Hostnames) > 0 {
46 | thisOutput += fmt.Sprintf("rDNS record for %s: %s\n\n", result.IP, result.Hostnames[0])
47 | }
48 | } else if len(result.Hostnames) > 0 {
49 | thisOutput += fmt.Sprintf("Nmap scan report for %s (%s)\nHost is up.\n\n", result.Hostnames[0], result.IP)
50 | } else {
51 | thisOutput += fmt.Sprintf("Nmap scan report for %s\nHost is up.\n\n", result.IP)
52 | }
53 | thisOutput += fmt.Sprintf("PORT %sSTATE SERVICE %sVERSION\n", pad("", longestPort-4), pad(" ", longestService-7))
54 | serviceString := ""
55 | for _, port := range result.Ports {
56 | strPort := fmt.Sprintf("%d/%s", port.Port, port.Protocol)
57 | productLine := ""
58 | if port.Product != "" {
59 | productLine += port.Product
60 | if port.Version != "" {
61 | productLine += " " + port.Version
62 | }
63 | }
64 | thisOutput += fmt.Sprintf("%s%s %s%s\n", strPort, pad("open", longestPort-len(strPort)+1), port.Service, pad(productLine, longestService-len(port.Service)+2))
65 | if result.OS.Name != "" && result.OS.Port == port.Port {
66 | serviceString += fmt.Sprintf("Service Info: OS: %s", result.OS.Name)
67 | if len(result.OS.Cpes) > 0 {
68 | for _, cpe := range result.OS.Cpes {
69 | if strings.Contains(cpe, strings.ToLower(result.OS.Name)) {
70 | serviceString += fmt.Sprintf("; CPE: %s", cpe)
71 | break
72 | }
73 | }
74 | }
75 | serviceString += "\n"
76 | }
77 | }
78 | thisOutput += serviceString
79 | thisOutput += "\n"
80 | if value, ok := g.Args["oN"]; ok {
81 | Write(thisOutput, value, openedNmapFile)
82 | } else {
83 | Write(thisOutput, "-", openedNmapFile)
84 | }
85 | }
86 |
87 | func EndNmap() {
88 | elapsed := fmt.Sprintf("%.2f", time.Since(g.ScanStartTime).Seconds())
89 | footer := "Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .\n"
90 | esTotal := ""
91 | if g.TotalHosts > 1 {
92 | esTotal = "es"
93 | }
94 | sAlive := ""
95 | if g.AliveHosts > 1 {
96 | sAlive = "s"
97 | }
98 | if value, ok := g.Args["oN"]; ok {
99 | endstr := ConvertTime(g.ScanEndTime, "nmap-file")
100 | footer += fmt.Sprintf("# Nmap done at %s -- %d IP address%s (%d host%s up) scanned in %s seconds\n", endstr, g.TotalHosts, esTotal, g.AliveHosts, sAlive, elapsed)
101 | Write(footer, value, openedNmapFile)
102 | } else {
103 | footer += fmt.Sprintf("Nmap done: %d IP address%s (%d host%s up) scanned in %s seconds\n", g.TotalHosts, esTotal, g.AliveHosts, sAlive, elapsed)
104 | Write(footer, "-", openedNmapFile)
105 | }
106 | defer openedNmapFile.Close()
107 | }
108 |
--------------------------------------------------------------------------------
/internal/output/pair.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "os"
6 |
7 | g "github.com/s0md3v/smap/internal/global"
8 | )
9 |
10 | var openedPairFile *os.File
11 |
12 | func StartPair() {
13 | if g.PairFilename != "-" {
14 | openedPairFile = OpenFile(g.PairFilename)
15 | }
16 | }
17 |
18 | func ContinuePair(result g.Output) {
19 | thisString := ""
20 | for _, port := range result.Ports {
21 | thisString += fmt.Sprintf("%s:%d\n", result.IP, port.Port)
22 | }
23 | Write(thisString, g.PairFilename, openedPairFile)
24 | }
25 |
26 | func EndPair() {
27 | }
28 |
--------------------------------------------------------------------------------
/internal/output/smap.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | g "github.com/s0md3v/smap/internal/global"
9 | )
10 |
11 | var openedSmapFile *os.File
12 |
13 | func StartSmap() {
14 | if g.SmapFilename != "-" {
15 | openedSmapFile = OpenFile(g.SmapFilename)
16 | }
17 | Write(fmt.Sprintf("\n\tSmap (%s)\n", g.Version), g.SmapFilename, openedSmapFile)
18 | }
19 |
20 | func ContinueSmap(result g.Output) {
21 | thisString := ""
22 | hostnames := result.Hostnames
23 | if result.UHostname != "" {
24 | hostnames = append(hostnames, result.UHostname)
25 | }
26 | if len(hostnames) != 0 {
27 | thisString += fmt.Sprintf("\n+ %s (%s)\n", result.IP, strings.Join(hostnames, ", "))
28 | } else {
29 | thisString += fmt.Sprintf("%s\n", result.IP)
30 | }
31 | if result.OS.Name != "" {
32 | thisString += fmt.Sprintf(" - OS: %s\n", result.OS.Name)
33 | }
34 | if len(result.Tags) != 0 {
35 | thisString += fmt.Sprintf(" - Tags: %s\n", strings.Join(result.Tags, ", "))
36 | }
37 | thisString += " + Ports:\n"
38 | for _, port := range result.Ports {
39 | thisString += fmt.Sprintf(" - %d %s", port.Port, port.Protocol)
40 | if port.Service != "" {
41 | thisString += fmt.Sprintf("/%s ", port.Service)
42 | } else {
43 | thisString += " "
44 | }
45 | if len(port.Cpes) != 0 {
46 | thisString += strings.Join(port.Cpes, " ")
47 | }
48 | thisString += "\n"
49 | }
50 | if len(result.Vulns) != 0 {
51 | thisString += fmt.Sprintf(" - Vulns: %s\n", strings.Join(result.Vulns, ", "))
52 | }
53 | Write(thisString, g.SmapFilename, openedSmapFile)
54 | }
55 |
56 | func EndSmap() {
57 | }
58 |
--------------------------------------------------------------------------------
/internal/output/xml.go:
--------------------------------------------------------------------------------
1 | package output
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 | "time"
8 |
9 | g "github.com/s0md3v/smap/internal/global"
10 | )
11 |
12 | var openedXmlFile *os.File
13 | var shodanPortList = "0,7,11,13,15,17,19-26,37-38,43,49,51,53,69-70,79-92,95-100,102,104,106,110-111,113,119,121,123,129,131,135,137,139,143,154,161,175,179-180,195,199,211,221-222,225,263-264,311,340,389,443-445,447-450,465,491,500,502-503,515,520,522-523,541,548,554-555,587,593,623,626,631,636,646,666,675,685,771-772,777,789,800-801,805-806,808,830,843,873,880,888,902,943,990,992-995,999-1000,1010,1012,1022-1029,1050,1063,1080,1099,1110-1111,1119,1167,1177,1194,1200,1234,1250,1290,1311,1344,1355,1366,1388,1400,1433-1434,1471,1494,1500,1515,1521,1554,1588,1599,1604,1650,1660,1723,1741,1777,1800,1820,1830,1833,1883,1900-1901,1911,1935,1947,1950-1951,1962,1981,1990-1991,2000-2003,2006,2008,2010,2012,2018,2020-2022,2030,2048-2070,2077,2079-2083,2086-2087,2095-2096,2100,2111,2121-2123,2126,2150,2152,2181,2200-2202,2211,2220-2223,2225,2232-2233,2250,2259,2266,2320,2323,2332,2345,2351-2352,2375-2376,2379,2382,2404,2443,2455,2480,2506,2525,2548-2563,2566-2570,2572,2598,2601-2602,2626,2628,2650,2701,2709,2761-2762,2806,2985,3000-3002,3005,3048-3063,3066-3121,3128-3129,3200,3211,3221,3260,3270,3283,3299,3306-3307,3310-3311,3333,3337,3352,3386,3388-3389,3391,3400-3410,3412,3443,3460,3479,3498,3503,3521-3524,3541-3542,3548-3552,3554-3563,3566-3570,3671,3689-3690,3702,3749,3780,3784,3790-3794,3838,3910,3922,3950-3954,4000-4002,4010,4022,4040,4042-4043,4063-4064,4070,4100,4117-4118,4157,4190,4200,4242-4243,4282,4321,4369,4430,4433,4443-4445,4482,4500,4505-4506,4523-4524,4545,4550,4567,4643,4646,4664,4700,4730,4734,4747,4782,4786,4800,4808,4840,4848,4911,4949,4999-5010,5025,5050,5060,5070,5080,5090,5094,5122,5150,5172,5190,5201,5209,5222,5269,5280,5321,5353,5357,5400,5431-5432,5443,5446,5454,5494,5500,5542,5552,5555,5560,5567-5569,5577,5590-5609,5632,5672-5673,5683-5684,5800-5801,5822,5853,5858,5900-5901,5906-5910,5938,5984-5986,6000-6010,6036,6080,6102,6161,6262,6264,6308,6352,6363,6379,6443,6464,6503,6510-6512,6543,6550,6560-6561,6565,6580-6581,6588,6590,6600-6603,6605,6622,6650,6662,6664,6666-6668,6697,6748,6789,6881,6887,6955,6969,6998,7000-7005,7010,7014,7070-7071,7080-7081,7090,7170-7171,7218,7401,7415,7433,7443-7445,7465,7474,7493,7500,7510,7535,7537,7547-7548,7634,7654,7657,7676,7700,7776-7779,7788,7887,7979,7998-8058,8060,8064,8066,8069,8071-8072,8080-8112,8118,8123,8126,8139-8140,8143,8159,8180-8182,8184,8190,8200,8222,8236-8239,8241,8243,8248-8249,8251-8252,8282,8291,8333-8334,8383,8401-8433,8442-8448,8500,8513,8545,8553-8554,8585-8586,8590,8602,8621-8623,8637,8649,8663,8666,8686,8688,8700,8733,8765-8767,8779,8782,8784,8787-8791,8800-8881,8885,8887-8891,8899,8935,8969,8988-8991,8993,8999-9051,9070,9080,9082,9084,9088-9111,9119,9136,9151,9160,9189,9191,9199-9222,9251,9295,9299-9311,9389,9418,9433,9443-9445,9500,9527,9530,9550,9595,9600,9606,9633,9663,9682,9690,9704,9743,9761,9765,9861,9869,9876,9898-9899,9943-9944,9950,9955,9966,9981,9988,9990-9994,9997-10001,10134,10243,10250,10443,10554,11112,11211,11300,12000,12345,13579,14147,14265,14344,16010,16464,16992-16993,17000,18081,18245,20000,20087,20256,20547,21025,21379,22222,23023,23424,25105,25565,27015-27017,27036,28015,28017,30718,32400,32764,33060,33338,37215,37777,41794,44818,47808,48899,49152-49153,50000,50050,50070,50100,51106,51235,52869,53413,54138,54984,55442-55443,55553-55554,60001,60129,62078,64738"
14 |
15 | func StartXML() {
16 | if g.XmlFilename != "-" {
17 | openedXmlFile = OpenFile(g.XmlFilename)
18 | }
19 | portsLen := 1237
20 | portsStr := shodanPortList
21 | if value, ok := g.Args["p"]; ok {
22 | portsLen = len(g.PortList)
23 | portsStr = value
24 | }
25 | startstr := ConvertTime(g.ScanStartTime, "nmap-file")
26 | Write(fmt.Sprintf(`
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 | `, startstr, GetCommand(), GetCommand(), g.ScanStartTime.Unix(), startstr, portsLen, portsStr), g.XmlFilename, openedXmlFile)
35 | }
36 |
37 | func portToXML(port g.Port, result g.Output) string {
38 | thisString := fmt.Sprintf(``, port.Protocol, port.Port)
39 | if port.Service != "" {
40 | thisString += fmt.Sprintf(``, result.OS.Name)
52 | } else if strings.HasSuffix(port.Service, "?") {
53 | thisString += ` method="table" conf="3">`
54 | } else {
55 | thisString += ` method="probed" conf="8">`
56 | }
57 | for _, cpe := range port.Cpes {
58 | thisString += fmt.Sprintf(`%s`, cpe)
59 | }
60 | thisString += "\n"
61 | }
62 | return thisString
63 | }
64 |
65 | func ContinueXML(result g.Output) {
66 | thisOutput := ""
67 | thisOutput += fmt.Sprintf(`
68 |
69 |
70 | `, result.Start.Unix(), result.End.Unix(), result.IP)
71 | for _, hostname := range result.Hostnames {
72 | thisOutput += fmt.Sprintf("\n", hostname)
73 | }
74 | if result.UHostname != "" {
75 | thisOutput += fmt.Sprintf("\n", result.UHostname)
76 | }
77 | thisOutput += "\n"
78 | for _, port := range result.Ports {
79 | thisOutput += portToXML(port, result)
80 | }
81 | thisOutput += "\n\n\n"
82 | Write(thisOutput, g.XmlFilename, openedXmlFile)
83 | }
84 |
85 | func EndXML() {
86 | timestr := ConvertTime(g.ScanEndTime, "nmap-file")
87 | elapsed := fmt.Sprintf("%.2f", time.Since(g.ScanStartTime).Seconds())
88 | Write(fmt.Sprintf(`
89 |
90 |
91 | `, g.ScanEndTime.Unix(), timestr, elapsed, timestr, g.TotalHosts, g.AliveHosts, elapsed, g.AliveHosts, g.TotalHosts-g.AliveHosts, g.TotalHosts), g.XmlFilename, openedXmlFile)
92 | defer openedXmlFile.Close()
93 | }
94 |
--------------------------------------------------------------------------------
/internal/util/util.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "fmt"
5 | "net"
6 | "os"
7 | "strconv"
8 | "strings"
9 | "time"
10 | )
11 |
12 | func RemoveByIndex(array []string, index int) []string {
13 | array[index] = array[len(array)-1]
14 | return array[:len(array)-1]
15 | }
16 |
17 | func Contains(array []string, item string) bool {
18 | for _, thisItem := range array {
19 | if thisItem == item {
20 | return true
21 | }
22 | }
23 | return false
24 | }
25 |
--------------------------------------------------------------------------------
/static/smap-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blackhatethicalhacking/Smap/dab48f2a6d2e868c6c0c98417154235d472c1614/static/smap-demo.png
--------------------------------------------------------------------------------
/static/smap-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/blackhatethicalhacking/Smap/dab48f2a6d2e868c6c0c98417154235d472c1614/static/smap-logo.png
--------------------------------------------------------------------------------