"
13 | RUN addgroup -S rustscan && \
14 | adduser -S -G rustscan rustscan && \
15 | ulimit -n 100000 && \
16 | apk add --no-cache nmap nmap-scripts wget
17 | COPY --from=builder /usr/local/cargo/bin/rustscan /usr/local/bin/rustscan
18 | USER rustscan
19 | ENTRYPOINT [ "/usr/local/bin/rustscan" ]
20 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | install:
2 | cargo install cross
3 |
4 | build:
5 | make build-linux
6 | make build-mac
7 |
8 | make shasum
9 |
10 | build-linux:
11 | @echo 'Building for Linux... 🐧'
12 | cross build --release --target=x86_64-unknown-linux-musl
13 | mkdir -p target/release-archives && tar -C target/x86_64-unknown-linux-musl/release -czf target/release-archives/rustscan-linux.tar.gz rustscan
14 |
15 | build-mac:
16 | @echo 'Building for MacOS... 🍏'
17 | cross build --release --target=x86_64-apple-darwin
18 | mkdir -p target/release-archives && tar -C target/x86_64-apple-darwin/release -czf target/release-archives/rustscan-mac.tar.gz rustscan
19 |
20 | shasum:
21 | shasum -a 256 target/release-archives/rustscan-*.tar.gz
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ➡️
3 | Discord |
4 | Installation Guide |
5 | Usage Guide
6 | ⬅️
7 |
8 |
9 |
10 |
11 | The Modern Port Scanner.
Fast, smart, effective.
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | | 🐋 Docker (Recommended)
| 👩💻 Kali / Debian
| 🏗️ Arch
| 🔧 Homebrew
|
24 | | ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
25 | | 
| 
| 
| 
|
26 | | `docker pull rustscan/rustscan:2.0.0` [Usage](https://github.com/RustScan/RustScan#docker-whale) | [Read the install guide](https://github.com/Rustscan/RustScan/blob/master/README.md#%EF%B8%8F-debian--kali) | `yay -S rustscan` | `brew install rustscan` |
27 |
28 |
29 |
30 | # 🤔 What is this?
31 |
32 | 
33 |
34 | The Modern Port Scanner. **Find ports quickly (3 seconds at its fastest)**. Run scripts through our scripting engine (Python, Lua, Shell supported).
35 |
36 | # ✨ Features
37 |
38 | - Scans all 65k ports in **3 seconds**.
39 | - Full scripting engine support. Automatically pipe results into Nmap, or use our scripts (or write your own) to do whatever you want.
40 | - Adaptive learning. RustScan improves the more you use it. No bloated machine learning here, just basic maths.
41 | - The usuals you would expect. IPv6, CIDR, file input and more.
42 | - Automatically pipes ports into Nmap.
43 |
44 | ## ‼️ Important Links
45 |
46 | | Installation Guide | Documentation | Discord |
47 | | -------------------------------------------------------------------------------------- | -------------------------------------------------------- | ---------------------------------------- |
48 | | 📖 [Installation Guide](https://github.com/RustScan/RustScan#-full-installation-guide) | 📚 [Documentation](https://rustscan.github.io/RustScan/) | 🦜 [Discord](http://discord.skerritt.blog) |
49 |
50 | ## 🙋 Table of Contents
51 |
52 | - 📖 [Installation Guide](https://github.com/RustScan/RustScan/wiki/Installation-Guide)
53 | - 🐋 [Docker Usage](https://github.com/RustScan/RustScan/wiki/Installation-Guide)
54 | - 🦜 [Discord](http://discord.skerritt.blog)
55 | - 🤸 [Usage](https://github.com/RustScan/RustScan/wiki/Usage)
56 | - 🎪 [Community](https://github.com/RustScan/RustScan#-community)
57 |
58 | # 🔭 Why RustScan?
59 |
60 | RustScan is a modern take on the port scanner. Sleek & fast. All while providing extensive extendability to you.
61 |
62 | Not to mention RustScan uses Adaptive Learning to improve itself over time, making it the best port scanner for **you**.
63 |
64 | ## 🧋 Speed
65 |
66 | 
67 |
68 | Speed is guaranteed via RustScan. However, if you want to run a slow scan due to stealth that is possible too.
69 |
70 | Firstly, let's talk code.
71 |
72 | We have tests that check to see if RustScan is significantly slower than the previous version. If it is, the continuous integration fails and we can't commit code to master unless we make it faster.
73 |
74 | [HyperFine](https://github.com/sharkdp/hyperfine) is used to monitor RustScan's performance over time to answer the question "Are we getting faster? Are we getting slower?".
75 |
76 | Every pull request is reviewed by 1 person, but more often than not 2 people review it. We test it manually and make sure the code doesn't affect performance negatively.
77 |
78 | [Read more here](https://github.com/RustScan/RustScan/wiki/Increasing-Speed-&-Accuracy).
79 |
80 | ## ⚙️ Extensible
81 |
82 | 
83 |
84 | _RustScan piping results into the custom Python script_
85 |
86 | RustScan has a new scripting engine which allows anyone to write scripts in most languages. Python, Lua, Shell are all supported.
87 |
88 | Want to take your found ports and pipe them into Nmap for further analysis? That's possible. Want to run `smb-enum` if SMB is found open? Possible.
89 |
90 | The possibilities are endless -- and you can write scripts in whatever language you feel comfortable with.
91 |
92 | [Read more here](https://github.com/RustScan/RustScan/wiki/RustScan-Scripting-Engine).
93 |
94 | ## 🌊 Adaptive
95 |
96 | 
97 |
98 | _RustScan automatically fine-tuning itself to match the host OS_.
99 |
100 | RustScan has a cool set of features called "Adaptive Learning". These features "learn" about the environment you are scanning and how _you_ use RustScan to **improve itself over time**.
101 |
102 | This is an umbrella term we use for any feature that fits this criteria. The list is constantly changing, so [check out our wiki for more information](https://github.com/RustScan/RustScan/wiki/Adaptive-Learning).
103 |
104 | ## 👩🦯 Accessible
105 |
106 | 
107 |
108 | RustScan is one of the first penetration testing tools that aims to be entirely accessible.
109 |
110 | [Most penetration testing tools are not accessible](https://bees.substack.com/p/making-hacking-accessible), which negatively affects the whole industry.
111 |
112 | RustScan has continuous integration testing that aims to make sure it is accessible, and we are constantly working on ways to improve our accessibility and make sure _everyone_ can use RustScan.
113 |
114 | # 📖 Full Installation Guide
115 |
116 | You can find our guide [here](https://github.com/RustScan/RustScan/wiki/Installation-Guide).
117 |
118 | ## 🦊 Community Distributions
119 |
120 | Here are all of RustScan's community distributions.
121 |
122 | If you maintain a community distribution and want it listed here, leave an issue / pull request / Discord message or however you want to let us know.
123 |
124 | - [OpenSuse](https://software.opensuse.org/package/rustscan?search_term=rustscan)
125 | - [Fedora/CentOS](https://copr.fedorainfracloud.org/coprs/atim/rustscan/)
126 |
127 | [](https://repology.org/project/rustscan/versions)
128 |
129 | # 🤸 Usage
130 |
131 | We have 2 usage guides. [Basic Usage](https://github.com/RustScan/RustScan/wiki/Usage) and [Things you may want to do](https://github.com/RustScan/RustScan/wiki/Things-you-may-want-to-do-with-RustScan-but-don't-understand-how).
132 |
133 | We also have documentation about our config file [here](https://github.com/RustScan/RustScan/wiki/Config-File).
134 |
135 | # 🎪 Community
136 |
137 | [Read this to learn how to contribute](https://github.com/RustScan/RustScan/wiki/Contributing).
138 |
139 | ## Contributors ✨
140 |
141 |
142 | [](#contributors-)
143 |
144 |
145 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
146 |
147 |
148 |
149 |
150 |
186 |
187 |
188 |
189 |
190 |
191 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
192 |
--------------------------------------------------------------------------------
/config.toml:
--------------------------------------------------------------------------------
1 | ip="127.0.0.1"
2 |
3 | # The hashmap of {ports: {1:1}}
4 | [ports]
5 | 1 = 1
6 | 3 = 1
7 | 4 = 1
8 | 6 = 1
9 | 7 = 1
10 | 9 = 1
11 | 13 = 1
12 | 17 = 1
13 | 19 = 1
14 | 20 = 1
15 | 21 = 1
16 | 22 = 1
17 | 23 = 1
18 | 24 = 1
19 | 25 = 1
20 | 26 = 1
21 | 30 = 1
22 | 32 = 1
23 | 33 = 1
24 | 37 = 1
25 | 42 = 1
26 | 43 = 1
27 | 49 = 1
28 | 53 = 1
29 | 70 = 1
30 | 79 = 1
31 | 80 = 1
32 | 81 = 1
33 | 82 = 1
34 | 83 = 1
35 | 84 = 1
36 | 85 = 1
37 | 88 = 1
38 | 89 = 1
39 | 90 = 1
40 | 99 = 1
41 | 100 = 1
42 | 106 = 1
43 | 109 = 1
44 | 110 = 1
45 | 111 = 1
46 | 113 = 1
47 | 119 = 1
48 | 125 = 1
49 | 135 = 1
50 | 139 = 1
51 | 143 = 1
52 | 144 = 1
53 | 146 = 1
54 | 161 = 1
55 | 163 = 1
56 | 179 = 1
57 | 199 = 1
58 | 211 = 1
59 | 212 = 1
60 | 222 = 1
61 | 254 = 1
62 | 255 = 1
63 | 256 = 1
64 | 259 = 1
65 | 264 = 1
66 | 280 = 1
67 | 301 = 1
68 | 306 = 1
69 | 311 = 1
70 | 340 = 1
71 | 366 = 1
72 | 389 = 1
73 | 406 = 1
74 | 407 = 1
75 | 416 = 1
76 | 417 = 1
77 | 425 = 1
78 | 427 = 1
79 | 443 = 1
80 | 444 = 1
81 | 445 = 1
82 | 458 = 1
83 | 464 = 1
84 | 465 = 1
85 | 481 = 1
86 | 497 = 1
87 | 500 = 1
88 | 512 = 1
89 | 513 = 1
90 | 514 = 1
91 | 515 = 1
92 | 524 = 1
93 | 541 = 1
94 | 543 = 1
95 | 544 = 1
96 | 545 = 1
97 | 548 = 1
98 | 554 = 1
99 | 555 = 1
100 | 563 = 1
101 | 587 = 1
102 | 593 = 1
103 | 616 = 1
104 | 617 = 1
105 | 625 = 1
106 | 631 = 1
107 | 636 = 1
108 | 646 = 1
109 | 648 = 1
110 | 666 = 1
111 | 667 = 1
112 | 668 = 1
113 | 683 = 1
114 | 687 = 1
115 | 691 = 1
116 | 700 = 1
117 | 705 = 1
118 | 711 = 1
119 | 714 = 1
120 | 720 = 1
121 | 722 = 1
122 | 726 = 1
123 | 749 = 1
124 | 765 = 1
125 | 777 = 1
126 | 783 = 1
127 | 787 = 1
128 | 800 = 1
129 | 801 = 1
130 | 808 = 1
131 | 843 = 1
132 | 873 = 1
133 | 880 = 1
134 | 888 = 1
135 | 898 = 1
136 | 900 = 1
137 | 901 = 1
138 | 902 = 1
139 | 903 = 1
140 | 911 = 1
141 | 912 = 1
142 | 981 = 1
143 | 987 = 1
144 | 990 = 1
145 | 992 = 1
146 | 993 = 1
147 | 995 = 1
148 | 999 = 1
149 | 1000 = 1
150 | 1001 = 1
151 | 1002 = 1
152 | 1007 = 1
153 | 1009 = 1
154 | 1010 = 1
155 | 1011 = 1
156 | 1021 = 1
157 | 1022 = 1
158 | 1023 = 1
159 | 1024 = 1
160 | 1025 = 1
161 | 1026 = 1
162 | 1027 = 1
163 | 1028 = 1
164 | 1029 = 1
165 | 1030 = 1
166 | 1031 = 1
167 | 1032 = 1
168 | 1033 = 1
169 | 1034 = 1
170 | 1035 = 1
171 | 1036 = 1
172 | 1037 = 1
173 | 1038 = 1
174 | 1039 = 1
175 | 1040 = 1
176 | 1041 = 1
177 | 1042 = 1
178 | 1043 = 1
179 | 1044 = 1
180 | 1045 = 1
181 | 1046 = 1
182 | 1047 = 1
183 | 1048 = 1
184 | 1049 = 1
185 | 1050 = 1
186 | 1051 = 1
187 | 1052 = 1
188 | 1053 = 1
189 | 1054 = 1
190 | 1055 = 1
191 | 1056 = 1
192 | 1057 = 1
193 | 1058 = 1
194 | 1059 = 1
195 | 1060 = 1
196 | 1061 = 1
197 | 1062 = 1
198 | 1063 = 1
199 | 1064 = 1
200 | 1065 = 1
201 | 1066 = 1
202 | 1067 = 1
203 | 1068 = 1
204 | 1069 = 1
205 | 1070 = 1
206 | 1071 = 1
207 | 1072 = 1
208 | 1073 = 1
209 | 1074 = 1
210 | 1075 = 1
211 | 1076 = 1
212 | 1077 = 1
213 | 1078 = 1
214 | 1079 = 1
215 | 1080 = 1
216 | 1081 = 1
217 | 1082 = 1
218 | 1083 = 1
219 | 1084 = 1
220 | 1085 = 1
221 | 1086 = 1
222 | 1087 = 1
223 | 1088 = 1
224 | 1089 = 1
225 | 1090 = 1
226 | 1091 = 1
227 | 1092 = 1
228 | 1093 = 1
229 | 1094 = 1
230 | 1095 = 1
231 | 1096 = 1
232 | 1097 = 1
233 | 1098 = 1
234 | 1099 = 1
235 | 1100 = 1
236 | 1102 = 1
237 | 1104 = 1
238 | 1105 = 1
239 | 1106 = 1
240 | 1107 = 1
241 | 1108 = 1
242 | 1110 = 1
243 | 1111 = 1
244 | 1112 = 1
245 | 1113 = 1
246 | 1114 = 1
247 | 1117 = 1
248 | 1119 = 1
249 | 1121 = 1
250 | 1122 = 1
251 | 1123 = 1
252 | 1124 = 1
253 | 1126 = 1
254 | 1130 = 1
255 | 1131 = 1
256 | 1132 = 1
257 | 1137 = 1
258 | 1138 = 1
259 | 1141 = 1
260 | 1145 = 1
261 | 1147 = 1
262 | 1148 = 1
263 | 1149 = 1
264 | 1151 = 1
265 | 1152 = 1
266 | 1154 = 1
267 | 1163 = 1
268 | 1164 = 1
269 | 1165 = 1
270 | 1166 = 1
271 | 1169 = 1
272 | 1174 = 1
273 | 1175 = 1
274 | 1183 = 1
275 | 1185 = 1
276 | 1186 = 1
277 | 1187 = 1
278 | 1192 = 1
279 | 1198 = 1
280 | 1199 = 1
281 | 1201 = 1
282 | 1213 = 1
283 | 1216 = 1
284 | 1217 = 1
285 | 1218 = 1
286 | 1233 = 1
287 | 1234 = 1
288 | 1236 = 1
289 | 1244 = 1
290 | 1247 = 1
291 | 1248 = 1
292 | 1259 = 1
293 | 1271 = 1
294 | 1272 = 1
295 | 1277 = 1
296 | 1287 = 1
297 | 1296 = 1
298 | 1300 = 1
299 | 1301 = 1
300 | 1309 = 1
301 | 1310 = 1
302 | 1311 = 1
303 | 1322 = 1
304 | 1328 = 1
305 | 1334 = 1
306 | 1352 = 1
307 | 1417 = 1
308 | 1433 = 1
309 | 1434 = 1
310 | 1443 = 1
311 | 1455 = 1
312 | 1461 = 1
313 | 1494 = 1
314 | 1500 = 1
315 | 1501 = 1
316 | 1503 = 1
317 | 1521 = 1
318 | 1524 = 1
319 | 1533 = 1
320 | 1556 = 1
321 | 1580 = 1
322 | 1583 = 1
323 | 1594 = 1
324 | 1600 = 1
325 | 1641 = 1
326 | 1658 = 1
327 | 1666 = 1
328 | 1687 = 1
329 | 1688 = 1
330 | 1700 = 1
331 | 1717 = 1
332 | 1718 = 1
333 | 1719 = 1
334 | 1720 = 1
335 | 1721 = 1
336 | 1723 = 1
337 | 1755 = 1
338 | 1761 = 1
339 | 1782 = 1
340 | 1783 = 1
341 | 1801 = 1
342 | 1805 = 1
343 | 1812 = 1
344 | 1839 = 1
345 | 1840 = 1
346 | 1862 = 1
347 | 1863 = 1
348 | 1864 = 1
349 | 1875 = 1
350 | 1900 = 1
351 | 1914 = 1
352 | 1935 = 1
353 | 1947 = 1
354 | 1971 = 1
355 | 1972 = 1
356 | 1974 = 1
357 | 1984 = 1
358 | 1998 = 1
359 | 1999 = 1
360 | 2000 = 1
361 | 2001 = 1
362 | 2002 = 1
363 | 2003 = 1
364 | 2004 = 1
365 | 2005 = 1
366 | 2006 = 1
367 | 2007 = 1
368 | 2008 = 1
369 | 2009 = 1
370 | 2010 = 1
371 | 2013 = 1
372 | 2020 = 1
373 | 2021 = 1
374 | 2022 = 1
375 | 2030 = 1
376 | 2033 = 1
377 | 2034 = 1
378 | 2035 = 1
379 | 2038 = 1
380 | 2040 = 1
381 | 2041 = 1
382 | 2042 = 1
383 | 2043 = 1
384 | 2045 = 1
385 | 2046 = 1
386 | 2047 = 1
387 | 2048 = 1
388 | 2049 = 1
389 | 2065 = 1
390 | 2068 = 1
391 | 2099 = 1
392 | 2100 = 1
393 | 2103 = 1
394 | 2105 = 1
395 | 2106 = 1
396 | 2107 = 1
397 | 2111 = 1
398 | 2119 = 1
399 | 2121 = 1
400 | 2126 = 1
401 | 2135 = 1
402 | 2144 = 1
403 | 2160 = 1
404 | 2161 = 1
405 | 2170 = 1
406 | 2179 = 1
407 | 2190 = 1
408 | 2191 = 1
409 | 2196 = 1
410 | 2200 = 1
411 | 2222 = 1
412 | 2251 = 1
413 | 2260 = 1
414 | 2288 = 1
415 | 2301 = 1
416 | 2323 = 1
417 | 2366 = 1
418 | 2381 = 1
419 | 2382 = 1
420 | 2383 = 1
421 | 2393 = 1
422 | 2394 = 1
423 | 2399 = 1
424 | 2401 = 1
425 | 2492 = 1
426 | 2500 = 1
427 | 2522 = 1
428 | 2525 = 1
429 | 2557 = 1
430 | 2601 = 1
431 | 2602 = 1
432 | 2604 = 1
433 | 2605 = 1
434 | 2607 = 1
435 | 2608 = 1
436 | 2638 = 1
437 | 2701 = 1
438 | 2702 = 1
439 | 2710 = 1
440 | 2717 = 1
441 | 2718 = 1
442 | 2725 = 1
443 | 2800 = 1
444 | 2809 = 1
445 | 2811 = 1
446 | 2869 = 1
447 | 2875 = 1
448 | 2909 = 1
449 | 2910 = 1
450 | 2920 = 1
451 | 2967 = 1
452 | 2968 = 1
453 | 2998 = 1
454 | 3000 = 1
455 | 3001 = 1
456 | 3003 = 1
457 | 3005 = 1
458 | 3006 = 1
459 | 3007 = 1
460 | 3011 = 1
461 | 3013 = 1
462 | 3017 = 1
463 | 3030 = 1
464 | 3031 = 1
465 | 3052 = 1
466 | 3071 = 1
467 | 3077 = 1
468 | 3128 = 1
469 | 3168 = 1
470 | 3211 = 1
471 | 3221 = 1
472 | 3260 = 1
473 | 3261 = 1
474 | 3268 = 1
475 | 3269 = 1
476 | 3283 = 1
477 | 3300 = 1
478 | 3301 = 1
479 | 3306 = 1
480 | 3322 = 1
481 | 3323 = 1
482 | 3324 = 1
483 | 3325 = 1
484 | 3333 = 1
485 | 3351 = 1
486 | 3367 = 1
487 | 3369 = 1
488 | 3370 = 1
489 | 3371 = 1
490 | 3372 = 1
491 | 3389 = 1
492 | 3390 = 1
493 | 3404 = 1
494 | 3476 = 1
495 | 3493 = 1
496 | 3517 = 1
497 | 3527 = 1
498 | 3546 = 1
499 | 3551 = 1
500 | 3580 = 1
501 | 3659 = 1
502 | 3689 = 1
503 | 3690 = 1
504 | 3703 = 1
505 | 3737 = 1
506 | 3766 = 1
507 | 3784 = 1
508 | 3800 = 1
509 | 3801 = 1
510 | 3809 = 1
511 | 3814 = 1
512 | 3826 = 1
513 | 3827 = 1
514 | 3828 = 1
515 | 3851 = 1
516 | 3869 = 1
517 | 3871 = 1
518 | 3878 = 1
519 | 3880 = 1
520 | 3889 = 1
521 | 3905 = 1
522 | 3914 = 1
523 | 3918 = 1
524 | 3920 = 1
525 | 3945 = 1
526 | 3971 = 1
527 | 3986 = 1
528 | 3995 = 1
529 | 3998 = 1
530 | 4000 = 1
531 | 4001 = 1
532 | 4002 = 1
533 | 4003 = 1
534 | 4004 = 1
535 | 4005 = 1
536 | 4006 = 1
537 | 4045 = 1
538 | 4111 = 1
539 | 4125 = 1
540 | 4126 = 1
541 | 4129 = 1
542 | 4224 = 1
543 | 4242 = 1
544 | 4279 = 1
545 | 4321 = 1
546 | 4343 = 1
547 | 4443 = 1
548 | 4444 = 1
549 | 4445 = 1
550 | 4446 = 1
551 | 4449 = 1
552 | 4550 = 1
553 | 4567 = 1
554 | 4662 = 1
555 | 4848 = 1
556 | 4899 = 1
557 | 4900 = 1
558 | 4998 = 1
559 | 5000 = 1
560 | 5001 = 1
561 | 5002 = 1
562 | 5003 = 1
563 | 5004 = 1
564 | 5009 = 1
565 | 5030 = 1
566 | 5033 = 1
567 | 5050 = 1
568 | 5051 = 1
569 | 5054 = 1
570 | 5060 = 1
571 | 5061 = 1
572 | 5080 = 1
573 | 5087 = 1
574 | 5100 = 1
575 | 5101 = 1
576 | 5102 = 1
577 | 5120 = 1
578 | 5190 = 1
579 | 5200 = 1
580 | 5214 = 1
581 | 5221 = 1
582 | 5222 = 1
583 | 5225 = 1
584 | 5226 = 1
585 | 5269 = 1
586 | 5280 = 1
587 | 5298 = 1
588 | 5357 = 1
589 | 5405 = 1
590 | 5414 = 1
591 | 5431 = 1
592 | 5432 = 1
593 | 5440 = 1
594 | 5500 = 1
595 | 5510 = 1
596 | 5544 = 1
597 | 5550 = 1
598 | 5555 = 1
599 | 5560 = 1
600 | 5566 = 1
601 | 5631 = 1
602 | 5633 = 1
603 | 5666 = 1
604 | 5678 = 1
605 | 5679 = 1
606 | 5718 = 1
607 | 5730 = 1
608 | 5800 = 1
609 | 5801 = 1
610 | 5802 = 1
611 | 5810 = 1
612 | 5811 = 1
613 | 5815 = 1
614 | 5822 = 1
615 | 5825 = 1
616 | 5850 = 1
617 | 5859 = 1
618 | 5862 = 1
619 | 5877 = 1
620 | 5900 = 1
621 | 5901 = 1
622 | 5902 = 1
623 | 5903 = 1
624 | 5904 = 1
625 | 5906 = 1
626 | 5907 = 1
627 | 5910 = 1
628 | 5911 = 1
629 | 5915 = 1
630 | 5922 = 1
631 | 5925 = 1
632 | 5950 = 1
633 | 5952 = 1
634 | 5959 = 1
635 | 5960 = 1
636 | 5961 = 1
637 | 5962 = 1
638 | 5963 = 1
639 | 5987 = 1
640 | 5988 = 1
641 | 5989 = 1
642 | 5998 = 1
643 | 5999 = 1
644 | 6000 = 1
645 | 6001 = 1
646 | 6002 = 1
647 | 6003 = 1
648 | 6004 = 1
649 | 6005 = 1
650 | 6006 = 1
651 | 6007 = 1
652 | 6009 = 1
653 | 6025 = 1
654 | 6059 = 1
655 | 6100 = 1
656 | 6101 = 1
657 | 6106 = 1
658 | 6112 = 1
659 | 6123 = 1
660 | 6129 = 1
661 | 6156 = 1
662 | 6346 = 1
663 | 6389 = 1
664 | 6502 = 1
665 | 6510 = 1
666 | 6543 = 1
667 | 6547 = 1
668 | 6565 = 1
669 | 6566 = 1
670 | 6567 = 1
671 | 6580 = 1
672 | 6646 = 1
673 | 6666 = 1
674 | 6667 = 1
675 | 6668 = 1
676 | 6669 = 1
677 | 6689 = 1
678 | 6692 = 1
679 | 6699 = 1
680 | 6779 = 1
681 | 6788 = 1
682 | 6789 = 1
683 | 6792 = 1
684 | 6839 = 1
685 | 6881 = 1
686 | 6901 = 1
687 | 6969 = 1
688 | 7000 = 1
689 | 7001 = 1
690 | 7002 = 1
691 | 7004 = 1
692 | 7007 = 1
693 | 7019 = 1
694 | 7025 = 1
695 | 7070 = 1
696 | 7100 = 1
697 | 7103 = 1
698 | 7106 = 1
699 | 7200 = 1
700 | 7201 = 1
701 | 7402 = 1
702 | 7435 = 1
703 | 7443 = 1
704 | 7496 = 1
705 | 7512 = 1
706 | 7625 = 1
707 | 7627 = 1
708 | 7676 = 1
709 | 7741 = 1
710 | 7777 = 1
711 | 7778 = 1
712 | 7800 = 1
713 | 7911 = 1
714 | 7920 = 1
715 | 7921 = 1
716 | 7937 = 1
717 | 7938 = 1
718 | 7999 = 1
719 | 8000 = 1
720 | 8001 = 1
721 | 8002 = 1
722 | 8007 = 1
723 | 8008 = 1
724 | 8009 = 1
725 | 8010 = 1
726 | 8011 = 1
727 | 8021 = 1
728 | 8022 = 1
729 | 8031 = 1
730 | 8042 = 1
731 | 8045 = 1
732 | 8080 = 1
733 | 8081 = 1
734 | 8082 = 1
735 | 8083 = 1
736 | 8084 = 1
737 | 8085 = 1
738 | 8086 = 1
739 | 8087 = 1
740 | 8088 = 1
741 | 8089 = 1
742 | 8090 = 1
743 | 8093 = 1
744 | 8099 = 1
745 | 8100 = 1
746 | 8180 = 1
747 | 8181 = 1
748 | 8192 = 1
749 | 8193 = 1
750 | 8194 = 1
751 | 8200 = 1
752 | 8222 = 1
753 | 8254 = 1
754 | 8290 = 1
755 | 8291 = 1
756 | 8292 = 1
757 | 8300 = 1
758 | 8333 = 1
759 | 8383 = 1
760 | 8400 = 1
761 | 8402 = 1
762 | 8443 = 1
763 | 8500 = 1
764 | 8600 = 1
765 | 8649 = 1
766 | 8651 = 1
767 | 8652 = 1
768 | 8654 = 1
769 | 8701 = 1
770 | 8800 = 1
771 | 8873 = 1
772 | 8888 = 1
773 | 8899 = 1
774 | 8994 = 1
775 | 9000 = 1
776 | 9001 = 1
777 | 9002 = 1
778 | 9003 = 1
779 | 9009 = 1
780 | 9010 = 1
781 | 9011 = 1
782 | 9040 = 1
783 | 9050 = 1
784 | 9071 = 1
785 | 9080 = 1
786 | 9081 = 1
787 | 9090 = 1
788 | 9091 = 1
789 | 9099 = 1
790 | 9100 = 1
791 | 9101 = 1
792 | 9102 = 1
793 | 9103 = 1
794 | 9110 = 1
795 | 9111 = 1
796 | 9200 = 1
797 | 9207 = 1
798 | 9220 = 1
799 | 9290 = 1
800 | 9415 = 1
801 | 9418 = 1
802 | 9485 = 1
803 | 9500 = 1
804 | 9502 = 1
805 | 9503 = 1
806 | 9535 = 1
807 | 9575 = 1
808 | 9593 = 1
809 | 9594 = 1
810 | 9595 = 1
811 | 9618 = 1
812 | 9666 = 1
813 | 9876 = 1
814 | 9877 = 1
815 | 9878 = 1
816 | 9898 = 1
817 | 9900 = 1
818 | 9917 = 1
819 | 9929 = 1
820 | 9943 = 1
821 | 9944 = 1
822 | 9968 = 1
823 | 9998 = 1
824 | 9999 = 1
825 | 10000 = 1
826 | 10001 = 1
827 | 10002 = 1
828 | 10003 = 1
829 | 10004 = 1
830 | 10009 = 1
831 | 10010 = 1
832 | 10012 = 1
833 | 10024 = 1
834 | 10025 = 1
835 | 10082 = 1
836 | 10180 = 1
837 | 10215 = 1
838 | 10243 = 1
839 | 10566 = 1
840 | 10616 = 1
841 | 10617 = 1
842 | 10621 = 1
843 | 10626 = 1
844 | 10628 = 1
845 | 10629 = 1
846 | 10778 = 1
847 | 11110 = 1
848 | 11111 = 1
849 | 11967 = 1
850 | 12000 = 1
851 | 12174 = 1
852 | 12265 = 1
853 | 12345 = 1
854 | 13456 = 1
855 | 13722 = 1
856 | 13782 = 1
857 | 13783 = 1
858 | 14000 = 1
859 | 14238 = 1
860 | 14441 = 1
861 | 14442 = 1
862 | 15000 = 1
863 | 15002 = 1
864 | 15003 = 1
865 | 15004 = 1
866 | 15660 = 1
867 | 15742 = 1
868 | 16000 = 1
869 | 16001 = 1
870 | 16012 = 1
871 | 16016 = 1
872 | 16018 = 1
873 | 16080 = 1
874 | 16113 = 1
875 | 16992 = 1
876 | 16993 = 1
877 | 17877 = 1
878 | 17988 = 1
879 | 18040 = 1
880 | 18101 = 1
881 | 18988 = 1
882 | 19101 = 1
883 | 19283 = 1
884 | 19315 = 1
885 | 19350 = 1
886 | 19780 = 1
887 | 19801 = 1
888 | 19842 = 1
889 | 20000 = 1
890 | 20005 = 1
891 | 20031 = 1
892 | 20221 = 1
893 | 20222 = 1
894 | 20828 = 1
895 | 21571 = 1
896 | 22939 = 1
897 | 23502 = 1
898 | 24444 = 1
899 | 24800 = 1
900 | 25734 = 1
901 | 25735 = 1
902 | 26214 = 1
903 | 27000 = 1
904 | 27352 = 1
905 | 27353 = 1
906 | 27355 = 1
907 | 27356 = 1
908 | 27715 = 1
909 | 28201 = 1
910 | 30000 = 1
911 | 30718 = 1
912 | 30951 = 1
913 | 31038 = 1
914 | 31337 = 1
915 | 32768 = 1
916 | 32769 = 1
917 | 32770 = 1
918 | 32771 = 1
919 | 32772 = 1
920 | 32773 = 1
921 | 32774 = 1
922 | 32775 = 1
923 | 32776 = 1
924 | 32777 = 1
925 | 32778 = 1
926 | 32779 = 1
927 | 32780 = 1
928 | 32781 = 1
929 | 32782 = 1
930 | 32783 = 1
931 | 32784 = 1
932 | 32785 = 1
933 | 33354 = 1
934 | 33899 = 1
935 | 34571 = 1
936 | 34572 = 1
937 | 34573 = 1
938 | 35500 = 1
939 | 38292 = 1
940 | 40193 = 1
941 | 40911 = 1
942 | 41511 = 1
943 | 42510 = 1
944 | 44176 = 1
945 | 44442 = 1
946 | 44443 = 1
947 | 44501 = 1
948 | 45100 = 1
949 | 48080 = 1
950 | 49152 = 1
951 | 49153 = 1
952 | 49154 = 1
953 | 49155 = 1
954 | 49156 = 1
955 | 49157 = 1
956 | 49158 = 1
957 | 49159 = 1
958 | 49160 = 1
959 | 49161 = 1
960 | 49163 = 1
961 | 49165 = 1
962 | 49167 = 1
963 | 49175 = 1
964 | 49176 = 1
965 | 49400 = 1
966 | 49999 = 1
967 | 50000 = 1
968 | 50001 = 1
969 | 50002 = 1
970 | 50003 = 1
971 | 50006 = 1
972 | 50300 = 1
973 | 50389 = 1
974 | 50500 = 1
975 | 50636 = 1
976 | 50800 = 1
977 | 51103 = 1
978 | 51493 = 1
979 | 52673 = 1
980 | 52822 = 1
981 | 52848 = 1
982 | 52869 = 1
983 | 54045 = 1
984 | 54328 = 1
985 | 55055 = 1
986 | 55056 = 1
987 | 55555 = 1
988 | 55600 = 1
989 | 56737 = 1
990 | 56738 = 1
991 | 57294 = 1
992 | 57797 = 1
993 | 58080 = 1
994 | 60020 = 1
995 | 60443 = 1
996 | 61532 = 1
997 | 61900 = 1
998 | 62078 = 1
999 | 63331 = 1
1000 | 64623 = 1
1001 | 64680 = 1
1002 | 65000 = 1
1003 | 65129 = 1
1004 | 65389 = 1
1005 |
--------------------------------------------------------------------------------
/contributing.Dockerfile:
--------------------------------------------------------------------------------
1 | FROM rust
2 | # Install nmap first.
3 | RUN apt-get update -qy && apt-get install -qy nmap
4 | # Then install rustfmt and clippy for cargo.
5 | RUN rustup component add rustfmt clippy
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | Howdy Space Cow-Person 🤠🌌
2 |
3 | RustScan is always looking for contributors. Whether that's spelling mistakes or major changes, your help is **wanted** and welcomed here.
4 |
5 | Before contributing, read our [code of conduct](https://github.com/RustScan/RustScan/blob/master/CODE_OF_CONDUCT.md).
6 |
7 | TL;DR if you abuse members of our community you will be **perma-banned** with no chance to get unbanned. No warnings either. 🤗
8 |
9 | RustScan has 2 major labels for GitHub issues you should look at:
10 | * Good First issue
11 | These are issues for newcomers to open source!
12 | [https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22)
13 | * Help wanted
14 | These are issues that aren't really for newcomers, but we could still do with help!
15 | [https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22](https://github.com/RustScan/RustScan/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22+label%3A%22help+wanted%22)
16 |
17 | If you want to, solve the issue or comment on the issue for help.
18 |
19 | The [flow for contributing](https://guides.github.com/introduction/flow/) to open source software is:
20 | * Fork the repo
21 | * Make changes
22 | * Pull request to the repo
23 |
24 | And then comment on the issue that you've done.
25 |
26 | RustScan also has some `// TODO`'s in the codebase, which are meant more for the core team but we wouldn't say no to help with these issues.
27 |
28 | If you have any feature suggestions or bugs, leave a GitHub issue. We welcome any and all support :D
29 |
30 | ## Rewarding you
31 | I cannot pay you :-( But, I can place your GitHub profile on the README under `#Contributors` as a thank you! :)
32 |
33 | ## Contributing development environment
34 |
35 | To ease contribution to RustScan, you can use the `contributing.Dockerfile` to create a Docker image ready to build and play with RustScan.
36 | To build it you just need to run:
37 |
38 | ```bash
39 | you@home:~/RustScan$ docker build -t rustscan_contributing -f contributing.Dockerfile
40 | ```
41 |
42 | Then you need to run the container with a volume so it can access, *with read and write permissions*, to RustScan files:
43 |
44 | ```bash
45 | you@home:~/RustScan$ docker run -ti --rm -v "$PWD":/rustscan -w /rustscan rustscan_contributing bash
46 | ```
47 |
48 | You can now modify RustScan files with your favorite editor, once you want to compile and test your modifications, type the following in the container prompt:
49 |
50 | ```bash
51 | root@container:/rustscan# cargo build
52 | ```
53 |
54 | You are now ready to use RustScan:
55 |
56 | ```bash
57 | root@container:/rustscan# cargo run -- -b 2000 -t 5000 -a 127.0.0.1
58 | ```
59 |
60 | You can also format, lint with `clippy` and test the code with the following commands:
61 |
62 | ```bash
63 | root@container:/rustscan# cargo fmt
64 | root@container:/rustscan# cargo clippy
65 | root@container:/rustscan# cargo test
66 | ```
--------------------------------------------------------------------------------
/fixtures/.rustscan_scripts/test_script.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | #tags = ["core_approved", "example",]
3 | #developer = [ "example", "https://example.org" ]
4 | #ports_separator = ","
5 | #call_format = "perl {{script}} {{ip}} {{port}}"
6 |
7 | # Sriptfile parser stops at the first blank line with parsing.
8 | # This script will run itself as an argument with the system installed perl interpreter, ports will be concatenated with "," .
9 | # Unused field: trigger_port = "80"
10 | # get total arg passed to this script
11 | my $total = $#ARGV + 1;
12 | my $counter = 1;
13 |
14 | # get script name
15 | my $scriptname = $0;
16 |
17 | print "Total args passed to $scriptname : $total\n";
18 |
19 | # Use loop to print all args stored in an array called @ARGV
20 | foreach my $a(@ARGV) {
21 | print "Arg # $counter : $a\n";
22 | $counter++;
23 | }
--------------------------------------------------------------------------------
/fixtures/.rustscan_scripts/test_script.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python3
2 | #tags = ["core_approved", "example",]
3 | #developer = [ "example", "https://example.org" ]
4 | #trigger_port = "80"
5 | #call_format = "python3 {{script}} {{ip}} {{port}}"
6 |
7 | # Sriptfile parser stops at the first blank line with parsing.
8 | # This script will run itself as an argument with the system installed python interpreter, only scanning port 80.
9 | # Unused filed: ports_separator = ","
10 |
11 | import sys
12 |
13 | print('Python script ran with arguments', str(sys.argv))
--------------------------------------------------------------------------------
/fixtures/.rustscan_scripts/test_script.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #tags = ["core_approved", "example",]
3 | #developer = [ "example", "https://example.org" ]
4 | #ports_separator = ","
5 | #call_format = "bash {{script}} {{ip}} {{port}}"
6 |
7 | # Sriptfile parser stops at the first blank line with parsing.
8 | # This script will run itself as an argument with the system installed bash interpreter, scanning all ports concatenated with "," .
9 | # Unused filed: trigger_port = "80"
10 |
11 | # print all arguments passed to the script
12 | echo $@
--------------------------------------------------------------------------------
/fixtures/.rustscan_scripts/test_script.txt:
--------------------------------------------------------------------------------
1 | #!intentional_blank_line
2 | #tags = ["core_approved", "example"]
3 | #developer = [ "example", "https://example.org" ]
4 | #ports_separator = ","
5 | #call_format = "nmap -vvv -p {{port}} {{ip}}"
6 |
7 | # Scriptfile parser stops at the first blank line with parsing.
8 | # This script will run the system installed nmap, ports will be concatenated with "," .
9 | # Unused field: trigger_port = "80"
--------------------------------------------------------------------------------
/fixtures/.rustscan_scripts/test_script_invalid_headers.txt:
--------------------------------------------------------------------------------
1 | #!intentional_blank_line
2 | #tags = "core_approved"
3 | #developer = [ "example", "https://example.org" ]
4 | #ports_separator = ","
5 | #call_format = "nmap -vvv -p {{port}} {{ip}}"
6 |
7 | # tags has to be an array, thus this file won't be parsed
--------------------------------------------------------------------------------
/fixtures/empty_hosts.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/fixtures/empty_hosts.txt
--------------------------------------------------------------------------------
/fixtures/hosts.txt:
--------------------------------------------------------------------------------
1 | 127.0.0.1
2 | google.com
3 | example.com
4 | 66666666666666.666666666666666666.66666666666666666.6666666666
5 | ....
6 | e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.radiatorFixtures
--------------------------------------------------------------------------------
/fixtures/naughty_strings.txt:
--------------------------------------------------------------------------------
1 | # https://github.com/minimaxir/big-list-of-naughty-strings
2 |
3 |
4 |
5 |
6 | Ω≈ç√∫˜µ≤≥÷
7 | åß∂ƒ©˙∆˚¬…æ
8 | œ∑´®†¥¨ˆøπ“‘
9 | ¡™£¢∞§¶•ªº–≠
10 | ¸˛Ç◊ı˜Â¯˘¿
11 | ÅÍÎÏ˝ÓÔÒÚÆ☃
12 | Œ„´‰ˇÁ¨ˆØ∏”’
13 | `⁄€‹›fifl‡°·‚—±
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 | ثم نفس سقطت وبالتحديد،, جزيرتي باستخدام أن دنو. إذ هنا؟ الستار وتنصيب كان. أهّل ايطاليا، بريطانيا-فرنسا قد أخذ. سليمان، إتفاقية بين ما, يذكر الحدود أي بعد, معاملة بولندا، الإطلاق عل إيو.
--------------------------------------------------------------------------------
/fixtures/test_rustscan_scripts.toml:
--------------------------------------------------------------------------------
1 | # Test/Example ScriptConfig file
2 |
3 | # Tags to filter on scripts. Only scripts containing all these tags will run.
4 | tags = ["core_approved", "example"]
5 |
6 | # If it's present then only those scripts will run which has a tag ports = "80". Not yet implemented.
7 | #
8 | # ex.:
9 | # ports = ["80"]
10 | # ports = ["80","81","8080"]
11 | ports = ["80"]
12 |
13 | # Only this developer(s) scripts to run. Not yet implemented.
14 | developer = ["example"]
--------------------------------------------------------------------------------
/pictures/8seconds.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/8seconds.gif
--------------------------------------------------------------------------------
/pictures/accessible.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/accessible.gif
--------------------------------------------------------------------------------
/pictures/accessible.yml:
--------------------------------------------------------------------------------
1 | # The configurations that used for the recording, feel free to edit them
2 | config:
3 |
4 | # Specify a command to be executed
5 | # like `/bin/bash -l`, `ls`, or any other commands
6 | # the default is bash for Linux
7 | # or powershell.exe for Windows
8 | command: zsh
9 |
10 | # Specify the current working directory path
11 | # the default is the current working directory path
12 | cwd: /home/bee/Documents/RustScan
13 |
14 | # Export additional ENV variables
15 | env:
16 | recording: true
17 |
18 | # Explicitly set the number of columns
19 | # or use `auto` to take the current
20 | # number of columns of your shell
21 | cols: 107
22 |
23 | # Explicitly set the number of rows
24 | # or use `auto` to take the current
25 | # number of rows of your shell
26 | rows: 43
27 |
28 | # Amount of times to repeat GIF
29 | # If value is -1, play once
30 | # If value is 0, loop indefinitely
31 | # If value is a positive number, loop n times
32 | repeat: 0
33 |
34 | # Quality
35 | # 1 - 100
36 | quality: 100
37 |
38 | # Delay between frames in ms
39 | # If the value is `auto` use the actual recording delays
40 | frameDelay: auto
41 |
42 | # Maximum delay between frames in ms
43 | # Ignored if the `frameDelay` isn't set to `auto`
44 | # Set to `auto` to prevent limiting the max idle time
45 | maxIdleTime: 2000
46 |
47 | # The surrounding frame box
48 | # The `type` can be null, window, floating, or solid`
49 | # To hide the title use the value null
50 | # Don't forget to add a backgroundColor style with a null as type
51 | frameBox:
52 | type: floating
53 | title: Terminalizer
54 | style:
55 | border: 0px black solid
56 | # boxShadow: none
57 | # margin: 0px
58 |
59 | # Add a watermark image to the rendered gif
60 | # You need to specify an absolute path for
61 | # the image on your machine or a URL, and you can also
62 | # add your own CSS styles
63 | watermark:
64 | imagePath: null
65 | style:
66 | position: absolute
67 | right: 15px
68 | bottom: 15px
69 | width: 100px
70 | opacity: 0.9
71 |
72 | # Cursor style can be one of
73 | # `block`, `underline`, or `bar`
74 | cursorStyle: block
75 |
76 | # Font family
77 | # You can use any font that is installed on your machine
78 | # in CSS-like syntax
79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace"
80 |
81 | # The size of the font
82 | fontSize: 12
83 |
84 | # The height of lines
85 | lineHeight: 1
86 |
87 | # The spacing between letters
88 | letterSpacing: 0
89 |
90 | # Theme
91 | theme:
92 | background: "transparent"
93 | foreground: "#afafaf"
94 | cursor: "#c7c7c7"
95 | black: "#232628"
96 | red: "#fc4384"
97 | green: "#b3e33b"
98 | yellow: "#ffa727"
99 | blue: "#75dff2"
100 | magenta: "#ae89fe"
101 | cyan: "#708387"
102 | white: "#d5d5d0"
103 | brightBlack: "#626566"
104 | brightRed: "#ff7fac"
105 | brightGreen: "#c8ed71"
106 | brightYellow: "#ebdf86"
107 | brightBlue: "#75dff2"
108 | brightMagenta: "#ae89fe"
109 | brightCyan: "#b1c6ca"
110 | brightWhite: "#f9f9f4"
111 |
112 | # Records, feel free to edit them
113 | records:
114 | - delay: 436
115 | content: "\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive:~/Documents/RustScan\a\e]1;..ents/RustScan\a"
116 | - delay: 162
117 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m master\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.10.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.46.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
118 | - delay: 471
119 | content: "r\brustscan 127.0.0.1 --accessible --no-nmap --ulimit 5000\e[55D\e[7mr\e[7mu\e[7ms\e[7mt\e[7ms\e[7mc\e[7ma\e[7mn\e[7m \e[7m1\e[7m2\e[7m7\e[7m.\e[7m0\e[7m.\e[7m0\e[7m.\e[7m1\e[7m \e[7m-\e[7m-\e[7ma\e[7mc\e[7mc\e[7me\e[7ms\e[7ms\e[7mi\e[7mb\e[7ml\e[7me\e[7m \e[7m-\e[7m-\e[7mn\e[7mo\e[7m-\e[7mn\e[7mm\e[7ma\e[7mp\e[7m \e[7m-\e[7m-\e[7mu\e[7ml\e[7mi\e[7mm\e[7mi\e[7mt\e[7m \e[7m5\e[7m0\e[7m0\e[7m0\e[27m"
120 | - delay: 269
121 | content: "\e[55D\e[27mr\e[27mu\e[27ms\e[27mt\e[27ms\e[27mc\e[27ma\e[27mn\e[27m \e[27m1\e[27m2\e[27m7\e[27m.\e[27m0\e[27m.\e[27m0\e[27m.\e[27m1\e[27m \e[27m-\e[27m-\e[27ma\e[27mc\e[27mc\e[27me\e[27ms\e[27ms\e[27mi\e[27mb\e[27ml\e[27me\e[27m \e[27m-\e[27m-\e[27mn\e[27mo\e[27m-\e[27mn\e[27mm\e[27ma\e[27mp\e[27m \e[27m-\e[27m-\e[27mu\e[27ml\e[27mi\e[27mm\e[27mi\e[27mt\e[27m \e[27m5\e[27m0\e[27m0\e[27m0\e[?1l\e>\e[?2004l\r\r\n\e]2;rustscan 127.0.0.1 --accessible --no-nmap --ulimit 5000\a\e]1;rustscan\aAutomatically increasing ulimit value to 5000.\r\n"
122 | - delay: 1536
123 | content: "Open 127.0.0.1:38602\r\n"
124 | - delay: 395
125 | content: "Open 127.0.0.1:50078\r\n"
126 | - delay: 450
127 | content: "127.0.0.1 -> [38602,50078]\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive:~/Documents/RustScan\a\e]1;..ents/RustScan\a"
128 | - delay: 143
129 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m master\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.10.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.46.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mtook \e[0m\e[1m\e[33m2s\e[0m\e[33m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
130 | - delay: 563
131 | content: "\e[?2004l\r\r\n"
132 |
--------------------------------------------------------------------------------
/pictures/adaptive.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/adaptive.gif
--------------------------------------------------------------------------------
/pictures/apple.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/apple.png
--------------------------------------------------------------------------------
/pictures/arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/arch.png
--------------------------------------------------------------------------------
/pictures/debiian.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/debiian.jpg
--------------------------------------------------------------------------------
/pictures/docker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/docker.png
--------------------------------------------------------------------------------
/pictures/fast.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/fast.gif
--------------------------------------------------------------------------------
/pictures/fast.yml:
--------------------------------------------------------------------------------
1 | # The configurations that used for the recording, feel free to edit them
2 | config:
3 |
4 | # Specify a command to be executed
5 | # like `/bin/bash -l`, `ls`, or any other commands
6 | # the default is bash for Linux
7 | # or powershell.exe for Windows
8 | command: zsh
9 |
10 | # Specify the current working directory path
11 | # the default is the current working directory path
12 | cwd: /home/bee/Documents/RustScan
13 |
14 | # Export additional ENV variables
15 | env:
16 | recording: true
17 |
18 | # Explicitly set the number of columns
19 | # or use `auto` to take the current
20 | # number of columns of your shell
21 | cols: 105
22 |
23 | # Explicitly set the number of rows
24 | # or use `auto` to take the current
25 | # number of rows of your shell
26 | rows: 34
27 |
28 | # Amount of times to repeat GIF
29 | # If value is -1, play once
30 | # If value is 0, loop indefinitely
31 | # If value is a positive number, loop n times
32 | repeat: 0
33 |
34 | # Quality
35 | # 1 - 100
36 | quality: 100
37 |
38 | # Delay between frames in ms
39 | # If the value is `auto` use the actual recording delays
40 | frameDelay: auto
41 |
42 | # Maximum delay between frames in ms
43 | # Ignored if the `frameDelay` isn't set to `auto`
44 | # Set to `auto` to prevent limiting the max idle time
45 | maxIdleTime: auto
46 |
47 | # The surrounding frame box
48 | # The `type` can be null, window, floating, or solid`
49 | # To hide the title use the value null
50 | # Don't forget to add a backgroundColor style with a null as type
51 | frameBox:
52 | type: floating
53 | title: Terminalizer
54 | style:
55 | border: 0px black solid
56 | # boxShadow: none
57 | # margin: 0px
58 |
59 | # Add a watermark image to the rendered gif
60 | # You need to specify an absolute path for
61 | # the image on your machine or a URL, and you can also
62 | # add your own CSS styles
63 | watermark:
64 | imagePath: null
65 | style:
66 | position: absolute
67 | right: 15px
68 | bottom: 15px
69 | width: 100px
70 | opacity: 0.9
71 |
72 | # Cursor style can be one of
73 | # `block`, `underline`, or `bar`
74 | cursorStyle: block
75 |
76 | # Font family
77 | # You can use any font that is installed on your machine
78 | # in CSS-like syntax
79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace"
80 |
81 | # The size of the font
82 | fontSize: 12
83 |
84 | # The height of lines
85 | lineHeight: 1
86 |
87 | # The spacing between letters
88 | letterSpacing: 0
89 |
90 | # Theme
91 | theme:
92 | background: "transparent"
93 | foreground: "#afafaf"
94 | cursor: "#c7c7c7"
95 | black: "#232628"
96 | red: "#fc4384"
97 | green: "#b3e33b"
98 | yellow: "#ffa727"
99 | blue: "#75dff2"
100 | magenta: "#ae89fe"
101 | cyan: "#708387"
102 | white: "#d5d5d0"
103 | brightBlack: "#626566"
104 | brightRed: "#ff7fac"
105 | brightGreen: "#c8ed71"
106 | brightYellow: "#ebdf86"
107 | brightBlue: "#75dff2"
108 | brightMagenta: "#ae89fe"
109 | brightCyan: "#b1c6ca"
110 | brightWhite: "#f9f9f4"
111 |
112 | # Records, feel free to edit them
113 | records:
114 | - delay: 815
115 | content: "\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;..ents/RustScan\a"
116 | - delay: 178
117 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m asyncMaster\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [$!?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
118 | - delay: 406
119 | content: "\e[32mc\e[39m\e[90margo build --release && ./target/release/rustscan 127.0.0.1\e[39m\e[59D"
120 | - delay: 137
121 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m"
122 | - delay: 72
123 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m"
124 | - delay: 203
125 | content: "\b\b\b\e[0m\e[32mc\e[0m\e[32ma\e[0m\e[32mr\e[32mg\e[32mo\e[39m\e[39m \e[39mb\e[39mu\e[39mi\e[39ml\e[39md\e[39m \e[39m-\e[39m-\e[39mr\e[39me\e[39ml\e[39me\e[39ma\e[39ms\e[39me\e[39m \e[39m&\e[39m&\e[39m \e[32m.\e[32m/\e[32mt\e[32ma\e[32mr\e[32mg\e[32me\e[32mt\e[32m/\e[32mr\e[32me\e[32ml\e[32me\e[32ma\e[32ms\e[32me\e[32m/\e[32mr\e[32mu\e[32ms\e[32mt\e[32ms\e[32mc\e[32ma\e[32mn\e[39m\e[39m \e[39m1\e[39m2\e[39m7\e[39m.\e[39m0\e[39m.\e[39m0\e[39m.\e[39m1"
126 | - delay: 145
127 | content: "\e[?1l\e>"
128 | - delay: 7
129 | content: "\e[?2004l\r\r\n\e]2;cargo\a"
130 | - delay: 37
131 | content: "\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 87/92: futures-executor, smol \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 89/92: futures, async-std \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 90/92: async-std \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:193:13\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m192\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Unable to convert to socket address\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m----------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m193\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(io::Error::new(io::ErrorKind::Other, e.to_string()))\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unreachable_code)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:57:14\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m57\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Some(x) => {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_variables)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: 2 warnings emitted\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[32m Finished\e[0m release [optimized] target(s) in 0.03s\r\n\e[32m\r\n _____ _ _____ \r\n | __ \\ | | / ____| \r\n | |__) | _ ___| |_| (___ ___ __ _ _ __ \r\n | _ / | | / __| __|\\___ \\ / __/ _` | '_ \\ \r\n | | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |\r\n |_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|\r\n Faster nmap scanning with rust.\e[0m \r\n \e[31mAutomated Decryption Tool - https://github.com/ciphey/ciphey\e[0m \r\n \e[32mCreator https://github.com/brandonskerritt\e[0m\r\n"
132 | - delay: 326
133 | content: "Open \e[35m53\e[0m\r\nOpen \e[35m631\e[0m\r\n"
134 | - delay: 6
135 | content: "Open \e[35m1716\e[0m\r\n"
136 | - delay: 1138
137 | content: "Open \e[35m46624\e[0m\r\n"
138 | - delay: 156
139 | content: "Open \e[35m52880\e[0m\r\n"
140 | - delay: 146
141 | content: "\e[34mStarting nmap.\e[0m\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;..ents/RustScan\a"
142 | - delay: 9
143 | content: "Starting Nmap 7.80 ( https://nmap.org ) at 2020-07-22 22:12 BST\r\n"
144 | - delay: 153
145 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m asyncMaster\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [$!?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
146 | - delay: 119
147 | content: "NSE: Loaded 151 scripts for scanning.\r\nNSE: Script Pre-scanning.\r\nNSE: Starting runlevel 1 (of 3) scan.\r\nInitiating NSE at 22:12\r\nCompleted NSE at 22:12, 0.00s elapsed\r\nNSE: Starting runlevel 2 (of 3) scan.\r\nInitiating NSE at 22:12\r\nCompleted NSE at 22:12, 0.00s elapsed\r\nNSE: Starting runlevel 3 (of 3) scan.\r\nInitiating NSE at 22:12\r\nCompleted NSE at 22:12, 0.00s elapsed\r\n"
148 | - delay: 21
149 | content: "Initiating Ping Scan at 22:12\r\nScanning 127.0.0.1 [2 ports]\r\nCompleted Ping Scan at 22:12, 0.00s elapsed (1 total hosts)\r\nInitiating Connect Scan at 22:12\r\nScanning localhost (127.0.0.1) [5 ports]\r\nDiscovered open port 53/tcp on 127.0.0.1\r\nDiscovered open port 46624/tcp on 127.0.0.1\r\nDiscovered open port 1716/tcp on 127.0.0.1\r\nDiscovered open port 631/tcp on 127.0.0.1\r\nCompleted Connect Scan at 22:12, 0.00s elapsed (5 total ports)\r\n"
150 | - delay: 52
151 | content: "Initiating Service scan at 22:12\r\nScanning 4 services on localhost (127.0.0.1)\r\n"
152 | - delay: 105
153 | content: "\e[?2004l\r\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;..ents/RustScan\a"
154 | - delay: 158
155 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m asyncMaster\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [$!?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[31m➜ \e[0m\e[31m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
156 | - delay: 1332
157 | content: "\e[?2004l\r\r\n"
158 |
--------------------------------------------------------------------------------
/pictures/intro.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/intro.gif
--------------------------------------------------------------------------------
/pictures/kali.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/kali.png
--------------------------------------------------------------------------------
/pictures/newfast.yml:
--------------------------------------------------------------------------------
1 | # The configurations that used for the recording, feel free to edit them
2 | config:
3 |
4 | # Specify a command to be executed
5 | # like `/bin/bash -l`, `ls`, or any other commands
6 | # the default is bash for Linux
7 | # or powershell.exe for Windows
8 | command: zsh
9 |
10 | # Specify the current working directory path
11 | # the default is the current working directory path
12 | cwd: /home/bee/Documents/RustScan
13 |
14 | # Export additional ENV variables
15 | env:
16 | recording: true
17 |
18 | # Explicitly set the number of columns
19 | # or use `auto` to take the current
20 | # number of columns of your shell
21 | cols: 235
22 |
23 | # Explicitly set the number of rows
24 | # or use `auto` to take the current
25 | # number of rows of your shell
26 | rows: 31
27 |
28 | # Amount of times to repeat GIF
29 | # If value is -1, play once
30 | # If value is 0, loop indefinitely
31 | # If value is a positive number, loop n times
32 | repeat: 0
33 |
34 | # Quality
35 | # 1 - 100
36 | quality: 100
37 |
38 | # Delay between frames in ms
39 | # If the value is `auto` use the actual recording delays
40 | frameDelay: auto
41 |
42 | # Maximum delay between frames in ms
43 | # Ignored if the `frameDelay` isn't set to `auto`
44 | # Set to `auto` to prevent limiting the max idle time
45 | maxIdleTime: auto
46 |
47 | # The surrounding frame box
48 | # The `type` can be null, window, floating, or solid`
49 | # To hide the title use the value null
50 | # Don't forget to add a backgroundColor style with a null as type
51 | frameBox:
52 | type: floating
53 | title: Terminalizer
54 | style:
55 | border: 0px black solid
56 | # boxShadow: none
57 | # margin: 0px
58 |
59 | # Add a watermark image to the rendered gif
60 | # You need to specify an absolute path for
61 | # the image on your machine or a URL, and you can also
62 | # add your own CSS styles
63 | watermark:
64 | imagePath: null
65 | style:
66 | position: absolute
67 | right: 15px
68 | bottom: 15px
69 | width: 100px
70 | opacity: 0.9
71 |
72 | # Cursor style can be one of
73 | # `block`, `underline`, or `bar`
74 | cursorStyle: block
75 |
76 | # Font family
77 | # You can use any font that is installed on your machine
78 | # in CSS-like syntax
79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace"
80 |
81 | # The size of the font
82 | fontSize: 12
83 |
84 | # The height of lines
85 | lineHeight: 1
86 |
87 | # The spacing between letters
88 | letterSpacing: 0
89 |
90 | # Theme
91 | theme:
92 | background: "transparent"
93 | foreground: "#afafaf"
94 | cursor: "#c7c7c7"
95 | black: "#232628"
96 | red: "#fc4384"
97 | green: "#b3e33b"
98 | yellow: "#ffa727"
99 | blue: "#75dff2"
100 | magenta: "#ae89fe"
101 | cyan: "#708387"
102 | white: "#d5d5d0"
103 | brightBlack: "#626566"
104 | brightRed: "#ff7fac"
105 | brightGreen: "#c8ed71"
106 | brightYellow: "#ebdf86"
107 | brightBlue: "#75dff2"
108 | brightMagenta: "#ae89fe"
109 | brightCyan: "#b1c6ca"
110 | brightWhite: "#f9f9f4"
111 |
112 | # Records, feel free to edit them
113 | records:
114 | - delay: 818
115 | content: "\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive: ~/Documents/RustScan\a\e]1;..ents/RustScan\a"
116 | - delay: 187
117 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m master\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [⇡$!?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
118 | - delay: 432
119 | content: "\e[32mc\e[39m\e[90margo build --release && ./target/release/rustscan 8.8.8.8\e[39m\e[57D"
120 | - delay: 106
121 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m"
122 | - delay: 115
123 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m"
124 | - delay: 178
125 | content: "\b\b\b\e[0m\e[32mc\e[0m\e[32ma\e[0m\e[32mr\e[32mg\e[32mo\e[39m\e[39m \e[39mb\e[39mu\e[39mi\e[39ml\e[39md\e[39m \e[39m-\e[39m-\e[39mr\e[39me\e[39ml\e[39me\e[39ma\e[39ms\e[39me\e[39m \e[39m&\e[39m&\e[39m \e[32m.\e[32m/\e[32mt\e[32ma\e[32mr\e[32mg\e[32me\e[32mt\e[32m/\e[32mr\e[32me\e[32ml\e[32me\e[32ma\e[32ms\e[32me\e[32m/\e[32mr\e[32mu\e[32ms\e[32mt\e[32ms\e[32mc\e[32ma\e[32mn\e[39m\e[39m \e[39m8\e[39m.\e[39m8\e[39m.\e[39m8\e[39m.\e[39m8"
126 | - delay: 143
127 | content: "\e[?1l\e>"
128 | - delay: 8
129 | content: "\e[?2004l\r\r\n\e]2;cargo build --release && ./target/release/rustscan 8.8.8.8\a\e]1;cargo\a"
130 | - delay: 40
131 | content: "\e[0m\e[0m\e[1m\e[36m Building\e[0m [=================================================> ] 82/92: proc-macro-nested(build), rayon-core \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==================================================> ] 83/92: rayon, proc-macro-nested(build) \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===================================================> ] 84/92: proc-macro-nested(build) \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===================================================> ] 85/92: proc-macro-nested \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 86/92: futures-util \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 87/92: smol, futures-executor \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 89/92: async-std, futures \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 90/92: async-std \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused import: `Command`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:5:26\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m5\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0muse std::process::{exit, Command};\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_imports)]` on by default\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:181:21\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m180\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Too many open files. Please reduce batch size.\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m---------------------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m181\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(e)\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unreachable_code)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:189:13\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m188\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Unable to convert to socket address\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m----------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m189\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(io::Error::new(io::ErrorKind::Other, e.to_string()))\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `command_run`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:55:9\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m55\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m let command_run: String = match command_matches {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_command_run`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_variables)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:57:14\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m57\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Some(x) => {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_x`\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: 5 warnings emitted\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[32m Finished\e[0m release [optimized] target(s) in 0.03s\r\n\e[32m\r\n _____ _ _____ \r\n | __ \\ | | / ____| \r\n | |__) | _ ___| |_| (___ ___ __ _ _ __ \r\n | _ / | | / __| __|\\___ \\ / __/ _` | '_ \\ \r\n | | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |\r\n |_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|\r\n Faster nmap scanning with rust.\e[0m \r\n \e[31mAutomated Decryption Tool - https://github.com/ciphey/ciphey\e[0m \r\n \e[32mCreator https://github.com/brandonskerritt\e[0m\r\n"
132 | - delay: 368
133 | content: "Open \e[35m53\e[0m\r\n"
134 | - delay: 37
135 | content: "Open \e[35m853\e[0m\r\n"
136 | - delay: 849
137 | content: "Open \e[35m443\e[0m\r\n"
138 | - delay: 6605
139 | content: "\e[34mStarting nmap.\e[0m\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive: ~/Documents/RustScan\a\e]1;..ents/RustScan\a"
140 | - delay: 175
141 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m master\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [⇡$!?]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mtook \e[0m\e[1m\e[33m8s\e[0m\e[33m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[31m➜ \e[0m\e[31m\e[39m\e[1m\e[0m\e[K\e[?1h\e=\e[?2004h"
142 | - delay: 554
143 | content: "\e[?2004l\r\r\n"
144 |
--------------------------------------------------------------------------------
/pictures/nice.yml:
--------------------------------------------------------------------------------
1 | # The configurations that used for the recording, feel free to edit them
2 | config:
3 |
4 | # Specify a command to be executed
5 | # like `/bin/bash -l`, `ls`, or any other commands
6 | # the default is bash for Linux
7 | # or powershell.exe for Windows
8 | command: zsh
9 |
10 | # Specify the current working directory path
11 | # the default is the current working directory path
12 | cwd: /home/bee/Documents/RustScan
13 |
14 | # Export additional ENV variables
15 | env:
16 | recording: true
17 |
18 | # Explicitly set the number of columns
19 | # or use `auto` to take the current
20 | # number of columns of your shell
21 | cols: 108
22 |
23 | # Explicitly set the number of rows
24 | # or use `auto` to take the current
25 | # number of rows of your shell
26 | rows: 45
27 |
28 | # Amount of times to repeat GIF
29 | # If value is -1, play once
30 | # If value is 0, loop indefinitely
31 | # If value is a positive number, loop n times
32 | repeat: 0
33 |
34 | # Quality
35 | # 1 - 100
36 | quality: 100
37 |
38 | # Delay between frames in ms
39 | # If the value is `auto` use the actual recording delays
40 | frameDelay: auto
41 |
42 | # Maximum delay between frames in ms
43 | # Ignored if the `frameDelay` isn't set to `auto`
44 | # Set to `auto` to prevent limiting the max idle time
45 | maxIdleTime: auto
46 |
47 | # The surrounding frame box
48 | # The `type` can be null, window, floating, or solid`
49 | # To hide the title use the value null
50 | # Don't forget to add a backgroundColor style with a null as type
51 | frameBox:
52 | type: floating
53 | title: Terminalizer
54 | style:
55 | border: 0px black solid
56 | # boxShadow: none
57 | # margin: 0px
58 |
59 | # Add a watermark image to the rendered gif
60 | # You need to specify an absolute path for
61 | # the image on your machine or a URL, and you can also
62 | # add your own CSS styles
63 | watermark:
64 | imagePath: null
65 | style:
66 | position: absolute
67 | right: 15px
68 | bottom: 15px
69 | width: 100px
70 | opacity: 0.9
71 |
72 | # Cursor style can be one of
73 | # `block`, `underline`, or `bar`
74 | cursorStyle: block
75 |
76 | # Font family
77 | # You can use any font that is installed on your machine
78 | # in CSS-like syntax
79 | fontFamily: "Monaco, Lucida Console, Ubuntu Mono, Monospace"
80 |
81 | # The size of the font
82 | fontSize: 12
83 |
84 | # The height of lines
85 | lineHeight: 1
86 |
87 | # The spacing between letters
88 | letterSpacing: 0
89 |
90 | # Theme
91 | theme:
92 | background: "transparent"
93 | foreground: "#afafaf"
94 | cursor: "#c7c7c7"
95 | black: "#232628"
96 | red: "#fc4384"
97 | green: "#b3e33b"
98 | yellow: "#ffa727"
99 | blue: "#75dff2"
100 | magenta: "#ae89fe"
101 | cyan: "#708387"
102 | white: "#d5d5d0"
103 | brightBlack: "#626566"
104 | brightRed: "#ff7fac"
105 | brightGreen: "#c8ed71"
106 | brightYellow: "#ebdf86"
107 | brightBlue: "#75dff2"
108 | brightMagenta: "#ae89fe"
109 | brightCyan: "#b1c6ca"
110 | brightWhite: "#f9f9f4"
111 |
112 | # Records, feel free to edit them
113 | records:
114 | - delay: 895
115 | content: "No protocol specified\r\nCan't open display :0\r\n"
116 | - delay: 253
117 | content: "\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive: ~/Documents/RustScan\a\e]1;..ents/RustScan\a"
118 | - delay: 29
119 | content: c
120 | - delay: 131
121 | content: a
122 | - delay: 29
123 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m asyncMaster\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [$!]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e[?2004hc"
124 | - delay: 5
125 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m\e[90mrgo build --release && ./target/release/rustscan 127.0.0.1\e[39m\e[58D"
126 | - delay: 719
127 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m"
128 | - delay: 697
129 | content: "\b\b\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m\e[0m\e[90mr\b"
130 | - delay: 172
131 | content: "\b\b\e[0m\e[32mc\e[39m\e[0m\e[90md\e[90m \e[90mR\e[90mu\e[90ms\e[90mt\e[90mS\e[90mc\e[90ma\e[90mn\e[39m\e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[59D"
132 | - delay: 174
133 | content: "\b\e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[39m \e[11D"
134 | - delay: 2955
135 | content: "\e[32mc\e[39m\e[90md RustScan\e[39m\e[10D"
136 | - delay: 111
137 | content: "\b\e[1m\e[31mc\e[1m\e[31ma\e[0m\e[39m\e[90mr\e[90mg\e[90mo\e[90m \e[90mb\e[90mu\e[90mi\e[90ml\e[90md\e[90m --release && ./target/release/rustscan 127.0.0.1\e[39m\e[58D"
138 | - delay: 99
139 | content: "\b\b\e[1m\e[31mc\e[1m\e[31ma\e[1m\e[31mr\e[0m\e[39m"
140 | - delay: 192
141 | content: "\b\b\b\e[0m\e[32mc\e[0m\e[32ma\e[0m\e[32mr\e[32mg\e[32mo\e[39m\e[39m \e[39mb\e[39mu\e[39mi\e[39ml\e[39md\e[39m \e[39m-\e[39m-\e[39mr\e[39me\e[39ml\e[39me\e[39ma\e[39ms\e[39me\e[39m \e[39m&\e[39m&\e[39m \e[32m.\e[32m/\e[32mt\e[32ma\e[32mr\e[32mg\e[32me\e[32mt\e[32m/\e[32mr\e[32me\e[32ml\e[32me\e[32ma\e[32ms\e[32me\e[32m/\e[32mr\e[32mu\e[32ms\e[32mt\e[32ms\e[32mc\e[32ma\e[32mn\e[39m\e[39m \e[39m1\e[39m2\e[39m7\e[39m.\e[39m0\e[39m.\e[39m0\e[39m.\e[39m1"
142 | - delay: 217
143 | content: "\e[?1l\e[?2004l\r\r\n\e]2;cargo build --release && ./target/release/rustscan 127.0.0.1\a\e]1;cargo\a"
144 | - delay: 38
145 | content: "\e[0m\e[0m\e[1m\e[36m Building\e[0m [ ] 0/92: autocfg \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==========================================> ] 70/92: crossbeam-utils(build.r...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==========================================> ] 71/92: crossbeam-utils(build.r...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [============================================> ] 73/92: crossbeam-utils(build),...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=============================================> ] 75/92: crossbeam-utils, quote \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [==============================================> ] 76/92: crossbeam-epoch, crossb...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===============================================> ] 78/92: crossbeam-deque, quote \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=================================================> ] 81/92: rayon, syn \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [===================================================> ] 85/92: pin-project \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [====================================================> ] 86/92: futures-util \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=====================================================> ] 88/92: async-std, futures-exec...\r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 89/92: futures-executor \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [======================================================> ] 90/92: futures \r\e[0m\e[0m\e[1m\e[36m Building\e[0m [=======================================================> ] 91/92: rustscan(bin) \r\e[K\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:193:13\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m192\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m panic!(\"Unable to convert to socket address\");\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m----------------------------------------------\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12many code following this expression is unreachable\e[0m\r\n\e[0m\e[1m\e[38;5;12m193\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Err(io::Error::new(io::ErrorKind::Other, e.to_string()))\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33munreachable expression\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unreachable_code)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: unused variable: `x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m--> \e[0m\e[0msrc/main.rs:57:14\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m\e[1m\e[38;5;12m57\e[0m\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m Some(x) => {\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m| \e[0m\e[0m \e[0m\e[0m\e[1m\e[33m^\e[0m\e[0m \e[0m\e[0m\e[1m\e[33mhelp: if this is intentional, prefix it with an underscore: `_x`\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m|\e[0m\r\n\e[0m \e[0m\e[0m\e[1m\e[38;5;12m= \e[0m\e[0m\e[1mnote\e[0m\e[0m: `#[warn(unused_variables)]` on by default\e[0m\r\n\r\n\e[0m\e[1m\e[33mwarning\e[0m\e[0m\e[1m: 2 warnings emitted\e[0m\r\n\r\n\e[0m\e[0m\e[1m\e[32m Finished\e[0m release [optimized] target(s) in 0.03s\r\n\e[32m\r\n _____ _ _____ \r\n | __ \\ | | / ____| \r\n | |__) | _ ___| |_| (___ ___ __ _ _ __ \r\n | _ / | | / __| __|\\___ \\ / __/ _` | '_ \\ \r\n | | \\ \\ |_| \\__ \\ |_ ____) | (_| (_| | | | |\r\n |_| \\_\\__,_|___/\\__|_____/ \\___\\__,_|_| |_|\r\n Faster nmap scanning with rust.\e[0m \r\n \e[31mAutomated Decryption Tool - https://github.com/ciphey/ciphey\e[0m \r\n \e[32mCreator https://github.com/brandonskerritt\e[0m\r\n"
146 | - delay: 309
147 | content: "Open \e[35m53\e[0m\r\nOpen \e[35m631\e[0m\r\n"
148 | - delay: 1132
149 | content: "Open \e[35m46624\e[0m\r\n"
150 | - delay: 136
151 | content: "Open \e[35m51934\e[0m\r\n"
152 | - delay: 159
153 | content: "\e[34mStarting nmap.\e[0m\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive: ~/Documents/RustScan\a\e]1;..ents/RustScan\aStarting Nmap 7.80 ( https://nmap.org ) at 2020-07-22 22:09 BST\r\n"
154 | - delay: 156
155 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m asyncMaster\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [$!]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mtook \e[0m\e[1m\e[33m2s\e[0m\e[33m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[32m➜ \e[0m\e[32m\e[39m\e[1m\e[0m\e[K\e[?1h\e[?2004h"
156 | - delay: 75
157 | content: "NSE: Loaded 151 scripts for scanning.\r\nNSE: Script Pre-scanning.\r\nNSE: Starting runlevel 1 (of 3) scan.\r\nInitiating NSE at 22:09\r\nCompleted NSE at 22:09, 0.00s elapsed\r\nNSE: Starting runlevel 2 (of 3) scan.\r\nInitiating NSE at 22:09\r\nCompleted NSE at 22:09, 0.00s elapsed\r\nNSE: Starting runlevel 3 (of 3) scan.\r\nInitiating NSE at 22:09\r\nCompleted NSE at 22:09, 0.00s elapsed\r\n"
158 | - delay: 21
159 | content: "Initiating Ping Scan at 22:09\r\nScanning 127.0.0.1 [2 ports]\r\nCompleted Ping Scan at 22:09, 0.00s elapsed (1 total hosts)\r\nInitiating Connect Scan at 22:09\r\nScanning localhost (127.0.0.1) [4 ports]\r\nDiscovered open port 53/tcp on 127.0.0.1\r\nDiscovered open port 631/tcp on 127.0.0.1\r\nDiscovered open port 46624/tcp on 127.0.0.1\r\nCompleted Connect Scan at 22:09, 0.00s elapsed (4 total ports)\r\n"
160 | - delay: 55
161 | content: "Initiating Service scan at 22:09\r\nScanning 3 services on localhost (127.0.0.1)\r\n"
162 | - delay: 42
163 | content: "\e[?2004l\r\r\n\e[1m\e[7m%\e[27m\e[1m\e[0m \r \r\e]2;bee@beehive: ~/Documents/RustScan\a\e]1;..ents/RustScan\a"
164 | - delay: 168
165 | content: "\r\e[0m\e[27m\e[24m\e[J\r\n\e[1m\e[0m\e[1m\e[36mRustScan\e[0m\e[36m\e[39m\e[1m \e[0m\e[1mon \e[0m\e[1m\e[37m\e[1m\e[37m\e[0m\e[37m\e[1m\e[37m\e[35m asyncMaster\e[0m\e[35m\e[39m\e[1m\e[0m\e[1m\e[0m\e[1m\e[31m [$!]\e[0m\e[31m\e[39m\e[1m\e[0m\e[0m\e[39m\e[1m \e[0m\e[1mis \e[0m\e[1m\e[31m\U0001F4E6 vrustscan:1.0.1\e[0m\e[31m\e[39m\e[1m \e[0m\e[1mvia \e[0m\e[1m\e[31m\U0001D5E5 v1.45.0\e[0m\e[31m\e[39m\e[1m \e[0m\r\n\e[1m\e[0m\e[1m\e[31m➜ \e[0m\e[31m\e[39m\e[1m\e[0m\e[K\e[?1h\e[?2004h"
166 | - delay: 403
167 | content: "\e[?2004l\r\r\n"
168 |
--------------------------------------------------------------------------------
/pictures/render1595455985190.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/render1595455985190.gif
--------------------------------------------------------------------------------
/pictures/render1595457244085.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/render1595457244085.gif
--------------------------------------------------------------------------------
/pictures/render1595457288918.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/render1595457288918.gif
--------------------------------------------------------------------------------
/pictures/rust.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/rust.png
--------------------------------------------------------------------------------
/pictures/rustscan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/rustscan.png
--------------------------------------------------------------------------------
/pictures/scripts.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/scripts.gif
--------------------------------------------------------------------------------
/pictures/with_rustscan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/with_rustscan.gif
--------------------------------------------------------------------------------
/pictures/without_rustscan.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/0dayCTF/RustScan/08a314485e7a76adcc87d8d9b1bdd0c164f90b49/pictures/without_rustscan.gif
--------------------------------------------------------------------------------
/rustscan-debbuilder/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM rust:latest
2 |
3 | RUN git clone https://github.com/brandonskerritt/RustScan
4 | WORKDIR "/RustScan"
5 | RUN git pull --force
6 | RUN cargo install cargo-deb
7 |
8 | RUN apt update -y && apt upgrade -y
9 | RUN apt install libc6-dev-i386 -y
10 | RUN git clone --depth=1 https://github.com/raspberrypi/tools /raspberrypi-tools
11 | ENV PATH=/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin/:$PATH
12 | ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
13 | RUN mkdir /root/.cargo
14 | RUN echo "[target.arm-unknown-linux-gnueabihf]" >> /root/.cargo/config
15 | RUN echo "strip = { path = \"arm-linux-gnueabihf-strip\" }" >> /root/.cargo/config
16 | RUN echo "objcopy = { path = \"arm-linux-gnueabihf-objcopy\" }" >> /root/.cargo/config
17 |
18 | COPY ./entrypoint.sh /entrypoint.sh
19 | RUN chmod +x /entrypoint.sh
20 | ENTRYPOINT ["/entrypoint.sh"]
21 |
--------------------------------------------------------------------------------
/rustscan-debbuilder/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | cd /RustScan
4 | git pull --force
5 |
6 | #amd64
7 | cargo deb
8 |
9 | #arm64
10 | rustup target add arm-unknown-linux-gnueabihf
11 | cargo deb --target=arm-unknown-linux-gnueabihf
12 |
13 | #i386
14 | rustup target add i686-unknown-linux-gnu
15 | cargo deb --target=i686-unknown-linux-gnu
16 |
17 | find target/ -name \*.deb -exec cp {} /debs \;
18 |
--------------------------------------------------------------------------------
/rustscan-debbuilder/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | docker build -t rustscan-builder . || exit
3 |
4 | # This creates a volume which binds your currentdirectory/debs to
5 | # the location where the deb files get spat out in the container.
6 | # You don't need to worry about it. Just chmod +x run.sh && ./run.sh and
7 | # you'll get yer .deb file in a few minutes. It runs faster after you've used it the first time.
8 | docker run -v "$(pwd)/debs:/debs" rustscan-builder
9 |
--------------------------------------------------------------------------------
/src/benchmark/mod.rs:
--------------------------------------------------------------------------------
1 | //! # Usage
2 | //! ```
3 | //! // Initiate Benchmark vector
4 | //! let mut bm = Benchmark::init();
5 | //! // Start named timer with name
6 | //! let mut example_bench = NamedTimer::start("Example Bench");
7 | //! // Stop named timer
8 | //! example_bench.stop();
9 | //! // Add named timer to Benchmarks
10 | //! bm.push(example_bench);
11 | //! // Print Benchmark Summary
12 | //! info!({}, bm.summary());
13 | //! ```
14 | use std::time::Instant;
15 |
16 | /// A Benchmark struct to hold NamedTimers with name, start and end Instants,
17 | #[derive(Debug)]
18 | pub struct Benchmark {
19 | named_timers: Vec,
20 | }
21 |
22 | impl Benchmark {
23 | pub fn init() -> Self {
24 | Self {
25 | named_timers: Vec::new(),
26 | }
27 | }
28 | pub fn push(&mut self, timer: NamedTimer) {
29 | self.named_timers.push(timer);
30 | }
31 |
32 | /// Summary of the benchmarks will destruct the vector,
33 | /// formats every element the same way and return
34 | /// a single String with all the available information
35 | /// for easy printing
36 | pub fn summary(&self) -> String {
37 | let mut summary = String::from("\nRustScan Benchmark Summary");
38 |
39 | for timer in &self.named_timers {
40 | if timer.start.is_some() && timer.end.is_some() {
41 | let runtime_secs = timer
42 | .end
43 | .unwrap()
44 | .saturating_duration_since(timer.start.unwrap())
45 | .as_secs_f32();
46 | summary.push_str(&format!("\n{0: <10} | {1: <10}s", timer.name, runtime_secs))
47 | }
48 | }
49 | summary
50 | }
51 | }
52 |
53 | /// The purpose of NamedTimer is to hold a name,
54 | /// start Instant and end Instant for a specific timer.
55 | /// The given name will be presented in the benchmark summary,
56 | /// start and end Instants will be used for calculating runtime.
57 | #[derive(Debug)]
58 | pub struct NamedTimer {
59 | name: &'static str,
60 | start: Option,
61 | end: Option,
62 | }
63 |
64 | impl NamedTimer {
65 | pub fn start(name: &'static str) -> Self {
66 | Self {
67 | name,
68 | start: Some(Instant::now()),
69 | end: None,
70 | }
71 | }
72 | pub fn end(&mut self) {
73 | self.end = Some(Instant::now());
74 | }
75 | }
76 |
77 | #[test]
78 | fn benchmark() {
79 | let mut benchmarks = Benchmark::init();
80 | let mut test_timer = NamedTimer::start("test");
81 | std::thread::sleep(std::time::Duration::from_millis(100));
82 | test_timer.end();
83 | benchmarks.push(test_timer);
84 | benchmarks.push(NamedTimer::start("only_start"));
85 | assert!(benchmarks
86 | .summary()
87 | .contains("\nRustScan Benchmark Summary\ntest | 0."));
88 | assert_ne!(benchmarks.summary().contains("only_start"), true);
89 | }
90 |
--------------------------------------------------------------------------------
/src/input.rs:
--------------------------------------------------------------------------------
1 | use serde_derive::Deserialize;
2 | use std::collections::HashMap;
3 | use std::fs;
4 | use structopt::{clap::arg_enum, StructOpt};
5 |
6 | const LOWEST_PORT_NUMBER: u16 = 1;
7 | const TOP_PORT_NUMBER: u16 = 65535;
8 |
9 | arg_enum! {
10 | /// Represents the strategy in which the port scanning will run.
11 | /// - Serial will run from start to end, for example 1 to 1_000.
12 | /// - Random will randomize the order in which ports will be scanned.
13 | #[derive(Deserialize, Debug, StructOpt, Clone, Copy, PartialEq)]
14 | pub enum ScanOrder {
15 | Serial,
16 | Random,
17 | }
18 | }
19 |
20 | arg_enum! {
21 | /// Represents the scripts variant.
22 | /// - none will avoid running any script, only portscan results will be shown.
23 | /// - default will run the default embedded nmap script, that's part of RustScan since the beginning.
24 | /// - custom will read the ScriptConfig file and the available scripts in the predefined folders
25 | #[derive(Deserialize, Debug, StructOpt, Clone, PartialEq, Copy)]
26 | pub enum ScriptsRequired {
27 | None,
28 | Default,
29 | Custom,
30 | }
31 | }
32 |
33 | /// Represents the range of ports to be scanned.
34 | #[derive(Deserialize, Debug, Clone, PartialEq)]
35 | pub struct PortRange {
36 | pub start: u16,
37 | pub end: u16,
38 | }
39 |
40 | #[cfg(not(tarpaulin_include))]
41 | fn parse_range(input: &str) -> Result {
42 | let range = input
43 | .split('-')
44 | .map(str::parse)
45 | .collect::, std::num::ParseIntError>>();
46 |
47 | if range.is_err() {
48 | return Err(String::from(
49 | "the range format must be 'start-end'. Example: 1-1000.",
50 | ));
51 | }
52 |
53 | match range.unwrap().as_slice() {
54 | [start, end] => Ok(PortRange {
55 | start: *start,
56 | end: *end,
57 | }),
58 | _ => Err(String::from(
59 | "the range format must be 'start-end'. Example: 1-1000.",
60 | )),
61 | }
62 | }
63 |
64 | #[derive(StructOpt, Debug, Clone)]
65 | #[structopt(name = "rustscan", setting = structopt::clap::AppSettings::TrailingVarArg)]
66 | #[allow(clippy::struct_excessive_bools)]
67 | /// Fast Port Scanner built in Rust.
68 | /// WARNING Do not use this program against sensitive infrastructure since the
69 | /// specified server may not be able to handle this many socket connections at once.
70 | /// - Discord https://discord.gg/GFrQsGy
71 | /// - GitHub https://github.com/RustScan/RustScan
72 | pub struct Opts {
73 | /// A list of comma separated CIDRs, IPs, or hosts to be scanned.
74 | #[structopt(short, long, use_delimiter = true)]
75 | pub addresses: Vec,
76 |
77 | /// A list of comma separed ports to be scanned. Example: 80,443,8080.
78 | #[structopt(short, long, use_delimiter = true)]
79 | pub ports: Option>,
80 |
81 | /// A range of ports with format start-end. Example: 1-1000.
82 | #[structopt(short, long, conflicts_with = "ports", parse(try_from_str = parse_range))]
83 | pub range: Option,
84 |
85 | /// Whether to ignore the configuration file or not.
86 | #[structopt(short, long)]
87 | pub no_config: bool,
88 |
89 | /// Greppable mode. Only output the ports. No Nmap. Useful for grep or outputting to a file.
90 | #[structopt(short, long)]
91 | pub greppable: bool,
92 |
93 | /// Accessible mode. Turns off features which negatively affect screen readers.
94 | #[structopt(long)]
95 | pub accessible: bool,
96 |
97 | /// The batch size for port scanning, it increases or slows the speed of
98 | /// scanning. Depends on the open file limit of your OS. If you do 65535
99 | /// it will do every port at the same time. Although, your OS may not
100 | /// support this.
101 | #[structopt(short, long, default_value = "4500")]
102 | pub batch_size: u16,
103 |
104 | /// The timeout in milliseconds before a port is assumed to be closed.
105 | #[structopt(short, long, default_value = "1500")]
106 | pub timeout: u32,
107 |
108 | /// The number of tries before a port is assumed to be closed.
109 | /// If set to 0, rustscan will correct it to 1.
110 | #[structopt(long, default_value = "1")]
111 | pub tries: u8,
112 |
113 | /// Automatically ups the ULIMIT with the value you provided.
114 | #[structopt(short, long)]
115 | pub ulimit: Option,
116 |
117 | /// The order of scanning to be performed. The "serial" option will
118 | /// scan ports in ascending order while the "random" option will scan
119 | /// ports randomly.
120 | #[structopt(long, possible_values = &ScanOrder::variants(), case_insensitive = true, default_value = "serial")]
121 | pub scan_order: ScanOrder,
122 |
123 | /// Level of scripting required for the run.
124 | #[structopt(long, possible_values = &ScriptsRequired::variants(), case_insensitive = true, default_value = "default")]
125 | pub scripts: ScriptsRequired,
126 |
127 | /// Use the top 1000 ports.
128 | #[structopt(long)]
129 | pub top: bool,
130 |
131 | /// The Script arguments to run.
132 | /// To use the argument -A, end RustScan's args with '-- -A'.
133 | /// Example: 'rustscan -T 1500 -a 127.0.0.1 -- -A -sC'.
134 | /// This command adds -Pn -vvv -p $PORTS automatically to nmap.
135 | /// For things like --script '(safe and vuln)' enclose it in quotations marks \"'(safe and vuln)'\"")
136 | #[structopt(last = true)]
137 | pub command: Vec,
138 | }
139 |
140 | #[cfg(not(tarpaulin_include))]
141 | impl Opts {
142 | pub fn read() -> Self {
143 | let mut opts = Opts::from_args();
144 |
145 | if opts.ports.is_none() && opts.range.is_none() {
146 | opts.range = Some(PortRange {
147 | start: LOWEST_PORT_NUMBER,
148 | end: TOP_PORT_NUMBER,
149 | });
150 | }
151 |
152 | opts
153 | }
154 |
155 | /// Reads the command line arguments into an Opts struct and merge
156 | /// values found within the user configuration file.
157 | pub fn merge(&mut self, config: &Config) {
158 | if !self.no_config {
159 | self.merge_required(&config);
160 | self.merge_optional(&config);
161 | }
162 | }
163 |
164 | fn merge_required(&mut self, config: &Config) {
165 | macro_rules! merge_required {
166 | ($($field: ident),+) => {
167 | $(
168 | if let Some(e) = &config.$field {
169 | self.$field = e.clone();
170 | }
171 | )+
172 | }
173 | }
174 |
175 | merge_required!(
176 | addresses, greppable, accessible, batch_size, timeout, tries, scan_order, scripts,
177 | command
178 | );
179 | }
180 |
181 | fn merge_optional(&mut self, config: &Config) {
182 | macro_rules! merge_optional {
183 | ($($field: ident),+) => {
184 | $(
185 | if config.$field.is_some() {
186 | self.$field = config.$field.clone();
187 | }
188 | )+
189 | }
190 | }
191 |
192 | // Only use top ports when the user asks for them
193 | if self.top && config.ports.is_some() {
194 | let mut ports: Vec = Vec::with_capacity(config.ports.clone().unwrap().len());
195 | for entry in config.ports.clone().unwrap().keys() {
196 | ports.push(entry.parse().unwrap())
197 | }
198 | self.ports = Some(ports);
199 | }
200 |
201 | merge_optional!(range, ulimit);
202 | }
203 | }
204 |
205 | /// Struct used to deserialize the options specified within our config file.
206 | /// These will be further merged with our command line arguments in order to
207 | /// generate the final Opts struct.
208 | #[cfg(not(tarpaulin_include))]
209 | #[derive(Debug, Deserialize)]
210 | pub struct Config {
211 | addresses: Option>,
212 | ports: Option>,
213 | range: Option,
214 | greppable: Option,
215 | accessible: Option,
216 | batch_size: Option,
217 | timeout: Option,
218 | tries: Option,
219 | ulimit: Option,
220 | scan_order: Option,
221 | command: Option>,
222 | scripts: Option,
223 | }
224 |
225 | #[cfg(not(tarpaulin_include))]
226 | impl Config {
227 | /// Reads the configuration file with TOML format and parses it into a
228 | /// Config struct.
229 | ///
230 | /// # Format
231 | ///
232 | /// addresses = ["127.0.0.1", "127.0.0.1"]
233 | /// ports = [80, 443, 8080]
234 | /// greppable = true
235 | /// scan_order: "Serial"
236 | ///
237 | pub fn read() -> Self {
238 | let mut home_dir = match dirs::home_dir() {
239 | Some(dir) => dir,
240 | None => panic!("Could not infer config file path."),
241 | };
242 | home_dir.push(".rustscan.toml");
243 |
244 | let mut content = String::new();
245 | if home_dir.exists() {
246 | content = match fs::read_to_string(home_dir) {
247 | Ok(content) => content,
248 | Err(_) => String::new(),
249 | }
250 | }
251 |
252 | let config: Config = match toml::from_str(&content) {
253 | Ok(config) => config,
254 | Err(e) => {
255 | println!("Found {} in configuration file.\nAborting scan.\n", e);
256 | std::process::exit(1);
257 | }
258 | };
259 |
260 | config
261 | }
262 | }
263 |
264 | #[cfg(test)]
265 | mod tests {
266 | use super::{Config, Opts, PortRange, ScanOrder, ScriptsRequired};
267 | impl Config {
268 | fn default() -> Self {
269 | Self {
270 | addresses: Some(vec!["127.0.0.1".to_owned()]),
271 | ports: None,
272 | range: None,
273 | greppable: Some(true),
274 | batch_size: Some(25_000),
275 | timeout: Some(1_000),
276 | tries: Some(1),
277 | ulimit: None,
278 | command: Some(vec!["-A".to_owned()]),
279 | accessible: Some(true),
280 | scan_order: Some(ScanOrder::Random),
281 | scripts: None,
282 | }
283 | }
284 | }
285 |
286 | impl Opts {
287 | pub fn default() -> Self {
288 | Self {
289 | addresses: vec![],
290 | ports: None,
291 | range: None,
292 | greppable: true,
293 | batch_size: 0,
294 | timeout: 0,
295 | tries: 0,
296 | ulimit: None,
297 | command: vec![],
298 | accessible: false,
299 | scan_order: ScanOrder::Serial,
300 | no_config: true,
301 | top: false,
302 | scripts: ScriptsRequired::Default,
303 | }
304 | }
305 | }
306 |
307 | #[test]
308 | fn opts_no_merge_when_config_is_ignored() {
309 | let mut opts = Opts::default();
310 | let config = Config::default();
311 |
312 | opts.merge(&config);
313 |
314 | assert_eq!(opts.addresses, vec![] as Vec);
315 | assert_eq!(opts.greppable, true);
316 | assert_eq!(opts.accessible, false);
317 | assert_eq!(opts.timeout, 0);
318 | assert_eq!(opts.command, vec![] as Vec);
319 | assert_eq!(opts.scan_order, ScanOrder::Serial);
320 | }
321 |
322 | #[test]
323 | fn opts_merge_required_arguments() {
324 | let mut opts = Opts::default();
325 | let config = Config::default();
326 |
327 | opts.merge_required(&config);
328 |
329 | assert_eq!(opts.addresses, config.addresses.unwrap());
330 | assert_eq!(opts.greppable, config.greppable.unwrap());
331 | assert_eq!(opts.timeout, config.timeout.unwrap());
332 | assert_eq!(opts.command, config.command.unwrap());
333 | assert_eq!(opts.accessible, config.accessible.unwrap());
334 | assert_eq!(opts.scan_order, config.scan_order.unwrap());
335 | assert_eq!(opts.scripts, ScriptsRequired::Default)
336 | }
337 |
338 | #[test]
339 | fn opts_merge_optional_arguments() {
340 | let mut opts = Opts::default();
341 | let mut config = Config::default();
342 | config.range = Some(PortRange {
343 | start: 1,
344 | end: 1_000,
345 | });
346 | config.ulimit = Some(1_000);
347 |
348 | opts.merge_optional(&config);
349 |
350 | assert_eq!(opts.range, config.range);
351 | assert_eq!(opts.ulimit, config.ulimit);
352 | }
353 | }
354 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | #![deny(clippy::all)]
2 | #![warn(clippy::pedantic)]
3 | #![allow(clippy::doc_markdown, clippy::if_not_else, clippy::non_ascii_literal)]
4 |
5 | extern crate shell_words;
6 |
7 | mod tui;
8 |
9 | mod input;
10 | use input::{Config, Opts, PortRange, ScanOrder, ScriptsRequired};
11 |
12 | mod scanner;
13 | use scanner::Scanner;
14 |
15 | mod port_strategy;
16 | use port_strategy::PortStrategy;
17 |
18 | mod benchmark;
19 | use benchmark::{Benchmark, NamedTimer};
20 |
21 | mod scripts;
22 | use scripts::{init_scripts, Script, ScriptFile};
23 |
24 | use cidr_utils::cidr::IpCidr;
25 | use colorful::{Color, Colorful};
26 | use futures::executor::block_on;
27 | use rlimit::{getrlimit, setrlimit, RawRlim, Resource, Rlim};
28 | use std::collections::HashMap;
29 | use std::convert::TryInto;
30 | use std::fs::File;
31 | use std::io::{prelude::*, BufReader};
32 | use std::net::{IpAddr, ToSocketAddrs};
33 | use std::path::Path;
34 | use std::string::ToString;
35 | use std::time::Duration;
36 | use trust_dns_resolver::{
37 | config::{ResolverConfig, ResolverOpts},
38 | Resolver,
39 | };
40 |
41 | extern crate colorful;
42 | extern crate dirs;
43 |
44 | // Average value for Ubuntu
45 | const DEFAULT_FILE_DESCRIPTORS_LIMIT: RawRlim = 8000;
46 | // Safest batch size based on experimentation
47 | const AVERAGE_BATCH_SIZE: RawRlim = 3000;
48 |
49 | #[macro_use]
50 | extern crate log;
51 |
52 | #[cfg(not(tarpaulin_include))]
53 | #[allow(clippy::too_many_lines)]
54 | /// Faster Nmap scanning with Rust
55 | /// If you're looking for the actual scanning, check out the module Scanner
56 | fn main() {
57 | env_logger::init();
58 | let mut benchmarks = Benchmark::init();
59 | let mut rustscan_bench = NamedTimer::start("RustScan");
60 |
61 | let mut opts: Opts = Opts::read();
62 | let config = Config::read();
63 | opts.merge(&config);
64 |
65 | debug!("Main() `opts` arguments are {:?}", opts);
66 |
67 | let scripts_to_run: Vec = match init_scripts(opts.scripts) {
68 | Ok(scripts_to_run) => scripts_to_run,
69 | Err(e) => {
70 | warning!(
71 | format!("Initiating scripts failed!\n{}", e.to_string()),
72 | opts.greppable,
73 | opts.accessible
74 | );
75 | std::process::exit(1);
76 | }
77 | };
78 |
79 | debug!("Scripts initialized {:?}", &scripts_to_run);
80 |
81 | if !opts.greppable && !opts.accessible {
82 | print_opening(&opts);
83 | }
84 |
85 | let ips: Vec = parse_addresses(&opts);
86 |
87 | if ips.is_empty() {
88 | warning!(
89 | "No IPs could be resolved, aborting scan.",
90 | opts.greppable,
91 | opts.accessible
92 | );
93 | std::process::exit(1);
94 | }
95 |
96 | let ulimit: RawRlim = adjust_ulimit_size(&opts);
97 | let batch_size: u16 = infer_batch_size(&opts, ulimit);
98 |
99 | let scanner = Scanner::new(
100 | &ips,
101 | batch_size,
102 | Duration::from_millis(opts.timeout.into()),
103 | opts.tries,
104 | opts.greppable,
105 | PortStrategy::pick(&opts.range, opts.ports, opts.scan_order),
106 | opts.accessible,
107 | );
108 | debug!("Scanner finished building: {:?}", scanner);
109 |
110 | let mut portscan_bench = NamedTimer::start("Portscan");
111 | let scan_result = block_on(scanner.run());
112 | portscan_bench.end();
113 | benchmarks.push(portscan_bench);
114 |
115 | let mut ports_per_ip = HashMap::new();
116 |
117 | for socket in scan_result {
118 | ports_per_ip
119 | .entry(socket.ip())
120 | .or_insert_with(Vec::new)
121 | .push(socket.port());
122 | }
123 |
124 | for ip in ips {
125 | if ports_per_ip.contains_key(&ip) {
126 | continue;
127 | }
128 |
129 | // If we got here it means the IP was not found within the HashMap, this
130 | // means the scan couldn't find any open ports for it.
131 |
132 | let x = format!("Looks like I didn't find any open ports for {:?}. This is usually caused by a high batch size.
133 | \n*I used {} batch size, consider lowering it with {} or a comfortable number for your system.
134 | \n Alternatively, increase the timeout if your ping is high. Rustscan -t 2000 for 2000 milliseconds (2s) timeout.\n",
135 | ip,
136 | opts.batch_size,
137 | "'rustscan -b -a '");
138 | warning!(x, opts.greppable, opts.accessible);
139 | }
140 |
141 | let mut script_bench = NamedTimer::start("Scripts");
142 | for (ip, ports) in &ports_per_ip {
143 | let vec_str_ports: Vec = ports.iter().map(ToString::to_string).collect();
144 |
145 | // nmap port style is 80,443. Comma separated with no spaces.
146 | let ports_str = vec_str_ports.join(",");
147 |
148 | // if option scripts is none, no script will be spawned
149 | if opts.greppable || opts.scripts == ScriptsRequired::None {
150 | println!("{} -> [{}]", &ip, ports_str);
151 | continue;
152 | }
153 | detail!("Starting Script(s)", opts.greppable, opts.accessible);
154 |
155 | // Run all the scripts we found and parsed based on the script config file tags field.
156 | for mut script_f in scripts_to_run.clone() {
157 | // This part allows us to add commandline arguments to the Script call_format, appending them to the end of the command.
158 | if !opts.command.is_empty() {
159 | let user_extra_args = &opts.command.join(" ");
160 | debug!("Extra args vec {:?}", user_extra_args);
161 | if script_f.call_format.is_some() {
162 | let mut call_f = script_f.call_format.unwrap();
163 | call_f.push(' ');
164 | call_f.push_str(user_extra_args);
165 | output!(
166 | format!("Running script {:?} on ip {}\nDepending on the complexity of the script, results may take some time to appear.", call_f, &ip),
167 | opts.greppable,
168 | opts.accessible
169 | );
170 | debug!("Call format {}", call_f);
171 | script_f.call_format = Some(call_f);
172 | }
173 | }
174 |
175 | // Building the script with the arguments from the ScriptFile, and ip-ports.
176 | let script = Script::build(
177 | script_f.path,
178 | *ip,
179 | ports.to_vec(),
180 | script_f.port,
181 | script_f.ports_separator,
182 | script_f.tags,
183 | script_f.call_format,
184 | );
185 | match script.run() {
186 | Ok(script_result) => {
187 | detail!(script_result.to_string(), opts.greppable, opts.accessible);
188 | }
189 | Err(e) => {
190 | warning!(
191 | &format!("Error {}", e.to_string()),
192 | opts.greppable,
193 | opts.accessible
194 | );
195 | }
196 | }
197 | }
198 | }
199 |
200 | // To use the runtime benchmark, run the process as: RUST_LOG=info ./rustscan
201 | script_bench.end();
202 | benchmarks.push(script_bench);
203 | rustscan_bench.end();
204 | benchmarks.push(rustscan_bench);
205 | debug!("Benchmarks raw {:?}", benchmarks);
206 | info!("{}", benchmarks.summary());
207 | }
208 |
209 | /// Prints the opening title of RustScan
210 | fn print_opening(opts: &Opts) {
211 | debug!("Printing opening");
212 | let s = r#".----. .-. .-. .----..---. .----. .---. .--. .-. .-.
213 | | {} }| { } |{ {__ {_ _}{ {__ / ___} / {} \ | `| |
214 | | .-. \| {_} |.-._} } | | .-._} }\ }/ /\ \| |\ |
215 | `-' `-'`-----'`----' `-' `----' `---' `-' `-'`-' `-'
216 | The Modern Day Port Scanner."#;
217 | println!("{}", s.gradient(Color::Green).bold());
218 | let info = r#"________________________________________
219 | : https://discord.gg/GFrQsGy :
220 | : https://github.com/RustScan/RustScan :
221 | --------------------------------------"#;
222 | println!("{}", info.gradient(Color::Yellow).bold());
223 | funny_opening!();
224 |
225 | let config_path = dirs::home_dir()
226 | .expect("Could not infer config file path.")
227 | .join(".rustscan.toml");
228 |
229 | detail!(
230 | format!("The config file is expected to be at {:?}", config_path),
231 | opts.greppable,
232 | opts.accessible
233 | );
234 | }
235 |
236 | /// Goes through all possible IP inputs (files or via argparsing)
237 | /// Parses the string(s) into IPs
238 | fn parse_addresses(input: &Opts) -> Vec {
239 | let mut ips: Vec = Vec::new();
240 | let mut unresolved_addresses: Vec<&str> = Vec::new();
241 | let backup_resolver =
242 | Resolver::new(ResolverConfig::cloudflare_tls(), ResolverOpts::default()).unwrap();
243 |
244 | for address in &input.addresses {
245 | let parsed_ips = parse_address(address, &backup_resolver);
246 | if !parsed_ips.is_empty() {
247 | ips.extend(parsed_ips);
248 | } else {
249 | unresolved_addresses.push(address);
250 | }
251 | }
252 |
253 | // If we got to this point this can only be a file path or the wrong input.
254 | for file_path in unresolved_addresses {
255 | let file_path = Path::new(file_path);
256 |
257 | if !file_path.is_file() {
258 | warning!(
259 | format!("Host {:?} could not be resolved.", file_path),
260 | input.greppable,
261 | input.accessible
262 | );
263 |
264 | continue;
265 | }
266 |
267 | if let Ok(x) = read_ips_from_file(file_path, &backup_resolver) {
268 | ips.extend(x);
269 | } else {
270 | warning!(
271 | format!("Host {:?} could not be resolved.", file_path),
272 | input.greppable,
273 | input.accessible
274 | );
275 | }
276 | }
277 |
278 | ips
279 | }
280 |
281 | /// Given a string, parse it as an host, IP address, or CIDR.
282 | /// This allows us to pass files as hosts or cidr or IPs easily
283 | /// Call this everytime you have a possible IP_or_host
284 | fn parse_address(address: &str, resolver: &Resolver) -> Vec {
285 | IpCidr::from_str(&address)
286 | .map(|cidr| cidr.iter().collect())
287 | .ok()
288 | .or_else(|| {
289 | format!("{}:{}", &address, 80)
290 | .to_socket_addrs()
291 | .ok()
292 | .map(|mut iter| vec![iter.next().unwrap().ip()])
293 | })
294 | .unwrap_or_else(|| resolve_ips_from_host(address, resolver))
295 | }
296 |
297 | /// Uses DNS to get the IPS assiocated with host
298 | fn resolve_ips_from_host(source: &str, backup_resolver: &Resolver) -> Vec {
299 | let mut ips: Vec = Vec::new();
300 |
301 | if let Ok(addrs) = source.to_socket_addrs() {
302 | for ip in addrs {
303 | ips.push(ip.ip());
304 | }
305 | } else if let Ok(addrs) = backup_resolver.lookup_ip(&source) {
306 | ips.extend(addrs.iter());
307 | }
308 |
309 | ips
310 | }
311 |
312 | #[cfg(not(tarpaulin_include))]
313 | /// Parses an input file of IPs and uses those
314 | fn read_ips_from_file(
315 | ips: &std::path::Path,
316 | backup_resolver: &Resolver,
317 | ) -> Result, std::io::Error> {
318 | let file = File::open(ips)?;
319 | let reader = BufReader::new(file);
320 |
321 | let mut ips: Vec = Vec::new();
322 |
323 | for address_line in reader.lines() {
324 | if let Ok(address) = address_line {
325 | ips.extend(parse_address(&address, backup_resolver));
326 | } else {
327 | debug!("Line in file is not valid");
328 | }
329 | }
330 |
331 | Ok(ips)
332 | }
333 |
334 | fn adjust_ulimit_size(opts: &Opts) -> RawRlim {
335 | if opts.ulimit.is_some() {
336 | let limit: Rlim = Rlim::from_raw(opts.ulimit.unwrap());
337 |
338 | if setrlimit(Resource::NOFILE, limit, limit).is_ok() {
339 | detail!(
340 | format!("Automatically increasing ulimit value to {}.", limit),
341 | opts.greppable,
342 | opts.accessible
343 | );
344 | } else {
345 | warning!(
346 | "ERROR. Failed to set ulimit value.",
347 | opts.greppable,
348 | opts.accessible
349 | );
350 | }
351 | }
352 |
353 | let (rlim, _) = getrlimit(Resource::NOFILE).unwrap();
354 |
355 | rlim.as_raw()
356 | }
357 |
358 | fn infer_batch_size(opts: &Opts, ulimit: RawRlim) -> u16 {
359 | let mut batch_size: RawRlim = opts.batch_size.into();
360 |
361 | // Adjust the batch size when the ulimit value is lower than the desired batch size
362 | if ulimit < batch_size {
363 | warning!("File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers",
364 | opts.greppable, opts.accessible
365 | );
366 |
367 | // When the OS supports high file limits like 8000, but the user
368 | // selected a batch size higher than this we should reduce it to
369 | // a lower number.
370 | if ulimit < AVERAGE_BATCH_SIZE {
371 | // ulimit is smaller than aveage batch size
372 | // user must have very small ulimit
373 | // decrease batch size to half of ulimit
374 | warning!("Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. ", opts.greppable, opts.accessible);
375 | info!("Halving batch_size because ulimit is smaller than average batch size");
376 | batch_size = ulimit / 2
377 | } else if ulimit > DEFAULT_FILE_DESCRIPTORS_LIMIT {
378 | info!("Batch size is now average batch size");
379 | batch_size = AVERAGE_BATCH_SIZE
380 | } else {
381 | batch_size = ulimit - 100
382 | }
383 | }
384 | // When the ulimit is higher than the batch size let the user know that the
385 | // batch size can be increased unless they specified the ulimit themselves.
386 | else if ulimit + 2 > batch_size && (opts.ulimit.is_none()) {
387 | detail!(format!("File limit higher than batch size. Can increase speed by increasing batch size '-b {}'.", ulimit - 100),
388 | opts.greppable, opts.accessible);
389 | }
390 |
391 | batch_size
392 | .try_into()
393 | .expect("Couldn't fit the batch size into a u16.")
394 | }
395 |
396 | #[cfg(test)]
397 | mod tests {
398 | use crate::{adjust_ulimit_size, infer_batch_size, parse_addresses, print_opening, Opts};
399 | use std::net::Ipv4Addr;
400 |
401 | #[test]
402 | fn batch_size_lowered() {
403 | let mut opts = Opts::default();
404 | opts.batch_size = 50_000;
405 | let batch_size = infer_batch_size(&opts, 120);
406 |
407 | assert!(batch_size < opts.batch_size);
408 | }
409 |
410 | #[test]
411 | fn batch_size_lowered_average_size() {
412 | let mut opts = Opts::default();
413 | opts.batch_size = 50_000;
414 | let batch_size = infer_batch_size(&opts, 9_000);
415 |
416 | assert!(batch_size == 3_000);
417 | }
418 | #[test]
419 | fn batch_size_equals_ulimit_lowered() {
420 | // because ulimit and batch size are same size, batch size is lowered
421 | // to ULIMIT - 100
422 | let mut opts = Opts::default();
423 | opts.batch_size = 50_000;
424 | let batch_size = infer_batch_size(&opts, 5_000);
425 |
426 | assert!(batch_size == 4_900);
427 | }
428 | #[test]
429 | fn batch_size_adjusted_2000() {
430 | // ulimit == batch_size
431 | let mut opts = Opts::default();
432 | opts.batch_size = 50_000;
433 | opts.ulimit = Some(2_000);
434 | let batch_size = adjust_ulimit_size(&opts);
435 |
436 | assert!(batch_size == 2_000);
437 | }
438 | #[test]
439 | fn test_print_opening_no_panic() {
440 | let mut opts = Opts::default();
441 | opts.ulimit = Some(2_000);
442 | // print opening should not panic
443 | print_opening(&opts);
444 | }
445 |
446 | #[test]
447 | fn test_high_ulimit_no_greppable_mode() {
448 | let mut opts = Opts::default();
449 | opts.batch_size = 10;
450 | opts.greppable = false;
451 |
452 | let batch_size = infer_batch_size(&opts, 1_000_000);
453 |
454 | assert!(batch_size == opts.batch_size);
455 | }
456 |
457 | #[test]
458 | fn parse_correct_addresses() {
459 | let mut opts = Opts::default();
460 | opts.addresses = vec!["127.0.0.1".to_owned(), "192.168.0.0/30".to_owned()];
461 | let ips = parse_addresses(&opts);
462 |
463 | assert_eq!(
464 | ips,
465 | [
466 | Ipv4Addr::new(127, 0, 0, 1),
467 | Ipv4Addr::new(192, 168, 0, 0),
468 | Ipv4Addr::new(192, 168, 0, 1),
469 | Ipv4Addr::new(192, 168, 0, 2),
470 | Ipv4Addr::new(192, 168, 0, 3)
471 | ]
472 | );
473 | }
474 |
475 | #[test]
476 | fn parse_correct_host_addresses() {
477 | let mut opts = Opts::default();
478 | opts.addresses = vec!["google.com".to_owned()];
479 | let ips = parse_addresses(&opts);
480 |
481 | assert_eq!(ips.len(), 1);
482 | }
483 |
484 | #[test]
485 | fn parse_correct_and_incorrect_addresses() {
486 | let mut opts = Opts::default();
487 | opts.addresses = vec!["127.0.0.1".to_owned(), "im_wrong".to_owned()];
488 | let ips = parse_addresses(&opts);
489 |
490 | assert_eq!(ips, [Ipv4Addr::new(127, 0, 0, 1),]);
491 | }
492 |
493 | #[test]
494 | fn parse_incorrect_addresses() {
495 | let mut opts = Opts::default();
496 | opts.addresses = vec!["im_wrong".to_owned(), "300.10.1.1".to_owned()];
497 | let ips = parse_addresses(&opts);
498 |
499 | assert_eq!(ips.is_empty(), true);
500 | }
501 | #[test]
502 | fn parse_hosts_file_and_incorrect_hosts() {
503 | // Host file contains IP, Hosts, incorrect IPs, incorrect hosts
504 | let mut opts = Opts::default();
505 | opts.addresses = vec!["fixtures/hosts.txt".to_owned()];
506 | let ips = parse_addresses(&opts);
507 | assert_eq!(ips.len(), 3);
508 | }
509 |
510 | #[test]
511 | fn parse_empty_hosts_file() {
512 | // Host file contains IP, Hosts, incorrect IPs, incorrect hosts
513 | let mut opts = Opts::default();
514 | opts.addresses = vec!["fixtures/empty_hosts.txt".to_owned()];
515 | let ips = parse_addresses(&opts);
516 | assert_eq!(ips.len(), 0);
517 | }
518 |
519 | #[test]
520 | fn parse_naughty_host_file() {
521 | // Host file contains IP, Hosts, incorrect IPs, incorrect hosts
522 | let mut opts = Opts::default();
523 | opts.addresses = vec!["fixtures/naughty_string.txt".to_owned()];
524 | let ips = parse_addresses(&opts);
525 | assert_eq!(ips.len(), 0);
526 | }
527 | }
528 |
--------------------------------------------------------------------------------
/src/port_strategy/mod.rs:
--------------------------------------------------------------------------------
1 | mod range_iterator;
2 | use super::{PortRange, ScanOrder};
3 | use rand::seq::SliceRandom;
4 | use rand::thread_rng;
5 | use range_iterator::RangeIterator;
6 |
7 | /// Represents options of port scanning.
8 | ///
9 | /// Right now all these options involve ranges, but in the future
10 | /// it will also contain custom lists of ports.
11 | #[derive(Debug)]
12 | pub enum PortStrategy {
13 | Manual(Vec),
14 | Serial(SerialRange),
15 | Random(RandomRange),
16 | }
17 |
18 | impl PortStrategy {
19 | pub fn pick(range: &Option, ports: Option>, order: ScanOrder) -> Self {
20 | match order {
21 | ScanOrder::Serial if ports.is_none() => {
22 | let range = range.as_ref().unwrap();
23 | PortStrategy::Serial(SerialRange {
24 | start: range.start,
25 | end: range.end,
26 | })
27 | }
28 | ScanOrder::Random if ports.is_none() => {
29 | let range = range.as_ref().unwrap();
30 | PortStrategy::Random(RandomRange {
31 | start: range.start,
32 | end: range.end,
33 | })
34 | }
35 | ScanOrder::Serial => PortStrategy::Manual(ports.unwrap()),
36 | ScanOrder::Random => {
37 | let mut rng = thread_rng();
38 | let mut ports = ports.unwrap();
39 | ports.shuffle(&mut rng);
40 | PortStrategy::Manual(ports)
41 | }
42 | }
43 | }
44 |
45 | pub fn order(&self) -> Vec {
46 | match self {
47 | PortStrategy::Manual(ports) => ports.to_vec(),
48 | PortStrategy::Serial(range) => range.generate(),
49 | PortStrategy::Random(range) => range.generate(),
50 | }
51 | }
52 | }
53 |
54 | /// Trait associated with a port strategy. Each PortStrategy must be able
55 | /// to generate an order for future port scanning.
56 | trait RangeOrder {
57 | fn generate(&self) -> Vec;
58 | }
59 |
60 | /// As the name implies SerialRange will always generate a vector in
61 | /// ascending order.
62 | #[derive(Debug)]
63 | pub struct SerialRange {
64 | start: u16,
65 | end: u16,
66 | }
67 |
68 | impl RangeOrder for SerialRange {
69 | fn generate(&self) -> Vec {
70 | (self.start..self.end).collect()
71 | }
72 | }
73 |
74 | /// As the name implies RandomRange will always generate a vector with
75 | /// a random order. This vector is built following the LCG algorithm.
76 | #[derive(Debug)]
77 | pub struct RandomRange {
78 | start: u16,
79 | end: u16,
80 | }
81 |
82 | impl RangeOrder for RandomRange {
83 | // Right now using RangeIterator and generating a range + shuffling the
84 | // vector is pretty much the same. The advantages of it will come once
85 | // we have to generate different ranges for different IPs without storing
86 | // actual vectors.
87 | //
88 | // Another benefit of RangeIterator is that it always generate a range with
89 | // a certain distance between the items in the Array. The chances of having
90 | // port numbers close to each other are pretty slim due to the way the
91 | // algorithm works.
92 | fn generate(&self) -> Vec {
93 | RangeIterator::new(self.start.into(), self.end.into()).collect()
94 | }
95 | }
96 |
97 | #[cfg(test)]
98 | mod tests {
99 | use super::PortStrategy;
100 | use crate::{PortRange, ScanOrder};
101 |
102 | #[test]
103 | fn serial_strategy_with_range() {
104 | let range = PortRange { start: 1, end: 100 };
105 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Serial);
106 | let result = strategy.order();
107 | let expected_range = (1..100).into_iter().collect::>();
108 | assert_eq!(expected_range, result);
109 | }
110 | #[test]
111 | fn random_strategy_with_range() {
112 | let range = PortRange { start: 1, end: 100 };
113 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
114 | let mut result = strategy.order();
115 | let expected_range = (1..100).into_iter().collect::>();
116 | assert_ne!(expected_range, result);
117 |
118 | result.sort();
119 | assert_eq!(expected_range, result);
120 | }
121 |
122 | #[test]
123 | fn serial_strategy_with_ports() {
124 | let strategy = PortStrategy::pick(&None, Some(vec![80, 443]), ScanOrder::Serial);
125 | let result = strategy.order();
126 | assert_eq!(vec![80, 443], result);
127 | }
128 |
129 | #[test]
130 | fn random_strategy_with_ports() {
131 | let strategy = PortStrategy::pick(&None, Some((1..10).collect()), ScanOrder::Random);
132 | let mut result = strategy.order();
133 | let expected_range = (1..10).into_iter().collect::>();
134 | assert_ne!(expected_range, result);
135 |
136 | result.sort();
137 | assert_eq!(expected_range, result);
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/src/port_strategy/range_iterator.rs:
--------------------------------------------------------------------------------
1 | use gcd::Gcd;
2 | use rand::Rng;
3 | use std::convert::TryInto;
4 |
5 | pub struct RangeIterator {
6 | active: bool,
7 | normalized_end: u32,
8 | normalized_first_pick: u32,
9 | normalized_pick: u32,
10 | actual_start: u32,
11 | step: u32,
12 | }
13 |
14 | /// An iterator that follows the `Linear Congruential Generator` algorithm.
15 | ///
16 | /// For more information: https://en.wikipedia.org/wiki/Linear_congruential_generator
17 | impl RangeIterator {
18 | /// Receives the the start and end of a range and normalize
19 | /// these values before selecting a coprime for the end of the range
20 | /// which will server as the step for the algorithm.
21 | ///
22 | /// For example, the range `1000-2500` will be normalized to `0-1500`
23 | /// before going through the algorithm.
24 | pub fn new(start: u32, end: u32) -> Self {
25 | let normalized_end = end - start;
26 | let step = pick_random_coprime(normalized_end);
27 |
28 | // Randomly choose a number within the range to be the first
29 | // and assign it as a pick.
30 | let mut rng = rand::thread_rng();
31 | let normalized_first_pick = rng.gen_range(0, normalized_end);
32 |
33 | Self {
34 | active: true,
35 | normalized_end,
36 | step,
37 | normalized_first_pick,
38 | normalized_pick: normalized_first_pick,
39 | actual_start: start,
40 | }
41 | }
42 | }
43 |
44 | impl Iterator for RangeIterator {
45 | type Item = u16;
46 |
47 | // The next step is always bound by the formula: N+1 = (N + STEP) % TOP_OF_THE_RANGE
48 | // It will only stop once we generate a number equal to the first generated number.
49 | fn next(&mut self) -> Option {
50 | if !self.active {
51 | return None;
52 | }
53 |
54 | let current_pick = self.normalized_pick;
55 | let next_pick = (current_pick + self.step) % self.normalized_end;
56 |
57 | // If the next pick is equal to the first pick this means that
58 | // we have iterated through the entire range.
59 | if next_pick == self.normalized_first_pick {
60 | self.active = false;
61 | }
62 |
63 | self.normalized_pick = next_pick;
64 | Some(
65 | (self.actual_start + current_pick)
66 | .try_into()
67 | .expect("Could not convert u32 to u16"),
68 | )
69 | }
70 | }
71 |
72 | /// The probability that two random integers are coprime to one another
73 | /// works out to be around 61%, given that we can safely pick a random
74 | /// number and test it. Just in case we are having a bad day and we cannot
75 | /// pick a coprime number after 10 tries we just return "end - 1" which
76 | /// is guaranteed to be a coprime, but won't provide ideal randomization.
77 | ///
78 | /// We pick between "lower_range" and "upper_range" since values too close to
79 | /// the boundaries, which in these case are the "start" and "end" arguments
80 | /// would also provide non-ideal randomization as discussed on the paragraph
81 | /// above.
82 | fn pick_random_coprime(end: u32) -> u32 {
83 | let range_boundary = end / 4;
84 | let lower_range = range_boundary;
85 | let upper_range = end - range_boundary;
86 | let mut rng = rand::thread_rng();
87 | let mut candidate = rng.gen_range(lower_range, upper_range);
88 |
89 | for _ in 0..10 {
90 | if end.gcd(candidate) == 1 {
91 | return candidate;
92 | } else {
93 | candidate = rng.gen_range(lower_range, upper_range);
94 | }
95 | }
96 |
97 | end - 1
98 | }
99 |
100 | #[cfg(test)]
101 | mod tests {
102 | use super::RangeIterator;
103 |
104 | #[test]
105 | fn range_iterator_iterates_through_the_entire_range() {
106 | let result = generate_sorted_range(1, 10);
107 | let expected_range = (1..10).into_iter().collect::>();
108 | assert_eq!(expected_range, result);
109 |
110 | let result = generate_sorted_range(1, 100);
111 | let expected_range = (1..100).into_iter().collect::>();
112 | assert_eq!(expected_range, result);
113 |
114 | let result = generate_sorted_range(1, 1000);
115 | let expected_range = (1..1000).into_iter().collect::>();
116 | assert_eq!(expected_range, result);
117 |
118 | let result = generate_sorted_range(1, 65_535);
119 | let expected_range = (1..65_535).into_iter().collect::>();
120 | assert_eq!(expected_range, result);
121 |
122 | let result = generate_sorted_range(1000, 2000);
123 | let expected_range = (1000..2000).into_iter().collect::>();
124 | assert_eq!(expected_range, result);
125 | }
126 |
127 | fn generate_sorted_range(start: u32, end: u32) -> Vec {
128 | let range = RangeIterator::new(start, end);
129 | let mut result = range.into_iter().collect::>();
130 | result.sort();
131 |
132 | result
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/scanner/mod.rs:
--------------------------------------------------------------------------------
1 | use super::PortStrategy;
2 |
3 | mod socket_iterator;
4 | use socket_iterator::SocketIterator;
5 |
6 | use async_std::io;
7 | use async_std::net::TcpStream;
8 | use async_std::prelude::*;
9 | use colored::Colorize;
10 | use futures::stream::FuturesUnordered;
11 | use std::{
12 | collections::HashSet,
13 | net::{IpAddr, Shutdown, SocketAddr},
14 | num::NonZeroU8,
15 | time::Duration,
16 | };
17 |
18 | /// The class for the scanner
19 | /// IP is data type IpAddr and is the IP address
20 | /// start & end is where the port scan starts and ends
21 | /// batch_size is how many ports at a time should be scanned
22 | /// Timeout is the time RustScan should wait before declaring a port closed. As datatype Duration.
23 | /// greppable is whether or not RustScan should print things, or wait until the end to print only the ip and open ports.
24 | #[cfg(not(tarpaulin_include))]
25 | #[derive(Debug)]
26 | pub struct Scanner {
27 | ips: Vec,
28 | batch_size: u16,
29 | timeout: Duration,
30 | tries: NonZeroU8,
31 | greppable: bool,
32 | port_strategy: PortStrategy,
33 | accessible: bool,
34 | }
35 |
36 | impl Scanner {
37 | pub fn new(
38 | ips: &[IpAddr],
39 | batch_size: u16,
40 | timeout: Duration,
41 | tries: u8,
42 | greppable: bool,
43 | port_strategy: PortStrategy,
44 | accessible: bool,
45 | ) -> Self {
46 | Self {
47 | batch_size,
48 | timeout,
49 | tries: NonZeroU8::new(std::cmp::max(tries, 1)).unwrap(),
50 | greppable,
51 | port_strategy,
52 | ips: ips.iter().map(ToOwned::to_owned).collect(),
53 | accessible,
54 | }
55 | }
56 |
57 | /// Runs scan_range with chunk sizes
58 | /// If you want to run RustScan normally, this is the entry point used
59 | /// Returns all open ports as Vec
60 | pub async fn run(&self) -> Vec {
61 | let ports: Vec = self.port_strategy.order();
62 | let mut socket_iterator: SocketIterator = SocketIterator::new(&self.ips, &ports);
63 | let mut open_sockets: Vec = Vec::new();
64 | let mut ftrs = FuturesUnordered::new();
65 | let mut errors: HashSet = HashSet::with_capacity(self.ips.len() * 1000);
66 |
67 | for _ in 0..self.batch_size {
68 | if let Some(socket) = socket_iterator.next() {
69 | ftrs.push(self.scan_socket(socket));
70 | } else {
71 | break;
72 | }
73 | }
74 |
75 | debug!("Start scanning sockets. \nBatch size {}\nNumber of ip-s {}\nNumber of ports {}\nTargets all together {} ",
76 | self.batch_size,
77 | self.ips.len(),
78 | &ports.len(),
79 | (self.ips.len() * ports.len()));
80 |
81 | while let Some(result) = ftrs.next().await {
82 | if let Some(socket) = socket_iterator.next() {
83 | ftrs.push(self.scan_socket(socket));
84 | }
85 |
86 | match result {
87 | Ok(socket) => open_sockets.push(socket),
88 | Err(e) => {
89 | let error_string = e.to_string();
90 | if errors.len() < self.ips.len() * 1000 {
91 | errors.insert(error_string);
92 | }
93 | }
94 | }
95 | }
96 | debug!("Typical socket connection errors {:?}", errors);
97 | debug!("Open Sockets found: {:?}", &open_sockets);
98 | open_sockets
99 | }
100 |
101 | /// Given a socket, scan it self.tries times.
102 | /// Turns the address into a SocketAddr
103 | /// Deals with the type
104 | /// If it experiences error ErrorKind::Other then too many files are open and it Panics!
105 | /// Else any other error, it returns the error in Result as a string
106 | /// If no errors occur, it returns the port number in Result to signify the port is open.
107 | /// This function mainly deals with the logic of Results handling.
108 | /// # Example
109 | ///
110 | /// self.scan_socket(socket)
111 | ///
112 | /// Note: `self` must contain `self.ip`.
113 | async fn scan_socket(&self, socket: SocketAddr) -> io::Result {
114 | let tries = self.tries.get();
115 |
116 | for nr_try in 1..=tries {
117 | match self.connect(socket).await {
118 | Ok(x) => {
119 | debug!(
120 | "Connection was successful, shutting down stream {}",
121 | &socket
122 | );
123 | if let Err(e) = x.shutdown(Shutdown::Both) {
124 | debug!("Shutdown stream error {}", &e);
125 | }
126 | if !self.greppable {
127 | if self.accessible {
128 | println!("Open {}", socket.to_string());
129 | } else {
130 | println!("Open {}", socket.to_string().purple());
131 | }
132 | }
133 |
134 | debug!("Return Ok after {} tries", nr_try);
135 | return Ok(socket);
136 | }
137 | Err(e) => {
138 | let mut error_string = e.to_string();
139 |
140 | if error_string.to_lowercase().contains("too many open files") {
141 | panic!("Too many open files. Please reduce batch size. The default is 5000. Try -b 2500.");
142 | }
143 |
144 | if nr_try == tries {
145 | error_string.push(' ');
146 | error_string.push_str(&socket.ip().to_string());
147 | return Err(io::Error::new(io::ErrorKind::Other, error_string));
148 | }
149 | }
150 | };
151 | }
152 | unreachable!();
153 | }
154 |
155 | /// Performs the connection to the socket with timeout
156 | /// # Example
157 | ///
158 | /// let port: u16 = 80
159 | /// // ip is an IpAddr type
160 | /// let ip = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
161 | /// let socket = SocketAddr::new(ip, port);
162 | /// self.connect(socket)
163 | /// // returns Result which is either Ok(stream) for port is open, or Er for port is closed.
164 | /// // Timeout occurs after self.timeout seconds
165 | ///
166 | async fn connect(&self, socket: SocketAddr) -> io::Result {
167 | let stream = io::timeout(
168 | self.timeout,
169 | async move { TcpStream::connect(socket).await },
170 | )
171 | .await?;
172 | Ok(stream)
173 | }
174 | }
175 |
176 | #[cfg(test)]
177 | mod tests {
178 | use super::*;
179 | use crate::{PortRange, ScanOrder};
180 | use async_std::task::block_on;
181 | use std::{net::IpAddr, time::Duration};
182 |
183 | #[test]
184 | fn scanner_runs() {
185 | // Makes sure the program still runs and doesn't panic
186 | let addrs = vec!["127.0.0.1".parse::().unwrap()];
187 | let range = PortRange {
188 | start: 1,
189 | end: 1_000,
190 | };
191 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
192 | let scanner = Scanner::new(
193 | &addrs,
194 | 10,
195 | Duration::from_millis(100),
196 | 1,
197 | true,
198 | strategy,
199 | true,
200 | );
201 | block_on(scanner.run());
202 | // if the scan fails, it wouldn't be able to assert_eq! as it panicked!
203 | assert_eq!(1, 1);
204 | }
205 | #[test]
206 | fn ipv6_scanner_runs() {
207 | // Makes sure the program still runs and doesn't panic
208 | let addrs = vec!["::1".parse::().unwrap()];
209 | let range = PortRange {
210 | start: 1,
211 | end: 1_000,
212 | };
213 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
214 | let scanner = Scanner::new(
215 | &addrs,
216 | 10,
217 | Duration::from_millis(100),
218 | 1,
219 | true,
220 | strategy,
221 | true,
222 | );
223 | block_on(scanner.run());
224 | // if the scan fails, it wouldn't be able to assert_eq! as it panicked!
225 | assert_eq!(1, 1);
226 | }
227 | #[test]
228 | fn quad_zero_scanner_runs() {
229 | let addrs = vec!["0.0.0.0".parse::().unwrap()];
230 | let range = PortRange {
231 | start: 1,
232 | end: 1_000,
233 | };
234 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
235 | let scanner = Scanner::new(
236 | &addrs,
237 | 10,
238 | Duration::from_millis(100),
239 | 1,
240 | true,
241 | strategy,
242 | true,
243 | );
244 | block_on(scanner.run());
245 | assert_eq!(1, 1);
246 | }
247 | #[test]
248 | fn google_dns_runs() {
249 | let addrs = vec!["8.8.8.8".parse::().unwrap()];
250 | let range = PortRange {
251 | start: 400,
252 | end: 445,
253 | };
254 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
255 | let scanner = Scanner::new(
256 | &addrs,
257 | 10,
258 | Duration::from_millis(100),
259 | 1,
260 | true,
261 | strategy,
262 | true,
263 | );
264 | block_on(scanner.run());
265 | assert_eq!(1, 1);
266 | }
267 | #[test]
268 | fn infer_ulimit_lowering_no_panic() {
269 | // Test behaviour on MacOS where ulimit is not automatically lowered
270 | let addrs = vec!["8.8.8.8".parse::().unwrap()];
271 |
272 | // mac should have this automatically scaled down
273 | let range = PortRange {
274 | start: 400,
275 | end: 600,
276 | };
277 | let strategy = PortStrategy::pick(&Some(range), None, ScanOrder::Random);
278 | let scanner = Scanner::new(
279 | &addrs,
280 | 10,
281 | Duration::from_millis(100),
282 | 1,
283 | true,
284 | strategy,
285 | true,
286 | );
287 | block_on(scanner.run());
288 | assert_eq!(1, 1);
289 | }
290 | }
291 |
--------------------------------------------------------------------------------
/src/scanner/socket_iterator.rs:
--------------------------------------------------------------------------------
1 | use itertools::{iproduct, Product};
2 | use std::net::{IpAddr, SocketAddr};
3 |
4 | pub struct SocketIterator<'s> {
5 | // product_it is a cartesian product iterator over
6 | // the slices of ports and IP addresses.
7 | //
8 | // The IP/port order is intentionally reversed here since we want
9 | // the itertools::iproduct! macro below to generate the pairs with
10 | // all the IPs for one port before moving on to the next one
11 | // ("hold the port, go through all the IPs, then advance the port...").
12 | // See also the comments in the iterator implementation for an example.
13 | product_it:
14 | Product>, Box>>,
15 | }
16 |
17 | /// An iterator that receives a slice of IPs and ports and returns a Socket
18 | /// for each IP and port pair until all of these combinations are exhausted.
19 | /// The goal of this iterator is to go over every IP and port combination
20 | /// wihout generating a big memory footprint. The alternative would be
21 | /// generating a vector containing all these combinations.
22 | impl<'s> SocketIterator<'s> {
23 | pub fn new(ips: &'s [IpAddr], ports: &'s [u16]) -> Self {
24 | let ports_it = Box::new(ports.iter());
25 | let ips_it = Box::new(ips.iter());
26 | Self {
27 | product_it: iproduct!(ports_it, ips_it),
28 | }
29 | }
30 | }
31 |
32 | impl<'s> Iterator for SocketIterator<'s> {
33 | type Item = SocketAddr;
34 |
35 | /// Returns a socket based on the combination of one of the provided
36 | /// IPs and ports or None when these combinations are exhausted. Every
37 | /// IP will have the same port until a port is incremented.
38 | ///
39 | /// let it = SocketIterator::new(&["127.0.0.1", "192.168.0.1"], &[80, 443]);
40 | /// it.next(); // 127.0.0.1:80
41 | /// it.next(); // 192.168.0.1:80
42 | /// it.next(); // 127.0.0.1:443
43 | /// it.next(); // 192.168.0.1:443
44 | /// it.next(); // None
45 | fn next(&mut self) -> Option {
46 | match self.product_it.next() {
47 | None => None,
48 | Some((port, ip)) => Some(SocketAddr::new(*ip, *port)),
49 | }
50 | }
51 | }
52 |
53 | #[cfg(test)]
54 | mod tests {
55 | use super::SocketIterator;
56 | use std::net::{IpAddr, SocketAddr};
57 |
58 | #[test]
59 | fn goes_through_every_ip_port_combination() {
60 | let addrs = vec![
61 | "127.0.0.1".parse::().unwrap(),
62 | "192.168.0.1".parse::().unwrap(),
63 | ];
64 | let ports: Vec = vec![22, 80, 443];
65 | let mut it = SocketIterator::new(&addrs, &ports);
66 |
67 | assert_eq!(Some(SocketAddr::new(addrs[0], ports[0])), it.next());
68 | assert_eq!(Some(SocketAddr::new(addrs[1], ports[0])), it.next());
69 | assert_eq!(Some(SocketAddr::new(addrs[0], ports[1])), it.next());
70 | assert_eq!(Some(SocketAddr::new(addrs[1], ports[1])), it.next());
71 | assert_eq!(Some(SocketAddr::new(addrs[0], ports[2])), it.next());
72 | assert_eq!(Some(SocketAddr::new(addrs[1], ports[2])), it.next());
73 | assert_eq!(None, it.next());
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/scripts/mod.rs:
--------------------------------------------------------------------------------
1 | //! Scripting engine to run scripts based on tags.
2 | //! This module serves to filter and run the scripts selected by the user.
3 | //!
4 | //! A new commandline and configuration file option was added.
5 | //!
6 | //! --scripts
7 | //!
8 | //! default
9 | //! This is the default behavior, like as it was from the beginning of RustScan.
10 | //! The user do not have to chose anything for this. This is the only script embedded in RustScan running as default.
11 | //!
12 | //! none
13 | //! The user have to use the --scripts none commandline argument or scripts = "none" in the config file.
14 | //! None of the scripts will run, this replaces the removed --no-nmap option.
15 | //!
16 | //! custom
17 | //! The user have to use the --scripts custom commandline argument or scripts = "custom" in the config file.
18 | //! Rustscan will look for the script configuration file in the user's home dir: home_dir/.rustscan_scripts.toml
19 | //! The config file have 3 optional fields, tag, developer and port. Just the tag field will be used forther in the process.
20 | //! RustScan will also look for available scripts in the user's home dir: home_dir/.rustscan_scripts
21 | //! and will try to read all the files, and parse them into a vector of ScriptFiles.
22 | //! Filtering on tags means the tags found in the rustscan_scripts.toml file will also have to be present in the Scriptfile,
23 | //! otherwise the script will not be selected.
24 | //! All of the rustscan_script.toml tags have to be present at minimum in a Scriptfile to get selected, but can be also more.
25 | //!
26 | //! Config file example:
27 | //! fixtures/test_rustscan_scripts.toml
28 | //!
29 | //! Script file examples:
30 | //! fixtures/test_script.py
31 | //! fixtures/test_script.pl
32 | //! fixtures/test_script.sh
33 | //! fixtures/test_script.txt
34 | //!
35 | //! call_format in script files can be of 2 variants.
36 | //! One is where all of the possible tags {{script}} {{ip}} {{port}} are there.
37 | //! The {{script}} part will be replaced with the scriptfile full path gathered while parsing available scripts.
38 | //! The {{ip}} part will be replaced with the ip we got from the scan.
39 | //! The {{port}} part will be reaplced with the ports separated with the ports_separator found in the script file
40 | //!
41 | //! And when there is only {{ip}} and {{port}} is in the format, ony those will be replaced with the arguments from the scan.
42 | //! This makes it easy to run a system installed command like nmap, and give any kind of arguments to it.
43 | //!
44 | //! If the format is different, the script will be silently discarded and will not run. With the Debug option it's possible to see where it goes wrong.
45 |
46 | #![allow(clippy::module_name_repetitions)]
47 |
48 | use crate::input::ScriptsRequired;
49 | use anyhow::{anyhow, Result};
50 | use serde_derive::{Deserialize, Serialize};
51 | use std::collections::HashSet;
52 | use std::convert::TryInto;
53 | use std::fs::{self, File};
54 | use std::io::{self, prelude::*};
55 | use std::net::IpAddr;
56 | use std::path::PathBuf;
57 | use std::string::ToString;
58 | use subprocess::{Exec, ExitStatus};
59 | use text_placeholder::Template;
60 |
61 | static DEFAULT: &str = r#"tags = ["core_approved", "RustScan", "default"]
62 | developer = [ "RustScan", "https://github.com/RustScan" ]
63 | ports_separator = ","
64 | call_format = "nmap -vvv -p {{port}} {{ip}}"
65 | "#;
66 |
67 | #[cfg(not(tarpaulin_include))]
68 | pub fn init_scripts(scripts: ScriptsRequired) -> Result> {
69 | let mut scripts_to_run: Vec = Vec::new();
70 |
71 | match scripts {
72 | ScriptsRequired::None => Ok(scripts_to_run),
73 | ScriptsRequired::Default => {
74 | let default_script =
75 | toml::from_str::(&DEFAULT).expect("Failed to parse Script file.");
76 | scripts_to_run.push(default_script);
77 | Ok(scripts_to_run)
78 | }
79 | ScriptsRequired::Custom => {
80 | let scripts_dir_base = match dirs::home_dir() {
81 | Some(dir) => dir,
82 | None => return Err(anyhow!("Could not infer scripts path.")),
83 | };
84 | let script_paths = match find_scripts(scripts_dir_base) {
85 | Ok(script_paths) => script_paths,
86 | Err(e) => return Err(anyhow!(e)),
87 | };
88 | debug!("Scripts paths \n{:?}", script_paths);
89 |
90 | let parsed_scripts = parse_scripts(script_paths);
91 | debug!("Scripts parsed \n{:?}", parsed_scripts);
92 |
93 | let script_config = match ScriptConfig::read_config() {
94 | Ok(script_config) => script_config,
95 | Err(e) => return Err(anyhow!(e)),
96 | };
97 | debug!("Script config \n{:?}", script_config);
98 |
99 | // Only Scripts that contain all the tags found in ScriptConfig will be selected.
100 | if script_config.tags.is_some() {
101 | let config_hashset: HashSet =
102 | script_config.tags.unwrap().into_iter().collect();
103 | for script in &parsed_scripts {
104 | if script.tags.is_some() {
105 | let script_hashset: HashSet =
106 | script.tags.clone().unwrap().into_iter().collect();
107 | if config_hashset.is_subset(&script_hashset) {
108 | scripts_to_run.push(script.to_owned());
109 | } else {
110 | debug!(
111 | "\nScript tags does not match config tags {:?} {}",
112 | &script_hashset,
113 | script.path.clone().unwrap().display()
114 | );
115 | }
116 | }
117 | }
118 | }
119 | debug!("\nScript(s) to run {:?}", scripts_to_run);
120 | Ok(scripts_to_run)
121 | }
122 | }
123 | }
124 |
125 | pub fn parse_scripts(scripts: Vec) -> Vec {
126 | let mut parsed_scripts: Vec = Vec::with_capacity(scripts.len());
127 | for script in scripts {
128 | debug!("Parsing script {}", &script.display());
129 | if let Some(script_file) = ScriptFile::new(script) {
130 | parsed_scripts.push(script_file);
131 | }
132 | }
133 | parsed_scripts
134 | }
135 |
136 | #[derive(Clone, Debug)]
137 | pub struct Script {
138 | // Path to the script itself.
139 | path: Option,
140 |
141 | // Ip got from scanner.
142 | ip: IpAddr,
143 |
144 | // Ports found with portscan.
145 | open_ports: Vec,
146 |
147 | // Port found in ScriptFile, if defined only this will run with the ip.
148 | trigger_port: Option,
149 |
150 | // Character to join ports in case we want to use a string format of them, for example nmap -p.
151 | ports_separator: Option,
152 |
153 | // Tags found in ScriptFile.
154 | tags: Option>,
155 |
156 | // The format how we want the script to run.
157 | call_format: Option,
158 | }
159 |
160 | #[derive(Serialize)]
161 | struct ExecPartsScript {
162 | script: String,
163 | ip: String,
164 | port: String,
165 | }
166 |
167 | #[derive(Serialize)]
168 | struct ExecParts {
169 | ip: String,
170 | port: String,
171 | }
172 |
173 | impl Script {
174 | pub fn build(
175 | path: Option,
176 | ip: IpAddr,
177 | open_ports: Vec,
178 | trigger_port: Option,
179 | ports_separator: Option,
180 | tags: Option>,
181 | call_format: Option,
182 | ) -> Self {
183 | Self {
184 | path,
185 | ip,
186 | open_ports,
187 | trigger_port,
188 | ports_separator,
189 | tags,
190 | call_format,
191 | }
192 | }
193 |
194 | // Some variables get changed before read, and compiler throws warning on warn(unused_assignments)
195 | #[allow(unused_assignments)]
196 | pub fn run(self) -> Result {
197 | debug!("run self {:?}", &self);
198 |
199 | let separator = self.ports_separator.unwrap_or_else(|| ",".into());
200 |
201 | let mut ports_str = self
202 | .open_ports
203 | .iter()
204 | .map(ToString::to_string)
205 | .collect::>()
206 | .join(&separator);
207 | if let Some(port) = self.trigger_port {
208 | ports_str = port;
209 | }
210 |
211 | let mut final_call_format = String::new();
212 | if let Some(call_format) = self.call_format {
213 | final_call_format = call_format;
214 | } else {
215 | return Err(anyhow!("Failed to parse execution format."));
216 | }
217 | let default_template: Template = Template::new(&final_call_format);
218 | let mut to_run = String::new();
219 |
220 | if final_call_format.contains("{{script}}") {
221 | let exec_parts_script: ExecPartsScript = ExecPartsScript {
222 | script: self.path.unwrap().to_str().unwrap().to_string(),
223 | ip: self.ip.to_string(),
224 | port: ports_str,
225 | };
226 | to_run = default_template.fill_with_struct(&exec_parts_script)?;
227 | } else {
228 | let exec_parts: ExecParts = ExecParts {
229 | ip: self.ip.to_string(),
230 | port: ports_str,
231 | };
232 | to_run = default_template.fill_with_struct(&exec_parts)?;
233 | }
234 | debug!("\nScript format to run {}", to_run);
235 |
236 | let arguments = shell_words::split(&to_run).expect("Failed to parse script arguments");
237 |
238 | execute_script(arguments)
239 | }
240 | }
241 |
242 | #[cfg(not(tarpaulin_include))]
243 | fn execute_script(mut arguments: Vec) -> Result {
244 | debug!("\nScript arguments vec: {:?}", &arguments);
245 | let process = Exec::cmd(&arguments.remove(0)).args(&arguments);
246 | match process.capture() {
247 | Ok(c) => {
248 | let es = match c.exit_status {
249 | ExitStatus::Exited(c) => c.try_into().unwrap(),
250 | ExitStatus::Signaled(c) => c.into(),
251 | ExitStatus::Other(c) => c,
252 | ExitStatus::Undetermined => -1,
253 | };
254 | if es != 0 {
255 | return Err(anyhow!("Exit code = {}", es));
256 | }
257 | Ok(c.stdout_str())
258 | }
259 | Err(error) => {
260 | debug!("Command error {}", error.to_string());
261 | Err(anyhow!(error.to_string()))
262 | }
263 | }
264 | }
265 |
266 | pub fn find_scripts(mut path: PathBuf) -> Result> {
267 | path.push(".rustscan_scripts");
268 | if path.is_dir() {
269 | debug!("Scripts folder found {}", &path.display());
270 | let mut files_vec: Vec = Vec::new();
271 | for entry in fs::read_dir(path)? {
272 | let entry = entry?;
273 | files_vec.push(entry.path());
274 | }
275 | Ok(files_vec)
276 | } else {
277 | Err(anyhow!("Can't find scripts folder {}", path.display()))
278 | }
279 | }
280 |
281 | #[derive(Debug, Clone, Deserialize)]
282 | pub struct ScriptFile {
283 | pub path: Option,
284 | pub tags: Option>,
285 | pub developer: Option>,
286 | pub port: Option,
287 | pub ports_separator: Option,
288 | pub call_format: Option,
289 | }
290 |
291 | impl ScriptFile {
292 | fn new(script: PathBuf) -> Option {
293 | let real_path = script.clone();
294 | let mut lines_buf = String::new();
295 | if let Ok(file) = File::open(script) {
296 | for line in io::BufReader::new(file).lines().skip(1) {
297 | if let Ok(mut line) = line {
298 | if line.starts_with('#') {
299 | line.retain(|c| c != '#');
300 | line = line.trim().to_string();
301 | line.push('\n');
302 | lines_buf.push_str(&line);
303 | } else {
304 | break;
305 | }
306 | }
307 | }
308 | } else {
309 | debug!("Failed to read file: {}", &real_path.display());
310 | return None;
311 | }
312 | debug!("ScriptFile {} lines\n{}", &real_path.display(), &lines_buf);
313 |
314 | match toml::from_str::(&lines_buf) {
315 | Ok(mut parsed) => {
316 | debug!("Parsed ScriptFile{} \n{:?}", &real_path.display(), &parsed);
317 | parsed.path = Some(real_path);
318 | // parsed_scripts.push(parsed);
319 | Some(parsed)
320 | }
321 | Err(e) => {
322 | debug!("Failed to parse ScriptFile headers {}", e.to_string());
323 | None
324 | }
325 | }
326 | }
327 | }
328 |
329 | #[derive(Debug, Deserialize, Clone)]
330 | pub struct ScriptConfig {
331 | pub tags: Option>,
332 | pub ports: Option>,
333 | pub developer: Option>,
334 | }
335 |
336 | #[cfg(not(tarpaulin_include))]
337 | impl ScriptConfig {
338 | pub fn read_config() -> Result {
339 | let mut home_dir = match dirs::home_dir() {
340 | Some(dir) => dir,
341 | None => return Err(anyhow!("Could not infer ScriptConfig path.")),
342 | };
343 | home_dir.push(".rustscan_scripts.toml");
344 |
345 | let content = fs::read_to_string(home_dir)?;
346 | let config = toml::from_str::(&content)?;
347 | Ok(config)
348 | }
349 | }
350 |
351 | #[cfg(test)]
352 | mod tests {
353 | use super::{find_scripts, parse_scripts, Script, ScriptFile};
354 |
355 | // Function for testing only, it inserts static values into ip and open_ports
356 | // Doesn't use impl in case it's implemented in the super module at some point
357 | fn into_script(script_f: ScriptFile) -> Script {
358 | Script::build(
359 | script_f.path,
360 | "127.0.0.1".parse().unwrap(),
361 | vec![80, 8080],
362 | script_f.port,
363 | script_f.ports_separator,
364 | script_f.tags,
365 | script_f.call_format,
366 | )
367 | }
368 |
369 | #[test]
370 | fn find_and_parse_scripts() {
371 | let scripts = find_scripts("fixtures/".into()).unwrap();
372 | let scripts = parse_scripts(scripts);
373 | assert_eq!(scripts.len(), 4);
374 | }
375 |
376 | #[test]
377 | #[should_panic]
378 | fn find_invalid_folder() {
379 | let _scripts = find_scripts("Cargo.toml".into()).unwrap();
380 | }
381 |
382 | #[test]
383 | #[should_panic]
384 | fn open_script_file_invalid_headers() {
385 | ScriptFile::new("fixtures/.rustscan_scripts/test_script_invalid_headers.txt".into())
386 | .unwrap();
387 | }
388 |
389 | #[test]
390 | #[should_panic]
391 | fn open_script_file_invalid_call_format() {
392 | let mut script_f =
393 | ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap();
394 | script_f.call_format = Some("qwertyuiop".to_string());
395 | let script: Script = into_script(script_f);
396 | let _output = script.run().unwrap();
397 | }
398 |
399 | #[test]
400 | #[should_panic]
401 | fn open_script_file_missing_call_format() {
402 | let mut script_f =
403 | ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap();
404 | script_f.call_format = None;
405 | let script: Script = into_script(script_f);
406 | let _output = script.run().unwrap();
407 | }
408 |
409 | #[test]
410 | #[should_panic]
411 | fn open_nonexisting_script_file() {
412 | ScriptFile::new("qwertyuiop.txt".into()).unwrap();
413 | }
414 |
415 | #[test]
416 | fn parse_txt_script() {
417 | let script_f =
418 | ScriptFile::new("fixtures/.rustscan_scripts/test_script.txt".into()).unwrap();
419 | assert_eq!(
420 | script_f.tags,
421 | Some(vec!["core_approved".to_string(), "example".to_string()])
422 | );
423 | assert_eq!(
424 | script_f.developer,
425 | Some(vec![
426 | "example".to_string(),
427 | "https://example.org".to_string()
428 | ])
429 | );
430 | assert_eq!(script_f.ports_separator, Some(",".to_string()));
431 | assert_eq!(
432 | script_f.call_format,
433 | Some("nmap -vvv -p {{port}} {{ip}}".to_string())
434 | );
435 | }
436 |
437 | #[test]
438 | fn run_bash_script() {
439 | let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.sh".into()).unwrap();
440 | let script: Script = into_script(script_f);
441 | let output = script.run().unwrap();
442 | // output has a newline at the end by default, .trim() trims it
443 | assert_eq!(output.trim(), "127.0.0.1 80,8080");
444 | }
445 |
446 | #[test]
447 | fn run_python_script() {
448 | let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.py".into()).unwrap();
449 | let script: Script = into_script(script_f);
450 | let output = script.run().unwrap();
451 | // output has a newline at the end by default, .trim() trims it
452 | assert_eq!(
453 | output.trim(),
454 | "Python script ran with arguments ['fixtures/.rustscan_scripts/test_script.py', '127.0.0.1', '80,8080']"
455 | );
456 | }
457 |
458 | #[test]
459 | fn run_perl_script() {
460 | let script_f = ScriptFile::new("fixtures/.rustscan_scripts/test_script.pl".into()).unwrap();
461 | let script: Script = into_script(script_f);
462 | let output = script.run().unwrap();
463 | // output has a newline at the end by default, .trim() trims it
464 | assert_eq!(output.trim(), "Total args passed to fixtures/.rustscan_scripts/test_script.pl : 2\nArg # 1 : 127.0.0.1\nArg # 2 : 80,8080");
465 | }
466 | }
467 |
--------------------------------------------------------------------------------
/src/tui.rs:
--------------------------------------------------------------------------------
1 | /// Terminal User Interface Module for RustScan
2 | /// Defines macros to use
3 |
4 | #[macro_export]
5 | macro_rules! warning {
6 | ($name:expr) => {
7 | use ansi_term::Colour::Red;
8 | println!("{} {}", Red.bold().paint("[!]"), $name);
9 | };
10 | ($name:expr, $greppable:expr, $accessible:expr) => {
11 | use ansi_term::Colour::Red;
12 | // if not greppable then print, otherwise no else statement so do not print.
13 | if !$greppable {
14 | if $accessible {
15 | // Don't print the ascii art
16 | println!("{}", $name);
17 | } else {
18 | println!("{} {}", Red.bold().paint("[!]"), $name);
19 | }
20 | }
21 | };
22 | }
23 |
24 | #[macro_export]
25 | macro_rules! detail {
26 | ($name:expr) => {
27 | use ansi_term::Colour::Blue;
28 | println!("{} {}", Blue.bold().paint("[~]"), $name);
29 | };
30 | ($name:expr, $greppable:expr, $accessible:expr) => {
31 | use ansi_term::Colour::Blue;
32 | // if not greppable then print, otherwise no else statement so do not print.
33 | if !$greppable {
34 | if $accessible {
35 | // Don't print the ascii art
36 | println!("{}", $name);
37 | } else {
38 | println!("{} {}", Blue.bold().paint("[~]"), $name);
39 | }
40 | }
41 | };
42 | }
43 |
44 | #[macro_export]
45 | macro_rules! output {
46 | ($name:expr) => {
47 | use ansi_term::Colour::RGB;
48 | println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
49 | };
50 | ($name:expr, $greppable:expr, $accessible:expr) => {
51 | use ansi_term::Colour::RGB;
52 | // if not greppable then print, otherwise no else statement so do not print.
53 | if !$greppable {
54 | if $accessible {
55 | // Don't print the ascii art
56 | println!("{}", $name);
57 | } else {
58 | println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
59 | }
60 | }
61 | };
62 | }
63 |
64 | #[macro_export]
65 | macro_rules! funny_opening {
66 | // prints a funny quote / opening
67 | () => {
68 | use rand::seq::SliceRandom;
69 | let quotes = vec![
70 | "Nmap? More like slowmap.🐢",
71 | "🌍HACK THE PLANET🌍",
72 | "Real hackers hack time ⌛",
73 | "Please contribute more quotes to our GitHub https://github.com/rustscan/rustscan",
74 | "😵 https://admin.tryhackme.com",
75 | "0day was here ♥",
76 | ];
77 | let random_quote = quotes.choose(&mut rand::thread_rng()).unwrap();
78 |
79 | println!("{}\n", random_quote);
80 | // println!("{} {}", RGB(0, 255, 9).bold().paint("[>]"), $name);
81 | };
82 | }
83 |
--------------------------------------------------------------------------------
/tests/timelimits.rs:
--------------------------------------------------------------------------------
1 | /*
2 | * Test rustscan against different targets with a time limit.
3 | * The tests assumes target/debug/rustscan has already been built.
4 | *
5 | * The tests are #[ignore] to avoid running them during normal development.
6 | *
7 | * Their tests in the timelimits module are run by travis during CI.
8 | */
9 |
10 | use std::process::Command;
11 | use std::time::Duration;
12 | use wait_timeout::ChildExt;
13 |
14 | const TIMEOUT_MARGIN: u32 = 3;
15 |
16 | #[cfg(not(tarpaulin_include))]
17 | fn run_rustscan_with_timeout(args: &[&str], timeout: Duration) {
18 | println!("Running: target/debug/rustscan: {}", args.join(" "));
19 |
20 | use std::time::Instant;
21 |
22 | let start = Instant::now();
23 |
24 | let mut child = Command::new("target/debug/rustscan")
25 | .args(args)
26 | .spawn()
27 | .unwrap();
28 |
29 | let mut tries = TIMEOUT_MARGIN;
30 | loop {
31 | match child.wait_timeout(timeout).unwrap() {
32 | Some(_status) => break,
33 | None => {
34 | tries -= 1;
35 | if tries == 0 {
36 | // child hasn't exited yet
37 | child.kill().unwrap();
38 | child.wait().unwrap().code();
39 | panic!("Timeout while running command");
40 | }
41 | }
42 | }
43 | }
44 | let end = Instant::now();
45 | let duration = end.saturating_duration_since(start).as_secs_f32();
46 |
47 | println!("time: {:1.1}s", duration);
48 | }
49 |
50 | mod timelimits {
51 |
52 | #[test]
53 | #[ignore]
54 | fn scan_localhost() {
55 | let timeout = super::Duration::from_secs(25);
56 | super::run_rustscan_with_timeout(&["--greppable", "--no-nmap", "127.0.0.1"], timeout);
57 | }
58 |
59 | #[test]
60 | #[ignore]
61 | fn scan_google_com() {
62 | super::run_rustscan_with_timeout(
63 | &[
64 | "--greppable",
65 | "--no-nmap",
66 | "-u",
67 | "5000",
68 | "-b",
69 | "2500",
70 | "google.com",
71 | ],
72 | super::Duration::from_secs(28),
73 | );
74 | }
75 |
76 | #[test]
77 | #[ignore]
78 | fn scan_example_com() {
79 | super::run_rustscan_with_timeout(
80 | &[
81 | "--greppable",
82 | "--no-nmap",
83 | "-u",
84 | "5000",
85 | "-b",
86 | "2500",
87 | "example.com",
88 | ],
89 | super::Duration::from_secs(28),
90 | );
91 | }
92 |
93 | #[test]
94 | #[ignore]
95 | fn scan_rustscan_cmnatic_co_uk() {
96 | super::run_rustscan_with_timeout(
97 | &[
98 | "--greppable",
99 | "--no-nmap",
100 | "-u",
101 | "5000",
102 | "-b",
103 | "2500",
104 | "rustscan.cmnatic.co.uk",
105 | ],
106 | super::Duration::from_secs(26),
107 | );
108 | }
109 | }
110 |
--------------------------------------------------------------------------------