diff --git a/CHANGELOG/1.3.0/date.md b/CHANGELOG/1.3.0/date.md new file mode 100644 index 00000000..ca52a58b --- /dev/null +++ b/CHANGELOG/1.3.0/date.md @@ -0,0 +1 @@ +Sep 2nd, 2020 \ No newline at end of file diff --git a/CHANGELOG/1.4.0/date.md b/CHANGELOG/1.4.0/date.md new file mode 100644 index 00000000..70af2e91 --- /dev/null +++ b/CHANGELOG/1.4.0/date.md @@ -0,0 +1 @@ +Dec 23rd, 2020 diff --git a/CHANGELOG/1.4.0/update_nuclei_fields.md b/CHANGELOG/1.4.0/update_nuclei_fields.md new file mode 100644 index 00000000..9d67a341 --- /dev/null +++ b/CHANGELOG/1.4.0/update_nuclei_fields.md @@ -0,0 +1 @@ +Update the fields of the nuclei output used to create a vuln \ No newline at end of file diff --git a/CHANGELOG/current/add_multilinejson_format.md b/CHANGELOG/1.4.0b1/add_multilinejson_format.md similarity index 100% rename from CHANGELOG/current/add_multilinejson_format.md rename to CHANGELOG/1.4.0b1/add_multilinejson_format.md diff --git a/CHANGELOG/current/add_ncrack.md b/CHANGELOG/1.4.0b1/add_ncrack.md similarity index 100% rename from CHANGELOG/current/add_ncrack.md rename to CHANGELOG/1.4.0b1/add_ncrack.md diff --git a/CHANGELOG/current/add_nuclei.md b/CHANGELOG/1.4.0b1/add_nuclei.md similarity index 100% rename from CHANGELOG/current/add_nuclei.md rename to CHANGELOG/1.4.0b1/add_nuclei.md diff --git a/CHANGELOG/current/add_sslyze_json.md b/CHANGELOG/1.4.0b1/add_sslyze_json.md similarity index 100% rename from CHANGELOG/current/add_sslyze_json.md rename to CHANGELOG/1.4.0b1/add_sslyze_json.md diff --git a/CHANGELOG/current/add_whatweb.md b/CHANGELOG/1.4.0b1/add_whatweb.md similarity index 100% rename from CHANGELOG/current/add_whatweb.md rename to CHANGELOG/1.4.0b1/add_whatweb.md diff --git a/CHANGELOG/1.4.0b1/date.md b/CHANGELOG/1.4.0b1/date.md new file mode 100644 index 00000000..c691eb27 --- /dev/null +++ b/CHANGELOG/1.4.0b1/date.md @@ -0,0 +1 @@ +Dec 14th, 2020 \ No newline at end of file diff --git a/CHANGELOG/current/fix_arachni_ip_missing.md b/CHANGELOG/1.4.0b1/fix_arachni_ip_missing.md similarity index 100% rename from CHANGELOG/current/fix_arachni_ip_missing.md rename to CHANGELOG/1.4.0b1/fix_arachni_ip_missing.md diff --git a/CHANGELOG/current/fix_netsparker.md b/CHANGELOG/1.4.0b1/fix_netsparker.md similarity index 100% rename from CHANGELOG/current/fix_netsparker.md rename to CHANGELOG/1.4.0b1/fix_netsparker.md diff --git a/CHANGELOG/current/fix_whois.md b/CHANGELOG/1.4.0b1/fix_whois.md similarity index 100% rename from CHANGELOG/current/fix_whois.md rename to CHANGELOG/1.4.0b1/fix_whois.md diff --git a/CHANGELOG/current/modify_json_reports_detection.md b/CHANGELOG/1.4.0b1/modify_json_reports_detection.md similarity index 100% rename from CHANGELOG/current/modify_json_reports_detection.md rename to CHANGELOG/1.4.0b1/modify_json_reports_detection.md diff --git a/CHANGELOG/1.4.0b2/date.md b/CHANGELOG/1.4.0b2/date.md new file mode 100644 index 00000000..585afaf5 --- /dev/null +++ b/CHANGELOG/1.4.0b2/date.md @@ -0,0 +1 @@ +Dec 15th, 2020 \ No newline at end of file diff --git a/CHANGELOG/1.4.0b2/fix_nuclei.md b/CHANGELOG/1.4.0b2/fix_nuclei.md new file mode 100644 index 00000000..ef0a22eb --- /dev/null +++ b/CHANGELOG/1.4.0b2/fix_nuclei.md @@ -0,0 +1 @@ +Fix nuclei plugin bug when url is None \ No newline at end of file diff --git a/CHANGELOG/1.4.1/add_microsoft_baseline.md b/CHANGELOG/1.4.1/add_microsoft_baseline.md new file mode 100644 index 00000000..bf729f6a --- /dev/null +++ b/CHANGELOG/1.4.1/add_microsoft_baseline.md @@ -0,0 +1 @@ +ADD microsoft baseline security analyzer plugin diff --git a/CHANGELOG/1.4.1/add_nextnet.md b/CHANGELOG/1.4.1/add_nextnet.md new file mode 100644 index 00000000..b9f53277 --- /dev/null +++ b/CHANGELOG/1.4.1/add_nextnet.md @@ -0,0 +1 @@ +ADD nextnet plugin diff --git a/CHANGELOG/1.4.1/add_openscap.md b/CHANGELOG/1.4.1/add_openscap.md new file mode 100644 index 00000000..4cfac92f --- /dev/null +++ b/CHANGELOG/1.4.1/add_openscap.md @@ -0,0 +1 @@ +ADD openscap plugin diff --git a/CHANGELOG/1.4.1/date.md b/CHANGELOG/1.4.1/date.md new file mode 100644 index 00000000..1c7356eb --- /dev/null +++ b/CHANGELOG/1.4.1/date.md @@ -0,0 +1 @@ +Feb 26th, 2021 diff --git a/CHANGELOG/1.4.1/fix_nessus_plugin.md b/CHANGELOG/1.4.1/fix_nessus_plugin.md new file mode 100644 index 00000000..34dba3ab --- /dev/null +++ b/CHANGELOG/1.4.1/fix_nessus_plugin.md @@ -0,0 +1 @@ +FIX old versions of Nessus plugins bugs diff --git a/CHANGELOG/1.4.2/date.md b/CHANGELOG/1.4.2/date.md new file mode 100644 index 00000000..45d9b329 --- /dev/null +++ b/CHANGELOG/1.4.2/date.md @@ -0,0 +1 @@ +Mar 10th, 2021 diff --git a/CHANGELOG/1.4.2/fix_bug_in_sslyze_output_file.md b/CHANGELOG/1.4.2/fix_bug_in_sslyze_output_file.md new file mode 100644 index 00000000..ce00a3c1 --- /dev/null +++ b/CHANGELOG/1.4.2/fix_bug_in_sslyze_output_file.md @@ -0,0 +1 @@ +Fix bug with sslyze output file diff --git a/CHANGELOG/1.4.2/fix_sslyze_plugin.md b/CHANGELOG/1.4.2/fix_sslyze_plugin.md new file mode 100644 index 00000000..36cd2cc4 --- /dev/null +++ b/CHANGELOG/1.4.2/fix_sslyze_plugin.md @@ -0,0 +1 @@ +FIX change id sslyze for JSON/XML diff --git a/CHANGELOG/1.4.3/date.md b/CHANGELOG/1.4.3/date.md new file mode 100644 index 00000000..8886c2b5 --- /dev/null +++ b/CHANGELOG/1.4.3/date.md @@ -0,0 +1 @@ +Mar 17th, 2021 diff --git a/CHANGELOG/1.4.3/new_ignore_info_option.md b/CHANGELOG/1.4.3/new_ignore_info_option.md new file mode 100644 index 00000000..64a23214 --- /dev/null +++ b/CHANGELOG/1.4.3/new_ignore_info_option.md @@ -0,0 +1 @@ +Add Ignore information vulnerabilities option diff --git a/CHANGELOG/1.4.4/csv_plugin_dont_user_ignore_info.md b/CHANGELOG/1.4.4/csv_plugin_dont_user_ignore_info.md new file mode 100644 index 00000000..78c8492c --- /dev/null +++ b/CHANGELOG/1.4.4/csv_plugin_dont_user_ignore_info.md @@ -0,0 +1 @@ +Faraday CSV Plugin do not consider ignore_info diff --git a/CHANGELOG/1.4.4/date.md b/CHANGELOG/1.4.4/date.md new file mode 100644 index 00000000..2bf6cb08 --- /dev/null +++ b/CHANGELOG/1.4.4/date.md @@ -0,0 +1 @@ +Mar 30th, 2021 diff --git a/CHANGELOG/1.4.5/add_bandit_plugin.md b/CHANGELOG/1.4.5/add_bandit_plugin.md new file mode 100644 index 00000000..32347321 --- /dev/null +++ b/CHANGELOG/1.4.5/add_bandit_plugin.md @@ -0,0 +1 @@ +Add Bandit plugin diff --git a/CHANGELOG/1.4.5/change_burp_fields.md b/CHANGELOG/1.4.5/change_burp_fields.md new file mode 100644 index 00000000..63529a11 --- /dev/null +++ b/CHANGELOG/1.4.5/change_burp_fields.md @@ -0,0 +1 @@ +Use background for description and detail for data en Burp plugin. diff --git a/CHANGELOG/1.4.5/date.md b/CHANGELOG/1.4.5/date.md new file mode 100644 index 00000000..0d5ebf9e --- /dev/null +++ b/CHANGELOG/1.4.5/date.md @@ -0,0 +1 @@ +Apr 15th, 2021 diff --git a/CHANGELOG/1.4.5/fix_appscan.md b/CHANGELOG/1.4.5/fix_appscan.md new file mode 100644 index 00000000..754678a8 --- /dev/null +++ b/CHANGELOG/1.4.5/fix_appscan.md @@ -0,0 +1 @@ +Rewrite Appscan Plugin diff --git a/CHANGELOG/1.4.5/parse_nmap_vulnes.md b/CHANGELOG/1.4.5/parse_nmap_vulnes.md new file mode 100644 index 00000000..6e79a68c --- /dev/null +++ b/CHANGELOG/1.4.5/parse_nmap_vulnes.md @@ -0,0 +1 @@ +Parse Nmap vulners script data diff --git a/CHANGELOG/1.4.6/add-attribute_command_for_plugins_of_all_command.md b/CHANGELOG/1.4.6/add-attribute_command_for_plugins_of_all_command.md new file mode 100644 index 00000000..1b2533a8 --- /dev/null +++ b/CHANGELOG/1.4.6/add-attribute_command_for_plugins_of_all_command.md @@ -0,0 +1,3 @@ +- add attribute "command" for the pluggins of each command +- adding test in test_command +- change some regex in self._command_regex \ No newline at end of file diff --git a/CHANGELOG/1.4.6/add_hostnames_to_cached_host.md b/CHANGELOG/1.4.6/add_hostnames_to_cached_host.md new file mode 100644 index 00000000..69bdad0c --- /dev/null +++ b/CHANGELOG/1.4.6/add_hostnames_to_cached_host.md @@ -0,0 +1 @@ +[FIX] add hostnames if host is already cached diff --git a/CHANGELOG/1.4.6/add_naabu_plugin.md b/CHANGELOG/1.4.6/add_naabu_plugin.md new file mode 100644 index 00000000..99102b7c --- /dev/null +++ b/CHANGELOG/1.4.6/add_naabu_plugin.md @@ -0,0 +1 @@ +Add Naabu plugin diff --git a/CHANGELOG/1.4.6/add_sonarqube.md b/CHANGELOG/1.4.6/add_sonarqube.md new file mode 100644 index 00000000..330d55cb --- /dev/null +++ b/CHANGELOG/1.4.6/add_sonarqube.md @@ -0,0 +1 @@ +Add Sonarqube plugin diff --git a/CHANGELOG/1.4.6/change_list_plugins.md b/CHANGELOG/1.4.6/change_list_plugins.md new file mode 100644 index 00000000..5bf08716 --- /dev/null +++ b/CHANGELOG/1.4.6/change_list_plugins.md @@ -0,0 +1 @@ +Add version and change list_plugins style diff --git a/CHANGELOG/1.4.6/clean_code.md b/CHANGELOG/1.4.6/clean_code.md new file mode 100644 index 00000000..d306b2ee --- /dev/null +++ b/CHANGELOG/1.4.6/clean_code.md @@ -0,0 +1 @@ +FIX unused import, innecesary list compression and unused variables \ No newline at end of file diff --git a/CHANGELOG/1.4.6/date.md b/CHANGELOG/1.4.6/date.md new file mode 100644 index 00000000..8c3ac607 --- /dev/null +++ b/CHANGELOG/1.4.6/date.md @@ -0,0 +1 @@ +May 14th, 2021 diff --git a/CHANGELOG/1.4.6/error_when_import_report_of_metasploit_with_faraday_plugins.md b/CHANGELOG/1.4.6/error_when_import_report_of_metasploit_with_faraday_plugins.md new file mode 100644 index 00000000..989f5f0e --- /dev/null +++ b/CHANGELOG/1.4.6/error_when_import_report_of_metasploit_with_faraday_plugins.md @@ -0,0 +1 @@ +FIX metasploit report when the web-site-id is null \ No newline at end of file diff --git a/CHANGELOG/1.4.6/fix_nmap_port_status.md b/CHANGELOG/1.4.6/fix_nmap_port_status.md new file mode 100644 index 00000000..52a24744 --- /dev/null +++ b/CHANGELOG/1.4.6/fix_nmap_port_status.md @@ -0,0 +1 @@ +Fix port stats in nmap diff --git a/CHANGELOG/1.4.6/fixup_sslyze.md b/CHANGELOG/1.4.6/fixup_sslyze.md new file mode 100644 index 00000000..279e498c --- /dev/null +++ b/CHANGELOG/1.4.6/fixup_sslyze.md @@ -0,0 +1,2 @@ +fixup ssylze +sacar unknown de version= diff --git a/CHANGELOG/1.4.6/issue_in_netsparker.md b/CHANGELOG/1.4.6/issue_in_netsparker.md new file mode 100644 index 00000000..e4e055f1 --- /dev/null +++ b/CHANGELOG/1.4.6/issue_in_netsparker.md @@ -0,0 +1 @@ +ADD remedy into resolution \ No newline at end of file diff --git a/CHANGELOG/1.4.6/update_nuclei.md b/CHANGELOG/1.4.6/update_nuclei.md new file mode 100644 index 00000000..f0ff714a --- /dev/null +++ b/CHANGELOG/1.4.6/update_nuclei.md @@ -0,0 +1 @@ +Support for nuclei 2.3.0 diff --git a/CHANGELOG/1.4.6/when_import_report_of_nessus_we_can_get_more_data.md b/CHANGELOG/1.4.6/when_import_report_of_nessus_we_can_get_more_data.md new file mode 100644 index 00000000..3fe6ad29 --- /dev/null +++ b/CHANGELOG/1.4.6/when_import_report_of_nessus_we_can_get_more_data.md @@ -0,0 +1 @@ +ADD cve, cvss3_base_score, cvss3_vector, exploit_available when import nessus and change the structure of external_id to NESSUS-XXX \ No newline at end of file diff --git a/CHANGELOG/1.4.6/when_import_reports_of_owasp_and_zap_we_might_get_more_data.md b/CHANGELOG/1.4.6/when_import_reports_of_owasp_and_zap_we_might_get_more_data.md new file mode 100644 index 00000000..d9d98473 --- /dev/null +++ b/CHANGELOG/1.4.6/when_import_reports_of_owasp_and_zap_we_might_get_more_data.md @@ -0,0 +1 @@ +ADD more data like attack, params, uri, method, WASC, CWE and format externail_id \ No newline at end of file diff --git a/CHANGELOG/1.5.0/add_nipper_plugin.md b/CHANGELOG/1.5.0/add_nipper_plugin.md new file mode 100644 index 00000000..10aa6052 --- /dev/null +++ b/CHANGELOG/1.5.0/add_nipper_plugin.md @@ -0,0 +1 @@ +Add Nipper Plugin diff --git a/CHANGELOG/1.5.0/add_shodan_plugin.md b/CHANGELOG/1.5.0/add_shodan_plugin.md new file mode 100644 index 00000000..5b2c4c79 --- /dev/null +++ b/CHANGELOG/1.5.0/add_shodan_plugin.md @@ -0,0 +1 @@ +add shodan plugin diff --git a/CHANGELOG/1.5.0/date.md b/CHANGELOG/1.5.0/date.md new file mode 100644 index 00000000..136efa0b --- /dev/null +++ b/CHANGELOG/1.5.0/date.md @@ -0,0 +1 @@ +Jun 28th, 2021 diff --git a/CHANGELOG/1.5.0/fix_acunetix_url_parser.md b/CHANGELOG/1.5.0/fix_acunetix_url_parser.md new file mode 100644 index 00000000..89fe358b --- /dev/null +++ b/CHANGELOG/1.5.0/fix_acunetix_url_parser.md @@ -0,0 +1 @@ +fix acunetix url parser diff --git a/CHANGELOG/1.5.0/fix_netsparker_multihost.md b/CHANGELOG/1.5.0/fix_netsparker_multihost.md new file mode 100644 index 00000000..720241a3 --- /dev/null +++ b/CHANGELOG/1.5.0/fix_netsparker_multihost.md @@ -0,0 +1 @@ +FIX netsparker multi-host \ No newline at end of file diff --git a/CHANGELOG/1.5.0/fixup_ssylyze_desc_data.md b/CHANGELOG/1.5.0/fixup_ssylyze_desc_data.md new file mode 100644 index 00000000..2f3a01c3 --- /dev/null +++ b/CHANGELOG/1.5.0/fixup_ssylyze_desc_data.md @@ -0,0 +1 @@ +Add vuln details for Certificate Mismatch and move unique details to data, now vulns can be grupped diff --git a/CHANGELOG/1.5.0/many-error-when-parse_the_xml_of_arachni_and_w3af.md b/CHANGELOG/1.5.0/many-error-when-parse_the_xml_of_arachni_and_w3af.md new file mode 100644 index 00000000..b390c4e6 --- /dev/null +++ b/CHANGELOG/1.5.0/many-error-when-parse_the_xml_of_arachni_and_w3af.md @@ -0,0 +1 @@ +ADD more data to plugins arachni and w3af \ No newline at end of file diff --git a/CHANGELOG/1.5.0/run_date_utc.md b/CHANGELOG/1.5.0/run_date_utc.md new file mode 100644 index 00000000..31440832 --- /dev/null +++ b/CHANGELOG/1.5.0/run_date_utc.md @@ -0,0 +1 @@ +Use run_date in UTC diff --git a/CHANGELOG/1.5.0/we_can_get_more_data_of_the_reports_of_openvas.md b/CHANGELOG/1.5.0/we_can_get_more_data_of_the_reports_of_openvas.md new file mode 100644 index 00000000..407f217a --- /dev/null +++ b/CHANGELOG/1.5.0/we_can_get_more_data_of_the_reports_of_openvas.md @@ -0,0 +1 @@ +ADD cvss_base, cpe, threat, severity into references \ No newline at end of file diff --git a/CHANGELOG/RELEASE.md b/CHANGELOG/RELEASE.md deleted file mode 100644 index 4b0ee7be..00000000 --- a/CHANGELOG/RELEASE.md +++ /dev/null @@ -1,22 +0,0 @@ -1.3.0: ---- - * ADD plugin AppSpider - * Add tests to faraday-plugins cli - * add a default value to plugin_version - * Add --output-file parameter to faraday-plugins process command - * Add plugins prowler - * Add plugins ssl labs - * Add support for tenable io - * delete old deprecated methods - * Bug fix: Arachni Plugin 'NoneType' object has no attribute 'find' - * Bug fix: Openvas Plugin - Import xml from OpenVas doesnt work - * Bug fix: QualysWebApp Plugin, error in get info OPERATING_SYSTEM - * Fix Hydra plugin to resolve ip address - * Fix Nessus mod severity HIGH for Low - * Bug Fix: Detect plugins AWS Prowler - * Fix broken xml on nmap plugin - * Add new rdpscan plugin - * UPDATE xml report to appscan - * Update Readme - * Fix how ZAP genereate vulns - diff --git a/COPYING b/COPYING new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/RELEASE.md b/RELEASE.md index 4b0ee7be..d803b3bc 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -1,4 +1,81 @@ -1.3.0: +1.5.0 [Jun 28th, 2021]: +--- + * Add Nipper Plugin + * add shodan plugin + * fix acunetix url parser + * FIX netsparker multi-host + * Add vuln details for Certificate Mismatch and move unique details to data, now vulns can be grupped + * ADD more data to plugins arachni and w3af + * Use run_date in UTC + * ADD cvss_base, cpe, threat, severity into references + +1.4.6 [May 14th, 2021]: +--- + * - add attribute "command" for the pluggins of each command +- adding test in test_command +- change some regex in self._command_regex + * [FIX] add hostnames if host is already cached + * Add Naabu plugin + * Add Sonarqube plugin + * Add version and change list_plugins style + * FIX unused import, innecesary list compression and unused variables + * FIX metasploit report when the web-site-id is null + * Fix port stats in nmap + * fixup ssylze +sacar unknown de version= + * ADD remedy into resolution + * Support for nuclei 2.3.0 + * ADD cve, cvss3_base_score, cvss3_vector, exploit_available when import nessus and change the structure of external_id to NESSUS-XXX + * ADD more data like attack, params, uri, method, WASC, CWE and format externail_id + +1.4.5 [Apr 15th, 2021]: +--- + * Add Bandit plugin + * Use background for description and detail for data en Burp plugin. + * Rewrite Appscan Plugin + * Parse Nmap vulners script data + +1.4.4 [Mar 30th, 2021]: +--- + * Faraday CSV Plugin do not consider ignore_info + +1.4.3 [Mar 17th, 2021]: +--- + * Add Ignore information vulnerabilities option + +1.4.2 [Mar 10th, 2021]: +--- + * Fix bug with sslyze output file + * FIX change id sslyze for JSON/XML + +1.4.1 [Feb 26th, 2021]: +--- + * ADD microsoft baseline security analyzer plugin + * ADD nextnet plugin + * ADD openscap plugin + * FIX old versions of Nessus plugins bugs + +1.4.0 [Dec 23rd, 2020]: +--- + * Update the fields of the nuclei output used to create a vuln + +1.4.0b2 [Dec 15th, 2020]: +--- + * Fix nuclei plugin bug when url is None + +1.4.0b1 [Dec 14th, 2020]: +--- + * Add new plugin base class, for multi line json + * New ncrack plugin + * New nuclei plugin + * New sslyze json plugin + * New WhatWeb plugin + * Fix missing ip in some arachni reports + * Fix change name vuln in Netsparker plugin + * Fix whois plugin, command whois IP not parse data + * Change the way we detect json reports when they are lists of dictionaries + +1.3.0 [Sep 2nd, 2020]: --- * ADD plugin AppSpider * Add tests to faraday-plugins cli diff --git a/faraday_plugins/__init__.py b/faraday_plugins/__init__.py index 19b4f1d6..77f1c8e6 100644 --- a/faraday_plugins/__init__.py +++ b/faraday_plugins/__init__.py @@ -1 +1 @@ -__version__ = '1.3.0' +__version__ = '1.5.0' diff --git a/faraday_plugins/commands.py b/faraday_plugins/commands.py index c4fc1cc7..a5395ad1 100644 --- a/faraday_plugins/commands.py +++ b/faraday_plugins/commands.py @@ -1,13 +1,16 @@ +import getpass import io +import json import logging import os +import shlex +import subprocess import sys -import json + import click -import subprocess -import shlex -import getpass +from tabulate import tabulate +from faraday_plugins import __version__ from faraday_plugins.plugins.manager import PluginsManager, ReportAnalyzer, CommandAnalyzer from faraday_plugins.plugins.plugin import PluginByExtension @@ -21,8 +24,10 @@ root_logger.addHandler(out_hdlr) root_logger.setLevel(logging.DEBUG) +CONTEXT_SETTINGS = dict(help_option_names=['-h', '--help']) -@click.group() +@click.group(context_settings=CONTEXT_SETTINGS) +@click.version_option(__version__, '-v', '--version') def cli(): pass @@ -31,8 +36,10 @@ def cli(): @click.option('-cpf', '--custom-plugins-folder', type=str) def list_plugins(custom_plugins_folder): plugins_manager = PluginsManager(custom_plugins_folder) - click.echo(click.style("Available Plugins:", fg="cyan")) + click.echo(click.style(f"Faraday Plugins v{__version__}", fg="cyan")) + click.echo(click.style("Available Plugins :", fg="cyan")) loaded_plugins = 0 + plugins_data = [] for plugin_id, plugin in plugins_manager.get_plugins(): console_enabled = plugin._command_regex is not None console_enabled_color = "green" if console_enabled else "red" @@ -40,10 +47,14 @@ def list_plugins(custom_plugins_folder): report_enabled = isinstance(plugin, PluginByExtension) report_enabled_color = "green" if report_enabled else "red" report_enabled_text = click.style(f"{'Yes' if report_enabled else 'No'}", fg=report_enabled_color) - click.echo(f"{plugin.id:15} - [Command: {console_enabled_text:>12} - Report: {report_enabled_text:>12}] - {plugin.name} ") - - loaded_plugins += 1 - click.echo(click.style(f"Loaded Plugins: {loaded_plugins}", fg="cyan")) + plugins_data.append({"Name": plugin.name, "ID": plugin.id, "Command": console_enabled_text, + "Report": report_enabled_text}) + click.echo(tabulate( + plugins_data, + headers="keys", + tablefmt="simple", + )) + click.echo(click.style(f"Loaded Plugins: {len(plugins_data)}", fg="cyan")) @cli.command() @@ -52,11 +63,12 @@ def list_plugins(custom_plugins_folder): @click.option('-cpf', '--custom-plugins-folder', type=str) @click.option('--summary', is_flag=True) @click.option('-o', '--output-file', type=click.Path(exists=False)) -def process_report(report_file, plugin_id, custom_plugins_folder, summary, output_file): +@click.option('--ignore-info', is_flag=True, help="Ignore information vulnerabilities") +def process_report(report_file, plugin_id, custom_plugins_folder, summary, output_file, ignore_info): if not os.path.isfile(report_file): click.echo(click.style(f"File {report_file} Don't Exists", fg="red"), err=True) else: - plugins_manager = PluginsManager(custom_plugins_folder) + plugins_manager = PluginsManager(custom_plugins_folder, ignore_info=ignore_info) analyzer = ReportAnalyzer(plugins_manager) if plugin_id: plugin = plugins_manager.get_plugin(plugin_id) @@ -87,8 +99,10 @@ def process_report(report_file, plugin_id, custom_plugins_folder, summary, outpu @click.option('--summary', is_flag=True) @click.option('-o', '--output-file', type=click.Path(exists=False)) @click.option('-sh', '--show-output', is_flag=True) -def process_command(command, plugin_id, custom_plugins_folder, dont_run, summary, output_file, show_output): - plugins_manager = PluginsManager(custom_plugins_folder) +@click.option('--ignore-info', is_flag=True, help="Ignore information vulnerabilities") +def process_command(command, plugin_id, custom_plugins_folder, dont_run, summary, output_file, show_output, + ignore_info): + plugins_manager = PluginsManager(custom_plugins_folder, ignore_info=ignore_info) analyzer = CommandAnalyzer(plugins_manager) if plugin_id: plugin = plugins_manager.get_plugin(plugin_id) diff --git a/faraday_plugins/plugins/manager.py b/faraday_plugins/plugins/manager.py index 9472c7dc..1ee8a85c 100644 --- a/faraday_plugins/plugins/manager.py +++ b/faraday_plugins/plugins/manager.py @@ -1,26 +1,21 @@ +import csv +import json import logging -import traceback -import re import os -import sys -import json import pkgutil +import re +import sys +import traceback +import xml.etree.ElementTree as ET import zipfile from importlib import import_module from importlib.machinery import SourceFileLoader -import csv from io import StringIO from . import repo logger = logging.getLogger("faraday").getChild(__name__) -try: - import xml.etree.cElementTree as ET -except ImportError: - logger.warning("cElementTree could not be imported. Using ElementTree instead") - import xml.etree.ElementTree as ET - class ReportAnalyzer: @@ -35,7 +30,7 @@ def get_plugin(self, report_path): else: file_name = os.path.basename(report_path) plugin = self._get_plugin_by_name(file_name) - if not plugin: # Was unable to detect plugin from report file name + if not plugin: # Was unable to detect plugin from report file name logger.debug("Plugin by name not found") plugin = self._get_plugin_by_file_type(report_path) if not plugin: @@ -68,7 +63,7 @@ def _get_plugin_by_file_type(self, report_path): file_name_base, file_extension = os.path.splitext(file_name) file_extension = file_extension.lower() main_tag = None - file_json_keys = {} + main_tag_attributes = {} file_csv_headers = set() file_json_keys = set() files_in_zip = set() @@ -81,9 +76,18 @@ def _get_plugin_by_file_type(self, report_path): else: try: for event, elem in ET.iterparse(report_file, ('start',)): - main_tag = elem.tag + prefix, has_namespace, postfix = elem.tag.partition("}") + if has_namespace: + main_tag = postfix + else: + main_tag = elem.tag + try: + main_tag_attributes = elem.attrib + except: + pass break - logger.debug("Found XML content on file: %s - Main tag: %s", report_path, main_tag) + logger.debug("Found XML content on file: %s - Main tag: %s Attributes: %s", report_path, main_tag, + main_tag_attributes) except Exception as e: logger.debug("Non XML content [%s] - %s", report_path, e) try: @@ -116,9 +120,10 @@ def _get_plugin_by_file_type(self, report_path): for _plugin_id, _plugin in self.plugin_manager.get_plugins(): logger.debug("Try plugin: %s", _plugin_id) try: - if _plugin.report_belongs_to(main_tag=main_tag, report_path=report_path, - extension=file_extension, file_json_keys=file_json_keys, - file_csv_headers=file_csv_headers, files_in_zip=files_in_zip): + if _plugin.report_belongs_to(main_tag=main_tag, main_tag_attributes=main_tag_attributes, + report_path=report_path, extension=file_extension, + file_json_keys=file_json_keys, file_csv_headers=file_csv_headers, + files_in_zip=files_in_zip): plugin = _plugin logger.debug("Plugin by File Found: %s", plugin.id) break @@ -147,7 +152,8 @@ def get_plugin(self, command_string): class PluginsManager: - def __init__(self, custom_plugins_folder=None): + def __init__(self, custom_plugins_folder=None, ignore_info=False): + self.ignore_info = ignore_info self.plugins = {} self.plugin_modules = {} self._load_plugins(custom_plugins_folder) @@ -210,7 +216,7 @@ def get_plugin(self, plugin_id): plugin = None plugin_id = plugin_id.lower() if plugin_id in self.plugin_modules: - plugin = self.plugin_modules[plugin_id].createPlugin() + plugin = self.plugin_modules[plugin_id].createPlugin(self.ignore_info) else: logger.debug("Unknown Plugin: %s", plugin_id) return plugin @@ -218,4 +224,4 @@ def get_plugin(self, plugin_id): def get_plugins(self): for plugin_id, plugin_module in self.plugin_modules.items(): logger.debug("Instance Plugin: %s", plugin_id) - yield plugin_id, plugin_module.createPlugin() + yield plugin_id, plugin_module.createPlugin(self.ignore_info) diff --git a/faraday_plugins/plugins/plugin.py b/faraday_plugins/plugins/plugin.py index 97272e0a..e9ad1a82 100644 --- a/faraday_plugins/plugins/plugin.py +++ b/faraday_plugins/plugins/plugin.py @@ -4,35 +4,35 @@ See the file 'doc/LICENSE' for the license information """ +import hashlib +import logging import os +import re import shutil import tempfile - -from collections import defaultdict - -import pytz -import re import uuid -import logging -import simplejson as json import zipfile +from collections import defaultdict from datetime import datetime -import hashlib +import pytz +import simplejson as json logger = logging.getLogger("faraday").getChild(__name__) VALID_SERVICE_STATUS = ("open", "closed", "filtered") VULN_SKIP_FIELDS_TO_HASH = ['run_date'] + class PluginBase: # TODO: Add class generic identifier class_signature = "PluginBase" - def __init__(self): + def __init__(self, ignore_info=False): # Must be unique. Check that there is not # an existant plugin with the same id. # TODO: Make script that list current ids. + self.ignore_info = ignore_info self.id = None self.auto_load = True self._rid = id(self) @@ -40,6 +40,7 @@ def __init__(self): self.name = None self.description = "" self._command_regex = None + self.command = None self._output_file_path = None self._use_temp_file = False self._delete_temp_file = False @@ -54,7 +55,7 @@ def __init__(self): self._hosts_cache = {} self._service_cache = {} self._vulns_cache = {} - self.start_date = datetime.now() + self.start_date = datetime.utcnow() self.logger = logger.getChild(self.__class__.__name__) self.open_options = {"mode": "r", "encoding": "utf-8"} self.plugin_version = "0.0" @@ -101,6 +102,7 @@ def align_string_based_vulns(severity): if severity[0:3] in sev: return sev return severity + severity = align_string_based_vulns(severity) # Transform numeric severity into desc severity numeric_severities = {"0": "info", @@ -124,6 +126,9 @@ def save_host_cache(self, host): self._hosts_cache[cache_id] = obj_uuid else: obj_uuid = self._hosts_cache[cache_id] + if host['hostnames']: + chached_host = self.get_from_cache(obj_uuid) + chached_host['hostnames'] = list(set(chached_host['hostnames'] + host['hostnames'])) return obj_uuid def save_service_cache(self, host_id, service): @@ -138,32 +143,37 @@ def save_service_cache(self, host_id, service): return obj_uuid def save_service_vuln_cache(self, host_id, service_id, vuln): - cache_id = self.get_service_vuln_cache_id(host_id, service_id, vuln) - if cache_id not in self._vulns_cache: - obj_uuid = self.save_cache(vuln) - service = self.get_from_cache(service_id) - service["vulnerabilities"].append(vuln) - self._vulns_cache[cache_id] = obj_uuid + if self.ignore_info and vuln['severity'] == 'info': + return None else: - obj_uuid = self._vulns_cache[cache_id] - return obj_uuid + cache_id = self.get_service_vuln_cache_id(host_id, service_id, vuln) + if cache_id not in self._vulns_cache: + obj_uuid = self.save_cache(vuln) + service = self.get_from_cache(service_id) + service["vulnerabilities"].append(vuln) + self._vulns_cache[cache_id] = obj_uuid + else: + obj_uuid = self._vulns_cache[cache_id] + return obj_uuid def save_host_vuln_cache(self, host_id, vuln): - cache_id = self.get_host_vuln_cache_id(host_id, vuln) - if cache_id not in self._vulns_cache: - obj_uuid = self.save_cache(vuln) - host = self.get_from_cache(host_id) - host["vulnerabilities"].append(vuln) - self._vulns_cache[cache_id] = obj_uuid + if self.ignore_info and vuln['severity'] == 'info': + return None else: - obj_uuid = self._vulns_cache[cache_id] - return obj_uuid + cache_id = self.get_host_vuln_cache_id(host_id, vuln) + if cache_id not in self._vulns_cache: + obj_uuid = self.save_cache(vuln) + host = self.get_from_cache(host_id) + host["vulnerabilities"].append(vuln) + self._vulns_cache[cache_id] = obj_uuid + else: + obj_uuid = self._vulns_cache[cache_id] + return obj_uuid @staticmethod def _get_dict_hash(d, keys): return hash(frozenset(map(lambda x: (x, d.get(x, None)), keys))) - @classmethod def get_host_cache_id(cls, host): cache_id = cls._get_dict_hash(host, ['ip']) @@ -180,14 +190,17 @@ def get_host_service_cache_id(cls, host_id, service): def get_service_vuln_cache_id(cls, host_id, service_id, vuln): vuln_copy = vuln.copy() vuln_copy.update({"host_cache_id": host_id, "service_cache_id": service_id}) - cache_id = cls._get_dict_hash(vuln_copy, ['host_cache_id', 'service_cache_id', 'name', 'desc', 'website', 'path', 'pname', 'method']) + cache_id = cls._get_dict_hash(vuln_copy, + ['host_cache_id', 'service_cache_id', 'name', 'desc', 'website', 'path', 'pname', + 'method']) return cache_id @classmethod def get_host_vuln_cache_id(cls, host_id, vuln): vuln_copy = vuln.copy() vuln_copy.update({"host_cache_id": host_id}) - cache_id = cls._get_dict_hash(vuln_copy, ['host_cache_id', 'name', 'desc', 'website', 'path', 'pname', 'method']) + cache_id = cls._get_dict_hash(vuln_copy, + ['host_cache_id', 'name', 'desc', 'website', 'path', 'pname', 'method']) return cache_id def save_cache(self, obj): @@ -221,7 +234,7 @@ def getSettings(self): for param, (param_type, value) in self._settings.items(): yield param, value - def get_ws(self): # TODO Borrar + def get_ws(self): # TODO Borrar return "" def getSetting(self, name): @@ -242,8 +255,17 @@ def canParseCommandString(self, current_input): This method can be overriden in the plugin implementation if a different kind of check is needed """ - return (self._command_regex is not None and - self._command_regex.match(current_input.strip()) is not None) + if (self._command_regex is not None and + self._command_regex.match(current_input.strip()) is not None): + self.command = self.get_command(current_input) + return True + + def get_command(self, current_input: str) -> str: + command = self._command_regex.findall(current_input)[0] + if isinstance(command, tuple): + return "".join(command).strip() + + return command.strip() def processCommandString(self, username, current_path, command_string): """ @@ -296,7 +318,7 @@ def _parse_filename(self, filename): def processReport(self, filepath, user="faraday"): if os.path.isfile(filepath): - self.vulns_data["command"]["params"] = filepath + self.vulns_data["command"]["params"] = filepath if not self.ignore_info else f"{filepath} (Info ignored)" self.vulns_data["command"]["user"] = user self.vulns_data["command"]["import_source"] = "report" self._parse_filename(filepath) @@ -327,15 +349,15 @@ def createAndAddHost(self, name, os="unknown", hostnames=None, mac=None, descrip tags = [] if isinstance(tags, str): tags = [tags] - host = {"ip": name, "os": os, "hostnames": hostnames, "description": description, "mac": mac, + host = {"ip": name, "os": os, "hostnames": hostnames, "description": description, "mac": mac, "credentials": [], "services": [], "vulnerabilities": [], "tags": tags} host_id = self.save_host_cache(host) return host_id def createAndAddServiceToHost(self, host_id, name, - protocol="tcp", ports=None, - status="open", version="unknown", - description="", tags=None): + protocol="tcp", ports=None, + status="open", version="", + description="", tags=None): if ports: if isinstance(ports, list): ports = int(ports[0]) @@ -376,7 +398,8 @@ def createAndAddVulnToHost(self, host_id, name, desc="", ref=None, tags = [tags] vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref, "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "data": data, - "custom_fields": custom_fields, "status": status, "impact": impact, "policyviolations": policyviolations, + "custom_fields": custom_fields, "status": status, "impact": impact, + "policyviolations": policyviolations, "confirmed": confirmed, "easeofresolution": easeofresolution, "tags": tags } if run_date: @@ -404,7 +427,8 @@ def createAndAddVulnToService(self, host_id, service_id, name, desc="", tags = [tags] vulnerability = {"name": name, "desc": desc, "severity": self.normalize_severity(severity), "refs": ref, "external_id": external_id, "type": "Vulnerability", "resolution": resolution, "data": data, - "custom_fields": custom_fields, "status": status, "impact": impact, "policyviolations": policyviolations, + "custom_fields": custom_fields, "status": status, "impact": impact, + "policyviolations": policyviolations, "easeofresolution": easeofresolution, "confirmed": confirmed, "tags": tags } if run_date: @@ -418,7 +442,8 @@ def createAndAddVulnWebToService(self, host_id, service_id, name, desc="", response="", method="", pname="", params="", query="", category="", data="", external_id=None, confirmed=False, status="", easeofresolution=None, impact=None, - policyviolations=None, status_code=None, custom_fields=None, run_date=None, tags=None): + policyviolations=None, status_code=None, custom_fields=None, run_date=None, + tags=None): if params is None: params = "" if response is None: @@ -468,7 +493,6 @@ def createAndAddVulnWebToService(self, host_id, service_id, name, desc="", def createAndAddNoteToHost(self, host_id, name, text): return None - def createAndAddNoteToService(self, host_id, service_id, name, text): return None @@ -484,8 +508,8 @@ def createAndAddCredToService(self, host_id, service_id, username, password): def get_data(self): self.vulns_data["command"]["tool"] = self.id - self.vulns_data["command"]["command"] = self.id - self.vulns_data["command"]["duration"] = (datetime.now() - self.start_date).microseconds + self.vulns_data["command"]["command"] = self.command if self.command else self.id + self.vulns_data["command"]["duration"] = (datetime.utcnow() - self.start_date).microseconds return self.vulns_data def get_json(self): @@ -521,6 +545,7 @@ def get_summary(self): summary['vuln_hashes'].append(dict_hash) return summary + # TODO Borrar class PluginTerminalOutput(PluginBase): def __init__(self): @@ -545,8 +570,8 @@ def processOutput(self, term_output): class PluginByExtension(PluginBase): - def __init__(self): - super().__init__() + def __init__(self, ignore_info=False): + super().__init__(ignore_info) self.extension = [] def report_belongs_to(self, extension="", **kwargs): @@ -561,27 +586,30 @@ def report_belongs_to(self, extension="", **kwargs): class PluginXMLFormat(PluginByExtension): - def __init__(self): - super().__init__() + def __init__(self, ignore_info=False): + super().__init__(ignore_info) self.identifier_tag = [] + self.identifier_tag_attributes = {} self.extension = ".xml" self.open_options = {"mode": "rb"} - def report_belongs_to(self, main_tag="", **kwargs): + def report_belongs_to(self, main_tag="", main_tag_attributes={}, **kwargs): match = False if super().report_belongs_to(**kwargs): if type(self.identifier_tag) == str: match = (main_tag == self.identifier_tag) elif type(self.identifier_tag) == list: match = (main_tag in self.identifier_tag) + if self.identifier_tag_attributes: + match = self.identifier_tag_attributes.issubset(main_tag_attributes) self.logger.debug("Tag Match: [%s =/in %s] -> %s", main_tag, self.identifier_tag, match) return match class PluginJsonFormat(PluginByExtension): - def __init__(self): - super().__init__() + def __init__(self, ignore_info=False): + super().__init__(ignore_info) self.json_keys = set() self.extension = ".json" @@ -597,8 +625,8 @@ def report_belongs_to(self, file_json_keys=None, **kwargs): class PluginMultiLineJsonFormat(PluginByExtension): - def __init__(self): - super().__init__() + def __init__(self, ignore_info=False): + super().__init__(ignore_info) self.json_keys = set() self.extension = ".json" @@ -622,8 +650,8 @@ def report_belongs_to(self, file_json_keys=None, **kwargs): class PluginCSVFormat(PluginByExtension): - def __init__(self): - super().__init__() + def __init__(self, ignore_info=False): + super().__init__(ignore_info) self.extension = ".csv" self.csv_headers = set() @@ -642,8 +670,8 @@ def report_belongs_to(self, file_csv_headers=None, **kwargs): class PluginZipFormat(PluginByExtension): - def __init__(self): - super().__init__() + def __init__(self, ignore_info=False): + super().__init__(ignore_info) self.extension = ".zip" self.files_list = set() @@ -659,5 +687,3 @@ def report_belongs_to(self, files_in_zip=None, **kwargs): match = bool(self.files_list & files_in_zip) self.logger.debug("Files List Match: [%s =/in %s] -> %s", files_in_zip, self.files_list, match) return match - - diff --git a/faraday_plugins/plugins/plugins_utils.py b/faraday_plugins/plugins/plugins_utils.py index 628ecd3a..a89525d7 100644 --- a/faraday_plugins/plugins/plugins_utils.py +++ b/faraday_plugins/plugins/plugins_utils.py @@ -4,14 +4,11 @@ See the file 'doc/LICENSE' for the license information """ -import os import logging +import os import socket -from collections import defaultdict - from urllib.parse import urlsplit - SERVICE_MAPPER = None logger = logging.getLogger(__name__) @@ -24,7 +21,8 @@ def get_vulnweb_url_fields(url): "website": "{}://{}".format(parse.scheme, parse.netloc), "path": parse.path, "query": parse.query - } + } + def filter_services(): global SERVICE_MAPPER @@ -109,4 +107,21 @@ def resolve_hostname(hostname): except Exception as e: return hostname else: - return ip_address \ No newline at end of file + return ip_address + + +def get_severity_from_cvss(cvss): + try: + if type(cvss) != float: + cvss = float(cvss) + + cvss_ranges = [(0.0, 0.1, 'info'), + (0.1, 4.0, 'low'), + (4.0, 7.0, 'med'), + (7.0, 9.0, 'high'), + (9.0, 10.1, 'critical')] + for (lower, upper, severity) in cvss_ranges: + if lower <= cvss < upper: + return severity + except ValueError: + return 'unclassified' diff --git a/faraday_plugins/plugins/repo/acunetix/__init__.py b/faraday_plugins/plugins/repo/acunetix/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/acunetix/__init__.py +++ b/faraday_plugins/plugins/repo/acunetix/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/acunetix/plugin.py b/faraday_plugins/plugins/repo/acunetix/plugin.py index 644850da..390afc9e 100644 --- a/faraday_plugins/plugins/repo/acunetix/plugin.py +++ b/faraday_plugins/plugins/repo/acunetix/plugin.py @@ -5,25 +5,12 @@ """ from urllib.parse import urlsplit -import re -import os from lxml import etree -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - from faraday_plugins.plugins.plugin import PluginXMLFormat from faraday_plugins.plugins.plugins_utils import resolve_hostname -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - - __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" __credits__ = ["Francisco Amato"] @@ -53,7 +40,8 @@ def __init__(self, xml_output): else: self.sites = [] - def parse_xml(self, xml_output): + @staticmethod + def parse_xml(xml_output): """ Open and parse an xml file. @@ -71,7 +59,8 @@ def parse_xml(self, xml_output): return tree - def get_items(self, tree): + @staticmethod + def get_items(tree): """ @return items A list of Host instances """ @@ -86,26 +75,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search("([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr) - - if match_obj is not None: - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -126,7 +97,6 @@ def __init__(self, item_node): self.host = None # Use the port in the URL if it is defined, or 80 or 443 by default self.port = url_data.port or (443 if url_data.scheme == "https" else 80) - self.ip = resolve_hostname(self.host) self.os = self.get_text_from_subnode('Os') self.banner = self.get_text_from_subnode('Banner') @@ -148,6 +118,8 @@ def get_text_from_subnode(self, subnode_xpath_expr): def get_url(self, node): url = self.get_text_from_subnode('StartURL') + if not url.startswith('http'): + url = f'http://{url}' url_data = urlsplit(url) if not url_data.scheme: # Getting url from subnode 'Crawler' @@ -217,8 +189,8 @@ class AcunetixPlugin(PluginXMLFormat): Example plugin to parse acunetix output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "ScanGroup" self.id = "Acunetix" self.name = "Acunetix XML Output Plugin" @@ -287,9 +259,6 @@ def parseOutputString(self, output): ref=item.ref) del parser - def setHost(self): - pass - -def createPlugin(): - return AcunetixPlugin() \ No newline at end of file +def createPlugin(ignore_info=False): + return AcunetixPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/amap/__init__.py b/faraday_plugins/plugins/repo/amap/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/amap/__init__.py +++ b/faraday_plugins/plugins/repo/amap/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/amap/plugin.py b/faraday_plugins/plugins/repo/amap/plugin.py index abd01f47..1d680477 100644 --- a/faraday_plugins/plugins/repo/amap/plugin.py +++ b/faraday_plugins/plugins/repo/amap/plugin.py @@ -4,24 +4,18 @@ See the file 'doc/LICENSE' for the license information """ import argparse -import random +import re import shlex -import tempfile - -from faraday_plugins.plugins.plugin import PluginBase import socket -import re -import os - -from faraday_plugins.plugins.plugins_utils import resolve_hostname +from faraday_plugins.plugins.plugin import PluginBase class AmapPlugin(PluginBase): """ Example plugin to parse amap output.""" - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Amap" self.name = "Amap Output Plugin" self.plugin_version = "0.0.3" @@ -31,6 +25,7 @@ def __init__(self): self._command_regex = re.compile(r'^(amap|sudo amap)\s+.*?') self._use_temp_file = True self._hosts = [] + self.args = None def parseOutputString(self, output): services = {} @@ -107,9 +102,6 @@ def get_ip_6(self, host, port=0): return ip6[0][4][0] - def setHost(self): - pass - def processCommandString(self, username, current_path, command_string): """ Adds the -m parameter to get machine readable output. @@ -136,18 +128,14 @@ def processCommandString(self, username, current_path, command_string): cmd.remove("-6") cmd.insert(1, "-6") - args = None if len(cmd) > 4: try: - args, unknown = parser.parse_known_args(cmd) + self.args, unknown = parser.parse_known_args(cmd) except SystemExit: pass - self.args = args return final -def createPlugin(): - return AmapPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return AmapPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/appscan/__init__.py b/faraday_plugins/plugins/repo/appscan/__init__.py index 00dc0fca..d417d2e7 100644 --- a/faraday_plugins/plugins/repo/appscan/__init__.py +++ b/faraday_plugins/plugins/repo/appscan/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/appscan/plugin.py b/faraday_plugins/plugins/repo/appscan/plugin.py index 572c61d9..9e76a027 100644 --- a/faraday_plugins/plugins/repo/appscan/plugin.py +++ b/faraday_plugins/plugins/repo/appscan/plugin.py @@ -1,45 +1,37 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- +from urllib.parse import urlparse + from faraday_plugins.plugins.plugin import PluginXMLFormat from faraday_plugins.plugins.plugins_utils import resolve_hostname -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET +import xml.etree.ElementTree as ET -__author__ = "Alejando Parodi, Ezequiel Tavella, Blas Moyano" -__copyright__ = "Copyright (c) 2015, Infobyte LLC" -__credits__ = ["Alejando Parodi", "Ezequiel Tavella"] +__author__ = "Nicolas Rebagliati" +__copyright__ = "Copyright (c) 2021, Infobyte LLC" +__credits__ = ["Nicolas Rebagliati"] __license__ = "" __version__ = "1.0" -__maintainer__ = "Ezequiel Tavella" +__maintainer__ = "Nicolas Rebagliati" __status__ = "Development" class AppScanParser: - def __init__(self, xml_output): - self.tree = self.parse_xml(xml_output) - if self.tree: - self.operating_system = self.tree.attrib['technology'] - url_group = [tags.tag for tags in self.tree] - check_url = True if 'url-group' in url_group else False - if check_url: - self.urls = self.get_urls_info(self.tree.find('url-group')) - else: - self.urls = None - self.layout = self.get_layout_info(self.tree.find('layout')) - self.item = self.get_issue_type(self.tree.find('issue-type-group')) - self.name_scan = self.get_issue_data(self.tree.find('advisory-group')) - self.host_data = None if self.tree.find('scan-configuration/scanned-hosts/item') is None else \ - self.get_scan_conf_data(self.tree.find('scan-configuration/scanned-hosts/item')) - self.issue_group = self.get_info_issue_group(self.tree.find("issue-group")) - self.fix_recomendation = self.get_fix_info(self.tree.find('fix-recommendation-group')) - else: - self.tree = None - - def parse_xml(self, xml_output): + def __init__(self, xml_output): + tree = self.parse_xml(xml_output) + if tree: + self.scan_type = tree.attrib['technology'] + self.issue_types = self.get_issue_types(tree.find('issue-type-group')) + if self.scan_type == "SAST": + self.fixes = self.get_fixes(tree.find("fix-group-group")) + self.issues = self.get_sast_issues(tree.find("issue-group")) + elif self.scan_type == "DAST": + self.hosts = self.get_hosts(tree.find('scan-configuration/scanned-hosts')) + self.remediations = self.get_remediations(tree.find('remediation-group')) + self.entities = self.get_entity_groups(tree.find('entity-group')) + self.issues = self.get_dast_issues(tree.find("issue-group")) + + @staticmethod + def parse_xml(xml_output): try: tree = ET.fromstring(xml_output) except SyntaxError as err: @@ -47,340 +39,238 @@ def parse_xml(self, xml_output): return None return tree - def get_fix_info(self, tree): - list_fix = [] + @staticmethod + def get_fixes(tree): + fixes = {} for item in tree: - text_info_join = [] - if item.find("general/fixRecommendation"): - for text_tag in tree.findall('text'): - text_info_join += text_tag.text - info_fix = { - "id": item.attrib.get('id', None), - "text": text_info_join - } - list_fix.append(info_fix) - return list_fix - - def get_info_issue_group(sef,tree): - data_res_req = [] + fix_id = item.attrib['id'] + library = item.find("LibraryName").text + location = item.find("Location").text + fixes[fix_id] = {"library": library, "location": location} + return fixes + + @staticmethod + def get_issue_types(tree): + issue_types = {} + for item in tree: + type_id = item.attrib['id'] + name = item.find("name").text + issue_types[type_id] = name + return issue_types + + @staticmethod + def get_remediations(tree): + remediations = {} + for item in tree: + remediation_id = item.attrib['id'] + name = item.find("name").text + remediations[remediation_id] = name + return remediations + + @staticmethod + def get_hosts(tree): + hosts = {} + for item in tree: + host = item.find("host").text + port = item.find("port").text + operating_system = item.find("operating-system").text + if "unknown" in operating_system.lower(): + operating_system = "unknown" + web_server = item.find("web-server").text + application_server = item.find("application-server").text + service_name = f"{web_server} ({application_server})" + host_key = f"{host}-{port}" + hosts[host_key] = {"host": host, "port": port, "os": operating_system, + "service_name": service_name} + return hosts + + @staticmethod + def get_entity_groups(tree): + entity_groups = {} for item in tree: - if item.find("variant-group/item/issue-information"): - resp = item.find("variant-group/item/issue-information").text + entity_id = item.attrib['id'] + name = item.find("name").text + url = item.find("url-name").text + type = item.find("entity-type").text + url_data = urlparse(url) + website = f"{url_data.scheme}://{url_data.netloc}" + host = url_data.netloc.split(":")[0] + if url_data.port: + port = url_data.port else: - resp = "Not Response" - - json_res_req = { - "request": "Not request" if item.find("variant-group/item/test-http-traffic") is None else - item.find("variant-group/item/test-http-traffic").text, - "response": resp, - "location": "Not Location" if item.find("location") is None else item.find("location").text, - "source_file": "0.0.0.0" if item.find("source-file") is None else item.find("source-file").text, - "line": 0 if item.find("line") is None else item.find("line").text, - "id_item": item.attrib.get('id', 'Not id item'), - "severity": 0 if item.find("severity-id") is None else item.find("severity-id").text, - "cvss": "No cvss" if item.find("cvss-score") is None else item.find("cvss-score").text, - "cwe": "No cwe" if item.find("cwe") is None else item.find("cwe").text, - "remediation": "No remedation" if item.find("remediation/ref") is None else item.find( - "remediation/ref").text, - "advisory": "No advisory" if item.find("advisory/ref") is None else item.find("advisory/ref").text, - "url_id": "No url id" if item.find("url/ref") is None else item.find("url/ref").text, - "id_adv": "Not info" if item.find("issue-type/ref") is None else item.find("issue-type/ref").text - } - - data_res_req.append(json_res_req) - return data_res_req - - def get_layout_info(self, tree): - info_layout = { - "name": "Not info" if tree.find("application-name") is None else tree.find("application-name").text, - "date": "Not info" if tree.find("report-date") is None else tree.find("report-date").text, - "details": f'Departamento: {"Not info" if tree.find("department") is None else tree.find("department").text}' - f'Compania: {"Not info" if tree.find("company") is None else tree.find("company").text}' - f'Titulo Reporte: {"Not info" if tree.find("title") is None else tree.find("title").text}', - "nro_issues": None if tree.find("total-issues-in-application") is None else tree.find("total-issues-in-application").text, - } - return info_layout - - def get_issue_type(self, tree): - list_item = [] + if url_data.scheme == "http": + port = 80 + elif url_data.scheme == "https": + port = 443 + path = url_data.path + entity_groups[entity_id] = {"name": name, "host": host, "port": port, "url": url, + "type": type, "website": website, "path": path} + return entity_groups + + def get_dast_issues(self, tree): + dast_issues = [] for item in tree: - severity = item.attrib.get('severity-id', None) - if severity is None: - severity = item.attrib.get('maxIssueSeverity', None) - - item_info = { - "id": item.attrib.get('id', None), - "name": item.find("name").text, - "severity_id": severity, - "severity": item.attrib.get('severity', None), - "cwe": "Not info" if item.find("cme") is None else item.find("cwe").text, - "xfid": "Not info" if item.find("xfid") is None else item.find("xfid").text, - "advisory": "Not info" if item.find("advisory/ref") is None else item.find("advisory/ref").text + entity = self.entities[item.find("entity/ref").text] + host = entity["host"] + port = entity["port"] + name = self.issue_types[item.find("issue-type/ref").text] + severity = 0 if item.find("severity-id") is None else int(item.find("severity-id").text) + resolution = self.remediations[item.find("remediation/ref").text] + description = "" if item.find("variant-group/item/reasoning") is None \ + else item.find("variant-group/item/reasoning").text + request = "" if item.find("variant-group/item/test-http-traffic") is None \ + else item.find("variant-group/item/test-http-traffic").text + response = "" if item.find("variant-group/item/issue-information/testResponseChunk") is None \ + else item.find("variant-group/item/issue-information/testResponseChunk").text + cvss = None if item.find("cvss-score") is None else f"CVSS: {item.find('cvss-score').text}" + cvss_base_vector = None if item.find('cvss-vector/base-vector') is None \ + else f"CVSS-base-vector: {item.find('cvss-vector/base-vector').text}" + cvss_temporal_vector = None if item.find('cvss-vector/temporal-vector') is None \ + else f"CVSS-temporal-vector: {item.find('cvss-vector/temporal-vector').text}" + cvss_environmental_vector = None if item.find('cvss-vector/environmental-vector') is None \ + else f"CVSS-environmental-vector: {item.find('cvss-vector/environmental-vector').text}" + cwe = None if item.find("cwe") is None else item.find('cwe').text + if item.attrib.get("cve"): + cve = None if item.find("variant-group/item/issue-information/display-name") is None \ + else item.find('variant-group/item/issue-information/display-name').text + if "CVE" not in cve: + cve = f"CVE-{cve}" + cve_url = item.attrib["cve"] + else: + cve = None + cve_url = None + host_key = f"{host}-{port}" + issue_data = { + "host": host, + "port": port, + "os": self.hosts[host_key]["os"], + "service_name": self.hosts[host_key]["service_name"], + "name": name, + "severity": severity, + "desc": description, + "ref": [], + "resolution": resolution, + "request": request, + "response": response, + "website": entity['website'], + "path": entity['path'], + "external_id": cve } - list_item.append(item_info) - - return list_item - - def get_issue_data(self, tree): - list_item_data = [] - item_data = {} + if cve: + issue_data["ref"].append(cve) + if cve_url: + issue_data["ref"].append(cve_url) + if cwe: + issue_data["ref"].append(f"CWE: {cwe}") + if cvss: + issue_data["ref"].append(cvss) + if cvss_base_vector: + issue_data["ref"].append(cvss_base_vector) + if cvss_temporal_vector: + issue_data["ref"].append(cvss_temporal_vector) + if cvss_environmental_vector: + issue_data["ref"].append(cvss_environmental_vector) + dast_issues.append(issue_data) + return dast_issues + + def get_sast_issues(self, tree): + sast_issues = [] for item in tree: - for adivisory in item: - if adivisory.find("cwe/link"): - cwe = adivisory.find("cwe/link").text - else: - cwe = "Not Response" - - if adivisory.find("xfid/link"): - xfid = adivisory.find("xfid/link").text - else: - xfid = "Not Response" - - item_data = { - "id": item.attrib.get('id', None), - "name": "Not info" if adivisory.find("name") is None else adivisory.find("name").text, - "description": "Not info" if adivisory.find("testDescription") is None else - adivisory.find("testDescription").text, - "threatClassification": { - "name": "Not info" if adivisory.find("threatClassification/name") is None else - adivisory.find("threatClassification/name").text, - "reference": "Not info" if adivisory.find("threatClassification/reference") is None else - adivisory.find("threatClassification/reference").text, - }, - "testTechnicalDescription": "Not info" if adivisory.find("testTechnicalDescription") is None else - self.get_parser(adivisory.find("testTechnicalDescription")), - "testTechnicalDescriptionMixed": "Not info" if adivisory.find("testTechnicalDescriptionMixed") is None else - self.get_parser(adivisory.find("testTechnicalDescriptionMixed")), - - "testDescriptionMixed": "Not info" if adivisory.find("testDescriptionMixed") is None else - self.get_parser(adivisory.find("testDescriptionMixed")), - "causes": "Not info" if adivisory.find("causes/cause") is None else - adivisory.find("causes/cause").text, - "securityRisks": "Not info" if adivisory.find("securityRisks/securityRisk") is None else - adivisory.find("securityRisks/securityRisk").text, - "affectedProducts": "Not info" if adivisory.find("affectedProducts/affectedProduct") is None else - adivisory.find("affectedProducts/affectedProduct").text, - "cwe": cwe, - "xfid": xfid, - "references": "Not info" if adivisory.find("references") is None else - self.get_parser(adivisory.find("references")), - "fixRecommendations": "Not info" if adivisory.find("fixRecommendations/fixRecommendation") is None else - self.get_parser(adivisory.find("fixRecommendations/fixRecommendation")) - } - list_item_data.append(item_data) - return list_item_data - - def get_parser(self, tree): - text_join = "" - code_join = "" - link_join = "" - - if tree.tag == 'testTechnicalDescription': - - for text_info in tree.findall('text'): - text_join += text_info.text - - for code_info in tree.findall('code'): - text_join += code_info.text - - tech_data = { - "text": text_join, - "code": code_join - } - - elif tree.tag == 'testDescriptionMixed': - - for text_info in tree.findall('p'): - text_join += text_info.text - - for code_info in tree.findall('li'): - text_join += code_info.text - - tech_data = { - "text": text_join, - "items": code_join - } - - elif tree.tag == 'testTechnicalDescriptionMixed': - - for text_info in tree.findall('p'): - text_join += text_info.text - - tech_data = { - "text": text_join, - } - - elif tree.tag == 'references': - for text_info in tree.findall('text'): - text_join += "no info " if text_info.text is None else text_info.text - - for link_info in tree.findall('link'): - link_join += "no info " if link_info.text is None else link_info.text - link_join += link_info.attrib.get('target', 'not target') - - tech_data = { - "text": text_join, - "Link": link_join - } - - elif tree.tag == 'fixRecommendation': - for text_info in tree.findall('text'): - text_join += "no info " if text_info.text is None else text_info.text - - for link_info in tree.findall('link'): - link_join += "no info " if link_info.text is None else link_info.text - link_join += link_info.attrib.get('target', 'not target') - - tech_data = { - "text": text_join, - "link": link_join - } - - return tech_data - - def get_urls_info(self, tree): - list_url = [] - for url in tree: - url_info = { - "id_item": url.attrib.get('id', 'Not id item'), - "id": "Not info" if url.find("issue-type") is None else url.find("issue-type").text, - "url": "Not info" if url.find("name") is None else url.find("name").text, + name = self.issue_types[item.find("issue-type/ref").text] + source_file = item.attrib["filename"] + severity = 0 if item.find("severity-id") is None else int(item.find("severity-id").text) + description = item.find("fix/item/general/text").text + resolution = "" if item.find("variant-group/item/issue-information/fix-resolution-text") is None \ + else item.find("variant-group/item/issue-information/fix-resolution-text").text + fix_id = item.attrib.get("fix-group-id") + if fix_id: + fix = self.fixes[fix_id] + resolution = f"{resolution}\nLibrary: {fix['library']}\nLocation: {fix['location']}" + cvss = None if item.find("cvss-score") is None else f"CVSS: {item.find('cvss-score').text}" + cvss_base_vector = None if item.find('cvss-vector/base-vector') is None \ + else f"CVSS-base-vector: {item.find('cvss-vector/base-vector').text}" + cvss_temporal_vector = None if item.find('cvss-vector/temporal-vector') is None \ + else f"CVSS-temporal-vector: {item.find('cvss-vector/temporal-vector').text}" + cvss_environmental_vector = None if item.find('cvss-vector/environmental-vector') is None \ + else f"CVSS-environmental-vector: {item.find('cvss-vector/environmental-vector').text}" + cwe = None if item.find("cwe/ref") is None else item.find('cwe/ref').text + if item.attrib.get("cve"): + cve = None if item.find("variant-group/item/issue-information/display-name") is None \ + else item.find('variant-group/item/issue-information/display-name').text + if "CVE" not in cve: + cve = f"CVE-{cve}" + cve_url = item.attrib["cve"] + else: + cve = None + cve_url = None + issue_data = { + "source_file": source_file, + "name": name, + "severity": severity, + "desc": description, + "ref": [], + "resolution": resolution, + "external_id": cve } - list_url.append(url_info) - - return list_url - - def get_scan_conf_data(self, host_info): - info_host = { - "host": "Not info" if host_info.find("host") is None else host_info.find("host").text, - "port": "Not info" if host_info.find("port") is None else host_info.find("port").text, - "os": "Not info" if host_info.find("operating-system") is None else host_info.find("operating-system").text, - "webserver": "Not info" if host_info.find("web-server") is None else host_info.find("web-server").text, - "appserver": "Not info" if host_info.find("application-server") is None else host_info.find("application-server").text, - } - return info_host + if cve: + issue_data["ref"].append(cve) + if cve_url: + issue_data["ref"].append(cve_url) + if cwe: + issue_data["ref"].append(f"CWE: {cwe}") + if cvss: + issue_data["ref"].append(cvss) + if cvss_base_vector: + issue_data["ref"].append(cvss_base_vector) + if cvss_temporal_vector: + issue_data["ref"].append(cvss_temporal_vector) + if cvss_environmental_vector: + issue_data["ref"].append(cvss_environmental_vector) + # Build data + data = [] + if item.attrib.get("caller"): + data.append(f"Caller: {item.attrib.get('caller')}") + if item.find("variant-group/item/issue-information/method-signature") is not None: + data.append(f"Method: {item.find('variant-group/item/issue-information/method-signature').text}") + if item.find("variant-group/item/issue-information/method-signature2") is not None: + data.append(f"Location: {item.find('variant-group/item/issue-information/method-signature2').text}") + issue_data['data'] = "\n".join(data) + sast_issues.append(issue_data) + return sast_issues class AppScanPlugin(PluginXMLFormat): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "xml-report" self.id = 'Appscan' self.name = 'Appscan XML Plugin' self.plugin_version = '0.0.1' self.version = '1.0.0' self.framework_version = '1.0.0' - self.options = None - self.protocol = None - self.port = '80' - self.address = None def parseOutputString(self, output): parser = AppScanParser(output) - layout = parser.layout - operating_system = parser.operating_system - host_data = parser.host_data - urls = parser.urls - item = parser.item - name_scan = parser.name_scan - issues = parser.issue_group - recomendation = parser.fix_recomendation - - if operating_system == 'DAST': - host_id = self.createAndAddHost(resolve_hostname(host_data['host']), os=host_data['os'], - hostnames=[host_data['host']], description=layout['details']) - - service_id = self.createAndAddServiceToHost(host_id, host_data['host'], ports=host_data['port'], - protocol="tcp?HTTP", - description=f'{host_data["webserver"]} - {host_data["appserver"]}') - if layout['nro_issues'] is None: - nro_check = True - - else: - nro_check = False - check_issues = [] - - for issue in issues: - id = f"{issue['url_id']}{issue['advisory']}" - if id in check_issues and nro_check is True: - check_issues.append(id) - else: - check_issues.append(id) - for info in name_scan: - if info['id'] == issue['advisory']: - vuln_name = info['name'] - vuln_desc = info['description'] - resolution = "" - if 'text' in info['fixRecommendations']: - resolution += info['fixRecommendations']['text'] - if 'link' in info['fixRecommendations']: - resolution += info['fixRecommendations']['link'] - - vuln_data = f'xfix: {info["xfid"]} cme: {info["cwe"]}' - - for url in urls: - if url['id'] == issue['advisory']: - url_name = url['url'] - elif url['id_item'] == issue['id_item']: - url_name = url['url'] - else: - url_name = None - - for rec in recomendation: - if rec['id'] == issue['advisory']: - vuln_data = f'{vuln_data}, {rec["text"]} ' - - ref = f'cwe: {issue["cwe"]} cvss: {issue["cvss"]} remediation: {issue["remediation"]}' - self.createAndAddVulnWebToService(host_id=host_id, service_id=service_id, name=vuln_name, - desc=vuln_desc, severity=issue['severity'], ref=[ref], - website=host_data['host'], request=issue['request'], - response=issue['response'], method=issue['request'], - resolution=resolution, data=vuln_data, path=url_name) - - elif operating_system == 'SAST': - for info_loc_source in issues: - source_file = info_loc_source['source_file'] - host_id = self.createAndAddHost(source_file, os=operating_system) - ref = f'{info_loc_source["location"]} - {info_loc_source["line"]}' - for vuln_data in name_scan: - if vuln_data['id'] == info_loc_source["id_adv"]: - desc = f'desc: {vuln_data["description"]} DescMix {vuln_data["testDescriptionMixed"]}' - - resolution = f'Fix Recomendarion {vuln_data["fixRecommendations"]}' \ - f' - TestTecnical {vuln_data["testTechnicalDescriptionMixed"]}' - - self.createAndAddVulnToHost(host_id=host_id, - name=vuln_data['name'], - desc=desc, - ref=[ref], - severity=info_loc_source['severity'], - resolution=resolution, - data=f'xfix: {vuln_data["xfid"]} cme: {vuln_data["cwe"]}', - run_date=None, - ) - else: - host_id = self.createAndAddHost(layout['name'], os=operating_system) - for vulnserv in name_scan: - for sev in item: - if sev['id'] == vulnserv['id']: - info_severity = sev['severity_id'] - if vulnserv['description'] is None: - desc = "" - else: - desc = vulnserv['description'] - - text_info = vulnserv['fixRecommendations']['text'] \ - if 'text' in vulnserv['fixRecommendations'] else "Not Text" - - link_info = vulnserv['fixRecommendations']['link'] \ - if 'link' in vulnserv['fixRecommendations'] else "Not Info" - - resolution = f"Text:{text_info}. Link: {link_info}." - - self.createAndAddVulnToHost(host_id=host_id, name=vulnserv['name'], desc=desc, - severity=info_severity, resolution=resolution, - data=f'xfix: {vulnserv["xfid"]} cme: {vulnserv["cwe"]}', run_date=None) - - -def createPlugin(): - return AppScanPlugin() + scan_type = parser.scan_type + + if scan_type == 'DAST': + for issue in parser.issues: + host = issue.pop("host") + port = issue.pop("port") + service_name = issue.pop("service_name") + ip = resolve_hostname(host) + host_os = issue.pop("os") + host_id = self.createAndAddHost(ip, hostnames=host, os=host_os) + service_id = self.createAndAddServiceToHost(host_id, service_name, ports=port) + self.createAndAddVulnWebToService(host_id=host_id, service_id=service_id, **issue) + + elif scan_type == 'SAST': + for issue in parser.issues: + source_file = issue.pop('source_file') + host_id = self.createAndAddHost(source_file) + self.createAndAddVulnToHost(host_id=host_id, **issue) + + +def createPlugin(ignore_info=False): + return AppScanPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/appspider/plugin.py b/faraday_plugins/plugins/repo/appspider/plugin.py index d3ebd48a..89dae76e 100644 --- a/faraday_plugins/plugins/repo/appspider/plugin.py +++ b/faraday_plugins/plugins/repo/appspider/plugin.py @@ -5,12 +5,10 @@ Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginXMLFormat +import xml.etree.ElementTree as ET from datetime import datetime -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET + +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = 'Blas Moyano' __copyright__ = 'Copyright 2020, Faraday Project' @@ -25,11 +23,12 @@ def __init__(self, xml_output): self.tree = self.parse_xml(xml_output) if self.tree: self.vuln_list = self.tree.find('VulnList') - self.name_scan = self.tree.find('ScanName').text + self.name_scan = self.tree.findtext('ScanName') else: self.tree = None - def parse_xml(self, xml_output): + @staticmethod + def parse_xml(xml_output): try: tree = ET.fromstring(xml_output) except SyntaxError as err: @@ -39,8 +38,9 @@ def parse_xml(self, xml_output): class AppSpiderPlugin(PluginXMLFormat): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["VulnSummary"] self.id = 'AppSpider' self.name = 'AppSpider XML Output Plugin' @@ -71,22 +71,22 @@ def parseOutputString(self, output): data_info = [] for vulns in parser.vuln_list: - vuln_name = vulns.find('VulnType').text - vuln_desc = vulns.find('Description').text - vuln_ref = vulns.find('VulnUrl').text - severity = vulns.find('AttackScore').text - vuln_resolution = vulns.find('Recommendation').text - vuln_external_id = vulns.find('DbId').text - vuln_run_date = vulns.find('ScanDate').text - data_info.append(vulns.find('AttackClass').text) - data_info.append(vulns.find('CweId').text) - data_info.append(vulns.find('CAPEC').text) - data_info.append(vulns.find('DISSA_ASC').text) - data_info.append(vulns.find('OWASP2007').text) - data_info.append(vulns.find('OWASP2010').text) - data_info.append(vulns.find('OWASP2013').text) - data_info.append(vulns.find('OVAL').text) - data_info.append(vulns.find('WASC').text) + vuln_name = vulns.findtext('VulnType') + vuln_desc = vulns.findtext('Description') + vuln_ref = vulns.findtext('VulnUrl') + severity = vulns.findtext('AttackScore') + vuln_resolution = vulns.findtext('Recommendation') + vuln_external_id = vulns.findtext('DbId') + vuln_run_date = vulns.findtext('ScanDate') + data_info.append(vulns.findtext('AttackClass')) + data_info.append(vulns.findtext('CweId')) + data_info.append(vulns.findtext('CAPEC')) + data_info.append(vulns.findtext('DISSA_ASC')) + data_info.append(vulns.findtext('OWASP2007')) + data_info.append(vulns.findtext('OWASP2010')) + data_info.append(vulns.findtext('OWASP2013')) + data_info.append(vulns.findtext('OVAL')) + data_info.append(vulns.findtext('WASC')) if severity == '1-Informational': severity = 0 @@ -113,5 +113,5 @@ def parseOutputString(self, output): external_id=vuln_external_id, data=str_data) -def createPlugin(): - return AppSpiderPlugin() +def createPlugin(ignore_info=False): + return AppSpiderPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/arachni/__init__.py b/faraday_plugins/plugins/repo/arachni/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/arachni/__init__.py +++ b/faraday_plugins/plugins/repo/arachni/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/arachni/plugin.py b/faraday_plugins/plugins/repo/arachni/plugin.py index 5b7dd838..667f02ec 100755 --- a/faraday_plugins/plugins/repo/arachni/plugin.py +++ b/faraday_plugins/plugins/repo/arachni/plugin.py @@ -3,17 +3,14 @@ Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ +import os import re +import xml.etree.ElementTree as ET from urllib.parse import urlparse -import os + from faraday_plugins.plugins.plugin import PluginXMLFormat from faraday_plugins.plugins.plugins_utils import resolve_hostname -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET - __author__ = 'Ezequiel Tavella' __copyright__ = 'Copyright 2016, Faraday Project' __credits__ = ['Ezequiel Tavella', 'Matías Ariel Ré Medina', 'Conrad Stein K'] @@ -24,12 +21,12 @@ class ArachniXmlParser: def __init__(self, xml_output): + self.tree = self.parse_xml(xml_output) if self.tree: self.issues = self.getIssues(self.tree) self.plugins = self.getPlugins(self.tree) self.system = self.getSystem(self.tree) - else: self.system = None self.issues = None @@ -77,6 +74,21 @@ def __init__(self, issue_node): self.parameters = self.getParameters() self.request = self.getRequest() self.response = self.getResponse() + self.data = self.getData() + + def getData(self): + name = self.node.findtext('check/name', '') + description = self.node.findtext('check/description', '') + author = self.node.findtext('check/description', '') + data = "" + if name: + data += f'\nname: {name}' + if description: + data += f'\ndescription: {description}' + if author: + data += f'\nauthor: {author}' + + return data def getDesc(self, tag): @@ -139,23 +151,50 @@ def getRequest(self): # Get data about request. try: - - raw_data = self.node.find('page').find('request').find('raw') + request = self.node.find('page/request') + raw_data = request.find('raw') data = raw_data.text + if not data: + data = self.contruct_request(request) return data - except: return 'None' + @staticmethod + def contruct_request(request): + data = request.findtext("method", "").upper() + data += f" {request.findtext('url', '')}" + for h in request.findall('headers/header'): + data += "\n%s: %s" % (h.get('name'), h.get('value')) + if request.findtext('body',""): + data += "\n%s" % request.findtext('body',"") + return data + + @staticmethod + def construct_response(request): + data = "" + if request.findtext("code", ""): + data += f'\ncode: {request.findtext("code", "")}' + if request.findtext("ip_address", ""): + data += f'\nip_address: {request.findtext("ip_address", "")}' + if request.findtext("time", ""): + data += f'\ntime: {request.findtext("time", "")}' + if request.findtext("return_code", ""): + data += f'\nreturn_code: {request.findtext("return_code", "")}' + if request.findtext("return_message", ""): + data += f'\nreturn_message: {request.findtext("return_message", "")}' + return data + def getResponse(self): - # Get data about response. try: - - raw_data = self.node.find('page').find('response').find('raw_headers') + request = self.node.find('page/response') + raw_data = request.find('raw_headers') data = raw_data.text + if not data: + data = self.contruct_request(request) + data += self.construct_response(request) return data - except: return 'None' @@ -181,7 +220,6 @@ def __init__(self, node, tag_exists): self.version = self.getDesc('version') self.start_time = self.getDesc('start_datetime') self.finish_time = self.getDesc('finish_datetime') - self.note = self.getNote() def getUrl(self): @@ -200,7 +238,6 @@ def getOptions(self): else: options_string = None - try: self.user_agent = self.node.find('user_agent').text except: @@ -231,17 +268,16 @@ def getDesc(self, tag): def getNote(self): result = ('Scan url:\n {} \nUser Agent:\n {} \nVersion Arachni:\n {} \nStart time:\n {} \nFinish time:\n {}' - '\nAudited Elements:\n {} \nModules:\n {} \nCookies:\n {}').format(self.url, self.user_agent, - self.version, self.start_time, - self.finish_time, - self.audited_elements, - self.modules, self.cookies) + '\nAudited Elements:\n {} \nModules:\n {} \nCookies:\n {}').format(self.url, self.user_agent, + self.version, self.start_time, + self.finish_time, + self.audited_elements, + self.modules, self.cookies) return result class Plugins(): - """ Support: WAF (Web Application Firewall) Detector (waf_detector) @@ -259,6 +295,14 @@ def __init__(self, plugins_node): except Exception: self.ip = None + @staticmethod + def get_value(name, node): + x = node.find(name) + if x: + return x.text + else: + return "" + def getHealthmap(self): # Get info about healthmap @@ -277,23 +321,14 @@ def getHealthmap(self): else: list_urls.append(f"Without Issues: {url.text}") - def get_value(name, node=None): - if not node: - node = healthmap_tree - x = healthmap_tree.find(name) - if x: - return x.text - else: - return "" - try: - plugin_name = get_value('name') - description = get_value('description') - results = get_value('results') - total = get_value('total', results) - with_issues = get_value('with_issues', results) - without_issues = get_value('without_issues', results) - issue_percentage = get_value('issue_percentage', results) + plugin_name = self.get_value('name', healthmap_tree) + description = self.get_value('description', healthmap_tree) + results = self.get_value('results', healthmap_tree) + total = self.get_value('total', results) + with_issues = self.get_value('with_issues', results) + without_issues = self.get_value('without_issues', results) + issue_percentage = self.get_value('issue_percentage', results) urls = '\n'.join(list_urls) result = (f"Plugin Name: {plugin_name}\nDescription: {description}\nStatistics:\nTotal: {total}" @@ -308,22 +343,12 @@ def getWaf(self): # Get info about waf plugin. waf_tree = self.plugins_node.find('waf_detector') - - def get_value(name, node=None): - if not node: - node = waf_tree - x = waf_tree.find(name) - if x: - return x.text - else: - return "" - try: - plugin_name = get_value('name') - description = get_value('description') + plugin_name = self.get_value('name', waf_tree) + description = self.get_value('description', waf_tree) results = waf_tree.find('results') - message = get_value('message', results) - status = get_value('status', results) + message = self.get_value('message', results) + status = self.get_value('status', results) result = (f"Plugin Name: {plugin_name}\nDescription: {description}\nResults:" f"\nMessage: {message}\nStatus: {status}") return result @@ -335,8 +360,8 @@ class ArachniPlugin(PluginXMLFormat): # Plugin that parses Arachni's XML report files. - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["report", "arachni_report"] self.id = 'Arachni' self.name = 'Arachni XML Output Plugin' @@ -429,6 +454,7 @@ def parseOutputString(self, output): service_id, name=issue.name, desc=description, + data=issue.data, resolution=resol, ref=references, severity=issue.severity, @@ -491,7 +517,5 @@ def getHostname(self, url): return self.hostname -def createPlugin(): - return ArachniPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return ArachniPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/arp_scan/__init__.py b/faraday_plugins/plugins/repo/arp_scan/__init__.py index ea531e17..625a6e25 100755 --- a/faraday_plugins/plugins/repo/arp_scan/__init__.py +++ b/faraday_plugins/plugins/repo/arp_scan/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/arp_scan/plugin.py b/faraday_plugins/plugins/repo/arp_scan/plugin.py index e955acb9..4d5de3ec 100644 --- a/faraday_plugins/plugins/repo/arp_scan/plugin.py +++ b/faraday_plugins/plugins/repo/arp_scan/plugin.py @@ -22,8 +22,8 @@ class CmdArpScanPlugin(PluginBase): Basically inserts into the tree the ouput of this tool """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "arp-scan" self.name = "arp-scan network scanner" self.plugin_version = "0.0.2" @@ -60,6 +60,6 @@ def parseOutputString(self, output): -def createPlugin(): - return CmdArpScanPlugin() +def createPlugin(ignore_info=False): + return CmdArpScanPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/awsprowler/plugin.py b/faraday_plugins/plugins/repo/awsprowler/plugin.py index 8444467a..7b37db19 100644 --- a/faraday_plugins/plugins/repo/awsprowler/plugin.py +++ b/faraday_plugins/plugins/repo/awsprowler/plugin.py @@ -33,8 +33,8 @@ class AwsProwlerPlugin(PluginMultiLineJsonFormat): and adds the information to Faraday. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "awsprowler" self.name = "AWS Prowler" self.plugin_version = "0.1" @@ -67,5 +67,5 @@ def parseOutputString(self, output, debug=False): external_id=vuln_external_id, policyviolations=[vuln_policy]) -def createPlugin(): - return AwsProwlerPlugin() +def createPlugin(ignore_info=False): + return AwsProwlerPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/bandit/__init__.py b/faraday_plugins/plugins/repo/bandit/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/faraday_plugins/plugins/repo/bandit/plugin.py b/faraday_plugins/plugins/repo/bandit/plugin.py new file mode 100644 index 00000000..98f53edb --- /dev/null +++ b/faraday_plugins/plugins/repo/bandit/plugin.py @@ -0,0 +1,72 @@ +from faraday_plugins.plugins.plugin import PluginXMLFormat +import xml.etree.ElementTree as ET +import re + +class BanditPlugin(PluginXMLFormat): + """ + Example plugin to parse bandit output. + """ + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.identifier_tag = 'testsuite' + self.extension = ".xml" + self.id = "Bandit" + self.name = "Bandit XML Output Plugin" + self.plugin_version = "0.0.1" + + def report_belongs_to(self, **kwargs): + if super().report_belongs_to(**kwargs): + report_path = kwargs.get("report_path", "") + with open(report_path) as f: + output = f.read() + return re.search("testsuite name=\"bandit\"", output) is not None + return False + + def parseOutputString(self, output): + bp = BanditParser(output) + + for vuln in bp.vulns: + host_id = self.createAndAddHost(vuln['path']) + + self.createAndAddVulnToHost( + host_id=host_id, + name=vuln['name'], + desc=vuln['issue_text'], + ref=vuln['references'], + severity=vuln['severity'], + ) + + return True + + +class BanditParser: + """ + Parser for bandit on demand + """ + + def __init__(self, xml_output): + self.vulns = self._parse_xml(xml_output) + + + def _parse_xml(self, xml_output): + vulns = [] + tree = ET.fromstring(xml_output) + testcases = tree.findall('testcase') + + for testcase in testcases: + error = testcase.find('error') + name = testcase.attrib['name'] + path = testcase.attrib['classname'] + severity = error.attrib['type'] + issue_text = error.text + more_info = error.attrib['more_info'] + ref = [more_info] + + vulns.append({'name': name, 'path': path, 'references': ref, 'issue_text': issue_text, 'severity': severity}) + + return vulns + + +def createPlugin(ignore_info=False): + return BanditPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/beef/__init__.py b/faraday_plugins/plugins/repo/beef/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/beef/__init__.py +++ b/faraday_plugins/plugins/repo/beef/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/beef/plugin.py b/faraday_plugins/plugins/repo/beef/plugin.py index b4cc400a..debb4a63 100644 --- a/faraday_plugins/plugins/repo/beef/plugin.py +++ b/faraday_plugins/plugins/repo/beef/plugin.py @@ -23,8 +23,8 @@ class BeefPlugin(PluginBase): Example plugin to parse beef output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Beef" self.name = "BeEF Online Service Plugin" self.plugin_version = "0.0.1" @@ -95,10 +95,7 @@ def parseOutputString(self, output): severity=3) - def setHost(self): - pass - -def createPlugin(): - return BeefPlugin() +def createPlugin(ignore_info=False): + return BeefPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/brutexss/__init__.py b/faraday_plugins/plugins/repo/brutexss/__init__.py index 454b1c0a..729fe446 100644 --- a/faraday_plugins/plugins/repo/brutexss/__init__.py +++ b/faraday_plugins/plugins/repo/brutexss/__init__.py @@ -3,4 +3,4 @@ Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/brutexss/plugin.py b/faraday_plugins/plugins/repo/brutexss/plugin.py index 59cacef6..6e3d9406 100644 --- a/faraday_plugins/plugins/repo/brutexss/plugin.py +++ b/faraday_plugins/plugins/repo/brutexss/plugin.py @@ -17,8 +17,8 @@ class brutexss (PluginBase): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "brutexss" self.name = "brutexss" self.plugin_version = "0.0.2" @@ -57,7 +57,7 @@ def parseOutputString(self, output): -def createPlugin(): - return brutexss() +def createPlugin(ignore_info=False): + return brutexss(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/burp/__init__.py b/faraday_plugins/plugins/repo/burp/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/burp/__init__.py +++ b/faraday_plugins/plugins/repo/burp/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/burp/plugin.py b/faraday_plugins/plugins/repo/burp/plugin.py index 1b5b195e..22bc77dd 100644 --- a/faraday_plugins/plugins/repo/burp/plugin.py +++ b/faraday_plugins/plugins/repo/burp/plugin.py @@ -4,25 +4,14 @@ See the file 'doc/LICENSE' for the license information """ -import re -import os import base64 -from bs4 import BeautifulSoup, Comment -from faraday_plugins.plugins.plugin import PluginXMLFormat +import distutils.util # pylint: disable=import-error +import xml.etree.ElementTree as ET from urllib.parse import urlsplit -import distutils.util #pylint: disable=import-error - - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +from bs4 import BeautifulSoup, Comment +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -78,7 +67,6 @@ def get_items(self, tree): """ @return items A list of Host instances """ - bugtype = '' for node in tree.findall('issue'): yield Item(node) @@ -90,27 +78,7 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr) - if match_obj is not None: - - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -165,8 +133,8 @@ def __init__(self, item_node): self.background = background self.external_id = external_id.text - - def do_clean(self, value): + @staticmethod + def do_clean(value): myreturn = "" if value is not None: @@ -210,8 +178,8 @@ class BurpPlugin(PluginXMLFormat): Example plugin to parse burp output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "issues" self.id = "Burp" self.name = "Burp XML Output Plugin" @@ -222,7 +190,6 @@ def __init__(self): self._current_output = None self.target = None - def parseOutputString(self, output): parser = BurpXmlParser(output) @@ -236,17 +203,24 @@ def parseOutputString(self, output): ports=[str(item.port)], status="open") - desc = "Detail\n" + item.detail if item.background: - desc += "\nBackground\n" + item.background + desc = item.background + else: + desc = "" desc = self.removeHtml(desc) + if item.detail: + data = item.detail + else: + data = "" + data = self.removeHtml(data) resolution = self.removeHtml(item.remediation) if item.remediation else "" - v_id = self.createAndAddVulnWebToService( + self.createAndAddVulnWebToService( h_id, s_id, item.name, desc=desc, + data=data, severity=item.severity, website=item.host, path=item.path, @@ -257,7 +231,6 @@ def parseOutputString(self, output): del parser - def removeHtml(self, markup): soup = BeautifulSoup(markup, "html.parser") @@ -285,11 +258,6 @@ def removeHtml(self, markup): return str(soup) - def setHost(self): - pass - - -def createPlugin(): - return BurpPlugin() -# I'm Py3 +def createPlugin(ignore_info=False): + return BurpPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/checkmarx/__init__.py b/faraday_plugins/plugins/repo/checkmarx/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/checkmarx/__init__.py +++ b/faraday_plugins/plugins/repo/checkmarx/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/checkmarx/plugin.py b/faraday_plugins/plugins/repo/checkmarx/plugin.py index fbcf549d..06ed2080 100755 --- a/faraday_plugins/plugins/repo/checkmarx/plugin.py +++ b/faraday_plugins/plugins/repo/checkmarx/plugin.py @@ -3,17 +3,11 @@ Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -import re +import xml.etree.ElementTree as ET from urllib.parse import urlparse from faraday_plugins.plugins.plugin import PluginXMLFormat - -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET - __author__ = 'Blas Moyano' __copyright__ = 'Copyright 2020, Faraday Project' __credits__ = ['Blas Moyano'] @@ -70,7 +64,8 @@ def get_path_node_info(self, path_node): for info_pn in pn: if info_pn.tag == 'Snippet': valor = ( - 'Number', info_pn.find('Line').find('Number').text, 'Code', info_pn.find('Line').find('Code').text) + 'Number', info_pn.find('Line').find('Number').text, 'Code', + info_pn.find('Line').find('Code').text) else: valor = (info_pn.tag, info_pn.text) lista_v.append(valor) @@ -79,8 +74,9 @@ def get_path_node_info(self, path_node): class CheckmarxPlugin(PluginXMLFormat): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["CxXMLResults"] self.id = 'Checkmarx' self.name = 'Checkmarx XML Output Plugin' @@ -89,7 +85,6 @@ def __init__(self): self.framework_version = '1.0.0' self.options = None - def parseOutputString(self, output): parser = CheckmarxXmlParser(output) if not parser.query: @@ -137,6 +132,5 @@ def parseOutputString(self, output): resolution=data, ref=refs) -def createPlugin(): - return CheckmarxPlugin() - +def createPlugin(ignore_info=False): + return CheckmarxPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/cobalt/__init__.py b/faraday_plugins/plugins/repo/cobalt/__init__.py index 81e2e0d9..cef3c4e2 100644 --- a/faraday_plugins/plugins/repo/cobalt/__init__.py +++ b/faraday_plugins/plugins/repo/cobalt/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/cobalt/plugin.py b/faraday_plugins/plugins/repo/cobalt/plugin.py index e2c089fc..a367199a 100644 --- a/faraday_plugins/plugins/repo/cobalt/plugin.py +++ b/faraday_plugins/plugins/repo/cobalt/plugin.py @@ -51,8 +51,8 @@ class CobaltPlugin(PluginCSVFormat): Example plugin to parse Cobalt output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.csv_headers = [{'Token'}, {'Tag'}] self.id = "Cobalt" self.name = "Cobalt CSV Output Plugin" @@ -102,5 +102,5 @@ def parseOutputString(self, output): data=row['StepsToReproduce'], external_id=row['Tag'], run_date=run_date) -def createPlugin(): - return CobaltPlugin() +def createPlugin(ignore_info=False): + return CobaltPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/dig/__init__.py b/faraday_plugins/plugins/repo/dig/__init__.py index f55478ec..4c2d40a8 100644 --- a/faraday_plugins/plugins/repo/dig/__init__.py +++ b/faraday_plugins/plugins/repo/dig/__init__.py @@ -3,4 +3,4 @@ Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dig/plugin.py b/faraday_plugins/plugins/repo/dig/plugin.py index e5c43424..d0519353 100644 --- a/faraday_plugins/plugins/repo/dig/plugin.py +++ b/faraday_plugins/plugins/repo/dig/plugin.py @@ -25,12 +25,12 @@ class DigPlugin(PluginBase): Handle DiG (http://linux.die.net/man/1/dig) output """ - def __init__(self): - super().__init__() - self.id = u"dig" - self.name = u"DiG" - self.plugin_version = u"0.0.1" - self.version = u"9.9.5-3" + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.id = "dig" + self.name = "DiG" + self.plugin_version = "0.0.1" + self.version = "9.9.5-3" self._command_regex = re.compile(r'^(dig)\s+.*?') def parseOutputString(self, output): @@ -141,7 +141,7 @@ def parseOutputString(self, output): return True -def createPlugin(): - return DigPlugin() +def createPlugin(ignore_info=False): + return DigPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/dirb/__init__.py b/faraday_plugins/plugins/repo/dirb/__init__.py index 0c98f519..fd94db9c 100644 --- a/faraday_plugins/plugins/repo/dirb/__init__.py +++ b/faraday_plugins/plugins/repo/dirb/__init__.py @@ -3,4 +3,4 @@ Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dirb/plugin.py b/faraday_plugins/plugins/repo/dirb/plugin.py index 2aa305d6..e6b51a30 100644 --- a/faraday_plugins/plugins/repo/dirb/plugin.py +++ b/faraday_plugins/plugins/repo/dirb/plugin.py @@ -19,15 +19,15 @@ class dirbPlugin(PluginBase): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "dirb" self.name = "Dirb" self.plugin_version = "0.0.1" self.version = "2.22" self.regexpUrl = r'((http[s]?)\:\/\/([\w\.]+)[.\S]+)' - self._command_regex = re.compile(r'^(?:sudo dirb|dirb|\.\/dirb|sudo \.\/dirb)\s+(?:(http[s]?)' - r'\:\/\/([\w\.]+)[.\S]+)') + self._command_regex = re.compile(r'^(sudo dirb|dirb|\.\/dirb|sudo \.\/dirb)\s+.*?') + self.text = [] def getPort(self, host, proto): @@ -116,7 +116,7 @@ def processCommandString(self, username, current_path, command_string): return "%s%s" % (command_string, extra_arg) -def createPlugin(): - return dirbPlugin() +def createPlugin(ignore_info=False): + return dirbPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/dirsearch/__init__.py b/faraday_plugins/plugins/repo/dirsearch/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/dirsearch/__init__.py +++ b/faraday_plugins/plugins/repo/dirsearch/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dirsearch/plugin.py b/faraday_plugins/plugins/repo/dirsearch/plugin.py index c53cd108..980ce133 100644 --- a/faraday_plugins/plugins/repo/dirsearch/plugin.py +++ b/faraday_plugins/plugins/repo/dirsearch/plugin.py @@ -52,13 +52,14 @@ class DirsearchPlugin(PluginBase): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "dirsearch" self.name = "dirsearch" self.plugin_version = "0.0.1" self.version = "0.0.1" - self._command_regex = re.compile(r'^(sudo )?(python[0-9\.]? )?dirsearch(\.py)\s+?') + self._command_regex = re.compile(r'^(sudo )?(python[0-9\.]? )?(dirsearch\.py)\s+?') self.addSetting("Ignore 403", str, "1") self._use_temp_file = True self._temp_file_extension = "json" @@ -66,7 +67,6 @@ def __init__(self): def parseOutputString(self, output): self.parse_json(output) - @property def should_ignore_403(self): val = self.getSetting('Ignore 403') @@ -132,7 +132,6 @@ def processCommandString(self, username, current_path, command_string): return '{} --json-report {}'.format(command_string, self._output_file_path) -def createPlugin(): - return DirsearchPlugin() +def createPlugin(ignore_info=False): + return DirsearchPlugin(ignore_info=ignore_info) -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/dnsenum/__init__.py b/faraday_plugins/plugins/repo/dnsenum/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/dnsenum/__init__.py +++ b/faraday_plugins/plugins/repo/dnsenum/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dnsenum/plugin.py b/faraday_plugins/plugins/repo/dnsenum/plugin.py index 9fba1611..ac7fbe58 100644 --- a/faraday_plugins/plugins/repo/dnsenum/plugin.py +++ b/faraday_plugins/plugins/repo/dnsenum/plugin.py @@ -4,20 +4,10 @@ See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginBase import re -import os - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +import xml.etree.ElementTree as ET +from faraday_plugins.plugins.plugin import PluginBase __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -69,7 +59,6 @@ def get_items(self, tree): """ @return items A list of Host instances """ - bugtype = '' node = tree.findall('testdata')[0] for hostnode in node.findall('host'): @@ -82,26 +71,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr) - if match_obj is not None: - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -150,8 +121,8 @@ class DnsenumPlugin(PluginBase): Example plugin to parse dnsenum output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Dnsenum" self.name = "Dnsenum XML Output Plugin" self.plugin_version = "0.0.1" @@ -176,7 +147,7 @@ def parseOutputString(self, output): parser = DnsenumXmlParser(output) for item in parser.items: - h_id = self.createAndAddHost(item.ip, hostnames=[item.hostname]) + self.createAndAddHost(item.ip, hostnames=[item.hostname]) del parser @@ -193,11 +164,6 @@ def processCommandString(self, username, current_path, command_string): else: return re.sub(arg_match.group(1), r"-o %s" % self._output_file_path, command_string) - def setHost(self): - pass - - -def createPlugin(): - return DnsenumPlugin() -# I'm Py3 +def createPlugin(ignore_info=False): + return DnsenumPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/dnsmap/__init__.py b/faraday_plugins/plugins/repo/dnsmap/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/dnsmap/__init__.py +++ b/faraday_plugins/plugins/repo/dnsmap/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dnsmap/plugin.py b/faraday_plugins/plugins/repo/dnsmap/plugin.py index 4ceebf98..14fffb09 100644 --- a/faraday_plugins/plugins/repo/dnsmap/plugin.py +++ b/faraday_plugins/plugins/repo/dnsmap/plugin.py @@ -3,10 +3,10 @@ Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginBase import re from collections import defaultdict +from faraday_plugins.plugins.plugin import PluginBase __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -91,8 +91,8 @@ def add_host_info_to_items(self, ip_address, hostname): class DnsmapPlugin(PluginBase): """Example plugin to parse dnsmap output.""" - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Dnsmap" self.name = "Dnsmap Output Plugin" self.plugin_version = "0.3" @@ -104,9 +104,9 @@ def __init__(self): self._use_temp_file = True self._temp_file_extension = "txt" - def canParseCommandString(self, current_input): if self._command_regex.match(current_input.strip()): + self.command = self.get_command(current_input) return True else: return False @@ -137,7 +137,5 @@ def processCommandString(self, username, current_path, command_string): command_string) -def createPlugin(): - return DnsmapPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return DnsmapPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/dnsrecon/__init__.py b/faraday_plugins/plugins/repo/dnsrecon/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/dnsrecon/__init__.py +++ b/faraday_plugins/plugins/repo/dnsrecon/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dnsrecon/plugin.py b/faraday_plugins/plugins/repo/dnsrecon/plugin.py index fd5a3c07..41e1d4d3 100644 --- a/faraday_plugins/plugins/repo/dnsrecon/plugin.py +++ b/faraday_plugins/plugins/repo/dnsrecon/plugin.py @@ -3,19 +3,10 @@ Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginBase import re +import xml.etree.ElementTree as ET -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - +from faraday_plugins.plugins.plugin import PluginBase __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -78,27 +69,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr) - if match_obj is not None: - - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -156,8 +128,8 @@ class DnsreconPlugin(PluginBase): Example plugin to parse dnsrecon output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Dnsrecon" self.name = "Dnsrecon XML Output Plugin" self.plugin_version = "0.0.2" @@ -205,7 +177,7 @@ def parseOutputString(self, output): status="open") if host.zonetransfer == "success": - v_id = self.createAndAddVulnToService( + self.createAndAddVulnToService( h_id, s_id, name="Zone transfer", @@ -239,11 +211,6 @@ def processCommandString(self, username, current_path, command_string): r"--xml %s" % self._output_file_path, command_string) - def setHost(self): - pass - - -def createPlugin(): - return DnsreconPlugin() -# I'm Py3 +def createPlugin(ignore_info=False): + return DnsreconPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/dnswalk/__init__.py b/faraday_plugins/plugins/repo/dnswalk/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/dnswalk/__init__.py +++ b/faraday_plugins/plugins/repo/dnswalk/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/dnswalk/plugin.py b/faraday_plugins/plugins/repo/dnswalk/plugin.py index 8ae73dc5..1f2b58c7 100644 --- a/faraday_plugins/plugins/repo/dnswalk/plugin.py +++ b/faraday_plugins/plugins/repo/dnswalk/plugin.py @@ -5,12 +5,10 @@ """ import re -import os from faraday_plugins.plugins.plugin import PluginBase from faraday_plugins.plugins.plugins_utils import resolve_hostname - __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" __credits__ = ["Francisco Amato"] @@ -42,7 +40,6 @@ def __init__(self, output): for line in lists: mregex = re.search("WARN: ([\w\.]+) ([\w]+) ([\w\.]+):", line) if mregex is not None: - item = { 'host': mregex.group(1), 'ip': mregex.group(3), @@ -63,14 +60,13 @@ def __init__(self, output): self.items.append(item) - class DnswalkPlugin(PluginBase): """ Example plugin to parse dnswalk output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Dnswalk" self.name = "Dnswalk XML Output Plugin" self.plugin_version = "0.0.1" @@ -80,9 +76,9 @@ def __init__(self): self._command_regex = re.compile( r'^(sudo dnswalk|dnswalk|\.\/dnswalk)\s+.*?') - def canParseCommandString(self, current_input): if self._command_regex.match(current_input.strip()): + self.command = self.get_command(current_input) return True else: return False @@ -113,7 +109,5 @@ def parseOutputString(self, output): return True -def createPlugin(): - return DnswalkPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return DnswalkPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/faraday_csv/__init__.py b/faraday_plugins/plugins/repo/faraday_csv/__init__.py index f55478ec..4c2d40a8 100644 --- a/faraday_plugins/plugins/repo/faraday_csv/__init__.py +++ b/faraday_plugins/plugins/repo/faraday_csv/__init__.py @@ -3,4 +3,4 @@ Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/faraday_csv/plugin.py b/faraday_plugins/plugins/repo/faraday_csv/plugin.py index 934313d7..faaa63b2 100644 --- a/faraday_plugins/plugins/repo/faraday_csv/plugin.py +++ b/faraday_plugins/plugins/repo/faraday_csv/plugin.py @@ -248,8 +248,9 @@ def parse_custom_fields(self, row, custom_fields_names): class FaradayCSVPlugin(PluginCSVFormat): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "faraday_csv" self.name = "Faraday CSV Plugin" self.plugin_version = "1.0" @@ -351,5 +352,5 @@ def parseOutputString(self, output): ) -def createPlugin(): - return FaradayCSVPlugin() +def createPlugin(ignore_info=False): + return FaradayCSVPlugin(ignore_info=False) diff --git a/faraday_plugins/plugins/repo/fierce/__init__.py b/faraday_plugins/plugins/repo/fierce/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/fierce/__init__.py +++ b/faraday_plugins/plugins/repo/fierce/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/fierce/plugin.py b/faraday_plugins/plugins/repo/fierce/plugin.py index 25a7adfa..fa3476ce 100644 --- a/faraday_plugins/plugins/repo/fierce/plugin.py +++ b/faraday_plugins/plugins/repo/fierce/plugin.py @@ -101,8 +101,8 @@ class FiercePlugin(PluginBase): Example plugin to parse fierce output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Fierce" self.name = "Fierce Output Plugin" self.plugin_version = "0.0.1" @@ -176,7 +176,7 @@ def parseOutputString(self, output): -def createPlugin(): - return FiercePlugin() +def createPlugin(ignore_info=False): + return FiercePlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/fortify/plugin.py b/faraday_plugins/plugins/repo/fortify/plugin.py index 6da3afae..9ab59f90 100644 --- a/faraday_plugins/plugins/repo/fortify/plugin.py +++ b/faraday_plugins/plugins/repo/fortify/plugin.py @@ -14,8 +14,8 @@ class FortifyPlugin(PluginByExtension): Example plugin to parse nmap output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Fortify" self.name = "Fortify XML Output Plugin" self.plugin_version = "0.0.1" @@ -392,6 +392,6 @@ def format_description(self, vulnID): return text -def createPlugin(): - return FortifyPlugin() +def createPlugin(ignore_info=False): + return FortifyPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/fruitywifi/__init__.py b/faraday_plugins/plugins/repo/fruitywifi/__init__.py index ea531e17..625a6e25 100755 --- a/faraday_plugins/plugins/repo/fruitywifi/__init__.py +++ b/faraday_plugins/plugins/repo/fruitywifi/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/fruitywifi/fruitywifi.py b/faraday_plugins/plugins/repo/fruitywifi/fruitywifi.py index 93a16dd4..3247d106 100755 --- a/faraday_plugins/plugins/repo/fruitywifi/fruitywifi.py +++ b/faraday_plugins/plugins/repo/fruitywifi/fruitywifi.py @@ -147,4 +147,4 @@ def submitGet(self, data): print(json.dumps("No clients connected")) -# I'm Py3 + diff --git a/faraday_plugins/plugins/repo/fruitywifi/plugin.py b/faraday_plugins/plugins/repo/fruitywifi/plugin.py index 86b6bfcb..001ac700 100644 --- a/faraday_plugins/plugins/repo/fruitywifi/plugin.py +++ b/faraday_plugins/plugins/repo/fruitywifi/plugin.py @@ -24,8 +24,8 @@ class FruityWiFiPlugin(PluginBase): This plugin handles FruityWiFi clients. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "fruitywifi" self.name = "FruityWiFi" self.plugin_version = "0.0.1" @@ -127,7 +127,7 @@ def processCommandString(self, username, current_path, command_string): -def createPlugin(): - return FruityWiFiPlugin() +def createPlugin(ignore_info=False): + return FruityWiFiPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/ftp/__init__.py b/faraday_plugins/plugins/repo/ftp/__init__.py index ea531e17..625a6e25 100755 --- a/faraday_plugins/plugins/repo/ftp/__init__.py +++ b/faraday_plugins/plugins/repo/ftp/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/ftp/plugin.py b/faraday_plugins/plugins/repo/ftp/plugin.py index 81faf91e..978caaae 100644 --- a/faraday_plugins/plugins/repo/ftp/plugin.py +++ b/faraday_plugins/plugins/repo/ftp/plugin.py @@ -27,8 +27,8 @@ class CmdFtpPlugin(PluginBase): Basically detects if user was able to connect to a device """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "ftp" self.name = "Ftp" self.plugin_version = "0.0.1" @@ -72,7 +72,7 @@ def processCommandString(self, username, current_path, command_string): self._port = count_args[c - 1] -def createPlugin(): - return CmdFtpPlugin() +def createPlugin(ignore_info=False): + return CmdFtpPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/goohost/__init__.py b/faraday_plugins/plugins/repo/goohost/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/goohost/__init__.py +++ b/faraday_plugins/plugins/repo/goohost/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/goohost/plugin.py b/faraday_plugins/plugins/repo/goohost/plugin.py index 4a1ac738..51cc46ea 100644 --- a/faraday_plugins/plugins/repo/goohost/plugin.py +++ b/faraday_plugins/plugins/repo/goohost/plugin.py @@ -65,8 +65,8 @@ class GoohostPlugin(PluginBase): Example plugin to parse goohost output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Goohost" self.name = "Goohost XML Output Plugin" self.plugin_version = "0.0.1" @@ -129,6 +129,6 @@ def processOutput(self, command_output): self.parseOutputString(command_output) -def createPlugin(): - return GoohostPlugin() +def createPlugin(ignore_info=False): + return GoohostPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/hping3/__init__.py b/faraday_plugins/plugins/repo/hping3/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/hping3/__init__.py +++ b/faraday_plugins/plugins/repo/hping3/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/hping3/plugin.py b/faraday_plugins/plugins/repo/hping3/plugin.py index 6ba684e9..03004fcd 100644 --- a/faraday_plugins/plugins/repo/hping3/plugin.py +++ b/faraday_plugins/plugins/repo/hping3/plugin.py @@ -14,8 +14,8 @@ class hping3(PluginBase): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Hping3" self.name = "hping3" self.plugin_version = "0.0.1" @@ -68,7 +68,7 @@ def parseOutputString(self, output): host_id, service, protocol="tcp", ports=port, status="open") -def createPlugin(): - return hping3() +def createPlugin(ignore_info=False): + return hping3(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/hydra/__init__.py b/faraday_plugins/plugins/repo/hydra/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/hydra/__init__.py +++ b/faraday_plugins/plugins/repo/hydra/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/hydra/plugin.py b/faraday_plugins/plugins/repo/hydra/plugin.py index 3e8ca486..fab9a443 100644 --- a/faraday_plugins/plugins/repo/hydra/plugin.py +++ b/faraday_plugins/plugins/repo/hydra/plugin.py @@ -50,8 +50,8 @@ class HydraPlugin(PluginBase): Example plugin to parse hydra output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Hydra" self.name = "Hydra XML Output Plugin" self.plugin_version = "0.0.1" @@ -127,11 +127,10 @@ def _isIPV4(self, ip): else: return False - def setHost(self): - pass -def createPlugin(): - return HydraPlugin() -# I'm Py3 +def createPlugin(ignore_info=False): + return HydraPlugin(ignore_info=ignore_info) + + diff --git a/faraday_plugins/plugins/repo/impact/__init__.py b/faraday_plugins/plugins/repo/impact/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/impact/__init__.py +++ b/faraday_plugins/plugins/repo/impact/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/impact/plugin.py b/faraday_plugins/plugins/repo/impact/plugin.py index ddf72d90..ff0c3fdf 100644 --- a/faraday_plugins/plugins/repo/impact/plugin.py +++ b/faraday_plugins/plugins/repo/impact/plugin.py @@ -4,19 +4,10 @@ See the file 'doc/LICENSE' for the license information """ -import re -from faraday_plugins.plugins.plugin import PluginXMLFormat -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +import xml.etree.ElementTree as ET +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -42,7 +33,7 @@ class ImpactXmlParser: def __init__(self, xml_output): tree = self.parse_xml(xml_output) if tree: - self.items = [data for data in self.get_items(tree)] + self.items = self.get_items(tree) else: self.items = [] @@ -104,7 +95,6 @@ def __init__(self, item_node, parent=None): agentip = node.get('name').split("/")[1] if self.ip == agentip: - self.agentip = agentip self.ipfrom = self.get_text_from_subnode( @@ -213,8 +203,8 @@ class ImpactPlugin(PluginXMLFormat): Example plugin to parse impact output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "entities" self.id = "CoreImpact" self.name = "Core Impact XML Output Plugin" @@ -228,7 +218,7 @@ def parseOutputString(self, output): mapped_services = {} mapped_ports = {} for item in parser.items: - os_string = f"{item.os} {item.arch }" + os_string = f"{item.os} {item.arch}" h_id = self.createAndAddHost(item.ip, os=os_string, hostnames=[item.host]) for service in item.services: @@ -282,11 +272,5 @@ def parseOutputString(self, output): del parser - def setHost(self): - pass - - -def createPlugin(): - return ImpactPlugin() - - +def createPlugin(ignore_info=False): + return ImpactPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/ip360/__init__.py b/faraday_plugins/plugins/repo/ip360/__init__.py index 454b1c0a..729fe446 100644 --- a/faraday_plugins/plugins/repo/ip360/__init__.py +++ b/faraday_plugins/plugins/repo/ip360/__init__.py @@ -3,4 +3,4 @@ Copyright (C) 2018 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/ip360/plugin.py b/faraday_plugins/plugins/repo/ip360/plugin.py index 3df01e6a..c066759e 100644 --- a/faraday_plugins/plugins/repo/ip360/plugin.py +++ b/faraday_plugins/plugins/repo/ip360/plugin.py @@ -68,8 +68,8 @@ class Ip360Plugin(PluginBase): Example plugin to parse Ip360 output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Ip360" self.name = "Ip360 CSV Output Plugin" self.plugin_version = "0.0.1" @@ -103,7 +103,7 @@ def parseOutputString(self, output): ref=vulnerability.get("ref")) -def createPlugin(): - return Ip360Plugin() +def createPlugin(ignore_info=False): + return Ip360Plugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/junit/__init__.py b/faraday_plugins/plugins/repo/junit/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/junit/__init__.py +++ b/faraday_plugins/plugins/repo/junit/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/junit/plugin.py b/faraday_plugins/plugins/repo/junit/plugin.py index 958383ff..639a4dbf 100644 --- a/faraday_plugins/plugins/repo/junit/plugin.py +++ b/faraday_plugins/plugins/repo/junit/plugin.py @@ -5,19 +5,10 @@ """ import os -from lxml import etree -from faraday_plugins.plugins.plugin import PluginXMLFormat +from lxml import etree -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +from faraday_plugins.plugins.plugin import PluginXMLFormat current_path = os.path.abspath(os.getcwd()) @@ -67,7 +58,7 @@ def __init__(self, xml_output): self.items = [data for data in self.get_items(tree)] else: self.items = [] - + def parse_xml(self, xml_output): """ Open and parse an xml file. @@ -122,8 +113,8 @@ class JunitPlugin(PluginXMLFormat): Example plugin to parse junit output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Junit" self.name = "Junit XML Output Plugin" self.plugin_version = "0.0.1" @@ -133,7 +124,6 @@ def __init__(self): self._current_output = None def parseOutputString(self, output): - parser = JunitXmlParser(output) for item in parser.items: h_id = self.createAndAddHost(item.host, os="Linux") @@ -141,5 +131,5 @@ def parseOutputString(self, output): del parser -def createPlugin(): - return JunitPlugin() +def createPlugin(ignore_info=False): + return JunitPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/lynis/__init__.py b/faraday_plugins/plugins/repo/lynis/__init__.py index 00dc0fca..d417d2e7 100644 --- a/faraday_plugins/plugins/repo/lynis/__init__.py +++ b/faraday_plugins/plugins/repo/lynis/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/lynis/plugin.py b/faraday_plugins/plugins/repo/lynis/plugin.py index cf8c6efa..a7ee102e 100644 --- a/faraday_plugins/plugins/repo/lynis/plugin.py +++ b/faraday_plugins/plugins/repo/lynis/plugin.py @@ -221,8 +221,8 @@ def parse_warnings(self): class LynisPlugin(PluginByExtension): """ Simple example plugin to parse lynis' lynis-report.dat file.""" - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Lynis" self.name = "Lynis DAT Output Plugin" self.plugin_version = "0.4" @@ -324,7 +324,7 @@ def processOutput(self, command_output): self._parse_filename(file_path) -def createPlugin(): - return LynisPlugin() +def createPlugin(ignore_info=False): + return LynisPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/maltego/__init__.py b/faraday_plugins/plugins/repo/maltego/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/maltego/__init__.py +++ b/faraday_plugins/plugins/repo/maltego/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/maltego/plugin.py b/faraday_plugins/plugins/repo/maltego/plugin.py index b1d2113e..f0ce98c9 100755 --- a/faraday_plugins/plugins/repo/maltego/plugin.py +++ b/faraday_plugins/plugins/repo/maltego/plugin.py @@ -3,24 +3,12 @@ Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginZipFormat -import re -import os +import xml.etree.ElementTree as ET import zipfile +from faraday_plugins.plugins.plugin import PluginZipFormat from faraday_plugins.plugins.plugins_utils import resolve_hostname -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - - __author__ = "Ezequiel Tavella" __copyright__ = "Copyright (c) 2015, Infobyte LLC" __credits__ = ["Ezequiel Tavella"] @@ -171,7 +159,6 @@ def getIpAndId(self, node): "{http://graphml.graphdrawing.org/xmlns}data/" "{http://maltego.paterva.com/xml/mtgx}MaltegoEntity") - # Check if is IPv4Address if entity.get("type") not in ("maltego.IPv4Address", "maltego.Domain", "maltego.Website"): return None @@ -370,8 +357,8 @@ def getInfoMtgl(self, xml, name): class MaltegoPlugin(PluginZipFormat): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "maltego" self.id = "Maltego" self.name = "Maltego MTGX & MTGL Output Plugin" @@ -419,7 +406,7 @@ def parseOutputString(self, output): try: text = f'Location:\n {host.location["name"]} \nArea:\n {host.location["area"]} ' \ f'\nArea 2:\n {host.location["area_2"]} ' \ - f'\nCountry_code:\n { host.location["country_code"]} ' \ + f'\nCountry_code:\n {host.location["country_code"]} ' \ f'\nLatitude:\n {host.location["latitude"]} \nLongitude:\n {host.location["longitude"]}' except TypeError: text = "unknown" @@ -467,7 +454,6 @@ def parseOutputString(self, output): host_ip = '0.0.0.0' host_id = self.createAndAddHost(name=host_ip, hostnames=hostnames) - if maltego_parser.xml.get('location'): location_name = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'location.name') location_area = maltego_parser.getInfoMtgl(maltego_parser.xml['location'], 'location.area') @@ -510,7 +496,5 @@ def parseOutputString(self, output): description="DNS Server") - - -def createPlugin(): - return MaltegoPlugin() +def createPlugin(ignore_info=False): + return MaltegoPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/mbsa/__init__.py b/faraday_plugins/plugins/repo/mbsa/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/faraday_plugins/plugins/repo/mbsa/plugin.py b/faraday_plugins/plugins/repo/mbsa/plugin.py new file mode 100644 index 00000000..a9a5ecab --- /dev/null +++ b/faraday_plugins/plugins/repo/mbsa/plugin.py @@ -0,0 +1,118 @@ +""" +Faraday Penetration Test IDE +Copyright (C) 2015 Infobyte LLC (http://www.infobytesec.com/) +See the file 'doc/LICENSE' for the license information +""" +from faraday_plugins.plugins.plugin import PluginByExtension +import re +from datetime import datetime + +__author__ = "Blas Moyano" +__copyright__ = "Copyright (c) 2020, Infobyte LLC" +__credits__ = ["Blas Moyano"] +__license__ = "" +__version__ = "1.0.0" +__maintainer__ = "Blas Moyano" +__status__ = "Development" + + +class MbsaParser: + def __init__(self, log_output): + self.computer_name = re.search('(Computer name:) (.*[A-Z])', log_output) + self.ip = re.search('(IP address:) ([0-9]+(?:\.[0-9]+){3})', log_output) + self.scan_date = re.search('(Scan date:) (.*[0-9])', log_output) + self.issues = re.findall(r'Issue: .*', log_output) + self.score = re.findall(r'Score: .*', log_output) + self.result = re.findall(r'Result: .*', log_output) + + +class MbsaPlugin(PluginByExtension): + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.id = "MBSA" + self.name = "Microsoft Baseline Security Analyzer" + self.plugin_version = "1.0.1" + self.version = "MBSA 1.0" + self.framework_version = "1.0.0" + self.extension = ".log" + + def parseOutputString(self, output): + parser = MbsaParser(output) + detail = '' + i = 0 + issues_top = len(parser.issues) + ip = '0.0.0.0' + hostname = [] + run_date = None + + if parser.ip is not None: + ip = parser.ip.group(2) + if parser.computer_name is not None: + hostname.append(parser.computer_name.group(2)) + if parser.scan_date is not None: + run_date = datetime.strptime(parser.scan_date.group(2), '%Y/%m/%d %H:%M') + + host_id = self.createAndAddHost( + ip, + 'Windows', + hostnames=hostname) + + for issue in parser.issues: + + test = re.search(parser.issues[i], output) + + if i+1 != issues_top: + test_issue = re.search(parser.issues[i+1], output) + else: + end = None + try: + start = test.end() + end = test_issue.start() + except: + start = None + + if start is not None: + if end is None: + result_info = output[start:] + else: + result_info = output[start:end] + result_info.rstrip('\n') + result_info = result_info.replace(parser.score[i], '') + result_info = result_info.replace(parser.result[i], '') + result_info = result_info.strip() + if result_info: + detail = re.search('(Detail:)', result_info) + if not None: + detail = result_info + result_info = parser.result[i] + + else: + detail = '' + result_info = parser.result[i] + score = parser.score[i].replace('Score: ', '').strip() + if score != 'Check passed': + if score == 'Best practice' or score == 'Unable to scan': + severity = "info" + elif score == 'Check failed (non-critical)': + severity = 'med' + elif score == 'Check failed': + severity = 'high' + else: + severity = 'info' + + self.createAndAddVulnToHost( + host_id, + issue.replace('Issue: ', '').strip(), + desc=result_info.replace('Result: ', '').strip(), + ref=None, + severity=severity, + data=detail, + run_date=run_date + ) + + i += 1 + + +def createPlugin(ignore_info=False): + return MbsaPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/medusa/__init__.py b/faraday_plugins/plugins/repo/medusa/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/medusa/__init__.py +++ b/faraday_plugins/plugins/repo/medusa/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/medusa/plugin.py b/faraday_plugins/plugins/repo/medusa/plugin.py index 7aa69d62..f56433cd 100644 --- a/faraday_plugins/plugins/repo/medusa/plugin.py +++ b/faraday_plugins/plugins/repo/medusa/plugin.py @@ -59,8 +59,8 @@ class MedusaPlugin(PluginBase): Example plugin to parse medusa output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Medusa" self.name = "Medusa Output Plugin" self.plugin_version = "0.0.1" @@ -130,9 +130,8 @@ def _isIPV4(self, ip): else: return False - def setHost(self): - pass -def createPlugin(): - return MedusaPlugin() + +def createPlugin(ignore_info=False): + return MedusaPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/metasploit/__init__.py b/faraday_plugins/plugins/repo/metasploit/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/metasploit/__init__.py +++ b/faraday_plugins/plugins/repo/metasploit/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/metasploit/plugin.py b/faraday_plugins/plugins/repo/metasploit/plugin.py index 914d8238..0189364b 100644 --- a/faraday_plugins/plugins/repo/metasploit/plugin.py +++ b/faraday_plugins/plugins/repo/metasploit/plugin.py @@ -3,19 +3,9 @@ Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginXMLFormat -import re - - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION +import xml.etree.ElementTree as ET -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -77,7 +67,6 @@ def get_items(self, tree, webVulns): """ @return items A list of Host instances """ - bugtype = "" for node in tree.findall('hosts/host'): yield Host(node, webVulns) @@ -86,7 +75,6 @@ def get_vulns(self, tree, services): """ @return items A list of WebVuln instances """ - bugtype = "" for node in tree.findall('web_vulns/web_vuln'): yield WebVuln(node, services) @@ -97,26 +85,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr) - if match_obj is not None: - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -211,7 +181,11 @@ def __init__(self, item_node, services): self.query = self.get_text_from_subnode('query') self.request = self.get_text_from_subnode('request') self.category = self.get_text_from_subnode('category-id') - self.service_id = services[self.get_text_from_subnode('web-site-id')] + web_id = self.get_text_from_subnode('web-site-id') + self.service_id = None + if web_id: + self.service_id = services[web_id] + self.isWeb = True def get_text_from_subnode(self, subnode_xpath_expr): @@ -220,6 +194,7 @@ def get_text_from_subnode(self, subnode_xpath_expr): @return An attribute value """ + sub_node = self.node.find(subnode_xpath_expr) if sub_node is not None: if sub_node.text is not None: @@ -325,8 +300,8 @@ class MetasploitPlugin(PluginXMLFormat): Example plugin to parse metasploit output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["MetasploitV4", "MetasploitV5"] self.id = "Metasploit" self.name = "Metasploit XML Output Plugin" @@ -336,7 +311,6 @@ def __init__(self): self.options = None self.target = None - def parseOutputString(self, output): """ This method will discard the output the shell sends, it will read it from @@ -349,7 +323,7 @@ def parseOutputString(self, output): self.hostnames = [] if item.host: self.hostnames = [item.host] - + h_id = self.createAndAddHost(item.ip, os=item.os, hostnames=self.hostnames) if item.id + "_" in item.notesByService: @@ -357,15 +331,15 @@ def parseOutputString(self, output): self.createAndAddNoteToHost(h_id, n.ntype, n.data) for v in item.vulnsByHost: - v_id = self.createAndAddVulnToHost( + self.createAndAddVulnToHost( h_id, v.name, v.desc, ref=v.refs) for s in item.services: s_id = self.createAndAddServiceToHost(h_id, s['name'], - protocol=s['proto'], - ports=[s['port']], - status=s['state'], - description=s['info']) + protocol=s['proto'], + ports=[s['port']], + status=s['state'], + description=s['info']) if item.id + "_" + s['id'] in item.notesByService: for n in item.notesByService[item.id + "_" + s['id']]: @@ -376,34 +350,30 @@ def parseOutputString(self, output): for c in item.credsByService[s['port']]: self.createAndAddCredToService( h_id, s_id, c.user, c.passwd) - self.createAndAddVulnToService(h_id, s_id, "Weak Credentials", "[metasploit found the following credentials]\nuser:%s\npass:%s" % ( - c.user, c.passwd), severity="high") + self.createAndAddVulnToService(h_id, s_id, "Weak Credentials", + "[metasploit found the following credentials]\nuser:%s\npass:%s" % ( + c.user, c.passwd), severity="high") for v in item.vulnsByService[s['id']]: if v.isWeb: - v_id = self.createAndAddVulnWebToService(h_id, s_id, v.name, v.desc, + self.createAndAddVulnWebToService(h_id, s_id, v.name, v.desc, severity=v.risk, website=v.host, path=v.path, request=v.request, method=v.method, pname=v.pname, params=v.params, query=v.query, category=v.category) else: - v_id = self.createAndAddVulnToService( + self.createAndAddVulnToService( h_id, s_id, v.name, v.desc, ref=v.refs) del parser - def _isIPV4(self, ip): + @staticmethod + def _isIPV4(ip): if len(ip.split(".")) == 4: return True else: return False - def setHost(self): - pass - - -def createPlugin(): - return MetasploitPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return MetasploitPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/naabu/__init__.py b/faraday_plugins/plugins/repo/naabu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/faraday_plugins/plugins/repo/naabu/plugin.py b/faraday_plugins/plugins/repo/naabu/plugin.py new file mode 100644 index 00000000..a737c960 --- /dev/null +++ b/faraday_plugins/plugins/repo/naabu/plugin.py @@ -0,0 +1,71 @@ +""" +Faraday Plugins +Copyright (c) 2021 Faraday Security LLC (https://www.faradaysec.com/) +See the file 'doc/LICENSE' for the license information + +""" +import socket +import json +import re +from faraday_plugins.plugins.plugin import PluginMultiLineJsonFormat + +__author__ = 'Emilio Couto' +__copyright__ = 'Copyright (c) 2021, Faraday Security LLC' +__credits__ = ['Emilio Couto'] +__license__ = '' +__version__ = '0.0.1' +__maintainer__ = 'Emilio Couto' +__email__ = 'ecouto@faradaysec.com' +__status__ = 'Development' + + +class NaabuPlugin(PluginMultiLineJsonFormat): + """ + Parse Naabu (from Project Discovery) scanner JSON output + """ + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.id = 'naabu' + self.name = 'Naabu' + self.plugin_version = '0.1' + self.version = '2.0.3' + self.json_keys = {'host', 'ip', 'port'} + self._command_regex = re.compile(r'^(sudo naabu|naabu|\.\/nmap)\s+.*?') + + def parseOutputString(self, output, debug=False): + for host_json in filter(lambda x: x != '', output.split('\n')): + host_dict = json.loads(host_json) + host = host_dict.get('host') + ip = host_dict.get('ip') + port = host_dict.get('port') + try: + service = socket.getservbyport(port) + except socket.error: + service = 'Unknown service on port ' + str(port) + host_id = self.createAndAddHost( + name=ip, + hostnames=[host]) + self.createAndAddServiceToHost( + host_id, + name=service, + ports=port, + protocol='tcp', + status='open', + version='', + description='') + + def processCommandString(self, username, current_path, command_string): + """ + Adds the -oX parameter to get xml output to the command string that the + user has set. + """ + super().processCommandString(username, current_path, command_string) + if " -json" not in command_string: + command_string += " -json" + if " -silent" not in command_string: + command_string += " -silent" + return command_string + +def createPlugin(ignore_info=False): + return NaabuPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/ncrack/plugin.py b/faraday_plugins/plugins/repo/ncrack/plugin.py index aaa7542a..7c3611c6 100644 --- a/faraday_plugins/plugins/repo/ncrack/plugin.py +++ b/faraday_plugins/plugins/repo/ncrack/plugin.py @@ -5,12 +5,9 @@ """ -from faraday_plugins.plugins.plugin import PluginXMLFormat -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET +import xml.etree.ElementTree as ET +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Blas Moyano" __copyright__ = "Copyright (c) 2020, Infobyte LLC" @@ -92,8 +89,9 @@ def get_service(self, tree): class NcrackPlugin(PluginXMLFormat): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "ncrackrun" self.id = 'ncrack' self.name = 'ncrack XML Plugin' @@ -121,5 +119,5 @@ def parseOutputString(self, output): password=service_vuln['passw']) -def createPlugin(): - return NcrackPlugin() +def createPlugin(ignore_info=False): + return NcrackPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/ndiff/__init__.py b/faraday_plugins/plugins/repo/ndiff/__init__.py index 81e2e0d9..cef3c4e2 100644 --- a/faraday_plugins/plugins/repo/ndiff/__init__.py +++ b/faraday_plugins/plugins/repo/ndiff/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/ndiff/plugin.py b/faraday_plugins/plugins/repo/ndiff/plugin.py index f7263331..75f86fd0 100644 --- a/faraday_plugins/plugins/repo/ndiff/plugin.py +++ b/faraday_plugins/plugins/repo/ndiff/plugin.py @@ -7,11 +7,7 @@ from faraday_plugins.plugins.plugin import PluginBase import re -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG -except ImportError: - import xml.etree.ElementTree as ET +import xml.etree.ElementTree as ET __author__ = 'Ezequiel Tavella' __copyright__ = 'Copyright (c) 2016, Infobyte LLC' @@ -109,8 +105,8 @@ class CmdNdiffPlugin(PluginBase): Add a new vuln INFO if detect a new host or a new port .. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Ndiff" self.name = "ndiff" self.plugin_version = "0.0.1" @@ -156,7 +152,7 @@ def processCommandString(self, username, current_path, command_string): return f"{command_string} --xml " -def createPlugin(): - return CmdNdiffPlugin() +def createPlugin(ignore_info=False): + return CmdNdiffPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/nessus/DTO.py b/faraday_plugins/plugins/repo/nessus/DTO.py new file mode 100644 index 00000000..599c99fe --- /dev/null +++ b/faraday_plugins/plugins/repo/nessus/DTO.py @@ -0,0 +1,397 @@ +from typing import List + + +class Attachment: + def __init__(self, node): + self.node = node + + @property + def name_attr(self): + return self.node.get("name") + + @property + def type_attr(self): + return self.node.get("type") + + @property + def text(self): + return self.node.text + + +class ReportItem: + def __init__(self, node): + self.node = node + + @property + def port_attr(self): + return self.node.get("port") + + @property + def svc_name_attr(self): + return self.node.get("svc_name") + + @property + def protocol_attr(self): + return self.node.get("protocol") + + @property + def severity_attr(self): + return self.node.get("severity") + + @property + def plugin_id_attr(self): + plugin_id = self.node.get("pluginID") + if plugin_id: + plugin_id = f'NESSUS-{plugin_id}' + return plugin_id + + @property + def plugin_name_attr(self): + return self.node.get("pluginName") + + @property + def plugin_family_attr(self): + return self.node.get("pluginFamily") + + @property + def agent(self): + return self.node.findtext("agent") + + @property + def description(self): + return self.node.findtext("description", "Not Description") + + @property + def fname(self): + return self.node.findtext("fname") + + @property + def plugin_modification_date(self): + return self.node.findtext("plugin_modification_date") + + @property + def plugin_name(self): + + plugin_name = self.node.findtext("plugin_name") + if not plugin_name: + plugin_name = self.plugin_name_attr + return plugin_name + + @property + def plugin_publication_date(self): + return self.node.findtext("plugin_publication_date") + + @property + def plugin_type(self): + return self.node.findtext("plugin_type") + + @property + def risk_factor(self): + risk_factor = self.node.findtext("risk_factor") + if risk_factor == 'None' or risk_factor is None: + risk_factor = self.severity_attr # I checked several external id and most of them were info + return risk_factor + + @property + def script_version(self): + return self.node.findtext("script_version") + + @property + def see_also(self): + return self.node.findtext("see_also") + + @property + def solution(self): + return self.node.findtext("solution", '') + + @property + def synopsis(self): + return self.node.findtext("synopsis") + + @property + def plugin_output(self): + return self.node.findtext("plugin_output", "") + + @property + def always_run(self): + return self.node.findtext("always_run") + + @property + def asset_inventory(self): + return self.node.findtext("asset_inventory") + + @property + def canvas_package(self): + return self.node.findtext("canvas_package") + + @property + def cvss3_base_score(self): + cvss_base_score = self.node.findtext("cvss3_base_score") + if cvss_base_score: + cvss_base_score = f"CVSS3:{cvss_base_score}" + return cvss_base_score + + @property + def cvss3_temporal_score(self): + return self.node.findtext("cvss3_temporal_score") + + @property + def cpe(self): + return self.node.findtext("cpe") + + @property + def cvss3_temporal_vector(self): + return self.node.findtext("cvss3_temporal_vector") + + @property + def cvss3_vector(self): + return self.node.findtext("cvss3_vector") + + @property + def cvss_base_score(self): + cvss_base_score = self.node.findtext("cvss_base_score") + if cvss_base_score: + cvss_base_score = f"CVSS:{cvss_base_score}" + return cvss_base_score + + @property + def cvss_score_rationale(self): + return self.node.findtext("cvss_score_rationale") + + @property + def cvss_score_source(self): + return self.node.findtext("cvss_score_source") + + @property + def cvss_temporal_score(self): + return self.node.findtext("cvss_temporal_score") + + @property + def cvss_temporal_vector(self): + return self.node.findtext("cvss_temporal_vector") + + @property + def cvss_vector(self): + cvss_vector = self.node.findtext("cvss_vector") + if cvss_vector: + cvss_vector = f"CVSSVECTOR:{cvss_vector}" + return cvss_vector + + @property + def exploit_available(self): + exploit_avalible = self.node.findtext("exploit_available", "") + if exploit_avalible: + exploit_avalible = f"Exploit available: {exploit_avalible.capitalize()}\n" + return exploit_avalible + + @property + def exploit_framework_canvas(self): + return self.node.findtext("exploit_framework_canvas") + + @property + def exploit_framework_core(self): + return self.node.findtext("exploit_framework_core") + + @property + def exploit_framework_d2_elliot(self): + return self.node.findtext("exploit_framework_d2_elliot") + + @property + def exploit_framework_metasploit(self): + return self.node.findtext("exploit_framework_metasploit") + + @property + def exploitability_ease(self): + return self.node.findtext("exploitability_ease") + + @property + def exploited_by_malware(self): + return self.node.findtext("exploited_by_malware") + + @property + def exploited_by_nessus(self): + return self.node.findtext("exploited_by_nessus") + + @property + def hardware_inventory(self): + return self.node.findtext("hardware_inventory") + + @property + def iava(self): + return self.node.findtext("iava") + + @property + def iavb(self): + return self.node.findtext("iavb") + + @property + def iavt(self): + return self.node.findtext("iavt") + + @property + def in_the_news(self): + return self.node.findtext("in_the_news") + + @property + def metasploit_name(self): + return self.node.findtext("metasploit_name") + + @property + def os_identification(self): + return self.node.findtext("os_identification") + + @property + def owasp(self): + return self.node.findtext("owasp") + + @property + def patch_publication_date(self): + return self.node.findtext("patch_publication_date") + + + @property + def stig_severity(self): + return self.node.findtext("stig_severity") + + @property + def d2_elliot_name(self): + return self.node.findtext("d2_elliot_name") + + @property + def unsupported_by_vendor(self): + return self.node.findtext("unsupported_by_vendor") + + @property + def vuln_publication_date(self): + return self.node.findtext("vuln_publication_date") + + @property + def msft(self): + return self.node.findtext("msft") + + + @property + def cert(self) -> list: + return self.node.findall("cert") + + @property + def bid(self) -> list: + return self.node.findall("bid") + + @property + def cve(self) -> list: + return [i.text for i in self.node.findall("cve")] + + @property + def cwe(self) -> list: + return [i.text for i in self.node.findall("cwe")] + + @property + def edb_id(self) -> list: + return self.node.findall("edb-id") + + @property + def mskb(self) -> list: + return self.node.findall("mskb") + + @property + def xref(self) -> str: + return self.node.findtext("xref") + + @property + def attachment(self) -> Attachment: + attachment = self.node.find("attachment") + return Attachment(attachment) if attachment else None + + def get_data(self): + item_tags = {} + for i in self.node: + item_tags.setdefault(i.tag, i.text) + return item_tags + + +class Tag: + def __init__(self, node): + self.node = node + + @property + def name_attr(self) -> str: + return self.node.get("name") + + @property + def text(self) -> str: + return self.node.text + + +class HostProperties: + def __init__(self, node): + self.node = node + + @property + def tag(self) -> list: + return [Tag(i) for i in self.node.findall('tag')] + + @property + def host_end(self) -> str: + _dict = self.dict_tags + return _dict.get("HOST_END") + + @property + def mac_address(self) -> str: + _dict = self.dict_tags + return _dict.get("mac-address", "") + + @property + def operating_system(self) -> str: + _dict = self.dict_tags + return _dict.get("operating-system", None) + + @property + def host_ip(self) -> str: + _dict = self.dict_tags + + return _dict.get("host-ip", None) + + @property + def host_fqdn(self) -> str: + _dict = self.dict_tags + return _dict.get("host-fqdn", None) + + @property + def dict_tags(self): + host_tags = {} + for t in self.node: + host_tags.setdefault(t.attrib.get('name'), t.text) + return host_tags + + +class ReportHost: + def __init__(self, node): + self.node = node + + @property + def name(self) -> str: + return self.node.get("name") + + @property + def host_properties(self) -> HostProperties: + return HostProperties(self.node.find("HostProperties")) + + @property + def report_items(self) -> List[ReportItem]: + return [ReportItem(i) for i in self.node.findall("ReportItem")] + + +class Report: + + def __init__(self, node): + self.node = node + + @property + def name_attr(self) -> str: + return self.node.get("name") + + @property + def report_hosts(self) -> List[ReportHost]: + return [ReportHost(i) for i in self.node.findall('ReportHost')] + diff --git a/faraday_plugins/plugins/repo/nessus/__init__.py b/faraday_plugins/plugins/repo/nessus/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/nessus/__init__.py +++ b/faraday_plugins/plugins/repo/nessus/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/nessus/checksvc.py b/faraday_plugins/plugins/repo/nessus/checksvc.py new file mode 100644 index 00000000..c4aa7a5d --- /dev/null +++ b/faraday_plugins/plugins/repo/nessus/checksvc.py @@ -0,0 +1,24 @@ +import http.client +import ssl + +def is_https(target): + try: + ctx = ssl._create_unverified_context() + ctx.set_ciphers('DEFAULT@SECLEVEL=0') + conn = http.client.HTTPSConnection(target, context=ctx, timeout=4) + headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE5.01; Windows NT)'} + conn.request(method="GET", url="/", headers=headers) + var = conn.getresponse() + return True + except: + return False + +def is_http(target): + try: + conn = http.client.HTTPConnection(target, timeout=4) + headers = {'User-Agent': 'Mozilla/4.0 (compatible; MSIE5.01; Windows NT)'} + conn.request(method="GET", url="/", headers=headers) + var = conn.getresponse() + return True + except: + return False diff --git a/faraday_plugins/plugins/repo/nessus/plugin.py b/faraday_plugins/plugins/repo/nessus/plugin.py index af2b6cd3..02d84884 100644 --- a/faraday_plugins/plugins/repo/nessus/plugin.py +++ b/faraday_plugins/plugins/repo/nessus/plugin.py @@ -4,21 +4,23 @@ See the file 'doc/LICENSE' for the license information """ -import dateutil -from faraday_plugins.plugins.plugin import PluginXMLFormat import xml.etree.ElementTree as ET +import dateutil +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Blas" __copyright__ = "Copyright (c) 2019, Infobyte LLC" -__credits__ = ["Blas"] +__credits__ = ["Blas", "Nicolas Rebagliati"] __license__ = "" __version__ = "1.0.0" __maintainer__ = "Blas" __email__ = "bmoyano@infobytesec.com" __status__ = "Development" +from faraday_plugins.plugins.repo.nessus.DTO import ReportHost, Report, ReportItem +from faraday_plugins.plugins.repo.nessus import checksvc class NessusParser: """ @@ -33,139 +35,13 @@ class NessusParser: def __init__(self, output): self.tree = ET.fromstring(output) - self.tag_control = [] - for x in self.tree: - self.tag_control.append(x) + self.report = [] if self.tree: - self.policy = self.getPolicy(self.tree) - self.report = self.getReport(self.tree) - else: - self.policy = None - self.report = None - - def getPolicy(self, tree): - policy_tree = tree.find('Policy') - if policy_tree: - return Policy(policy_tree) - else: - return None - - def getReport(self, tree): - report_tree = tree.find('Report') - return Report(report_tree) - - -class Policy(): - def __init__(self, policy_node): - self.node = policy_node - self.policy_name = self.node.find('policyName').text - self.preferences = self.getPreferences(self.node.find('Preferences')) - self.family_selection = self.getFamilySelection(self.node.find('FamilySelection')) - self.individual_plugin_selection = self.getIndividualPluginSelection( - self.node.find('IndividualPluginSelection')) - - def getPreferences(self, preferences): - server_preferences = preferences.find('ServerPreferences') - plugins_preferences = preferences.find('PluginsPreferences') - server_preferences_all = [] - plugins_preferences_json = {} - plugins_preferences_all = [] - for sp in server_preferences: - sp_value = sp.find('value').text - sp_name = sp.find('name').text - server_preferences_all.append("Server Preferences name: {}, Server Preferences value: {}".format(sp_name, - sp_value)) - for pp in plugins_preferences: - for pp_detail in pp: - plugins_preferences_json.setdefault(pp_detail.tag, pp_detail.text) - plugins_preferences_all.append(plugins_preferences_json) - return server_preferences_all, plugins_preferences_all - - def getFamilySelection(self, family): - family_all = [] - for f in family: - family_name = f.find('FamilyName').text - family_value = f.find('Status').text - family_all.append("Family Name: {}, Family Value: {}".format(family_name, family_value)) - return family_all - - def getIndividualPluginSelection(self, individual): - item_plugin = [] - for i in individual: - plugin_id = i.find('PluginId').text - plugin_name = i.find('PluginName').text - plugin_family = i.find('Family').text - plugin_status = i.find('Status').text - item_plugin.append("Plugin ID: {}, Plugin Name: {}, Family: {}, Status: {}".format(plugin_id, plugin_name, - plugin_family, - plugin_status)) - return item_plugin + self.report = self.__get_report() - -class Report(): - def __init__(self, report_node): - self.node = report_node - self.report_name = self.node.attrib.get('name') - self.report_host = self.node.find('ReportHost') - self.report_desc = [] - self.report_ip = [] - self.report_serv = [] - self.report_json = {} - if self.report_host is not None: - for x in self.node: - self.report_host_ip = x.attrib.get('name') - self.host_properties = self.gethosttag(x.find('HostProperties')) - self.report_item = self.getreportitems(x.findall('ReportItem')) - self.report_ip.append(self.report_host_ip) - self.report_desc.append(self.host_properties) - self.report_serv.append(self.report_item) - self.report_json['ip'] = self.report_ip - self.report_json['desc'] = self.report_desc - self.report_json['serv'] = self.report_serv - self.report_json['host_end'] = self.host_properties.get('HOST_END') - - else: - self.report_host_ip = None - self.host_properties = None - self.report_item = None - self.report_json = None - - def getreportitems(self, items): - result_item = [] - - for item in items: - self.port = item.attrib.get('port') - self.svc_name = item.attrib.get('svc_name') - self.protocol = item.attrib.get('protocol') - self.severity = item.attrib.get('severity') - self.plugin_id = item.attrib.get('pluginID') - self.plugin_name = item.attrib.get('pluginName') - self.plugin_family = item.attrib.get('pluginFamily') - if item.find('plugin_output') is not None: - self.plugin_output = item.find('plugin_output').text - else: - self.plugin_output = "Not Description" - if item.find('description') is not None: - self.description = item.find('description').text - else: - self.description = "Not Description" - - self.info = self.getinfoitem(item) - result_item.append((self.port, self.svc_name, self.protocol, self.severity, self.plugin_id, - self.plugin_name, self.plugin_family, self.description, self.plugin_output, self.info)) - return result_item - - def getinfoitem(self, item): - item_tags = {} - for i in item: - item_tags.setdefault(i.tag, i.text) - return item_tags - - def gethosttag(self, tags): - host_tags = {} - for t in tags: - host_tags.setdefault(t.attrib.get('name'), t.text) - return host_tags + def __get_report(self) -> Report: + report = self.tree.find('Report') + return Report(report) if report else None class NessusPlugin(PluginXMLFormat): @@ -173,8 +49,8 @@ class NessusPlugin(PluginXMLFormat): Example plugin to parse nessus output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.extension = ".nessus" self.identifier_tag = "NessusClientData_v2" self.id = "Nessus" @@ -184,6 +60,71 @@ def __init__(self): self.framework_version = "1.0.1" self.options = None + @staticmethod + def parse_compliance_data(data: dict): + compliance_data = {} + for key, value in data.items(): + if 'compliance-' in key: + compliance_name = key.split("}")[-1] + compliance_data[compliance_name] = value + return compliance_data + + @staticmethod + def map_properties(host: ReportHost): + return { + "name": host.host_properties.host_ip if host.host_properties.host_ip else host.name, + "hostnames": host.host_properties.host_fqdn, + "mac": host.host_properties.mac_address, + "os": host.host_properties.operating_system + } + + @staticmethod + def map_item(host_id, run_date, plugin_name, item: ReportItem) -> dict: + cvss_base_score = item.cvss_base_score + data = item.plugin_output + data += f'{item.exploit_available}' + return { + "host_id": host_id, + "name": plugin_name, + "severity": item.risk_factor, + "data": data, + "external_id": item.plugin_id_attr, + "run_date": run_date, + "desc": item.description, + "resolution": item.solution, + "ref": [cvss_base_score] if cvss_base_score else [] + } + + def map_policy_general(self, kwargs, item: ReportItem): + kwargs.update({"policyviolations": []}) + if item.plugin_family_attr == 'Policy Compliance': + data = item.get_data() + bis_benchmark_data = kwargs["desc"].split('\n') + compliance_data = self.parse_compliance_data(data) + compliance_info = compliance_data.get('compliance-info', '') + if compliance_info and not kwargs["desc"]: + kwargs["desc"] = compliance_info + compliance_reference = compliance_data.get( + 'compliance-reference', '').replace('|', ':').split(',') + compliance_result = compliance_data.get('compliance-result', '') + for reference in compliance_reference: + kwargs["ref"].append(reference) + compliance_check_name = compliance_data.get('compliance-check-name', '') + compliance_solution = compliance_data.get('compliance-solution', '') + if compliance_solution and not kwargs["resolution"]: + kwargs["resolution"] = compliance_solution + policy_item = f'{compliance_check_name} - {compliance_result}' + for policy_check_data in bis_benchmark_data: + if 'ref.' in policy_check_data: + kwargs["ref"].append(policy_check_data) + if 'compliance-see-also' in compliance_data: + kwargs["ref"].append(compliance_data.get('compliance-see-also')) + # We used this info from tenable: https://community.tenable.com/s/article/Compliance-checks-in-SecurityCenter + kwargs["policyviolations"].append(policy_item) + kwargs["name"] = f'{kwargs["name"]}: {policy_item}' + + return kwargs + def parseOutputString(self, output): """ This method will discard the output the shell sends, it will read it from @@ -192,183 +133,72 @@ def parseOutputString(self, output): NOTE: if 'debug' is true then it is being run from a test case and the output being sent is valid. """ + try: parser = NessusParser(output) except Exception as e: - print(e) + self.logger.error(str(e)) return None - - if parser.report.report_json is not None: - run_date = parser.report.report_json.get('host_end') - if run_date: - run_date = dateutil.parser.parse(run_date) - for set_info, ip in enumerate(parser.report.report_json['ip'], start=1): - if 'mac-address' in parser.report.report_json['desc'][set_info - 1]: - mac = parser.report.report_json['desc'][set_info - 1]['mac-address'] - else: - mac = '' - if 'operating-system' in parser.report.report_json['desc'][set_info - 1]: - os = parser.report.report_json['desc'][set_info - 1]['operating-system'] - else: - os = None - - if 'host-ip' in parser.report.report_json['desc'][set_info - 1]: - ip_host = parser.report.report_json['desc'][set_info - 1]['host-ip'] - else: - ip_host = "0.0.0.0" - if 'host-fqdn' in parser.report.report_json['desc'][set_info - 1]: - website = parser.report.report_json['desc'][set_info - 1]['host-fqdn'] - host_name = [] - host_name.append(parser.report.report_json['desc'][set_info - 1]['host-fqdn']) - else: - website = None - host_name = None - - host_id = self.createAndAddHost(ip_host, os=os, hostnames=host_name, mac=mac) - cve = [] - for serv in parser.report.report_json['serv'][set_info -1]: - serv_name = serv[1] - serv_port = serv[0] - serv_protocol = serv[2] - serv_status = serv[3] - external_id = serv[4] - serv_description = serv[7] - cve.append(serv[8]) - severity = serv[3] - desc = serv[8] - - if serv_name == 'general': - ref = [] - vulnerability_name = serv[5] - data = serv[9] - if not data: - continue - if 'description' in data: - desc = data['description'] - else: - desc = "No description" - if 'solution' in data: - resolution = data['solution'] - else: - resolution = "No Solution" - if 'plugin_output' in data: - data_po = data['plugin_output'] - else: - data_po = "Not data" - - risk_factor = "unclassified" - if 'risk_factor' in data: - risk_factor = data['risk_factor'] - if risk_factor == 'None': - risk_factor = "info" # I checked several external id and most of them were info - - if 'cvss_base_score' in data: - cvss_base_score = "CVSS :{}".format(data['cvss_base_score']) - ref.append(cvss_base_score) - else: - ref = [] - - policyviolations = [] - tags_info = None - if serv[6] == 'Policy Compliance': - # This condition was added to support CIS Benchmark in policy violation field. - risk_factor = 'info' - tags_info = "Passed Checks" - bis_benchmark_data = serv[7].split('\n') - policy_item = bis_benchmark_data[0] - - for policy_check_data in bis_benchmark_data: - if 'ref.' in policy_check_data: - ref.append(policy_check_data) - - if 'FAILED' in policy_item: - risk_factor = 'low' - tags_info = "Failed checks" - policyviolations.append(policy_item) - - vulnerability_name = f'{serv[6]} {vulnerability_name} {policy_item}' - self.createAndAddVulnToHost(host_id, - vulnerability_name, - desc=desc, - severity=risk_factor, - resolution=resolution, - data=data_po, - ref=ref, - policyviolations=policyviolations, - external_id=external_id, - run_date=run_date, - tags=tags_info) + report_hosts = parser.report.report_hosts + + if report_hosts: + for host in report_hosts: + run_date = host.host_properties.host_end + if run_date: + run_date = dateutil.parser.parse(run_date) + website = host.host_properties.host_fqdn + host_id = self.createAndAddHost(**self.map_properties(host)) + + for item in host.report_items: + vulnerability_name = item.plugin_name + if not vulnerability_name: + continue + item_name = item.svc_name_attr + #check if service is http or https: + if not item_name == 'general' and item.protocol_attr == 'tcp': + target = host.name + ":" + item.port_attr + if checksvc.is_https(target): + item_name = 'https' + elif checksvc.is_http(target): + item_name = 'http' + #End check + _main_data = self.map_item( + host_id, run_date, vulnerability_name, item) + + _main_data = self.map_add_ref(_main_data, item) + if item_name == 'general': + _main_data = self.map_policy_general(_main_data, item) + self.createAndAddVulnToHost(**_main_data) else: - data = serv[9] - if not data: - continue - ref = [] - vulnerability_name = serv[5] - if 'description' in data: - desc = data['description'] - else: - desc = "No description" - if 'solution' in data: - resolution = data['solution'] - else: - resolution = "No Solution" - if 'plugin_output' in data: - data_po = data['plugin_output'] + _main_data["service_id"] = self.createAndAddServiceToHost( + host_id, name=item_name, protocol=item.protocol_attr, + ports=item.port_attr) + if item_name == 'www' or item_name == 'http' or item_name == 'https': + _main_data.update({"website": website}) + self.createAndAddVulnWebToService(**_main_data) else: - data_po = "Not data" - - risk_factor = "info" - if 'risk_factor' in data: - risk_factor = data['risk_factor'] - - if risk_factor == 'None': - risk_factor = 'info' - - if 'cvss_base_score' in data: - cvss_base_score = f"CVSS:{data['cvss_base_score']}" - ref.append(cvss_base_score) - if 'cvss_vector' in data: - cvss_vector = f"CVSSVECTOR:{data['cvss_vector']}" - ref.append(cvss_vector) - if 'see_also' in data: - ref.append(data['see_also']) - if 'cpe' in data: - ref.append(data['cpe']) - if 'xref' in data: - ref.append(data['xref']) - - service_id = self.createAndAddServiceToHost(host_id, name=serv_name, protocol=serv_protocol, - ports=serv_port) - - if serv_name == 'www' or serv_name == 'http': - self.createAndAddVulnWebToService(host_id, - service_id, - name=vulnerability_name, - desc=desc, - data=data_po, - severity=risk_factor, - resolution=resolution, - ref=ref, - external_id=external_id, - website=website, - run_date=run_date) - else: - self.createAndAddVulnToService(host_id, - service_id, - name=vulnerability_name, - severity=risk_factor, - desc=desc, - ref=ref, - data=data_po, - external_id=external_id, - resolution=resolution, - run_date=run_date) - else: - ip = '0.0.0.0' - host_id = self.createAndAddHost(ip, hostnames=None) - service_id = self.createAndAddServiceToHost(host_id,name="Not Information") - self.createAndAddVulnToService(host_id, service_id, name=parser.policy.policy_name, desc="No Description") - - -def createPlugin(): - return NessusPlugin() + self.createAndAddVulnToService(**_main_data) + + @staticmethod + def map_add_ref(kwargs, item: ReportItem): + + if item.cvss_vector: + kwargs["ref"].append(item.cvss_vector) + if item.see_also: + kwargs["ref"].append(item.see_also) + if item.cpe: + kwargs["ref"].append(item.cpe) + if item.xref: + kwargs["ref"].append(item.xref) + if item.cve: + kwargs["ref"] = kwargs["ref"] + item.cve + if item.cvss3_base_score: + kwargs["ref"].append(item.cvss3_base_score) + if item.cvss3_vector: + kwargs["ref"].append(item.cvss3_vector) + + return kwargs + + +def createPlugin(ignore_info=False): + return NessusPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/netdiscover/__init__.py b/faraday_plugins/plugins/repo/netdiscover/__init__.py index 0c98f519..fd94db9c 100644 --- a/faraday_plugins/plugins/repo/netdiscover/__init__.py +++ b/faraday_plugins/plugins/repo/netdiscover/__init__.py @@ -3,4 +3,4 @@ Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/netdiscover/plugin.py b/faraday_plugins/plugins/repo/netdiscover/plugin.py index b87e7a17..e1072cb9 100644 --- a/faraday_plugins/plugins/repo/netdiscover/plugin.py +++ b/faraday_plugins/plugins/repo/netdiscover/plugin.py @@ -16,8 +16,8 @@ class NetdiscoverPlugin(PluginBase): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Netdiscover" self.name = "netdiscover" self.plugin_version = "0.0.1" @@ -39,7 +39,7 @@ def parseOutputString(self, output): -def createPlugin(): - return NetdiscoverPlugin() +def createPlugin(ignore_info=False): + return NetdiscoverPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/netsparker/__init__.py b/faraday_plugins/plugins/repo/netsparker/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/netsparker/__init__.py +++ b/faraday_plugins/plugins/repo/netsparker/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/netsparker/plugin.py b/faraday_plugins/plugins/repo/netsparker/plugin.py index 7fdd1403..8292a387 100644 --- a/faraday_plugins/plugins/repo/netsparker/plugin.py +++ b/faraday_plugins/plugins/repo/netsparker/plugin.py @@ -5,21 +5,13 @@ """ import re +import xml.etree.ElementTree as ET + from bs4 import BeautifulSoup + from faraday_plugins.plugins.plugin import PluginXMLFormat from faraday_plugins.plugins.plugins_utils import resolve_hostname -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - - __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" __credits__ = ["Francisco Amato"] @@ -83,12 +75,7 @@ class Item: @param item_node A item_node taken from an netsparker xml tree """ - def re_map_severity(self, severity): - if severity == "Important": - return "high" - return severity - - def __init__(self, item_node, encoding="ascii"): + def __init__(self, item_node): self.node = item_node self.url = self.get_text_from_subnode("url") @@ -118,7 +105,8 @@ def __init__(self, item_node, encoding="ascii"): self.param = self.get_text_from_subnode("vulnerableparameter") self.paramval = self.get_text_from_subnode("vulnerableparametervalue") self.reference = self.get_text_from_subnode("externalReferences") - self.resolution = self.get_text_from_subnode("actionsToTake") + remedy = self.get_text_from_subnode("remedy") + self.resolution = self.get_text_from_subnode("actionsToTake") + remedy if remedy else "" self.request = self.get_text_from_subnode("rawrequest") self.response = self.get_text_from_subnode("rawresponse") self.kvulns = [] @@ -167,6 +155,12 @@ def __init__(self, item_node, encoding="ascii"): repr(self.paramval) if self.paramval else "" self.data += "\nExtra: " + "\n".join(self.extra) if self.extra else "" + @staticmethod + def re_map_severity(severity): + if severity == "Important": + return "high" + return severity + def get_text_from_subnode(self, subnode_xpath_expr): """ Finds a subnode in the host node and the retrieves a value from it. @@ -185,8 +179,8 @@ class NetsparkerPlugin(PluginXMLFormat): Example plugin to parse netsparker output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "netsparker" self.id = "Netsparker" self.name = "Netsparker XML Output Plugin" @@ -197,17 +191,21 @@ def __init__(self): def parseOutputString(self, output): parser = NetsparkerXmlParser(output) - first = True + host_names_resolve = {} for i in parser.items: - if first: + + if (i.hostname not in host_names_resolve): ip = resolve_hostname(i.hostname) - h_id = self.createAndAddHost(ip, hostnames=[ip]) - - s_id = self.createAndAddServiceToHost(h_id, str(i.port), - protocol = str(i.protocol), - ports=[str(i.port)], - status="open") - first = False + host_names_resolve[i.hostname] = ip + else: + ip = host_names_resolve[i.hostname] + h_id = self.createAndAddHost(ip, hostnames=[i.hostname]) + + s_id = self.createAndAddServiceToHost(h_id, str(i.port), + protocol = str(i.protocol), + ports=[str(i.port)], + status="open") + if i.resolution is not None: resolution = BeautifulSoup(i.resolution, "lxml").text else: @@ -221,7 +219,7 @@ def parseOutputString(self, output): name = i.name else: name = i.name_title - v_id = self.createAndAddVulnWebToService(h_id, s_id, name, ref=i.ref, website=i.hostname, + self.createAndAddVulnWebToService(h_id, s_id, name, ref=i.ref, website=i.hostname, severity=i.severity, desc=desc, path=i.url, method=i.method, request=i.request, response=i.response, resolution=resolution, pname=i.param, data=i.data) @@ -229,7 +227,7 @@ def parseOutputString(self, output): del parser -def createPlugin(): - return NetsparkerPlugin() +def createPlugin(ignore_info=False): + return NetsparkerPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/netsparkercloud/__init__.py b/faraday_plugins/plugins/repo/netsparkercloud/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/netsparkercloud/__init__.py +++ b/faraday_plugins/plugins/repo/netsparkercloud/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/netsparkercloud/plugin.py b/faraday_plugins/plugins/repo/netsparkercloud/plugin.py index ce85c6b4..aadfaf30 100644 --- a/faraday_plugins/plugins/repo/netsparkercloud/plugin.py +++ b/faraday_plugins/plugins/repo/netsparkercloud/plugin.py @@ -5,21 +5,12 @@ """ import re +import xml.etree.ElementTree as ET from urllib.parse import urlparse + from faraday_plugins.plugins.plugin import PluginXMLFormat from faraday_plugins.plugins.plugins_utils import resolve_hostname -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - - __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" __credits__ = ["Francisco Amato"] @@ -30,7 +21,6 @@ __status__ = "Development" - def get_urls(string): if isinstance(string, bytes): string_decode = string.decode("utf-8") @@ -55,7 +45,7 @@ def __init__(self, xml_output): self.filepath = xml_output tree = self.parse_xml(xml_output) if tree: - self.items = [data for data in self.get_items(tree)] + self.items = self.get_items(tree) else: self.items = [] @@ -90,6 +80,7 @@ class Item: @param item_node A item_node taken from an netsparkercloud xml tree """ + def re_map_severity(self, severity): if severity == "Important": return "high" @@ -120,7 +111,7 @@ def __init__(self, item_node, encoding="ascii"): self.response = self.get_text_from_subnode("content") self.extra = [] for v in item_node.findall("extra-information/info"): - self.extra.append(v.get('name') + ":" + v.get('value') ) + self.extra.append(v.get('name') + ":" + v.get('value')) self.node = item_node.find("classification") self.owasp = self.get_text_from_subnode("owasp") @@ -180,8 +171,8 @@ class NetsparkerCloudPlugin(PluginXMLFormat): Example plugin to parse netsparkercloud output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "netsparker-cloud" self.id = "NetsparkerCloud" self.name = "NetsparkerCloud XML Output Plugin" @@ -206,9 +197,5 @@ def parseOutputString(self, output): del parser - def setHost(self): - pass - - -def createPlugin(): - return NetsparkerCloudPlugin() +def createPlugin(ignore_info=False): + return NetsparkerCloudPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/nexpose_full/__init__.py b/faraday_plugins/plugins/repo/nexpose_full/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/nexpose_full/__init__.py +++ b/faraday_plugins/plugins/repo/nexpose_full/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/nexpose_full/plugin.py b/faraday_plugins/plugins/repo/nexpose_full/plugin.py index c565809e..22359c98 100644 --- a/faraday_plugins/plugins/repo/nexpose_full/plugin.py +++ b/faraday_plugins/plugins/repo/nexpose_full/plugin.py @@ -4,21 +4,10 @@ See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginXMLFormat import re +import xml.etree.ElementTree as ET -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Micaela Ranea Sanchez" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -51,7 +40,8 @@ def __init__(self, xml_output): else: self.items = [] - def parse_xml(self, xml_output): + @staticmethod + def parse_xml(xml_output): """ Open and parse an xml file. @@ -122,7 +112,6 @@ def parse_tests_type(self, node, vulnsDefinitions): for tests in node.findall('tests'): for test in tests.iter('test'): - vuln = dict() if test.get('id').lower() in vulnsDefinitions: vuln = vulnsDefinitions[test.get('id').lower()].copy() key = test.get('key', '') @@ -249,8 +238,8 @@ class NexposeFullPlugin(PluginXMLFormat): Example plugin to parse nexpose output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "NexposeReport" self.id = "NexposeFull" self.name = "Nexpose XML 2.0 Report Plugin" @@ -275,7 +264,7 @@ def parseOutputString(self, output): h_id = self.createAndAddHost(item['name'], item['os'], hostnames=item['hostnames'], mac=mac) for v in item['vulns']: v['data'] = {"vulnerable_since": v['vulnerable_since'], "scan_id": v['scan_id'], "PCI": v['pci']} - v_id = self.createAndAddVulnToHost( + self.createAndAddVulnToHost( h_id, v['name'], v['desc'], @@ -285,7 +274,6 @@ def parseOutputString(self, output): ) for s in item['services']: - web = False version = s.get("version", "") s_id = self.createAndAddServiceToHost( h_id, @@ -298,7 +286,7 @@ def parseOutputString(self, output): for v in s['vulns']: if v['is_web']: - v_id = self.createAndAddVulnWebToService( + self.createAndAddVulnWebToService( h_id, s_id, v['name'], @@ -308,7 +296,7 @@ def parseOutputString(self, output): v['resolution'], path=v.get('path', '')) else: - v_id = self.createAndAddVulnToService( + self.createAndAddVulnToService( h_id, s_id, v['name'], @@ -321,11 +309,5 @@ def parseOutputString(self, output): del parser - def setHost(self): - pass - - -def createPlugin(): - return NexposeFullPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return NexposeFullPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/nextnet/__init__.py b/faraday_plugins/plugins/repo/nextnet/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/faraday_plugins/plugins/repo/nextnet/plugin.py b/faraday_plugins/plugins/repo/nextnet/plugin.py new file mode 100644 index 00000000..6eb07194 --- /dev/null +++ b/faraday_plugins/plugins/repo/nextnet/plugin.py @@ -0,0 +1,69 @@ +""" +Faraday Penetration Test IDE +Copyright (C) 2020 Infobyte LLC (http://www.infobytesec.com/) +See the file 'doc/LICENSE' for the license information + +""" +import re +import os +import json +from faraday_plugins.plugins.plugin import PluginBase +from faraday_plugins.plugins.plugins_utils import resolve_hostname + + +__author__ = "Blas Moyano" +__copyright__ = "Copyright (c) 2020, Infobyte LLC" +__credits__ = ["Blas Moyano"] +__license__ = "" +__version__ = "1.0.0" +__maintainer__ = "Blas Moyano" +__email__ = "bmoyano@infobytesec.com" +__status__ = "Development" + + +class CmdNextNetin(PluginBase): + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.id = "nextnet" + self.name = "nextnet" + self.plugin_version = "0.0.1" + self.version = "5.0.20" + self.framework_version = "1.0.0" + self.options = None + self._current_output = None + self._command_regex = re.compile(r'^[.]*?[/]*?nextnet\s+.*?') + self._host_ip = None + self._info = 0 + + def parseOutputString(self, output): + output_lines = output.split('\n') + output_lines = output_lines[:-1] + + for line in output_lines: + json_line = json.loads(line) + info_data = json_line.get("info", None) + desc = "" + mac = None + if info_data is not None: + desc = f'Domain Tag: {info_data.get("domain", "Not tag info")}' + mac = info_data.get("hwaddr") + + h_id = self.createAndAddHost( + json_line.get("host", "0.0.0.0"), + os=json_line.get("name", "unknown"), + hostnames=json_line.get("nets"), + mac=mac + ) + self.createAndAddServiceToHost( + h_id, + name=json_line.get("probe", "unknown"), + protocol=json_line.get("proto", "tcp"), + ports=json_line.get("port", None), + description=desc + ) + return True + + +def createPlugin(ignore_info=False): + return CmdNextNetin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/nikto/__init__.py b/faraday_plugins/plugins/repo/nikto/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/nikto/__init__.py +++ b/faraday_plugins/plugins/repo/nikto/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/nikto/plugin.py b/faraday_plugins/plugins/repo/nikto/plugin.py index ee556abf..652db7a7 100644 --- a/faraday_plugins/plugins/repo/nikto/plugin.py +++ b/faraday_plugins/plugins/repo/nikto/plugin.py @@ -4,21 +4,11 @@ See the file 'doc/LICENSE' for the license information """ import re +import xml.etree.ElementTree as ET from html.parser import HTMLParser -from faraday_plugins.plugins.plugin import PluginXMLFormat -from faraday_plugins.plugins import plugins_utils - - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +from faraday_plugins.plugins import plugins_utils +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -46,7 +36,7 @@ def __init__(self, xml_output): tree = self.parse_xml(xml_output) if tree: - self.hosts = [host for host in self.get_hosts(tree)] + self.hosts = self.get_hosts(tree) else: self.hosts = [] @@ -85,28 +75,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", subnode_xpath_expr) - if match_obj is not None: - - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -222,7 +192,7 @@ def __init__(self, host_node): self.starttime = self.node.get('starttime') self.sitename = self.node.get('sitename') self.siteip = self.node.get('hostheader') - self.items = [item for item in self.get_items()] + self.items = self.get_items() def get_items(self): """ @@ -247,8 +217,8 @@ class NiktoPlugin(PluginXMLFormat): Example plugin to parse nikto output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "niktoscan" self.id = "Nikto" self.name = "Nikto XML Output Plugin" @@ -258,7 +228,7 @@ def __init__(self): self.parent = None self._use_temp_file = True self._temp_file_extension = "xml" - self.xml_arg_re = re.compile(r"^.*(-output\s*[^\s]+).*$") + self.xml_alrg_re = re.compile(r"^.*(-output\s*[^\s]+).*$") self._command_regex = re.compile( r'^(sudo nikto|nikto|sudo nikto\.pl|nikto\.pl|perl nikto\.pl|\.\/nikto\.pl|\.\/nikto)\s+.*?') self._completition = { @@ -301,8 +271,6 @@ def __init__(self): "-vhost+": "Virtual host (for Host header)", } - - def parseOutputString(self, output): """ This method will discard the output the shell sends, it will read it from @@ -330,8 +298,7 @@ def parseOutputString(self, output): ) for item in host.items: - - v_id = self.createAndAddVulnWebToService( + self.createAndAddVulnWebToService( h_id, s_id, name=item.desc, @@ -343,8 +310,6 @@ def parseOutputString(self, output): del parser - - def processCommandString(self, username, current_path, command_string): """ Adds the -oX parameter to get xml output to the command string that the @@ -360,11 +325,6 @@ def processCommandString(self, username, current_path, command_string): data = re.sub(" \-Format XML", "", command_string) return re.sub(arg_match.group(1), r"-output %s -Format XML" % self._output_file_path, data) - def setHost(self): - pass - - -def createPlugin(): - return NiktoPlugin() -# I'm Py3 +def createPlugin(ignore_info=False): + return NiktoPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/nipper/__init__.py b/faraday_plugins/plugins/repo/nipper/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/faraday_plugins/plugins/repo/nipper/plugin.py b/faraday_plugins/plugins/repo/nipper/plugin.py new file mode 100644 index 00000000..a63fa8ad --- /dev/null +++ b/faraday_plugins/plugins/repo/nipper/plugin.py @@ -0,0 +1,159 @@ +import xml.etree.ElementTree as ET +from colorama import Fore, Style + +from faraday_plugins.plugins.plugins_utils import resolve_hostname +from faraday_plugins.plugins.plugin import PluginXMLFormat + +__author__ = "@rfocke and @pasta <3" +__copyright__ = "Copyright (c) 2021, Faradaysec LLC" +__credits__ = ["Roberto Focke", "Javier Aguinaga"] +__license__ = "GPL" +__version__ = "0.8" +__mantainer__ = "@rfocke" +__status__ = "Development" + +class VulnSoftNipper: + def __init__(self, **kwargs): + self.name = '' + self.data = '' + self.device = '' + self.refs = [] + +class VulnerabilityNipper: + def __init__(self, **kwargs): + self.name = '' + self.rating = '' + self.recommendation = '' + self.affected_devices = [] + self.section = '' + self.name2 = '' + self.data = '' + self.recommendation2 = '' + +class NipperParser: + def __init__(self, output, debug=False): + self.vulns_first = [] + self.vulns_audit = [] + self.vulns_thirds = [] + self.debug = debug + + self.tree = ET.fromstring(output) + self.report_tree = self.tree.find("report/part/[@index='2']/section/[@title='Recommendations']/table/[@title='Security Audit recommendations list']/tablebody") + self.process_xml() + + def process_xml(self): + if not self.report_tree: + return + + for tablerow in self.report_tree: + for i, tablecell in enumerate(tablerow.findall('tablecell')): + if len(tablecell.findall('item')) == 1: + if i == 0: #Item + vuln = VulnerabilityNipper() + vuln.name = tablecell.find('item').text + elif i == 1: #Rating + vuln.rating = tablecell.find('item').text + elif i == 2: #Recommendations + vuln.recommendation = tablecell.find('item').text + elif i == 3: #Affected devices (with 1 element only) + vuln.affected_devices = [] + vuln.affected_devices.append(tablecell.find('item').text) + elif i == 4: #Section + subdetail = tablecell.find('item').text + vuln.section = subdetail + + path = "./report/part/[@index='2']/section/[@index='" + subdetail + "']" + for detail in self.tree.findall(path): + # nombre de la vuln + vuln.name2 = detail.attrib.get('title') + + if vuln.name2 != vuln.name: + pass + + path = "./report/part/[@index='2']/section/[@index='" + subdetail + "']/section/[@index='" + subdetail + ".2']" + for detail in self.tree.findall(path): + # data de la vuln + vuln.data = detail.find('text').text + + path = "./report/part/[@index='2']/section/[@index='" + subdetail + "']/section/[@index='" + subdetail + ".5']" + for detail in self.tree.findall(path): + # recomendacion de la vuln + vuln.recommendation2 = detail.find('text').text + + self.vulns_first.append(vuln) # <- GUARDADO + elif len(tablecell.findall('item')) > 1 and i == 3: + # affected devices + vuln.affected_devices = [] + for item in tablecell.findall('item'): + vuln.affected_devices.append(item.text) + + # parseo vuln de software + report_tree = self.tree.find("./report/part/[@title='Vulnerability Audit']") + for itemv in report_tree: + vuln_soft = VulnSoftNipper() + # nombre de la vuln + + vuln_soft.name = itemv.attrib.get('title') + for itemvv in itemv: + if itemvv.attrib.get('title') == 'Summary': + for i in itemvv: + # data de la vuln + vuln_soft.data = i.text + if itemvv.attrib.get('title') == 'Affected Device': + for i in itemvv: + # data del device + aux = i.text.split('The')[1] + vuln_soft.device = aux.split(' may be affected by this security vulnerability')[0] + if itemvv.attrib.get('title') == 'References': + # referencias de la vuln + vuln_soft.refs = [] + for texto in itemvv.findall('list/listitem/weblink'): + vuln_soft.refs.append(texto.text) + + self.vulns_audit.append(vuln_soft) + +class NipperPlugin(PluginXMLFormat): + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.extension = ".xml" + self.identifier_tag = "document" + self.identifier_tag_attributes = {'nipperstudio'} + self.id = "Nipper" + self.name = "Nipper XML Output Plugin" + self.plugin_version = "0.9" + self.version = "0.9" + self.framewor_version = "1.0.1" + self.options = None + + def parseOutputString(self, output): + parser = NipperParser(output, debug=False) + for vuln in parser.vulns_first: + for device in vuln.affected_devices: + ip = resolve_hostname(device) + h_id = self.createAndAddHost(ip, hostnames = device) + self.createAndAddVulnToHost(h_id, + name = vuln.name, + desc = vuln.data, + severity = vuln.rating, + resolution = vuln.recommendation, + data = vuln.data, + ref = [], + policyviolations = [] + ) + for vuln in parser.vulns_audit: + if vuln.data: + ip = resolve_hostname(device) + h_id = self.createAndAddHost(ip, hostnames = vuln.device) + self.createAndAddVulnToHost(h_id, + name = vuln.name, + desc = vuln.data, + severity = '', + resolution = '', + data = vuln.data, + ref = vuln.refs + ) + + +def createPlugin(ignore_info=False): + return NipperPlugin(ignore_info=ignore_info) + diff --git a/faraday_plugins/plugins/repo/nmap/__init__.py b/faraday_plugins/plugins/repo/nmap/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/nmap/__init__.py +++ b/faraday_plugins/plugins/repo/nmap/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/nmap/plugin.py b/faraday_plugins/plugins/repo/nmap/plugin.py index 0c493b56..bc756c4e 100644 --- a/faraday_plugins/plugins/repo/nmap/plugin.py +++ b/faraday_plugins/plugins/repo/nmap/plugin.py @@ -1,30 +1,21 @@ +import os +import re +from io import BytesIO + """ Faraday Penetration Test IDE Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ - -import re -import os -from io import BytesIO - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION from lxml import etree from lxml.etree import XMLParser from faraday_plugins.plugins.plugin import PluginXMLFormat +from faraday_plugins.plugins.plugins_utils import get_severity_from_cvss -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] current_path = os.path.abspath(os.getcwd()) - class NmapXmlParser: """ The objective of this class is to parse an xml file generated by @@ -60,7 +51,7 @@ def parse_xml(self, xml_output): magical_parser = XMLParser(recover=True) return etree.parse(BytesIO(xml_output), magical_parser) except SyntaxError as err: - #logger.error("SyntaxError: %s." % (err)) + # logger.error("SyntaxError: %s." % (err)) return None def get_hosts(self, tree): @@ -77,30 +68,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", - subnode_xpath_expr) - if match_obj is not None: - - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -242,7 +211,6 @@ def get_os_guesses(self): ostype = service.get("ostype", "unknown") yield ("%s" % ostype, 0) - def top_os_guess(self): """ @return The most accurate os_guess_id or 'unknown'. @@ -295,6 +263,8 @@ class Port: @param port_node A port_node taken from an nmap xml tree """ + PORT_STATUS_FIX = {"filtered": "closed", "open|filtered": "closed"} + def __init__(self, port_node): self.node = port_node @@ -322,7 +292,8 @@ def get_state(self): @return (state, reason, reason_ttl) or ('unknown','unknown','unknown') """ - state = self.get_attrib_from_subnode('state', 'state') + state = self.PORT_STATUS_FIX.get(self.get_attrib_from_subnode('state', 'state'), + self.get_attrib_from_subnode('state', 'state')) reason = self.get_attrib_from_subnode('state', 'reason') reason_ttl = self.get_attrib_from_subnode('state', 'reason_ttl') @@ -345,12 +316,57 @@ def get_scripts(self): Expects to find a scripts in the node. """ for s in self.node.findall('script'): - yield Script(s) + if s.get("id") == 'vulners': + for service_table in s.findall('table'): + for vulnerability_table in service_table.findall('table'): + yield ScriptVulners(s, service_table, vulnerability_table) + else: + yield Script(s) def __str__(self): return "%s, %s, Service: %s" % (self.number, self.state, self.service) +class ScriptVulners: + """ + An abstract representation of a script table when script is 'vulners'. + https://nmap.org/nsedoc/scripts/vulners.html + + '' + + @param script_node The reference to the node of the 'vulners' script + @param service_table The outer table taken from an nmap 'vulners' script xml tree + @param vulnerability_table The inner table taken from an nmap 'vulners' script xml tree + """ + + def __init__(self, script_node, service_table, vulnerability_table): + self.node = script_node + self.table = {} + for e in vulnerability_table.findall('elem'): + self.table[e.get("key")] = str(e.text) + + self.name = self.table["id"] + + self.desc = script_node.get("id") + "-" + self.table["id"] + if self.table["is_exploit"] == 'true': + self.desc += " *EXPLOIT*" + + self.refs = ["https://vulners.com/" + self.table["type"] + "/" + self.table["id"]] + self.refs.append("CVSS: " + self.table["cvss"]) + self.response = "" + self.web = "" + self.severity = get_severity_from_cvss(self.table["cvss"]) + + def __str__(self): + return "%s, %s, %s" % (self.name, self.product, self.version) + + class Script: """ An abstract representation of a Script. @@ -377,7 +393,7 @@ def __init__(self, script_node): self.name = script_node.get("id") self.desc = script_node.get("output") - self.refs = self.parse_output(self.desc) + self.refs = self.parse_output(self.desc) self.response = "" for k in script_node.findall("elem"): self.response += "\n" + str(k.get('key')) + ": " + str(k.text) @@ -431,8 +447,8 @@ class NmapPlugin(PluginXMLFormat): Example plugin to parse nmap output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "nmaprun" self.id = "Nmap" self.name = "Nmap XML Output Plugin" @@ -502,23 +518,28 @@ def parseOutputString(self, output): description=srvname) for v in port.vulns: - severity = "info" + desc = v.desc refs = v.refs - if re.search(r"(? -1: + ip = ip[:len_start_port] + try: + ipaddress.ip_address(ip) + ips.append(ip) + except ValueError: + pass + for ip in ips: + if ip != '127.0.0.1': + ip = ip + ips.remove(ip) + break + + list_mac = parser.result_data[0]['mac'] + for mac in list_mac: + if mac != '00:00:00:00:00:00': + mac_address = mac + list_mac.remove(mac_address) + break + + description = f'Title: {parser.result_data[0]["result_title"]} ' \ + f'Ips: {ips} ' \ + f'Macs: {list_mac}' + host_id = self.createAndAddHost( + name=ip, + hostnames=[parser.result_data[0]['target']], + description=description, + mac=mac_address + ) + + rules_fail = parser.result_data[0]['rule_result'] + if rules_fail: + info_rules = parser.rule_date + severity = 0 + + for rule in rules_fail: + vuln_run_date = datetime.strptime( + rule['time'].replace('T', ' '), + '%Y-%m-%d %H:%M:%S') + + if rule['severity'] == 'low': + severity = 1 + elif rule['severity'] == 'medium': + severity = 2 + elif rule['severity'] == 'high': + severity = 3 + + desc = f'name: {rule["ref_name"]} - link: {rule["ref_href"]}' + + for info in info_rules: + if rule['id'] == info['rule_id']: + vuln_name = info['rule_title'] + vuln_data = info['rule_check'] + vuln_ref = info['rule_ident'] + + self.createAndAddVulnToHost( + host_id, + vuln_name, + desc=desc, + ref=[vuln_ref], + severity=severity, + data=vuln_data, + external_id=rule['id'], + run_date=vuln_run_date) + + +def createPlugin(ignore_info=False): + return OpenScapPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/openvas/__init__.py b/faraday_plugins/plugins/repo/openvas/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/openvas/__init__.py +++ b/faraday_plugins/plugins/repo/openvas/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/openvas/plugin.py b/faraday_plugins/plugins/repo/openvas/plugin.py index ebbd768c..cfcd964c 100644 --- a/faraday_plugins/plugins/repo/openvas/plugin.py +++ b/faraday_plugins/plugins/repo/openvas/plugin.py @@ -1,26 +1,17 @@ +import re +from collections import defaultdict +from copy import copy """ Faraday Penetration Test IDE Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -import re -from collections import defaultdict -from copy import copy - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION from faraday_plugins.plugins.plugin import PluginXMLFormat from faraday_plugins.plugins.plugins_utils import filter_services -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] - +import xml.etree.ElementTree as ET __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -136,28 +127,8 @@ def get_attrib_from_subnode(xml_node, subnode_xpath_expr, attrib_name): @return An attribute value """ - global ETREE_VERSION - node = None - - if ETREE_VERSION[0] <= 1 and ETREE_VERSION[1] < 3: - - match_obj = re.search( - "([^\@]+?)\[\@([^=]*?)=\'([^\']*?)\'", - subnode_xpath_expr) - - if match_obj is not None: - node_to_find = match_obj.group(1) - xpath_attrib = match_obj.group(2) - xpath_value = match_obj.group(3) - for node_found in xml_node.findall(node_to_find): - if node_found.attrib[xpath_attrib] == xpath_value: - node = node_found - break - else: - node = xml_node.find(subnode_xpath_expr) - else: - node = xml_node.find(subnode_xpath_expr) + node = xml_node.find(subnode_xpath_expr) if node is not None: return node.get(attrib_name) @@ -174,13 +145,17 @@ class Item: def __init__(self, item_node, hosts): self.node = item_node self.host = self.get_text_from_subnode('host') + self.threat = self.get_text_from_subnode('threat') self.subnet = self.get_text_from_subnode('subnet') if self.subnet == '': self.subnet = self.host self.port = None self.severity = self.severity_mapper() + self.severity_nr = self.get_text_from_subnode("severity") self.service = "Unknown" self.protocol = "" + self.cpe = self.node.findall("detection/result/details/detail") + self.cpe = self.cpe[0].findtext("value") if self.cpe else None port_string = self.get_text_from_subnode('port') info = port_string.split("/") self.protocol = "".join(filter(lambda x: x.isalpha() or x in ("-", "_"), info[1])) @@ -196,6 +171,7 @@ def __init__(self, item_node, hosts): self.nvt = self.node.findall('nvt')[0] self.node = self.nvt self.id = self.node.get('oid') + self.cvss_base = self.get_text_from_subnode("cvss_base") self.name = self.get_text_from_subnode('name') self.cve = self.get_text_from_subnode('cve') if self.get_text_from_subnode('cve') != "NOCVE" else "" self.bid = self.get_text_from_subnode('bid') if self.get_text_from_subnode('bid') != "NOBID" else "" @@ -320,8 +296,8 @@ class OpenvasPlugin(PluginXMLFormat): Example plugin to parse openvas output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["report", "get_results_response"] self.id = "Openvas" self.name = "Openvas XML Output Plugin" @@ -336,7 +312,7 @@ def report_belongs_to(self, **kwargs): with open(report_path) as f: output = f.read() return re.search("OpenVAS", output) is not None \ - or re.search('', output) is not None\ + or re.search('', output) is not None \ or re.search('', output) is not None return False @@ -373,6 +349,14 @@ def parseOutputString(self, output): ref.append(item.xref) if item.tags and item.cvss_vector: ref.append(item.cvss_vector) + if item.cvss_base: + ref.append(f"CVSS_BASE: {item.cvss_base}") + if item.cpe: + ref.append(f"{item.cpe}") + if item.threat: + ref.append(f"THREAT: {item.threat}") + if item.severity_nr: + ref.append(f"SEVERITY NUMBER: {item.severity_nr}") if item.subnet in ids: h_id = ids[item.host] @@ -437,17 +421,13 @@ def parseOutputString(self, output): data=item.data) del parser - def _isIPV4(self, ip): + @staticmethod + def _isIPV4(ip): if len(ip.split(".")) == 4: return True else: return False - def setHost(self): - pass - - -def createPlugin(): - return OpenvasPlugin() -# I'm Py3 +def createPlugin(ignore_info=False): + return OpenvasPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/pasteanalyzer/__init__.py b/faraday_plugins/plugins/repo/pasteanalyzer/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/pasteanalyzer/__init__.py +++ b/faraday_plugins/plugins/repo/pasteanalyzer/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/pasteanalyzer/plugin.py b/faraday_plugins/plugins/repo/pasteanalyzer/plugin.py index 1e7fd647..47aeed69 100644 --- a/faraday_plugins/plugins/repo/pasteanalyzer/plugin.py +++ b/faraday_plugins/plugins/repo/pasteanalyzer/plugin.py @@ -21,8 +21,8 @@ class pasteAnalyzerPlugin(PluginBase): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "pasteAnalyzer" self.name = "pasteAnalyzer JSON Output Plugin" self.plugin_version = "1.0.0" @@ -86,7 +86,7 @@ def processCommandString(self, username, current_path, command_string): return command_string -def createPlugin(): - return pasteAnalyzerPlugin() +def createPlugin(ignore_info=False): + return pasteAnalyzerPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/peepingtom/__init__.py b/faraday_plugins/plugins/repo/peepingtom/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/peepingtom/__init__.py +++ b/faraday_plugins/plugins/repo/peepingtom/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/peepingtom/plugin.py b/faraday_plugins/plugins/repo/peepingtom/plugin.py index cc50f3fd..49bc59b8 100644 --- a/faraday_plugins/plugins/repo/peepingtom/plugin.py +++ b/faraday_plugins/plugins/repo/peepingtom/plugin.py @@ -25,8 +25,8 @@ class PeepingTomPlugin(PluginBase): Handle PeepingTom (https://bitbucket.org/LaNMaSteR53/peepingtom) output """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "peepingtom" self.name = "PeepingTom" self.plugin_version = "0.0.1" @@ -71,7 +71,7 @@ def processCommandString(self, username, current_path, command_string): self._path = current_path -def createPlugin(): - return PeepingTomPlugin() +def createPlugin(ignore_info=False): + return PeepingTomPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/ping/__init__.py b/faraday_plugins/plugins/repo/ping/__init__.py index ea531e17..625a6e25 100755 --- a/faraday_plugins/plugins/repo/ping/__init__.py +++ b/faraday_plugins/plugins/repo/ping/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/ping/plugin.py b/faraday_plugins/plugins/repo/ping/plugin.py index 89bb6216..f4c5e7c2 100644 --- a/faraday_plugins/plugins/repo/ping/plugin.py +++ b/faraday_plugins/plugins/repo/ping/plugin.py @@ -4,9 +4,10 @@ See the file 'doc/LICENSE' for the license information """ -from faraday_plugins.plugins.plugin import PluginBase import re +from faraday_plugins.plugins.plugin import PluginBase + __author__ = "Facundo de Guzmán, Esteban Guillardoy" __copyright__ = "Copyright (c) 2013, Infobyte LLC" __credits__ = ["Facundo de Guzmán", "Esteban Guillardoy"] @@ -23,8 +24,8 @@ class CmdPingPlugin(PluginBase): Basically detects if user was able to connect to a device """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "ping" self.name = "Ping" self.plugin_version = "0.0.1" @@ -35,10 +36,9 @@ def parseOutputString(self, output): reg = re.search(r"PING ([\w\.-:]+)( |)\(([\w\.:]+)\)", output) if re.search("0 received|unknown host", output) is None and reg is not None: - ip_address = reg.group(3) hostname = reg.group(1) - h_id = self.createAndAddHost(ip_address, hostnames=[hostname]) + self.createAndAddHost(ip_address, hostnames=[hostname]) return True def _isIPV4(self, ip): @@ -48,9 +48,5 @@ def _isIPV4(self, ip): return False - -def createPlugin(): - return CmdPingPlugin() - - -# I'm Py3 +def createPlugin(ignore_info=False): + return CmdPingPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/propecia/__init__.py b/faraday_plugins/plugins/repo/propecia/__init__.py index ea531e17..625a6e25 100755 --- a/faraday_plugins/plugins/repo/propecia/__init__.py +++ b/faraday_plugins/plugins/repo/propecia/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/propecia/plugin.py b/faraday_plugins/plugins/repo/propecia/plugin.py index b762c62f..453d0521 100644 --- a/faraday_plugins/plugins/repo/propecia/plugin.py +++ b/faraday_plugins/plugins/repo/propecia/plugin.py @@ -24,8 +24,8 @@ class CmdPropeciaPlugin(PluginBase): Basically inserts into the tree the ouput of this tool """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "propecia" self.name = "propecia port scanner" self.plugin_version = "0.0.1" @@ -61,7 +61,7 @@ def processCommandString(self, username, current_path, command_string): self._port = count_args[2] -def createPlugin(): - return CmdPropeciaPlugin() +def createPlugin(ignore_info=False): + return CmdPropeciaPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/qualysguard/__init__.py b/faraday_plugins/plugins/repo/qualysguard/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/qualysguard/__init__.py +++ b/faraday_plugins/plugins/repo/qualysguard/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/qualysguard/plugin.py b/faraday_plugins/plugins/repo/qualysguard/plugin.py index 46658a8c..c5398615 100644 --- a/faraday_plugins/plugins/repo/qualysguard/plugin.py +++ b/faraday_plugins/plugins/repo/qualysguard/plugin.py @@ -4,13 +4,9 @@ See the file 'doc/LICENSE' for the license information """ import re -from faraday_plugins.plugins.plugin import PluginXMLFormat - import xml.etree.ElementTree as ET -ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split('.')] +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = 'Francisco Amato' __copyright__ = 'Copyright (c) 2013, Infobyte LLC' @@ -335,8 +331,8 @@ class QualysguardPlugin(PluginXMLFormat): Example plugin to parse qualysguard output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["ASSET_DATA_REPORT", "SCAN"] self.id = 'Qualysguard' self.name = 'Qualysguard XML Output Plugin' @@ -350,7 +346,6 @@ def parseOutputString(self, output): parser = QualysguardXmlParser(output) - for item in parser.items: h_id = self.createAndAddHost( item.ip, @@ -415,11 +410,5 @@ def parseOutputString(self, output): del parser - def setHost(self): - pass - - -def createPlugin(): - return QualysguardPlugin() - - +def createPlugin(ignore_info=False): + return QualysguardPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/qualyswebapp/plugin.py b/faraday_plugins/plugins/repo/qualyswebapp/plugin.py index 76eae4de..5e769251 100644 --- a/faraday_plugins/plugins/repo/qualyswebapp/plugin.py +++ b/faraday_plugins/plugins/repo/qualyswebapp/plugin.py @@ -5,18 +5,13 @@ Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/) See the file 'doc/LICENSE' for the license information """ -import re +import xml.etree.ElementTree as ET from urllib.parse import urlparse from dateutil.parser import parse from faraday_plugins.plugins.plugin import PluginXMLFormat -try: - import xml.etree.cElementTree as ET -except ImportError: - import xml.etree.ElementTree as ET - __author__ = 'Blas Moyano' __copyright__ = 'Copyright 2020, Faraday Project' __credits__ = ['Blas Moyano'] @@ -81,7 +76,6 @@ class Glossary(): def __init__(self, glossary_tags): self.lista_qid = self.get_qid_list(glossary_tags) - def get_qid_list(self, qid_list_tags): self.dict_result_qid = {} for qid in qid_list_tags: @@ -101,8 +95,9 @@ def get_qid_list(self, vul_list_tags): class QualysWebappPlugin(PluginXMLFormat): - def __init__(self): - super().__init__() + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = ["WAS_SCAN_REPORT"] self.id = 'QualysWebapp' self.name = 'QualysWebapp XML Output Plugin' @@ -175,5 +170,5 @@ def parseOutputString(self, output): external_id=vuln_scan_id, data=vuln_data_add) -def createPlugin(): - return QualysWebappPlugin() +def createPlugin(ignore_info=False): + return QualysWebappPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/rdpscan/plugin.py b/faraday_plugins/plugins/repo/rdpscan/plugin.py index 473ead5a..0ddcf22f 100644 --- a/faraday_plugins/plugins/repo/rdpscan/plugin.py +++ b/faraday_plugins/plugins/repo/rdpscan/plugin.py @@ -7,8 +7,8 @@ class RDPScanPlugin(PluginBase): - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "rdpscan" self.id = "rdpscan" self.name = "rdpscan" @@ -43,5 +43,5 @@ def parseOutputString(self, output): ) -def createPlugin(): - return RDPScanPlugin() +def createPlugin(ignore_info=False): + return RDPScanPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/reconng/__init__.py b/faraday_plugins/plugins/repo/reconng/__init__.py index 308ccd2c..8b137891 100644 --- a/faraday_plugins/plugins/repo/reconng/__init__.py +++ b/faraday_plugins/plugins/repo/reconng/__init__.py @@ -1 +1 @@ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/reconng/plugin.py b/faraday_plugins/plugins/repo/reconng/plugin.py index 04042fea..5d152227 100644 --- a/faraday_plugins/plugins/repo/reconng/plugin.py +++ b/faraday_plugins/plugins/repo/reconng/plugin.py @@ -128,8 +128,8 @@ class ReconngPlugin(PluginXMLFormat): Example plugin to parse qualysguard output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "reconng" self.id = 'Reconng' self.name = 'Reconng XML Output Plugin' @@ -171,7 +171,7 @@ def parseOutputString(self, output): -def createPlugin(): - return ReconngPlugin() +def createPlugin(ignore_info=False): + return ReconngPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/retina/__init__.py b/faraday_plugins/plugins/repo/retina/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/retina/__init__.py +++ b/faraday_plugins/plugins/repo/retina/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/retina/plugin.py b/faraday_plugins/plugins/repo/retina/plugin.py index ff196cc5..f71c5f47 100644 --- a/faraday_plugins/plugins/repo/retina/plugin.py +++ b/faraday_plugins/plugins/repo/retina/plugin.py @@ -5,19 +5,9 @@ """ import re -from faraday_plugins.plugins.plugin import PluginXMLFormat - - -try: - import xml.etree.cElementTree as ET - import xml.etree.ElementTree as ET_ORIG - ETREE_VERSION = ET_ORIG.VERSION -except ImportError: - import xml.etree.ElementTree as ET - ETREE_VERSION = ET.VERSION - -ETREE_VERSION = [int(i) for i in ETREE_VERSION.split(".")] +import xml.etree.ElementTree as ET +from faraday_plugins.plugins.plugin import PluginXMLFormat __author__ = "Francisco Amato" __copyright__ = "Copyright (c) 2013, Infobyte LLC" @@ -43,7 +33,7 @@ class RetinaXmlParser: def __init__(self, xml_output): tree = self.parse_xml(xml_output) if tree: - self.items = [data for data in self.get_items(tree)] + self.items = self.get_items(tree) else: self.items = [] @@ -117,7 +107,7 @@ def get_text_from_subnode(self, subnode_xpath_expr): return None -class Results(): +class Results: def __init__(self, issue_node): self.node = issue_node @@ -172,8 +162,8 @@ class RetinaPlugin(PluginXMLFormat): Example plugin to parse retina output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.identifier_tag = "scanJob" self.id = "Retina" self.name = "Retina XML Output Plugin" @@ -182,13 +172,12 @@ def __init__(self): self.framework_version = "1.0.0" self.options = None - def parseOutputString(self, output): parser = RetinaXmlParser(output) for item in parser.items: hostname = item.hostname if item.hostname else None - h_id = self.createAndAddHost(item.ip, item.os,hostnames=[hostname]) + h_id = self.createAndAddHost(item.ip, item.os, hostnames=[hostname]) if not item.netbiosname == 'N/A': self.createAndAddNoteToHost( @@ -223,11 +212,5 @@ def parseOutputString(self, output): del parser - def setHost(self): - pass - - -def createPlugin(): - return RetinaPlugin() - -# I'm Py3 +def createPlugin(ignore_info=False): + return RetinaPlugin(ignore_info=ignore_info) diff --git a/faraday_plugins/plugins/repo/reverseraider/__init__.py b/faraday_plugins/plugins/repo/reverseraider/__init__.py index ea531e17..625a6e25 100644 --- a/faraday_plugins/plugins/repo/reverseraider/__init__.py +++ b/faraday_plugins/plugins/repo/reverseraider/__init__.py @@ -4,4 +4,4 @@ See the file 'doc/LICENSE' for the license information """ -# I'm Py3 \ No newline at end of file + diff --git a/faraday_plugins/plugins/repo/reverseraider/plugin.py b/faraday_plugins/plugins/repo/reverseraider/plugin.py index decad09c..d7b18e9c 100644 --- a/faraday_plugins/plugins/repo/reverseraider/plugin.py +++ b/faraday_plugins/plugins/repo/reverseraider/plugin.py @@ -49,8 +49,8 @@ class ReverseraiderPlugin(PluginBase): Example plugin to parse reverseraider output. """ - def __init__(self): - super().__init__() + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) self.id = "Reverseraider" self.name = "Reverseraider XML Output Plugin" self.plugin_version = "0.0.1" @@ -78,7 +78,7 @@ def parseOutputString(self, output): -def createPlugin(): - return ReverseraiderPlugin() +def createPlugin(ignore_info=False): + return ReverseraiderPlugin(ignore_info=ignore_info) + -# I'm Py3 diff --git a/faraday_plugins/plugins/repo/shodan/__init__.py b/faraday_plugins/plugins/repo/shodan/__init__.py new file mode 100644 index 00000000..d8862701 --- /dev/null +++ b/faraday_plugins/plugins/repo/shodan/__init__.py @@ -0,0 +1,6 @@ +""" +Faraday Penetration Test IDE +Copyright (C) 2021 Infobyte LLC (http://www.infobytesec.com/) +See the file 'doc/LICENSE' for the license information + +""" diff --git a/faraday_plugins/plugins/repo/shodan/plugin.py b/faraday_plugins/plugins/repo/shodan/plugin.py new file mode 100644 index 00000000..d4842550 --- /dev/null +++ b/faraday_plugins/plugins/repo/shodan/plugin.py @@ -0,0 +1,107 @@ +""" +Faraday Penetration Test IDE +Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) +See the file 'doc/LICENSE' for the license information + +""" +import re +import json +import argparse +import shlex +import gzip +import os +import shutil + +from faraday_plugins.plugins.plugin import PluginMultiLineJsonFormat + +__author__ = "Valentin Vila" +__copyright__ = "Copyright (c) 2021, Faraday" +__credits__ = ["Valentin Vila"] +__license__ = "" +__version__ = "1.0.0" +__maintainer__ = "Valentin Vila" +__email__ = "vvila@faradaysec.com" +__status__ = "Development" + + +class ShodanPlugin(PluginMultiLineJsonFormat): + """ + This plugin handles the Shodan tool. + Detects the output of the tool + and adds the information to Faraday. + """ + + def __init__(self, *arg, **kwargs): + super().__init__(*arg, **kwargs) + self.id = "shodan" + self.name = "Shodan" + self.plugin_version = "0.0.1" + self.version = "1.0.0" + self._command_regex = re.compile(r'^shodan\s+(?P