├── .gitignore ├── LICENSE ├── README.md ├── api_keys.txt ├── common-ua.txt ├── gasmask.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | api_keys.txt 2 | output/ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # Misc 107 | results 108 | .idea 109 | -------------------------------------------------------------------------------- /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 | ## gasmask 2 | 3 | All in one Information gathering tool - OSINT 4 | 5 | *For a full list of our tools, please visit our website https://www.twelvesec.com/* 6 | 7 | Written by: 8 | 9 | * [maldevel](https://github.com/maldevel) ([twitter](https://twitter.com/maldevel)) 10 | * [mikismaos](https://github.com/mikismaos) 11 | * [xvass](https://github.com/xen0vas) 12 | * [ndamoulianos](https://github.com/ndamoulianos) 13 | * [sbrb](https://github.com/sbrb) 14 | --- 15 | 16 | ### Dependencies 17 | 18 | * Python 3.x 19 | * validators 20 | * python-whois 21 | * dnspython 22 | * requests 23 | * shodan 24 | * censys 25 | * mmap 26 | * pprint 27 | 28 | 29 | ### Information Gathering 30 | 31 | * ask 32 | * bing 33 | * crt 34 | * censys.io 35 | * dns 36 | * dnsdumpster 37 | * dogpile 38 | * github 39 | * google 40 | * googleplus 41 | * instagram 42 | * linkedin 43 | * netcraft 44 | * pgp 45 | * reddit 46 | * reverse dns 47 | * shodan 48 | * twitter 49 | * vhosts 50 | * virustotal 51 | * whois 52 | * yahoo 53 | * yandex 54 | * youtube 55 | * spyse 56 | 57 | --- 58 | 59 | ### Dependencies 60 | 61 | ``` 62 | sudo pip3 install -r requirements.txt 63 | ``` 64 | 65 | --- 66 | 67 | ### Usage 68 | 69 | ``` 70 | 71 | ___________ .__ _________ 72 | \__ ___/_ _ __ ____ | |___ __ ____ / _____/ ____ ____ 73 | | | \ \/ \/ // __ \| |\ \/ // __ \ \_____ \_/ __ \_/ ___\ 74 | | | \ /\ ___/| |_\ /\ ___/ / \ ___/\ \___ 75 | |____| \/\_/ \___ >____/\_/ \___ >_______ /\___ >\___ > 76 | \/ \/ \/ \/ \/ 77 | 78 | GasMasK v. 2.0 - All in one Information gathering tool - OSINT 79 | GasMasK is an open source tool licensed under GPLv3. 80 | Written by: @maldevel, mikismaos, xvass, ndamoulianos, sbrb 81 | https://www.twelvesec.com/ 82 | Please visit https://github.com/twelvesec/gasmask for more.. 83 | 84 | usage: gasmask.py [-h] [-d DOMAIN] [-s NAMESERVER] [-x PROXY] [-l LIMIT] 85 | [-i MODE] [-o BASENAME] [-k API-KEY] [-e SPYSE_API_KEY] 86 | [-m MATCH] [-f FILTER] [--count] [-R REPORT] 87 | [-B REPORT_BUCKET] [-1 CENSYS_API_ID] [-2 CENSYS_API_SECRET] 88 | [-r] [-u] [-a ASN] [-c COUNTRY] [-O CERT_ORG] 89 | [-I CERT_ISSUER] [-z CERT_HOST] [-S HTTP_SERVER] 90 | [-t HTML_TITLE] [-b HTML_BODY] [-T TAGS] [-L LIMIT] [-D] 91 | [-v] [-H] 92 | [arguments [arguments ...]] 93 | 94 | positional arguments: 95 | arguments Censys query 96 | 97 | optional arguments: 98 | -h, --help show this help message and exit 99 | -d DOMAIN, --domain DOMAIN 100 | Domain to search. 101 | -s NAMESERVER, --server NAMESERVER 102 | DNS server to use. 103 | -x PROXY, --proxy PROXY 104 | Use a proxy server when retrieving results from search engines (eg. '-x http://127.0.0.1:8080') 105 | -l LIMIT, --limit LIMIT 106 | Limit the number of search engine results (default: 100). 107 | -i MODE, --info MODE Limit information gathering (basic,nongoogle,whois,dns,revdns,vhosts,google,bing,yahoo,ask,dogpile,yandex,linkedin,twitter,youtube,reddit,github,instagram,crt,pgp,netcraft,virustotal,dnsdump,shodan,censys,spyse). 108 | -o BASENAME, --output BASENAME 109 | Output in the four major formats at once (markdown, txt, xml and html). 110 | -k API-KEY, --shodan-key API-KEY 111 | API key to use with Shodan search (MODE="shodan") 112 | -e SPYSE_API_KEY, --spyse-key SPYSE_API_KEY 113 | -m MATCH, --match MATCH 114 | Highlight a string within an existing query result 115 | -f FILTER, --filter FILTER 116 | Filter the JSON keys to display for each result, use value 'help' for interesting fields 117 | --count Print the count result and exit 118 | -R REPORT, --report REPORT 119 | Stats on given field (use value 'help' for listing interesting fields)' 120 | -B REPORT_BUCKET, --report_bucket REPORT_BUCKET 121 | Bucket len in report mode (default: 10) 122 | -1 CENSYS_API_ID, --censys_api_id CENSYS_API_ID 123 | Provide the authentication ID for the censys.io search engine 124 | -2 CENSYS_API_SECRET, --censys_api_secret CENSYS_API_SECRET 125 | Provide the secret hash for the censys.io search engine 126 | -r, --read_api_keys Read the API Keys stored in api_keys.txt file. (e.g. '-i censys -r') 127 | -u, --update_api_keys 128 | Update the API Keys stored in api_keys.txt file. (e.g. '-i censys -u') 129 | -a ASN, --asn ASN Filter with ASN (e.g 5408 for GR-NET AS) 130 | -c COUNTRY, --country COUNTRY 131 | Filter with country 132 | -O CERT_ORG, --cert-org CERT_ORG 133 | Certificate issued to organization 134 | -I CERT_ISSUER, --cert-issuer CERT_ISSUER 135 | Certificate issued by organization 136 | -z CERT_HOST, --cert-host CERT_HOST 137 | hostname Certificate is issued to 138 | -S HTTP_SERVER, --http-server HTTP_SERVER 139 | Server header 140 | -t HTML_TITLE, --html-title HTML_TITLE 141 | Filter on html page title 142 | -b HTML_BODY, --html-body HTML_BODY 143 | Filter on html body content 144 | -T TAGS, --tags TAGS Filter on specific tags. e.g: -T tag1,tag2,... (use keyword 'list' to list usual tags 145 | -L LIMIT, --Limit LIMIT 146 | Limit to N results 147 | -D, --debug Debug information 148 | -v, --verbose Print raw JSON records 149 | -H, --html Renders html elements in a browser 150 | 151 | ``` 152 | 153 | --- 154 | 155 | ### Modes 156 | 157 | * Basic Mode 158 | * Whois lookup 159 | * DNS queries 160 | * Reverse DNS Lookup 161 | * Bing Virtual Hosts 162 | 163 | * Nongoogle Mode 164 | * Whois lookup 165 | * DNS queries 166 | * Reverse DNS Lookup 167 | * Bing Virtual Hosts 168 | * Search in Bing 169 | * Search in Yahoo 170 | * Search in ASK 171 | * Search in Dogpile 172 | * Search in Yandex 173 | * Search in Crt 174 | * Search in DNSdumpster 175 | * Search in Netcraft 176 | * Search in VirusTotal 177 | * Search in Spyse 178 | 179 | --- 180 | 181 | ### Usage Examples 182 | 183 | ``` 184 | python gasmask.py -d example.com -i basic 185 | 186 | python gasmask.py -d example.com -i dnsdump 187 | 188 | python gasmask.py -d example.com -i shodan -k xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 189 | 190 | python gasmask.py -d example.com -i whois,dns,revdns 191 | 192 | python gasmask.py -d example.com -i basic,yahoo,github -o myresults/example_com_search_results 193 | ``` 194 | 195 | ### censys.io usage examples 196 | 197 | ``` 198 | python gasmask.py -i censys --Limit 10 nessus 199 | 200 | python gasmask.py -i censys -I SAP --report location.country.raw --report_bucket 10 201 | 202 | python gasmask.py -i censys --html-title "Hacked By" --Limit 10 --html 203 | 204 | python gasmask.py -i censys --tags heartbleed --report location.country.raw 205 | 206 | python gasmask.py -i censys -S NGINX --count 207 | 208 | python gasmask.py -i censys -d example.com 209 | 210 | python gasmask.py -i censys -t "Internal Server Error" -S Apache -m "HTTP 500" --Limit 15 211 | ``` 212 | 213 | ### Read the API Keys usage example - e.g in censys.io 214 | 215 | ``` 216 | python gasmask.py -i censys -r 217 | 218 | ``` 219 | 220 | ### Update the API Keys usage example - e.g in censys.io 221 | 222 | ``` 223 | python gasmask.py -i censys -u 224 | 225 | ``` 226 | 227 | --- 228 | 229 | ### Credits 230 | 231 | * [EmailHarvester](https://github.com/maldevel/EmailHarvester) 232 | * [theHarvester](https://github.com/laramies/theHarvester) 233 | * [Sublist3r](https://github.com/aboul3la/Sublist3r) 234 | * [gelim](https://github.com/gelim/censys) 235 | 236 | --- 237 | -------------------------------------------------------------------------------- /api_keys.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twelvesec/gasmask/252737157d98f84b77d876045509006373ba00d1/api_keys.txt -------------------------------------------------------------------------------- /common-ua.txt: -------------------------------------------------------------------------------- 1 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 2 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 3 | Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0 4 | Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 5 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7 6 | Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 7 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 8 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 9 | Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0 10 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36 11 | Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0 12 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 13 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 14 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:57.0) Gecko/20100101 Firefox/57.0 15 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36 16 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 OPR/49.0.2725.64 17 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299 18 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7 19 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 20 | Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 21 | Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0 22 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 23 | Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko 24 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 25 | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 26 | Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36 27 | Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 28 | Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:52.0) Gecko/20100101 Firefox/52.0 29 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 30 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.12; rv:57.0) Gecko/20100101 Firefox/57.0 31 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36 32 | Mozilla/5.0 (Windows NT 6.3; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0 33 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/63.0.3239.84 Chrome/63.0.3239.84 Safari/537.36 34 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5 35 | Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 36 | Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko 37 | Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0 38 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 39 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0.2 Safari/604.4.7 40 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063 41 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 42 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.108 Safari/537.36 43 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 44 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 45 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 OPR/50.0.2762.58 46 | Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 47 | Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 48 | Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:58.0) Gecko/20100101 Firefox/58.0 49 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:57.0) Gecko/20100101 Firefox/57.0 50 | Mozilla/5.0 (Windows NT 6.1; rv:57.0) Gecko/20100101 Firefox/57.0 51 | Mozilla/5.0 (Windows NT 10.0; WOW64; rv:56.0) Gecko/20100101 Firefox/56.0 52 | Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0 53 | Mozilla/5.0 (X11; Fedora; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0 54 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 55 | Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0 56 | Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko 57 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8 58 | Mozilla/5.0 (iPad; CPU OS 11_2_1 like Mac OS X) AppleWebKit/604.4.7 (KHTML, like Gecko) Version/11.0 Mobile/15C153 Safari/604.1 59 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 60 | Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0; Trident/5.0) 61 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36 62 | Mozilla/5.0 (Windows NT 6.1; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0 63 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 64 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/604.3.5 (KHTML, like Gecko) Version/11.0.1 Safari/604.3.5 65 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 66 | Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; Trident/5.0) 67 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 68 | Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 69 | Mozilla/5.0 (Windows NT 5.1; rv:52.0) Gecko/20100101 Firefox/52.0 70 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 71 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:57.0) Gecko/20100101 Firefox/57.0 72 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/603.3.8 (KHTML, like Gecko) Version/10.1.2 Safari/603.3.8 73 | Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36 74 | Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36 75 | Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 76 | Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 77 | -------------------------------------------------------------------------------- /gasmask.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # encoding: UTF-8 3 | 4 | """ 5 | GasMasK - All in one Information gathering tool - OSINT 6 | This file is part of GasMasK Project 7 | 8 | Written by: @maldevel 9 | Website: https://www.twelvesec.com/ 10 | GIT: https://github.com/twelvesec/gasmask 11 | 12 | TwelveSec (@Twelvesec) 13 | 14 | This program is free software: you can redistribute it and/or modify 15 | it under the terms of the GNU General Public License as published by 16 | the Free Software Foundation, either version 3 of the License, or 17 | (at your option) any later version. 18 | 19 | This program is distributed in the hope that it will be useful, 20 | but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | GNU General Public License for more details. 23 | 24 | You should have received a copy of the GNU General Public License 25 | along with this program. If not, see . 26 | 27 | For more see the file 'LICENSE' for copying permission. 28 | """ 29 | 30 | __author__ = "maldevel" 31 | __credits__ = ["maldevel", "mikismaos", "xvass", "ndamoulianos", "sbrb"] 32 | __license__ = "GPLv3" 33 | __version__ = "2.0" 34 | 35 | ####################################################### 36 | 37 | import argparse 38 | import json 39 | import mmap 40 | import tempfile 41 | from argparse import RawTextHelpFormatter 42 | from pathlib import Path 43 | 44 | import censys.certificates 45 | import validators 46 | import sys 47 | import socket 48 | import whois 49 | import dns.resolver 50 | import collections 51 | import re 52 | import os 53 | import random 54 | 55 | from censys.base import CensysException 56 | from censys.ipv4 import CensysIPv4 57 | from colorama import Style, Fore 58 | from dns import reversename 59 | import requests 60 | import time 61 | import shodan 62 | import pprint 63 | # import pdb 64 | 65 | ####################################################### 66 | 67 | # remove insecure https warning 68 | from requests.packages.urllib3.exceptions import InsecureRequestWarning 69 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning) 70 | 71 | ####################################################### 72 | 73 | message = """ 74 | ___________ .__ _________ 75 | \__ ___/_ _ __ ____ | |___ __ ____ / _____/ ____ ____ 76 | | | \ \/ \/ // __ \| |\ \/ // __ \ \_____ \_/ __ \_/ ___\ 77 | | | \ /\ ___/| |_\ /\ ___/ / \ ___/\ \___ 78 | |____| \/\_/ \___ >____/\_/ \___ >_______ /\___ >\___ > 79 | \/ \/ \/ \/ \/ 80 | 81 | GasMasK v. {} - All in one Information gathering tool - OSINT 82 | GasMasK is an open source tool licensed under GPLv3. 83 | Written by: @maldevel, mikismaos, xvass, ndamoulianos, sbrb 84 | https://www.twelvesec.com/ 85 | Please visit https://github.com/twelvesec/gasmask for more.. 86 | """.format(__version__) 87 | 88 | 89 | # ###################### Global Variables ################## # 90 | 91 | pp = pprint.PrettyPrinter(indent=4) 92 | KEYS_FILE = Path(__file__).parent.absolute().joinpath('api_keys.txt') 93 | DEBUG = True 94 | 95 | 96 | # ###################### Censys.io ######################### # 97 | 98 | def print_short(res): 99 | """ 100 | Build Short results - Censys.io 101 | :param res: results, type: `dict` 102 | :return: 103 | """ 104 | max_title_len = 50 105 | # public_info = [] 106 | title_head = 'Title: ' 107 | cut = '[...]' 108 | http_title = res.get('80.http.get.title', 'N/A') 109 | cert_name = res.get('443.https.tls.certificate.parsed.subject.common_name', '') 110 | cert_alt = res.get('443.https.tls.certificate.parsed.extensions.subject_alt_name.dns_names', 111 | '') 112 | as_name = res.get('autonomous_system.name', 'N/A') 113 | as_num = res.get('autonomous_system.asn', '') 114 | loc = '%s / %s' % (res.get('location.country_code', 'N/A'), res.get('location.city', 'N/A')) 115 | os = res.get('metadata.os', 'N/A') 116 | tags = res.get('tags', '') 117 | ip = res.get('ip', 'N/A') 118 | 119 | http_title = http_title.replace('\n', '\\n') 120 | http_title = http_title.replace('\r', '\\r') 121 | 122 | # cleanup of list values 123 | if isinstance(cert_name, list): 124 | if len(cert_name) > 1: 125 | cert_name = cert_name[0] + "+" 126 | else: 127 | cert_name = cert_name[0] 128 | if isinstance(cert_alt, list): 129 | if len(cert_alt) > 1: 130 | cert_alt = cert_alt[0] + "+" 131 | else: 132 | cert_alt = cert_alt[0] 133 | 134 | # encoding to UTF-8 135 | http_title = str(http_title.encode('UTF-8'), errors='ignore') 136 | cert_name = str(cert_name.encode('UTF-8'), errors='ignore') 137 | cert_alt = str(cert_alt.encode('UTF-8'), errors='ignore') 138 | tags = ', '.join([str(t.encode('UTF-8'), errors='ignore') for t in tags]) 139 | as_name = str(as_name.encode('UTF-8'), errors='ignore') 140 | os = str(os.encode('UTF-8'), errors='ignore') 141 | loc = str(loc.encode('UTF-8'), errors='ignore') 142 | 143 | if cert_alt != '' and cert_alt != cert_name: 144 | cert_name = cert_name + ' + ' + cert_alt 145 | 146 | # shorten title if too long 147 | if len(http_title) > (max_title_len - len(title_head) - 1): 148 | http_title = http_title[:max_title_len - len(title_head) - len(cut) - 1] + cut 149 | 150 | ret = (ip.ljust(16) + 151 | ((title_head + '%s') % http_title).ljust(max_title_len) + 152 | ('SSL: %s' % cert_name).ljust(50) + 153 | ('AS: %s (%s)' % (as_name, as_num)).ljust(40) + 154 | ('Loc: %s' % loc).ljust(30) + 155 | ('OS: %s' % os).ljust(15) + 156 | ('Tags: %s' % tags)) 157 | print(ret) 158 | 159 | return ret 160 | 161 | 162 | def CensysPublicReport(engine, public_info, output_basename): 163 | """ Censys Public Scan report """ 164 | if output_basename: 165 | output1 = output_basename + ".txt" 166 | output2 = output_basename + ".md" 167 | output3 = output_basename + ".xml" 168 | output4 = output_basename + ".html" 169 | 170 | with open(output1, 'a') as txt, open(output2, 'a') as md, \ 171 | open(output3, 'a') as xml, open(output4, 'a') as html: 172 | txt.write("[+] {} results\n".format(engine)) 173 | txt.write("-------------------------\n") 174 | md.write("---\n\n") 175 | md.write("## {} results\n".format(engine)) 176 | xml.write("<{}Results>\n".format(engine)) 177 | html.write("

