├── CHANGES ├── Code_of_Conduct.md ├── LICENSE ├── Makefile ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── doc ├── check-log4j.1 └── check-log4j.1.txt └── src └── check-log4j.sh /CHANGES: -------------------------------------------------------------------------------- 1 | Version 3.0 (2022-02-28) 2 | * remove '-f' option 3 | 4 | Version 2.4 (2022-02-28) 5 | * handle corrupt zip files more gracefully 6 | 7 | Version 2.3 (2022-01-27) 8 | * bug fix to really match all pathnames case-insensitively 9 | 10 | Version 2.2 (2022-01-26) 11 | * match all pathnames case-insensitively 12 | 13 | Version 2.1 (2022-01-11) 14 | * suppress warnings from lsof(8) 15 | * handle zero-sized files more gracefully 16 | * perform case-insensitive search 17 | * use 'command -v' instead of which(1) 18 | 19 | Version 2.0 (2022-01-03) 20 | * still note if we encounter versions <= 2.17.1 21 | 22 | Version 1.9 (2021-12-17) 23 | * use correct IFS 24 | 25 | Version 1.8 (2021-12-17) 26 | * add '-V' to report version number 27 | 28 | Version 1.7 (2021-12-17) 29 | * allow user to pass flags to find(1) 30 | * also check EAR files 31 | 32 | Version 1.6 (2021-12-16) 33 | * correctly detect fixed versions 34 | * use full directory names to ensure find(1) traverses symlinks 35 | 36 | Version 1.5 (2021-12-16) 37 | * also look into WAR files 38 | 39 | Version 1.4 (2021-12-15) 40 | * detect fatal class even if not inside a jar 41 | * first open source version 42 | 43 | Version 1.3 (2021-12-15) 44 | * better 2.16.0 detection 45 | * handle relative directories better 46 | * use more portable ps(1) flags 47 | * handle given jar files better 48 | 49 | Version 1.2 (2021-12-15) 50 | * logic error 51 | 52 | Version 1.1 (2021-12-15) 53 | * if unzip(1) isn't found, try grep(1) 54 | 55 | Version 1.0 (2021-12‐15) 56 | * first version number introduced 57 | * rapid iterations and changes from the initial 58 | version pushed to git on 2021-12-13 59 | 60 | 61 | Version 0.1 (2021-12-13) 62 | * hello ${jndi:ldap://world/!} 63 | -------------------------------------------------------------------------------- /Code_of_Conduct.md: -------------------------------------------------------------------------------- 1 | # Yahoo Inc Open Source Code of Conduct 2 | 3 | ## Summary 4 | This Code of Conduct is our way to encourage good behavior and discourage bad behavior in our open source projects. We invite participation from many people to bring different perspectives to our projects. We will do our part to foster a welcoming and professional environment free of harassment. We expect participants to communicate professionally and thoughtfully during their involvement with this project. 5 | 6 | Participants may lose their good standing by engaging in misconduct. For example: insulting, threatening, or conveying unwelcome sexual content. We ask participants who observe conduct issues to report the incident directly to the project's Response Team at opensource-conduct@yahooinc.com. Yahoo will assign a respondent to address the issue. We may remove harassers from this project. 7 | 8 | This code does not replace the terms of service or acceptable use policies of the websites used to support this project. We acknowledge that participants may be subject to additional conduct terms based on their employment which may govern their online expressions. 9 | 10 | ## Details 11 | This Code of Conduct makes our expectations of participants in this community explicit. 12 | * We forbid harassment and abusive speech within this community. 13 | * We request participants to report misconduct to the project’s Response Team. 14 | * We urge participants to refrain from using discussion forums to play out a fight. 15 | 16 | ### Expected Behaviors 17 | We expect participants in this community to conduct themselves professionally. Since our primary mode of communication is text on an online forum (e.g. issues, pull requests, comments, emails, or chats) devoid of vocal tone, gestures, or other context that is often vital to understanding, it is important that participants are attentive to their interaction style. 18 | 19 | * **Assume positive intent.** We ask community members to assume positive intent on the part of other people’s communications. We may disagree on details, but we expect all suggestions to be supportive of the community goals. 20 | * **Respect participants.** We expect occasional disagreements. Open Source projects are learning experiences. Ask, explore, challenge, and then _respectfully_ state if you agree or disagree. If your idea is rejected, be more persuasive not bitter. 21 | * **Welcoming to new members.** New members bring new perspectives. Some ask questions that have been addressed before. _Kindly_ point to existing discussions. Everyone is new to every project once. 22 | * **Be kind to beginners.** Beginners use open source projects to get experience. They might not be talented coders yet, and projects should not accept poor quality code. But we were all beginners once, and we need to engage kindly. 23 | * **Consider your impact on others.** Your work will be used by others, and you depend on the work of others. We expect community members to be considerate and establish a balance their self-interest with communal interest. 24 | * **Use words carefully.** We may not understand intent when you say something ironic. Often, people will misinterpret sarcasm in online communications. We ask community members to communicate plainly. 25 | * **Leave with class.** When you wish to resign from participating in this project for any reason, you are free to fork the code and create a competitive project. Open Source explicitly allows this. Your exit should not be dramatic or bitter. 26 | 27 | ### Unacceptable Behaviors 28 | Participants remain in good standing when they do not engage in misconduct or harassment (some examples follow). We do not list all forms of harassment, nor imply some forms of harassment are not worthy of action. Any participant who *feels* harassed or *observes* harassment, should report the incident to the Response Team. 29 | * **Don't be a bigot.** Calling out project members by their identity or background in a negative or insulting manner. This includes, but is not limited to, slurs or insinuations related to protected or suspect classes e.g. race, color, citizenship, national origin, political belief, religion, sexual orientation, gender identity and expression, age, size, culture, ethnicity, genetic features, language, profession, national minority status, mental or physical ability. 30 | * **Don't insult.** Insulting remarks about a person’s lifestyle practices. 31 | * **Don't dox.** Revealing private information about other participants without explicit permission. 32 | * **Don't intimidate.** Threats of violence or intimidation of any project member. 33 | * **Don't creep.** Unwanted sexual attention or content unsuited for the subject of this project. 34 | * **Don't inflame.** We ask that victim of harassment not address their grievances in the public forum, as this often intensifies the problem. Report it, and let us address it off-line. 35 | * **Don't disrupt.** Sustained disruptions in a discussion. 36 | 37 | ### Reporting Issues 38 | If you experience or witness misconduct, or have any other concerns about the conduct of members of this project, please report it by contacting our Response Team at opensource-conduct@yahooinc.com who will handle your report with discretion. Your report should include: 39 | * Your preferred contact information. We cannot process anonymous reports. 40 | * Names (real or usernames) of those involved in the incident. 41 | * Your account of what occurred, and if the incident is ongoing. Please provide links to or transcripts of the publicly available records (e.g. a mailing list archive or a public IRC logger), so that we can review it. 42 | * Any additional information that may be helpful to achieve resolution. 43 | 44 | After filing a report, a representative will contact you directly to review the incident and ask additional questions. If a member of the Yahoo Response Team is named in an incident report, that member will be recused from handling your incident. If the complaint originates from a member of the Response Team, it will be addressed by a different member of the Response Team. We will consider reports to be confidential for the purpose of protecting victims of abuse. 45 | 46 | ### Scope 47 | Yahoo will assign a Response Team member with admin rights on the project and legal rights on the project copyright. The Response Team is empowered to restrict some privileges to the project as needed. Since this project is governed by an open source license, any participant may fork the code under the terms of the project license. The Response Team’s goal is to preserve the project if possible, and will restrict or remove participation from those who disrupt the project. 48 | 49 | This code does not replace the terms of service or acceptable use policies that are provided by the websites used to support this community. Nor does this code apply to communications or actions that take place outside of the context of this community. Many participants in this project are also subject to codes of conduct based on their employment. This code is a social-contract that informs participants of our social expectations. It is not a terms of service or legal contract. 50 | 51 | ## License and Acknowledgment. 52 | This text is shared under the [CC-BY-4.0 license](https://creativecommons.org/licenses/by/4.0/). This code is based on a study conducted by the [TODO Group](https://todogroup.org/) of many codes used in the open source community. If you have feedback about this code, contact our Response Team at the address listed above. 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright 2021 Yahoo Inc. 3 | 4 | 5 | Apache License 6 | Version 2.0, January 2004 7 | http://www.apache.org/licenses/ 8 | 9 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 10 | 11 | 1. Definitions. 12 | 13 | "License" shall mean the terms and conditions for use, reproduction, 14 | and distribution as defined by Sections 1 through 9 of this document. 15 | 16 | "Licensor" shall mean the copyright owner or entity authorized by 17 | the copyright owner that is granting the License. 18 | 19 | "Legal Entity" shall mean the union of the acting entity and all 20 | other entities that control, are controlled by, or are under common 21 | control with that entity. For the purposes of this definition, 22 | "control" means (i) the power, direct or indirect, to cause the 23 | direction or management of such entity, whether by contract or 24 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 25 | outstanding shares, or (iii) beneficial ownership of such entity. 26 | 27 | "You" (or "Your") shall mean an individual or Legal Entity 28 | exercising permissions granted by this License. 29 | 30 | "Source" form shall mean the preferred form for making modifications, 31 | including but not limited to software source code, documentation 32 | source, and configuration files. 33 | 34 | "Object" form shall mean any form resulting from mechanical 35 | transformation or translation of a Source form, including but 36 | not limited to compiled object code, generated documentation, 37 | and conversions to other media types. 38 | 39 | "Work" shall mean the work of authorship, whether in Source or 40 | Object form, made available under the License, as indicated by a 41 | copyright notice that is included in or attached to the work 42 | (an example is provided in the Appendix below). 43 | 44 | "Derivative Works" shall mean any work, whether in Source or Object 45 | form, that is based on (or derived from) the Work and for which the 46 | editorial revisions, annotations, elaborations, or other modifications 47 | represent, as a whole, an original work of authorship. For the purposes 48 | of this License, Derivative Works shall not include works that remain 49 | separable from, or merely link (or bind by name) to the interfaces of, 50 | the Work and Derivative Works thereof. 51 | 52 | "Contribution" shall mean any work of authorship, including 53 | the original version of the Work and any modifications or additions 54 | to that Work or Derivative Works thereof, that is intentionally 55 | submitted to Licensor for inclusion in the Work by the copyright owner 56 | or by an individual or Legal Entity authorized to submit on behalf of 57 | the copyright owner. For the purposes of this definition, "submitted" 58 | means any form of electronic, verbal, or written communication sent 59 | to the Licensor or its representatives, including but not limited to 60 | communication on electronic mailing lists, source code control systems, 61 | and issue tracking systems that are managed by, or on behalf of, the 62 | Licensor for the purpose of discussing and improving the Work, but 63 | excluding communication that is conspicuously marked or otherwise 64 | designated in writing by the copyright owner as "Not a Contribution." 65 | 66 | "Contributor" shall mean Licensor and any individual or Legal Entity 67 | on behalf of whom a Contribution has been received by Licensor and 68 | subsequently incorporated within the Work. 69 | 70 | 2. Grant of Copyright License. Subject to the terms and conditions of 71 | this License, each Contributor hereby grants to You a perpetual, 72 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 73 | copyright license to reproduce, prepare Derivative Works of, 74 | publicly display, publicly perform, sublicense, and distribute the 75 | Work and such Derivative Works in Source or Object form. 76 | 77 | 3. Grant of Patent License. Subject to the terms and conditions of 78 | this License, each Contributor hereby grants to You a perpetual, 79 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 80 | (except as stated in this section) patent license to make, have made, 81 | use, offer to sell, sell, import, and otherwise transfer the Work, 82 | where such license applies only to those patent claims licensable 83 | by such Contributor that are necessarily infringed by their 84 | Contribution(s) alone or by combination of their Contribution(s) 85 | with the Work to which such Contribution(s) was submitted. If You 86 | institute patent litigation against any entity (including a 87 | cross-claim or counterclaim in a lawsuit) alleging that the Work 88 | or a Contribution incorporated within the Work constitutes direct 89 | or contributory patent infringement, then any patent licenses 90 | granted to You under this License for that Work shall terminate 91 | as of the date such litigation is filed. 92 | 93 | 4. Redistribution. You may reproduce and distribute copies of the 94 | Work or Derivative Works thereof in any medium, with or without 95 | modifications, and in Source or Object form, provided that You 96 | meet the following conditions: 97 | 98 | (a) You must give any other recipients of the Work or 99 | Derivative Works a copy of this License; and 100 | 101 | (b) You must cause any modified files to carry prominent notices 102 | stating that You changed the files; and 103 | 104 | (c) You must retain, in the Source form of any Derivative Works 105 | that You distribute, all copyright, patent, trademark, and 106 | attribution notices from the Source form of the Work, 107 | excluding those notices that do not pertain to any part of 108 | the Derivative Works; and 109 | 110 | (d) If the Work includes a "NOTICE" text file as part of its 111 | distribution, then any Derivative Works that You distribute must 112 | include a readable copy of the attribution notices contained 113 | within such NOTICE file, excluding those notices that do not 114 | pertain to any part of the Derivative Works, in at least one 115 | of the following places: within a NOTICE text file distributed 116 | as part of the Derivative Works; within the Source form or 117 | documentation, if provided along with the Derivative Works; or, 118 | within a display generated by the Derivative Works, if and 119 | wherever such third-party notices normally appear. The contents 120 | of the NOTICE file are for informational purposes only and 121 | do not modify the License. You may add Your own attribution 122 | notices within Derivative Works that You distribute, alongside 123 | or as an addendum to the NOTICE text from the Work, provided 124 | that such additional attribution notices cannot be construed 125 | as modifying the License. 126 | 127 | You may add Your own copyright statement to Your modifications and 128 | may provide additional or different license terms and conditions 129 | for use, reproduction, or distribution of Your modifications, or 130 | for any such Derivative Works as a whole, provided Your use, 131 | reproduction, and distribution of the Work otherwise complies with 132 | the conditions stated in this License. 133 | 134 | 5. Submission of Contributions. Unless You explicitly state otherwise, 135 | any Contribution intentionally submitted for inclusion in the Work 136 | by You to the Licensor shall be under the terms and conditions of 137 | this License, without any additional terms or conditions. 138 | Notwithstanding the above, nothing herein shall supersede or modify 139 | the terms of any separate license agreement you may have executed 140 | with Licensor regarding such Contributions. 141 | 142 | 6. Trademarks. This License does not grant permission to use the trade 143 | names, trademarks, service marks, or product names of the Licensor, 144 | except as required for reasonable and customary use in describing the 145 | origin of the Work and reproducing the content of the NOTICE file. 146 | 147 | 7. Disclaimer of Warranty. Unless required by applicable law or 148 | agreed to in writing, Licensor provides the Work (and each 149 | Contributor provides its Contributions) on an "AS IS" BASIS, 150 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 151 | implied, including, without limitation, any warranties or conditions 152 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 153 | PARTICULAR PURPOSE. You are solely responsible for determining the 154 | appropriateness of using or redistributing the Work and assume any 155 | risks associated with Your exercise of permissions under this License. 156 | 157 | 8. Limitation of Liability. In no event and under no legal theory, 158 | whether in tort (including negligence), contract, or otherwise, 159 | unless required by applicable law (such as deliberate and grossly 160 | negligent acts) or agreed to in writing, shall any Contributor be 161 | liable to You for damages, including any direct, indirect, special, 162 | incidental, or consequential damages of any character arising as a 163 | result of this License or out of the use or inability to use the 164 | Work (including but not limited to damages for loss of goodwill, 165 | work stoppage, computer failure or malfunction, or any and all 166 | other commercial damages or losses), even if such Contributor 167 | has been advised of the possibility of such damages. 168 | 169 | 9. Accepting Warranty or Additional Liability. While redistributing 170 | the Work or Derivative Works thereof, You may choose to offer, 171 | and charge a fee for, acceptance of support, warranty, indemnity, 172 | or other liability obligations and/or rights consistent with this 173 | License. However, in accepting such obligations, You may act only 174 | on Your own behalf and on Your sole responsibility, not on behalf 175 | of any other Contributor, and only if You agree to indemnify, 176 | defend, and hold each Contributor harmless for any liability 177 | incurred by, or claims asserted against, such Contributor by reason 178 | of your accepting any such warranty or additional liability. 179 | 180 | END OF TERMS AND CONDITIONS 181 | 182 | APPENDIX: How to apply the Apache License to your work. 183 | 184 | To apply the Apache License to your work, attach the following 185 | boilerplate notice, with the fields enclosed by brackets "[]" 186 | replaced with your own identifying information. (Don't include 187 | the brackets!) The text should be enclosed in the appropriate 188 | comment syntax for the file format. We also recommend that a 189 | file or class name and description of purpose be included on the 190 | same "printed page" as the copyright notice for easier 191 | identification within third-party archives. 192 | 193 | Copyright 2021 Yahoo Inc. 194 | 195 | Licensed under the Apache License, Version 2.0 (the "License"); 196 | you may not use this file except in compliance with the License. 197 | You may obtain a copy of the License at 198 | 199 | http://www.apache.org/licenses/LICENSE-2.0 200 | 201 | Unless required by applicable law or agreed to in writing, software 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 204 | See the License for the specific language governing permissions and 205 | limitations under the License. 206 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | NAME= check-log4j 2 | 3 | PREFIX?=/usr/local 4 | 5 | help: 6 | @echo "The following targets are available:" 7 | @echo "install install ${NAME} under ${PREFIX}" 8 | @echo "man generate the formatted manual page" 9 | @echo "readme generate the README after a manual page update" 10 | 11 | install: 12 | mkdir -p ${PREFIX}/bin ${PREFIX}/share/man/man1 13 | install -c -m 555 src/${NAME}.sh ${PREFIX}/bin/${NAME} 14 | install -c -m 444 doc/${NAME}.1 ${PREFIX}/share/man/man1/${NAME}.1 15 | 16 | man: doc/${NAME}.1.txt 17 | 18 | doc/${NAME}.1.txt: doc/${NAME}.1 19 | nroff -man $? | col -b >$@ 20 | 21 | readme: man 22 | sed -n -e '/^NAME/!p;//q' README.md >.readme 23 | sed -n -e '/^NAME/,$$p' -e '/emailing/q' doc/${NAME}.1.txt >>.readme 24 | echo '```' >>.readme 25 | mv .readme README.md 26 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | I confirm that this contribution is made under the terms of the license found in the root directory of this repository's source tree and that I have the authority necessary to make this contribution on behalf of its copyright owner. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | check-log4j 2 | =========== 3 | 4 | This tool will try to determine if the host it is 5 | running on is likely vulnerable to the latest reason 6 | that [the internet is on 7 | fire](https://istheinternetonfire.com): the [log4j 8 | RCE](https://logging.apache.org/log4j/2.x/security.html) 9 | [CVE‐2021‐44228](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228). 10 | 11 | This is different from other tools that attempt to 12 | verify whether a specific service is vulnerable by 13 | triggering the exploit and e.g., tracking pingbacks on 14 | a DNS canary token. That approach tells you whether a 15 | _service_ is vulnerable, but it doesn't even tell you 16 | which _specific systems_: the payload may have been 17 | proxied on to another system and from there logged via 18 | `log4j` on yet another one. So inspection of the 19 | service does not tell you that that specific host is 20 | vulnerable. 21 | 22 | On the other hand, host owners may not know whether 23 | they have a vulnerable version of `log4j` on their 24 | system: The `log4j` package may be pulled in as a 25 | dependency by various packages, or included inside a 26 | Java application jar. 27 | 28 | The `check-log4j` tool attempts to give host owners a 29 | tool to determine likely vulnerability by looking at 30 | running java processes and inside of any common Java 31 | archive files found. 32 | 33 | Please see the [manual 34 | page](./doc/check-log4j.1.txt) for full 35 | details. 36 | 37 | Installation 38 | ============ 39 | 40 | To install the command and manual page somewhere 41 | convenient, run `make install`; the Makefile defaults 42 | to '/usr/local' but you can change the PREFIX: 43 | 44 | ``` 45 | $ make PREFIX=~ install 46 | ``` 47 | 48 | FAQ 49 | === 50 | 51 | ## Dude, this is a shell script. You suck. Why isn't this written in $MyFavoriteLanguage? 52 | 53 | `check-log4j` is intended to run on any Unix-like 54 | system without depending on any particular language 55 | runtime. It's not pretty, but hey. 56 | 57 | ## Why does it say "Possibly vulnerable"? 58 | 59 | Actual vulnerability depends on runtime configuration. 60 | `check-log4j` basically checks whether the file 61 | `JndiLookup.class` is found in any archive files. If so, 62 | the system becomes suspect. If `check-log4j` can 63 | determine that this might be a `log4j-2.16.x` version, 64 | it will remain silent, but otherwise, it simply 65 | doesn't know whether that class might be used or 66 | just sits there as an unused dependency or what. 67 | 68 | ## This doesn't work on my system, explodes in some way, or doesn't correctly detect a vulnerable host! 69 | 70 | I'm sorry. Please let me know about this via email or 71 | a GitHub issue or, better yet, a pull request with a 72 | fix. 73 | 74 | 75 | Documentation 76 | ============= 77 | 78 | ``` 79 | NAME 80 | check-log4j -- try to determine if a host is vulnerable to log4j 81 | CVE-2021-44228 82 | 83 | SYNOPSIS 84 | check-log4j [-Vhv] [-j jar] [-p path] [-s skip] 85 | 86 | DESCRIPTION 87 | The check-log4j tool attempts to determine whether the host it is exe- 88 | cuted on is vulnerable to the log4j RCE vulnerability identified as 89 | CVE-2021-4428. 90 | 91 | Since this vulnerability is in a specific Java class that may be inside 92 | nested Java archive files, check-log4j may be somewhat intrusive to run 93 | and should be executed with care and consideration of the system's load. 94 | Please see DETAILS for more information. 95 | 96 | OPTIONS 97 | The following options are supported by check-log4j: 98 | 99 | -V Print version number and exit. 100 | 101 | -h Print a short help message and exit. 102 | 103 | -j jar Check only this archive, nothing else. Can be specified multi- 104 | ple times for multiple JAR (or other zip formatted archive) 105 | files. 106 | 107 | -p path Limit filesystem traversal to this directory. Can be specified 108 | multiple times. If not specified, check-log4j will default to 109 | '/'. 110 | 111 | -s skip Skip the given checks. Valid arguments are 'files', 'packages', 112 | and 'processes'. 113 | 114 | -v Be verbose. Can be specified multiple times. 115 | 116 | DETAILS 117 | CVE-2021-4428 describes a possible remote code execution (RCE) vulnera- 118 | bility in the popular log4j framework. Simply causing the vulnerable 119 | system to log a specifically crafted message can the attacker gain com- 120 | mand execution and information disclosure capabilities. This vulnerabil- 121 | ity relies on an insecure default setting applying to the Java Naming and 122 | Directory Interface (JNDI). 123 | 124 | Specifically, a system that contains the JndiLookup.class may enable the 125 | attack path in question. 126 | 127 | To determine whether a host is vulnerable, the check-log4j tool will per- 128 | form the following checks: 129 | o check for the existence of likely vulnerable packages 130 | o check for the existence of java processes using the 'JndiLookup' 131 | class 132 | 133 | The discovery process may include running find(1), lsof(1), or rpm(1); 134 | please use the -s flag to skip any checks that might have a negative 135 | impact on your host. 136 | 137 | The output of the command attempts to be human readable and provide suf- 138 | ficient information to judge whether the host requires attention. 139 | 140 | ENVIRONMENT 141 | The following environment variables influence the behavior of 142 | check-log4j: 143 | 144 | CHECK_LOG4J_FIND_OPTS_PRE 145 | Additional options to pass to find(1) prior to the path 146 | name(s). 147 | 148 | By default, check-log4j runs "find / -type f -name 149 | '*.[ejw]ar'"; the contents of this variable are placed 150 | immediately after the 'find' and before the path name(s). 151 | 152 | CHECK_LOG4J_FIND_OPTS_POST 153 | Additional options to pass to find(1) immediately after the 154 | path name(s). 155 | 156 | EXAMPLES 157 | Sample invocation on a non-vulnerable host: 158 | 159 | $ check-log4j 160 | No obvious indicators of vulnerability found. 161 | $ 162 | 163 | Sample invocation only looking at processes 164 | 165 | $ ./check-log4j.sh -s files -s packages -v -v 166 | => Running all checks... 167 | ==> Skipping package check. 168 | ==> Looking for jars... 169 | ==> Skipping files check. 170 | ==> Checking all found jars... 171 | check-log4j.sh 1.0 localhost: Possibly vulnerable jar 'BOOT-INF/lib/log4j-core-2.14.1.jar' (inside of /home/jans/log4shell-vulnerable-app-0.0.1-SNAPSHOT.jar) used by process 15569. 172 | 173 | $ 174 | 175 | Sample invocation searching only /var and /usr/local/lib and skipping 176 | package and process checks: 177 | 178 | $ check-log4j -p /var -p /usr/local/lib -s packages -s processes 179 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.15.0.jar'. 180 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.15.jar'. 181 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.jar'. 182 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core.jar'. 183 | 184 | $ 185 | 186 | Note version comparisons are only done for packages, which is why the 187 | above output incudes files ending in a seemingly non-vulnerable version. 188 | 189 | To avoid mountpoint traversal on a Unix system where find(1) requires the 190 | -x flag to precede the paths: 191 | 192 | $ env CHECK_LOG4J_FIND_OPTS_PRE="-x" check-log4j 193 | No obvious indicators of vulnerability found. 194 | 195 | To only search files newer than '/tmp/foo': 196 | 197 | $ env CHECK_LOG4J_FIND_OPTS_POST="-newer /tmp/foo" check-log4j 198 | No obvious indicators of vulnerability found. 199 | 200 | EXIT STATUS 201 | check-log4j will return 0 if the host was found not to be vulnerable and 202 | not in need of any update; it will return 1 if a vulnerable jar or pack- 203 | age was detected. 204 | 205 | If no vulnerability to CVE-2021-44228 / CVE-2021-45046 was found, but 206 | versions below the desired minimum were found, check-log4j will return 2. 207 | 208 | SEE ALSO 209 | find(1), lsof(1), rpm(1) 210 | 211 | HISTORY 212 | check-log4j was originally written by Jan Schaumann 213 | in December 2021. 214 | 215 | BUGS 216 | Please file bugs and feature requests via GitHub pull requests and issues 217 | or by emailing the author. 218 | ``` 219 | -------------------------------------------------------------------------------- /doc/check-log4j.1: -------------------------------------------------------------------------------- 1 | .Dd February 28, 2022 2 | .Dt check-log4j 1 3 | .Os 4 | .Sh NAME 5 | .Nm check-log4j 6 | .Nd try to determine if a host is vulnerable to log4j CVE-2021-44228 7 | .Sh SYNOPSIS 8 | .Nm 9 | .Op Fl Vhv 10 | .Op Fl j Ar jar 11 | .Op Fl p Ar path 12 | .Op Fl s Ar skip 13 | .Sh DESCRIPTION 14 | The 15 | .Nm 16 | tool attempts to determine whether the host it is 17 | executed on is vulnerable to the log4j RCE 18 | vulnerability identified as CVE-2021-4428. 19 | .Pp 20 | Since this vulnerability is in a specific Java class 21 | that may be inside nested Java archive files, 22 | .Nm 23 | may be somewhat intrusive to run and should be 24 | executed with care and consideration of the system's 25 | load. 26 | Please see DETAILS for more information. 27 | .Sh OPTIONS 28 | The following options are supported by 29 | .Nm : 30 | .Bl -tag -width p_path_ 31 | .It Fl V 32 | Print version number and exit. 33 | .It Fl h 34 | Print a short help message and exit. 35 | .It Fl j Ar jar 36 | Check only this archive, nothing else. 37 | Can be specified multiple times for multiple JAR 38 | (or other zip formatted archive) files. 39 | .It Fl p Ar path 40 | Limit filesystem traversal to this directory. 41 | Can be specified multiple times. 42 | If not specified, 43 | .Nm 44 | will default to '/'. 45 | .It Fl s Ar skip 46 | Skip the given checks. 47 | Valid arguments are 'files', 'packages', and 48 | \'processes'. 49 | .It Fl v 50 | Be verbose. 51 | Can be specified multiple times. 52 | .El 53 | .Sh DETAILS 54 | CVE-2021-4428 describes a possible remote code 55 | execution (RCE) vulnerability in the popular log4j 56 | framework. 57 | Simply causing the vulnerable system to log a 58 | specifically crafted message can the attacker gain 59 | command execution and information disclosure 60 | capabilities. 61 | This vulnerability relies on an insecure default 62 | setting applying to the Java Naming and Directory 63 | Interface (JNDI). 64 | .Pp 65 | Specifically, a system that contains the 66 | JndiLookup.class may enable the attack path in 67 | question. 68 | .Pp 69 | To determine whether a host is vulnerable, the 70 | .Nm 71 | tool will perform the following checks: 72 | .Bl -bullet -compact 73 | .It 74 | check for the existence of likely vulnerable packages 75 | .It 76 | check for the existence of java processes using the 77 | \'JndiLookup' class 78 | .El 79 | .Pp 80 | The discovery process may include running 81 | .Xr find 1 , 82 | .Xr lsof 1 , 83 | or 84 | .Xr rpm 1 ; 85 | please use the 86 | .Fl s 87 | flag to skip any checks that might have a negative 88 | impact on your host. 89 | .Pp 90 | The output of the command attempts to be human 91 | readable and provide sufficient information to judge 92 | whether the host requires attention. 93 | .Sh ENVIRONMENT 94 | The following environment variables influence the 95 | behavior of 96 | .Nm : 97 | .Bl -tag 98 | .It CHECK_LOG4J_FIND_OPTS_PRE 99 | Additional options to pass to 100 | .Xr find 1 101 | prior to the path name(s). 102 | .Pp 103 | By default, 104 | .Nm 105 | runs "find / -type f -name '*.[ejw]ar'"; 106 | the contents of this variable are placed immediately 107 | after the 'find' and before the path name(s). 108 | .It CHECK_LOG4J_FIND_OPTS_POST 109 | Additional options to pass to 110 | .Xr find 1 111 | immediately after the path name(s). 112 | .El 113 | .Sh EXAMPLES 114 | Sample invocation on a non-vulnerable host: 115 | .Bd -literal -offset indent 116 | $ check-log4j 117 | No obvious indicators of vulnerability found. 118 | $ 119 | .Ed 120 | .Pp 121 | Sample invocation only looking at processes 122 | .Bd -literal -offset indent 123 | $ ./check-log4j.sh -s files -s packages -v -v 124 | => Running all checks... 125 | ==> Skipping package check. 126 | ==> Looking for jars... 127 | ==> Skipping files check. 128 | ==> Checking all found jars... 129 | check-log4j.sh 1.0 localhost: Possibly vulnerable jar 'BOOT-INF/lib/log4j-core-2.14.1.jar' (inside of /home/jans/log4shell-vulnerable-app-0.0.1-SNAPSHOT.jar) used by process 15569. 130 | 131 | $ 132 | .Ed 133 | .Pp 134 | Sample invocation searching only /var and /usr/local/lib 135 | and skipping package and process checks: 136 | .Bd -literal -offset indent 137 | $ check-log4j -p /var -p /usr/local/lib -s packages -s processes 138 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.15.0.jar'. 139 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.15.jar'. 140 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.jar'. 141 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core.jar'. 142 | 143 | $ 144 | .Ed 145 | .Pp 146 | Note version comparisons are only done for packages, 147 | which is why the above output incudes files ending in 148 | a seemingly non-vulnerable version. 149 | .Pp 150 | To avoid mountpoint traversal on a Unix system where 151 | .Xr find 1 152 | requires the 153 | .Fl x 154 | flag to precede the paths: 155 | .Bd -literal -offset indent 156 | $ env CHECK_LOG4J_FIND_OPTS_PRE="-x" check-log4j 157 | No obvious indicators of vulnerability found. 158 | .Ed 159 | .Pp 160 | To only search files newer than '/tmp/foo': 161 | .Bd -literal -offset indent 162 | $ env CHECK_LOG4J_FIND_OPTS_POST="-newer /tmp/foo" check-log4j 163 | No obvious indicators of vulnerability found. 164 | .Ed 165 | .Sh EXIT STATUS 166 | .Nm 167 | will return 0 if the host was found not to be 168 | vulnerable and not in need of any update; 169 | it will return 1 if a vulnerable jar or package was 170 | detected. 171 | .Pp 172 | If no vulnerability to CVE-2021-44228 / CVE-2021-45046 173 | was found, but versions below the desired minimum were 174 | found, 175 | .Nm 176 | will return 2. 177 | .Sh SEE ALSO 178 | .Xr find 1 , 179 | .Xr lsof 1 , 180 | .Xr rpm 1 181 | .Sh HISTORY 182 | .Nm 183 | was originally written by 184 | .An Jan Schaumann 185 | .Aq jans@yahooinc.com 186 | in December 2021. 187 | .Sh BUGS 188 | Please file bugs and feature requests via GitHub pull 189 | requests and issues or by emailing the author. 190 | -------------------------------------------------------------------------------- /doc/check-log4j.1.txt: -------------------------------------------------------------------------------- 1 | check-log4j(1) NetBSD General Commands Manual check-log4j(1) 2 | 3 | NAME 4 | check-log4j -- try to determine if a host is vulnerable to log4j 5 | CVE-2021-44228 6 | 7 | SYNOPSIS 8 | check-log4j [-Vhv] [-j jar] [-p path] [-s skip] 9 | 10 | DESCRIPTION 11 | The check-log4j tool attempts to determine whether the host it is exe- 12 | cuted on is vulnerable to the log4j RCE vulnerability identified as 13 | CVE-2021-4428. 14 | 15 | Since this vulnerability is in a specific Java class that may be inside 16 | nested Java archive files, check-log4j may be somewhat intrusive to run 17 | and should be executed with care and consideration of the system's load. 18 | Please see DETAILS for more information. 19 | 20 | OPTIONS 21 | The following options are supported by check-log4j: 22 | 23 | -V Print version number and exit. 24 | 25 | -h Print a short help message and exit. 26 | 27 | -j jar Check only this archive, nothing else. Can be specified multi- 28 | ple times for multiple JAR (or other zip formatted archive) 29 | files. 30 | 31 | -p path Limit filesystem traversal to this directory. Can be specified 32 | multiple times. If not specified, check-log4j will default to 33 | '/'. 34 | 35 | -s skip Skip the given checks. Valid arguments are 'files', 'packages', 36 | and 'processes'. 37 | 38 | -v Be verbose. Can be specified multiple times. 39 | 40 | DETAILS 41 | CVE-2021-4428 describes a possible remote code execution (RCE) vulnera- 42 | bility in the popular log4j framework. Simply causing the vulnerable 43 | system to log a specifically crafted message can the attacker gain com- 44 | mand execution and information disclosure capabilities. This vulnerabil- 45 | ity relies on an insecure default setting applying to the Java Naming and 46 | Directory Interface (JNDI). 47 | 48 | Specifically, a system that contains the JndiLookup.class may enable the 49 | attack path in question. 50 | 51 | To determine whether a host is vulnerable, the check-log4j tool will per- 52 | form the following checks: 53 | o check for the existence of likely vulnerable packages 54 | o check for the existence of java processes using the 'JndiLookup' 55 | class 56 | 57 | The discovery process may include running find(1), lsof(1), or rpm(1); 58 | please use the -s flag to skip any checks that might have a negative 59 | impact on your host. 60 | 61 | The output of the command attempts to be human readable and provide suf- 62 | ficient information to judge whether the host requires attention. 63 | 64 | ENVIRONMENT 65 | The following environment variables influence the behavior of 66 | check-log4j: 67 | 68 | CHECK_LOG4J_FIND_OPTS_PRE 69 | Additional options to pass to find(1) prior to the path 70 | name(s). 71 | 72 | By default, check-log4j runs "find / -type f -name 73 | '*.[ejw]ar'"; the contents of this variable are placed 74 | immediately after the 'find' and before the path name(s). 75 | 76 | CHECK_LOG4J_FIND_OPTS_POST 77 | Additional options to pass to find(1) immediately after the 78 | path name(s). 79 | 80 | EXAMPLES 81 | Sample invocation on a non-vulnerable host: 82 | 83 | $ check-log4j 84 | No obvious indicators of vulnerability found. 85 | $ 86 | 87 | Sample invocation only looking at processes 88 | 89 | $ ./check-log4j.sh -s files -s packages -v -v 90 | => Running all checks... 91 | ==> Skipping package check. 92 | ==> Looking for jars... 93 | ==> Skipping files check. 94 | ==> Checking all found jars... 95 | check-log4j.sh 1.0 localhost: Possibly vulnerable jar 'BOOT-INF/lib/log4j-core-2.14.1.jar' (inside of /home/jans/log4shell-vulnerable-app-0.0.1-SNAPSHOT.jar) used by process 15569. 96 | 97 | $ 98 | 99 | Sample invocation searching only /var and /usr/local/lib and skipping 100 | package and process checks: 101 | 102 | $ check-log4j -p /var -p /usr/local/lib -s packages -s processes 103 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.15.0.jar'. 104 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.15.jar'. 105 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core-2.jar'. 106 | Possibly vulnerable jar '/usr/local/lib/jars/log4j-core.jar'. 107 | 108 | $ 109 | 110 | Note version comparisons are only done for packages, which is why the 111 | above output incudes files ending in a seemingly non-vulnerable version. 112 | 113 | To avoid mountpoint traversal on a Unix system where find(1) requires the 114 | -x flag to precede the paths: 115 | 116 | $ env CHECK_LOG4J_FIND_OPTS_PRE="-x" check-log4j 117 | No obvious indicators of vulnerability found. 118 | 119 | To only search files newer than '/tmp/foo': 120 | 121 | $ env CHECK_LOG4J_FIND_OPTS_POST="-newer /tmp/foo" check-log4j 122 | No obvious indicators of vulnerability found. 123 | 124 | EXIT STATUS 125 | check-log4j will return 0 if the host was found not to be vulnerable and 126 | not in need of any update; it will return 1 if a vulnerable jar or pack- 127 | age was detected. 128 | 129 | If no vulnerability to CVE-2021-44228 / CVE-2021-45046 was found, but 130 | versions below the desired minimum were found, check-log4j will return 2. 131 | 132 | SEE ALSO 133 | find(1), lsof(1), rpm(1) 134 | 135 | HISTORY 136 | check-log4j was originally written by Jan Schaumann 137 | in December 2021. 138 | 139 | BUGS 140 | Please file bugs and feature requests via GitHub pull requests and issues 141 | or by emailing the author. 142 | 143 | NetBSD 8.0 February 28, 2022 NetBSD 8.0 144 | -------------------------------------------------------------------------------- /src/check-log4j.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # 3 | # Originally written by Jan Schaumann 4 | # in December 2021. 5 | # 6 | # This script attempts to determine whether the host 7 | # it runs on is likely to be vulnerable to log4j RCE 8 | # CVE-2021-44228 / CVE-2021-45046. 9 | # 10 | # Copyright 2021 Yahoo Inc. 11 | # 12 | # Licensed under the Apache License, Version 2.0 (the 13 | # "License"); you may not use this file except in 14 | # compliance with the License. You may obtain a copy of 15 | # the License at 16 | # 17 | # http://www.apache.org/licenses/LICENSE-2.0 18 | # 19 | # Unless required by applicable law or agreed to in 20 | # writing, software distributed under the License is 21 | # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 22 | # CONDITIONS OF ANY KIND, either express or implied. 23 | # See the License for the specific language governing 24 | # permissions and limitations under the License. 25 | 26 | 27 | set -eu 28 | IFS="$(printf '\n\t')" 29 | 30 | umask 077 31 | 32 | ### 33 | ### Globals 34 | ### 35 | 36 | PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin 37 | 38 | # We broadly only care about versions >= 2.16. 39 | # 1.x has been determined not to be vulnerable, and we 40 | # boldly hope any version after 2.16 (including new 41 | # major versions, if any) will not regress. 42 | MAJOR_WANTED="2" 43 | MINOR_MINIMUM="16" 44 | 45 | # CVE-2021-44832 affects log4j <= 2.17.0, but requires 46 | # the attacker to control the config change, so really 47 | # not critical; we still _want_ folks to update, 48 | # though. 49 | MINOR_WANTED="17" 50 | TINY_WANTED="1" 51 | 52 | # log4j-2.16.0 _disables_ JNDI lookups, but leaves in 53 | # place the class, meaning it could be enabled if 54 | # log4j2.enableJndi=true. 55 | KNOWN_DISABLED="log4j-core-${MAJOR_WANTED}.${MINOR_MINIMUM}" 56 | FATAL_SETTING="-Dlog4j2.enableJndi=true" 57 | FATAL_CLASS="JndiLookup.class" 58 | 59 | _TMPDIR="" 60 | CHECK_JARS="" 61 | ENV_VAR_SET="no" 62 | FOUND_JARS="" 63 | PROGNAME="${0##*/}" 64 | RETVAL=1 65 | SEARCH_PATHS="" 66 | SKIP="" 67 | SEEN_JARS="" 68 | SHOULD_UPGRADE="unknown" 69 | SUSPECT_CLASSES="" 70 | SUSPECT_JARS="" 71 | SUSPECT_PACKAGES="" 72 | UNZIP="$(command -v unzip 2>/dev/null || true)" 73 | VERBOSITY=0 74 | VERSION="3.0" 75 | 76 | LOGPREFIX="${PROGNAME} ${VERSION} ${HOSTNAME:-"localhost"}" 77 | 78 | ### 79 | ### Functions 80 | ### 81 | 82 | 83 | cdtmp() { 84 | if [ -z "${_TMPDIR}" ]; then 85 | _TMPDIR=$(mktemp -d ${TMPDIR:-/tmp}/${PROGNAME}.XXXX) 86 | fi 87 | cd "${_TMPDIR}" 88 | } 89 | 90 | checkFilesystem() { 91 | local class="" 92 | local classes="" 93 | local okVersion="" 94 | local newjars="" 95 | local findCmd="" 96 | 97 | if expr "${SKIP}" : ".*files" >/dev/null; then 98 | verbose "Skipping files check." 2 99 | return 100 | fi 101 | 102 | verbose "Searching for java archives on the filesystem..." 3 103 | findCmd=$(echo find ${CHECK_LOG4J_FIND_OPTS_PRE:-""} "${SEARCH_PATHS:-/}" ${CHECK_LOG4J_FIND_OPTS_POST:-""}) 104 | 105 | verbose "Running '${findCmd}'..." 4 106 | 107 | newjars=$(eval ${findCmd} -type f -iname \'*.[ejw]ar\' 2>/dev/null || true) 108 | FOUND_JARS="$(printf "${FOUND_JARS:+${FOUND_JARS}\n}${newjars}")" 109 | 110 | verbose "Searching for ${FATAL_CLASS} on the filesystem..." 3 111 | classes=$(eval ${findCmd} -type f -iname "${FATAL_CLASS}" 2>/dev/null || true) 112 | 113 | for class in ${classes}; do 114 | okVersion="$(checkFixedVersion "${class}")" 115 | if [ -z "${okVersion}" ]; then 116 | log "Possibly vulnerable class ${class}." 117 | SUSPECT_CLASSES="$(printf "${SUSPECT_CLASSES:+${SUSPECT_CLASSES}\n}${class}")" 118 | fi 119 | done 120 | } 121 | 122 | checkFixedVersion() { 123 | local file="${1}" 124 | local ver="" 125 | local mgrClass="" 126 | local suffix="${file##*.}" 127 | local dir="" 128 | 129 | set +e 130 | if [ x"${suffix##*[ejw]}" = x"ar" ]; then 131 | if [ -z "${UNZIP}" ]; then 132 | warn "Unable to check if ${suffix} contains a fixed version since unzip(1) is missing." 133 | return 134 | fi 135 | verbose "Checking for fixed classes in '${file}'..." 6 136 | if zeroSize "${file}"; then 137 | verbose "Skipping zero-size file '${file}'..." 6 138 | return 139 | fi 140 | 141 | mgrClass="$(${UNZIP} -q -l "${file}" 2>/dev/null | awk 'tolower($0) ~ /jndimanager.class$/ { print $NF; }')" 142 | if [ -n "${mgrClass}" ]; then 143 | cdtmp 144 | ${UNZIP} -o -q "${file}" "${mgrClass}" 2>/dev/null 145 | fi 146 | elif [ x"${suffix}" = x"class" ]; then 147 | # If we find the fatal class outside of a jar, let's guess that 148 | # there might be an accompanying JndiManager.class nearby... 149 | mgrClass="${file%/*}/../net/JndiManager.class" 150 | fi 151 | 152 | if [ -f "${mgrClass}" ]; then 153 | if grep -q 'log4j2.enableJndi' "${mgrClass}" ; then 154 | echo "log4j2.enableJndi found" 155 | fi 156 | fi 157 | set -e 158 | } 159 | 160 | checkInJar() { 161 | local jar="${1}" 162 | local needle="${2}" 163 | local pid="${3}" 164 | local parent="${4:-""}" 165 | local msg="" 166 | local match="" 167 | local flags="" 168 | local okVersion="" 169 | local rval=0 170 | 171 | local thisJar="${parent:+${parent}:}${jar}" 172 | for j in $(echo "${SEEN_JARS}" | tr ' ' '\n'); do 173 | if [ x"${j}" = x"${thisJar}" ]; then 174 | verbose "Skipping already seen archive '${thisJar}'..." 6 175 | return 176 | fi 177 | done 178 | SEEN_JARS="${SEEN_JARS:+${SEEN_JARS} }${thisJar}" 179 | 180 | if [ -z "${parent}" ]; then 181 | if zeroSize "${thisJar}"; then 182 | verbose "Skipping zero-size file '${thisJar}'..." 6 183 | return 184 | fi 185 | fi 186 | 187 | verbose "Checking for '${needle}' inside of ${jar}..." 5 188 | 189 | set +e 190 | if [ -n "${UNZIP}" ]; then 191 | ${UNZIP} -q -l "${jar}" 2>/dev/null | grep -q "${needle}" 192 | else 193 | warn "unzip(1) not found, trying to grep..." 194 | grep -q "${needle}" "${jar}" 195 | fi 196 | rval=$? 197 | set -e 198 | 199 | if [ ${rval} -eq 0 ]; then 200 | if [ -n "${parent}" ]; then 201 | msg=" (inside of ${parent})" 202 | fi 203 | if [ x"${jar}" != x"${pid}" ] && expr "${pid}" : "[0-9]*$" >/dev/null; then 204 | if checkPid "${pid}" ; then 205 | flags="JNDI Lookups enabled via command-line flags" 206 | fi 207 | msg="${msg} used by process ${pid}" 208 | fi 209 | 210 | okVersion="$(checkFixedVersion "${jar}")" 211 | 212 | # We're specifically looking for a jar, so no need to match .[ew]ar # here. 213 | match="$(echo "${jar}" | sed -n -e "s|.*/\(${KNOWN_DISABLED}[0-9.]*.jar\)$|\1|p")" 214 | if [ -n "${match}" -o -n "${okVersion}" ]; then 215 | if [ -n "${flags}" ]; then 216 | log "Normally non-vulnerable archive '${jar}'${msg} found, but ${flags}!" 217 | fi 218 | verbose "Allowing archive '${jar}' with known disabled JNDI Lookup." 6 219 | return 220 | fi 221 | if [ -z "${flags}" ]; then 222 | log "Possibly vulnerable archive '${jar}'${msg}." 223 | fi 224 | SUSPECT_JARS="${SUSPECT_JARS} ${thisJar}" 225 | fi 226 | } 227 | 228 | checkJars() { 229 | local found jar jarjar msg pid 230 | 231 | if [ -z "${CHECK_JARS}" ]; then 232 | findJars 233 | fi 234 | 235 | if [ -z "${FOUND_JARS}" ]; then 236 | return 237 | fi 238 | 239 | verbose "Checking all found jars and wars..." 2 240 | 241 | if [ -z "${UNZIP}" ]; then 242 | warn "unzip(1) not found, unable to peek into jars inside of jar!" 243 | fi 244 | for found in ${FOUND_JARS}; do 245 | pid="${found%%--*}" 246 | jar="${found#*--}" 247 | 248 | if [ -n "${UNZIP}" ]; then 249 | if zeroSize "${jar}"; then 250 | verbose "Skipping zero-size file '${jar}'..." 3 251 | continue 252 | fi 253 | jarjar="$(${UNZIP} -q -l "${jar}" 2>/dev/null | awk 'tolower($0) ~ /^ .*log4j.*[ejw]ar$/ { print $NF; }')" 254 | if [ -n "${jarjar}" ]; then 255 | extractAndInspect "${jar}" "${jarjar}" ${pid} 256 | fi 257 | fi 258 | 259 | checkInJar "${jar}" "${FATAL_CLASS}" "${pid}" 260 | done 261 | } 262 | 263 | checkOnlyGivenJars() { 264 | verbose "Checking only given jars..." 1 265 | FOUND_JARS="${CHECK_JARS}" 266 | checkJars 267 | } 268 | 269 | checkRpms() { 270 | verbose "Checking rpms..." 4 271 | 272 | local pkg version 273 | 274 | for pkg in $(rpm -qa --queryformat '%{NAME}--%{VERSION}\n' | grep log4j); do 275 | version="${pkg##*--}" 276 | if ! isFixedVersion "${version}"; then 277 | # Squeeze '--' so users don't get confused. 278 | pkg="$(echo "${pkg}" | tr -s -)" 279 | SUSPECT_PACKAGES="${SUSPECT_PACKAGES} ${pkg}" 280 | fi 281 | done 282 | } 283 | 284 | checkPackages() { 285 | if expr "${SKIP}" : ".*packages" >/dev/null; then 286 | verbose "Skipping package check." 2 287 | return 288 | fi 289 | 290 | verbose "Checking for vulnerable packages..." 2 291 | 292 | if [ x"$(command -v rpm 2>/dev/null)" != x"" ]; then 293 | checkRpms 294 | fi 295 | } 296 | 297 | checkPid() { 298 | local pid="${1}" 299 | verbose "Checking process ${pid} for command-line flags..." 6 300 | 301 | ps -www -q "${pid}" -o command= | grep -q -- "${FATAL_SETTING}" 302 | } 303 | 304 | checkProcesses() { 305 | local jars 306 | if expr "${SKIP}" : ".*processes" >/dev/null; then 307 | verbose "Skipping process check." 2 308 | return 309 | fi 310 | 311 | verbose "Checking running processes..." 3 312 | local lsof="$(command -v lsof 2>/dev/null || true)" 313 | if [ -z "${lsof}" ]; then 314 | jars="$(ps -o pid,command= -wwwax | awk 'tolower($0) ~ /[ejw]ar$/ { print $1 "--" $NF; }' | uniq)" 315 | else 316 | jars="$(${lsof} -c java 2>/dev/null | awk 'tolower($0) ~ /reg.*[ejw]ar$/ { print $2 "--" $NF; }' | uniq)" 317 | fi 318 | FOUND_JARS="${FOUND_JARS:+${FOUND_JARS} }${jars}" 319 | } 320 | 321 | cleanup() { 322 | if [ -n "${_TMPDIR}" ]; then 323 | rm -fr "${_TMPDIR}" 324 | fi 325 | } 326 | 327 | extractAndInspect() { 328 | local jar="${1}" 329 | local jarjar="${2}" 330 | local pid="${3}" 331 | local f 332 | 333 | verbose "Extracting ${jar} to look inside jars inside of jars..." 5 334 | 335 | cdtmp 336 | if ${UNZIP} -o -q "${jar}" ${jarjar} 2>/dev/null; then 337 | for f in ${jarjar}; do 338 | checkInJar "${f}" "${FATAL_CLASS}" ${pid} "${jar}" 339 | done 340 | fi 341 | } 342 | 343 | findJars() { 344 | verbose "Looking for jars..." 2 345 | checkProcesses 346 | checkFilesystem 347 | } 348 | 349 | isFixedVersion () { 350 | local version="${1}" 351 | local major minor 352 | 353 | major="${version%%.*}" # 2.15.0 => 2 354 | minor="${version#*.}" # 2.15.0 => 15.0 355 | tiny="${minor#*.}" # 15.0 => 0 356 | 357 | # strip off any possible other sub-versions 358 | # e.g., 2.15.0.12345 359 | tiny="${tiny%%.*}" # 0.12345 => 0 360 | minor="${minor%%.*}" # 15.0 => 15 361 | 362 | # NaN => unknown 363 | if ! expr "${major}" : "[0-9]*$" >/dev/null; then 364 | return 1 365 | fi 366 | if ! expr "${minor}" : "[0-9]*$" >/dev/null; then 367 | return 1 368 | fi 369 | 370 | if [ ${major} -lt ${MAJOR_WANTED} ] || 371 | [ ${major} -eq ${MAJOR_WANTED} -a ${minor} -lt ${MINOR_WANTED} ] || 372 | [ ${major} -eq ${MAJOR_WANTED} -a ${minor} -eq ${MINOR_WANTED} -a ${tiny} -lt ${TINY_WANTED} ]; then 373 | SHOULD_UPGRADE="yes" 374 | fi 375 | 376 | if [ ${major} -lt ${MAJOR_WANTED} -o ${minor} -ge ${MINOR_MINIMUM} ]; then 377 | return 0 378 | fi 379 | 380 | return 1 381 | } 382 | 383 | log() { 384 | msg="${1}" 385 | echo "${LOGPREFIX}: ${msg}" 386 | } 387 | 388 | log4jcheck() { 389 | verbose "Running all checks..." 1 390 | 391 | checkPackages 392 | checkJars 393 | } 394 | 395 | usage() { 396 | cat < ${msg}" >&2 417 | fi 418 | } 419 | 420 | verdict() { 421 | if [ -z "${SUSPECT_JARS}" -a -z "${SUSPECT_PACKAGES}" -a -z "${SUSPECT_CLASSES}" ]; then 422 | log "No obvious indicators of vulnerability to CVE-2021-44228 / CVE-2021-45046 found." 423 | RETVAL=0 424 | fi 425 | 426 | if [ -n "${SUSPECT_JARS}" ]; then 427 | echo 428 | echo "The following archives were found to include '${FATAL_CLASS}':" 429 | echo "${SUSPECT_JARS# *}" | tr ' ' '\n' 430 | fi 431 | 432 | if [ -n "${SUSPECT_PACKAGES}" ]; then 433 | echo 434 | echo "The following packages might still be vulnerable:" 435 | echo "${SUSPECT_PACKAGES}" 436 | RETVAL=1 437 | fi 438 | 439 | if [ x"${SHOULD_UPGRADE}" = x"yes" ]; then 440 | echo 441 | echo "Note: You appear to be using (at least some version of) log4j <= ${MAJOR_WANTED}.${MINOR_WANTED}.${TINY_WANTED}." 442 | echo "You should upgrade to that or a later version even if no obvious" 443 | echo "vulnerability to CVE-2021-44228 / CVE-2021-45046 was reported." 444 | 445 | if [ ${RETVAL} -eq 0 ]; then 446 | RETVAL=2 447 | fi 448 | fi 449 | } 450 | 451 | warn() { 452 | msg="${1}" 453 | echo "${LOGPREFIX}: ${msg}" >&2 454 | } 455 | 456 | zeroSize() { 457 | local file="${1}" 458 | local size 459 | 460 | # stat(1) is not portable :-/ 461 | size="$(ls -l "${file}" | awk '{print $5}')" 462 | if [ x"${size}" = x"0" ]; then 463 | return 0 464 | fi 465 | 466 | return 1 467 | } 468 | 469 | ### 470 | ### Main 471 | ### 472 | 473 | trap 'cleanup' 0 474 | 475 | while getopts 'Vhj:s:p:v' opt; do 476 | case "${opt}" in 477 | V) 478 | echo "${PROGNAME} ${VERSION}" 479 | exit 0 480 | # NOTREACHED 481 | ;; 482 | h\?) 483 | usage 484 | exit 0 485 | # NOTREACHED 486 | ;; 487 | j) 488 | d="${OPTARG%/*}" 489 | if [ x"${d}" = x"${OPTARG}" ]; then 490 | d="." 491 | fi 492 | f="$(cd "${d}" && pwd)/${OPTARG##*/}" 493 | CHECK_JARS="${CHECK_JARS:+${CHECK_JARS} }${f}" 494 | ;; 495 | p) 496 | SEARCH_PATHS="${SEARCH_PATHS:+${SEARCH_PATHS} }$(cd "${OPTARG}" && pwd)/." 497 | ;; 498 | s) 499 | SKIP="${SKIP} ${OPTARG}" 500 | ;; 501 | v) 502 | VERBOSITY=$(( ${VERBOSITY} + 1 )) 503 | ;; 504 | *) 505 | usage 506 | exit 1 507 | # NOTREACHED 508 | ;; 509 | esac 510 | done 511 | shift $(($OPTIND - 1)) 512 | 513 | if [ $# -gt 0 ]; then 514 | usage 515 | exit 1 516 | # NOTREACHED 517 | fi 518 | 519 | if [ -z "${CHECK_JARS}" ]; then 520 | log4jcheck 521 | else 522 | checkOnlyGivenJars 523 | fi 524 | verdict 525 | 526 | exit ${RETVAL} 527 | --------------------------------------------------------------------------------