├── .github └── FUNDING.yml ├── LICENSE ├── README.md ├── installz0e.sh ├── sticker-ZeroE.png └── zero-e /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Inscyght 2 | buy_me_a_coffee: Inscyght 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | # Description 6 | Host discovery and service enumeration are part of every network pentest and routine network check. It's relatively straightforward, and some could probably do it while sleeping (you will be with this tool). But ensuring thoroughness and accuracy while maximizing efficiency is a tedious process that requires attentiveness. Zero-E (z0e) aims to automate this process in a fire-and-forget manner to free up your attention, enabling you to work on other things and save valuable time. It uses a thoughtful, extensively-tested methodology that balances thoroughness, accuracy, and efficiency. Among many other functions, it generates multiple files for various analysis purposes and easy post-scan target acquisition. 7 | 8 | Zero in on your environment with zero experience required, taking you from zero to elite-- ...ok you get it. It's zero effort, zero error network enumeration made e-z. So embrace your inner script kiddie, sit back in your reclining ergonomic chair, and take a nap while Zero-E does your work for you. 9 | 10 | Please consider supporting this project (especially if using it for commercial or business purposes) with Github Sponsor, [BuyMeACoffee](https://www.buymeacoffee.com/inscyght), or Bitcoin (wallet address: 37Gofs5XGv8zB8odoFTJLv8NZk9TvwSr3i) 11 | 12 | 13 | # Features 14 | 1. Performs initial discovery scans for alive hosts and open ports 15 | 1. Generates a file with alive hosts and a file with open TCP and UDP (if enabled) ports for reference 16 | 1. Performs in-depth TCP and UDP (if enabled) service scans against only the alive hosts and open ports from the discovery scans 17 | 1. Includes a checkpoint system for resuming scans in case they're stopped before completion 18 | 1. Supports sessions. Useful for saving multiple scan states or running simultaneous scans (not recommended) 19 | 1. Option for both external and internal scans, which changes scan methodology appropriately 20 | 1. Allows for enabling or disabling UDP scans 21 | 1. Detects, alerts on, and excludes from service scans hosts with more than 100 ports open 22 | - It's highly unusual for a host to have this many ports open and indicates a possible deception host or firewall affecting scan results 23 | 1. Accepts command options, but reverts to interactive prompts if required options are left out 24 | 1. Performs a plethora of checks and includes functions to prevent as many potential scan errors as possible 25 | 1. Integrated [ntfy](https://github.com/binwiederhier/ntfy) functionality for sending notifications to your devices 26 | - Useful for large networks with long scan times 27 | 1. For internal scans, which typically include more target hosts, detects the total number of hosts and adjusts scan speeds accordingly 28 | 1. Includes functions to calculate the total number of target IP addresses, and to generate a list of unique, single IP addresses from the provided file(s) and/or IP(s) without needing ipcalc or prips 29 | 1. Generates a file with open ports in Nessus-ready format for faster vulnerability scanning 30 | 1. Written as a single Bash script for maximum portability, compatibility, and ease of use 31 | 1. Includes timestamps in terminal output and a log file for reference 32 | 1. Checks if running on MacOS and adjusts commands accordingly (untested) 33 | 1. Includes an option to enable only UDP scanning and/or running only the specified stage/scan 34 | - Use case example: Initial run has UDP scans disabled for faster completion. Once completed, and while analyzing TCP results, use `--only` and enable UDP to only run UDP scans 35 | 1. Has a function that enables the entry of custom masscan and nmap options for each step in the scanning process (experimental) 36 | 1. Includes functions that parse Nmap output and create lists for various analysis purposes, which also run automatically during scans. 37 | 38 | 39 | # Requirements 40 | - Nmap 41 | - Masscan 42 | - iptables (pfctl for MacOS) 43 | - dos2unix 44 | - realpath 45 | - curl (if enabling ntfy notifications) 46 | 47 | 48 | # How To 49 | 50 | ## Basic usage (interactive prompts) 51 | 1. `sudo ./zero-e` 52 | 2. At the prompts, enter: 53 | 1. the stage to start at 54 | 1. the scan type ([i]nternal or [e]xternal) 55 | 1. whether to enable UDP scans 56 | 1. the desired file path of the output directory for generated files 57 | 1. the file path of the file(s) containing the target IP addresses and/or single IP addresses, ranges, or CIDRs (comma-separated, e.g. targets.txt,1.1.1.1/24) 58 | 1. the file path of the file(s) containing the IP addresses and/or single IP addresses, ranges, or CIDRs (comma-separated, e.g. excludes.txt,1.1.1.1/24) to exclude from scans, if any 59 | 3. Embrace your inner script kiddie, sit back in your reclining ergonomic chair, and take a nap while z0e does your work for you 60 | 61 | ## Advanced usage (switches) 62 | 1. 63 | ```` 64 | sudo $(basename $0) [-e | -i] [-o ] [-t ] [-x [excludes_file and/or IP(s)]] 65 | [-U | -u] [-S [stage] | -s] 66 | [--defaults] [--ngineer] [--only] 67 | [--count ] [--geniplist ] 68 | [--listwinhosts [OutputFile]] 69 | [--parseports [OutputFileName]] 70 | [--listiphostname [OutputFile]] 71 | [--ntfy [priority,]] [--session ] 72 | [--help] [--version] 73 | 74 | PRIMARY OPTIONS (z0e will prompt for these if not provided): 75 | -e Run external assessment scans (cannot be used with -i) 76 | -i Run internal assessment scans (cannot be used with -e) 77 | -o Set output directory for generated files 78 | -t Provide target IP addresses and/or files in a comma-separated list (file.txt,1.1.1.1) 79 | Supports single IPs, ranges, or CIDR notation 80 | -x [file(s) and/or IPs] Provide target IP addresses and/or files to exclude in a comma-separated list (file.txt,1.1.1.1) 81 | Supports single IPs, ranges, or CIDR notation -- Omit argument to disable exclusion prompt 82 | -U Enable UDP scans (cannot be used with -u) 83 | -u Disable UDP scans (cannot be used with -U) 84 | -S [stage] If no stage provided, resume from saved stage (cannot be used with -s) 85 | If stage provided, skip to the specified stage 86 | Available stages: 87 | - discovery-hosts (TCP-only) 88 | - discovery-ports (TCP-only) 89 | - discovery-udp 90 | - discovery-lists 91 | - services-tcp 92 | - services-udp 93 | -s Start from the beginning (disables stage resuming but still saves stages for later resumption) 94 | 95 | AUXILIARY OPTIONS (Enable additional functionality): 96 | --defaults Run z0e with default settings (overridden by explicitly provided options) 97 | Default settings: 98 | - Stage (-S/-s): Starts at initial alives scan 99 | - Targets file (-t): ./targets.txt 100 | - Output directory (-o): ./z0e-output 101 | - Excluded targets (-x): None 102 | - UDP scans (-U/-u): Enabled 103 | --ngineer Enable entry of custom command options 104 | --only Run only UDP scans (if enabled) and/or specified stage (does not apply to other options) 105 | --count Count total IP addresses in the provided comma-separated file(s) and/or IPs (does not require sudo) 106 | --geniplist 107 | Generate a list of single IP addresses from the provided comma-separated file(s) and/or IPs 108 | (does not require sudo) 109 | --listwinhosts [OutputFile] 110 | Parse a standard Nmap file (.nmap) to list IP addresses of Windows hosts (does not require sudo) 111 | --parseports [OutputFileName] 112 | Parse a grepable Nmap file (.gnmap) for hosts with specified open ports 113 | and output results in a readable format (does not require sudo) 114 | --listiphostnames [OutputFile] 115 | Parse a standard Nmap file (.nmap) to list IP address and hostname pairs 116 | (does not require sudo) 117 | --ntfy [priority,] 118 | Enable ntfy notifications (priority 1-5 optional, followed by server/topic URL) 119 | --session Enable session functionality (provide a new or existing session name) 120 | To resume a session, provide the session name with the -S option 121 | --help Display this help message 122 | --version Display the version of Zero-E 123 | ```` 124 | 2. If required options aren't provided, Zero-E will prompt you for the missing option(s) 125 | 3. Embrace your inner script kiddie, sit back in your reclining ergonomic chair, and take a nap while z0e does your work for you 126 | 127 | ## Install to $PATH 128 | 1. Add `zero-e` to PATH, so it's able to be called as a command 129 | - Run the included `installz0e.sh`, which will add Zero-E to PATH for you; or use `-b` to specify a destination. 130 | - If you prefer doing this manually, here's how I set mine up: I set up an alias (`z0epath`) in my shell (`~/.zshrc`) that quickly copies z0e into the primary PATH directory (`/usr/local/bin`) as `zeroe` for quick updating when changes are made 131 | - `alias z0epath='sudo cp /path/to/zero-e /usr/local/bin/zeroe && sudo chmod +x /usr/local/bin/zeroe'` 132 | - It must be copied to _/usr/local/bin_ so it's runnable with _sudo_ 133 | - Whenever you pull updates, rerun `installz0e.sh` or your alias 134 | 2. Run Zero-E by calling it with `zeroe` if _installz0e.sh_ was used, or whatever you named it if set up manually, with or without options: `sudo zeroe [options]` 135 | 3. Embrace your inner script kiddie, sit back in your reclining ergonomic chair, and take a nap while z0e does your work for you 136 | 137 | ## Stage system 138 | - The stage function allows for resuming from the automatically saved stage, or from a specified stage 139 | - If resuming a stage, it resumes both masscan and Nmap scans from exactly where they left off 140 | - If creating a session with `--session`, each session will have its own saved stage 141 | ### Resuming from a saved stage 142 | 1. Option 1: Pass the `-S` option with no arguments 143 | - Also include `--session ` if the initial scan was in a session 144 | 2. Option 2: Run z0e without any options (or with `--session `) 145 | - At the prompt, enter `r` to resume 146 | ### Starting at a specified stage 147 | - Skipping to a specific stage will only work if doing so after running z0e up to that point, and specifying the previous output directory. Skipping will error if running z0e at that stage for the first time, as certain stages require files that won't yet exist. 148 | - z0e will automatically create backups if it detects important output files that will be overwritten when running subsequent stages. 149 | 150 | **Option 1:** 151 | 152 | - Pass the `-S` option with the desired stage name 153 | 154 | **Option 2:** 155 | 156 | - Run z0e without any options 157 | - At the prompt, enter the desired stage name 158 | 159 | **Stages and explanations:** 160 | 161 | - discovery-hosts 162 | - The start of the external and internal scan process. 163 | - External: runs an Nmap ping scan 164 | - Internal: runs masscan with variable (depending on the total number of initial targets) `--top-ports` to discover alive hosts 165 | - discovery-ports 166 | - External: runs masscan against all targets to discover alive hosts and open ports 167 | - Internal: runs masscan against all ports of alives only 168 | - discovery-udp 169 | - If UDP is enabled, runs Nmap against alives to discover open UDP ports 170 | - discovery-lists 171 | - Creates the alives list and open ports list 172 | - services-tcp 173 | - Runs an in-depth Nmap service scan against alive hosts and open TCP ports 174 | - services-udp 175 | - Runs an in-depth Nmap service scan against alive hosts and open UDP ports 176 | 177 | # Methodology 178 | ## External 179 | 1. Nmap alive host discovery 180 | - `sudo nmap -n -vv -sn -oG - --excludefile <$excludes_file> -iL <$targets_file>` 181 | 2. Masscan open port/alive host discovery (customizable with `--ngineer`) 182 | - `sudo masscan --open-only -p 1-65535 --rate=5000 --excludefile <$excludes_file> --include-file <$targets_file> -oG <$output_file>` 183 | 3. UDP alive host/open port scan, if enabled (customizable with `--ngineer`) 184 | - `sudo nmap -v -Pn -sU --open --min-rate 1000 --max-rate 3000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG <$output_file> --excludefile <$excludes_file> -iL <$targets_file> -d` 185 | - 15094 top ports is 99% effective. Reference [this chart](https://nmap.org/book/performance-port-selection.html) for --top-ports number effectiveness 186 | 4. Generates lists of alive hosts and open ports 187 | 5. Nmap TCP service scans (customizable with `--ngineer`) 188 | - `sudo nmap -sC -sV -Pn -O -p <$open_ports> --open --reason --excludefile <$excludes_file> -iL <$targets_file> -oA <$output_file>` 189 | 6. Nmap UDP service scans, if enabled (customizable with `--ngineer`) 190 | - `sudo nmap -v -sU -Pn -sV --open --min-rate 1000 --max-rate 3000 --reason -p <$open_ports> -oA <$output_file> --excludefile <$excludes_file> -iL <$targets_file>` 191 | 192 | ## Internal 193 | 1. Creates a firewall rule to prevent RST packets from interfering with scans 194 | - Linux: `sudo iptables -A INPUT -p tcp --dport 55555 -j DROP` 195 | - Mac: `cp "/etc/pf.conf" "$filepath/logs/pf.conf.bak-prescript" && block drop in proto tcp from any to any port 55555" | sudo tee -a /etc/pf.conf && sudo pfctl -f /etc/pf.conf` (untested) 196 | 2. Masscan alive host discovery 197 | - `sudo masscan --rate=8000 --src-port=55555 --top-ports --excludefile <$excludes_file> --include-file <$targets_file> -oG <$output_file>` 198 | - Detects total number of targets and adjusts `--top-ports` number accordingly to keep initial alives scan as quick as possible while remaining accurate 199 | 3. Masscan open ports discovery (customizable with `--ngineer`) 200 | - `sudo masscan --open-only -p 1-65535 --rate=8000 --src-port=55555 --excludefile <$excludes_file> --include-file <$targets_file> -oG` 201 | 4. UDP alive host/open port scan, if enabled (customizable with `--ngineer`) 202 | - `nmap -v -Pn -sU --open --min-rate 3000 --max-rate 5000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG <$output_file> --excludefile <$excludes_file> -iL <$targets_file>` 203 | - 15094 top ports is 99% effective. Reference [this chart](https://nmap.org/book/performance-port-selection.html) for `--top-ports` number effectiveness 204 | 5. Removes the firewall rule 205 | - Linux: `sudo iptables -D INPUT -p tcp --dport 55555 -j DROP` 206 | - Mac: `cp "/etc/pf.conf" && sudo sed -i "/block drop in proto tcp from any to any port 55555/d" /etc/pf.conf && sudo pfctl -f /etc/pf.conf` (untested) 207 | - If `pfctl` was originally disabled: `sudo pfctl -d` 208 | 5. Generates lists of alive hosts and open ports 209 | 6. Nmap TCP service scans (customizable with `--ngineer`) 210 | - `nmap -sC -sV -Pn -O -p <$open_ports> --open --reason -oA <$output_file> --excludefile <$excludes_file> -iL <$targets_file>` 211 | 7. Nmap UDP service scans, if enabled (customizable with `--ngineer`) 212 | - `nmap -v -sU -Pn -sV --open --min-rate 1000 --max-rate 3000 --reason -p <$open_ports> -oA <$output_file> --excludefile <$excludes_file> -iL <$targets_file>` 213 | 214 | # Planned improvements 215 | - Stuff I happen to think of 216 | - Docker-ization 217 | - Option to automate launching Nessus scans 218 | -------------------------------------------------------------------------------- /installz0e.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #Adds Zero-E to $PATH 3 | 4 | #Set default installation directory 5 | install_dir="/usr/local/bin" 6 | 7 | #cd to install script dir 8 | SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 9 | cd "$SCRIPT_DIR" 10 | 11 | #Parse command line options 12 | while getopts ":b:" opt; do 13 | case $opt in 14 | b) 15 | install_dir="$OPTARG" 16 | ;; 17 | \?) 18 | echo "Invalid option: -$OPTARG" >&2 19 | exit 1 20 | ;; 21 | esac 22 | done 23 | 24 | #Create the installation directory if it doesn't exist 25 | mkdir -p "$install_dir" 26 | 27 | #Copy Zero-E to the installation directory 28 | sudo cp ./zero-e "$install_dir/zeroe" && sudo chmod +x "$install_dir/zeroe" 29 | exitstatus=$? 30 | 31 | if [ $exitstatus -eq 0 ]; then 32 | echo -e "\e[32m [+] Zero-E copied to $install_dir/zeroe \e[0m" 33 | echo -e "\e[36m [-] Zero-E can now be invoked as a command with \e[32mzeroe\e[36m \e[0m" 34 | else 35 | echo -e "\e[31m [X] Error: Failed to install Zero-E \e[0m" 36 | fi 37 | -------------------------------------------------------------------------------- /sticker-ZeroE.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Inscyght/Zero-E/38919cc2e46d53040ba1378533b00206b91fa5e9/sticker-ZeroE.png -------------------------------------------------------------------------------- /zero-e: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | banner="Zero-E (z0e) by @Inscyght" 3 | version="v1.4.0.2" 4 | ###Functions 5 | function updatecheck { # Check version and update the script 6 | { 7 | URL="https://raw.githubusercontent.com/inscyght/zero-e/main/zero-e" 8 | TIMEOUT=1 # Set the maximum time (in seconds) for the curl request 9 | 10 | # Function to get the latest version string from the script on GitHub 11 | get_latest_version() { 12 | latest_version=$(curl -s --fail --max-time "$TIMEOUT" "$URL" | grep -m 1 '^version=' | awk -F'"' '{print $2}') 13 | if [ $? -ne 0 ] || [ -z "$latest_version" ]; then 14 | echo "Failed to fetch the latest version from GitHub" 15 | return 1 # Indicate failure 16 | fi 17 | echo "$latest_version" 18 | } 19 | # Function to update the script 20 | update_script() { 21 | # Ensure correct location and overwrite 22 | script_path="$(dirname "$0")/zero-e" 23 | curl -o "$script_path" --max-time "$TIMEOUT" "$URL" && chmod +x "$script_path" 24 | if [ $? -ne 0 ]; then 25 | echo "Failed to update the script -- check your internet connection or GitHub access" 26 | return 1 27 | fi 28 | 29 | # Check if zeroe is already in PATH; if so, reinstall to that location 30 | if command -v zeroe >/dev/null 2>&1; then 31 | zeroe_cmd_path="$(command -v zeroe)" 32 | echo "Detected Zero-E is already installed at $zeroe_cmd_path" 33 | echo "Reinstalling Zero-E to $zeroe_cmd_path" 34 | sudo cp "$script_path" "$zeroe_cmd_path" && sudo chmod +x "$zeroe_cmd_path" 35 | if [ $? -eq 0 ]; then 36 | echo "Zero-E reinstalled successfully." 37 | z0e_resinstall=true 38 | else 39 | echo "Failed to reinstall Zero-E." 40 | fi 41 | fi 42 | } 43 | 44 | latest_version=$(get_latest_version) 45 | # If the version fetch fails, skip the update check 46 | if [ $? -ne 0 ]; then 47 | #echo "Skipping update check due to connectivity issues." 48 | return 0 49 | fi 50 | # Proceed with the update check if the version fetch was successful 51 | if [ "$version" != "$latest_version" ]; then 52 | echo "A new version of Zero-E is available: $latest_version" 53 | read -p "Update to the latest version? : " response 54 | if [ "$response" == "y" ]; then 55 | echo "" 56 | echo "Updating..." 57 | update_script 58 | if [ $? -eq 0 ]; then 59 | echo "" 60 | echo "Zero-E updated to $latest_version" 61 | if [ "$z0e_resinstall" != true ]; then 62 | echo "If previously installed as a command, reinstall Zero-E to \$PATH" 63 | fi 64 | exit 0 65 | else 66 | echo "Update failed" 67 | fi 68 | else 69 | echo "Continuing with the local version" 70 | echo "" 71 | fi 72 | fi 73 | # Check if script is named 'zero-e.sh' and rename 74 | script_name=$(basename "$0") 75 | if [ "$script_name" = "zero-e.sh" ]; then 76 | echo "z0e's script file renamed to zero-e in v1.1" 77 | mv "$0" "$(dirname "$0")/zero-e" 78 | echo "Local zero-e.sh file renamed to 'zero-e' -- please re-run z0e" 79 | exit 0 80 | fi 81 | } >&2 82 | } 83 | 84 | function settype { #Set external or internal 85 | if [ "$e_opt" = true ] || [ "$i_opt" = true ]; then 86 | if [ "$e_opt" = true ]; then 87 | typevar="ext" 88 | i_opt=false 89 | elif [ "$i_opt" = true ]; then 90 | typevar="int" 91 | e_opt=false 92 | fi 93 | else 94 | echo "[?] Perform xternal or nternal scans:" 95 | while true; do 96 | read -e -p " [>] " type 97 | if [ "$type" = "E" ] || [ "$type" = "e" ] || [ "$type" = "external" ] || [ "$type" = "External" ] || [ "$type" = "Ext" ] || [ "$type" = "ext" ]; then 98 | typevar="ext" 99 | e_opt=true 100 | break 101 | elif [ "$type" = "I" ] || [ "$type" = "i" ] || [ "$type" = "internal" ] || [ "$type" = "Internal" ] || [ "$type" = "Int" ] || [ "$type" = "int" ]; then 102 | typevar="int" 103 | i_opt=true 104 | break 105 | else 106 | echo -e "\e[31m [X] Error: You must enter 'e' for external or 'i' for internal \e[0m" >&2 107 | fi 108 | done 109 | fi 110 | } 111 | 112 | function output { #sets the output dir 113 | if [ -z "$o_opt" ] && [ "$defaults" = true ]; then 114 | filepath="./z0e-output" 115 | elif [ -n "$o_opt" ]; then 116 | filepath="$o_opt" 117 | else 118 | echo "[?] Enter the output directory path:" 119 | while true; do 120 | read -e -p " [>] " filepath 121 | if [ -f "$filepath" ]; then 122 | echo -e "\e[31m [X] Error: File exists with the same name \e[0m" >&2 123 | elif [ -z "$filepath" ]; then 124 | echo -e "\e[31m [X] Error: You must enter a directory name or path \e[0m" >&2 125 | elif [[ "$filepath" == "-"* ]]; then 126 | echo -e "\e[31m [X] Error: Directory names starting with '-' may cause issues with commands \e[0m" >&2 127 | elif [[ "$filepath" == *" "* ]]; then 128 | echo -e "\e[31m [X] Error: To proactively avoid errors, whitespace is not allowed in file names \e[0m" >&2 129 | elif [[ "$filepath" == "/dev/null" ]]; then 130 | echo -e "\e[31m [X] Error: /dev/null cannot be used as an output directory \e[0m" >&2 131 | elif [ -n "$filepath" ]; then 132 | break 133 | fi 134 | done 135 | fi 136 | if [ "$filepath" = "." ]; then 137 | filepath=$(pwd) 138 | elif [ "$filepath" = "~" ]; then 139 | filepath="/home/$SUDO_USER" 140 | elif [[ "$filepath" = '~/'* ]]; then 141 | filepath=${filepath/#\~/"/home/$SUDO_USER"} 142 | if [ ! -d "$filepath" ];then 143 | mkdir -p "$filepath" 144 | fi 145 | else 146 | if [ ! -d "$filepath" ];then 147 | mkdir -p "$filepath" 148 | fi 149 | fi 150 | 151 | ###Create backup of output dir if scans have already ran 152 | local shouldBackup=false 153 | # Check for specific files in the $filepath and $filepath/reporting directories 154 | if [ "$typevar" = "ext" ] && [ "$(find $filepath -maxdepth 1 -type f \( -name 'ext-*' \) 2>/dev/null)" ]; then 155 | shouldBackup=true 156 | elif [ "$typevar" = "int" ] && [ "$(find $filepath -maxdepth 1 -type f \( -name 'int-*' \) 2>/dev/null)" ]; then 157 | shouldBackup=true 158 | fi 159 | # Proceed with backup if necessary 160 | if [ "$shouldBackup" = true ]; then 161 | # The directory contains specific files indicating significant script activity, 162 | # find the next available z0escan#.bak name for backup 163 | local baseDir=$(dirname "$filepath") 164 | local name=$(basename "$filepath") 165 | local prefix="${name}-z0e" 166 | local suffix=".bak" 167 | local idx=1 168 | while true; do 169 | if [ ! -d "$baseDir/$prefix$idx$suffix" ]; then 170 | break 171 | fi 172 | ((idx++)) 173 | done 174 | local newDir="$baseDir/$prefix$idx$suffix" 175 | echo " z0e results files detected in output directory -- backing up '$filepath' to '$newDir'" 176 | cp -r "$filepath" "$newDir" 177 | fi 178 | 179 | #Remove old files if they exist in the output dir and choosing fresh start 180 | if { [ "$stage" = "script-start" ] && [ "$resume" != "y" ]; } || \ 181 | { [ -f "$filepath/logs/misc-files/targets-file.z0e" ] && [ "$(realpath "$t_opt")" != "$(cat "$filepath/logs/misc-files/targets-file.z0e")" ]; }; then 182 | find "$filepath" -type f -name "${typevar}-*" -exec rm -f {} + 183 | fi 184 | 185 | mkdir -p $filepath/logs/ 186 | mkdir -p $filepath/logs/misc-files/ 187 | mkdir -p $filepath/analysis/ 188 | 189 | o_opt="$(realpath $filepath)" 190 | filepath="$(realpath $filepath)" 191 | } 192 | 193 | function targets { # Sets the targets file 194 | if [[ "$session_flag" = true && -n "$session" ]]; then 195 | session_dir="/var/lib/zeroe/sessions/$session" 196 | targets_file="$session_dir/targets-single-ips.z0e" 197 | else 198 | targets_file="/var/lib/zeroe/targets-single-ips.z0e" 199 | fi 200 | ################################################################ 201 | # 1) If t_opt is a single existing, non-empty file with no IP pattern, just use it 202 | ################################################################ 203 | if [ -n "$t_opt" ] && [ -f "$t_opt" ] && [ -s "$t_opt" ] \ 204 | && ! [[ "$t_opt" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 205 | && [[ "$t_opt" != *","* ]] 206 | then 207 | ips="$t_opt" 208 | checkfile="$ips" 209 | checkinvalidips 210 | ################################################################ 211 | # 2) Else if t_opt is a comma‐separated list (or includes IP pattern), 212 | # merge them (just like the -t option) 213 | ################################################################ 214 | elif [ -n "$t_opt" ] && ( 215 | { ! [ -f "$t_opt" ] && [[ "$t_opt" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; } \ 216 | || [[ "$t_opt" == *","* ]] 217 | ) 218 | then 219 | IFS=',' read -ra token_array <<< "$t_opt" 220 | # build 2 parallel files: 221 | # metadata_file => lines have a prefix for "FILE" or "INPUT" 222 | # raw_file => lines have no prefix (the actual IP or range) 223 | metadata_file=$(mktemp /tmp/targets_single_ips.metadata.XXXXXX) 224 | raw_file=$(mktemp /tmp/targets_single_ips.raw.XXXXXX) 225 | > "$metadata_file" 226 | > "$raw_file" 227 | 228 | anyerrors=false 229 | for token in "${token_array[@]}"; do 230 | trimmed_token="$(echo "$token" | xargs)" 231 | if [ -f "$trimmed_token" ]; then 232 | # If it's a file, ensure non-empty 233 | if [ ! -s "$trimmed_token" ]; then 234 | echo -e "\e[31m [X] Error: '$trimmed_token' is empty \e[0m" >&2 235 | anyerrors=true 236 | break 237 | fi 238 | # Merge its lines 239 | while IFS= read -r fileline; do 240 | echo "__SRC:FILE:${trimmed_token}__ $fileline" >> "$metadata_file" 241 | echo "$fileline" >> "$raw_file" 242 | done < "$trimmed_token" 243 | else 244 | # It's an IP or range 245 | echo "__SRC:INPUT__ $trimmed_token" >> "$metadata_file" 246 | echo "$trimmed_token" >> "$raw_file" 247 | fi 248 | done 249 | 250 | if $anyerrors; then 251 | exit 1 252 | fi 253 | 254 | # Now run checkinvalidips on the metadata file 255 | checkfile="$metadata_file" 256 | checkinvalidips 257 | 258 | # If validation passed, we set ips to the raw file 259 | # so the rest of the function sees no metadata lines 260 | ips="$raw_file" 261 | ################################################################ 262 | # 3) If the user didn't set t_opt or set an invalid one, 263 | # handle the defaults or prompt logic 264 | ################################################################ 265 | elif [[ -z "$t_opt" && "$defaults" = true ]]; then 266 | ips="targets.txt" 267 | if [[ ! -f "$ips" ]]; then 268 | echo -e "\e[31m [X] Error: The default targets file (./targets.txt) does not exist \e[0m" >&2 269 | exit 1 270 | elif [ ! -s "$ips" ]; then 271 | echo -e "\e[31m [X] Error: $ips is empty \e[0m" >&2 272 | exit 1 273 | fi 274 | checkfile="$ips" 275 | checkinvalidips 276 | elif [ -n "$t_opt" ]; then 277 | # t_opt is set but doesn't fit the above patterns 278 | if [[ "$t_opt" == "-"* ]]; then 279 | echo -e "\e[31m [X] Error: File names starting with '-' may cause issues with commands \e[0m" >&2 280 | exit 1 281 | elif [[ "$t_opt" == *" "* ]]; then 282 | echo -e "\e[31m [X] Error: To proactively avoid errors, whitespace is not allowed in file names \e[0m" >&2 283 | exit 1 284 | elif [ ! -s "$t_opt" ]; then 285 | echo -e "\e[31m [X] Error: $t_opt is empty or doesn't exist \e[0m" >&2 286 | exit 1 287 | fi 288 | ips="$t_opt" 289 | checkfile="$ips" 290 | checkinvalidips 291 | else 292 | ################################################################ 293 | # 4) Interactive prompt logic 294 | ################################################################ 295 | echo "[?] Enter the target IP addresses and/or filename(s): " 296 | while true; do 297 | read -e -p " [>] " user_input 298 | 299 | if [[ -z "$user_input" ]]; then 300 | echo -e "\e[31m [X] Error: You must enter the target IP addresses and/or filename(s) \e[0m" >&2 301 | continue 302 | fi 303 | 304 | if [ -f "$user_input" ] && [ -s "$user_input" ] \ 305 | && ! [[ "$user_input" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 306 | && [[ "$user_input" != *","* ]] 307 | then 308 | # Single existing file 309 | ips="$user_input" 310 | ips_log="$ips" 311 | checkfile="$ips" 312 | checkinvalidips 313 | if [[ "$allvalid" = 1 ]]; then 314 | t_opt="$ips" 315 | break 316 | fi 317 | 318 | elif { ! [ -f "$user_input" ] && [[ "$user_input" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; } \ 319 | || [[ "$user_input" == *","* ]] 320 | then 321 | ips_log="$user_input" 322 | # Merge tokens 323 | IFS=',' read -ra token_array <<< "$user_input" 324 | metadata_file=$(mktemp /tmp/targets_single_ips.metadata.XXXXXX) 325 | raw_file=$(mktemp /tmp/targets_single_ips.raw.XXXXXX) 326 | > "$metadata_file" 327 | > "$raw_file" 328 | anyerrors=false 329 | for token in "${token_array[@]}"; do 330 | trimmed_token="$(echo "$token" | xargs)" 331 | if [ -f "$trimmed_token" ]; then 332 | if [ ! -s "$trimmed_token" ]; then 333 | echo -e "\e[31m [X] Error: '$trimmed_token' is empty \e[0m" >&2 334 | anyerrors=true 335 | break 336 | fi 337 | while IFS= read -r fileline; do 338 | echo "__SRC:FILE:${trimmed_token}__ $fileline" >> "$metadata_file" 339 | echo "$fileline" >> "$raw_file" 340 | done < "$trimmed_token" 341 | else 342 | echo "__SRC:INPUT__ $trimmed_token" >> "$metadata_file" 343 | echo "$trimmed_token" >> "$raw_file" 344 | fi 345 | done 346 | if $anyerrors; then 347 | continue # Re-prompt 348 | fi 349 | checkfile="$metadata_file" 350 | checkinvalidips 351 | if [[ "$allvalid" = 1 ]]; then 352 | ips="$raw_file" 353 | t_opt="$ips" 354 | break 355 | fi 356 | else 357 | # Possibly a single file with IP pattern or a non-existent file 358 | if [[ ! -f "$user_input" ]]; then 359 | echo -e "\e[31m [X] Error: $user_input is not a file or a valid IP list \e[0m" >&2 360 | elif [ ! -s "$user_input" ]; then 361 | echo -e "\e[31m [X] Error: $user_input is empty \e[0m" >&2 362 | else 363 | # It's an existing file but might also look like an IP pattern or commas treat it as file anyway 364 | ips="$user_input" 365 | ips_log="$ips" 366 | checkfile="$ips" 367 | checkinvalidips 368 | if [[ "$allvalid" = 1 ]]; then 369 | t_opt="$ips" 370 | break 371 | fi 372 | fi 373 | fi 374 | done 375 | fi 376 | ################################################################ 377 | # 5) final block 378 | ################################################################ 379 | t_opt="$(realpath "$ips")" 380 | ips="$(realpath "$ips")" 381 | checkfile="$ips" 382 | geniplist "$ips" > "$targets_file" & 383 | GEN_PID=$! 384 | status_msg=" Generating temporary list of single IP addresses for targets..." 385 | statusgeniplist "$GEN_PID" 386 | wait "$GEN_PID" 387 | ips="$targets_file" 388 | t_opt="$targets_file" 389 | } 390 | 391 | function excludes { # Sets the excludes file 392 | # Global nullexcludes path 393 | mkdir -p /var/lib/zeroe 394 | touch /var/lib/zeroe/nullexcludes.z0e 395 | nullexcludes="/var/lib/zeroe/nullexcludes.z0e" 396 | 397 | # Session-based exclude file 398 | if [[ "$session_flag" = true && -n "$session" ]]; then 399 | session_dir="/var/lib/zeroe/sessions/$session" 400 | excludes_file="$session_dir/excludes-single-ips.z0e" 401 | else 402 | excludes_file="/var/lib/zeroe/excludes-single-ips.z0e" 403 | fi 404 | 405 | # 1) If x_opt is empty and defaults=true => use global nullexcludes 406 | if [[ -z "$x_opt" && "$defaults" = true ]]; then 407 | nostrikes="$nullexcludes" 408 | # 2) If x_opt is not empty, check if it's a single existing file or comma/IP list 409 | elif [ -n "$x_opt" ]; then 410 | # If it’s a single file, etc. => same logic as the updated -x option 411 | if [ -f "$x_opt" ] && [ -s "$x_opt" ] \ 412 | && ! [[ "$x_opt" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 413 | && [[ "$x_opt" != *","* ]] 414 | then 415 | nostrikes="$x_opt" 416 | checkfile="$nostrikes" 417 | checkinvalidips 418 | elif [[ ( ! -f "$nostrikes" && "$nostrikes" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ) \ 419 | || "$nostrikes" == *","* ]] 420 | then 421 | IFS=',' read -ra token_array <<< "$x_opt" 422 | metadata_file=$(mktemp /tmp/excludes_single_ips.metadata.XXXXXX) 423 | raw_file=$(mktemp /tmp/excludes_single_ips.raw.XXXXXX) 424 | anyerrors=false 425 | for token in "${token_array[@]}"; do 426 | trimmed_ip="$(echo "$token" | xargs)" 427 | if [ -f "$trimmed_ip" ]; then 428 | if [ ! -s "$trimmed_ip" ]; then 429 | echo -e "\e[31m [X] Error: '$trimmed_ip' is empty \e[0m" >&2 430 | anyerrors=true 431 | break 432 | fi 433 | while IFS= read -r line; do 434 | echo "__SRC:FILE:${trimmed_ip}__ $line" >> "$metadata_file" 435 | echo "$line" >> "$raw_file" 436 | done < "$trimmed_ip" 437 | else 438 | echo "__SRC:INPUT__ $trimmed_ip" >> "$metadata_file" 439 | echo "$trimmed_ip" >> "$raw_file" 440 | fi 441 | done 442 | if $anyerrors; then 443 | exit 1 444 | fi 445 | checkfile="$metadata_file" 446 | checkinvalidips 447 | if [[ "$allvalid" -eq 1 ]]; then 448 | nostrikes="$raw_file" 449 | else 450 | exit 1 451 | fi 452 | else 453 | # Otherwise error out or handle the final file 454 | if [[ "$x_opt" == "-"* ]]; then 455 | echo -e "\e[31m [X] Error: File names starting with '-' may cause issues \e[0m" >&2 456 | exit 1 457 | elif [[ "$x_opt" == *" "* ]]; then 458 | echo -e "\e[31m [X] Error: Whitespace not allowed in file names \e[0m" >&2 459 | exit 1 460 | elif [ ! -s "$x_opt" ] && [ "$x_opt" != "/var/lib/zeroe/nullexcludes.z0e" ]; then 461 | echo -e "\e[31m [X] Error: '$x_opt' is empty or doesn't exist \e[0m" >&2 462 | exit 1 463 | fi 464 | nostrikes="$x_opt" 465 | checkfile="$nostrikes" 466 | checkinvalidips 467 | fi 468 | # 3) If x_opt is still empty => prompt user 469 | else 470 | echo "[?] Enter the excluded IP addresses and/or filename(s) -- if none, press : " 471 | excludesprompt=true 472 | while true; do 473 | read -e -p " [>] " nostrikes 474 | if [[ -z "$nostrikes" ]]; then 475 | nostrikes="$nullexcludes" 476 | break 477 | elif [ -f "$nostrikes" ] && [ -s "$nostrikes" ] \ 478 | && ! [[ "$nostrikes" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 479 | && [[ "$nostrikes" != *","* ]] 480 | then 481 | # Single existing file 482 | exc_log="$nostrikes" 483 | checkfile="$nostrikes" 484 | checkinvalidips 485 | if [[ "$allvalid" = 1 ]]; then 486 | x_opt="$nostrikes" 487 | excludesprompt=false 488 | break 489 | fi 490 | elif [[ ( ! -f "$nostrikes" && "$nostrikes" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ) \ 491 | || "$nostrikes" == *","* ]] 492 | then 493 | exc_log="$nostrikes" 494 | # Comma or IP => two-file approach 495 | IFS=',' read -ra token_array <<< "$nostrikes" 496 | metadata_file=$(mktemp /tmp/excludes_single_ips.metadata.XXXXXX) 497 | raw_file=$(mktemp /tmp/excludes_single_ips.raw.XXXXXX) 498 | anyerrors=false 499 | for token in "${token_array[@]}"; do 500 | trimmed_ip="$(echo "$token" | xargs)" 501 | if [ -f "$trimmed_ip" ]; then 502 | if [ ! -s "$trimmed_ip" ]; then 503 | echo -e "\e[31m [X] Error: '$trimmed_ip' is empty \e[0m" >&2 504 | anyerrors=true 505 | break 506 | fi 507 | while IFS= read -r line; do 508 | echo "__SRC:FILE:${trimmed_ip}__ $line" >> "$metadata_file" 509 | echo "$line" >> "$raw_file" 510 | done < "$trimmed_ip" 511 | else 512 | echo "__SRC:INPUT__ $trimmed_ip" >> "$metadata_file" 513 | echo "$trimmed_ip" >> "$raw_file" 514 | fi 515 | done 516 | if $anyerrors; then 517 | continue 518 | fi 519 | checkfile="$metadata_file" 520 | checkinvalidips 521 | if [[ "$allvalid" = 1 ]]; then 522 | nostrikes="$raw_file" 523 | x_opt="$raw_file" 524 | excludesprompt=false 525 | break 526 | fi 527 | else 528 | # Possibly a single file or error out 529 | if [[ ! -f "$nostrikes" ]]; then 530 | echo -e "\e[31m [X] Error: You must pass an existing file(s) and/or comma-separated IP list \e[0m" >&2 531 | elif [ ! -s "$nostrikes" ]; then 532 | echo -e "\e[31m [X] Error: '$nostrikes' is empty \e[0m" >&2 533 | else 534 | # It's a file 535 | exc_log="$nostrikes" 536 | checkfile="$nostrikes" 537 | checkinvalidips 538 | if [[ "$allvalid" = 1 ]]; then 539 | x_opt="$nostrikes" 540 | excludesprompt=false 541 | break 542 | fi 543 | fi 544 | fi 545 | done 546 | fi 547 | # 4) Final block: if nostrikes != nullexcludes, we run geniplist 548 | if [[ "$nostrikes" != "$nullexcludes" ]]; then 549 | x_opt="$(realpath "$nostrikes")" 550 | nostrikes="$(realpath "$nostrikes")" 551 | checkfile="$nostrikes" 552 | geniplist "$nostrikes" > "$excludes_file" & 553 | GEN_PID=$! 554 | status_msg=" Generating temporary list of single IP addresses for excludes..." 555 | statusgeniplist "$GEN_PID" 556 | wait "$GEN_PID" 557 | nostrikes="$excludes_file" 558 | x_opt="$excludes_file" 559 | fi 560 | } 561 | 562 | function enableudp { #Enables UDP scans 563 | if [[ "$u_opt" = false && "$U_opt" = false && "$defaults" = true ]]; then 564 | udp="y" 565 | U_opt=true 566 | elif [ "$U_opt" = true ]; then 567 | udp="y" 568 | elif [ "$u_opt" = true ]; then 569 | udp="n" 570 | else 571 | echo "[?] nable or isable UDP scans:" 572 | while true; do 573 | read -e -p " [>] " udp 574 | if [ "$udp" = "e" ] || [ "$udp" = "E" ] || [ "$udp" = "enable" ] || [ "$udp" = "Enable" ] || [ "$udp" = "d" ] || [ "$udp" = "D" ] || [ "$udp" = "disable" ] || [ "$udp" = "Disable" ]; then 575 | break 576 | else 577 | echo -e "\e[31m [X] Error: You must enter 'e', 'enable', 'd', or 'disable' \e[0m" >&2 578 | fi 579 | done 580 | if [ "$udp" = "e" ] || [ "$udp" = "E" ] || [ "$udp" = "enable" ] || [ "$udp" = "Enable" ]; then 581 | udp="y" 582 | U_opt=true 583 | u_opt=false 584 | elif [ "$udp" = "d" ] || [ "$udp" = "D" ] || [ "$udp" = "disable" ] || [ "$udp" = "Disable" ]; then 585 | udp="n" 586 | U_opt=false 587 | u_opt=true 588 | fi 589 | fi 590 | 591 | if [[ "$u_opt" = true || "$udp" = "n" ]] && [[ "$S_opt" = *"udp" || "$stage" = *"udp" ]]; then #If UDP scans are disabled, but UDP stage is selected 592 | echo -e "\e[31m [X] Error: A UDP stage cannot be selected if UDP scans are disabled \e[0m" >&2 593 | exit 1 594 | fi 595 | 596 | if [[ "$only_flag" == true ]] && ([[ "$U_opt" == true ]] || [[ "$udp" == "y" ]]) && [[ "$S_opt" != *"udp"* && "$stage" != *"udp"* ]] && [[ "$stage" != "script-start" && "$stage" != "discovery-lists" ]]; then #If only UDP scans are enabled, but TCP stage is selected 597 | echo -e "\e[31m [X] Error: A TCP stage cannot be selected if only UDP scans are enabled \e[0m" >&2 598 | exit 1 599 | fi 600 | } 601 | 602 | function filtersusips { #filters out IPs that may be deception hosts 603 | #Use awk to count the occurrences of each IP and print those with <= 100 entries to the output file 604 | #[ -e "$susips" ] && rm "$susips" #2>>"$filepath/logs/$typevar-errors.log" 605 | #[ -e "$susoutput" ] && rm "$susoutput" #2>>"$filepath/logs/$typevar-errors.log" 606 | awk ' 607 | { 608 | ip[$4]++; 609 | } 610 | END { 611 | for (i in ip) { 612 | if (ip[i] > 100) { 613 | print i >> "'"$susips"'"; 614 | sus_ips[i] = 1; 615 | } 616 | } 617 | }' "$susinput" 2>>"$filepath/logs/$typevar-errors.log" 618 | #Check if sus_ips.txt exists before trying to read it 619 | if [ -f "$susips" ]; then 620 | #Then filter out the sus IPs from the input file 621 | awk 'FNR==NR {sus_ips[$0]=1; next} !($4 in sus_ips)' "$susips" "$susinput" >> "$susoutput" 2>>"$filepath/logs/$typevar-errors.log" 622 | else 623 | cat "$susinput" >> "$susoutput" 2>>"$filepath/logs/$typevar-errors.log" 624 | fi 625 | } 626 | 627 | function singleportstorange { #Converts individual sequential port numbers into a range 628 | awk ' 629 | BEGIN{start=end=""} 630 | { 631 | if(start == ""){ 632 | start=end=$1; 633 | } 634 | else if($1 == end+1){ 635 | end=$1; 636 | } 637 | else{ 638 | if(start == end) 639 | print start; 640 | else 641 | print start"-"end; 642 | start=end=$1; 643 | } 644 | } 645 | END{ 646 | if(start == end) 647 | print start; 648 | else 649 | print start"-"end; 650 | }' <(sort -n "$filepath/rangetemp.txt") >> $rangeout 651 | rm "$filepath/rangetemp.txt" 652 | #sed -i '/^[ \t]*$/d' "$checkfile" #cant remember why i had $checkfiles here. its only ever set to $ips and $nostrikes 653 | sed -i '/^[ \t]*$/d' "$rangeout" #Removes blank lines and lines that only contain spaces 654 | sort -u -o "$rangeout" "$rangeout" 655 | } 656 | 657 | function checkinvalidips { #Checks targets file for invalid entries 658 | dos2unix -q "$checkfile" 659 | sed -i '/^[ \t]*$/d' "$checkfile" # remove blank lines 660 | sed -i 's/^[[:space:]]*//; s/[[:space:]]*$//' "$checkfile" # remove leading/trailing whitespace 661 | 662 | #Regex pattern to match single IPv4 addresses, ranges, etc. 663 | ip_single='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' 664 | ip_range_full='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])-(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' 665 | ip_cidr='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\/([0-9]|[1-2][0-9]|3[0-2])$' 666 | ip_range_anyoct='^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)-(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.[0-9.]+$|^...LONGREGEX...' 667 | 668 | anyinvalid=0 669 | 670 | # Pattern to detect the source prefix: 671 | # __SRC:FILE:(somefile)__ actual_line 672 | # __SRC:INPUT__ actual_line 673 | prefix_regex='^__SRC:(FILE|INPUT)(:([^[:space:]]+))?__[[:space:]]+(.*)$' 674 | # Explanation: 675 | # ^__SRC:(FILE|INPUT) => "FILE" or "INPUT" 676 | # (:([^[:space:]]+))? => optionally captures the filename after a colon (no spaces) 677 | # __ => literal double underscore 678 | # [[:space:]]+ => space(s) after the prefix 679 | # (.*)$ => the rest is the actual IP/range line 680 | 681 | while IFS= read -r rawline || [[ -n "$rawline" ]]; do 682 | # 1) Parse out the source 683 | srcType="" 684 | fileSource="" 685 | line="$rawline" 686 | 687 | if [[ "$rawline" =~ $prefix_regex ]]; then 688 | srcType="${BASH_REMATCH[1]}" # "FILE" or "INPUT" 689 | fileSource="${BASH_REMATCH[3]}" # e.g. "test-ip.txt" 690 | line="${BASH_REMATCH[4]}" # the actual IP/range line 691 | else 692 | # If no prefix, fallback to older logic. Possibly from a single-file scenario. 693 | srcType="FILE" 694 | fileSource="$checkfile" 695 | # 'line' is already set to "$rawline" 696 | fi 697 | 698 | # 2) Decide whether we consider it "entered IPs" or "file lines" 699 | if [[ "$srcType" == "INPUT" ]]; then 700 | enteredips=true 701 | else 702 | enteredips=false 703 | fi 704 | 705 | # 3) If the file ended up empty after removing blank lines 706 | if [ ! -s "$checkfile" ]; then 707 | echo -e "\e[31m [X] Error: $checkfile is empty \e[0m" >&2 708 | fi 709 | 710 | # 4) Check the line against the IP patterns 711 | if [[ "$line" =~ $ip_single ]] || [[ "$line" =~ $ip_cidr ]] || [[ "$line" =~ $ip_range_full ]]; then 712 | # It's valid => do nothing 713 | : 714 | elif [[ "$line" =~ $ip_range_anyoct ]]; then 715 | # It's an improperly formatted range that masscan cannot parse 716 | if [[ "$enteredips" = true ]]; then 717 | echo -e "\e[31m [X] Error: $line is an improperly formatted range \e[0m" >&2 718 | echo -e "\e[31m IP address ranges must be in the format x.x.x.x-y.y.y.y \e[0m" >&2 719 | else 720 | echo -e "\e[31m [X] Error: $fileSource contains an improperly formatted range -- $line \e[0m" >&2 721 | echo -e "\e[31m IP address ranges must be in the format x.x.x.x-y.y.y.y \e[0m" >&2 722 | fi 723 | anyinvalid=1 724 | else 725 | # Not a recognized IP/range/CIDR 726 | if [[ "$enteredips" = true ]]; then 727 | echo -e "\e[31m [X] Error: $line is an invalid IP address \e[0m" >&2 728 | anyinvalid=1 729 | else 730 | echo -e "\e[31m [X] Error: $fileSource contains an invalid entry -- $line \e[0m" >&2 731 | anyinvalid=1 732 | fi 733 | fi 734 | done < "$checkfile" 735 | 736 | if [ "$anyinvalid" -ne 0 ]; then 737 | allvalid=0 738 | enteredips=false 739 | if [[ -z "$t_opt" && "$defaults" = true ]] || { [ -n "$t_opt" ] && [ "$excludesprompt" != true ]; } || [ -n "$x_opt" ] || [[ "$countopt" == "y" ]] || [[ "$geniplistopt" == true ]]; then 740 | exit 1 741 | fi 742 | else 743 | allvalid=1 744 | enteredips=false 745 | fi 746 | } 747 | 748 | function stageinit { # The checkpoint system 749 | mkdir -p /var/lib/zeroe 750 | 751 | function checkinvalidstage { # Defines the function to check for an invalid stage 752 | if [[ "$stage" = "script-start" || "$stage" = "discovery-ports" || "$stage" = "discovery-hosts" || "$stage" = "discovery-udp" || "$stage" = "discovery-lists" || "$stage" = "services-tcp" || "$stage" = "services-udp" ]]; then 753 | return 0 754 | else 755 | echo -e "\e[31m [X] Error: Invalid stage '$stage' -- check help or README for valid stages \e[0m" >&2 756 | return 1 757 | fi 758 | } 759 | 760 | # Determine the appropriate stage file location 761 | if [[ "$session_flag" = true && -n "$session" ]]; then 762 | stage_file="/var/lib/zeroe/sessions/$session/stage.z0e" 763 | initdir_file="/var/lib/zeroe/sessions/$session/initdir.z0e" 764 | else 765 | stage_file="/var/lib/zeroe/stage.z0e" 766 | initdir_file="/var/lib/zeroe/initdir.z0e" 767 | fi 768 | 769 | if [[ "$s_opt" = false && "$S_opt" = "disabled" && "$defaults" = true ]]; then # If only using --defaults option 770 | stage="script-start" 771 | echo "$(pwd)" > "$initdir_file" 772 | stage_cont=true 773 | only_flag=false 774 | elif [[ "$s_opt" = true ]]; then # If stage disabled 775 | stage="script-start" 776 | echo "$(pwd)" > "$initdir_file" 777 | stage_cont=true 778 | elif [[ ! -f "$stage_file" && "$S_opt" = "disabled" ]] || [[ -f "$stage_file" && "$(cat "$stage_file")" = "script-start" ]]; then # If stage file does not exist and stage option is disabled OR saved stage is script-start 779 | echo "[?] Start a ew scan, or enter a specific stage:" 780 | while true; do 781 | read -e -p " [>] " stage 782 | if [[ "$stage" = "n" || "$stage" = "new" ]]; then 783 | stage="script-start" 784 | echo "$(pwd)" > "$initdir_file" 785 | stage_cont=true 786 | break 787 | else 788 | echo "$(pwd)" > "$initdir_file" 789 | if checkinvalidstage; then 790 | if [[ "$only_flag" = false ]]; then 791 | stage_cont=true 792 | fi 793 | break 794 | fi 795 | fi 796 | done 797 | elif [[ -f "$stage_file" ]] && [[ -z "$S_opt" ]]; then # Load stage if it exists and resume stage option is enabled 798 | stage=$(cat "$stage_file") 799 | resume="y" 800 | elif [[ ! -f "$stage_file" ]] && [[ -z "$S_opt" ]]; then # If stage file does not exist and resume option is enabled 801 | echo -e "\e[31m [X] Error: No saved stage exists \e[0m" >&2 802 | echo "[?] Start a ew scan, or enter a specific stage:" 803 | while true; do 804 | read -e -p " [>] " stage 805 | if [[ "$stage" = "n" || "$stage" = "new" ]]; then 806 | stage="script-start" 807 | echo "$(pwd)" > "$initdir_file" 808 | stage_cont=true 809 | break 810 | else 811 | echo "$(pwd)" > "$initdir_file" 812 | if checkinvalidstage; then 813 | if [[ "$only_flag" != true ]]; then 814 | stage_cont=true 815 | fi 816 | break 817 | fi 818 | fi 819 | done 820 | elif [[ -f "$stage_file" && "$S_opt" = "disabled" && "$(cat "$stage_file")" != "script-start" ]]; then # If stage file exists, stage option is disabled, and saved stage is not script-start 821 | echo "[?] esume from '$(cat "$stage_file")', start a ew scan, or specify stage:" 822 | while true; do 823 | read -e -p " [>] " stage 824 | if [[ "$stage" != "script-start" && "$stage" != "discovery-ports" && "$stage" != "discovery-hosts" && "$stage" != "discovery-udp" && "$stage" != "discovery-lists" && "$stage" != "services-tcp" && "$stage" != "services-udp" && "$stage" != "R" && "$stage" != "r" && "$stage" != "Resume" && "$stage" != "resume" && "$stage" != "n" && "$stage" != "new" ]]; then 825 | echo -e "\e[31m [X] Error: Invalid stage '$stage' -- check help or README for valid stages \e[0m" >&2 826 | elif [[ "$stage" = "n" || "$stage" = "new" ]]; then 827 | stage="script-start" 828 | echo "$(pwd)" > "$initdir_file" 829 | stage_cont=true 830 | break 831 | elif [[ "$stage" = "R" || "$stage" = "r" || "$stage" = "Resume" || "$stage" = "resume" ]]; then 832 | resume="y" 833 | stage=$(cat "$stage_file") 834 | break 835 | else # Specifying a stage 836 | if checkinvalidstage; then 837 | echo "$(pwd)" > "$initdir_file" 838 | if [[ "$only_flag" == true && "$stage" == "discovery-udp" ]]; then 839 | stage_cont=false 840 | elif [[ "$only_flag" != true ]]; then 841 | stage_cont=true 842 | fi 843 | break 844 | fi 845 | fi 846 | done 847 | elif [[ -n "$S_opt" ]]; then # Start at the specified stage 848 | if [[ "$S_opt" = "script-start" || "$S_opt" = "discovery-ports" || "$S_opt" = "discovery-hosts" || "$S_opt" = "discovery-udp" || "$S_opt" = "discovery-lists" || "$S_opt" = "services-tcp" || "$S_opt" = "services-udp" ]]; then 849 | stage="$S_opt" 850 | echo "$(pwd)" > "$initdir_file" 851 | if [[ "$only_flag" == true && "$S_opt" == "discovery-udp" ]]; then 852 | stage_cont=false 853 | elif [[ "$only_flag" != true ]]; then 854 | stage_cont=true 855 | fi 856 | else 857 | echo -e "\e[31m [X] Error: Invalid stage '$S_opt' -- check help or README for valid stages \e[0m" >&2 858 | exit 1 859 | fi 860 | fi 861 | } 862 | 863 | function stagefilescheck { #Checks if required files are present for the specified stage 864 | local missing_files=() 865 | 866 | function checkstagefiles { 867 | for file in "$@"; do 868 | if [ ! -f "$file" ]; then 869 | missing_files+=("$file") 870 | fi 871 | done 872 | } 873 | #Defines required files for each stage 874 | if [[ "$e_opt" = true || "$type" = "E" || "$type" = "e" || "$type" = "external" || "$type" = "External" || "$type" = "Ext" || "$type" = "ext" ]]; then 875 | case "$stage" in 876 | "discovery-hosts") 877 | checkstagefiles "$nostrikes" "$ips" 878 | ;; 879 | "discovery-ports") 880 | checkstagefiles "$nostrikes" "$ips" 881 | ;; 882 | "discovery-udp") 883 | checkstagefiles "$nostrikes" "$ips" 884 | ;; 885 | "discovery-lists") 886 | # Check for $filepath/$typevar-alives.txt 887 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoresults.txt" 888 | if ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 889 | # Check for $filepath/$typevar-openPorts.txt and $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt 890 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt" 891 | fi 892 | if [[ "$udp" = "y" || "$udp" = "yes" || "$U_opt" = true ]]; then 893 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 894 | # Check for $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt and $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt 895 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt" 896 | else 897 | # Check for $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap and $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt 898 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" 899 | fi 900 | fi 901 | ;; 902 | "services-tcp") 903 | checkstagefiles "$nostrikes" "$filepath/$typevar-alives.txt" "$filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt" 904 | ;; 905 | "services-udp") 906 | checkstagefiles "$nostrikes" "$filepath/$typevar-alives.txt" "$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt" 907 | ;; 908 | esac 909 | elif [[ "$i_opt" = true || "$type" = "I" || "$type" = "i" || "$type" = "internal" || "$type" = "Internal" || "$type" = "Int" || "$type" = "int" ]]; then 910 | case "$stage" in 911 | "discovery-hosts") 912 | checkstagefiles "$nostrikes" "$ips" 913 | ;; 914 | "discovery-ports") 915 | checkstagefiles "$nostrikes" "$filepath/$typevar-alives.txt" 916 | ;; 917 | "discovery-udp") 918 | if [ -f "$filepath/logs/misc-files/$typevar-discoresults.txt" ]; then 919 | checkstagefiles "$nostrikes" "$filepath/logs/misc-files/$typevar-discoresults.txt" 920 | else 921 | checkstagefiles "$nostrikes" "$ips" 922 | fi 923 | ;; 924 | "discovery-lists") 925 | # Check for $filepath/$typevar-alives.txt 926 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoresults.txt" 927 | if ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 928 | # Check for $filepath/$typevar-openPorts.txt and $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt 929 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt" 930 | fi 931 | if [[ "$udp" = "y" || "$udp" = "yes" || "$U_opt" = true ]]; then 932 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 933 | # Check for $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt and $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt 934 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt" 935 | else 936 | # Check for $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap and $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt 937 | checkstagefiles "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" 938 | fi 939 | fi 940 | ;; 941 | "services-tcp") 942 | checkstagefiles "$nostrikes" "$filepath/$typevar-alives.txt" "$filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt" 943 | ;; 944 | "services-udp") 945 | checkstagefiles "$nostrikes" "$filepath/$typevar-alives.txt" "$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt" 946 | ;; 947 | esac 948 | fi 949 | if [ ${#missing_files[@]} -ne 0 ]; then 950 | if [[ -f /var/lib/zeroe/stage.z0e ]]; then 951 | echo -e "\e[31m [X] Error: Required z0e files for $stage stage do not exist" >&2 952 | echo -e " Resume z0e from the saved $(cat /var/lib/zeroe/stage.z0e) stage, run preceding stage, or restart z0e \e[0m" >&2 953 | exit 1 954 | else 955 | echo -e "\e[31m [X] Error: Required z0e files for $stage stage do not exist" >&2 956 | echo -e " Correct output directory if repeating stage, run preceding stage, or restart z0e \e[0m" >&2 957 | exit 1 958 | fi 959 | fi 960 | } 961 | 962 | function nessusports { #Formats open ports as Nessus-compatible for easy copy/pasting 963 | rm $filepath/$typevar-portsForNessus.txt #Prevents duplicate entries 964 | #Outputs TCP ports in Nessus-compatible format (T:#,) 965 | awk '{printf "T:%s,", $0}' $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt | sed 's/,$/\n/' >> $filepath/$typevar-portsForNessus.txt 966 | if grep -q "\S" "$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt"; then 967 | #Outputs UDP ports in Nessus-compatible format (U:#,) 968 | awk '{printf "U:%s,", $0}' $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | sed 's/,$/\n/' >> $filepath/$typevar-portsForNessus.txt 969 | #Joins TCP and UDP ports into single line 970 | sed -i -z 's/\n/,/' $filepath/$typevar-portsForNessus.txt 971 | fi 972 | } 973 | 974 | function genwindowshostlist { 975 | local inputfile="$1" 976 | local outputfile="$2" 977 | 978 | # Check if inputfile is provided and exists 979 | if [[ -z "$inputfile" ]]; then 980 | echo -e "\e[31m [X] Error: Correct syntax: --listwinhosts [OutputFile]" >&2 981 | return 1 982 | elif [[ ! -f "$inputfile" ]]; then 983 | echo -e "\e[31m [X] Error: File '$inputfile' does not exist \e[0m" >&2 984 | return 1 985 | fi 986 | 987 | # awk command to parse Windows hosts 988 | if [[ -n "$outputfile" ]]; then 989 | awk ' 990 | /Nmap scan report for/ { 991 | line = $0 992 | ip = "Unknown IP" 993 | 994 | # Check if line contains "(" 995 | if (index(line, "(") > 0) { 996 | # IP is between "(" and ")" 997 | start = index(line, "(") + 1 998 | end = index(line, ")") - 1 999 | ip = substr(line, start, end - start + 1) 1000 | } else { 1001 | # IP is after "Nmap scan report for " 1002 | prefix = "Nmap scan report for " 1003 | start = index(line, prefix) 1004 | if (start > 0) { 1005 | ip = substr(line, start + length(prefix)) 1006 | } 1007 | } 1008 | } 1009 | /OS:.*[Ww]indows/ { 1010 | print ip 1011 | } 1012 | ' "$inputfile" | sort -u > "$outputfile" 1013 | else 1014 | awk ' 1015 | /Nmap scan report for/ { 1016 | line = $0 1017 | ip = "Unknown IP" 1018 | 1019 | # Check if line contains "(" 1020 | if (index(line, "(") > 0) { 1021 | # IP is between "(" and ")" 1022 | start = index(line, "(") + 1 1023 | end = index(line, ")") - 1 1024 | ip = substr(line, start, end - start + 1) 1025 | } else { 1026 | # IP is after "Nmap scan report for " 1027 | prefix = "Nmap scan report for " 1028 | start = index(line, prefix) 1029 | if (start > 0) { 1030 | ip = substr(line, start + length(prefix)) 1031 | } 1032 | } 1033 | } 1034 | /OS:.*[Ww]indows/ { 1035 | print ip 1036 | } 1037 | ' "$inputfile" | sort -u 1038 | fi 1039 | } 1040 | 1041 | function genwindowshostlist_inscript { #For genwinhostlist when ran inside the script 1042 | if [[ "$stage" == "services-tcp" ]]; then 1043 | resultsfile="$filepath/$typevar-tcp-servicescan-results.nmap" 1044 | outputfile="$filepath/logs/misc-files/$typevar-windowshoststcp.txt" 1045 | genwindowshostlist "$resultsfile" >> "$outputfile" 1046 | elif [[ "$stage" == "services-udp" ]] && [[ "$udp" == "y" || "$udp" == "yes" || "$U_opt" == true ]]; then 1047 | resultsfile="$filepath/$typevar-udp-servicescan-results.nmap" 1048 | outputfile="$filepath/logs/misc-files/$typevar-windowshostsudp.txt" 1049 | genwindowshostlist "$resultsfile" >> "$outputfile" 1050 | fi 1051 | 1052 | # Combine and sort the outputs 1053 | cat "$filepath/logs/misc-files/$typevar-windowshoststcp.txt" "$filepath/logs/misc-files/$typevar-windowshostsudp.txt" 2>/dev/null | sort -u > "$filepath/analysis/$typevar-windowsHosts.txt" 1054 | 1055 | # Remove the output file if it's empty 1056 | if [[ ! -s "$filepath/analysis/$typevar-windowsHosts.txt" ]]; then 1057 | rm "$filepath/analysis/$typevar-windowsHosts.txt" 1058 | fi 1059 | } 1060 | 1061 | function parsegnmap { 1062 | local gnmapfile="$1" 1063 | local portsofinterest="$2" 1064 | local outputfilename="$3" 1065 | 1066 | # Error handling 1067 | if [[ -z "$gnmapfile" || -z "$portsofinterest" ]]; then 1068 | echo -e "\e[31m [X] Error: Correct syntax: --parseports [OutputFileName] \e[0m" >&2 1069 | return 1 1070 | elif [[ ! -f "$gnmapfile" ]]; then 1071 | echo -e "\e[31m [X] Error: File '$gnmapfile' does not exist \e[0m" >&2 1072 | return 1 1073 | fi 1074 | 1075 | # Parsing function for AWK 1076 | parse_with_awk() { 1077 | grep '^Host:' "$gnmapfile" | \ 1078 | sed -n 's/^Host: \([^ ]*\).*Ports: \(.*\)/\1\t\2/p' | \ 1079 | awk -F'\t' -v ports="$portsofinterest" ' 1080 | BEGIN { 1081 | IGNORECASE = 1 1082 | split(ports, portlist, ",") 1083 | for (i in portlist) portmap[portlist[i]] = 1 1084 | } 1085 | { 1086 | ip = $1 1087 | ports_field = $2 1088 | 1089 | ports_list = "" 1090 | services_list = "" 1091 | 1092 | n = split(ports_field, port_entries, ", ") 1093 | for (j = 1; j <= n; j++) { 1094 | port_entry = port_entries[j] 1095 | split(port_entry, port_parts, "/") 1096 | portnum = port_parts[1] 1097 | state = port_parts[2] 1098 | service = port_parts[5] 1099 | if (state == "open" && portmap[portnum]) { 1100 | if (ports_list == "") { 1101 | ports_list = portnum 1102 | services_list = service 1103 | } else { 1104 | ports_list = ports_list "," portnum 1105 | services_list = services_list "," service 1106 | } 1107 | } 1108 | } 1109 | if (ports_list != "") { 1110 | # Print data with fixed-width columns for alignment 1111 | printf "%-15s %-25s %-30s\n", ip, ports_list, services_list 1112 | } 1113 | }' 1114 | } 1115 | 1116 | if [[ "$parsegnmap_inscript" == true ]]; then 1117 | # Automated process: Output only to file, no terminal output 1118 | temp_data_output=$(mktemp) 1119 | 1120 | # Parse the gnmap file and store data in temporary file 1121 | parse_with_awk > "$temp_data_output" 1122 | 1123 | # Check if the temporary data output file is empty 1124 | if [[ -s "$temp_data_output" ]]; then 1125 | # There is data, write instructions, header, and data to the output file 1126 | { 1127 | # Print the header first 1128 | echo 'To parse the contents for a specific port or service and output the results in IP Address:Port/Service format, use the following command (replace with your value):' 1129 | echo '' 1130 | echo "awk -F' ' -v t=\"PORT/SERVICE\" 'BEGIN{IGNORECASE=1} \$2\",\"\$3 ~ \"(^|,)\" t \"(,|$)\" {print \$1\":\"t}'" "$outputfilename" 1131 | echo '' 1132 | # Append the parsed data 1133 | cat "$temp_data_output" 1134 | } > "$outputfilename" 1135 | else 1136 | # No data, remove the output file if it exists 1137 | rm -f "$outputfilename" 1138 | fi 1139 | 1140 | # Clean up the temporary file 1141 | rm -f "$temp_data_output" 1142 | elif [[ -z "$outputfilename" ]]; then 1143 | # If no output filename is given, output to the terminal with instructions 1144 | echo 'To parse the output for a specific port or service and output the results in IP Address:Port/Service format, use the following command (replace with your value):' 1145 | echo '' 1146 | echo 'awk -F'\'' '\'' -v t="PORT/SERVICE" '\''BEGIN{IGNORECASE=1} $2","$3 ~ "(^|,)" t "(,|$)" {print $1":"t}'\'' resultsFilename' 1147 | echo '' 1148 | parse_with_awk 1149 | elif [[ -n "$outputfilename" ]]; then 1150 | # Output instructions to the terminal and data to the file 1151 | { 1152 | echo 'To parse the output file for a specific port or service and output the results in IP Address:Port/Service format, use the following command (replace with your value):' 1153 | echo '' 1154 | echo 'awk -F'\'' '\'' -v t="PORT/SERVICE" '\''BEGIN{IGNORECASE=1} $2","$3 ~ "(^|,)" t "(,|$)" {print $1":"t}'\'' '"$outputfilename" 1155 | echo '' 1156 | } 1157 | 1158 | # Append the parsed data to the output file 1159 | parse_with_awk > "$outputfilename" 1160 | fi 1161 | } 1162 | 1163 | function listiptohostname { #Lists IP addresses and their corresponding hostnames 1164 | local inputfile="$1" 1165 | local outputfile="$2" 1166 | 1167 | # Check if inputfile is provided and exists 1168 | if [[ -z "$inputfile" ]]; then 1169 | echo -e "\e[31m [X] Error: Correct syntax: --listiphostname [OutputFile]" >&2 1170 | return 1 1171 | elif [[ ! -f "$inputfile" ]]; then 1172 | echo -e "\e[31m [X] Error: File '$inputfile' does not exist \e[0m" >&2 1173 | return 1 1174 | fi 1175 | 1176 | if [[ -n "$outputfile" ]]; then 1177 | awk ' 1178 | /^Nmap scan report for/ { 1179 | line = $0 1180 | hostname = "Unknown" 1181 | ip = "" 1182 | 1183 | prefix = "Nmap scan report for " 1184 | prefix_length = length(prefix) 1185 | # Extract the rest of the line after the prefix 1186 | rest = substr(line, prefix_length + 1) 1187 | 1188 | # Check if rest contains "(" indicating a hostname and IP 1189 | paren_pos = index(rest, "(") 1190 | if (paren_pos > 0) { 1191 | hostname = substr(rest, 1, paren_pos - 1) 1192 | # Remove any trailing spaces from hostname 1193 | sub(/[[:space:]]+$/, "", hostname) 1194 | ip_start = paren_pos + 1 1195 | ip_end = index(rest, ")") - 1 1196 | ip = substr(rest, ip_start, ip_end - paren_pos) 1197 | } else { 1198 | # No hostname, only IP is present 1199 | ip = rest 1200 | # Remove any leading/trailing spaces from IP 1201 | gsub(/^[[:space:]]+|[[:space:]]+$/, "", ip) 1202 | } 1203 | 1204 | if (ip != "") { 1205 | print ip, hostname 1206 | } 1207 | } 1208 | ' "$inputfile" > "$outputfile" 1209 | else 1210 | awk ' 1211 | /^Nmap scan report for/ { 1212 | line = $0 1213 | hostname = "Unknown" 1214 | ip = "" 1215 | 1216 | prefix = "Nmap scan report for " 1217 | prefix_length = length(prefix) 1218 | # Extract the rest of the line after the prefix 1219 | rest = substr(line, prefix_length + 1) 1220 | 1221 | # Check if rest contains "(" indicating a hostname and IP 1222 | paren_pos = index(rest, "(") 1223 | if (paren_pos > 0) { 1224 | hostname = substr(rest, 1, paren_pos - 1) 1225 | # Remove any trailing spaces from hostname 1226 | sub(/[[:space:]]+$/, "", hostname) 1227 | ip_start = paren_pos + 1 1228 | ip_end = index(rest, ")") - 1 1229 | ip = substr(rest, ip_start, ip_end - paren_pos) 1230 | } else { 1231 | # No hostname, only IP is present 1232 | ip = rest 1233 | # Remove any leading/trailing spaces from IP 1234 | gsub(/^[[:space:]]+|[[:space:]]+$/, "", ip) 1235 | } 1236 | 1237 | if (ip != "") { 1238 | print ip, hostname 1239 | } 1240 | } 1241 | ' "$inputfile" 1242 | fi 1243 | } 1244 | 1245 | function listiptohostname_inscript { #For genwinhostlist when ran inside the script 1246 | if [[ "$stage" == "services-tcp" ]]; then 1247 | resultsfile="$filepath/$typevar-tcp-servicescan-results.nmap" 1248 | outputfile="$filepath/logs/misc-files/$typevar-iphostnamestcp.txt" 1249 | listiptohostname "$resultsfile" >> "$outputfile" 1250 | elif [[ "$stage" == "services-udp" ]] && [[ "$udp" == "y" || "$udp" == "yes" || "$U_opt" == true ]]; then 1251 | resultsfile="$filepath/$typevar-udp-servicescan-results.nmap" 1252 | outputfile="$filepath/logs/misc-files/$typevar-iphostnamesudp.txt" 1253 | listiptohostname "$resultsfile" >> "$outputfile" 1254 | fi 1255 | 1256 | # Combine and sort the outputs 1257 | cat "$filepath/logs/misc-files/$typevar-iphostnamestcp.txt" "$filepath/logs/misc-files/$typevar-iphostnamesudp.txt" 2>/dev/null | sort -u > "$filepath/analysis/$typevar-ipHostnames.txt" 1258 | 1259 | # Remove the output file if it's empty 1260 | if [[ ! -s "$filepath/analysis/$typevar-ipHostnames.txt" ]]; then 1261 | rm "$filepath/analysis/$typevar-ipHostnames.txt" 1262 | fi 1263 | } 1264 | 1265 | function statusnmap { 1266 | indicator="^-_-^" 1267 | # Start a background process to cat the last line of the file every 15 minutes 1268 | ( 1269 | while kill -0 $pid >/dev/null 2>&1; do 1270 | sleep 900 # 15 minutes 1271 | printf "\r\e[K" # Clear continuous status line 1272 | echo "" 1273 | echo "" 1274 | tail -n 5 "$periodicfile" 1275 | echo "" 1276 | done 1277 | ) & 1278 | status_pid=$! #Capture the PID of the status process 1279 | 1280 | while kill -0 $pid >/dev/null 2>&1; do 1281 | i=$(( (i+1) % ${#indicator} )) 1282 | # "\r" returns to the beginning of the line, "\e[K" clears from the cursor to the end. 1283 | printf "\r\e[K%s %s" "${indicator:$i:1}" "$contstatus" 1284 | sleep 0.2 1285 | done 1286 | printf "\r\e[K" #Clears status indicator line 1287 | 1288 | kill $status_pid #Kill the status process when done 1289 | } 1290 | 1291 | function statusmasscan { 1292 | indicator="^-_-^" 1293 | # Start a background process to cat the last line of the file every 15 minutes 1294 | ( 1295 | while kill -0 $pid >/dev/null 2>&1; do 1296 | sleep 900 # 15 minutes 1297 | printf "\r\e[K" 1298 | echo "" 1299 | tail -n 5 "$periodicfile" 1300 | echo "" 1301 | done 1302 | ) & 1303 | status_pid=$! # Capture the PID of the background status process 1304 | 1305 | while kill -0 $pid >/dev/null 2>&1; do 1306 | i=$(( (i+1) % ${#indicator} )) 1307 | printf "\r\e[K%s %s" "${indicator:$i:1}" "$contstatus" 1308 | sleep 0.2 1309 | done 1310 | 1311 | printf "\r\e[K" #Clears status indicator line 1312 | 1313 | kill $status_pid 2>/dev/null #Kill the status process when done 1314 | } 1315 | 1316 | function statusgeniplist { 1317 | if [ "$use_stderr" = true ]; then 1318 | status_target="/dev/stderr" 1319 | else 1320 | status_target="/dev/stdout" 1321 | fi 1322 | local pid=$1 1323 | local delay=0.2 1324 | local chars='.oO0@' 1325 | while kill -0 "$pid" 2>/dev/null; do 1326 | for (( i=0; i<${#chars}; i++ )); do 1327 | # \r returns to the beginning of the line 1328 | printf "\r%s %s" "$status_msg" "${chars:$i:1}" > "$status_target" 1329 | sleep "$delay" 1330 | done 1331 | done 1332 | # Print the final message once done 1333 | #if [ "$geniplistopt" != true ] && [ "$countopt" != true ]; then 1334 | printf "\r%s Done\n" "$status_msg" > "$status_target" 1335 | use_stderr=false 1336 | #fi 1337 | } 1338 | 1339 | function errorcheck { #Checks for errors in command output 1340 | errorlog="/logs/$typevar-errors.log" 1341 | 1342 | if [ "$exitstatus" -ne 0 ]; then 1343 | echo "--------" >> "$errorlog" 1344 | echo "COMMAND: $checked_cmd" >> "$errorlog" 1345 | echo "===================================" >> "$errorlog" 1346 | echo '' >> "$errorlog" 1347 | echo -e "\e[31m [X] Error occured -- check logs in $errorlog \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" >&2 1348 | fi 1349 | } 1350 | 1351 | function whenkilled { 1352 | #Kill the periodic status process if it's running 1353 | if [[ -n $status_pid ]] && kill -0 $status_pid >/dev/null 2>&1; then 1354 | kill $status_pid 1355 | fi 1356 | #Display exiting status 1357 | if [[ "$firewallset" = true ]]; then 1358 | removefirewallrule 1359 | fi 1360 | printf "\r\e[K" 1361 | status_msg="[!] Zero-E stopped -- saving progress..." 1362 | echo -e "\n\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1363 | ntfy "$status_msg" 1364 | sleep 3 1365 | sed -i -e 's/\x1b\[[0-9;]*m//g' -e 's/\x1b\[A//g' -e 's/\x1b\[K//g' "$filepath/logs/$typevar-timestamps.log" #cleans up timestamps log 1366 | sed -i '/nocapture = servername/d' paused.conf 2>/dev/null #Removes line that breaks masscan --resume function 1367 | sed -i 's/adapter-port = [0-9]*-[0-9]*/adapter-port = 40000-41023/' paused.conf 2>/dev/null #Fixes another line that breaks masscan --resume function... cmon masscan, do better 1368 | #Remove the trap to prevent it from repeating 1369 | trap - INT TERM 1370 | exit 1 1371 | } 1372 | 1373 | function z0ecleanup { 1374 | sed -i 's/\x1b\[[0-9;?]*[A-Za-z]//g' "$filepath/logs/$typevar-timestamps.log" # Cleans up timestamps log 1375 | 1376 | if [[ "$session_flag" = true && -n "$session" ]]; then 1377 | # Remove only session-specific files 1378 | rm -rf "/var/lib/zeroe/sessions/$session" 2>/dev/null 1379 | else 1380 | # No active session; remove only global files, keep session data 1381 | rm -rf /var/lib/zeroe/stage.z0e /var/lib/zeroe/vars.z0e /var/lib/zeroe/initdir.z0e \ 1382 | /var/lib/zeroe/excludes-single-ips.z0e /var/lib/zeroe/targets-single-ips.z0e 2>/dev/null 1383 | fi 1384 | 1385 | rm paused.conf 2>/dev/null 1386 | } 1387 | 1388 | function checktools { 1389 | oscheck=$(uname) 1390 | if [ "$oscheck" = "Darwin" ]; then # For macOS 1391 | missing=() 1392 | for tool in "${mactools[@]}"; do 1393 | if ! command -v "$tool" >/dev/null 2>&1; then 1394 | if [ "$tool" = "realpath" ]; then 1395 | missing+=(coreutils) 1396 | else 1397 | missing+=("$tool") 1398 | fi 1399 | fi 1400 | done 1401 | if [ ${#missing[@]} -gt 0 ]; then 1402 | echo -e "\e[31m[X] Error: The following required tools are not installed: ${missing[*]}\e[0m" 1403 | # Ask if user wants to install 1404 | read -p "Install them now using Homebrew? " resp 1405 | if [[ "$resp" =~ ^[Yy]$ ]]; then 1406 | # Install missing tools 1407 | for tool in "${missing[@]}"; do 1408 | echo "Installing $tool..." 1409 | brew install "$tool" 1410 | done 1411 | echo -e "\e[32m[+] All missing required tools have been installed\e[0m\n" 1412 | else 1413 | echo -e "\nYou can install them manually with:\n brew install \n" 1414 | exit 1 1415 | fi 1416 | fi 1417 | else # For Linux 1418 | missing=() 1419 | for tool in "${linuxtools[@]}"; do 1420 | if ! command -v "$tool" >/dev/null 2>&1; then 1421 | if [ "$tool" = "realpath" ]; then 1422 | missing+=(coreutils) 1423 | else 1424 | missing+=("$tool") 1425 | fi 1426 | fi 1427 | done 1428 | if [ ${#missing[@]} -gt 0 ]; then 1429 | echo -e "\e[31m[X] Error: The following required tools are not installed: ${missing[*]}\e[0m" 1430 | # Ask if user wants to install 1431 | read -p "Install them now using apt? " resp 1432 | if [[ "$resp" =~ ^[Yy]$ ]]; then 1433 | # Update apt cache and install missing tools 1434 | sudo apt update 1435 | for tool in "${missing[@]}"; do 1436 | echo "Installing $tool..." 1437 | sudo apt install -y "$tool" 1438 | done 1439 | echo -e "\e[32m[+] All missing required tools have been installed\e[0m\n" 1440 | else 1441 | echo -e "\nYou can install them manually with:\n sudo apt install \n" 1442 | exit 1 1443 | fi 1444 | fi 1445 | fi 1446 | } 1447 | 1448 | function z0engineer { #Enables users to customize commands 1449 | echo -e "\e[33m [!] z0e ngineer mode is experimental [!]" 1450 | echo -e " [!] It tries to prevent command options that may cause errors" 1451 | echo -e " [!] But given the large number of possible options, it does not catch everything" 1452 | echo -e " [!] If z0e errors, it is likely due to input passed in these commands \e[0m" 1453 | #create prompts for all necessary commands depending on if internal or external, saying not to include targets, excludes, output, etc 1454 | if [[ "$e_opt" = true || "$type" = "E" || "$type" = "e" || "$type" = "external" || "$type" = "External" || "$type" = "Ext" || "$type" = "ext" ]]; then 1455 | #while true; do 1456 | # echo "[?] Provide nmap alives discovery command:" 1457 | # read -e -p " [>] " z0eng_ext_alives 1458 | # if [[ "$z0eng_ext_alives" =~ (-|--excludefile|-iL|>>|>|&|-o\*) ]] || [[ "$z0eng_ports_opts" == *"- "* ]] || { [[ "$z0eng_ports_opts" =~ (-)$ ]] && [[ ! "$z0eng_ports_opts" =~ "-p-" ]]; }; then 1459 | # echo -e "\e[31m [X] Error: The command cannot contain [-|--excludefile|-iL|>>|>|&] -- this will likely cause errors \e[0m" 1460 | # elif [[ "$z0eng_ext_alives" != *"nmap"* ]]; then 1461 | # echo -e "\e[31m [X] Error: Nmap must be used for alives discovery or errors will occur \e[0m" 1462 | # elif [[ -z "$z0eng_ext_alives" ]]; then #leave blank to use default 1463 | # ngineer_default=true 1464 | # break 1465 | # else 1466 | # echo "$z0eng_ext_alives" 1467 | # break 1468 | # fi 1469 | #done 1470 | echo "[?] Provide custom options for the masscan open port discovery scan command:" 1471 | echo " Leave blank to use the z0e default" 1472 | echo " Hardcoded: <--open-only> <--excludefile (null if not specified)> <--include-file> <-oG>" 1473 | while true; do 1474 | read -e -p " [>] " z0eng_ports_opts 1475 | if [[ -z "$z0eng_ports_opts" ]]; then #leave blank to use default 1476 | ngineer_ports_default=true 1477 | break 1478 | elif [[ "$z0eng_ports_opts" =~ (-sV|--excludefile|--include-file|>>|>|&|-o\*) ]] || [[ "$z0eng_ports_opts" == "- " ]] || { [[ "$z0eng_ports_opts" =~ (-)$ ]] && [[ ! "$z0eng_ports_opts" =~ "-p-" ]]; }; then 1479 | echo -e "\e[31m [X] Error: The command cannot contain [ --excludefile | --include-file | >> | > | & | - | -o* ] -- this will likely cause errors\e[0m" >&2 1480 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1481 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1482 | elif [[ "$z0eng_ports_opts" == *"nmap"* ]]; then 1483 | echo -e "\e[31m [X] Error: Currently, Nmap cannot be used here \e[0m" >&2 1484 | elif ! [[ "$z0eng_ports_opts" =~ (-p[[:space:]]*([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?|--top-ports[[:space:]]+([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))|-p- ]]; then 1485 | echo -e "\e[31m [X] Error: At minimum, you must specify ports with -p or --top-ports with proper syntax \e[0m" >&2 1486 | else 1487 | break 1488 | fi 1489 | done 1490 | 1491 | if [[ "$U_opt" = true || "$udp" = "y" || "$udp" = "yes" ]]; then 1492 | echo "[?] Provide custom options for the Nmap UDP discovery scan command:" 1493 | echo " Leave blank to use the z0e default" 1494 | echo " Hardcoded: <-sU> <--open> <--excludefile (null if not specified)> <-iL> <-oG>" 1495 | while true; do 1496 | read -e -p " [>] " z0eng_udpa_opts 1497 | if [[ -z "$z0eng_udpa_opts" ]]; then #leave blank to use default 1498 | ngineer_udpa_default=true 1499 | break 1500 | elif [[ "$z0eng_udpa_opts" =~ (-sV|--excludefile|-iL|>>|>|&|-o\*) ]] || [[ "$z0eng_udpa_opts" == *"- "* ]] || { [[ "$z0eng_udpa_opts" =~ (-)$ ]] && [[ ! "$z0eng_udpa_opts" =~ "-p-" ]]; }; then 1501 | echo -e "\e[31m [X] Error: The command cannot contain [ -sV | --excludefile | -iL | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1502 | elif [[ "$z0eng_udpa_opts" == *"nmap"* ]]; then 1503 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1504 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1505 | echo -e "\e[31m [X] Error: Currently, masscan cannot be used here \e[0m" >&2 1506 | elif ! [[ "$z0eng_udpa_opts" =~ (-p[[:space:]]*([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?|--top-ports[[:space:]]+([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))|-p- ]]; then 1507 | echo -e "\e[31m [X] Error: At minimum, you must specify ports with -p or --top-ports with proper syntax \e[0m" >&2 1508 | else 1509 | break 1510 | fi 1511 | done 1512 | fi 1513 | 1514 | echo "[?] Provide custom options for the Nmap TCP service scan command:" 1515 | echo " Leave blank to use the z0e default" 1516 | echo " Hardcoded: <-sV> <-Pn> <-p> <--excludefile (null if not specified)> <-iL> <-oA>" 1517 | while true; do 1518 | read -e -p " [>] " z0eng_tcps_opts 1519 | if [[ -z "$z0eng_tcps_opts" ]]; then #leave blank to use default 1520 | ngineer_tcps_default=true 1521 | break 1522 | elif [[ "$z0eng_tcps_opts" =~ (--excludefile|-iL|>>|>|&|-o\*) ]] || [[ "$z0eng_tcps_opts" == *"- "* ]] || { [[ "$z0eng_tcps_opts" =~ (-)$ ]] && [[ ! "$z0eng_tcps_opts" =~ "-p-" ]]; }; then 1523 | echo -e "\e[31m [X] Error: The command cannot contain [ --excludefile | -iL | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1524 | elif [[ "$z0eng_tcps_opts" == *"nmap"* ]]; then 1525 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1526 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1527 | echo -e "\e[31m [X] Error: Currently, masscan cannot be used here \e[0m" >&2 1528 | elif [[ "$z0eng_tcps_opts" == *"-p"* ]] || [[ "$z0eng_tcps_opts" == *"--top-ports"* ]]; then 1529 | echo -e "\e[31m [X] Error: z0e will provide only open ports to this scan \e[0m" >&2 1530 | else 1531 | break 1532 | fi 1533 | done 1534 | 1535 | if [[ "$U_opt" = true || "$udp" = "y" || "$udp" = "yes" ]]; then 1536 | echo "[?] Provide custom options for the Nmap UDP service scan command:" 1537 | echo " Leave blank to use the z0e default" 1538 | echo " Hardcoded: <-sU> <-sV> <-Pn> <-p> <--excludefile (null if not specified)> <-iL> <-oA>" 1539 | while true; do 1540 | read -e -p " [>] " z0eng_udps_opts 1541 | if [[ -z "$z0eng_udps_opts" ]]; then #leave blank to use default 1542 | ngineer_udps_default=true 1543 | break 1544 | elif [[ "$z0eng_udps_opts" =~ (--excludefile|-iL|>>|>|&|-o\*) ]] || [[ "$z0eng_udps_opts" == *"- "* ]] || { [[ "$z0eng_udps_opts" =~ (-)$ ]] && [[ ! "$z0eng_udps_opts" =~ "-p-" ]]; }; then 1545 | echo -e "\e[31m [X] Error: The command cannot contain [ --excludefile | -iL | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1546 | elif [[ "$z0eng_udps_opts" == *"nmap"* ]]; then 1547 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1548 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1549 | echo -e "\e[31m [X] Error: Currently, masscan cannot be used here \e[0m" >&2 1550 | elif [[ "$z0eng_udps_opts" == *"-p"* ]] || [[ "$z0eng_udps_opts" == *"--top-ports"* ]]; then 1551 | echo -e "\e[31m [X] Error: z0e will provide only open ports to this scan \e[0m" >&2 1552 | else 1553 | break 1554 | fi 1555 | done 1556 | fi 1557 | 1558 | elif [[ "$i_opt" = true || "$type" = "I" || "$type" = "i" || "$type" = "internal" || "$type" = "Internal" || "$type" = "Int" || "$type" = "int" ]]; then 1559 | ###Internal### 1560 | echo "[?] Provide custom options for the masscan open port discovery scan command:" 1561 | echo " Leave blank to use the z0e default" 1562 | echo " Hardcoded: <--open-only> <--excludefile (null if not specified)> <--include-file> <-oG>" 1563 | while true; do 1564 | read -e -p " [>] " z0eng_ports_opts 1565 | if [[ -z "$z0eng_ports_opts" ]]; then #leave blank to use default 1566 | ngineer_ports_default=true 1567 | break 1568 | elif [[ "$z0eng_ports_opts" =~ (--src-port|--excludefile|--include-file|>>|>|&|-o\*) ]] || [[ "$z0eng_ports_opts" == *"- "* ]] || { [[ "$z0eng_ports_opts" =~ (-)$ ]] && [[ ! "$z0eng_ports_opts" =~ "-p-" ]]; }; then 1569 | echo -e "\e[31m [X] Error: The command cannot contain [ --src-port | --excludefile | --include-file | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1570 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1571 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1572 | elif [[ "$z0eng_ports_opts" == *"nmap"* ]]; then 1573 | echo -e "\e[31m [X] Error: Currently, Nmap cannot be used here \e[0m" >&2 1574 | elif ! [[ "$z0eng_ports_opts" =~ (-p[[:space:]]*([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?|--top-ports[[:space:]]+([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))|-p- ]]; then 1575 | echo -e "\e[31m [X] Error: At minimum, you must specify ports with -p or --top-ports with proper syntax \e[0m" >&2 1576 | else 1577 | break 1578 | fi 1579 | done 1580 | 1581 | if [[ "$U_opt" = true || "$udp" = "y" || "$udp" = "yes" ]]; then 1582 | echo "[?] Provide custom options for the Nmap UDP discovery scan command:" 1583 | echo " Leave blank to use the z0e default" 1584 | echo " Hardcoded: <-sU> <--open> <--excludefile (null if not specified)> <-iL> <-oG>" 1585 | while true; do 1586 | read -e -p " [>] " z0eng_udpa_opts 1587 | if [[ -z "$z0eng_udpa_opts" ]]; then #leave blank to use default 1588 | ngineer_udpa_default=true 1589 | break 1590 | elif [[ "$z0eng_udpa_opts" =~ (--excludefile|-iL|-sV|>>|>|&|-o\*) ]] || [[ "$z0eng_udpa_opts" == *"- "* ]] || { [[ "$z0eng_udpa_opts" =~ (-)$ ]] && [[ ! "$z0eng_udpa_opts" =~ "-p-" ]]; }; then 1591 | echo -e "\e[31m [X] Error: The command cannot contain [ --excludefile | -iL | -sV | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1592 | elif [[ "$z0eng_udpa_opts" == *"nmap"* ]]; then 1593 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1594 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1595 | echo -e "\e[31m [X] Error: Currently, masscan cannot be used here \e[0m" >&2 1596 | elif ! [[ "$z0eng_udpa_opts" =~ (-p[[:space:]]*([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])(-([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?|--top-ports[[:space:]]+([0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))|-p- ]]; then 1597 | echo -e "\e[31m [X] Error: At minimum, you must specify ports with -p or --top-ports with proper syntax \e[0m" >&2 1598 | else 1599 | break 1600 | fi 1601 | done 1602 | fi 1603 | 1604 | echo "[?] Provide custom options for the Nmap TCP service scan command:" 1605 | echo " Leave blank to use the z0e default" 1606 | echo " Hardcoded: <-sV> <-Pn> <-p> <--excludefile (null if not specified)> <-iL> <-oA>" 1607 | while true; do 1608 | read -e -p " [>] " z0eng_tcps_opts 1609 | if [[ -z "$z0eng_tcps_opts" ]]; then #leave blank to use default 1610 | ngineer_tcps_default=true 1611 | break 1612 | elif [[ "$z0eng_tcps_opts" =~ (--excludefile|-iL|>>|>|&|-o\*) ]] || [[ "$z0eng_tcps_opts" == *"- "* ]] || { [[ "$z0eng_tcps_opts" =~ (-)$ ]] && [[ ! "$z0eng_tcps_opts" =~ "-p-" ]]; }; then 1613 | echo -e "\e[31m [X] Error: The command cannot contain [ --excludefile | -iL | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1614 | elif [[ "$z0eng_tcps_opts" == *"nmap"* ]]; then 1615 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1616 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1617 | echo -e "\e[31m [X] Error: Currently, masscan cannot be used here \e[0m" >&2 1618 | elif [[ "$z0eng_tcps_opts" == *"-p"* ]] || [[ "$z0eng_tcps_opts" == *"--top-ports"* ]]; then 1619 | echo -e "\e[31m [X] Error: z0e will provide only open ports to this scan \e[0m" >&2 1620 | else 1621 | break 1622 | fi 1623 | done 1624 | 1625 | if [[ "$U_opt" = true || "$udp" = "y" || "$udp" = "yes" ]]; then 1626 | echo "[?] Provide custom options for the Nmap UDP service scan command:" 1627 | echo " Leave blank to use the z0e default" 1628 | echo " Hardcoded: <-sU> <-sV> <-Pn> <-p> <--excludefile (null if not specified)> <-iL> <-oA>" 1629 | while true; do 1630 | read -e -p " [>] " z0eng_udps_opts 1631 | if [[ -z "$z0eng_udps_opts" ]]; then #leave blank to use default 1632 | ngineer_udps_default=true 1633 | break 1634 | elif [[ "$z0eng_udps_opts" =~ (--excludefile|-iL|>>|>|&|-o\*) ]] || [[ "$z0eng_udps_opts" == *"- "* ]] || { [[ "$z0eng_udps_opts" =~ (-)$ ]] && [[ ! "$z0eng_udps_opts" =~ "-p-" ]]; }; then 1635 | echo -e "\e[31m [X] Error: The command cannot contain [ --excludefile | -iL | >> | > | & | - | -o* ] -- this will likely cause errors \e[0m" >&2 1636 | elif [[ "$z0eng_udps_opts" == *"nmap"* ]]; then 1637 | echo -e "\e[31m [X] Error: Only provide the desired options \e[0m" >&2 1638 | elif [[ "$z0eng_ports_opts" == *"masscan"* ]]; then 1639 | echo -e "\e[31m [X] Error: Currently, masscan cannot be used here \e[0m" >&2 1640 | elif [[ "$z0eng_udps_opts" == *"-p"* ]] || [[ "$z0eng_udps_opts" == *"--top-ports"* ]]; then 1641 | echo -e "\e[31m [X] Error: z0e will provide only open ports to this scan \e[0m" >&2 1642 | else 1643 | break 1644 | fi 1645 | done 1646 | fi 1647 | fi 1648 | } 1649 | 1650 | function totaltargets { # Counts total number of hosts in a targets file 1651 | 1652 | # Function to convert a dotted-decimal IP to an integer. 1653 | # Returns -1 if the IP is invalid. 1654 | ip_to_int() { 1655 | local ip="$1" 1656 | local IFS='.' 1657 | read -ra octets <<< "$ip" 1658 | # Ensure exactly 4 octets are provided. 1659 | if (( ${#octets[@]} != 4 )); then 1660 | echo "-1" 1661 | return 1 1662 | fi 1663 | 1664 | local result=0 1665 | for octet in "${octets[@]}"; do 1666 | # Check that each octet is a number between 0 and 255. 1667 | if ! [[ "$octet" =~ ^[0-9]+$ ]] || (( octet < 0 || octet > 255 )); then 1668 | echo "-1" 1669 | return 1 1670 | fi 1671 | result=$(( (result << 8) + octet )) 1672 | done 1673 | echo "$result" 1674 | } 1675 | 1676 | # Function to calculate the number of hosts in a CIDR block. 1677 | # It subtracts the network and broadcast addresses for prefixes < 31. 1678 | calculate_cidr_hosts() { 1679 | local cidr="$1" 1680 | local ip prefix 1681 | IFS=/ read -r ip prefix <<< "$cidr" 1682 | 1683 | # Validate CIDR prefix. 1684 | if ! [[ "$prefix" =~ ^[0-9]+$ ]] || (( prefix < 0 || prefix > 32 )); then 1685 | echo "0" 1686 | return 1687 | fi 1688 | 1689 | # Calculate the total number of addresses. 1690 | local total_addresses=$(( 1 << (32 - prefix) )) 1691 | # For subnets with more than 2 addresses, subtract network and broadcast. 1692 | if (( prefix < 31 )); then 1693 | total_addresses=$(( total_addresses - 2 )) 1694 | fi 1695 | 1696 | # Ensure we don't get a negative number. 1697 | if (( total_addresses < 0 )); then 1698 | total_addresses=0 1699 | fi 1700 | 1701 | echo "$total_addresses" 1702 | } 1703 | 1704 | # Function to calculate the number of hosts in an IP range. 1705 | calculate_ip_range_hosts() { 1706 | local ip_range="$1" 1707 | local IFS='-' 1708 | local start end 1709 | 1710 | read -r start end <<< "$ip_range" 1711 | 1712 | # Convert both IP addresses to their integer representations. 1713 | local start_long=$(ip_to_int "$start") 1714 | local end_long=$(ip_to_int "$end") 1715 | 1716 | # If either conversion failed (indicated by a negative value) or 1717 | # if the start IP is greater than the end IP, return 0. 1718 | if (( start_long < 0 || end_long < 0 || start_long > end_long )); then 1719 | echo "0" 1720 | return 1721 | fi 1722 | 1723 | # Calculate the number of IP addresses in the range. 1724 | local num_hosts=$(( end_long - start_long + 1 )) 1725 | echo "$num_hosts" 1726 | } 1727 | 1728 | total_hosts=0 # Global variable to accumulate the total number of hosts. 1729 | local line hosts 1730 | 1731 | # Process the input file specified by the variable 'checkfile'. 1732 | while IFS= read -r line || [[ -n "$line" ]]; do 1733 | # Remove comments and whitespace. 1734 | line="${line%%#*}" 1735 | line="${line//[[:space:]]/}" 1736 | # Skip empty lines. 1737 | if [[ -z "$line" ]]; then 1738 | continue 1739 | fi 1740 | 1741 | if [[ "$line" == *"/"* ]]; then 1742 | # CIDR notation. 1743 | hosts=$(calculate_cidr_hosts "$line") 1744 | elif [[ "$line" == *"-"* ]]; then 1745 | # IP range. 1746 | hosts=$(calculate_ip_range_hosts "$line") 1747 | else 1748 | # Single IP. 1749 | hosts=1 1750 | fi 1751 | total_hosts=$(( total_hosts + hosts )) 1752 | done < "$checkfile" 1753 | } 1754 | 1755 | function geniplist { 1756 | ip_to_int() { 1757 | # Function to convert dotted-decimal IP to integer 1758 | local ip="$1" 1759 | local o1 o2 o3 o4 1760 | # Extract octets 1761 | o1="${ip%%.*}" 1762 | ip="${ip#*.}" 1763 | o2="${ip%%.*}" 1764 | ip="${ip#*.}" 1765 | o3="${ip%%.*}" 1766 | o4="${ip#*.}" 1767 | # Convert to integers 1768 | echo $(( (o1 << 24) + (o2 << 16) + (o3 << 8) + o4 )) 1769 | } 1770 | 1771 | int_to_ip() { 1772 | # Function to convert integer IP to dotted-decimal format 1773 | local ip="$1" 1774 | local octet1=$(( (ip >> 24) & 255 )) 1775 | local octet2=$(( (ip >> 16) & 255 )) 1776 | local octet3=$(( (ip >> 8) & 255 )) 1777 | local octet4=$(( ip & 255 )) 1778 | echo "$octet1.$octet2.$octet3.$octet4" 1779 | } 1780 | 1781 | cidr_to_ip_range() { 1782 | # Function to convert CIDR to IP range 1783 | local cidr="$1" 1784 | local ip cidr_val netmask ip_int network broadcast 1785 | IFS=/ read -r ip cidr_val <<< "$cidr" 1786 | ip_int=$(ip_to_int "$ip") 1787 | netmask=$(( (0xFFFFFFFF << (32 - cidr_val)) & 0xFFFFFFFF )) 1788 | network=$(( ip_int & netmask )) 1789 | broadcast=$(( network | ((~netmask) & 0xFFFFFFFF) )) 1790 | echo "$(int_to_ip "$network")-$(int_to_ip "$broadcast")" 1791 | } 1792 | 1793 | expand_ips() { 1794 | # Function to expand IPs from a file that can include single IPs, ranges, and CIDRs. 1795 | local inputfile="$1" 1796 | # Declare all variables local to this function. 1797 | local ip range start end start_int end_int network prefix i ip_addr 1798 | 1799 | while IFS= read -r ip || [[ -n "$ip" ]]; do 1800 | # Remove comments and whitespace. 1801 | ip="${ip%%#*}" 1802 | ip="${ip//[[:space:]]/}" 1803 | 1804 | # Skip empty lines. 1805 | if [[ -z "$ip" ]]; then 1806 | continue 1807 | fi 1808 | 1809 | if [[ "$ip" == *"/"* ]]; then 1810 | # Handle CIDR notation. 1811 | range=$(cidr_to_ip_range "$ip") 1812 | IFS=- read -r start end <<< "$range" 1813 | start_int=$(ip_to_int "$start") 1814 | end_int=$(ip_to_int "$end") 1815 | # Get the CIDR prefix to determine if network/broadcast addresses should be excluded. 1816 | IFS=/ read -r network prefix <<< "$ip" 1817 | if (( prefix < 31 )); then 1818 | start_int=$(( start_int + 1 )) 1819 | end_int=$(( end_int - 1 )) 1820 | fi 1821 | for (( i = start_int; i <= end_int; i++ )); do 1822 | int_to_ip "$i" 1823 | done 1824 | elif [[ "$ip" == *"-"* ]]; then 1825 | # Handle IP range. 1826 | IFS=- read -r start end <<< "$ip" 1827 | start_int=$(ip_to_int "$start") 1828 | end_int=$(ip_to_int "$end") 1829 | for (( i = start_int; i <= end_int; i++ )); do 1830 | ip_addr=$(int_to_ip "$i") 1831 | echo "$ip_addr" 1832 | done 1833 | else 1834 | # Handle single IP address. 1835 | echo "$ip" 1836 | fi 1837 | done < "$inputfile" 1838 | } 1839 | # Main function to generate a sorted, unique IP list. 1840 | expand_ips "$checkfile" | sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4 | uniq 1841 | } 1842 | 1843 | function alivesandportscheck { #checks if alives and openPorts lists are populated 1844 | if grep -q "\S" "$filepath/$typevar-alives.txt" && grep -q "\S" "$filepath/$typevar-openPorts.txt"; then 1845 | status_msg="[+] Discovery scans completed -- $(date)" 1846 | echo -e "\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1847 | ntfy "$status_msg" 1848 | elif ! grep -q "\S" "$filepath/$typevar-alives.txt" && grep -q "\S" "$filepath/$typevar-openPorts.txt"; then 1849 | status_msg="[!] No alive hosts detected, check targets or inquire about scan defenses or other interference -- Exiting Zero-E..." 1850 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1851 | ntfy "$status_msg" 1852 | z0ecleanup 1853 | exit 1 1854 | elif grep -q "\S" "$filepath/$typevar-alives.txt" && ! grep -q "\S" "$filepath/$typevar-openPorts.txt"; then 1855 | status_msg="[!] No open ports detected, check targets or inquire about scan defenses or other interference -- Exiting Zero-E..." 1856 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1857 | ntfy "$status_msg" 1858 | z0ecleanup 1859 | exit 1 1860 | else 1861 | status_msg="[!] No alive hosts or open ports detected, check targets or inquire about scan defenses or other interference -- Exiting Zero-E..." 1862 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1863 | ntfy "$status_msg" 1864 | z0ecleanup 1865 | exit 1 1866 | fi 1867 | } 1868 | 1869 | function removefirewallrule { #removes the firewall rule used in int methodology 1870 | firewallset=false 1871 | oscheck=$(uname) 1872 | if [ "$oscheck" = "Darwin" ]; then #For st00pid Macs 1873 | cp "/etc/pf.conf" "$filepath/logs/pf.conf.bak-postscript" 1874 | sudo sed -i "/block drop in proto tcp from any to any port 55555/d" /etc/pf.conf 1875 | pfctl -f /etc/pf.conf >> $filepath/logs/mac-pfctl.log 2>&1 1876 | if [ "$macpf" = "Disabled" ]; then 1877 | pfctl -d >> $filepath/logs/mac-pfctl.log 2>&1 #disable pfctl 1878 | fi 1879 | else #For Linux 1880 | sudo iptables -D INPUT -p tcp --dport 55555 -j DROP 2>/dev/null 1881 | fi 1882 | echo -e "\e[33m [!] Firewall rule removed \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1883 | } 1884 | 1885 | function setfirewallrule { #sets the firewall rule used in int methodology 1886 | firewallset=true 1887 | oscheck=$(uname) 1888 | if [ "$oscheck" = "Darwin" ]; then #For st00pid Macs 1889 | macpf=$(pfctl -s info | grep -o "Status: .*" | cut -d' ' -f2) #gets pfctl status 1890 | if [ "$macpf" = "Disabled" ]; then 1891 | pfctl -e >> $filepath/logs/mac-pfctl.log 2>&1 #enable pfctl 1892 | fi 1893 | if pfctl -sr 2>> $filepath/logs/mac-pfctl.log | grep -q "block drop in proto tcp from any to any port 55555"; then #check if rule exists 1894 | echo -e "\e[33m [!] Firewall rule on port 55555 to prevent RST packets already exists -- skipping rule creation \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1895 | else 1896 | cp "/etc/pf.conf" "$filepath/logs/pf.conf.bak-prescript" 1897 | echo "block drop in proto tcp from any to any port 55555" | sudo tee -a /etc/pf.conf >> $filepath/logs/mac-pfctl.log #creates the rule 1898 | sudo pfctl -f /etc/pf.conf >> $filepath/logs/mac-pfctl.log 2>&1 #loads the pfctl configuration 1899 | echo -e "\e[33m [!] Firewall rule created on port 55555 to prevent RST packet interference \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1900 | fi 1901 | else #For Linux 1902 | if sudo iptables -C INPUT -p tcp --dport 55555 -j DROP 2> /dev/null; then 1903 | echo -e "\e[33m [!] Firewall rule on port 55555 to prevent RST packets already exists -- skipping rule creation \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1904 | else 1905 | sudo iptables -A INPUT -p tcp --dport 55555 -j DROP 1906 | echo -e "\e[33m [!] Firewall rule created on port 55555 to prevent RST packet interference \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 1907 | fi 1908 | fi 1909 | } 1910 | 1911 | function ntfy { # enables ntfy functionality 1912 | if [ "$ntfy_flag" = true ]; then 1913 | 1914 | if [ "$z0e_started" != true ]; then 1915 | # Remove any spaces from the provided value 1916 | ntfy_arg=$(echo "$ntfy_arg" | tr -d '[:space:]') 1917 | # Check if the value contains a comma 1918 | if [[ "$ntfy_arg" == *,* ]]; then 1919 | ntfy_pri=$(echo "$ntfy_arg" | cut -d',' -f1) 1920 | ntfy_url=$(echo "$ntfy_arg" | cut -d',' -f2-) 1921 | # Validate that the priority is a number between 1 and 5 1922 | if ! [[ "$ntfy_pri" =~ ^[1-5]$ ]]; then 1923 | echo -e "\e[31m [X] Error: ntfy priority must be a number between 1 and 5 \e[0m" >&2 1924 | exit 1 1925 | fi 1926 | else 1927 | # Default priority to 3 if not provided 1928 | ntfy_pri=3 1929 | ntfy_url="$ntfy_arg" 1930 | fi 1931 | # Validate that the URL starts with http:// or https:// 1932 | if ! [[ "$ntfy_url" =~ ^https?:// ]]; then 1933 | echo -e "\e[31m [X] Error: ntfy URL must start with http:// or https:// \e[0m" >&2 1934 | exit 1 1935 | fi 1936 | fi 1937 | 1938 | if [ "$z0e_started" == true ]; then 1939 | # Send the POST request to the ntfy server 1940 | local msg="$1" 1941 | curl -H "X-Priority: $ntfy_pri" -d "$msg" "$ntfy_url" >> "$filepath/logs/$typevar-ntfy.log" 2>&1 1942 | fi 1943 | fi 1944 | } 1945 | 1946 | function logconfig { # Logs the configuration of the scan 1947 | local file="$1" 1948 | if [[ ! -f "$file" ]]; then 1949 | echo "Error: File '$file' not found." >&2 1950 | return 1 1951 | fi 1952 | 1953 | # Read the entire file content into a variable. 1954 | local config_str 1955 | config_str=$(< "$file") 1956 | 1957 | local udp_line="" 1958 | # Determine UDP configuration: 1959 | if grep -q 'U_opt="true"' "$file"; then 1960 | udp_line="udp=enabled" 1961 | elif grep -q 'u_opt="true"' "$file"; then 1962 | udp_line="udp=disabled" 1963 | fi 1964 | 1965 | # Variables for specific keys in desired order. 1966 | local session_line="" 1967 | local output_dir="" 1968 | local targets_file="" 1969 | local total_targets="" 1970 | local excludes_file="" 1971 | local other_lines=() 1972 | 1973 | # Add session name if session is active 1974 | if [[ "$session_flag" = true && -n "$session" ]]; then 1975 | session_line="sessionName=\"$session\"" 1976 | fi 1977 | 1978 | # Process each token (assumes tokens are whitespace-separated). 1979 | for token in $config_str; do 1980 | local key=${token%%=*} 1981 | local value=${token#*=} 1982 | # Remove surrounding quotes. 1983 | value=${value%\"} 1984 | value=${value#\"} 1985 | 1986 | case "$key" in 1987 | # These keys are removed. 1988 | e_opt|i_opt|typevar|stage_cont|reportips|only_flag|U_opt|u_opt) 1989 | ;; 1990 | filepath) 1991 | output_dir="outputDirectory=\"$value\"" 1992 | ;; 1993 | ips) 1994 | targets_file="targets=\"$ips_log\"" 1995 | ;; 1996 | total_hosts) 1997 | total_targets="totalTargets=\"$value\"" 1998 | ;; 1999 | nostrikes) 2000 | if [ "$value" = "/var/lib/zeroe/nullexcludes.z0e" ]; then 2001 | excludes_file="excludes=\"none\"" 2002 | else 2003 | excludes_file="excludes=\"$exc_log\"" 2004 | fi 2005 | ;; 2006 | *) 2007 | other_lines+=("$key=\"$value\"") 2008 | ;; 2009 | esac 2010 | done 2011 | 2012 | # Output in the specified order: 2013 | # 1. Session name (if active) 2014 | # 2. Output directory 2015 | # 3. Targets file 2016 | # 4. Total targets 2017 | # 5. Excludes file 2018 | # 6. Any other key-value pairs 2019 | # 7. UDP status (last) 2020 | if [ -n "$session_line" ]; then 2021 | echo "$session_line" 2022 | fi 2023 | if [ -n "$output_dir" ]; then 2024 | echo "$output_dir" 2025 | fi 2026 | if [ -n "$targets_file" ]; then 2027 | echo "$targets_file" 2028 | fi 2029 | if [ -n "$total_targets" ]; then 2030 | echo "$total_targets" 2031 | fi 2032 | if [ -n "$excludes_file" ]; then 2033 | echo "$excludes_file" 2034 | fi 2035 | for line in "${other_lines[@]}"; do 2036 | echo "$line" 2037 | done 2038 | if [ -n "$udp_line" ]; then 2039 | echo "$udp_line" 2040 | fi 2041 | } 2042 | 2043 | function sessioninit { # Initializes sessions 2044 | if [ "$session_flag" = true ]; then 2045 | session_path="/var/lib/zeroe/sessions/$session" 2046 | 2047 | if [[ -z "$S_opt" && -d "$session_path" ]]; then # If -S is provided and the session exists, skip prompts and just resume 2048 | echo "[#] Resuming session '$session'" 2049 | return 2050 | elif [ -d "$session_path" ]; then 2051 | echo -e "[?] Session '$session' already exists -- Overwrite? : " 2052 | echo -e " If trying to resume the session, exit and also provide the '-S' option" 2053 | while true; do 2054 | read -e -p " [>] " response 2055 | if [ "$response" == "y" ]; then 2056 | rm -rf "$session_path" 2057 | mkdir -p "$session_path" # Ensure directory is recreated 2058 | echo "[#] Overwriting session '$session'" 2059 | break 2060 | elif [ "$response" == "n" ]; then 2061 | while true; do 2062 | echo -e "[?] Enter a new session name: " 2063 | read -e -p " [>] " session 2064 | session_path="/var/lib/zeroe/sessions/$session" 2065 | if [ -d "$session_path" ]; then 2066 | echo -e "\e[31m [X] Error: You opted to not overwrite, but Session '$session' already exists \e[0m" >&2 2067 | else 2068 | echo "[#] Creating new session '$session'" 2069 | mkdir -p "$session_path" 2070 | break 2071 | fi 2072 | done 2073 | break 2074 | else 2075 | echo "[#] Exiting Zero-E" 2076 | exit 1 2077 | fi 2078 | done 2079 | else 2080 | echo "[#] Creating new session '$session'" 2081 | mkdir -p "$session_path" # Ensure session directory exists 2082 | fi 2083 | fi 2084 | } 2085 | 2086 | #Banner 2087 | echo "$banner -- https://github.com/Inscyght/Zero-E" 2088 | echo "Support this project -- https://github.com/sponsors/Inscyght" 2089 | echo " https://buymeacoffee.com/inscyght" 2090 | echo '' 2091 | 2092 | updatecheck 2093 | 2094 | #--Switches 2095 | #auxiliary options 2096 | help_flag=false 2097 | version_flag=false 2098 | geniplistopt=false 2099 | geniplist_file="" 2100 | countopt=false 2101 | checkfile="" 2102 | defaults=false 2103 | only_flag=false 2104 | ngineer_mode=false 2105 | listwinhosts_flag=false 2106 | parseports_flag=false 2107 | listiphostnames_flag=false 2108 | ntfy_flag=false 2109 | session="" 2110 | session_flag=false 2111 | 2112 | while [[ $# -gt 0 ]]; do 2113 | case "$1" in 2114 | --help) 2115 | help_flag=true 2116 | shift # Remove --help from processing 2117 | ;; 2118 | --version) 2119 | version_flag=true 2120 | shift # Remove --version from processing 2121 | ;; 2122 | --geniplist) 2123 | shift # remove --geniplist from $@ 2124 | if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then 2125 | arg="$1" 2126 | shift # remove the file/IP argument 2127 | # 1) Single existing, non-empty file with no commas or IP pattern => use it directly 2128 | if [ -f "$arg" ] && [ -s "$arg" ] \ 2129 | && ! [[ "$arg" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 2130 | && [[ "$arg" != *","* ]] 2131 | then 2132 | geniplistopt=true 2133 | geniplist_file="$arg" 2134 | # Optionally validate: 2135 | checkfile="$geniplist_file" 2136 | checkinvalidips 2137 | # 2) If the arg has commas or IP pattern => two-file approach 2138 | elif { ! [ -f "$arg" ] && [[ "$arg" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; } \ 2139 | || [[ "$arg" == *","* ]] 2140 | then 2141 | geniplistopt=true 2142 | # Create metadata + raw files 2143 | metadata_file=$(mktemp /tmp/geniplist_ips.metadata.XXXXXX) 2144 | raw_file=$(mktemp /tmp/geniplist_ips.raw.XXXXXX) 2145 | IFS=',' read -ra token_array <<< "$arg" 2146 | anyerrors=false 2147 | for token in "${token_array[@]}"; do 2148 | trimmed="$(echo "$token" | xargs)" 2149 | if [ -f "$trimmed" ]; then 2150 | # If it's a file, ensure non-empty 2151 | if [ ! -s "$trimmed" ]; then 2152 | echo -e "\e[31m [X] Error: '$trimmed' is empty \e[0m" >&2 2153 | anyerrors=true 2154 | break 2155 | fi 2156 | # Merge lines with a prefix in metadata_file 2157 | while IFS= read -r line; do 2158 | echo "__SRC:FILE:${trimmed}__ $line" >> "$metadata_file" 2159 | echo "$line" >> "$raw_file" 2160 | done < "$trimmed" 2161 | else 2162 | # It's an IP / range / CIDR 2163 | echo "__SRC:INPUT__ $trimmed" >> "$metadata_file" 2164 | echo "$trimmed" >> "$raw_file" 2165 | fi 2166 | done 2167 | if $anyerrors; then 2168 | exit 1 2169 | fi 2170 | # Validate the metadata file 2171 | checkfile="$metadata_file" 2172 | checkinvalidips 2173 | if [[ "$allvalid" -eq 1 ]]; then 2174 | geniplist_file="$raw_file" 2175 | else 2176 | exit 1 2177 | fi 2178 | else 2179 | echo -e "\e[31m [X] Error: --geniplist requires an existing file and/or a comma-separated IP list \e[0m" >&2 2180 | exit 1 2181 | fi 2182 | else 2183 | echo -e "\e[31m [X] Error: --geniplist requires a file and/or IP list argument \e[0m" >&2 2184 | exit 1 2185 | fi 2186 | ;; 2187 | --count) 2188 | shift # Remove --count 2189 | if [[ $# -gt 0 && ! "$1" =~ ^- ]]; then 2190 | arg="$1" 2191 | countopt_arg="$1" 2192 | shift 2193 | 2194 | # 1) Single file, non-empty, no commas or IP pattern => direct use 2195 | if [ -f "$arg" ] && [ -s "$arg" ] \ 2196 | && ! [[ "$arg" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 2197 | && [[ "$arg" != *","* ]] 2198 | then 2199 | countopt=true 2200 | checkfile="$arg" 2201 | checkinvalidips # optional validation 2202 | 2203 | # 2) If argument has commas or IP pattern => create metadata + raw 2204 | elif { ! [ -f "$arg" ] && [[ "$arg" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; } \ 2205 | || [[ "$arg" == *","* ]] 2206 | then 2207 | countopt=true 2208 | metadata_file=$(mktemp /tmp/count_ips.metadata.XXXXXX) 2209 | raw_file=$(mktemp /tmp/count_ips.raw.XXXXXX) 2210 | 2211 | IFS=',' read -ra ip_array <<< "$arg" 2212 | anyerrors=false 2213 | for token in "${ip_array[@]}"; do 2214 | trimmed="$(echo "$token" | xargs)" 2215 | if [ -f "$trimmed" ]; then 2216 | if [ ! -s "$trimmed" ]; then 2217 | echo -e "\e[31m [X] Error: '$trimmed' is empty \e[0m" >&2 2218 | anyerrors=true 2219 | break 2220 | fi 2221 | while IFS= read -r line; do 2222 | echo "__SRC:FILE:${trimmed}__ $line" >> "$metadata_file" 2223 | echo "$line" >> "$raw_file" 2224 | done < "$trimmed" 2225 | else 2226 | echo "__SRC:INPUT__ $trimmed" >> "$metadata_file" 2227 | echo "$trimmed" >> "$raw_file" 2228 | fi 2229 | done 2230 | 2231 | if $anyerrors; then 2232 | exit 1 2233 | fi 2234 | 2235 | # Validate if desired 2236 | checkfile="$metadata_file" 2237 | checkinvalidips 2238 | if [[ "$allvalid" -eq 1 ]]; then 2239 | # Now run your counting logic on $raw_file 2240 | checkfile="$raw_file" 2241 | else 2242 | exit 1 2243 | fi 2244 | else 2245 | echo -e "\e[31m [X] Error: --count requires an existing file or a comma-separated IP list \e[0m" >&2 2246 | exit 1 2247 | fi 2248 | else 2249 | echo -e "\e[31m [X] Error: --count requires a file and/or IP list argument \e[0m" >&2 2250 | exit 1 2251 | fi 2252 | ;; 2253 | --ntfy) 2254 | shift # Remove --ntfy from processing 2255 | if [[ $# -gt 0 && ! $1 =~ ^- ]]; then 2256 | ntfy_flag=true 2257 | ntfy_arg="$1" 2258 | shift # Remove the ntfy argument from processing 2259 | else 2260 | echo -e "\e[31m [X] Error: --ntfy requires an argument of [an optional priority of 1-5,] \e[0m" >&2 2261 | exit 1 2262 | fi 2263 | ;; 2264 | --session) 2265 | shift # Remove --session from processing 2266 | if [[ $# -gt 0 && ! $1 =~ ^- ]]; then 2267 | session="$1" 2268 | session_flag=true 2269 | shift # Remove the argument from processing 2270 | else 2271 | echo -e "\e[31m [X] Error: --session requires the name of a new or existing session \e[0m" >&2 2272 | exit 1 2273 | fi 2274 | ;; 2275 | --defaults) 2276 | defaults=true 2277 | shift #Removes --defaults from processing 2278 | ;; 2279 | --only) 2280 | only_flag=true 2281 | shift 2282 | ;; 2283 | --ngineer) 2284 | ngineer_mode=true 2285 | shift 2286 | ;; 2287 | --listwinhosts) 2288 | listwinhosts_flag=true 2289 | shift 2290 | break 2291 | ;; 2292 | --parseports) 2293 | parseports_flag=true 2294 | shift #Remove --parseports from processing 2295 | break #Exit the loop to preserve remaining arguments 2296 | ;; 2297 | --listiphostnames) 2298 | listiphostnames_flag=true 2299 | shift 2300 | break 2301 | ;; 2302 | *) 2303 | remaining_args+=("$1") #Stores other opts/args for processing in post-sudo functions 2304 | shift 2305 | ;; 2306 | esac 2307 | done 2308 | if [ "$help_flag" = true ]; then 2309 | #echo "$banner -- https://github.com/Inscyght/Zero-E" 2310 | echo '' 2311 | echo "USAGE: sudo $(basename $0) [options]" 2312 | echo "While there are options, providing them is not necessary with Zero-E" 2313 | echo "z0e will prompt you for required configuration settings if not provided" 2314 | echo '' 2315 | echo "For advanced usage:" 2316 | echo "sudo $(basename $0) [-e | -i] [-o ] [-t ] [-x [excludes_file and/or IP(s)]]" 2317 | echo " [-U | -u] [-S [stage] | -s]" 2318 | echo " [--defaults] [--ngineer] [--only]" 2319 | echo " [--count ] [--geniplist ]" 2320 | echo " [--listwinhosts [OutputFile]]" 2321 | echo " [--parseports [OutputFileName]]" 2322 | echo " [--listiphostname [OutputFile]]" 2323 | echo " [--ntfy [priority,]] [--session ]" 2324 | echo " [--help] [--version]" 2325 | echo '' 2326 | echo "PRIMARY OPTIONS (z0e will prompt for these if not provided):" 2327 | echo " -e Run external assessment scans (cannot be used with -i)" 2328 | echo " -i Run internal assessment scans (cannot be used with -e)" 2329 | echo " -o Set output directory for generated files" 2330 | echo " -t Provide target IP addresses and/or files in a comma-separated list (file.txt,1.1.1.1)" 2331 | echo " Supports single IPs, ranges, or CIDR notation" 2332 | echo " -x [file(s) and/or IPs] Provide target IP addresses and/or files to exclude in a comma-separated list (file.txt,1.1.1.1)" 2333 | echo " Supports single IPs, ranges, or CIDR notation -- Omit argument to disable exclusion prompt" 2334 | echo " -U Enable UDP scans (cannot be used with -u)" 2335 | echo " -u Disable UDP scans (cannot be used with -U)" 2336 | echo " -S [stage] If no stage provided, resume from saved stage (cannot be used with -s)" 2337 | echo " If stage provided, skip to the specified stage" 2338 | echo " Available stages:" 2339 | echo " - discovery-hosts (TCP-only)" 2340 | echo " - discovery-ports (TCP-only)" 2341 | echo " - discovery-udp" 2342 | echo " - discovery-lists" 2343 | echo " - services-tcp" 2344 | echo " - services-udp" 2345 | echo " -s Start from the beginning (disables stage resuming but still saves stages for later resumption)" 2346 | echo '' 2347 | echo "AUXILIARY OPTIONS (Enable additional functionality):" 2348 | echo " --defaults Run z0e with default settings (overridden by explicitly provided options)" 2349 | echo " Default settings:" 2350 | echo " - Stage (-S/-s): Starts at initial alives scan" 2351 | echo " - Targets file (-t): ./targets.txt" 2352 | echo " - Output directory (-o): ./z0e-output" 2353 | echo " - Excluded targets (-x): None" 2354 | echo " - UDP scans (-U/-u): Enabled" 2355 | echo " --ngineer Enable entry of custom command options" 2356 | echo " --only Run only UDP scans (if enabled) and/or specified stage (does not apply to other options)" 2357 | echo " --count Count total IP addresses in the provided comma-separated file(s) and/or IPs (does not require sudo)" 2358 | echo " --geniplist " 2359 | echo " Generate a list of single IP addresses from the provided comma-separated file(s) and/or IPs" 2360 | echo " (does not require sudo)" 2361 | echo " --listwinhosts [OutputFile]" 2362 | echo " Parse a standard Nmap file (.nmap) to list IP addresses of Windows hosts (does not require sudo)" 2363 | echo " --parseports [OutputFileName]" 2364 | echo " Parse a grepable Nmap file (.gnmap) for hosts with specified open ports" 2365 | echo " and output results in a readable format (does not require sudo)" 2366 | echo " --listiphostnames [OutputFile]" 2367 | echo " Parse a standard Nmap file (.nmap) to list IP address and hostname pairs" 2368 | echo " (does not require sudo)" 2369 | echo " --ntfy [priority,]" 2370 | echo " Enable ntfy notifications (priority 1-5 optional, followed by server/topic URL)" 2371 | echo " --session Enable session functionality (provide a new or existing session name)" 2372 | echo " To resume a session, provide the session name with the -S option" 2373 | echo " --help Display this help message" 2374 | echo " --version Display the version of Zero-E" 2375 | exit 0 2376 | #Check for --version option (for troubleshooting purposes) 2377 | elif [ "$version_flag" = true ]; then 2378 | echo $version 2379 | exit 0 2380 | #Check for --count option 2381 | elif [ "$countopt" == true ]; then 2382 | mactools=("dos2unix") 2383 | linuxtools=("dos2unix") 2384 | checktools #Check if required tools are installed 2385 | echo "Counting total number of target hosts..." 2386 | totaltargets 2387 | echo "$total_hosts -- total number of host IP addresses in $countopt_arg" 2388 | exit 0 2389 | #Check for --geniplist option 2390 | elif [ "$geniplistopt" == true ]; then 2391 | mactools=("dos2unix") 2392 | linuxtools=("dos2unix") 2393 | checktools #Check if required tools are installed 2394 | checkfile="$geniplist_file" 2395 | # Create a temporary file to buffer geniplist's output 2396 | temp_output=$(mktemp) 2397 | geniplist > "$temp_output" & 2398 | GEN_PID=$! 2399 | status_msg="Generating list of single IP addresses..." 2400 | use_stderr=true 2401 | statusgeniplist "$GEN_PID" 2402 | wait "$GEN_PID" 2403 | # output the buffered IP addresses to stdout 2404 | cat "$temp_output" 2405 | rm "$temp_output" 2406 | exit 0 2407 | elif [ "$listwinhosts_flag" = true ]; then 2408 | genwindowshostlist "$1" "$2" 2409 | exit 0 2410 | #Check for --parseports option 2411 | elif [ "$parseports_flag" = true ]; then 2412 | parsegnmap "$1" "$2" "$3" 2413 | exit 0 2414 | elif [ "$listiphostnames_flag" = true ]; then 2415 | listiptohostname "$1" "$2" 2416 | exit 0 2417 | fi 2418 | 2419 | #Check sudo 2420 | if [[ "$EUID" -ne 0 ]]; then # && [[ "$arg" != "--help" || "$arg" != "--version" || "$arg" != "--count" || "$arg" != "--geniplist" || "$arg" != "--parseports" ]]; then 2421 | echo -e "\e[31m [X] Error: $(basename $0) requires sudo \e[0m" >&2 2422 | exit 1 2423 | fi 2424 | 2425 | #Check if post-sudo required tools are installed 2426 | if [ "$ntfy_flag" = true ]; then 2427 | mactools=("curl" "nmap" "masscan" "pfctl" "dos2unix" "realpath") 2428 | linuxtools=("curl" "nmap" "masscan" "iptables" "dos2unix" "realpath") 2429 | checktools 2430 | else 2431 | mactools=("nmap" "masscan" "pfctl" "dos2unix" "realpath") 2432 | linuxtools=("nmap" "masscan" "iptables" "dos2unix" "realpath") 2433 | checktools 2434 | fi 2435 | 2436 | #primary options 2437 | e_opt=false 2438 | i_opt=false 2439 | o_opt="" 2440 | t_opt="" 2441 | x_opt="" 2442 | U_opt=false 2443 | u_opt=false 2444 | s_opt=false 2445 | S_opt="disabled" 2446 | #Loop to parse options using getopts 2447 | while getopts ':eio:t:x:UusS:' opt "${remaining_args[@]}" 2>/dev/null; do 2448 | case "${opt}" in 2449 | e) 2450 | e_opt=true 2451 | ;; 2452 | i) 2453 | i_opt=true 2454 | ;; 2455 | o) 2456 | if [[ "$OPTARG" == -* ]]; then 2457 | echo -e "\e[31m [X] Error: -o requires a directory name or path \e[0m" >&2 2458 | exit 1 2459 | elif [ -f "$OPTARG" ]; then 2460 | echo -e "\e[31m [X] Error: File exists with the same name \e[0m" >&2 2461 | exit 1 2462 | elif [[ "$OPTARG" == *" "* ]]; then 2463 | echo -e "\e[31m [X] Error: To proactively avoid errors, whitespace is not allowed in directory names \e[0m" >&2 2464 | exit 1 2465 | elif [[ "$OPTARG" == "/dev/null" ]]; then 2466 | echo -e "\e[31m [X] Error: /dev/null cannot be used as an output directory \e[0m" >&2 2467 | exit 1 2468 | else 2469 | o_opt="$OPTARG" 2470 | fi 2471 | ;; 2472 | t) 2473 | ips_log="$OPTARG" 2474 | ip_list="$OPTARG" 2475 | # If OPTARG is a single file with no IP pattern, just use it 2476 | if [ -f "$OPTARG" ] && [ -s "$OPTARG" ] \ 2477 | && ! [[ "$OPTARG" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 2478 | && [[ "$OPTARG" != *","* ]]; then 2479 | t_opt="$OPTARG" 2480 | checkfile="$t_opt" 2481 | checkinvalidips 2482 | # Else if it’s comma-separated or looks like IP 2483 | elif { ! [ -f "$OPTARG" ] && [[ "$OPTARG" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; } \ 2484 | || [[ "$OPTARG" == *","* ]]; then 2485 | # Split on commas 2486 | IFS=',' read -ra ip_array <<< "$ip_list" 2487 | # Create two files: metadata and raw 2488 | metadata_file=$(mktemp /tmp/targets_single_ips.metadata.XXXXXX) 2489 | raw_file=$(mktemp /tmp/targets_single_ips.raw.XXXXXX) 2490 | anyerrors=false 2491 | for token in "${ip_array[@]}"; do 2492 | trimmed_token="$(echo "$token" | xargs)" 2493 | if [ -f "$trimmed_token" ]; then 2494 | # It's a file 2495 | if [ ! -s "$trimmed_token" ]; then 2496 | echo -e "\e[31m [X] Error: '$trimmed_token' is empty \e[0m" >&2 2497 | anyerrors=true 2498 | break 2499 | fi 2500 | # Add each line to metadata (with prefix) and raw (no prefix) 2501 | while IFS= read -r fileline; do 2502 | echo "__SRC:FILE:${trimmed_token}__ $fileline" >> "$metadata_file" 2503 | echo "$fileline" >> "$raw_file" 2504 | done < "$trimmed_token" 2505 | else 2506 | # It's an IP or range => prefix for metadata 2507 | echo "__SRC:INPUT__ $trimmed_token" >> "$metadata_file" 2508 | echo "$trimmed_token" >> "$raw_file" 2509 | fi 2510 | done 2511 | if $anyerrors; then 2512 | exit 1 2513 | fi 2514 | # Validate using the metadata file 2515 | checkfile="$metadata_file" 2516 | checkinvalidips 2517 | # If valid, set t_opt to the raw file (no metadata) 2518 | if [[ "$allvalid" -eq 1 ]]; then 2519 | t_opt="$raw_file" 2520 | else 2521 | exit 1 2522 | fi 2523 | else 2524 | # Otherwise, assume single file or error out 2525 | if [[ "$OPTARG" == -* ]] || [[ -z "$OPTARG" ]]; then 2526 | echo -e "\e[31m [X] Error: -t requires an existing file, IP address, or any combination in a comma-separated list \e[0m" >&2 2527 | exit 1 2528 | elif [[ "$OPTARG" == *" "* ]]; then 2529 | echo -e "\e[31m [X] Error: Whitespace is not allowed in file names \e[0m" >&2 2530 | exit 1 2531 | elif [ -f "$OPTARG" ]; then 2532 | if [ ! -s "$OPTARG" ]; then 2533 | echo -e "\e[31m [X] Error: $OPTARG is empty \e[0m" >&2 2534 | exit 1 2535 | fi 2536 | t_opt="$OPTARG" 2537 | checkfile="$t_opt" 2538 | checkinvalidips 2539 | else 2540 | echo -e "\e[31m [X] Error: -t requires an existing file, IP address, or any combination in a comma-separated list \e[0m" >&2 2541 | exit 1 2542 | fi 2543 | fi 2544 | ;; 2545 | x) 2546 | exc_log="$OPTARG" 2547 | # If -x is run with or without an argument 2548 | if [[ "${OPTARG:0:1}" == '-' ]]; then 2549 | # Move back one step for getopts so the next arg is re-read 2550 | OPTIND=$((OPTIND - 1)) 2551 | mkdir -p /var/lib/zeroe 2552 | touch /var/lib/zeroe/nullexcludes.z0e 2553 | x_opt="/var/lib/zeroe/nullexcludes.z0e" 2554 | # If OPTARG looks like a single, existing, non-empty file with no IP pattern, just use it 2555 | elif [ -f "$OPTARG" ] && [ -s "$OPTARG" ] \ 2556 | && ! [[ "$OPTARG" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]] \ 2557 | && [[ "$OPTARG" != *","* ]] 2558 | then 2559 | x_opt="$OPTARG" 2560 | checkfile="$x_opt" 2561 | checkinvalidips 2562 | # If OPTARG either contains commas or IP octet => treat as comma-separated 2563 | elif { ! [ -f "$OPTARG" ] && [[ "$OPTARG" =~ [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+ ]]; } \ 2564 | || [[ "$OPTARG" == *","* ]] 2565 | then 2566 | ip_list="$OPTARG" 2567 | IFS=',' read -ra ip_array <<< "$ip_list" 2568 | # Two files: metadata + raw 2569 | metadata_file=$(mktemp /tmp/excludes_single_ips.metadata.XXXXXX) 2570 | raw_file=$(mktemp /tmp/excludes_single_ips.raw.XXXXXX) 2571 | anyerrors=false 2572 | for token in "${ip_array[@]}"; do 2573 | trimmed_ip="$(echo "$token" | xargs)" 2574 | # If it's a file, merge its lines 2575 | if [ -f "$trimmed_ip" ]; then 2576 | if [ ! -s "$trimmed_ip" ]; then 2577 | echo -e "\e[31m [X] Error: '$trimmed_ip' is empty \e[0m" >&2 2578 | anyerrors=true 2579 | break 2580 | fi 2581 | while IFS= read -r line; do 2582 | echo "__SRC:FILE:${trimmed_ip}__ $line" >> "$metadata_file" 2583 | echo "$line" >> "$raw_file" 2584 | done < "$trimmed_ip" 2585 | else 2586 | # It's an IP or range 2587 | echo "__SRC:INPUT__ $trimmed_ip" >> "$metadata_file" 2588 | echo "$trimmed_ip" >> "$raw_file" 2589 | fi 2590 | done 2591 | if $anyerrors; then 2592 | exit 1 2593 | fi 2594 | # Validate the metadata file 2595 | checkfile="$metadata_file" 2596 | checkinvalidips 2597 | # If valid, set x_opt to the raw file 2598 | if [[ "$allvalid" -eq 1 ]]; then 2599 | x_opt="$raw_file" 2600 | else 2601 | exit 1 2602 | fi 2603 | else 2604 | # Catch-all for invalid usage 2605 | if [[ "$OPTARG" == -* ]] || [[ -z "$OPTARG" ]]; then 2606 | echo -e "\e[31m [X] Error: -x requires an existing file, IP address, or any combination in a comma-separated list \e[0m" >&2 2607 | exit 1 2608 | elif [[ "$OPTARG" == *" "* ]]; then 2609 | echo -e "\e[31m [X] Error: To proactively avoid errors, whitespace is not allowed in file names \e[0m" >&2 2610 | exit 1 2611 | elif [ -f "$OPTARG" ]; then 2612 | if [ ! -s "$OPTARG" ]; then 2613 | echo -e "\e[31m [X] Error: $OPTARG is empty \e[0m" >&2 2614 | exit 1 2615 | fi 2616 | x_opt="$OPTARG" 2617 | checkfile="$x_opt" 2618 | checkinvalidips 2619 | else 2620 | echo -e "\e[31m [X] Error: -x requires an existing file, IP address, or any combination in a comma-separated list \e[0m" >&2 2621 | exit 1 2622 | fi 2623 | fi 2624 | # Mark that -x was provided 2625 | x_opt_provided="y" 2626 | ;; 2627 | U) 2628 | U_opt=true 2629 | ;; 2630 | u) 2631 | u_opt=true 2632 | ;; 2633 | s) 2634 | s_opt=true 2635 | ;; 2636 | S) 2637 | if [[ "${OPTARG:0:1}" == '-' ]]; then # Allows -S to be run with or without an argument 2638 | OPTIND=$((OPTIND - 1)) 2639 | S_opt="" 2640 | else 2641 | S_opt="$OPTARG" 2642 | fi 2643 | ;; 2644 | :) #For when -S or -x is passed without an argument 2645 | if [[ ${OPTARG} == "S" ]]; then 2646 | S_opt="" 2647 | elif [[ ${OPTARG} == "x" ]]; then 2648 | mkdir -p /var/lib/zeroe 2649 | touch /var/lib/zeroe/nullexcludes.z0e 2650 | x_opt="/var/lib/zeroe/nullexcludes.z0e" 2651 | fi 2652 | ;; 2653 | \?) 2654 | echo -e "\e[31m [X] Error: invalid option "\`-$OPTARG\`" -- valid options are [-e || -i] [-o] [-t] [-U || -u] [-S || -s] [-x] [--help] [--defaults] [--ngineer] [--only] [--count] [--geniplist] [--listwinhosts] [--parseports] [--listiphostnames] [--ntfy] [--session] \e[0m" >&2 2655 | exit 1 2656 | ;; 2657 | esac 2658 | done 2659 | #Check if the last option was -o or -t and if the arg is missing. 2660 | last_arg="${@: -1}" 2661 | if [[ "$last_arg" == "-o" ]] && [[ -z "$o_opt" ]]; then 2662 | echo -e "\e[31m [X] Error: -o requires a directory name or path \e[0m" >&2 2663 | exit 1 2664 | elif [[ "$last_arg" == "-t" ]] && [[ -z "$t_opt" ]]; then 2665 | echo -e "\e[31m [X] Error: -t requires an existing file, IP address, or any combination in a comma-separated list \e[0m" >&2 2666 | exit 1 2667 | fi 2668 | #Check if both -e and -i options are used 2669 | if [ "$e_opt" = true ] && [ "$i_opt" = true ]; then 2670 | echo -e "\e[31m [X] Error: You seem confused, script kiddie... -e and -i options cannot be used together \e[0m" >&2 2671 | exit 1 2672 | fi 2673 | #Check if both -U and -u options are used 2674 | if [ "$U_opt" = true ] && [ "$u_opt" = true ]; then 2675 | echo -e "\e[31m [X] Error: You seem confused, script kiddie... -U and -u options used together doesn't make sense \e[0m" >&2 2676 | exit 1 2677 | fi 2678 | #Check if both -s and -S options are used 2679 | if [ "$s_opt" = true ] && [ "$S_opt" != "disabled" ]; then 2680 | echo -e "\e[31m [X] Error: You seem confused, script kiddie... -s disables stage resuming and selection \e[0m" >&2 2681 | exit 1 2682 | fi 2683 | #Check if both -t and -x are the same file 2684 | if [ "$t_opt" = "$x_opt" ] && [ -n "$t_opt" ] && [ -n "$x_opt" ]; then 2685 | echo -e "\e[31m [X] Error: You must be perplexed, script kiddie... targets and excludes cannot be the same file \e[0m" >&2 2686 | exit 1 2687 | fi 2688 | #Validate ntfy argument 2689 | ntfy 2690 | 2691 | #Set the stage to start at 2692 | sessioninit 2693 | stageinit 2694 | # Determine file paths based on session 2695 | if [[ "$session_flag" = true && -n "$session" ]]; then 2696 | stage_file="/var/lib/zeroe/sessions/$session/stage.z0e" 2697 | vars_file="/var/lib/zeroe/sessions/$session/vars.z0e" 2698 | initdir_file="/var/lib/zeroe/sessions/$session/initdir.z0e" 2699 | else 2700 | stage_file="/var/lib/zeroe/stage.z0e" 2701 | vars_file="/var/lib/zeroe/vars.z0e" 2702 | initdir_file="/var/lib/zeroe/initdir.z0e" 2703 | fi 2704 | # Final checks and functions 2705 | if [ -f "$stage_file" ] && [ -f "$vars_file" ] && [[ "$resume" = "y" ]]; then #If successfully resuming saved stage... 2706 | if [[ "$(cat "$initdir_file")" = "$(pwd)" ]]; then #check if the current and previous working dir are equal, then... 2707 | : #silently continue to... 2708 | else 2709 | cd "$(cat "$initdir_file")" || { echo -e "\e[31m [X] Error: Cannot change to previous working directory -- exiting Zero-E \e[0m" >&2; exit; } #change dirs to the previous working dir (or exit if cannot) and... 2710 | fi 2711 | #parse options to resume scans 2712 | while IFS='=' read -r key rest; do # The value is everything after the first '=', preserving internal quotes and spaces 2713 | value="${rest#\"}" # Remove leading quote 2714 | value="${value%\"}" # Remove trailing quote 2715 | eval "$key=\"$value\"" # Use eval to correctly handle complex values, ensuring to escape as needed 2716 | done < "$vars_file" 2717 | else #Starting new scan or from specific stage 2718 | if [ ! -f "$vars_file" ] && [[ "$resume" = "y" ]]; then #If choosing resuming without saved options 2719 | echo -e "\e[31m [X] Error: No saved options exist -- configure new scan \e[0m" >&2 2720 | S_opt='disabled' 2721 | resume='' 2722 | while true; do 2723 | stageinit 2724 | if [[ "$resume" = "y" ]]; then 2725 | echo -e "\e[31m [X] Error: New scan configuration required -- resuming without saved options will cause errors \e[0m" >&2 2726 | S_opt='disabled' 2727 | resume='' 2728 | else 2729 | break 2730 | fi 2731 | done 2732 | fi 2733 | #Set external or internal 2734 | settype 2735 | #Enable or disable UDP scans 2736 | enableudp 2737 | #Set the generated file output directory 2738 | output 2739 | #Set the target IPs file 2740 | targets 2741 | #Calculate total number of target hosts 2742 | echo -ne " Counting total number of target hosts..." 2743 | totaltargets 2744 | echo -e "\r Counting total number of target hosts... $total_hosts" 2745 | #Set the excluded IPs file 2746 | excludes 2747 | #Check if targets and excludes are the same and repeat loop while they are 2748 | if cmp -s "$ips" "$nostrikes"; then 2749 | echo -e "\e[31m [X] Error: You seem confused, script kiddie... all of the targets are excluded \e[0m" >&2 2750 | ips="" 2751 | t_opt="" 2752 | nostrikes="" 2753 | x_opt="" 2754 | while true; do 2755 | targets 2756 | echo -ne " Counting total number of target hosts..." 2757 | totaltargets 2758 | echo -e "\r Counting total number of target hosts... $total_hosts" 2759 | excludes 2760 | if cmp -s "$ips" "$nostrikes"; then 2761 | echo -e "\e[31m [X] Error: You must be perplexed, script kiddie... all of the targets are still excluded \e[0m" >&2 2762 | ips="" 2763 | t_opt="" 2764 | nostrikes="" 2765 | x_opt="" 2766 | else 2767 | break 2768 | fi 2769 | done 2770 | fi 2771 | fi 2772 | stagefilescheck 2773 | #if --only is not applicable 2774 | if [[ "$only_flag" == true ]] && [[ "$u_opt" == true ]] && [[ "$U_opt" == false ]] && [[ "$S_opt" == "disabled" ]]; then 2775 | only_flag=false 2776 | fi 2777 | #If ngineer mode 2778 | if [[ "$ngineer_mode" == true && "$resume" != "y" ]]; then 2779 | z0engineer 2780 | fi 2781 | #Save options to file for resuming stage 2782 | if [[ "$resume" != "y" ]]; then 2783 | echo "e_opt=\"$e_opt\" i_opt=\"$i_opt\" filepath=\"$filepath\" ips=\"$ips\" nostrikes=\"$nostrikes\" U_opt=\"$U_opt\" u_opt=\"$u_opt\" total_hosts=\"$total_hosts\" typevar=\"$typevar\" only_flag=\"$only_flag\" stage_cont=\"$stage_cont\"" | tee "$vars_file" > /dev/null 2784 | echo "$stage" > "$stage_file" 2785 | logconfig "$vars_file" > $filepath/logs/$typevar-config.txt 2786 | #Save ngineer options 2787 | if [[ "$ngineer_mode" == true ]]; then 2788 | echo "ngineer_mode=\"$ngineer_mode\" ngineer_ports_default=\"$ngineer_ports_default\" ngineer_udpa_default=\"$ngineer_udpa_default\" ngineer_tcps_default=\"$ngineer_tcps_default\" ngineer_udps_default=\"$ngineer_udps_default\" z0eng_ports_opts=\"$z0eng_ports_opts\" z0eng_udpa_opts=\"$z0eng_udpa_opts\" z0eng_tcps_opts=\"$z0eng_tcps_opts\" z0eng_udps_opts=\"$z0eng_udps_opts\"" >> "$vars_file" 2789 | fi 2790 | fi 2791 | 2792 | #cd to the output dir for better masscan resuming 2793 | cd $filepath 2794 | #Sets trap for when script stops before finishing 2795 | trap whenkilled INT TERM 2796 | 2797 | #Start scans 2798 | echo -e "\e[35m \n [=] Zero-E started -- progress updates for scans displayed every 15 minutes \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2799 | z0e_started=true 2800 | #================================== 2801 | #=============EXTERNAL============= 2802 | #================================== 2803 | if [ "$e_opt" = true ] || [ "$type" = "E" ] || [ "$type" = "e" ] || [ "$type" = "external" ] || [ "$type" = "External" ] || [ "$type" = "Ext" ] || [ "$type" = "ext" ]; then 2804 | 2805 | #Stage -- start 2806 | if { [[ "$stage" == "discovery-hosts" ]] || [[ "$stage" == "script-start" ]]; } && ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 2807 | echo "discovery-hosts" > "$stage_file" 2808 | stage="discovery-hosts" 2809 | 2810 | if [[ "$stage" == "discovery-hosts" ]] && [[ "$resume" = "y" ]]; then 2811 | echo -e "\e[36m [-] Resuming alive host discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2812 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2813 | else 2814 | echo -e "\e[36m [-] Starting discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2815 | fi 2816 | 2817 | ###Nmap alive host discovery 2818 | echo -ne "\e[36m [-] Discovering alive hosts with Nmap... \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2819 | echo "" 2820 | ntdscan="nmap -n -vv -sn -oG - --excludefile $nostrikes -iL $ips" #Stored as variable for report generation 2821 | echo "ntdscan=\"$ntdscan\"" >> "$vars_file" 2822 | if [[ "$resume" = "y" ]]; then 2823 | resume='' 2824 | nmap --resume "$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" 1>>"$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" 2>>"$filepath/logs/$typevar-errors.log" & 2825 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2826 | else 2827 | #if [[ "$ngineer_mode" == true ]]; then 2828 | # if [[ "$ngineer_default" == true ]]; then 2829 | # eval "$ntdscan" 1>>"$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" 2>>"$filepath/logs/$typevar-errors.log" & #TCP ping scan 2830 | # else 2831 | # eval "$z0eng_ext_alives - -oG --excludefile $nostrikes -iL $ips" 1>>"$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" 2>>"$filepath/logs/$typevar-errors.log" & 2832 | # fi 2833 | #else 2834 | eval "$ntdscan" 1>>"$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" 2>>"$filepath/logs/$typevar-errors.log" & 2835 | #fi 2836 | fi 2837 | #Status indicator 2838 | pid=$! 2839 | periodicfile="$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" 2840 | contstatus="Pinging hosts" 2841 | statusnmap 2842 | #Error check and alert 2843 | checked_cmd="$ntdscan" 2844 | wait $pid 2845 | exitstatus=$? 2846 | errorcheck 2847 | 2848 | status_msg="[+] Discovering alive hosts with Nmap... Done" 2849 | printf "\033[A\r\e[K\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2850 | ntfy "$status_msg" 2851 | #Generate alives file 2852 | cat "$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" | grep 'Up' | awk '{print $2}' | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n > $filepath/$typevar-alives.txt 2853 | if [[ "$stage" == "discovery-hosts" ]] && [[ "$only_flag" == true ]]; then 2854 | if grep -q "\S" "$filepath/$typevar-alives.txt"; then 2855 | echo "" | tee -a "$filepath/logs/$typevar-timestamps.log" 2856 | status_msg="[+] Alive hosts discovered -- $(date)" 2857 | echo -e "\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2858 | ntfy "$status_msg" 2859 | else 2860 | status_msg="[!] No alive hosts detected" 2861 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2862 | ntfy "$status_msg" 2863 | fi 2864 | fi 2865 | #Stage update 2866 | if [[ "$only_flag" != true && "$stage_cont" == true ]]; then 2867 | echo "discovery-ports" > "$stage_file" 2868 | stage="discovery-ports" 2869 | fi 2870 | fi 2871 | 2872 | #Stage -- start 2873 | if [[ "$stage" == "discovery-ports" ]] && ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 2874 | #Masscan open port/alive host discovery 2875 | #################################################################### 2876 | ####### External masscan command if adjustment is necessary ######## 2877 | emscan="sudo masscan --open-only -p 1-65535 --rate=5000 --excludefile $nostrikes --include-file $ips -oG $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp.txt" 2878 | echo "emscan=\"$emscan\"" >> "$vars_file" 2879 | ### Stored as variable to correctly reflect in report if changed ### 2880 | #################################################################### 2881 | #^^^If scans are taking too long, remove -p 1-65535 and use --top-ports=32768 2882 | if [[ "$stage" == "discovery-ports" ]] && [[ -f "$(pwd)/paused.conf" ]] && [[ "$resume" = "y" ]]; then 2883 | resume='' 2884 | echo -e "\e[36m [-] Resuming discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2885 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2886 | masscan --resume paused.conf >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 2887 | else 2888 | if [[ "$ngineer_mode" == true ]]; then 2889 | if [[ "$ngineer_ports_default" == true ]]; then 2890 | eval "$emscan" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 2891 | else 2892 | echo -e "\e[33m [!] Using z0e ngineer options for masscan discovery scan \e[0m" 2893 | eval "masscan $z0eng_ports_opts --open-only --excludefile $nostrikes --include-file $ips -oG $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp.txt" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 2894 | fi 2895 | else 2896 | eval "$emscan" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 2897 | fi 2898 | fi 2899 | pid=$! 2900 | sleep 4 2901 | if [[ "$only_flag" != true ]]; then #Prevents unwanted newline in terminal output 2902 | if ! tail -n 1 "$filepath/logs/$typevar-timestamps.log" | grep -q 'Zero-E started'; then 2903 | echo "" | tee -a "$filepath/logs/$typevar-timestamps.log" 2904 | fi 2905 | fi 2906 | echo -ne "\e[36m [-] Discovering alive hosts and open TCP ports with Masscan... $(grep -o '[0-9]\+:[0-9]\+:[0-9]\+ remaining' "$filepath/logs/misc-files/$typevar-masscan-tcp.log" | tail -1) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2907 | echo "" 2908 | #Status indicator 2909 | periodicfile="$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2910 | contstatus="Scanning TCP ports" 2911 | statusmasscan 2912 | #Error check and alert 2913 | checked_cmd="$emscan" 2914 | wait $pid 2915 | exitstatus=$? 2916 | errorcheck 2917 | 2918 | status_msg="[+] Discovering alive hosts and open TCP ports with Masscan... Done" 2919 | printf "\033[A\r\e[K\e[32m $status_msg \e[0m\n" | tee -a "$filepath/logs/$typevar-timestamps.log" 2920 | ntfy "$status_msg" 2921 | #Filter out hosts with more than 100 ports open 2922 | susinput="$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp.txt" 2923 | susips="$filepath/$typevar-100port-hosts-tcp.txt" 2924 | susoutput="$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt" 2925 | filtersusips 2>>"$filepath/logs/$typevar-errors.log" 2926 | 2927 | #Carve out Nmap TCP IP addresses and put them into a file 2928 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 2929 | awk 'NR==FNR{ips[$0];next} {for (ip in ips) if ($0 !~ ip) print}' $filepath/$typevar-100port-hosts-tcp.txt "$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" > $filepath/logs/misc-files/$typevar-discoscan-nmap-nosusips.txt #Filter out hosts with more than 100 open tcp ports from the nmap ping scan results 2930 | cat $filepath/logs/misc-files/$typevar-discoscan-nmap-nosusips.txt | grep 'Up' | awk '{print $2}' >> $filepath/logs/misc-files/$typevar-discoresults.txt 2931 | else 2932 | cat "$filepath/logs/misc-files/$typevar-discoscan-nmap.gnmap" | grep 'Up' | awk '{print $2}' >> $filepath/logs/misc-files/$typevar-discoresults.txt 2933 | fi 2934 | 2935 | #Carve out Masscan TCP IP addresses and put them into a file 2936 | { cat $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt | grep 'Host' | awk '{print $4}' ; } >> $filepath/logs/misc-files/$typevar-discoresults.txt # this excludes 100port-hosts. {;} groups the piped commands so all output is redirected 2937 | 2938 | #Generate list of alive hosts 2939 | cat $filepath/logs/misc-files/$typevar-discoresults.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n >> $filepath/$typevar-alives.txt 2940 | cp $filepath/$typevar-alives.txt $filepath/$typevar-alives-tmp.txt 2941 | cat $filepath/$typevar-alives-tmp.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n > $filepath/$typevar-alives.txt 2942 | rm $filepath/$typevar-alives-tmp.txt 2943 | 2944 | #Stage -- update 2945 | if [ "$udp" = "y" ] || [ "$udp" = "yes" ] || [ "$U_opt" = true ]; then 2946 | echo "discovery-udp" > "$stage_file" 2947 | stage="discovery-udp" 2948 | elif [[ "$stage_cont" == true ]] || { [[ "$stage" == "discovery-ports" ]] && [[ "$only_flag" == true ]]; }; then 2949 | echo "discovery-lists" > "$stage_file" 2950 | stage="discovery-lists" 2951 | fi 2952 | 2953 | fi 2954 | 2955 | #Stage -- start 2956 | if { [[ "$stage" == "discovery-udp" ]] && [[ "$udp" = "y" || "$udp" = "yes" || "$U_opt" = true ]]; } || { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]] && [[ "$stage" != "discovery-lists" && "$stage" != "services-udp" ]]; }; then 2957 | #UDP open port scan 2958 | #15094 top ports is 99% effective. Reference this chart for --top-port number effectiveness: https://nmap.org/book/performance-port-selection.html 2959 | nudpa="nmap -v -Pn -sU --open --min-rate 1000 --max-rate 3000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" --excludefile $nostrikes -iL $ips -d" 2960 | if [[ "$resume" = "y" ]]; then 2961 | resume='' 2962 | echo -e "\e[36m [-] Resuming UDP discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2963 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2964 | nmap --resume "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>>"$filepath/logs/$typevar-errors.log" & 2965 | else 2966 | if [[ "$ngineer_mode" == true ]]; then 2967 | if [[ "$ngineer_udpa_default" == true ]]; then 2968 | eval "$nudpa" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>>"$filepath/logs/$typevar-errors.log" & 2969 | else 2970 | echo -e "\e[33m [!] Using z0e ngineer options for Nmap UDP discovery scan \e[0m" 2971 | eval "nmap -sU --open $z0eng_udpa_opts -oG "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" --excludefile $nostrikes -iL $ips -d" >> "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log" 2>>"$filepath/logs/$typevar-errors.log" & 2972 | fi 2973 | else 2974 | eval "$nudpa" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>>"$filepath/logs/$typevar-errors.log" & 2975 | fi 2976 | fi 2977 | pid=$! 2978 | echo -ne "\e[36m [-] Discovering alive hosts and open UDP ports with Nmap... \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 2979 | echo "" 2980 | #Status indicator 2981 | periodicfile="$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log" 2982 | contstatus="Scanning UDP ports" 2983 | statusnmap 2984 | #Error check and alert 2985 | checked_cmd="nmap -v -Pn -sU --open --min-rate 1000 --max-rate 3000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" --excludefile $nostrikes -iL $ips -d" 2986 | wait $pid 2987 | exitstatus=$? 2988 | errorcheck 2989 | 2990 | status_msg="[+] Discovering alive hosts and open UDP ports with Nmap... Done" 2991 | printf "\033[A\r\e[K\e[32m $status_msg \e[0m\n" | tee -a "$filepath/logs/$typevar-timestamps.log" 2992 | ntfy "$status_msg" 2993 | #Carve out UDP IP addresses and put them into a file 2994 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 2995 | awk 'NR==FNR{ips[$0];next} {for (ip in ips) if ($0 !~ ip) print}' $filepath/$typevar-100port-hosts-tcp.txt "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" > $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt #Filter out hosts with more than 100 open tcp ports 2996 | cat $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt | grep '/open' | cut -d ' ' -f2 >> $filepath/logs/misc-files/$typevar-discoresults.txt 2997 | else 2998 | cat "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" | grep '/open' | cut -d ' ' -f2 >> $filepath/logs/misc-files/$typevar-discoresults.txt 2999 | fi 3000 | 3001 | #Stage -- update 3002 | echo "discovery-lists" > "$stage_file" 3003 | stage="discovery-lists" 3004 | fi 3005 | 3006 | #Stage -- start 3007 | if [[ "$stage" == "discovery-lists" ]]; then 3008 | #Make final list of ordered, unique alive hosts excluding sus ips 3009 | cat $filepath/logs/misc-files/$typevar-discoresults.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n >> $filepath/$typevar-alives.txt 3010 | cp $filepath/$typevar-alives.txt $filepath/$typevar-alives-tmp.txt 3011 | cat $filepath/$typevar-alives-tmp.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n > $filepath/$typevar-alives.txt 3012 | rm $filepath/$typevar-alives-tmp.txt 3013 | 3014 | #Generate list of all open ports 3015 | if ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 3016 | cat $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt | grep 'open' | cut -d ' ' -f5 | cut -d '/' -f1 | sort -u >> $filepath/rangetemp.txt 3017 | #continue with ports list 3018 | rangeout="$filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt" 3019 | singleportstorange 3020 | cat $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt > $filepath/$typevar-openPorts.txt 3021 | sed -i 's/$/\/TCP/' "$filepath/$typevar-openPorts.txt" 3022 | fi 3023 | if [ "$udp" = "y" ] || [ "$udp" = "yes" ] || [ "$U_opt" = true ]; then 3024 | if [[ "$only_flag" == true ]]; then 3025 | rm $filepath/$typevar-openPorts.txt 2>/dev/null 3026 | fi 3027 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 3028 | cat $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt | grep '/open' | cut -d ' ' -f4 | cut -d '/' -f1 | sort -u >> $filepath/rangetempu.txt 3029 | else 3030 | cat "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" | grep '/open' | cut -d ' ' -f4 | cut -d '/' -f1 | sort -u >> $filepath/rangetempu.txt 3031 | fi 3032 | sort -u $filepath/rangetempu.txt >> $filepath/rangetemp.txt 3033 | rm $filepath/rangetempu.txt 3034 | rangeout="$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt" 3035 | singleportstorange 3036 | cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt >> $filepath/$typevar-openPorts.txt 3037 | sed -i '/\/TCP$/! s/$/\/UDP/' "$filepath/$typevar-openPorts.txt" 3038 | fi 3039 | nessusports 2>/dev/null 3040 | 3041 | #Status update 3042 | alivesandportscheck 3043 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ] && [ -s "$filepath/$typevar-100port-hosts-udp.txt" ]; then 3044 | echo -e "\e[33m [!] Excluding potential deception or firewall-protected hosts showing more than 100 open TCP/UDP ports -- recommend inquiring about hosts in ext-100port-hosts-tcp.txt and ext-100port-hosts-udp.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3045 | elif [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 3046 | echo -e "\e[33m [!] Excluding potential deception or firewall-protected hosts showing more than 100 open TCP ports -- recommend inquiring about hosts in ext-100port-hosts-tcp.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3047 | elif [ -s "$filepath/$typevar-100port-hosts-udp.txt" ]; then 3048 | echo -e "\e[33m [!] Excluding potential deception or firewall-protected hosts showing more than 100 open UDP ports -- recommend inquiring about hosts in ext-100port-hosts-udp.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3049 | fi 3050 | echo -e "\e[33m [!] Generated files for Nessus vulnerability scans -- Hosts: $typevar-alives.txt | Ports: $typevar-portsForNessus.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3051 | 3052 | #Stage -- update 3053 | if [[ "$only_flag" != true && "$stage_cont" == true ]]; then 3054 | echo "services-tcp" > "$stage_file" 3055 | stage="services-tcp" 3056 | elif { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]] && [[ "$stage_cont" == true ]]; }; then 3057 | echo "services-udp" > "$stage_file" 3058 | stage="services-udp" 3059 | fi 3060 | 3061 | fi 3062 | 3063 | #Stage -- start 3064 | if [[ "$stage" == "services-tcp" ]]; then 3065 | ##Nmap TCP service scans 3066 | entscan="nmap -sC -sV -Pn -O -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt | paste -sd "," -) --open --reason --excludefile $nostrikes -iL $filepath/$typevar-alives.txt -oA $filepath/$typevar-tcp-servicescan-results" 3067 | echo "entscan=\"$entscan\"" >> "$vars_file" 3068 | if grep -q "\S" "$filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt"; then 3069 | if [[ "$resume" = "y" ]]; then 3070 | resume='' 3071 | echo -e "\e[36m [-] Resuming TCP service scans \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3072 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3073 | nmap --resume $filepath/$typevar-tcp-servicescan-results.gnmap 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3074 | else 3075 | if [[ "$ngineer_mode" == true ]]; then 3076 | if [[ "$ngineer_tcps_default" == true ]]; then 3077 | eval "$entscan -v" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3078 | else 3079 | echo -e "\e[33m [!] Using z0e ngineer options for Nmap TCP service scan \e[0m" 3080 | eval "nmap $z0eng_tcps_opts -sV -Pn -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt | paste -sd "," -) -oA $filepath/$typevar-tcp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3081 | fi 3082 | else 3083 | eval "$entscan -v" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3084 | fi 3085 | fi 3086 | pid=$! 3087 | echo -e "\e[36m [-] Scanning services on open TCP ports of alive hosts with Nmap -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3088 | #Status indicator 3089 | periodicfile="$filepath/$typevar-tcp-servicescan-results.nmap" 3090 | contstatus="Scanning TCP ports" 3091 | statusnmap 3092 | #Error check and alert 3093 | checked_cmd="$entscan" 3094 | wait $pid 3095 | exitstatus=$? 3096 | printf "\r%-${#indicator}s\r" "" #Clears status indicator line 3097 | if [ "$exitstatus" -eq 0 ]; then 3098 | status_msg="[+] Nmap TCP service scan complete, results saved as $typevar-tcp-servicescan-results -- $(date)" 3099 | echo -e "\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3100 | ntfy "$status_msg" 3101 | else 3102 | errorcheck 3103 | if [ "$ntfy_flag" = true ]; then 3104 | ntfy "[!] Nmap TCP service scan failed -- Check errors.log" 3105 | fi 3106 | fi 3107 | 3108 | genwindowshostlist_inscript 3109 | listiptohostname_inscript 3110 | parsegnmap_inscript=true 3111 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 139,445,137,138 "$filepath/analysis/$typevar-smbHosts.txt" 3112 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 80,443,8080,8443,8000,8008,8888 "$filepath/analysis/$typevar-httpHosts.txt" 3113 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 22,23,3389,5985,5986,5900,5800,1494,5631,5632 "$filepath/analysis/$typevar-remoteAccessHosts.txt" 3114 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 1723,1194 "$filepath/analysis/$typevar-vpnHosts.txt" 3115 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 20,21,22,69,111,873,990,2049,3260 "$filepath/analysis/$typevar-fileshareHosts.txt" 3116 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 3306,5432,1433,1521 "$filepath/analysis/$typevar-databaseHosts.txt" 3117 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 25,465,587,143,993,110,995 "$filepath/analysis/$typevar-emailHosts.txt" 3118 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 389,636 "$filepath/analysis/$typevar-ldapHosts.txt" 3119 | else 3120 | status_msg="[!] No open TCP ports detected -- TCP service scans skipped" 3121 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3122 | ntfy "$status_msg" 3123 | fi 3124 | 3125 | #Stage -- update 3126 | if { [ "$udp" = "y" ] || [ "$udp" = "yes" ] || [ "$U_opt" = true ]; } && [[ "$stage_cont" == true ]]; then 3127 | echo "services-udp" > "$stage_file" 3128 | stage="services-udp" 3129 | fi 3130 | 3131 | fi 3132 | 3133 | #Stage -- start 3134 | if [[ "$stage" == "services-udp" ]] && [[ "$udp" = "y" || "$udp" = "yes" || "$U_opt" = true ]]; then 3135 | #Nmap UDP service scans 3136 | nsudp="nmap -v -sU -Pn -sV --open --min-rate 1000 --max-rate 3000 --reason -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | paste -sd "," -) -oA $filepath/$typevar-udp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 3137 | if grep -q "\S" "$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt"; then 3138 | if [[ "$resume" = "y" ]]; then 3139 | resume='' 3140 | echo -e "\e[36m [-] Resuming UDP service scans \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3141 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3142 | nmap --resume $filepath/$typevar-udp-servicescan-results.gnmap 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3143 | else 3144 | if [[ "$ngineer_mode" == true ]]; then 3145 | if [[ "$ngineer_udps_default" == true ]]; then 3146 | eval "$nsudp" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3147 | else 3148 | echo -e "\e[33m [!] Using z0e ngineer options for Nmap UDP service scan \e[0m" 3149 | eval "nmap $z0eng_udps_opts -sU -sV -Pn --reason -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | paste -sd "," -) -oA $filepath/$typevar-udp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3150 | fi 3151 | else 3152 | eval "$nsudp -v" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3153 | fi 3154 | fi 3155 | pid=$! 3156 | echo -e "\e[36m [-] Scanning services on open UDP ports of alive hosts with Nmap -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3157 | #Status indicator 3158 | periodicfile="$filepath/$typevar-udp-servicescan-results.nmap" 3159 | contstatus="Scanning UDP ports" 3160 | statusnmap 3161 | #Error check and alert 3162 | checked_cmd="nmap -v -sU -Pn -sV --open --min-rate 1000 --max-rate 3000 --reason -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | paste -sd "," -) -oA $filepath/$typevar-udp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt 1>/dev/null 2>>$filepath/logs/$typevar-errors.log &" 3163 | wait $pid 3164 | exitstatus=$? 3165 | printf "\r%-${#indicator}s\r" "" #Clears status indicator line 3166 | if [ "$exitstatus" -eq 0 ]; then 3167 | status_msg="[+] Nmap UDP service scan complete, results saved as $typevar-udp-servicescan-results -- $(date)" 3168 | echo -e "\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3169 | ntfy "$status_msg" 3170 | else 3171 | errorcheck 3172 | if [ "$ntfy_flag" = true ]; then 3173 | ntfy "[!] Nmap UDP service scan failed -- Check errors.log" 3174 | fi 3175 | fi 3176 | 3177 | genwindowshostlist_inscript 3178 | listiptohostname_inscript 3179 | else 3180 | status_msg="[!] No open UDP ports detected -- UDP service scans skipped" 3181 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3182 | ntfy "$status_msg" 3183 | fi 3184 | fi 3185 | 3186 | status_msg="[=] Zero-E completed -- happy hacking!" 3187 | echo -e "\e[35m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3188 | ntfy "$status_msg" 3189 | z0ecleanup 3190 | fi 3191 | #================================ 3192 | #===========INTERNAL============= 3193 | #================================ 3194 | if [ "$i_opt" = true ] || [ "$type" = "I" ] || [ "$type" = "i" ] || [ "$type" = "internal" ] || [ "$type" = "Internal" ] || [ "$type" = "Int" ] || [ "$type" = "int" ]; then 3195 | 3196 | #Stage -- start 3197 | if { [[ "$stage" == "discovery-hosts" ]] || [[ "$stage" == "script-start" ]]; } && ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 3198 | echo "discovery-hosts" > "$stage_file" 3199 | stage="discovery-hosts" 3200 | 3201 | #cidrconvert >> $filepath/logs/$typevar-fpingcidrs.txt 3202 | if [[ "$stage" == "discovery-hosts" ]] && [[ "$resume" = "y" ]]; then 3203 | resume="y" 3204 | echo -e "\e[36m [-] Resuming alive host discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3205 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3206 | else 3207 | echo -e "\e[36m [-] Starting discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3208 | fi 3209 | #Prevent system from sending RST packets by setting the firewall to block packets returning to the Masscan origin port 3210 | setfirewallrule 3211 | 3212 | #Masscan alive host discovery 3213 | alivesmscan="sudo masscan --rate=8000 --src-port=55555 --excludefile $nostrikes --include-file $ips -oG $filepath/logs/misc-files/$typevar-masscanalives-results.txt" 3214 | if [[ "$stage" == "discovery-hosts" ]] && [[ -f "$(pwd)/paused.conf" ]] && [[ "$resume" = "y" ]]; then 3215 | resume='' 3216 | { masscan --resume paused.conf >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & } && sleep 2 3217 | elif [[ "$stage" == "discovery-hosts" ]] || [[ "$stage" == "discovery-hosts" && ! -f "$(pwd)/paused.conf" && "$resume" = "y" ]]; then 3218 | if [[ "$stage" == "discovery-hosts" && ! -f "$(pwd)/paused.conf" && "$resume" = "y" ]]; then 3219 | echo -e "\e[33m [!] masscan paused.conf file not found -- restarting alive host discovery scans \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3220 | fi 3221 | #fpscan='while IFS= read -r i; do fping -a -q -g "$i" >> "$#filepath/$typevar-fping-results.txt"; done < "$filepath/logs/$typevar-fpingcidrs.txt"' 3222 | if (($total_hosts >= 1 && $total_hosts <= 9999)); then 3223 | checked_cmd="$alivesmscan --top-ports 5000 && sleep 1" 3224 | eval "$alivesmscan --top-ports 5000 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3225 | elif (($total_hosts >= 10000 && $total_hosts <= 24999)); then 3226 | checked_cmd="$alivesmscan --top-ports 2000 && sleep 1" 3227 | eval "$alivesmscan --top-ports 2000 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3228 | elif (($total_hosts >= 25000 && $total_hosts <= 49999)); then 3229 | checked_cmd="$alivesmscan --top-ports 1500 && sleep 1" 3230 | eval "$alivesmscan --top-ports 1500 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3231 | elif (($total_hosts >= 50000 && $total_hosts <= 99999)); then 3232 | checked_cmd="$alivesmscan --top-ports 1000 && sleep 1" 3233 | eval "$alivesmscan --top-ports 1000 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3234 | elif (($total_hosts >= 100000 && $total_hosts <= 149999)); then 3235 | checked_cmd="$alivesmscan --top-ports 500 && sleep 1" 3236 | eval "$alivesmscan --top-ports 500 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3237 | elif (($total_hosts >= 150000 && $total_hosts <= 199999)); then 3238 | checked_cmd="$alivesmscan --top-ports 250 && sleep 1" 3239 | eval "$alivesmscan --top-ports 250 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3240 | elif (($total_hosts >= 200000 && $total_hosts <= 249999)); then 3241 | checked_cmd="$alivesmscan --top-ports 150 && sleep 1" 3242 | eval "$alivesmscan --top-ports 150 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3243 | elif (($total_hosts >= 250000 && $total_hosts <= 499999)); then 3244 | checked_cmd="$alivesmscan --top-ports 50 && sleep 1" 3245 | eval "$alivesmscan --top-ports 50 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3246 | elif (($total_hosts >= 500000)); then 3247 | checked_cmd="$alivesmscan --top-ports 20 && sleep 1" 3248 | eval "$alivesmscan --top-ports 20 && sleep 1" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3249 | fi 3250 | pid=$! 3251 | sleep 4 3252 | echo -ne "\e[36m [-] Discovering alive hosts with masscan... $(grep -o '[0-9]\+:[0-9]\+:[0-9]\+ remaining' "$filepath/logs/misc-files/$typevar-masscan-tcp.log" | tail -1) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3253 | echo "" 3254 | #Status indicator 3255 | periodicfile="$filepath/logs/misc-files/$typevar-masscan-tcp.log" 3256 | contstatus="Scanning hosts" 3257 | statusmasscan 3258 | #Error check and alert 3259 | wait $pid 3260 | exitstatus=$? 3261 | errorcheck 3262 | fi 3263 | 3264 | status_msg="[+] Discovering alive hosts with Masscan... Done" 3265 | printf "\033[A\r\e[K\e[32m $status_msg \e[0m\n" | tee -a "$filepath/logs/$typevar-timestamps.log" 3266 | ntfy "$status_msg" 3267 | #Filter out hosts with more than 100 ports open 3268 | susinput="$filepath/logs/misc-files/$typevar-masscanalives-results.txt" 3269 | susips="$filepath/$typevar-100port-hosts-tcp.txt" 3270 | susoutput="$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt" 3271 | filtersusips 3272 | #Carve out TCP IP addresses and put them into a file 3273 | cat $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt | grep 'Host' | awk '{print $4}' >> "$filepath/logs/misc-files/$typevar-discoresults.txt" #this excludes 100port-hosts 3274 | #cat $filepath/logs/misc-files/$typevar-masscanalives-results.txt | grep 'Host' | awk '{print $4}' | sort -u >> $filepath/$typevar-masscan-alives.txt 3275 | 3276 | #Generate alives file 3277 | cat "$filepath/logs/misc-files/$typevar-discoresults.txt" | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n > "$filepath/$typevar-alives.txt" 3278 | if [[ "$stage" == "discovery-hosts" ]] && [[ "$only_flag" == true ]]; then 3279 | if grep -q "\S" "$filepath/$typevar-alives.txt"; then 3280 | echo -e "\e[32m [+] Alive hosts discovered -- "$(date)" \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3281 | else 3282 | echo -e "\e[33m [!] No alive hosts detected -- if unexpected, check targets or inquire about scan defenses or other interference \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3283 | #Remove firewall rule 3284 | removefirewallrule 3285 | echo -e "\e[33m [!] Exiting Zero-E... \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3286 | z0ecleanup 3287 | exit 1 3288 | fi 3289 | else 3290 | if ! grep -q "\S" "$filepath/logs/misc-files/$typevar-discoresults.txt" 2>/dev/null; then 3291 | echo -e "\e[33m [!] No alive hosts detected -- if unexpected, check targets or inquire about scan defenses or other interference \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3292 | #Remove firewall rule 3293 | removefirewallrule 3294 | echo -e "\e[33m [!] Exiting Zero-E... \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3295 | z0ecleanup 3296 | exit 1 3297 | fi 3298 | fi 3299 | 3300 | #Stage -- update 3301 | if [[ "$only_flag" != true && "$stage_cont" == true ]]; then 3302 | echo "discovery-ports" > "$stage_file" 3303 | stage="discovery-ports" 3304 | fi 3305 | 3306 | fi 3307 | 3308 | #Stage -- start 3309 | if [[ "$stage" == "discovery-ports" ]] && ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 3310 | 3311 | if [ "$firewallset" != true ]; then 3312 | setfirewallrule 3313 | fi 3314 | #Masscan open port discovery 3315 | #################################################################### 3316 | ####### Internal masscan command if adjustment is necessary ######## 3317 | imscan="sudo masscan --open-only -p 1-65535 --rate=8000 --src-port=55555 --excludefile $nostrikes --include-file "$filepath/$typevar-alives.txt" -oG "$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp.txt"" 3318 | echo "imscan=\"$imscan\"" >> "$vars_file" 3319 | ### Stored as variable to correctly reflect in report if changed ### 3320 | #################################################################### 3321 | #^^^If scans are taking too long, remove -p 1-65535 and use --top-ports=32768 3322 | #^^^If not getting any, or low number of, alive hosts, use --rate=500 3323 | if [[ "$stage" == "discovery-ports" ]] && [[ -f "$(pwd)/paused.conf" ]] && [[ "$resume" = "y" ]]; then 3324 | resume='' 3325 | echo -e "\e[36m [-] Resuming masscan open port discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3326 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3327 | if [[ "$firewallset" != true ]]; then 3328 | setfirewallrule 3329 | fi 3330 | masscan --resume paused.conf >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3331 | else 3332 | if [[ "$ngineer_mode" == true ]]; then #for ngineer mode 3333 | if [[ "$ngineer_ports_default" == true ]]; then 3334 | if [[ "$firewallset" != true ]]; then 3335 | setfirewallrule 3336 | fi 3337 | eval "$imscan" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3338 | else 3339 | echo -e "\e[33m [!] Using z0e ngineer options for masscan open ports scan \e[0m" 3340 | if [[ "$firewallset" != true ]]; then 3341 | setfirewallrule 3342 | fi 3343 | eval "masscan $z0eng_ports_opts --open-only --excludefile $nostrikes --include-file $filepath/$typevar-alives.txt -oG $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp.txt" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3344 | fi 3345 | else 3346 | if [[ "$firewallset" != true ]]; then 3347 | setfirewallrule 3348 | fi 3349 | eval "$imscan" >> "$filepath/logs/misc-files/$typevar-masscan-tcp.log" 2>&1 & 3350 | fi 3351 | fi 3352 | pid=$! 3353 | sleep 4 3354 | echo -ne "\e[36m [-] Discovering open TCP ports with Masscan... $(grep -o '[0-9]\+:[0-9]\+:[0-9]\+ remaining' "$filepath/logs/misc-files/$typevar-masscan-tcp.log" | tail -1) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3355 | echo "" 3356 | #Status indicators 3357 | periodicfile="$filepath/logs/misc-files/$typevar-masscan-tcp.log" 3358 | contstatus="Scanning TCP ports" 3359 | statusmasscan 3360 | #Error check and alert 3361 | checked_cmd="$imscan" 3362 | wait $pid 3363 | exitstatus=$? 3364 | errorcheck 3365 | 3366 | status_msg="[+] Discovering open TCP ports with Masscan... Done" 3367 | printf "\033[A\r\e[K\e[32m $status_msg \e[0m\n" | tee -a "$filepath/logs/$typevar-timestamps.log" 3368 | ntfy "$status_msg" 3369 | #Filter out hosts with more than 100 ports open 3370 | susinput="$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp.txt" 3371 | susips="$filepath/$typevar-100port-hosts-tcp.txt" 3372 | susoutput="$filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt" 3373 | filtersusips 3374 | #Carve out TCP IP addresses and put them into a file 3375 | cat $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt | grep 'Host' | awk '{print $4}' >> $filepath/logs/misc-files/$typevar-discoresults.txt #this excludes 100port-hosts 3376 | 3377 | #Generate list of alive hosts 3378 | cat $filepath/logs/misc-files/$typevar-discoresults.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n >> $filepath/$typevar-alives.txt 3379 | cp $filepath/$typevar-alives.txt $filepath/$typevar-alives-tmp.txt 3380 | cat $filepath/$typevar-alives-tmp.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n > $filepath/$typevar-alives.txt 3381 | rm $filepath/$typevar-alives-tmp.txt 3382 | 3383 | #Stage -- update 3384 | if [ "$udp" = "y" ] || [ "$udp" = "yes" ] || [ "$U_opt" = true ]; then 3385 | echo "discovery-udp" > "$stage_file" 3386 | stage="discovery-udp" 3387 | elif [[ "$stage_cont" == true ]] || { [[ "$stage" == "discovery-ports" ]] && [[ "$only_flag" == true ]]; }; then 3388 | echo "discovery-lists" > "$stage_file" 3389 | stage="discovery-lists" 3390 | fi 3391 | 3392 | fi 3393 | 3394 | #Stage -- start 3395 | if { [[ "$stage" == "discovery-udp" ]] && [[ "$udp" = "y" || "$udp" = "yes" || "$U_opt" = true ]]; } || { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]] && [[ "$stage" != "discovery-lists" && "$stage" != "services-udp" ]]; }; then 3396 | #UDP open port scan 3397 | if [[ "$resume" = "y" ]]; then 3398 | resume='' 3399 | echo -e "\e[36m [-] Resuming UDP discovery scans -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3400 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3401 | nmap --resume "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>>"$filepath/logs/$typevar-errors.log" & 3402 | else 3403 | if [[ "$ngineer_mode" == true ]]; then 3404 | if [[ "$ngineer_udpa_default" == true ]]; then 3405 | if [[ -f $filepath/logs/misc-files/$typevar-discoresults.txt ]]; then 3406 | #15094 top ports is 99% effective. Reference this chart for --top-port number effectiveness: https://nmap.org/book/performance-port-selection.html 3407 | nudpa="nmap -v -Pn -sU --open --min-rate 3000 --max-rate 5000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $filepath/logs/misc-files/$typevar-discoresults.txt -d" 3408 | else 3409 | nudpa="nmap -v -Pn -sU --open --min-rate 3000 --max-rate 5000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $ips -d" 3410 | fi 3411 | eval "$nudpa" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>> $filepath/logs/$typevar-errors.log & 3412 | else 3413 | echo -e "\e[33m [!] Using z0e ngineer options for Nmap UDP discovery scan \e[0m" 3414 | if [[ -f $filepath/logs/misc-files/$typevar-discoresults.txt ]]; then 3415 | eval "nmap -sU --open $z0eng_udpa_opts -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $filepath/logs/misc-files/$typevar-discoresults.txt -d" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>> $filepath/logs/$typevar-errors.log & 3416 | else 3417 | eval "nmap -sU --open $z0eng_udpa_opts -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $ips -d" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>> $filepath/logs/$typevar-errors.log & 3418 | fi 3419 | fi 3420 | else 3421 | if [[ -f $filepath/logs/misc-files/$typevar-discoresults.txt ]]; then 3422 | #15094 top ports is 99% effective. Reference this chart for --top-port number effectiveness: https://nmap.org/book/performance-port-selection.html 3423 | nudpa="nmap -v -Pn -sU --open --min-rate 3000 --max-rate 5000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $filepath/logs/misc-files/$typevar-discoresults.txt -d" 3424 | else 3425 | nudpa="nmap -v -Pn -sU --open --min-rate 3000 --max-rate 5000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $ips -d" 3426 | fi 3427 | eval "$nudpa" >> $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log 2>> $filepath/logs/$typevar-errors.log & 3428 | fi 3429 | fi 3430 | pid=$! 3431 | echo -ne "\e[36m [-] Discovering alive hosts and open UDP ports with Nmap... \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3432 | echo "" 3433 | #Status indicator 3434 | periodicfile="$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.log" 3435 | contstatus="Scanning UDP ports" 3436 | statusnmap 3437 | #Error check and alert 3438 | checked_cmd="nmap -Pn -sU --open --min-rate 3000 --max-rate 5000 --top-ports 15094 --max-retries 3 --host-timeout 30 -oG $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap --excludefile $nostrikes -iL $filepath/logs/misc-files/$typevar-discoresults.txt -d" 3439 | wait $pid 3440 | exitstatus=$? 3441 | errorcheck 3442 | 3443 | status_msg="[+] Discovering alive hosts and open UDP ports with Nmap... Done" 3444 | printf "\033[A\r\e[K\e[32m $status_msg \e[0m\n" | tee -a "$filepath/logs/$typevar-timestamps.log" 3445 | ntfy "$status_msg" 3446 | #Carve out UDP IP addresses and put them into a file 3447 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 3448 | awk 'NR==FNR{ips[$0];next} {for (ip in ips) if ($0 !~ ip) print}' $filepath/$typevar-100port-hosts-tcp.txt $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap > $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt #Filter out hosts with more than 100 open tcp ports 3449 | cat $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt | grep '/open' | cut -d ' ' -f2 >> $filepath/logs/misc-files/$typevar-discoresults.txt 3450 | else 3451 | cat "$filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap" | grep '/open' | cut -d ' ' -f2 >> $filepath/logs/misc-files/$typevar-discoresults.txt 3452 | fi 3453 | 3454 | #Stage -- update 3455 | echo "discovery-lists" > "$stage_file" 3456 | stage="discovery-lists" 3457 | fi 3458 | 3459 | #Stage discovery-lists -- start 3460 | if [[ "$stage" == "discovery-lists" ]]; then 3461 | #Remove firewall rule 3462 | if ! [[ "$udp" == "y" && "$only_flag" == true ]]; then 3463 | removefirewallrule 3464 | fi 3465 | 3466 | #Make final list of ordered, unique alive hosts excluding sus ips 3467 | cat $filepath/logs/misc-files/$typevar-discoresults.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n >> $filepath/$typevar-alives.txt 3468 | cp $filepath/$typevar-alives.txt $filepath/$typevar-alives-tmp.txt 3469 | cat $filepath/$typevar-alives-tmp.txt | sort -u | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n > $filepath/$typevar-alives.txt 3470 | rm $filepath/$typevar-alives-tmp.txt 3471 | 3472 | #Generate list of all open ports 3473 | if ! { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]]; }; then 3474 | cat $filepath/logs/misc-files/$typevar-discoscan-masscan-tcp-nosusips.txt | grep 'open' | cut -d ' ' -f5 | cut -d '/' -f1 | sort -u >> $filepath/rangetemp.txt 3475 | rangeout="$filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt" 3476 | singleportstorange 3477 | cat $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt > $filepath/$typevar-openPorts.txt 3478 | sed -i 's/$/\/TCP/' "$filepath/$typevar-openPorts.txt" 3479 | fi 3480 | if [ "$udp" = "y" ] || [ "$udp" = "yes" ] || [ "$U_opt" = true ]; then 3481 | if [[ "$only_flag" == true ]]; then 3482 | rm $filepath/$typevar-openPorts.txt 2>/dev/null 3483 | fi 3484 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 3485 | cat $filepath/logs/misc-files/$typevar-discoscan-nmap-udp-nosusips.txt | grep '/open' | cut -d ' ' -f4 | cut -d '/' -f1 | sort -u >> $filepath/rangetemp.txt 3486 | else 3487 | cat $filepath/logs/misc-files/$typevar-discoscan-nmap-udp.gnmap | grep '/open' | cut -d ' ' -f4 | cut -d '/' -f1 | sort -u >> $filepath/rangetemp.txt 3488 | fi 3489 | rangeout="$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt" 3490 | singleportstorange 3491 | cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt >> $filepath/$typevar-openPorts.txt 3492 | sed -i '/\/TCP$/! s/$/\/UDP/' "$filepath/$typevar-openPorts.txt" 3493 | fi 3494 | nessusports 2>/dev/null 3495 | 3496 | #Status update 3497 | alivesandportscheck 3498 | if [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ] && [ -s "$filepath/$typevar-100port-hosts-udp.txt" ]; then 3499 | echo -e "\e[33m [!] Excluding potential deception or firewall-protected hosts showing more than 100 open TCP/UDP ports -- recommend inquiring about hosts in $typevar-100port-hosts-tcp.txt and $typevar-100port-hosts-udp.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3500 | elif [ -s "$filepath/$typevar-100port-hosts-tcp.txt" ]; then 3501 | echo -e "\e[33m [!] Excluding potential deception or firewall-protected hosts showing more than 100 open TCP ports -- recommend inquiring about hosts in $typevar-100port-hosts-tcp.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3502 | elif [ -s "$filepath/$typevar-100port-hosts-udp.txt" ]; then 3503 | echo -e "\e[33m [!] Excluding potential deception or firewall-protected hosts showing more than 100 open UDP ports -- recommend inquiring about hosts in $typevar-100port-hosts-udp.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3504 | fi 3505 | echo -e "\e[33m [!] Generated files for Nessus vulnerability scans -- Hosts: $typevar-alives.txt | Ports: $typevar-portsForNessus.txt \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3506 | 3507 | #Stage -- update 3508 | if [[ "$only_flag" != true && "$stage_cont" == true ]]; then 3509 | echo "services-tcp" > "$stage_file" 3510 | stage="services-tcp" 3511 | elif { [[ "$udp" == "y" ]] && [[ "$only_flag" == true ]] && [[ "$stage_cont" == true ]]; }; then 3512 | echo "services-udp" > "$stage_file" 3513 | stage="services-udp" 3514 | fi 3515 | 3516 | fi 3517 | 3518 | #Stage -- start 3519 | if [[ "$stage" == "services-tcp" ]]; then 3520 | #Nmap TCP service scans 3521 | intscan="nmap -sC -sV -Pn -O -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt | paste -sd "," -) --open --reason -oA $filepath/$typevar-tcp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 3522 | echo "intscan=\"$intscan\"" >> "$vars_file" 3523 | if grep -q "\S" "$filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt"; then 3524 | if [[ "$resume" = "y" ]]; then 3525 | resume='' 3526 | echo -e "\e[36m [-] Resuming TCP service scans \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3527 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3528 | nmap --resume $filepath/$typevar-tcp-servicescan-results.gnmap 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3529 | else 3530 | if [[ "$ngineer_mode" == true ]]; then 3531 | if [[ "$ngineer_tcps_default" == true ]]; then 3532 | eval "$intscan -v" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3533 | else 3534 | echo -e "\e[33m [!] Using z0e ngineer options for Nmap TCP service scan \e[0m" 3535 | eval "nmap $z0eng_tcps_opts -sV -Pn -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-tcp.txt | paste -sd "," -) -oA $filepath/$typevar-tcp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3536 | fi 3537 | else 3538 | eval "$intscan -v" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3539 | fi 3540 | fi 3541 | pid=$! 3542 | echo -e "\e[36m [-] Scanning services on open TCP ports of alive hosts with Nmap -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3543 | #Status indicator 3544 | periodicfile="$filepath/$typevar-tcp-servicescan-results.nmap" 3545 | contstatus="Scanning TCP ports" 3546 | statusnmap 3547 | #Error check and alert 3548 | checked_cmd="$intscan" 3549 | wait $pid 3550 | exitstatus=$? 3551 | printf "\r%-${#indicator}s\r" "" #Clears status indicator line 3552 | if [ "$exitstatus" -eq 0 ]; then 3553 | status_msg="[+] Nmap TCP service scan complete, results saved as $typevar-tcp-servicescan-results -- $(date)" 3554 | echo -e "\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3555 | ntfy "$status_msg" 3556 | else 3557 | errorcheck 3558 | if [ "$ntfy_flag" = true ]; then 3559 | ntfy "[!] Nmap TCP service scan failed -- Check errors.log" 3560 | fi 3561 | fi 3562 | 3563 | genwindowshostlist_inscript 3564 | listiptohostname_inscript 3565 | parsegnmap_inscript=true 3566 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 139,445,137,138 "$filepath/analysis/$typevar-smbHosts.txt" 3567 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 80,443,8080,8443,8000,8008,8888 "$filepath/analysis/$typevar-httpHosts.txt" 3568 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 22,23,3389,5985,5986,5900,5800,1494,5631,5632 "$filepath/analysis/$typevar-remoteAccessHosts.txt" 3569 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 1723,1194 "$filepath/analysis/$typevar-vpnHosts.txt" 3570 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 20,21,22,69,111,873,990,2049,3260 "$filepath/analysis/$typevar-fileshareHosts.txt" 3571 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 3306,5432,1433,1521 "$filepath/analysis/$typevar-databaseHosts.txt" 3572 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 25,465,587,143,993,110,995 "$filepath/analysis/$typevar-emailHosts.txt" 3573 | parsegnmap "$filepath/$typevar-tcp-servicescan-results.gnmap" 389,636 "$filepath/analysis/$typevar-ldapHosts.txt" 3574 | else 3575 | status_msg="[!] No open TCP ports detected -- TCP service scans skipped" 3576 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3577 | ntfy "$status_msg" 3578 | fi 3579 | 3580 | #Stage -- update 3581 | if { [ "$udp" = "y" ] || [ "$udp" = "yes" ] || [ "$U_opt" = true ]; } && [[ "$stage_cont" == true ]]; then 3582 | echo "services-udp" > "$stage_file" 3583 | stage="services-udp" 3584 | fi 3585 | 3586 | fi 3587 | 3588 | #Stage -- start 3589 | if [[ "$stage" == "services-udp" ]] && [[ "$udp" = "y" || "$udp" = "yes" || "$U_opt" = true ]]; then 3590 | #Nmap UDP service scans 3591 | nsudp="nmap -v -sU -Pn -sV --open --min-rate 1000 --max-rate 3000 --reason -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | paste -sd "," -) -oA $filepath/$typevar-udp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 3592 | if grep -q "\S" "$filepath/logs/misc-files/$typevar-portsfornmap-udp.txt"; then 3593 | if [[ "$resume" = "y" ]]; then 3594 | resume='' 3595 | echo -e "\e[36m [-] Resuming UDP service scans \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3596 | echo -e "\e[36m Using options from resumed scan \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3597 | nmap --resume $filepath/$typevar-udp-servicescan-results.gnmap 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3598 | else 3599 | if [[ "$ngineer_mode" == true ]]; then 3600 | if [[ "$ngineer_udps_default" == true ]]; then 3601 | eval "$nsudp" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3602 | else 3603 | echo -e "\e[33m [!] Using z0e ngineer options for Nmap UDP service scan \e[0m" 3604 | eval "nmap $z0eng_udps_opts -sU -sV -Pn --reason -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | paste -sd "," -) -oA $filepath/$typevar-udp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3605 | fi 3606 | else 3607 | eval "$nsudp -v" 1>/dev/null 2>>$filepath/logs/$typevar-errors.log & 3608 | fi 3609 | fi 3610 | pid=$! 3611 | echo -e "\e[36m [-] Scanning services on open UDP ports of alive hosts with Nmap -- $(date) \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3612 | #Status indicator 3613 | periodicfile="$filepath/$typevar-udp-servicescan-results.nmap" 3614 | contstatus="Scanning UDP ports" 3615 | statusnmap 3616 | #Error check and alert 3617 | checked_cmd="nmap -v -sU -Pn -sV --open --min-rate 1000 --max-rate 3000 --reason -p $(cat $filepath/logs/misc-files/$typevar-portsfornmap-udp.txt | paste -sd "," -) -oA $filepath/$typevar-udp-servicescan-results --excludefile $nostrikes -iL $filepath/$typevar-alives.txt" 3618 | wait $pid 3619 | exitstatus=$? 3620 | printf "\r%-${#indicator}s\r" "" #Clears status indicator line 3621 | if [ "$exitstatus" -eq 0 ]; then 3622 | status_msg="[+] Nmap UDP service scan complete, results saved as $typevar-udp-servicescan-results -- $(date)" 3623 | echo -e "\e[32m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3624 | ntfy "$status_msg" 3625 | else 3626 | errorcheck 3627 | if [ "$ntfy_flag" = true ]; then 3628 | ntfy "[!] Nmap UDP service scan failed -- Check errors.log" 3629 | fi 3630 | fi 3631 | 3632 | genwindowshostlist_inscript 3633 | listiptohostname_inscript 3634 | else 3635 | status_msg="[!] No open UDP ports detected -- UDP service scans skipped" 3636 | echo -e "\e[33m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3637 | ntfy "$status_msg" 3638 | fi 3639 | fi 3640 | 3641 | #fi 3642 | if [[ "$firewallset" = true ]]; then 3643 | removefirewallrule 3644 | fi 3645 | status_msg="[=] Zero-E completed -- happy hacking!" 3646 | echo -e "\e[35m $status_msg \e[0m" | tee -a "$filepath/logs/$typevar-timestamps.log" 3647 | ntfy "$status_msg" 3648 | z0ecleanup 3649 | fi 3650 | --------------------------------------------------------------------------------