{} results

\n".format(engine)) 178 | 179 | txt.write("\n") 180 | md.write("\n") 181 | 182 | txt.write("Censys Public Scan:\n") 183 | md.write("### Censys\n\n") 184 | xml.write("\n") 185 | html.write("

Censys

\n
    \n") 186 | 187 | for publicscan in public_info: 188 | txt.write("%s\n" % publicscan) 189 | # md.write("* {}\n".format(publicscan)) 190 | # xml.write("{}\n".format(publicscan)) 191 | # html.write("
  • {}
  • \n" % publicscan) 192 | 193 | html.write("
\n") 194 | xml.write("
\n") 195 | txt.write("\n") 196 | md.write("\n") 197 | 198 | 199 | def CensysPublicScan(api_id, api_sec, output_basename, args, report_buckets, 200 | match, public_info, custom_filter_fields=None): 201 | """ Censys.io Public Scan - Censys.io """ 202 | if (args.mode == 'censys' and ( 203 | args.Limit != float('inf') or args.asn != None or args.report != None or 204 | args.html != False or args.http_server != None or args.tags != None or 205 | args.cert_org != None or args.cert_host != None or args.count != False or 206 | args.html_body != None or args.html_title != None or args.country != None)): 207 | q, s = BuildQuery(api_id, api_sec, args) 208 | # count the number of results 209 | try: 210 | count = q.report(s, "updated_at")['metadata']['count'] 211 | except CensysException as e: 212 | print(e.message) 213 | sys.exit(-1) 214 | 215 | if args.report: 216 | try: 217 | r = q.report(s, args.report, report_buckets) 218 | except CensysException as e: 219 | print(e.message) 220 | sys.exit(-1) 221 | sys.stderr.write("Number of results: %d\n" % count) 222 | print_report(r, args.report, public_info, output_basename) 223 | sys.exit(0) 224 | 225 | if args.count: 226 | print("Number of results: %d\n" % count) 227 | sys.exit(0) 228 | else: 229 | sys.stderr.write("Number of results: %d\n" % count) 230 | 231 | # prepare temp dir for html files 232 | if args.html: 233 | htmldir = tempfile.mkdtemp() 234 | open(htmldir + "/README", "w").write( 235 | "html body dumped via command:" + ' '.join(sys.argv)) 236 | print("HTML body dumped to %s" % htmldir) 237 | 238 | public_info2 = [] 239 | # search the API 240 | if args.filter: 241 | filter_fields = args.filter.split(',') 242 | else: 243 | filter_fields = custom_filter_fields 244 | r = q.search(s, fields=filter_fields) 245 | i = 0 246 | for e in r: 247 | if i >= float(args.Limit): 248 | break 249 | if args.verbose: 250 | pp.pprint(q.view(e['ip'])) 251 | elif args.filter: 252 | print(e) # todo: by default dump raw JSON if filters are used 253 | else: 254 | public_info = print_short(e) 255 | public_info2.extend(public_info) 256 | if args.html: 257 | dump_html_to_file(htmldir, e) 258 | if match != 'None': 259 | print_match(q.view(e['ip']), match) 260 | i += 1 261 | 262 | return True 263 | return False 264 | 265 | 266 | def print_match(res, m): 267 | """ finding matching results - Censys.io """ 268 | for k in list(res.keys()): 269 | json_find(res[k], k, list(), m) 270 | print() 271 | 272 | 273 | def print_report(res, key, public_info, output_basename): 274 | """ Format the result output - Censys.io """ 275 | r = res['results'] 276 | print("count".ljust(10) + "\t" + key.split(".")[-1]) 277 | for e in r: 278 | print(("%d" % e['doc_count']).ljust(10) + "\t" + str(e['key']).ljust(30)) 279 | public_info = ("%d" % e['doc_count']).ljust(10) + "\t" + str(e['key']).ljust(30) 280 | 281 | CensysPublicReport('censys', public_info, output_basename) 282 | 283 | 284 | def build_query_string(args): 285 | """ Build the Query String - Censys.io """ 286 | if len(args.arguments) == 0: 287 | s = '*' 288 | else: 289 | s = "(" + args.arguments[0] + ")" 290 | if args.tags: 291 | if ',' in args.tags: 292 | tags_l = args.tags.split(',') 293 | tags_q = " AND tags:" + " AND tags:".join(tags_l) 294 | else: 295 | tags_q = " AND tags:%s" % args.tags 296 | s += tags_q 297 | if args.asn: 298 | s += " AND autonomous_system.asn:%s" % args.asn 299 | if args.cert_org: 300 | s += " AND 443.https.tls.certificate.parsed.subject.organization:%s" % args.cert_org 301 | if args.cert_issuer: 302 | s += " AND 443.https.tls.certificate.parsed.issuer.organization:%s" % args.cert_issuer 303 | if args.cert_host: 304 | s += " AND 443.https.tls.certificate.parsed.subject.common_name:%s" % args.cert_host 305 | if args.country: 306 | s += " AND location.country_code:%s" % args.country 307 | if args.http_server: 308 | s += " AND 80.http.get.headers.server:%s" % args.http_server 309 | if args.html_title: 310 | if " " in args.html_title: 311 | title = "\"%s\"" % args.html_title 312 | else: 313 | title = args.html_title 314 | s += " AND 80.http.get.title:%s" % title 315 | if args.html_body: 316 | if " " in args.html_body: 317 | body = "\"%s\"" % args.html_body 318 | else: 319 | body = args.html_body 320 | s += " AND 80.http.get.body:%s" % body 321 | if args.debug: 322 | print('Query: %s' % s) 323 | return s 324 | 325 | 326 | def is_contained(a, b): 327 | """ returns true if b is contained inside a """ 328 | if type(a) == type(b): 329 | m = re.search(b, a, re.UNICODE + re.IGNORECASE) 330 | if m: 331 | return True 332 | else: 333 | return False 334 | 335 | 336 | def BuildQuery(api_id, api_sec, pargs): 337 | """ uild The Query - Censys.io """ 338 | # build up query 339 | q = CensysIPv4(api_id, api_sec) 340 | s = build_query_string(pargs) 341 | return q, s 342 | 343 | 344 | def print_res(path, match, val): 345 | """ Printing Results - Censys.io """ 346 | sep = ' ' 347 | pre = '[...]' 348 | post = pre 349 | pos = match.lower().index(val.lower()) 350 | if len(match) >= 80: 351 | if pos < 35: 352 | pre = '' 353 | match_c = Style.DIM + pre + match[pos - 35:pos] + Fore.RED + Style.BRIGHT + match[ 354 | pos:pos + len( 355 | val)] + \ 356 | Style.RESET_ALL + Style.DIM + match[ 357 | pos + len(val):pos + 35] + post + Style.RESET_ALL 358 | match = pre + match[pos - 35:pos + 35] + post 359 | else: 360 | match_c = Style.DIM + match[:pos] + Fore.RED + Style.BRIGHT + match[pos:pos + len(val)] + \ 361 | Style.RESET_ALL + Style.DIM + match[pos + len(val):] + Style.RESET_ALL 362 | 363 | match_c = match_c.replace('\n', '\\n') 364 | match_c = match_c.replace('\r', '\\r') 365 | match = match.replace('\n', '\\n') 366 | match = match.replace('\r', '\\r') 367 | 368 | if len(path) >= 60: 369 | sep = '\n\t' 370 | if sys.stdout.isatty(): 371 | print(" %s:%s%s" % (path, sep, match_c)) 372 | else: 373 | print(" %s:%s%s" % (path, sep, match)) 374 | 375 | 376 | def append_if_new(l, e): 377 | if e not in l: 378 | return l + [e] 379 | else: 380 | return l 381 | 382 | 383 | def json_find(obj, k, visited, val): 384 | """ 385 | Searching JSON - Censys.io 386 | recursively find values in dict 'obj' that macthes 'val' 387 | store the keys to access the matching value in 'path' 388 | """ 389 | if visited is None: 390 | visited = list() 391 | 392 | # case of sub-dict : recursivity 393 | if isinstance(obj, dict): 394 | visited = append_if_new(visited, k) 395 | # visited = visited + [k] 396 | for key in list(obj.keys()): 397 | visited = json_find(obj[key], key, visited, val) 398 | 399 | # case of list : check all members 400 | elif isinstance(obj, list): 401 | for e in obj: 402 | if is_contained(e, val): 403 | print_res('.'.join(visited + [k]), e, val) 404 | 405 | # finally easiest case, leaf 406 | elif is_contained(obj, val): 407 | print_res('.'.join(visited + [k]), obj, val) 408 | 409 | # remove nodes already visited before returning 410 | if k in visited: 411 | visited.pop() 412 | return visited 413 | 414 | 415 | def dump_html_to_file(d, rec): 416 | """ Dump HTML to file - Censys.io """ 417 | html = rec.get('80.http.get.body') 418 | if html: 419 | filename = "%s/%s.html" % (d, rec['ip']) 420 | open(filename, "w").write(html.encode('UTF-8', errors='ignore')) 421 | 422 | 423 | def DomainSearchCensys(domain_name, api_id, api_sec, output_basename, domains): 424 | """ Domain Searching with Censys.io """ 425 | if domain_name != None: 426 | subdomains = CensysSearch(domain_name, api_id, api_sec) 427 | domains.extend(subdomains) 428 | SubdomainsReport('Censys', subdomains, output_basename) 429 | return True 430 | 431 | return False 432 | 433 | ####################################################### 434 | 435 | 436 | def checkFile(): 437 | """ Check if file exists """ 438 | is_true = os.path.isfile(KEYS_FILE) 439 | if is_true != True: 440 | return False 441 | if os.path.getsize(KEYS_FILE) == 0: 442 | return False 443 | else: 444 | return True 445 | 446 | 447 | def checkUser(engine): 448 | """ Check if engine exists in API key file """ 449 | chk = checkFile() 450 | if not chk: 451 | return False 452 | else: 453 | with open(KEYS_FILE) as f: 454 | s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) 455 | if s.find(engine) != -1: 456 | return True 457 | else: 458 | return False 459 | 460 | 461 | # ################# API KEYS ################## # 462 | 463 | def readFileContents(): 464 | """ Read API Keys from file """ 465 | with open(KEYS_FILE) as f: 466 | lines = f.read().splitlines() 467 | print( 468 | "| Engine | API Keys ID | API Secret Keys |" 469 | ) 470 | print( 471 | "|========================================================================================================|" 472 | ) 473 | for line in lines: 474 | print("|", line.split(":")[0], " " * (17 - len(line.split(":")[0])), "|", 475 | line.split(":")[1], " " * (38 - len(line.split(":")[1])), "|", 476 | line.split(":")[2], " " * (38 - len(line.split(":")[2])), "|") 477 | 478 | 479 | def createFileAndStoreAPIKeys(engine): 480 | """ Create file and Store the API Keys """ 481 | f = open(KEYS_FILE, "w+") 482 | api_id = input("[*] please provide the new %s API ID: " % engine) 483 | api_sec = input("[*] please provide the new %s API Secret: " % engine) 484 | f.write(engine + ":" + api_id + ":" + api_sec) 485 | f.close() 486 | return ("stored") 487 | 488 | 489 | def updateAPIKeys(engine): 490 | """ Update API Keys """ 491 | ckhstored = checkUser(engine) 492 | api_id = input("[*] please provide the new %s API ID: " % engine) 493 | api_sec = input("[*] please provide the new %s API Secret: " % engine) 494 | with open(KEYS_FILE, "r+") as op: 495 | lines = op.read().splitlines() 496 | if ckhstored == True: 497 | for line in lines: 498 | if line != line.split(":")[0]: 499 | with open(KEYS_FILE, "w+") as f: 500 | f.write(line) 501 | 502 | for line in lines: 503 | if line != line.split(":")[0]: 504 | with open(KEYS_FILE, "w+") as f1: 505 | f1.write(engine + ":" + api_id + ":" + api_sec) 506 | return 'y' 507 | 508 | if ckhstored == False: 509 | print("[!] user does not exist in file") 510 | return 'n' 511 | 512 | 513 | # ############### Check Domain, IP, Hostname ###################### # 514 | 515 | def CheckDomain(value): 516 | """ Validate Domain name """ 517 | if not validators.domain(value): 518 | raise argparse.ArgumentTypeError('Invalid {} domain.'.format(value)) 519 | return value 520 | 521 | 522 | def CheckDomainOrIP(value): 523 | """ Verify domain/ip """ 524 | if not validators.domain(value) and not validators.ip_address.ipv4(value): 525 | raise argparse.ArgumentTypeError('Invalid domain or ip address ({}).'.format(value)) 526 | return value 527 | 528 | 529 | def VerifyHostname(value): 530 | """ Get Domain ip address """ 531 | try: 532 | ip = socket.gethostbyname(value) 533 | return ip 534 | except Exception as e: 535 | return False 536 | 537 | 538 | # ##################### Whois, DNS ########################### # 539 | 540 | def WhoisQuery(value): 541 | whoisData = collections.OrderedDict() 542 | whoisData["name"] = ["-", "Name:"] 543 | whoisData["org"] = ["-", "Organization:"] 544 | whoisData["address"] = ["-", "Address:"] 545 | whoisData["city"] = ["-", "City:"] 546 | whoisData["zipcode"] = ["-", "Zip code:"] 547 | whoisData["country"] = ["-", "Country:"] 548 | whoisData["emails"] = ["-", "Emails:"] 549 | whoisData["registrar"] = ["-", "Registrar:"] 550 | whoisData["whois_server"] = ["-", "Whois Server:"] 551 | whoisData["updated_date"] = ["-", "Updated Date:"] 552 | whoisData["expiration_date"] = ["-", "Expiration Date:"] 553 | whoisData["creation_date"] = ["-", "Creation Date:"] 554 | whoisData["name_servers"] = ["-", "Name Servers:"] 555 | 556 | domain = whois.whois(value) 557 | 558 | for rec in whoisData: 559 | if domain[rec]: 560 | if isinstance(domain[rec], list): 561 | if rec == 'name_servers': 562 | whoisData[rec][0] = [] 563 | for val in domain[rec]: 564 | whoisData[rec][0].append(val + ":" + VerifyHostname(val)) 565 | else: 566 | whoisData[rec][0] = [] 567 | for val in domain[rec]: 568 | whoisData[rec][0].append(val) 569 | else: 570 | whoisData[rec][0] = str(domain[rec]) 571 | 572 | return whoisData 573 | 574 | 575 | def _query(value, dnsserver, record): 576 | myresolver = dns.resolver.Resolver() 577 | myresolver.nameservers = [dnsserver] 578 | 579 | try: 580 | answers = myresolver.query(value, record) 581 | for answer in answers: 582 | # TODO check: this for loop is returning on first loop 583 | if record == 'NS': 584 | return answer.to_text() + ":" + VerifyHostname(answer.to_text()) 585 | elif record == 'MX': 586 | domain_name = re.search(' (.*)\.', answer.to_text(), re.IGNORECASE).group(1) 587 | return answer.to_text() + ":" + VerifyHostname(domain_name) 588 | else: 589 | return answer.to_text() 590 | except Exception: 591 | return '-' 592 | 593 | return None # dnsData? 594 | 595 | 596 | def DnsQuery(value, dnsserver, record=None): 597 | """ Perform DNS queries """ 598 | dnsData = { 599 | "A": [], 600 | "CNAME": [], 601 | "HINFO": [], 602 | "MX": [], 603 | "NS": [], 604 | "PTR": [], 605 | "SOA": [], 606 | "TXT": [], 607 | "SPF": [], 608 | "SRV": [], 609 | "RP": [] 610 | } 611 | 612 | if record is None: 613 | for record in dnsData: 614 | dnsData[record].append(_query(value, dnsserver, record)) 615 | else: 616 | dnsData[record].append(_query(value, dnsserver, record)) 617 | 618 | return dnsData 619 | 620 | 621 | def ReverseIPQuery(value, dnsserver): 622 | """ IP DNS Reverse lookup """ 623 | try: 624 | revname = reversename.from_address(value) 625 | myresolver = dns.resolver.Resolver() 626 | myresolver.nameservers = [dnsserver] 627 | return str(myresolver.query(revname, 'PTR')[0]).rstrip('.') 628 | except Exception as e: 629 | print(e) 630 | return '' 631 | 632 | 633 | # ########## Parse the Results to get usefull data ############## # 634 | 635 | def CleanHTML(results): 636 | """ Clean HTML tags """ 637 | res = results 638 | res = re.sub('', '', res) 639 | res = re.sub('', '', res) 640 | res = re.sub('', '', res) 641 | res = re.sub('', '', res) 642 | res = re.sub('%2f', ' ', res) 643 | res = re.sub('%3a', ' ', res) 644 | res = re.sub('', '', res) 645 | res = re.sub('', '', res) 646 | res = re.sub('', '', res) 647 | res = re.sub('', '', res) 648 | res = re.sub('<', '', res) 649 | 650 | return res 651 | 652 | 653 | def GetEmails(results, value): 654 | """ Extract Emails """ 655 | res = results 656 | res = CleanHTML(res) 657 | 658 | # todo this regex needs improvement per case/service 659 | temp = re.compile( 660 | '[a-zA-Z0-9.\-_+#~!$&\',;=:]+' + 661 | '@' + 662 | '[a-zA-Z0-9.-]*' + 663 | value) 664 | 665 | emails = temp.findall(res) 666 | final = [] 667 | for email in emails: 668 | final.append(email.lower()) 669 | 670 | return sorted(set(final)) 671 | 672 | 673 | def GetHostnames(results, value): 674 | """ Extract Hostnames """ 675 | res = results 676 | res = CleanHTML(res) 677 | 678 | temp = re.compile('[a-zA-Z0-9.-]*\.' + value) 679 | hostnames = temp.findall(res) 680 | final = [] 681 | for host in hostnames: 682 | final.append(host.lower()) 683 | 684 | return sorted(set(final)) 685 | 686 | 687 | def GetHostnamesAll(results): 688 | """ Get All Hostnames """ 689 | res = results 690 | temp = re.compile('(.*?)') 691 | hostname = temp.findall(res) 692 | vhosts = [] 693 | 694 | for x in hostname: 695 | r = '' 696 | if x.count(':'): 697 | r = x.split(':')[1].split('/')[2] 698 | else: 699 | r = x.split("/")[0] 700 | vhosts.append(r) 701 | 702 | return sorted(set(vhosts)) 703 | 704 | 705 | def GetDNSDumpsterHostnames(value, results): 706 | tbl_regex = re.compile('<\/a>Host Records.*?(.*?)', 707 | re.S) 708 | link_regex = re.compile('(.*?)
', re.S) 709 | subdomains = [] 710 | 711 | try: 712 | results_tbl = tbl_regex.findall(results)[0] 713 | except IndexError: 714 | results_tbl = '' 715 | links_list = link_regex.findall(results_tbl) 716 | links = list(set(links_list)) 717 | 718 | for link in links: 719 | subdomain = link.strip() 720 | if not subdomain.endswith(value): 721 | continue 722 | if subdomain and subdomain not in subdomains and subdomain != value: 723 | subdomains.append(subdomain.strip()) 724 | 725 | return sorted(set(subdomains)) 726 | 727 | 728 | def SpyseGetDomains(data): 729 | """ 730 | :type data: dict 731 | :rtype: list 732 | """ 733 | domains = [] 734 | try: 735 | domains = [i['name'] for i in data['data']['items']] 736 | except IndexError: 737 | print("Unrecognised data returned by Spyse") 738 | 739 | return domains 740 | 741 | 742 | # ################## User Agent #################### # 743 | 744 | def PickRandomUA(value): 745 | """ Pick random User-Agent string """ 746 | if value: 747 | secure_random = random.SystemRandom() 748 | return secure_random.choice(value) 749 | 750 | return None 751 | 752 | 753 | def PickRandomTimeout(value): 754 | if value: 755 | secure_random = random.SystemRandom() 756 | return secure_random.choice(value) 757 | 758 | return 5 759 | 760 | 761 | # ####################### Common search ########################### # 762 | 763 | def CommonSearch(value, urltemplate, quantity, step, limit, uas, proxies, timeouts): 764 | counter = 0 765 | results = "" 766 | 767 | while counter <= limit: 768 | try: 769 | url = urltemplate.format(quantity=quantity, counter=counter, value=value) 770 | s = requests.Session() 771 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, 772 | proxies=proxies) 773 | if r.status_code != 200: 774 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 775 | return [], [] 776 | results += r.text 777 | except Exception as e: 778 | print(e) 779 | 780 | time.sleep(PickRandomTimeout(timeouts)) 781 | counter += step 782 | 783 | return GetEmails(results, value), GetHostnames(results, value) 784 | 785 | 786 | def CommonSearch2(value, urltemplate, step, limit, uas, proxies, timeouts): 787 | counter = 1 788 | results = "" 789 | 790 | while counter <= limit: 791 | try: 792 | url = urltemplate.format(counter=counter, value=value) 793 | s = requests.Session() 794 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, 795 | proxies=proxies) 796 | if r.status_code != 200: 797 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 798 | return [], [] 799 | results += r.text 800 | except Exception as e: 801 | print(e) 802 | 803 | time.sleep(PickRandomTimeout(timeouts)) 804 | counter += step 805 | 806 | return GetEmails(results, value), GetHostnames(results, value) 807 | 808 | 809 | # ###################### Search Engines & Services ######################### # 810 | 811 | def GoogleSearch(value, limit, uas, proxies, timeouts): 812 | quantity = 100 813 | step = 100 814 | url = "https://www.google.com/search?num={quantity}&start={counter}&hl=en&meta=&q=%40%22{value}%22" 815 | 816 | return CommonSearch(value, url, quantity, step, limit, uas, proxies, timeouts) 817 | 818 | 819 | def BingSearch(value, limit, uas, proxies, timeouts): 820 | quantity = 50 821 | step = 50 822 | url = "https://www.bing.com/search?q=%40{value}&count={quantity}&first={counter}" 823 | 824 | return CommonSearch(value, url, quantity, step, limit, uas, proxies, timeouts) 825 | 826 | 827 | def AskSearch(value, limit, uas, proxies, timeouts): 828 | step = 1 829 | url = "https://www.ask.com/web?q=%40%22{value}%22&page={counter}" 830 | 831 | return CommonSearch2(value, url, step, limit, uas, proxies, timeouts) 832 | 833 | 834 | # todo captcha 835 | def DogpileSearch(value, limit, uas, proxies, timeouts): 836 | step = 15 837 | url = "https://www.dogpile.com/search/web?qsi={counter}&q=%40{value}" 838 | 839 | return CommonSearch2(value, url, step, limit, uas, proxies, timeouts) 840 | 841 | 842 | def YahooSearch(value, limit, uas, proxies, timeouts): 843 | step = 10 844 | url = "https://search.yahoo.com/search?p=%40{value}&b={counter}&pz=10" 845 | 846 | return CommonSearch2(value, url, step, limit, uas, proxies, timeouts) 847 | 848 | 849 | def YandexSearch(value, limit, uas, proxies, timeouts): 850 | server = "yandex.com" 851 | quantity = 50 852 | step = 50 853 | results = "" 854 | page = 0 855 | counter = 0 856 | 857 | while counter <= limit: 858 | try: 859 | url = "https://" + server + "/search/?text=%22%40" + value + "%22&numdoc=" \ 860 | + str(quantity) + "&p=" + str(page) + "&lr=10418" 861 | s = requests.Session() 862 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, 863 | proxies=proxies) 864 | if r.status_code != 200: 865 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 866 | return [], [] 867 | results += r.text 868 | except Exception as e: 869 | print(e) 870 | 871 | time.sleep(PickRandomTimeout(timeouts)) 872 | counter += step 873 | page += 1 874 | 875 | return GetEmails(results, value), GetHostnames(results, value) 876 | 877 | 878 | def CrtSearch(value, uas, proxies): 879 | server = "crt.sh" 880 | results = "" 881 | 882 | try: 883 | url = "https://" + server + "/?q=" + value 884 | s = requests.Session() 885 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, proxies=proxies) 886 | if r.status_code != 200: 887 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 888 | return [], [] 889 | results += r.text 890 | except Exception as e: 891 | print(e) 892 | 893 | return GetHostnames(results, value) 894 | 895 | 896 | def DNSDumpsterSearch(targetip, uas, proxies): 897 | server = "dnsdumpster.com" 898 | results = "" 899 | timeout = 25 900 | subdomains = None 901 | 902 | try: 903 | url = "https://" + server + "/" 904 | s = requests.Session() 905 | myheaders = {'User-Agent': PickRandomUA(uas), 'Referer': 'https://dnsdumpster.com'} 906 | r = s.get(url, verify=False, headers=myheaders, proxies=proxies, timeout=timeout) 907 | if r.status_code != 200: 908 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 909 | return [], [] 910 | 911 | # get csrf token 912 | csrf_regex = re.compile('name="csrfmiddlewaretoken" value="(.*?)"', re.S) 913 | try: 914 | token = csrf_regex.findall(r.text)[0] 915 | except IndexError: 916 | print("[-] CSRF Token not found") 917 | return None 918 | 919 | params = {'csrfmiddlewaretoken': token, 'targetip': targetip} 920 | pr = s.post(url, verify=False, headers=myheaders, proxies=proxies, data=params, 921 | timeout=timeout) 922 | if pr.status_code != 200: 923 | print("[-] Something is going wrong (status code: {})".format(pr.status_code)) 924 | return [], [] 925 | 926 | subdomains = GetDNSDumpsterHostnames(targetip, pr.text) 927 | 928 | except Exception as e: 929 | print(e) 930 | 931 | return subdomains 932 | 933 | 934 | # todo pgp.mit.edu lookup service seems down 935 | def PGPSearch(value, uas, proxies): 936 | server = "pgp.mit.edu" 937 | results = "" 938 | 939 | try: 940 | url = "https://" + server + "/pks/lookup?search=" + value + "&op=index" 941 | s = requests.Session() 942 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, proxies=proxies) 943 | if r.status_code != 200: 944 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 945 | return [], [] 946 | results += r.text 947 | except Exception as e: 948 | print(e) 949 | 950 | return GetEmails(results, value), GetHostnames(results, value) 951 | 952 | 953 | def NetcraftSearch(value, uas, proxies): 954 | server = "searchdns.netcraft.com" 955 | results = "" 956 | 957 | try: 958 | url = "https://" + server + "?restriction=site+ends+with&host=" + value 959 | s = requests.Session() 960 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, proxies=proxies) 961 | if r.status_code != 200: 962 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 963 | return [], [] 964 | results += r.text 965 | except Exception as e: 966 | print(e) 967 | 968 | return GetHostnames(results, value) 969 | 970 | 971 | def VTSearch(value, uas, proxies): 972 | server = "www.virustotal.com" 973 | results = "" 974 | 975 | try: 976 | url = "https://" + server + "/en/domain/" + value + "/information/" 977 | s = requests.Session() 978 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, proxies=proxies) 979 | if r.status_code != 200: 980 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 981 | return [], [] 982 | results += r.text 983 | except Exception as e: 984 | print(e) 985 | 986 | return GetHostnames(results, value) 987 | 988 | 989 | def SpyseSearch(value, apikey, limit=100, proxies=None): 990 | server = "api.spyse.com" 991 | data = {} 992 | 993 | try: 994 | url = f'https://{server}/v2/data/domain/subdomain?limit={limit}&domain={value}' 995 | headers = { 996 | 'accept': 'application/json', 997 | 'Authorization': f'Bearer {apikey}', 998 | } 999 | s = requests.Session() 1000 | r = s.get(url, verify=False, headers=headers, proxies=proxies) 1001 | if DEBUG: print(r.text) 1002 | if r.status_code != 200: 1003 | print(f'[-] Something is going wrong - status code: {r.status_code}') 1004 | else: 1005 | data = json.loads(r.text) 1006 | except (ValueError, TypeError): 1007 | print("Unrecognised data returned by spyse api") 1008 | except NameError as e: 1009 | print("Error: Insufficient data passed to SpyseSearch") 1010 | print(e) 1011 | except Exception as e: 1012 | print(e) 1013 | 1014 | return data 1015 | 1016 | 1017 | def CensysSearch(value, api_id, api_secret): 1018 | try: 1019 | censys_certificates = censys.certificates.CensysCertificates(api_id=api_id, 1020 | api_secret=api_secret) 1021 | certificate_query = 'parsed.names: %s' % value 1022 | certificates_search_results = censys_certificates.search(certificate_query, 1023 | fields=['parsed.names']) 1024 | subdomains = [] 1025 | for search_result in certificates_search_results: 1026 | subdomains.extend(search_result['parsed.names']) 1027 | return set(subdomains) 1028 | except censys.base.CensysUnauthorizedException: 1029 | sys.stderr.write('[-] Your Censys credentials look invalid.\n') 1030 | exit(1) 1031 | except censys.base.CensysRateLimitExceededException: 1032 | sys.stderr.write('[-] Looks like you exceeded your Censys account limits rate. Exiting\n') 1033 | exit(1) 1034 | 1035 | 1036 | def GoogleSearchEngine(value, site, limit, uas, proxies, timeouts): 1037 | """ google search in site """ 1038 | server = "www.google.com" 1039 | quantity = 100 1040 | counter = 0 1041 | step = 100 1042 | results = "" 1043 | 1044 | while counter <= limit: 1045 | try: 1046 | url = "https://" + server + "/search?num=" + str(quantity) + "&start=" + str( 1047 | counter) + "&hl=en&meta=&q=site%3A" + site + "%20%40%22" + value + "%22" 1048 | s = requests.Session() 1049 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, 1050 | proxies=proxies) 1051 | if r.status_code != 200: 1052 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 1053 | return [], [] 1054 | results += r.text 1055 | except Exception as e: 1056 | print(e) 1057 | 1058 | time.sleep(PickRandomTimeout(timeouts)) 1059 | counter += step 1060 | 1061 | return GetEmails(results, value), GetHostnames(results, value) 1062 | 1063 | 1064 | def BingVHostsSearch(value, limit, uas, proxies, timeouts): 1065 | """ Bing Virtual Hosts Search """ 1066 | server = "www.bing.com" 1067 | quantity = 50 1068 | step = 50 1069 | counter = 0 1070 | results = "" 1071 | vhosts = [] 1072 | 1073 | while counter <= limit: 1074 | try: 1075 | url = "https://" + server + "/search?q=ip%3A" + value + "&go=&count=" + str( 1076 | quantity) + "&FORM=QBHL&qs=n&first=" + str(counter) 1077 | s = requests.Session() 1078 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, 1079 | proxies=proxies) 1080 | if r.status_code != 200: 1081 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 1082 | return [], [] 1083 | results += r.text 1084 | except Exception as e: 1085 | print(e) 1086 | 1087 | time.sleep(PickRandomTimeout(timeouts)) 1088 | counter += step 1089 | 1090 | all_hostnames = GetHostnamesAll(results) 1091 | 1092 | for x in all_hostnames: 1093 | x = re.sub(r'[[\<\/?]*[\w]*>]*', '', x) 1094 | x = re.sub('<', '', x) 1095 | x = re.sub('>', '', x) 1096 | vhosts.append(x) 1097 | 1098 | return sorted(set(vhosts)) 1099 | 1100 | 1101 | # todo if the API is used (valid key), the rtype is `dict` else 1102 | # the search is done via the website and the rtype is a `set` of vhosts 1103 | def ShodanSearch(api_key, domain, value, uas, proxies, timeouts, limit=0): 1104 | 1105 | api = shodan.Shodan(api_key) 1106 | server = "shodan.io" 1107 | counter = 0 1108 | quantity = 10 1109 | step = 10 1110 | vhosts = [] 1111 | results = '' 1112 | 1113 | # Warning: Shodan api needs a payment plan! 1114 | # Wrap the request in a try/ except block to catch errors 1115 | try: 1116 | # Search by hostname 1117 | query = 'hostname:' + domain 1118 | return api.search(query) 1119 | 1120 | except shodan.APIError as e: 1121 | print('Error: %s' % e) 1122 | 1123 | while counter <= limit: 1124 | try: 1125 | url = "https://" + server + "/search?q=ip%3A" + value + "&go=&count=" + str( 1126 | quantity) + "&FORM=QBHL&qs=n&first=" + str(counter) 1127 | s = requests.Session() 1128 | r = s.get(url, verify=False, headers={'User-Agent': PickRandomUA(uas)}, 1129 | proxies=proxies) 1130 | if r.status_code != 200: 1131 | print("[-] Something is going wrong (status code: {})".format(r.status_code)) 1132 | return [], [] 1133 | results += r.text 1134 | except Exception as e: 1135 | print(e) 1136 | 1137 | time.sleep(PickRandomTimeout(timeouts)) 1138 | counter += step 1139 | 1140 | all_hostnames = GetHostnamesAll(results) 1141 | 1142 | for x in all_hostnames: 1143 | x = re.sub(r'[[\<\/?]*[\w]*>]*', '', x) 1144 | x = re.sub('<', '', x) 1145 | x = re.sub('>', '', x) 1146 | vhosts.append(x) 1147 | 1148 | return sorted(set(vhosts)) 1149 | 1150 | 1151 | # ################## Reports ######################## # 1152 | 1153 | def Report(engine, emails, hostnames, output_basename): 1154 | """ Emails & Hostnames Console report """ 1155 | print() 1156 | print("Emails:") 1157 | for email in emails: 1158 | print(email) 1159 | 1160 | print() 1161 | print("Hostnames:") 1162 | for host in hostnames: 1163 | print(host) 1164 | print() 1165 | 1166 | if output_basename: 1167 | output1 = output_basename + ".txt" 1168 | output2 = output_basename + ".md" 1169 | output3 = output_basename + ".xml" 1170 | output4 = output_basename + ".html" 1171 | 1172 | with open(output1, 'a') as txt, open(output2, 'a') as md, \ 1173 | open(output3, 'a') as xml, open(output4, 'a') as html: 1174 | txt.write("[+] {} results\n".format(engine)) 1175 | txt.write("-------------------------\n") 1176 | md.write("---\n\n") 1177 | md.write("## {} results\n".format(engine)) 1178 | xml.write("<{}Results>\n".format(engine)) 1179 | html.write("

{} results

\n".format(engine)) 1180 | 1181 | txt.write("\n") 1182 | md.write("\n") 1183 | 1184 | txt.write("Emails:\n") 1185 | md.write("### Emails\n\n") 1186 | xml.write("\n") 1187 | html.write("

Emails

\n
    \n") 1188 | 1189 | for email in emails: 1190 | txt.write("{}\n".format(email)) 1191 | md.write("* {}\n".format(email)) 1192 | xml.write("{}\n".format(email)) 1193 | html.write("
  • {}
  • \n".format(email)) 1194 | 1195 | html.write("
\n") 1196 | xml.write("
\n") 1197 | txt.write("\n") 1198 | md.write("\n") 1199 | 1200 | txt.write("Hostnames:\n") 1201 | md.write("### Hostnames\n\n") 1202 | xml.write("\n") 1203 | html.write("

Hostnames

\n
    \n") 1204 | 1205 | for host in hostnames: 1206 | txt.write("{}\n".format(host)) 1207 | md.write("* {}\n".format(host)) 1208 | xml.write("{}\n".format(host)) 1209 | html.write("
  • {}
  • \n".format(host)) 1210 | 1211 | html.write("
\n") 1212 | xml.write("
\n") 1213 | txt.write("\n") 1214 | md.write("\n") 1215 | xml.write("\n".format(engine)) 1216 | 1217 | 1218 | def SubdomainsReport(engine, subdomains, output_basename): 1219 | """ Subdomains Console report """ 1220 | assert type(subdomains) in (list, tuple, dict) # should be list 1221 | if len(subdomains) == 0: 1222 | print('[-] Did not find any subdomain') 1223 | return 1224 | 1225 | print('') 1226 | print('[*] Found %d subdomains' % (len(subdomains))) 1227 | print('') 1228 | for subdomain in subdomains: 1229 | print(subdomain) 1230 | print('') 1231 | 1232 | if output_basename: 1233 | output1 = output_basename + ".txt" 1234 | output2 = output_basename + ".md" 1235 | output3 = output_basename + ".xml" 1236 | output4 = output_basename + ".html" 1237 | 1238 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1239 | output4, 'a') as html: 1240 | txt.write("[+] {} results\n".format(engine)) 1241 | txt.write("-------------------------\n") 1242 | md.write("---\n\n") 1243 | md.write("## {} results\n".format(engine)) 1244 | xml.write("<{}Results>\n".format(engine)) 1245 | html.write("

{} results

\n".format(engine)) 1246 | 1247 | txt.write("\n") 1248 | md.write("\n") 1249 | 1250 | txt.write("Subdomains:\n") 1251 | md.write("### Subdomains\n\n") 1252 | xml.write("\n") 1253 | html.write("

Subdomains

\n
    \n") 1254 | 1255 | for subdomain in subdomains: 1256 | txt.write("{}\n".format(subdomain)) 1257 | md.write("* {}\n".format(subdomain)) 1258 | xml.write("{}\n".format(subdomain)) 1259 | html.write("
  • {}
  • \n".format(subdomain)) 1260 | 1261 | html.write("
\n") 1262 | xml.write("
\n") 1263 | txt.write("\n") 1264 | md.write("\n") 1265 | 1266 | 1267 | def ShodanReport(results, output_basename): 1268 | """ Shodan Console Report """ 1269 | engine = "Shodan" 1270 | if len(results) == 0: 1271 | print('[-] Did not find any results') 1272 | return 1273 | 1274 | if type(results) in (set, list, tuple): 1275 | print('Shodan Report is not available for the current results') 1276 | print('Results found: %s' % len(results)) 1277 | for r in results: 1278 | print(r) 1279 | return 1280 | 1281 | print() 1282 | print('Results found: %s' % results['total']) 1283 | print('------------------') 1284 | print() 1285 | 1286 | # pdb.set_trace() 1287 | 1288 | for result in results['matches']: 1289 | 1290 | # Print host info 1291 | print('IP: %s' % result['ip_str']) 1292 | print('-------------------') 1293 | print('Hostnames: ' + ','.join(result['hostnames'])) 1294 | print('Organization: %s' % result.get('org', 'n/a')) 1295 | print('Operating System: %s' % result.get('os', 'n/a')) 1296 | print('Port: %s' % result['port']) 1297 | 1298 | # Print banner 1299 | banner = result.get('data') 1300 | if banner is not None: 1301 | print('Banner:') 1302 | print(result['data']) 1303 | print() 1304 | 1305 | # Output to file 1306 | if output_basename: 1307 | output1 = output_basename + ".txt" 1308 | output2 = output_basename + ".md" 1309 | output3 = output_basename + ".xml" 1310 | output4 = output_basename + ".html" 1311 | 1312 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1313 | output4, 'a') as html: 1314 | txt.write("[+] {} results\n".format(engine)) 1315 | txt.write("-------------------------\n") 1316 | md.write("---\n\n") 1317 | md.write("## {} results\n".format(engine)) 1318 | xml.write("<{}Results>\n".format(engine)) 1319 | html.write("

{} results

\n".format(engine)) 1320 | 1321 | txt.write("\n") 1322 | md.write("\n") 1323 | 1324 | for result in results['matches']: 1325 | ip = result['ip_str'] 1326 | hostnames = ','.join(result['hostnames']) 1327 | organization = result.get('org', 'n/a') 1328 | os = result.get('os', 'n/a') 1329 | port = result['port'] 1330 | banner = result['data'] 1331 | 1332 | # Print IP 1333 | PrintField("IP", ip, txt, md, xml, html) 1334 | 1335 | # Print Hostnames 1336 | PrintField("Hostnames", hostnames, txt, md, xml, html) 1337 | 1338 | # Print Organization 1339 | PrintField("Organization", organization, txt, md, xml, html) 1340 | 1341 | # Print Operating System 1342 | PrintField("OS", os, txt, md, xml, html) 1343 | 1344 | # Print Port 1345 | PrintField("Port", port, txt, md, xml, html) 1346 | 1347 | # Print Banner 1348 | PrintField("Banner", banner, txt, md, xml, html) 1349 | 1350 | xml.write("\n".format(engine)) 1351 | 1352 | 1353 | def PrintField(label, value, txt, md, xml, html): 1354 | """ Print field to output files """ 1355 | txt.write("{}:\n".format(label)) 1356 | md.write("### {}\n\n".format(label)) 1357 | xml.write("<{}>\n".format(label)) 1358 | html.write("

{}

\n
    \n".format(label)) 1359 | 1360 | txt.write("{}\n".format(value)) 1361 | md.write("* {}\n".format(value)) 1362 | xml.write("<{}>{}\n".format(label, value, label)) 1363 | html.write("
  • {}
  • \n".format(value)) 1364 | 1365 | html.write("
\n") 1366 | xml.write("\n".format(label)) 1367 | txt.write("\n") 1368 | md.write("\n") 1369 | 1370 | 1371 | def HostnamesReport(engine, hostnames, output_basename): 1372 | """ Hostnames Console report """ 1373 | assert type(hostnames) in (list, tuple, dict) # should be list 1374 | 1375 | print() 1376 | print("Hostnames:") 1377 | for host in hostnames: 1378 | print(host) 1379 | print() 1380 | 1381 | if output_basename: 1382 | output1 = output_basename + ".txt" 1383 | output2 = output_basename + ".md" 1384 | output3 = output_basename + ".xml" 1385 | output4 = output_basename + ".html" 1386 | 1387 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1388 | output4, 'a') as html: 1389 | txt.write("[+] {} results\n".format(engine)) 1390 | txt.write("-------------------------\n") 1391 | md.write("---\n\n") 1392 | md.write("## {} results\n".format(engine)) 1393 | xml.write("<{}Results>\n".format(engine)) 1394 | html.write("

{} results

\n".format(engine)) 1395 | 1396 | txt.write("\n") 1397 | md.write("\n") 1398 | 1399 | txt.write("Hostnames:\n") 1400 | md.write("### Hostnames\n\n") 1401 | xml.write("\n") 1402 | html.write("

Hostnames

\n
    \n") 1403 | 1404 | for host in hostnames: 1405 | txt.write("{}\n".format(host)) 1406 | md.write("* {}\n".format(host)) 1407 | xml.write("{}\n".format(host)) 1408 | html.write("
  • {}
  • \n".format(host)) 1409 | 1410 | html.write("
\n") 1411 | xml.write("
\n") 1412 | txt.write("\n") 1413 | md.write("\n") 1414 | xml.write("\n".format(engine)) 1415 | 1416 | 1417 | def InfoReport(mode, limit, dnsserver, proxy, domain, ip, uas, output_basename): 1418 | """ Information Console report """ 1419 | print("[+] Information gathering: {}".format(mode)) 1420 | print("[+] Looking into first {} search engines results".format(limit)) 1421 | print("[+] Using DNS server: {}".format(dnsserver)) 1422 | if proxy: 1423 | print("[+] Using Proxy server: {}".format(proxy)) 1424 | print("[+] Target: {}:{}".format(domain, ip)) 1425 | print("[+] User-agent strings: {}".format(uas)) 1426 | print() 1427 | 1428 | if output_basename: 1429 | output1 = output_basename + ".txt" 1430 | output2 = output_basename + ".md" 1431 | output3 = output_basename + ".xml" 1432 | output4 = output_basename + ".html" 1433 | 1434 | with open(output1, 'w') as txt, open(output2, 'w') as md, open(output3, 'w') as xml, open( 1435 | output4, 'w') as html: 1436 | txt.write("{}\n".format(message)) 1437 | md.write("```\n") 1438 | md.write("{}\n".format(message)) 1439 | md.write("```\n\n") 1440 | xml.write('\n'.format(message)) 1441 | xml.write('\n') 1442 | xml.write("\n") 1443 | xml.write("\n") 1444 | html.write("GasMasK Report\n") 1445 | html.write("
{}
\n
\n
    \n".format(message)) 1446 | 1447 | txt.write("[+] Information gathering: {}\n".format(",".join(mode))) 1448 | md.write("---\n\n") 1449 | md.write("* Information gathering: {}\n".format(",".join(mode))) 1450 | html.write("
  • Information gathering: {}
  • \n".format(",".join(mode))) 1451 | xml.write("\n") 1452 | 1453 | for m in mode: 1454 | xml.write("{}\n".format(m)) 1455 | xml.write("\n") 1456 | 1457 | txt.write("[+] Looking into first {} search engine results\n".format(limit)) 1458 | md.write("* Looking into first {} search engine results\n".format(limit)) 1459 | xml.write("{}\n".format(limit)) 1460 | html.write("
  • Search Engine Results: {}
  • \n".format(limit)) 1461 | 1462 | txt.write("[+] Using DNS server: {}\n".format(dnsserver)) 1463 | md.write("* Using DNS server: {}\n".format(dnsserver)) 1464 | xml.write("{}\n".format(dnsserver)) 1465 | html.write("
  • Using DNS server: {}
  • \n".format(dnsserver)) 1466 | 1467 | if proxy: 1468 | txt.write("[+] Using Proxy server: {}\n".format(proxy)) 1469 | md.write("* Using Proxy server: {}\n".format(proxy)) 1470 | xml.write("{}\n".format(proxy)) 1471 | html.write("
  • Using Proxy server: {}
  • \n".format(proxy)) 1472 | 1473 | txt.write("[+] Target: {}:{}\n".format(domain, ip)) 1474 | md.write("* Target: {}:{}\n".format(domain, ip)) 1475 | xml.write("\n") 1476 | xml.write("{}\n".format(domain)) 1477 | xml.write("{}\n".format(ip)) 1478 | xml.write("\n") 1479 | html.write("
  • Target: {}:{}
  • \n".format(domain, ip)) 1480 | 1481 | txt.write("[+] User-agent strings: {}\n".format(uas)) 1482 | md.write("* User-agent strings: {}\n".format(uas)) 1483 | xml.write("{}\n".format(uas)) 1484 | html.write("
  • User-agent strings: {}
  • \n".format(uas)) 1485 | 1486 | txt.write("\n") 1487 | md.write("\n") 1488 | xml.write("\n") 1489 | html.write("
\n") 1490 | 1491 | 1492 | def WhoisReport(data, output_basename): 1493 | """ Whois Console report""" 1494 | for key, value in data.items(): 1495 | if isinstance(value[0], list): 1496 | print() 1497 | print(value[1]) 1498 | for val in value[0]: 1499 | print(val) 1500 | print() 1501 | else: 1502 | print(value[1] + " " + value[0]) 1503 | print() 1504 | 1505 | if output_basename: 1506 | output1 = output_basename + ".txt" 1507 | output2 = output_basename + ".md" 1508 | output3 = output_basename + ".xml" 1509 | output4 = output_basename + ".html" 1510 | 1511 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1512 | output4, 'a') as html: 1513 | txt.write("[+] Whois lookup\n") 1514 | txt.write("----------------\n") 1515 | md.write("---\n\n") 1516 | md.write("## Whois lookup\n\n") 1517 | xml.write("\n") 1518 | html.write("

Whois lookup

\n
    \n") 1519 | 1520 | for key, value in data.items(): 1521 | if isinstance(value[0], list): 1522 | txt.write("\n") 1523 | md.write("\n") 1524 | 1525 | txt.write("{}\n".format(value[1])) 1526 | md.write("* {}\n".format(value[1])) 1527 | xml.write("<{}>\n".format(key)) 1528 | html.write("
  • {}
  • \n
      \n".format(value[1])) 1529 | 1530 | for val in value[0]: 1531 | txt.write("{}\n".format(val)) 1532 | md.write(" * {}\n".format(val)) 1533 | xml.write("{}\n".format(val)) 1534 | html.write("
    • {}
    • \n".format(val)) 1535 | 1536 | xml.write("\n".format(key)) 1537 | html.write("
    \n") 1538 | 1539 | txt.write("\n") 1540 | md.write("\n") 1541 | else: 1542 | txt.write("{} {}\n".format(value[1], value[0])) 1543 | md.write("* {} {}\n".format(value[1], value[0])) 1544 | xml.write("<{}>{}\n".format(key, value[0], key)) 1545 | html.write("
  • {} {}
  • \n".format(value[1], value[0])) 1546 | 1547 | txt.write("\n") 1548 | md.write("\n") 1549 | xml.write("\n") 1550 | html.write("
\n") 1551 | 1552 | 1553 | def DNSReport(data, output_basename): 1554 | """ DNS Console report """ 1555 | for key, value in data.items(): 1556 | if len(value) == 1: 1557 | print(key + " DNS record: " + value[0]) 1558 | else: 1559 | print() 1560 | print(key + " DNS record: ") 1561 | for val in value: 1562 | print(val) 1563 | print() 1564 | print() 1565 | 1566 | if output_basename: 1567 | output1 = output_basename + ".txt" 1568 | output2 = output_basename + ".md" 1569 | output3 = output_basename + ".xml" 1570 | output4 = output_basename + ".html" 1571 | 1572 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1573 | output4, 'a') as html: 1574 | txt.write("[+] DNS queries\n") 1575 | txt.write("---------------\n") 1576 | md.write("---\n\n") 1577 | md.write("## DNS queries\n\n") 1578 | xml.write("\n") 1579 | html.write("

DNS queries

\n
    \n") 1580 | 1581 | for key, value in data.items(): 1582 | if len(value) == 1: 1583 | txt.write("{} DNS record: {}\n".format(key, value[0])) 1584 | md.write("* {} DNS record: {}\n".format(key, value[0])) 1585 | xml.write("<{}>{}\n".format(key, value[0], key)) 1586 | html.write("
  • {} DNS record: {}
  • \n".format(key, value[0])) 1587 | 1588 | else: 1589 | txt.write("\n") 1590 | md.write("\n") 1591 | 1592 | txt.write("{} DNS record:\n".format(key)) 1593 | md.write("* {} DNS record:\n".format(key)) 1594 | xml.write("<{}>\n".format(key)) 1595 | html.write("
  • {} DNS record:
  • \n
      \n".format(key)) 1596 | 1597 | for val in value: 1598 | txt.write("{}\n".format(val)) 1599 | md.write(" * {}\n".format(val)) 1600 | xml.write("{}\n".format(val)) 1601 | html.write("
    • {}
    • \n".format(val)) 1602 | 1603 | html.write("
    \n") 1604 | md.write("\n".format(key)) 1605 | txt.write("\n") 1606 | md.write("\n") 1607 | 1608 | txt.write("\n") 1609 | md.write("\n") 1610 | xml.write("\n") 1611 | html.write("
\n") 1612 | 1613 | 1614 | def ReverseDNSReport(ip, data, output_basename): 1615 | """ Reverse DNS Console report """ 1616 | if data: 1617 | print(ip + ":" + data) 1618 | print() 1619 | 1620 | if output_basename: 1621 | output1 = output_basename + ".txt" 1622 | output2 = output_basename + ".md" 1623 | output3 = output_basename + ".xml" 1624 | output4 = output_basename + ".html" 1625 | 1626 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1627 | output4, 'a') as html: 1628 | txt.write("[+] Reverse DNS Lookup\n") 1629 | txt.write("----------------------\n") 1630 | md.write("---\n\n") 1631 | md.write("## Reverse DNS Lookup\n\n") 1632 | xml.write("\n") 1633 | html.write("

Reverse DNS Lookup

\n") 1634 | 1635 | if data: 1636 | txt.write("{}:{}\n".format(ip, data)) 1637 | md.write("* {}:{}\n".format(ip, data)) 1638 | xml.write("{}\n".format(ip)) 1639 | xml.write("{}\n".format(data)) 1640 | html.write("
  • {}:{}
\n".format(ip, data)) 1641 | 1642 | txt.write("\n") 1643 | md.write("\n") 1644 | xml.write("
\n") 1645 | 1646 | 1647 | def VHostsReport(data, output_basename): 1648 | """ VHosts Console report """ 1649 | for host in data: 1650 | print(host) 1651 | print() 1652 | 1653 | if output_basename: 1654 | output1 = output_basename + ".txt" 1655 | output2 = output_basename + ".md" 1656 | output3 = output_basename + ".xml" 1657 | output4 = output_basename + ".html" 1658 | 1659 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1660 | output4, 'a') as html: 1661 | txt.write("[+] Bing Virtual Hosts\n") 1662 | txt.write("----------------------\n") 1663 | md.write("---\n\n") 1664 | md.write("## Bing Virtual Hosts\n\n") 1665 | xml.write("\n") 1666 | html.write("

Bing Virtual Hosts

\n
    \n") 1667 | 1668 | for host in data: 1669 | txt.write("{}\n".format(host)) 1670 | md.write("* {}\n".format(host)) 1671 | xml.write("{}\n".format(host)) 1672 | html.write("
  • {}
  • \n".format(host)) 1673 | 1674 | txt.write("\n") 1675 | md.write("\n") 1676 | xml.write("\n") 1677 | html.write("
\n") 1678 | 1679 | 1680 | def FinalReport(info, output_basename): 1681 | """ All major formats final report """ 1682 | print() 1683 | print("[+] Search engines results - Final Report") 1684 | print("-----------------------------------------") 1685 | 1686 | if info['all_emails']: 1687 | print() 1688 | print("Emails:") 1689 | print() 1690 | for email in info['all_emails']: 1691 | print(email) 1692 | 1693 | if info['all_hosts']: 1694 | print() 1695 | print("Hostnames:") 1696 | print() 1697 | for host in info['all_hosts']: 1698 | print(host) 1699 | 1700 | if info['domains']: 1701 | print() 1702 | print("Subdomains:") 1703 | print() 1704 | for domain in info['domains']: 1705 | print(domain) 1706 | print() 1707 | 1708 | if output_basename: 1709 | output1 = output_basename + ".txt" 1710 | output2 = output_basename + ".md" 1711 | output3 = output_basename + ".xml" 1712 | output4 = output_basename + ".html" 1713 | 1714 | with open(output1, 'a') as txt, open(output2, 'a') as md, open(output3, 'a') as xml, open( 1715 | output4, 'a') as html: 1716 | txt.write("[+] Search engines results - Final Report\n") 1717 | txt.write("-----------------------------------------\n") 1718 | md.write("---\n\n") 1719 | md.write("## Search engines results - Final Report\n") 1720 | xml.write("\n") 1721 | html.write("

Search engines results - Final Report

\n") 1722 | 1723 | txt.write("\n") 1724 | md.write("\n") 1725 | 1726 | txt.write("Emails:\n") 1727 | md.write("### Emails\n\n") 1728 | xml.write("\n") 1729 | html.write("

Emails

\n
    \n") 1730 | 1731 | for email in info['all_emails']: 1732 | txt.write("{}\n".format(email)) 1733 | md.write("* {}\n".format(email)) 1734 | xml.write("{}\n".format(email)) 1735 | html.write("
  • {}
  • \n".format(email)) 1736 | 1737 | html.write("
\n") 1738 | xml.write("
\n") 1739 | txt.write("\n") 1740 | md.write("\n") 1741 | 1742 | txt.write("Hostnames:\n") 1743 | md.write("### Hostnames\n\n") 1744 | xml.write("\n") 1745 | html.write("

Hostnames

\n
    \n") 1746 | 1747 | for host in info['all_hosts']: 1748 | txt.write("{}\n".format(host)) 1749 | md.write("* {}\n".format(host)) 1750 | xml.write("{}\n".format(host)) 1751 | html.write("
  • {}
  • \n".format(host)) 1752 | 1753 | html.write("
\n") 1754 | xml.write("
\n") 1755 | txt.write("\n") 1756 | md.write("\n") 1757 | 1758 | txt.write("Subdomains:\n") 1759 | md.write("### Subdomains\n\n") 1760 | xml.write("\n") 1761 | html.write("

Subdomains

\n
    \n") 1762 | 1763 | for host in info['domains']: 1764 | txt.write("{}\n".format(host)) 1765 | md.write("* {}\n".format(host)) 1766 | xml.write("{}\n".format(host)) 1767 | html.write("
  • {}
  • \n".format(host)) 1768 | 1769 | html.write("
\n") 1770 | xml.write("\n") 1771 | txt.write("\n") 1772 | md.write("\n") 1773 | xml.write("
\n") 1774 | 1775 | ####################################################### 1776 | 1777 | 1778 | def _get_key(service_name): 1779 | """ 1780 | Parses the api keys file and returns the corresponding key 1781 | Doesn't work for censys. Works only for services with one key 1782 | 1783 | :param service_name: The online service e.g spyse 1784 | :type service_name: :class: `str` 1785 | """ 1786 | key = None 1787 | if checkFile(): 1788 | with open(KEYS_FILE, 'r') as fin: 1789 | lines = fin.readlines() 1790 | for l in lines: 1791 | if l.lower().startswith(service_name) and ':' in l: 1792 | try: 1793 | key = l.strip(' \r\n').split(':')[1] 1794 | except IndexError: 1795 | print(KEYS_FILE 1796 | + " doesnt follow the correct format name:value") 1797 | 1798 | return key 1799 | 1800 | ####################################################### 1801 | 1802 | 1803 | def MainFunc(): 1804 | print(message) 1805 | 1806 | report_buckets = 50 1807 | info = { 1808 | 'all_emails': [], 1809 | 'all_hosts': [], 1810 | 'domains': [], 1811 | 'public': [] 1812 | } 1813 | uas = [] 1814 | 1815 | user_agent_strings_file = 'common-ua.txt' 1816 | timeouts = [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] 1817 | report_buckets = 10 1818 | modes = ['basic', 'nongoogle', 'whois', 'dns', 'revdns', 'vhosts', 'google', 'bing', 'yahoo', 1819 | 'ask', 'dogpile', 'yandex', 'linkedin', 'twitter', 'youtube', 'reddit', 1820 | 'github', 'instagram', 'crt', 'pgp', 'netcraft', 'virustotal', 'dnsdump', 'shodan', 1821 | 'censys', 'spyse'] 1822 | 1823 | parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter) 1824 | parser.add_argument("-d", '--domain', action="store", metavar='DOMAIN', dest='domain', 1825 | default=None, type=CheckDomain, help="Domain to search.") 1826 | parser.add_argument("-s", '--server', action="store", metavar='NAMESERVER', dest='dnsserver', 1827 | default='8.8.8.8', type=CheckDomainOrIP, help="DNS server to use.") 1828 | parser.add_argument('-x', '--proxy', action="store", metavar='PROXY', dest='proxy', 1829 | default=None, type=str, help="Use a proxy server when retrieving " 1830 | "results from search engines (eg. " 1831 | "'-x http://127.0.0.1:8080')") 1832 | parser.add_argument("-l", '--limit', action="store", metavar='LIMIT', dest='limit', type=int, 1833 | default=100, 1834 | help="Limit the number of search engine results (default: 100).") 1835 | parser.add_argument("-i", '--info', action="store", metavar='MODE', dest='mode', type=str, 1836 | default='basic', 1837 | help="Limit information gathering (" + ','.join(modes) + ").") 1838 | parser.add_argument('-o', '--output', action='store', metavar='BASENAME', dest='basename', 1839 | type=str, default=None, help='Output in the four major formats at once ' 1840 | '(markdown, txt, xml and html).') 1841 | parser.add_argument('-k', '--shodan-key', action='store', metavar='API-KEY', dest='shodankey', 1842 | type=str, default=None, 1843 | help='API key to use with Shodan search (MODE="shodan")') 1844 | parser.add_argument('-e', '--spyse-key', action='store', metavar='SPYSE_API_KEY', 1845 | dest='spysekey', type=str, default=None) 1846 | # censys.io 1847 | parser.add_argument('-m', '--match', default=None, 1848 | help='Highlight a string within an existing query result') 1849 | parser.add_argument('-f', '--filter', default=None, 1850 | help="Filter the JSON keys to display for each result, use value 'help' " 1851 | "for interesting fields") 1852 | parser.add_argument('--count', action='store_true', help='Print the count result and exit') 1853 | parser.add_argument('-R', '--report', default=None, 1854 | help="Stats on given field (use value 'help' for listing interesting " 1855 | "fields)'") 1856 | parser.add_argument('-B', '--report_bucket', default=report_buckets, 1857 | help='Bucket len in report mode (default: %s)' % report_buckets) 1858 | # query filter shortcuts - censys.io 1859 | parser.add_argument('-1', '--censys_api_id', action='store', metavar='CENSYS_API_ID', 1860 | dest='censys_api_id', type=str, default=None, 1861 | help='Provide the authentication ID for the censys.io search engine') 1862 | parser.add_argument('-2', '--censys_api_secret', action='store', metavar='CENSYS_API_SECRET', 1863 | dest='censys_api_secret', type=str, default=None, 1864 | help='Provide the secret hash for the censys.io search engine') 1865 | parser.add_argument('-r', '--read_api_keys', action='store_true', 1866 | help="Read the API Keys stored in api_keys.txt file. (e.g. '-i censys -r')") 1867 | parser.add_argument('-u', '--update_api_keys', action='store_true', 1868 | help="Update the API Keys stored in api_keys.txt file. (e.g. '-i censys -u')") 1869 | parser.add_argument('-a', '--asn', metavar='ASN', dest='asn', type=str, default=None, 1870 | help='Filter with ASN (e.g 5408 for GR-NET AS)') 1871 | parser.add_argument('-c', '--country', metavar='COUNTRY', dest='country', type=str, 1872 | default=None, 1873 | help='Filter with country') 1874 | parser.add_argument('-O', '--cert-org', metavar='CERT_ORG', dest='cert_org', type=str, 1875 | default=None, 1876 | help='Certificate issued to organization') 1877 | parser.add_argument('-I', '--cert-issuer', metavar='CERT_ISSUER', dest='cert_issuer', type=str, 1878 | default=None, 1879 | help='Certificate issued by organization') 1880 | parser.add_argument('-z', '--cert-host', metavar='CERT_HOST', dest='cert_host', type=str, 1881 | default=None, 1882 | help='hostname Certificate is issued to') 1883 | parser.add_argument('-S', '--http-server', metavar='HTTP_SERVER', dest='http_server', type=str, 1884 | default=None, 1885 | help='Server header') 1886 | parser.add_argument('-t', '--html-title', metavar='HTML_TITLE', dest='html_title', type=str, 1887 | default=None, 1888 | help='Filter on html page title') 1889 | parser.add_argument('-b', '--html-body', metavar='HTML_BODY', dest='html_body', type=str, 1890 | default=None, 1891 | help='Filter on html body content') 1892 | parser.add_argument('-T', '--tags', default=None, 1893 | help='Filter on specific tags. e.g: -T tag1,tag2,... (use keyword \'list\'' 1894 | ' to list usual tags') 1895 | parser.add_argument('-L', '--Limit', default=float('inf'), help='Limit to N results') 1896 | parser.add_argument('-D', '--debug', action='store_true', help='Debug information') 1897 | parser.add_argument('-v', '--verbose', action='store_true', help='Print raw JSON records') 1898 | parser.add_argument('-H', '--html', action='store_true', 1899 | help='Renders html elements in a browser') 1900 | parser.add_argument('arguments', metavar='arguments', nargs='*', help='Censys query') 1901 | 1902 | args = parser.parse_args() 1903 | 1904 | match = str(args.match) 1905 | 1906 | # TODO tags/fields missing: 1907 | # fire help before doing any request 1908 | # if args.tags in ['list', 'help']: 1909 | # pp.pprint(tags_available) 1910 | # sys.exit(0) 1911 | # if args.report in ['list', 'help']: 1912 | # pp.pprint(report_fields) 1913 | # sys.exit(0) 1914 | # if args.filter in ['list', 'help']: 1915 | # pp.pprint(filter_fields) 1916 | # sys.exit(0) 1917 | 1918 | if args.report_bucket: 1919 | report_buckets = args.report_bucket 1920 | 1921 | if len(sys.argv) == 1: 1922 | parser.print_help() 1923 | sys.exit() 1924 | 1925 | # args = parser.parse_args() 1926 | info['domain'] = args.domain 1927 | info['proxies'] = {} 1928 | 1929 | ####################################################### 1930 | 1931 | # Load User-Agents strings from file # 1932 | if os.path.isfile(user_agent_strings_file): 1933 | uas = [line.strip() for line in open(user_agent_strings_file)] 1934 | else: 1935 | print("[-] An error occured while loading user-agent strings from file") 1936 | sys.exit() 1937 | 1938 | output_basename = None 1939 | if args.basename is not None: 1940 | output_basename = args.basename 1941 | 1942 | ####################################################### 1943 | 1944 | # information # 1945 | info['mode'] = [x.strip() for x in args.mode.lower().split(',')] 1946 | info['limit'] = args.limit 1947 | info['dnsserver'] = args.dnsserver 1948 | info['ip'] = VerifyHostname(info['domain']) 1949 | 1950 | if args.proxy: 1951 | print("[+] Proxy will ONLY be used during search engines searches") 1952 | info['proxies'] = { 1953 | 'http': args.proxy, 1954 | 'https': args.proxy, 1955 | } 1956 | 1957 | InfoReport(info['mode'], info['limit'], info['dnsserver'], args.proxy, info['domain'], 1958 | info['ip'], len(uas), output_basename) 1959 | 1960 | ####################################################### 1961 | 1962 | # Whois query report # 1963 | if any(i in ['whois', 'basic', 'nongoogle'] for i in info['mode']): 1964 | print("[+] Whois lookup") 1965 | print("----------------") 1966 | info['whois'] = WhoisQuery(info['domain']) 1967 | WhoisReport(info['whois'], output_basename) 1968 | 1969 | ####################################################### 1970 | 1971 | # DNS records report # 1972 | if any(i in ['dns', 'basic', 'nongoogle'] for i in info['mode']): 1973 | print("[+] DNS queries") 1974 | print("---------------") 1975 | info['dns'] = DnsQuery(info['domain'], info['dnsserver']) 1976 | DNSReport(info['dns'], output_basename) 1977 | 1978 | ####################################################### 1979 | 1980 | # IP Reverse DNS lookup report # 1981 | if any(i in ['revdns', 'basic', 'nongoogle'] for i in info['mode']): 1982 | print("[+] Reverse DNS Lookup") 1983 | print("----------------------") 1984 | info['revdns'] = ReverseIPQuery(info['ip'], info['dnsserver']) 1985 | ReverseDNSReport(info['ip'], info['revdns'], output_basename) 1986 | 1987 | ####################################################### 1988 | 1989 | # Bing Virtual Hosts search results report ## 1990 | if any(i in ['vhosts', 'basic', 'nongoogle'] for i in info['mode']): 1991 | print("[+] Bing Virtual Hosts") 1992 | print("----------------------") 1993 | info['bingvhosts'] = BingVHostsSearch(info['ip'], info['limit'], uas, 1994 | info['proxies'], timeouts) 1995 | VHostsReport(info['bingvhosts'], output_basename) 1996 | 1997 | ####################################################### 1998 | 1999 | # Google search # 2000 | if any(i in ['google'] for i in info['mode']): 2001 | print("[+] Searching in Google..") 2002 | temp1, temp2 = GoogleSearch(info['domain'], info['limit'], uas, info['proxies'], timeouts) 2003 | info['all_emails'].extend(temp1) 2004 | info['all_hosts'].extend(temp2) 2005 | Report("Google", temp1, temp2, output_basename) 2006 | 2007 | ####################################################### 2008 | 2009 | # Bing search # 2010 | if any(i in ['bing', 'nongoogle'] for i in info['mode']): 2011 | print("[+] Searching in Bing..") 2012 | temp1, temp2 = BingSearch(info['domain'], info['limit'], uas, info['proxies'], timeouts) 2013 | info['all_emails'].extend(temp1) 2014 | info['all_hosts'].extend(temp2) 2015 | Report("Bing", temp1, temp2, output_basename) 2016 | 2017 | ####################################################### 2018 | 2019 | # Yahoo search # 2020 | if any(i in ['yahoo', 'nongoogle'] for i in info['mode']): 2021 | print("[+] Searching in Yahoo..") 2022 | temp1, temp2 = YahooSearch(info['domain'], info['limit'], uas, info['proxies'], timeouts) 2023 | info['all_emails'].extend(temp1) 2024 | info['all_hosts'].extend(temp2) 2025 | Report("Yahoo", temp1, temp2, output_basename) 2026 | 2027 | ####################################################### 2028 | 2029 | # Shodan search # 2030 | if any(i in ['shodan'] for i in info['mode']): 2031 | 2032 | # pdb.set_trace() 2033 | 2034 | if args.shodankey is None: 2035 | print("[-] API key required for the Shodan search: '-k API-KEY, --shodan-key API-KEY'") 2036 | sys.exit() 2037 | print("[+] Searching in Shodan..") 2038 | print("-------------------") 2039 | 2040 | results = ShodanSearch(args.shodankey, info['domain'], info['ip'], uas, info['proxies'], 2041 | timeouts) 2042 | ShodanReport(results, output_basename) 2043 | 2044 | ####################################################### 2045 | 2046 | # ASK search # 2047 | if any(i in ['ask', 'nongoogle'] for i in info['mode']): 2048 | print("[+] Searching in ASK..") 2049 | temp1, temp2 = AskSearch(info['domain'], 5, uas, info['proxies'], timeouts) # 5 pages 2050 | info['all_emails'].extend(temp1) 2051 | info['all_hosts'].extend(temp2) 2052 | Report("ASK", temp1, temp2, output_basename) 2053 | 2054 | ####################################################### 2055 | 2056 | # Dogpile search # 2057 | if any(i in ['dogpile', 'nongoogle'] for i in info['mode']): 2058 | print("[+] Searching in Dogpile..") 2059 | temp1, temp2 = DogpileSearch(info['domain'], info['limit'], uas, info['proxies'], timeouts) 2060 | info['all_emails'].extend(temp1) 2061 | info['all_hosts'].extend(temp2) 2062 | Report("Dogpile", temp1, temp2, output_basename) 2063 | 2064 | ####################################################### 2065 | 2066 | # Yandex search # 2067 | if any(i in ['yandex', 'nongoogle'] for i in info['mode']): 2068 | print("[+] Searching in Yandex..") 2069 | temp1, temp2 = YandexSearch(info['domain'], info['limit'], uas, info['proxies'], timeouts) 2070 | info['all_emails'].extend(temp1) 2071 | info['all_hosts'].extend(temp2) 2072 | Report("Yandex", temp1, temp2, output_basename) 2073 | 2074 | ####################################################### 2075 | 2076 | # crt search # 2077 | if any(i in ['crt', 'nongoogle'] for i in info['mode']): 2078 | print("[+] Searching in Crt..") 2079 | temp = CrtSearch(info['domain'], uas, info['proxies']) 2080 | info['all_hosts'].extend(temp) 2081 | HostnamesReport("CRT", temp, output_basename) 2082 | 2083 | ####################################################### 2084 | 2085 | # dnsdumpster search # 2086 | if any(i in ['dnsdump', 'nongoogle'] for i in info['mode']): 2087 | print("[+] Searching in DNSdumpster..") 2088 | temp = DNSDumpsterSearch(info['domain'], uas, info['proxies']) 2089 | info['all_hosts'].extend(temp) 2090 | HostnamesReport("DNSdumpster", temp, output_basename) 2091 | 2092 | ####################################################### 2093 | 2094 | # PGP search # 2095 | if any(i in ['pgp', 'nongoogle'] for i in info['mode']): 2096 | print("[+] Searching in PGP..") 2097 | temp1, temp2 = PGPSearch(info['domain'], uas, info['proxies']) 2098 | info['all_emails'].extend(temp1) 2099 | info['all_hosts'].extend(temp2) 2100 | Report("PGP", temp1, temp2, output_basename) 2101 | 2102 | ####################################################### 2103 | 2104 | # netcraft search # 2105 | if any(i in ['netcraft', 'nongoogle'] for i in info['mode']): 2106 | print("[+] Searching in Netcraft..") 2107 | temp = NetcraftSearch(info['domain'], uas, info['proxies']) 2108 | info['all_hosts'].extend(temp) 2109 | HostnamesReport("Netcraft", temp, output_basename) 2110 | 2111 | ####################################################### 2112 | 2113 | # virustotal search # 2114 | if any(i in ['virustotal', 'nongoogle'] for i in info['mode']): 2115 | print("[+] Searching in VirusTotal..") 2116 | temp = VTSearch(info['domain'], uas, info['proxies']) 2117 | info['all_hosts'].extend(temp) 2118 | HostnamesReport("VirusTotal", temp, output_basename) 2119 | 2120 | ####################################################### 2121 | 2122 | # spyse search # 2123 | if any(i in ['spyse', 'nongoogle'] for i in info['mode']): 2124 | spysekey = args.spysekey or _get_key('spyse') 2125 | if not spysekey: 2126 | print("Api Key for spyse was neither provided in cmd line " 2127 | "nor located inside %s file" % KEYS_FILE) 2128 | else: 2129 | print("[+] Searching in Spyse..") 2130 | temp = SpyseSearch(info['domain'], spysekey, args.limit, info['proxies']) 2131 | subdomains = SpyseGetDomains(temp) 2132 | info['all_hosts'].extend(subdomains) 2133 | SubdomainsReport("SpyseSearch", subdomains, output_basename) 2134 | 2135 | # LinkedIn search # 2136 | if any(i in ['linkedin'] for i in info['mode']): 2137 | print("[+] Searching in LinkedIn..") 2138 | temp1, temp2 = GoogleSearchEngine(info['domain'], 'linkedin.com', info['limit'], uas, 2139 | info['proxies'], timeouts) 2140 | info['all_emails'].extend(temp1) 2141 | info['all_hosts'].extend(temp2) 2142 | Report("LinkedIn", temp1, temp2, output_basename) 2143 | 2144 | ####################################################### 2145 | 2146 | # Twitter search # 2147 | if any(i in ['twitter'] for i in info['mode']): 2148 | print("[+] Searching in Twitter..") 2149 | temp1, temp2 = GoogleSearchEngine(info['domain'], "twitter.com", info['limit'], uas, 2150 | info['proxies'], timeouts) 2151 | info['all_emails'].extend(temp1) 2152 | info['all_hosts'].extend(temp2) 2153 | Report("Twitter", temp1, temp2, output_basename) 2154 | 2155 | ####################################################### 2156 | 2157 | # Youtube search # 2158 | if any(i in ['youtube'] for i in info['mode']): 2159 | print("[+] Searching in Youtube..") 2160 | temp1, temp2 = GoogleSearchEngine(info['domain'], "youtube.com", info['limit'], uas, 2161 | info['proxies'], timeouts) 2162 | info['all_emails'].extend(temp1) 2163 | info['all_hosts'].extend(temp2) 2164 | Report("Youtube", temp1, temp2, output_basename) 2165 | 2166 | ####################################################### 2167 | 2168 | # Reddit search # 2169 | if any(i in ['reddit'] for i in info['mode']): 2170 | print("[+] Searching in Reddit..") 2171 | temp1, temp2 = GoogleSearchEngine(info['domain'], "reddit.com", info['limit'], uas, 2172 | info['proxies'], timeouts) 2173 | info['all_emails'].extend(temp1) 2174 | info['all_hosts'].extend(temp2) 2175 | Report("Reddit", temp1, temp2, output_basename) 2176 | 2177 | ####################################################### 2178 | 2179 | # Github search # 2180 | if any(i in ['github'] for i in info['mode']): 2181 | print("[+] Searching in Github..") 2182 | temp1, temp2 = GoogleSearchEngine(info['domain'], "github.com", info['limit'], uas, 2183 | info['proxies'], timeouts) 2184 | info['all_emails'].extend(temp1) 2185 | info['all_hosts'].extend(temp2) 2186 | Report("Github", temp1, temp2, output_basename) 2187 | 2188 | ####################################################### 2189 | 2190 | # Instagram search # 2191 | if any(i in ['instagram'] for i in info['mode']): 2192 | print("[+] Searching in Instagram..") 2193 | temp1, temp2 = GoogleSearchEngine(info['domain'], "instagram.com", info['limit'], uas, 2194 | info['proxies'], 2195 | timeouts) 2196 | info['all_emails'].extend(temp1) 2197 | info['all_hosts'].extend(temp2) 2198 | Report("Instagram", temp1, temp2, output_basename) 2199 | 2200 | ####################################################### 2201 | 2202 | # Censys.io search # 2203 | if any(i in ['censys'] for i in info['mode']): 2204 | api_id = args.censys_api_id 2205 | api_secret = args.censys_api_secret 2206 | if api_id is not None and api_secret is not None: 2207 | print("[+] Searching in Censys.io..\n") 2208 | res1 = DomainSearchCensys(info['domain'], api_id, api_secret, 2209 | output_basename, info['domains']) 2210 | res2 = CensysPublicScan(api_id, api_secret, output_basename, args, 2211 | report_buckets, match, info['public']) 2212 | if res1 == False and res2 == False: 2213 | print( 2214 | "Please use the available censys.io options in order to perform scanning. " 2215 | "For more information use the '--help' option") 2216 | print() 2217 | else: 2218 | chkstored = checkFile() 2219 | flag = 0 2220 | if chkstored == False: 2221 | chkanswer = input( 2222 | "[!] API Keys not provided. Would you like to store your API keys ? [y/n]: ") 2223 | if chkanswer == 'y': 2224 | stored = createFileAndStoreAPIKeys('censys') 2225 | if stored == "stored": 2226 | print() 2227 | readFileContents() 2228 | print() 2229 | answer1 = input( 2230 | "[*] would you like to continue searching with censys.io ? [y/n] ") 2231 | print() 2232 | if answer1 == 'n': 2233 | flag = 1 2234 | print("[*] Exiting...") 2235 | exit(0) 2236 | if answer1 == 'y': 2237 | with open(KEYS_FILE) as f: 2238 | lines = f.read().splitlines() 2239 | print("[+] Searching in Censys.io..") 2240 | print() 2241 | for line in lines: 2242 | res1 = DomainSearchCensys( 2243 | info['domain'], line.split(":")[1], line.split(":")[2], 2244 | output_basename, info['domains']) 2245 | res2 = CensysPublicScan( 2246 | line.split(":")[1], line.split(":")[2], output_basename, 2247 | args, report_buckets, match, info['public']) 2248 | if res1 == False and res2 == False: 2249 | print('Please use the available censys.io options in order' 2250 | 'to perform scanning. For more information use the ' 2251 | '\'--help\' option') 2252 | print() 2253 | flag = 1 2254 | else: 2255 | print("[x] API keys has not been stored..") 2256 | print("[*] Exiting...") 2257 | exit(0) 2258 | if chkanswer == 'n': 2259 | print( 2260 | "[!] Please provide the API keys in the command line to continue searching") 2261 | print("[*] Exiting....") 2262 | exit(0) 2263 | 2264 | if chkstored is True: 2265 | if args.update_api_keys is True and args.mode == 'censys' and flag != 1: 2266 | keysupdate = updateAPIKeys('censys') 2267 | if keysupdate == 'n': 2268 | print("[x] the keys have not been updated") 2269 | print("[*] Exiting...") 2270 | exit(0) 2271 | print() 2272 | else: 2273 | print("[!] the keys have been successfully updated!") 2274 | answer1 = input( 2275 | "[*] would you like to continue searching with censys.io ? [y/n] ") 2276 | print() 2277 | if answer1 == 'y': 2278 | with open(KEYS_FILE) as f: 2279 | lines = f.read().splitlines() 2280 | print("[+] Searching in Censys.io..") 2281 | print() 2282 | for line in lines: 2283 | res1 = DomainSearchCensys( 2284 | info['domain'], line.split(":")[1], line.split(":")[2], 2285 | output_basename, info['domains']) 2286 | res2 = CensysPublicScan( 2287 | line.split(":")[1], line.split(":")[2], output_basename, 2288 | args, report_buckets, match, info['public']) 2289 | if res1 == False and res2 == False: 2290 | print("Please use the available censys.io options in order" 2291 | " to perform scanning. For more information use " 2292 | " the '--help' option\n") 2293 | else: 2294 | print("[*] Exiting...") 2295 | exit(0) 2296 | else: 2297 | if args.read_api_keys is not True: 2298 | with open(KEYS_FILE) as f: 2299 | lines = f.read().splitlines() 2300 | print("[+] Searching in Censys.io..") 2301 | print() 2302 | for line in lines: 2303 | res1 = DomainSearchCensys( 2304 | info['domain'], line.split(":")[1], line.split(":")[2], 2305 | output_basename, info['domains']) 2306 | res2 = CensysPublicScan( 2307 | line.split(":")[1], line.split(":")[2], output_basename, args, 2308 | report_buckets, match, info['public']) 2309 | if res1 == False and res2 == False: 2310 | print("Please use the available censys.io options in order to " 2311 | "perform scanning. For more information use the '--help'" 2312 | " option\n") 2313 | 2314 | if args.read_api_keys is True and args.mode == 'censys' and flag != 1: 2315 | print() 2316 | readFileContents() 2317 | print() 2318 | answer1 = input( 2319 | "[*] would you like to continue searching with censys.io ? [y/n] ") 2320 | print() 2321 | if answer1 == 'y': 2322 | with open(KEYS_FILE) as f: 2323 | lines = f.read().splitlines() 2324 | print("[+] Searching in Censys.io..") 2325 | print() 2326 | for line in lines: 2327 | res1 = DomainSearchCensys( 2328 | info['domain'], line.split(":")[1], line.split(":")[2], 2329 | output_basename, info['domains']) 2330 | res2 = CensysPublicScan( 2331 | line.split(":")[1], line.split(":")[2], output_basename, args, 2332 | report_buckets, match, info['public']) 2333 | if res1 is False and res2 is False: 2334 | print( 2335 | "Please use the available censys.io options in order to " 2336 | "perform scanning. For more information use the " 2337 | "'--help' option\n") 2338 | else: 2339 | print("[*] Exiting...") 2340 | exit(0) 2341 | 2342 | ####################################################### 2343 | 2344 | # Search Results Final Report # 2345 | info['all_emails'] = sorted(set(info['all_emails'])) 2346 | info['all_hosts'] = sorted(set(info['all_hosts'])) 2347 | info['domains'] = sorted(set(info['domains'])) 2348 | FinalReport(info, output_basename) 2349 | 2350 | ####################################################### 2351 | 2352 | # Close tags for xml and html # 2353 | if output_basename: 2354 | output = output_basename + ".xml" 2355 | output1 = output_basename + ".html" 2356 | 2357 | with open(output, 'a') as xml, open(output1, 'a') as html: 2358 | xml.write("
\n") 2359 | html.write("\n") 2360 | 2361 | 2362 | ####################################################### 2363 | 2364 | if __name__ == '__main__': 2365 | 2366 | try: 2367 | MainFunc() 2368 | except KeyboardInterrupt: 2369 | print("Search interrupted by user..") 2370 | except Exception as e: 2371 | if DEBUG: 2372 | print(e) 2373 | sys.exit() 2374 | 2375 | ####################################################### 2376 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | validators 2 | python-whois 3 | dnspython 4 | requests 5 | shodan 6 | censys --------------------------------------------------------------------------------