├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── COPYRIGHT ├── LICENSE ├── README.md ├── semmle-security-cpp ├── .project ├── .qlpath ├── README.md ├── lib │ └── ghostscript │ │ ├── CollectionsTracking.qll │ │ ├── README.md │ │ ├── Stack.qll │ │ └── Type.qll ├── queries.xml └── queries │ └── ghostscript │ ├── README.md │ └── TypeConfusion.ql └── semmle-security-java ├── .project ├── .qlpath ├── README.md ├── lib ├── dataflow_extra │ ├── CollectionsEdges.qll │ ├── ExtraEdges.qll │ ├── ExtraSources.qll │ └── Sanitizers.qll └── struts │ ├── DataFlow.qll │ ├── OGNL.qll │ ├── README.md │ └── Sanitizers.qll ├── queries.xml └── queries └── struts ├── README.md ├── cve_2018_11776 ├── README.md ├── final.ql ├── initial.ql ├── ognl_Injection_final.ql └── ognl_Injection_initial.ql └── double_evaluation ├── README.md └── double_eval_final.ql /.gitignore: -------------------------------------------------------------------------------- 1 | # emacs 2 | *~ 3 | 4 | #ql cache 5 | .cache 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This code of conduct outlines expectations for participation in the Semmle open source community, including any open source repositories on GitHub.com, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all. 4 | 5 | People violating this code of conduct may be banned from the community. 6 | 7 | Our community strives to: 8 | * Be friendly and patient: Remember you might not be communicating in someone else’s primary spoken or programming language, and others may not have your level of understanding. 9 | * Be welcoming: Our community welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. 10 | * Be respectful: We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to: 11 | * Violent threats or language. 12 | * Discriminatory or derogatory jokes and language. 13 | * Posting sexually explicit or violent material. 14 | * Posting, or threatening to post, people’s personally identifying information (“doxing”). 15 | * Insults, especially those using discriminatory terms or slurs. 16 | * Behavior that could be perceived as sexual attention. 17 | * Advocating for or encouraging any of the above behaviors. 18 | * Understand disagreements: Disagreements, both social and technical, are useful learning opportunities. Seek to understand others’ viewpoints and resolve differences constructively. 19 | 20 | This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter. 21 | 22 | # Scope 23 | 24 | This code of conduct applies to all repositories and communities for Semmle open source projects, regardless of whether or not the repository explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing the Semmle open source community. Examples include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 25 | 26 | 27 | # Reporting Code of Conduct Issues 28 | We encourage members of the community to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by contacting code-of-conduct@semmle.com. 29 | In your report please include: 30 | * Your contact information. 31 | * Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. 32 | * Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment. 33 | * Any additional information that may be helpful. 34 | 35 | All reports will be reviewed by a multi-person team and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, the team may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team. 36 | 37 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the review team may take any action they deem appropriate, including a permanent ban from the community. 38 | 39 | *This text is licensed under the [CC-BY-4.0](https://creativecommons.org/licenses/by/4.0/) license. It is based on a template established by the [TODO Group](http://todogroup.org/) and variants thereof used by numerous other large communities (e.g., [Microsoft](https://microsoft.github.io/codeofconduct/), [Facebook](https://code.fb.com/codeofconduct/), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://github.com/twitter/code-of-conduct), [GitHub](https://blog.github.com/2015-07-20-adopting-the-open-code-of-conduct/)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/).* 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Semmle open-source projects 2 | 3 | We welcome contributions to our project. If you have an idea or a bugfix then please go ahead an open a Pull Request! 4 | 5 | Before we accept your pull request, we will require that you have agreed to our Contributor License Agreement. This is not something that you need to do before you submit your pull request, but until you've done so, we will be unable to accept your contribution. 6 | 7 | ## Using your personal data 8 | 9 | If you contribute to this project, we will record your name and email 10 | address (as provided by you with your contributions) as part of the code 11 | repositories, which might be made public. We might also use this information 12 | to contact you in relation to your contributions, as well as in the 13 | normal course of software development. We also store records of your 14 | CLA agreements. Under GDPR legislation, we do this 15 | on the basis of our legitimate interest in creating the QL product. 16 | 17 | Please do get in touch (privacy@semmle.com) if you have any questions about 18 | this or our data protection policies. 19 | 20 | ## Contributor License Agreement 21 | 22 | This Contributor License Agreement (“Agreement”) is entered into between Semmle Limited (“Semmle,” “we” or “us” etc.), and You (as defined and further identified below). 23 | 24 | Accordingly, You hereby agree to the following terms for Your present and future Contributions submitted to Semmle: 25 | 26 | 1. **Definitions**. 27 | 28 | * "You" (or "Your") shall mean the Contribution copyright owner (whether an individual or organization) or legal entity authorized by the copyright owner that is making this Agreement with Semmle. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 29 | 30 | * "Contribution(s)" shall mean the code, documentation or other original works of authorship, including any modifications or additions to an existing work, submitted by You to Semmle for inclusion in, or documentation of, any of the products or projects owned or managed by Semmle (the "Work(s)"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to Semmle or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, Semmle for the purpose of discussing and/or improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." 31 | 32 | 2. **Grant of Copyright License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. 33 | 34 | 3. **Grant of Patent License**. You hereby grant to Semmle and to recipients of software distributed by Semmle a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that Your Contribution, or the Work to which You have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. 35 | 36 | 4. **Ownership**. Except as set out above, You keep all right, title, and interest in Your Contribution. The rights that You grant to us under this Agreement are effective on the date You first submitted a Contribution to us, even if Your submission took place before the date You entered this Agreement. 37 | 38 | 5. **Representations**. You represent and warrant that: (i) the Contributions are an original work and that You can legally grant the rights set out in this Agreement; (ii) the Contributions and Semmle’s exercise of any license rights granted hereunder, does not and will not, infringe the rights of any third party; (iii) You are not aware of any pending or threatened claims, suits, actions, or charges pertaining to the Contributions, including without limitation any claims or allegations that any or all of the Contributions infringes, violates, or misappropriate the intellectual property rights of any third party (You further agree that You will notify Semmle immediately if You become aware of any such actual or potential claims, suits, actions, allegations or charges). 39 | 40 | 6. **Employer**. If Your employer(s) has rights to intellectual property that You create that includes Your Contributions, You represent and warrant that Your employer has waived such rights for Your Contributions to Semmle, or that You have received permission to make Contributions on behalf of that employer and that You are authorized to execute this Agreement on behalf of Your employer. 41 | 42 | 7. **Inclusion of Code**. We determine the code that is in our Works. You understand that the decision to include the Contribution in any project or source repository is entirely that of Semmle, and this agreement does not guarantee that the Contributions will be included in any product. 43 | 44 | 8. **Disclaimer**. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Except as set forth herein, and unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND. 45 | 46 | 9. **General**. The failure of either party to enforce its rights under this Agreement for any period shall not be construed as a waiver of such rights. No changes or modifications or waivers to this Agreement will be effective unless in writing and signed by both parties. In the event that any provision of this Agreement shall be determined to be illegal or unenforceable, that provision will be limited or eliminated to the minimum extent necessary so that this Agreement shall otherwise remain in full force and effect and enforceable. This Agreement shall be governed by and construed in accordance with the laws of the State of California in the United States without regard to the conflicts of laws provisions thereof. In any action or proceeding to enforce rights under this Agreement, the prevailing party will be entitled to recover costs and attorneys’ fees. 47 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyright (c) Semmle Inc and other contributors. All rights reserved. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use 4 | this file except in compliance with the License. You may obtain a copy of the 5 | License at http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 8 | KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED 9 | WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, 10 | MERCHANTABLITY OR NON-INFRINGEMENT. 11 | 12 | See the Apache Version 2.0 License for specific language governing permissions 13 | and limitations under the License. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Security queries 2 | 3 | This repository is a place to store and share customized security queries and libraries. This contains queries in [LGTM blog posts](https://lgtm.com/blog), but also project specific libraries. If you find it useful and would like to share some cool queries that you wrote for finding vulnerabilities in open source projects with us, or if you would like to help us to improve the project-specific QL libraries, please feel free to contribute and create a PR! 4 | 5 | These queries should be used with the free [QL for Eclipse plugin](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/home-page.html). To run these queries on an open source project that is available on [LGTM](https://lgtm.com), follow the first two steps in [Basic Usage](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/basic-usage.html) to obtain and import the project snapshot, then go to the specific `.ql` file in this repository that contains the query, [import the parent project into Eclipse](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/import-ql-project.html), open and select the file and press `Ctrl+R`. There are also some links to snapshots for specific versions of certain projects in the `README` files in this repository. 6 | 7 | ## Using the Path Explorer 8 | 9 | Many queries in this repository makes use of the Taint-Tracking library in QL, which allows you to visualize the code path that goes from a tainted source to a dangerous sink. To enable this, click `Window` on the Eclipse menu bar, then click `Show View > Other... > Semmle > Path Explorer` to display the Path Explorer window. This will show you the tainted path when you click on a query result. 10 | 11 | -------------------------------------------------------------------------------- /semmle-security-cpp/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | semmle-security-cpp 4 | 5 | 6 | 7 | 8 | 9 | 10 | com.semmle.plugin.qdt.core.qlnature 11 | 12 | 13 | -------------------------------------------------------------------------------- /semmle-security-cpp/.qlpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.semmle.code.cpp.library 5 | 6 | com.semmle.code.cpp.dbscheme 7 | 8 | cpp 9 | 10 | 11 | -------------------------------------------------------------------------------- /semmle-security-cpp/README.md: -------------------------------------------------------------------------------- 1 | # Semmle C++ security queries 2 | 3 | This directory contains QL libraries and queries that are used for finding vulnerabilities in open source C++ projects. The libraries are in the `lib` directory and queries in the `queries` directory. These directories are organized according to the open source project that they are meant to be used with. 4 | -------------------------------------------------------------------------------- /semmle-security-cpp/lib/ghostscript/CollectionsTracking.qll: -------------------------------------------------------------------------------- 1 | import cpp 2 | import semmle.code.cpp.dataflow.TaintTracking 3 | import lib.ghostscript.Type 4 | import lib.ghostscript.Stack 5 | /** Various dataflow utilities related to collections. */ 6 | 7 | /** A method that takes a collection as an input argument, and store the result in the output argument.*/ 8 | abstract class FetchMethod extends Function { 9 | 10 | abstract int getInput(); 11 | 12 | abstract int getOutput(); 13 | } 14 | 15 | /** FetchMethod for array.*/ 16 | class ArrayGetMethod extends FetchMethod { 17 | int input; 18 | int output; 19 | 20 | ArrayGetMethod() { 21 | this.hasName("array_get") and input = 1 and output = 3 22 | } 23 | 24 | override int getInput() { 25 | result = input 26 | } 27 | 28 | override int getOutput() { 29 | result = output 30 | } 31 | } 32 | 33 | /** FetchMethod for dictionary.*/ 34 | class DictionaryFetchMethod extends FetchMethod { 35 | int input; 36 | int output; 37 | 38 | DictionaryFetchMethod() { 39 | (this.hasName("dict_find_string") and input = 0 and output = 2) or 40 | (this.hasName("dict_find") and input = 0 and output = 2) 41 | } 42 | 43 | override int getInput() { 44 | result = input 45 | } 46 | 47 | override int getOutput() { 48 | result = output 49 | } 50 | } 51 | 52 | class InputSourceConfig extends DataFlow::Configuration { 53 | InputSourceConfig() { 54 | this = "inputSourceConfig" 55 | } 56 | 57 | override predicate isSource(DataFlow::Node source) { 58 | isOSPSource(source) 59 | } 60 | 61 | override predicate isSink(DataFlow::Node sink) { 62 | exists(FetchMethod f, FunctionCall fc | fc.getTarget() = f | 63 | fc.getArgument(f.getOutput()).(RefAccess).getVariable().getAnAccess() = sink.asExpr() 64 | ) 65 | } 66 | 67 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 68 | isFetchEdge(node1, node2) 69 | } 70 | } 71 | 72 | predicate isFetchEdge(DataFlow::Node node1, DataFlow::Node node2) { 73 | exists(FunctionCall fc, FetchMethod f | fc.getTarget() = f | 74 | node1.asExpr() = fc.getArgument(f.getInput()) and 75 | node2.asExpr() = fc.getArgument(f.getOutput()).(RefAccess).getVariable().getAnAccess() and 76 | node1.getEnclosingCallable() = node2.getEnclosingCallable() 77 | ) 78 | } -------------------------------------------------------------------------------- /semmle-security-cpp/lib/ghostscript/README.md: -------------------------------------------------------------------------------- 1 | #Ghostscript QL libraries 2 | 3 | This contains project specific libraries for Ghostscript You can import these libraries in your custom queries and use them on snapshots of [Ghostscript].(https://downloads.lgtm.com/snapshots/cpp/ghostscript/ghostscript/ghostscript-revision-2018-October-15--10-34-16.zip) 4 | -------------------------------------------------------------------------------- /semmle-security-cpp/lib/ghostscript/Stack.qll: -------------------------------------------------------------------------------- 1 | import cpp 2 | import semmle.code.cpp.dataflow.DataFlow 3 | 4 | /** An access to an operator stack variable, which are arguments to PostScript functions. */ 5 | predicate isOSPAccess(FieldAccess fa) { 6 | exists(FieldAccess opStack, FieldAccess stack | 7 | fa.getTarget().hasName("p") 8 | | fa.getQualifier() = stack and stack.getTarget().hasName("stack") and 9 | stack.getQualifier() = opStack and opStack.getTarget().hasName("op_stack") 10 | ) 11 | } 12 | 13 | /** A DataFlow::Node that is an access to an operator stack variable. */ 14 | predicate isOSPSource(DataFlow::Node source) { 15 | exists(FieldAccess fa | isOSPAccess(fa) and 16 | source.asExpr() = fa 17 | ) 18 | } -------------------------------------------------------------------------------- /semmle-security-cpp/lib/ghostscript/Type.qll: -------------------------------------------------------------------------------- 1 | import cpp 2 | import semmle.code.cpp.dataflow.TaintTracking 3 | import semmle.code.cpp.dataflow.DataFlow 4 | /** Contains QL classes specific to various structures in Ghostscript. */ 5 | 6 | /** Access to a variable of type `ref` or various other equivalent types.*/ 7 | class RefAccess extends Expr { 8 | /** The variable of this `ref`*/ 9 | Variable v; 10 | 11 | RefAccess() { 12 | (v.getAnAccess() = this or v.getAnAccess() = this.(AddressOfExpr).getAnOperand()) and 13 | exists(string name | v.getType().getName() = name | 14 | v.getType() instanceof PointerType or 15 | name = "os_ptr" or name = "const_os_ptr" or 16 | name = "const os_ptr" or name = "ref" or 17 | name = "const ref" 18 | ) 19 | } 20 | 21 | Variable getVariable() { 22 | result = v 23 | } 24 | 25 | /** Convenient predicate to check that two access are to the same variable.*/ 26 | predicate isEquivalent(RefAccess other) { 27 | this.getVariable() = other.getVariable() 28 | } 29 | } 30 | 31 | /** An access to a field that requires type checking.*/ 32 | class TypeFieldAccess extends FieldAccess { 33 | TypeFieldAccess() { 34 | exists(FieldAccess f | f.getTarget().hasName("value") and 35 | this.getQualifier() = f and this.getTarget().getDeclaringType().hasName("v") 36 | ) 37 | } 38 | 39 | RefAccess getRef() { 40 | result = this.getQualifier().(FieldAccess).getQualifier() 41 | } 42 | } 43 | 44 | /** An access to a field that represents the type of a `ref`.*/ 45 | class TypeAttrAccess extends FieldAccess { 46 | TypeAttrAccess() { 47 | exists(FieldAccess fa | fa.getTarget().hasName("tas") and 48 | fa = this.getQualifier() and this.getTarget().getName() = "type_attrs" 49 | ) 50 | } 51 | 52 | RefAccess getRef() { 53 | result = this.getQualifier().(FieldAccess).getQualifier() 54 | } 55 | } 56 | 57 | /** A comparison statement that is likely to be a type check. */ 58 | class TypeCheck extends TypeAttrAccess { 59 | TypeCheck() { 60 | exists(EqualityOperation eq | eq.getAnOperand().getAChild*() = this) 61 | } 62 | } 63 | 64 | /** A switch statement that uses the type of a `ref` as the condition. This is a common pattern for checking types. */ 65 | class SwitchCheck extends TypeAttrAccess { 66 | SwitchStmt stmt; 67 | SwitchCheck() { 68 | stmt.getControllingExpr().getAChild*() = this 69 | } 70 | 71 | SwitchStmt getSwitch() { 72 | result = stmt 73 | } 74 | } 75 | 76 | /** A DataFlow::Configuration to see whether a function checks the type of its argument.*/ 77 | class CheckTypeConfig extends DataFlow::Configuration { 78 | CheckTypeConfig() { 79 | this = "checkTypeConfig" 80 | } 81 | 82 | override predicate isSource(DataFlow::Node source) { 83 | exists(Parameter p | p = source.asParameter()) 84 | } 85 | 86 | override predicate isSink(DataFlow::Node sink) { 87 | exists(TypeAttrAccess tc | tc.getRef() = sink.asExpr() and 88 | (tc instanceof TypeCheck or tc instanceof SwitchCheck) 89 | ) 90 | } 91 | } 92 | 93 | /** A function that checks the type of a `ref` when it is passed as parameter `index`. */ 94 | class CheckFunction extends Function { 95 | int index; 96 | CheckFunction() { 97 | exists(CheckTypeConfig cfg, DataFlow::Node source | source.asParameter() = this.getParameter(index) and 98 | cfg.hasFlow(source, _) 99 | ) or 100 | //This allocates an array, so the parameter is overwritten and have array type afterwards. (i.e. Not user controlled anymore) 101 | (this.hasName("gs_alloc_ref_array") and index = 1) 102 | } 103 | 104 | int getIndex() { 105 | result = index 106 | } 107 | } 108 | 109 | /** A DataFlow::Node that can only be reached after a type check. */ 110 | predicate isTypeCheck(DataFlow::Node node) { 111 | exists(TypeAttrAccess tc, RefAccess e | dominates(tc, e) and 112 | e = node.asExpr() and tc.getRef().isEquivalent(e) and 113 | (tc instanceof TypeCheck or tc instanceof SwitchCheck) 114 | ) or 115 | exists(FunctionCall fc | 116 | dominates(fc, node.asExpr()) and 117 | fc.getArgument(fc.getTarget().(CheckFunction).getIndex()).(RefAccess).isEquivalent(node.asExpr()) 118 | ) 119 | } -------------------------------------------------------------------------------- /semmle-security-cpp/queries.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /semmle-security-cpp/queries/ghostscript/README.md: -------------------------------------------------------------------------------- 1 | # Ghostscript specific queries 2 | 3 | This directory contains security queries specific for [Ghostscript](https://www.ghostscript.com/). You can run the queries here on a [Ghostscript snapshot](https://downloads.lgtm.com/snapshots/cpp/ghostscript/ghostscript/ghostscript-revision-2018-October-15--10-34-16.zip). Follow the steps [here](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/basic-usage.html) to import the snapshot and run the queries. 4 | -------------------------------------------------------------------------------- /semmle-security-cpp/queries/ghostscript/TypeConfusion.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name TypeConfusion 3 | * @description Type confusion in GhostScript. 4 | * @kind path-problem 5 | * @id type-confusion-final 6 | * @problem.severity error 7 | */ 8 | 9 | import cpp 10 | import lib.ghostscript.CollectionsTracking 11 | import lib.ghostscript.Type 12 | import DataFlow::PathGraph 13 | 14 | class TypeConfusionConfig extends TaintTracking::Configuration2 { 15 | TypeConfusionConfig() { 16 | this = "typeConfusionConfig" 17 | } 18 | 19 | override predicate isSource(DataFlow::Node source) { 20 | exists(InputSourceConfig cfg, DataFlow::Node input | cfg.hasFlow(input, source)) 21 | } 22 | 23 | override predicate isSink(DataFlow::Node sink) { 24 | exists(TypeFieldAccess tc | tc.getRef() = sink.asExpr() 25 | ) 26 | } 27 | 28 | override predicate isSanitizer(DataFlow::Node node) { 29 | isTypeCheck(node) or 30 | //Manually remove the assignment of this argument inside array_get is safe. 31 | exists(Function f | f.getName() = "array_get" and 32 | f.getParameter(3) = node.asParameter() 33 | ) 34 | } 35 | } 36 | 37 | from InputSourceConfig inputCfg, TypeConfusionConfig cfg, DataFlow::Node sink, DataFlow::PathNode inputSource, 38 | DataFlow::PathNode inputSink 39 | where cfg.hasFlow(inputSink.getNode(), sink) and inputCfg.hasFlowPath(inputSource, inputSink) 40 | select inputSink, inputSource, inputSink, "$@ flows to here and is used later without type check.", inputSource, "Function argument" -------------------------------------------------------------------------------- /semmle-security-java/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | semmle-security-java 4 | 5 | 6 | 7 | 8 | 9 | 10 | com.semmle.plugin.qdt.core.qlnature 11 | 12 | 13 | -------------------------------------------------------------------------------- /semmle-security-java/.qlpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.semmle.code.java.library 5 | 6 | com.semmle.code.java.dbscheme 7 | 8 | java 9 | 10 | 11 | -------------------------------------------------------------------------------- /semmle-security-java/README.md: -------------------------------------------------------------------------------- 1 | # Semmle Java security queries 2 | 3 | This directory contains QL libraries and queries that are used for finding vulnerabilities in open source Java projects. The libraries are in the `lib` directory and queries in the `queries` directory. These directories are organized according to the open source project that they are meant to be used with. 4 | -------------------------------------------------------------------------------- /semmle-security-java/lib/dataflow_extra/CollectionsEdges.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DefUse 3 | import semmle.code.java.dataflow.DataFlow 4 | /** Contains DataFlow edges for handling access to Collections. These edges usually takes the approach of tainting the entire 5 | * collection when an tainted element is added to the Collection and are considered too aggressive for default QL library. 6 | */ 7 | 8 | /** Tracks from a tainted Collection to an element of it iterated in a loop.*/ 9 | predicate forLoopEdge(DataFlow::Node node1, DataFlow::Node node2) { 10 | exists(EnhancedForStmt for | for.getExpr() = node1.asExpr() and 11 | defUsePair(for.getVariable(), node2.asExpr().(VarAccess)) 12 | ) 13 | } 14 | 15 | /** Tracks from a tainted Map entry to the return of its `getKey` or `getValue` methods */ 16 | private predicate mapEntryEdge(DataFlow::Node node1, DataFlow::Node node2) { 17 | exists(MethodAccess ma, Method m | m.hasName("getKey") or m.hasName("getValue") | 18 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Map<>$Entry%") and 19 | ma.getMethod() = m and ma = node2.asExpr() and ma.getQualifier() = node1.asExpr() 20 | ) 21 | } 22 | 23 | /** Tracks from a tainted Map to the return of its `get` or `getOrDefault` method. */ 24 | private predicate mapGetEdge(DataFlow::Node node1, DataFlow::Node node2) { 25 | exists(MethodAccess ma, Method m | m.hasName("get") or m.hasName("getOrDefault") | 26 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Map%") and 27 | ma.getMethod() = m and 28 | ma = node2.asExpr() and ma.getQualifier() = node1.asExpr() 29 | ) 30 | } 31 | 32 | /** Tracks from a tainted List to the return of various methods that outputs its elements or sublist.*/ 33 | private predicate listEdge(DataFlow::Node node1, DataFlow::Node node2) { 34 | exists(MethodAccess ma, Method m | ma.getMethod() = m and 35 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.List%") and 36 | ( 37 | m.getName() = "get" or 38 | m.getName() = "elementAt" or 39 | m.getName() = "elements" or 40 | m.getName() = "firstElement" or 41 | m.getName() = "lastElement" or 42 | m.getName() = "listIterator" or 43 | m.getName() = "subList" 44 | ) and 45 | ma = node2.asExpr() and ma.getQualifier() = node1.asExpr() 46 | ) or 47 | exists(MethodAccess ma, Method m | ma.getMethod() = m and 48 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.List%") and 49 | m.hasName("copyInto") and node1.asExpr() = ma.getQualifier() and node2.asExpr() = ma.getAnArgument() 50 | ) 51 | } 52 | 53 | /** Tracks from a tainted Map to various of its internal data.*/ 54 | private predicate keyEntryEdge(DataFlow::Node node1, DataFlow::Node node2) { 55 | exists(MethodAccess ma, Method m | 56 | m.hasName("entrySet") or m.hasName("keySet") or m.hasName("values") | 57 | node1.asExpr() = ma.getQualifier() and node2.asExpr() = ma and 58 | ma.getMethod() = m and 59 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Map%") 60 | ) 61 | } 62 | 63 | /** Tracks from a tainted element into a Map. Note that this taints the whole map and can increase FP significantly.*/ 64 | private predicate mapPutEdge(DataFlow::Node node1, DataFlow::Node node2) { 65 | exists(MethodAccess ma, Method m | m = ma.getMethod() and 66 | ( 67 | m.hasName("put") or m.hasName("putAll") or m.hasName("replace") or m.hasName("putIfAbsent") or 68 | m.hasName("replaceAll") or m.hasName("compute") or m.hasName("computeIfAbsent") or m.hasName("merge") 69 | ) 70 | and 71 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Map%") and 72 | ma.getAnArgument() = node1.asExpr() and ma.getQualifier() = node2.asExpr() 73 | ) 74 | } 75 | 76 | /** Tracks from a tainted Collection to various methods that transforms it into different data type.*/ 77 | private predicate collectionTransformEdge(DataFlow::Node node1, DataFlow::Node node2) { 78 | exists(MethodAccess ma, Method m | ma.getMethod() = m and 79 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Collection%") and 80 | ( 81 | m.getName() = "stream" or 82 | m.getName() = "iterator" or 83 | m.getName() = "toArray" or 84 | m.getName() = "parallelStream" 85 | ) and 86 | ma = node2.asExpr() and ma.getQualifier() = node1.asExpr() 87 | ) or 88 | exists(MethodAccess ma, Method m | ma.getMethod() = m and 89 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Collection%") and 90 | ( 91 | m.getName() = "toArray" 92 | ) and 93 | ma.getQualifier() = node1.asExpr() and ma.getAnArgument() = node2.asExpr() 94 | ) 95 | } 96 | 97 | /** Tracks from a tainted Collection to its elements or other representations of its data.*/ 98 | predicate collectionsGetEdge(DataFlow::Node node1, DataFlow::Node node2) { 99 | collectionTransformEdge(node1, node2) or 100 | keyEntryEdge(node1, node2) or 101 | listEdge(node1, node2) or 102 | mapGetEdge(node1, node2) or 103 | mapEntryEdge(node1, node2) 104 | } 105 | 106 | /** Tracks from an element into a tainted Collection. Taints the whole collection and may cause significant increase in FP.*/ 107 | predicate collectionsPutEdge(DataFlow::Node node1, DataFlow::Node node2) { 108 | mapPutEdge(node1, node2) 109 | } -------------------------------------------------------------------------------- /semmle-security-java/lib/dataflow_extra/ExtraEdges.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | import semmle.code.java.dataflow.DefUse 4 | import lib.dataflow_extra.CollectionsEdges 5 | import semmle.code.java.dataflow.TaintTracking 6 | 7 | /** General dataflow edges that are useful for hunting vulnerabilities but not included in the standard library.*/ 8 | 9 | /** Holds if `node1` is a tainted value assigned to a field and `node2` is an access to the field. This essentially 10 | * says that if a field is assigned to a tainted value at some point, then any access to that field is considered tainted. 11 | * This has high risk of producing FP, but worth a try to see what you get. 12 | */ 13 | predicate isTaintedFieldStep(DataFlow::Node node1, DataFlow::Node node2) { 14 | exists(Field f, RefType t | node1.asExpr() = f.getAnAssignedValue() and node2.asExpr() = f.getAnAccess() and 15 | node1.asExpr().getEnclosingCallable().getDeclaringType() = t and 16 | node2.asExpr().getEnclosingCallable().getDeclaringType() = t 17 | ) 18 | } 19 | 20 | /** Tracks an object once any of its field is assigned to something tainted. Can be risky in terms of FP.*/ 21 | predicate taintFieldFromQualifier(DataFlow::Node n1, DataFlow::Node n2) { 22 | exists(Field f | 23 | n1 = DataFlow::getFieldQualifier(n2.asExpr()) and 24 | n2.asExpr() = f.getAnAccess() 25 | ) 26 | } 27 | 28 | /** Tracks from an object to the output of its `toString` method. */ 29 | predicate toStringStep(DataFlow::Node node1, DataFlow::Node node2) { 30 | exists(MethodAccess ma | ma.getMethod().getName() = "toString" and 31 | ma = node2.asExpr() and ma.getQualifier() = node1.asExpr() 32 | ) 33 | } 34 | 35 | /** Bundle up the less risky edges that are usually ok for bug hunting.*/ 36 | predicate standardExtraEdges(DataFlow::Node node1, DataFlow::Node node2) { 37 | collectionsGetEdge(node1, node2) or 38 | forLoopEdge(node1, node2) or 39 | toStringStep(node1, node2) or 40 | TaintTracking::localAdditionalTaintStep(node1, node2) 41 | } -------------------------------------------------------------------------------- /semmle-security-java/lib/dataflow_extra/ExtraSources.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | 4 | /** Extra remote sources that comes from `ServletRequest`.*/ 5 | predicate extraRequestSource(DataFlow::Node source) { 6 | exists(Method m, MethodAccess ma | m.getName().matches("get%") and 7 | not (m.getName() = "getAttribute" or m.getName() = "getContextPath") | 8 | (m.getDeclaringType().getASupertype*().hasQualifiedName("javax.servlet.http", "HttpServletRequest") 9 | or m.getDeclaringType().getASupertype*().hasQualifiedName("javax.servlet", "ServletRequest"))and 10 | source.asExpr() = ma and ma.getMethod() = m 11 | ) 12 | } -------------------------------------------------------------------------------- /semmle-security-java/lib/dataflow_extra/Sanitizers.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | 4 | /** A `Node` that is inside commonly used `Map` methods of rarely used subclasses of `Map`.*/ 5 | abstract class MapMethodSanitizer extends DataFlow::Node { 6 | } 7 | 8 | /** A `Node` that is inside the `toString` method of subclasses.*/ 9 | abstract class ToStringSanitizer extends DataFlow::Node { 10 | } 11 | 12 | /** Holds if `node` is inside test code. */ 13 | predicate isNodeInTestCode(DataFlow::Node node) { 14 | node.getEnclosingCallable() instanceof TestMethod 15 | or 16 | node.getEnclosingCallable().getDeclaringType() instanceof TestClass 17 | or 18 | node.asExpr().getFile().getAbsolutePath().toLowerCase().matches("%test%") 19 | } -------------------------------------------------------------------------------- /semmle-security-java/lib/struts/DataFlow.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | /** Contains data flow steps that are specific to apache struts2*/ 4 | 5 | 6 | /** A method in `ActionProxy` that may return remote user input. */ 7 | class ActionProxyGetMethod extends Method { 8 | ActionProxyGetMethod() { 9 | getDeclaringType().getASupertype*().hasQualifiedName("com.opensymphony.xwork2", "ActionProxy") and 10 | ( 11 | hasName("getMethod") or 12 | hasName("getNamespace") or 13 | hasName("getActionName") 14 | ) 15 | } 16 | } 17 | 18 | /** The class `ActionMapping`.*/ 19 | class ActionMapping extends RefType { 20 | ActionMapping() { 21 | hasQualifiedName("org.apache.struts2.dispatcher.mapper", "ActionMapping") 22 | } 23 | } 24 | 25 | /** A method in `ActionMapper` that returns a field that may be tainted.*/ 26 | class ActionMappingGetMethod extends Method { 27 | ActionMappingGetMethod() { 28 | getDeclaringType() instanceof ActionMapping and 29 | ( 30 | hasName("getName") or 31 | hasName("getNamespace") or 32 | hasName("getMethod") or 33 | hasName("getParams") or 34 | hasName("getExtension") 35 | ) 36 | } 37 | } 38 | 39 | /** Holds if `source` is a remote user input in struts. */ 40 | predicate isRemoteUserInputSource(DataFlow::Node source) { 41 | source.asExpr().(MethodAccess).getMethod() instanceof ActionProxyGetMethod 42 | } 43 | 44 | /** Tracks through constructor and getter of `ActionMapping`.*/ 45 | predicate actionMapperEdge(DataFlow::Node node1, DataFlow::Node node2) { 46 | exists(ConstructorCall c | c.getAnArgument() = node1.asExpr() and 47 | c.getConstructedType() instanceof ActionMapping and c = node2.asExpr() 48 | ) or 49 | exists(MethodAccess ma | ma.getMethod() instanceof ActionMappingGetMethod and 50 | node1.asExpr() = ma.getQualifier() and node2.asExpr() = ma 51 | ) 52 | } -------------------------------------------------------------------------------- /semmle-security-java/lib/struts/OGNL.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | import semmle.code.java.dataflow.DataFlow3 4 | import lib.dataflow_extra.CollectionsEdges 5 | import lib.dataflow_extra.ExtraEdges 6 | import lib.struts.Sanitizers 7 | import lib.dataflow_extra.Sanitizers 8 | 9 | /** Contains classes and predicates related to the use of ognl.*/ 10 | 11 | class OgnlUtil extends RefType { 12 | OgnlUtil() { 13 | hasQualifiedName("com.opensymphony.xwork2.ognl", "OgnlUtil") 14 | } 15 | } 16 | 17 | /** Holds when `sink` is an argument to a call that ended up executing it as ognl.*/ 18 | predicate isOgnlSink(DataFlow::Node sink) { 19 | exists(MethodAccess ma | (ma.getMethod().hasName("compileAndExecute")) and 20 | ma.getMethod().getDeclaringType() instanceof OgnlUtil and 21 | sink.asExpr() = ma.getArgument(0) 22 | ) 23 | } 24 | 25 | /** Tracks parameters of a `Method` that ended up being evaluated as ognl.*/ 26 | class OgnlCallConfiguration extends DataFlow3::Configuration { 27 | OgnlCallConfiguration() { 28 | this = "OgnlCallConfiguration" 29 | } 30 | 31 | override predicate isSource(DataFlow::Node source) { 32 | exists(Method m | m.getAParameter() = source.asParameter() and 33 | source.asParameter().getType() instanceof TypeString 34 | ) 35 | } 36 | 37 | override predicate isSink(DataFlow::Node sink) { 38 | isOgnlSink(sink) 39 | } 40 | 41 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 42 | standardExtraEdges(node1, node2) or 43 | collectionsPutEdge(node1, node2) 44 | } 45 | 46 | override predicate isBarrier(DataFlow::Node node) { 47 | ognlSanitizers(node) or 48 | node instanceof StrutsTestSanitizer or 49 | node instanceof ToStringSanitizer or 50 | node instanceof MapMethodSanitizer 51 | } 52 | } 53 | 54 | /** A `Method` whose parameter ended gets evaluated as ognl.*/ 55 | class OgnlCallMethod extends Method { 56 | OgnlCallMethod() { 57 | exists(OgnlCallConfiguration cfg, DataFlow::Node source | 58 | cfg.hasFlow(source, _) and source.asParameter() = this.getAParameter() 59 | ) and 60 | not hasName("completeExpressionIfAltSyntax") and 61 | not hasName("stripExpressionIfAltSyntax") and 62 | not hasName("setLocation") 63 | } 64 | } 65 | 66 | /** An `OgnlCallMethod` that is not used by another `OgnlCallMethod`.*/ 67 | class OgnlEntryPointMethod extends OgnlCallMethod { 68 | OgnlEntryPointMethod() { 69 | exists(Method m | m.polyCalls(this) and not m instanceof OgnlCallMethod) 70 | } 71 | } -------------------------------------------------------------------------------- /semmle-security-java/lib/struts/README.md: -------------------------------------------------------------------------------- 1 | #Struts QL libraries 2 | 3 | This contains project specific libraries for Apache Struts. You can import these libraries in your custom queries and use them on snapshots of [Apache Struts].(https://lgtm.com/projects/g/apache/struts) 4 | -------------------------------------------------------------------------------- /semmle-security-java/lib/struts/Sanitizers.qll: -------------------------------------------------------------------------------- 1 | import java 2 | import semmle.code.java.dataflow.DataFlow 3 | import lib.dataflow_extra.Sanitizers 4 | import OGNL 5 | /** Contains sanitizers that are specific to apache struts 2.*/ 6 | 7 | /** A `MapMethodSanitizer` where the `Map` is the `ValueStackShadowMap`.*/ 8 | class StrutsMapMethodSanitizer extends MapMethodSanitizer { 9 | StrutsMapMethodSanitizer() { 10 | exists(Method m | (m.hasName("get") or m.hasName("containsKey") or m.hasName("entrySet")) and 11 | m.getDeclaringType().getASourceSupertype*().getQualifiedName().matches("java.util.Map%") and 12 | this.getEnclosingCallable() = m 13 | ) 14 | } 15 | } 16 | 17 | /** A `ToStringSanitizer` that corresponds to the various `toString` method.*/ 18 | class StrutsToStringSanitizer extends ToStringSanitizer { 19 | StrutsToStringSanitizer() { 20 | exists(Method m | 21 | m.hasName("toString") and this.getEnclosingCallable() = m 22 | ) 23 | } 24 | } 25 | 26 | /** A `DataFlow::Node` that is inside test code. */ 27 | class StrutsTestSanitizer extends DataFlow::Node { 28 | StrutsTestSanitizer() { 29 | isNodeInTestCode(this) or 30 | this.getEnclosingCallable().getDeclaringType().getName().matches("%Mock%") 31 | } 32 | } 33 | 34 | /** Holds if a node flows into a sanitizer method that filters out ognl script.*/ 35 | predicate ognlSanitizers(DataFlow::Node node) { 36 | exists(MethodAccess ma, Method m | ma.getMethod() = m and m.hasName("callMethod") and 37 | ma.getAnArgument() = node.asExpr() and m.getDeclaringType() instanceof OgnlUtil 38 | ) or 39 | exists(MethodAccess ma, Method m | m.hasName("cleanupActionName") or m.hasName("cleanupMethodName") | 40 | m = ma.getMethod() and ma.getAnArgument() = node.asExpr() and 41 | m.getDeclaringType().hasQualifiedName("org.apache.struts2.dispatcher.mapper", "DefaultActionMapper") 42 | ) 43 | or 44 | exists(VarAccess v | v.getVariable().getType() instanceof NumericType and node.asExpr() = v) 45 | } -------------------------------------------------------------------------------- /semmle-security-java/queries.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/README.md: -------------------------------------------------------------------------------- 1 | # Apache Struts specific queries 2 | 3 | This directory contains security queries specific for [Apache Struts](https://struts.apache.org/). You can run the queries here on Apache Struts snapshots that you get from [LGTM](https://lgtm.com/projects/g/apache/struts). Follow the steps [here](https://help.semmle.com/ql-for-eclipse/Content/WebHelp/basic-usage.html) to import the snapshot for Struts and run the queries. Many of the queries here are used for identifying a particular vulnerabilities and you may need to run them on a specific snapshot that contains the vulnerabilities to see the results. Please see the `README` files in the subdirectories that contains the query. 4 | -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/cve_2018_11776/README.md: -------------------------------------------------------------------------------- 1 | #CVE-2018-11776 2 | 3 | The queries here are used for identifying CVE-2018-11776 and should be used on the following snapshot: 4 | 5 | * [Apache Struts `7fd1622` for `CVE-2018-11776`](https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-7fd1622-CVE-2018-11776.zip) 6 | 7 | The blog post write up for the queries `initial.ql` and `final.ql` can be found [here.](https://lgtm.com/blog/apache_struts_CVE-2018-11776) 8 | 9 | The blog post write up for the more detailed queries `ognl_injection_initial.ql` and `ognl_injection_final.ql` can be found [here.](https://lgtm.com/blog/apache_struts_CVE-2018-11776-part2) 10 | -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/cve_2018_11776/final.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name cve-2018-11776 final 3 | * @description Final query for detecting CVE-2018-11776 in Struts. 4 | * @kind path-problem 5 | * @id cve_2018_11776_final 6 | */ 7 | 8 | 9 | import java 10 | import lib.struts.DataFlow 11 | import lib.struts.OGNL 12 | import lib.struts.Sanitizers 13 | import lib.dataflow_extra.ExtraEdges 14 | import DataFlow::PathGraph 15 | import semmle.code.java.dataflow.TaintTracking 16 | 17 | 18 | class MappingCfg extends DataFlow::Configuration { 19 | MappingCfg() { 20 | this = "cve 2018-11776" 21 | } 22 | 23 | override predicate isSource(DataFlow::Node source) { 24 | isRemoteUserInputSource(source) 25 | } 26 | 27 | override predicate isSink(DataFlow::Node sink) { 28 | isOgnlSink(sink) 29 | } 30 | 31 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 32 | TaintTracking::localAdditionalTaintStep(node1, node2) or 33 | isTaintedFieldStep(node1, node2) 34 | } 35 | 36 | override predicate isBarrier(DataFlow::Node node) { 37 | node instanceof MapMethodSanitizer or 38 | node instanceof ToStringSanitizer 39 | } 40 | } 41 | 42 | from MappingCfg cfg, DataFlow::PathNode source, DataFlow::PathNode sink 43 | where cfg.hasFlowPath(source, sink) 44 | select source, source, sink, "This user input flows to an $@.", sink, "OGNL sink" -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/cve_2018_11776/initial.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name cve-2018-11776 final 3 | * @description Final query for detecting CVE-2018-11776 in Struts. 4 | * @kind path-problem 5 | * @id cve_2018_11776_final 6 | */ 7 | 8 | 9 | import java 10 | import lib.struts.DataFlow 11 | import lib.struts.OGNL 12 | import lib.struts.Sanitizers 13 | import lib.dataflow_extra.ExtraEdges 14 | import DataFlow::PathGraph 15 | import semmle.code.java.dataflow.TaintTracking 16 | 17 | 18 | class MappingCfg extends DataFlow::Configuration { 19 | MappingCfg() { 20 | this = "cve 2018-11776" 21 | } 22 | 23 | override predicate isSource(DataFlow::Node source) { 24 | isRemoteUserInputSource(source) 25 | } 26 | 27 | override predicate isSink(DataFlow::Node sink) { 28 | isOgnlSink(sink) 29 | } 30 | 31 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 32 | TaintTracking::localAdditionalTaintStep(node1, node2) or 33 | isTaintedFieldStep(node1, node2) 34 | } 35 | } 36 | 37 | from MappingCfg cfg, DataFlow::PathNode source, DataFlow::PathNode sink 38 | where cfg.hasFlowPath(source, sink) 39 | select source, source, sink, "This user input flows to an $@.", sink, "OGNL sink" -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/cve_2018_11776/ognl_Injection_final.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Ognl injection 3 | * @description Identifies where remote user input may get evaluated as Ognl expression. 4 | * @kind path-problem 5 | * @problem.severity error 6 | * @id OgnlInjection 7 | */ 8 | 9 | import java 10 | import semmle.code.java.dataflow.FlowSources 11 | import semmle.code.java.dataflow.TaintTracking 12 | import semmle.code.java.frameworks.Servlets 13 | import semmle.code.java.dataflow.internal.DataFlowUtil 14 | import DataFlow::PathGraph 15 | import lib.struts.Sanitizers 16 | import lib.dataflow_extra.ExtraEdges 17 | import lib.dataflow_extra.CollectionsEdges 18 | import lib.dataflow_extra.ExtraSources 19 | import lib.struts.OGNL 20 | 21 | class OgnlInjectionCfg extends DataFlow::Configuration { 22 | OgnlInjectionCfg() { 23 | this = "ognl injection final" 24 | } 25 | 26 | override predicate isSource(DataFlow::Node source) { 27 | source instanceof RemoteUserInput or extraRequestSource(source) 28 | } 29 | 30 | override predicate isSink(DataFlow::Node sink) { 31 | isOgnlSink(sink) 32 | } 33 | 34 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 35 | standardExtraEdges(node1, node2) or 36 | collectionsPutEdge(node1, node2) or 37 | taintStringFieldFromQualifier(node1, node2) or 38 | isTaintedFieldStep(node1, node2) 39 | } 40 | 41 | override predicate isBarrier(DataFlow::Node node) { 42 | node instanceof StrutsTestSanitizer 43 | or 44 | ognlSanitizers(node) 45 | or 46 | node instanceof ToStringSanitizer 47 | or 48 | node instanceof MapMethodSanitizer 49 | or 50 | restfulMapperSanitizer(node) 51 | or 52 | tagUtilsSanitizer(node) 53 | or 54 | exists(MethodAccess ma, FieldAccess fa | ma.getMethod().hasName("get") and 55 | ma.getQualifier() = fa and fa.getField().hasName("validatorCache") and 56 | fa.getField().getDeclaringType().hasName("DefaultActionValidatorManager") and 57 | node.asExpr() = ma 58 | ) 59 | or 60 | actionContextSanitizer(node) 61 | } 62 | } 63 | 64 | /** Tracks from tainted object to its field access when the field is of type string.*/ 65 | predicate taintStringFieldFromQualifier(DataFlow::Node node1, DataFlow::Node node2) { 66 | taintFieldFromQualifier(node1, node2) and 67 | exists(Field f | f.getAnAccess() = node2.asExpr() and f.getType() instanceof TypeString) 68 | } 69 | 70 | /** Filters out the internals of `ActionContext`, which are unlikely to be of user control.*/ 71 | predicate actionContextSanitizer(DataFlow::Node node) { 72 | exists(MethodAccess ma, Method m | ma.getMethod() = m and m.getDeclaringType().hasName("ActionContext") and 73 | m.hasName("get") and node.asExpr() = ma 74 | ) 75 | } 76 | 77 | /** Filters out the cases where `RestfulActionMapper` is used.*/ 78 | predicate restfulMapperSanitizer(DataFlow::Node node) { 79 | node.asExpr().getEnclosingCallable().hasName("getUriFromActionMapping") and 80 | node.asExpr().getEnclosingCallable().getDeclaringType().hasName("RestfulActionMapper") 81 | } 82 | 83 | /** Filters out cases where jsp file is exposed. Not considered as vulnerable, see https://issues.apache.org/jira/browse/WW-4945*/ 84 | predicate tagUtilsSanitizer(DataFlow::Node node) { 85 | node.asExpr().getEnclosingCallable().getDeclaringType().hasName("TagUtils") and 86 | node.asExpr().getEnclosingCallable().hasName("buildNamespace") 87 | } 88 | 89 | /** Filters out results that we've already seen, use and modify this to highlight results in different files.*/ 90 | predicate knowResultsSanitizer(DataFlow::Node node) { 91 | node.asExpr().getFile().getBaseName() = "ServletUrlRenderer.java" 92 | or 93 | node.asExpr().getFile().getBaseName() = "PostbackResult.java" 94 | or 95 | node.asExpr().getFile().getBaseName() = "ActionChainResult.java" 96 | or 97 | node.asExpr().getFile().getBaseName() = "ServletActionRedirectResult.java" 98 | or 99 | node.asExpr().getFile().getBaseName() = "PortletActionRedirectResult.java" 100 | } 101 | 102 | from OgnlInjectionCfg cfg, DataFlow::PathNode source, DataFlow::PathNode sink 103 | where cfg.hasFlowPath(source, sink) 104 | select source,source, sink, "$@ flows to here and is used in OGNL.", sink, "User input" -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/cve_2018_11776/ognl_Injection_initial.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Ognl injection 3 | * @description Identifies where remote user input may get evaluated as Ognl expression. 4 | * @kind path-problem 5 | * @problem.severity error 6 | * @id OgnlInjection 7 | */ 8 | 9 | import java 10 | import semmle.code.java.dataflow.FlowSources 11 | import semmle.code.java.dataflow.TaintTracking 12 | import semmle.code.java.frameworks.Servlets 13 | import semmle.code.java.dataflow.internal.DataFlowUtil 14 | import DataFlow::PathGraph 15 | import lib.struts.Sanitizers 16 | import lib.dataflow_extra.ExtraEdges 17 | import lib.dataflow_extra.CollectionsEdges 18 | import lib.dataflow_extra.ExtraSources 19 | import lib.struts.OGNL 20 | 21 | class OgnlInjectionCfg extends DataFlow::Configuration { 22 | OgnlInjectionCfg() { 23 | this = "ognl injection initial" 24 | } 25 | 26 | override predicate isSource(DataFlow::Node source) { 27 | source instanceof RemoteUserInput or extraRequestSource(source) 28 | } 29 | 30 | override predicate isSink(DataFlow::Node sink) { 31 | isOgnlSink(sink) 32 | } 33 | 34 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 35 | standardExtraEdges(node1, node2) or 36 | collectionsPutEdge(node1, node2) or 37 | taintStringFieldFromQualifier(node1, node2) or 38 | isTaintedFieldStep(node1, node2) 39 | } 40 | 41 | override predicate isBarrier(DataFlow::Node node) { 42 | node instanceof StrutsTestSanitizer 43 | or 44 | ognlSanitizers(node) 45 | or 46 | node instanceof ToStringSanitizer 47 | or 48 | node instanceof MapMethodSanitizer 49 | } 50 | } 51 | 52 | /** Tracks from tainted object to its field access when the field is of type string.*/ 53 | predicate taintStringFieldFromQualifier(DataFlow::Node node1, DataFlow::Node node2) { 54 | taintFieldFromQualifier(node1, node2) and 55 | exists(Field f | f.getAnAccess() = node2.asExpr() and f.getType() instanceof TypeString) 56 | } 57 | 58 | from OgnlInjectionCfg cfg, DataFlow::PathNode source, DataFlow::PathNode sink 59 | where cfg.hasFlowPath(source, sink) 60 | select source,source, sink, "$@ flows to here and is used in OGNL.", sink, "User input" -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/double_evaluation/README.md: -------------------------------------------------------------------------------- 1 | #Double evaluation 2 | 3 | The queries here are used for identifying double evaluations in Struts. 4 | 5 | The blog post write up can be found [here.](https://lgtm.com/blog/apache_struts_double_evaluations) This [snapshot](https://downloads.lgtm.com/snapshots/java/apache/struts/apache-struts-7fd1622-CVE-2018-11776.zip) is used in the blog post, but the query can be run on any Struts snapshot from lgtm.com. 6 | -------------------------------------------------------------------------------- /semmle-security-java/queries/struts/double_evaluation/double_eval_final.ql: -------------------------------------------------------------------------------- 1 | /** 2 | * @name Double Ognl Evaluation 3 | * @description Fields that are initialized from configuration that may get evaluated twice as Ognl expression. 4 | * @kind path-problem 5 | * @problem.severity error 6 | * @id double_eval 7 | */ 8 | 9 | import java 10 | import semmle.code.java.dataflow.FlowSources 11 | import semmle.code.java.dataflow.TaintTracking 12 | import semmle.code.java.frameworks.Servlets 13 | import semmle.code.java.dataflow.internal.DataFlowUtil 14 | import lib.dataflow_extra.CollectionsEdges 15 | import lib.dataflow_extra.ExtraEdges 16 | import lib.struts.Sanitizers 17 | import lib.struts.DataFlow 18 | import lib.dataflow_extra.Sanitizers 19 | import DataFlow::PathGraph 20 | import lib.struts.OGNL 21 | 22 | /** A `Field` whose value is a compile time constant.*/ 23 | class ConstantField extends Field { 24 | ConstantField() { 25 | exists(CompileTimeConstantExpr e | this.getAnAssignedValue() = e and 26 | this.isFinal() 27 | ) 28 | } 29 | } 30 | 31 | /** A `Class` whose fields may be initialized from a config file.*/ 32 | class InputClass extends RefType { 33 | InputClass() { 34 | hasQualifiedName("org.apache.struts2.views.jsp", "StrutsBodyTagSupport") or 35 | hasQualifiedName("org.apache.struts2.components", "Component") or 36 | hasQualifiedName("com.opensymphony.xwork2", "Result") or 37 | hasQualifiedName("org.apache.struts.action", "Action") or 38 | hasQualifiedName("com.opensymphony.xwork2.interceptor", "Interceptor") 39 | } 40 | } 41 | 42 | /** A `Field` that may take value from a config file.*/ 43 | class ConfigurableField extends Field { 44 | ConfigurableField() { 45 | getDeclaringType().getASupertype*() instanceof InputClass 46 | } 47 | } 48 | 49 | /** Tracks from a field that may take value from configuration file into the an ognl evaluation method.*/ 50 | class InputCfg extends DataFlow::Configuration { 51 | InputCfg() { 52 | this = "input" 53 | } 54 | 55 | override predicate isSource(DataFlow::Node source) { 56 | exists(ConfigurableField f | f.getAnAccess() = source.asExpr() and 57 | not f instanceof ConstantField) 58 | or 59 | //`ActionConfig` params are also taken from configuration. 60 | exists(MethodAccess ma, Method m | ma.getMethod() = m and 61 | m.getDeclaringType().hasName("ActionConfig") and 62 | m.hasName("getParams") and source.asExpr() = ma 63 | ) 64 | } 65 | 66 | override predicate isSink(DataFlow::Node sink) { 67 | exists(MethodAccess ma, OgnlEntryPointMethod entry, Method m | ma.getMethod() = m and 68 | ma.getAnArgument() = sink.asExpr() and entry.overridesOrInstantiates*(m) 69 | ) 70 | } 71 | 72 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 73 | standardExtraEdges(node1, node2) or 74 | collectionsPutEdge(node1, node2) or 75 | actionMapperEdge(node1, node2) 76 | } 77 | 78 | override predicate isBarrier(DataFlow::Node node) { 79 | node instanceof StrutsTestSanitizer 80 | or 81 | ognlSanitizers(node) 82 | or 83 | node instanceof ToStringSanitizer 84 | or 85 | node instanceof MapMethodSanitizer 86 | } 87 | } 88 | 89 | class DoubleEvalConfig extends DataFlow::Configuration { 90 | DoubleEvalConfig() { 91 | this = "doubleEvalConfig" 92 | } 93 | 94 | override predicate isSource(DataFlow::Node source) { 95 | exists(MethodAccess ma, OgnlEntryPointMethod entry, Method m | ma.getMethod() = m and 96 | source.asExpr() = ma and entry.overridesOrInstantiates*(m) 97 | ) 98 | } 99 | 100 | override predicate isSink(DataFlow::Node sink) { 101 | exists(MethodAccess ma, OgnlEntryPointMethod entry, Method m, int i | ma.getMethod() = m and 102 | sink.asExpr() = ma.getArgument(i) and entry.overridesOrInstantiates*(m) and 103 | m.getParameter(i).getType() instanceof TypeString 104 | ) or 105 | isOgnlSink(sink) 106 | } 107 | 108 | 109 | override predicate isAdditionalFlowStep(DataFlow::Node node1, DataFlow::Node node2) { 110 | standardExtraEdges(node1, node2) or 111 | collectionsPutEdge(node1, node2) or 112 | actionMapperEdge(node1, node2) 113 | } 114 | 115 | override predicate isBarrier(DataFlow::Node node) { 116 | ognlSanitizers(node) or 117 | node instanceof StrutsTestSanitizer or 118 | node instanceof ToStringSanitizer or 119 | node instanceof MapMethodSanitizer or 120 | //Stops when we reach an `OgnlEntryPointMethod` to cut down on duplicate results. 121 | exists(OgnlEntryPointMethod m, DataFlow::Node n |TaintTracking::localTaint(n, node) and m.getAParameter() = n.asParameter()) or 122 | exists(ReturnStmt stmt, DataFlow::Node rtn | stmt.getResult() = rtn.asExpr() and TaintTracking::localTaint(node, rtn) and 123 | stmt.getEnclosingCallable() instanceof OgnlEntryPointMethod) 124 | } 125 | } 126 | 127 | from DoubleEvalConfig cfg, DataFlow::PathNode source, DataFlow::PathNode sink, 128 | InputCfg input, DataFlow::Node inputSrc, DataFlow::Node inputSink 129 | where cfg.hasFlowPath(source, sink) and input.hasFlow(inputSrc, inputSink) and 130 | //use call site of `InputCfg` as source of `DoubleEvalConfig` 131 | exists(MethodAccess ma | ma = source.getNode().asExpr() and ma.getAnArgument() = inputSink.asExpr()) and 132 | //Remove `circular` evaluations 133 | not exists(MethodAccess ma | source.getNode().asExpr() = ma and sink.getNode().asExpr() = ma.getAnArgument()) and 134 | //Cut down on paths that contains one another 135 | not exists(DataFlow::Node s | TaintTracking::localTaintStep+(s, inputSrc)) 136 | select source, source, sink, "$@ from configuration first $@ and then evaluated $@.", inputSrc, "Input", source, "evaluated", sink, "here" --------------------------------------------------------------------------------