This extension integrates Burp with the Retire.js repository to find vulnerable JavaScript libraries.
2 |
It passively looks at JavaScript files loaded and identifies those which are vulnerable based on various signature types (URL, filename, file content or specific hash).
3 |
--------------------------------------------------------------------------------
/BappManifest.bmf:
--------------------------------------------------------------------------------
1 | Uuid: 36238b534a78494db9bf2d03f112265c
2 | ExtensionType: 1
3 | Name: Retire.js
4 | RepoName: retire-js
5 | ScreenVersion: 2.1.1
6 | SerialVersion: 6
7 | MinPlatformVersion: 0
8 | ProOnly: True
9 | Author: Philippe Arteau
10 | ShortDescription: Integrates with the Retire.js repository to find vulnerable JavaScript libraries.
11 | EntryPoint: retirejs-burp-plugin/target/burp-retire-js-2.jar
12 | BuildCommand: mvn package -DskipTests=true -Dmaven.javadoc.skip=true -B -Pburp-only
13 |
--------------------------------------------------------------------------------
/DEPLOYMENT.md:
--------------------------------------------------------------------------------
1 | # Burp
2 |
3 | Contact : [support@portswigger.net](mailto:support@portswigger.net)
4 |
5 | ```
6 | Subject: RetireJs plugin updated
7 |
8 | Hi,
9 | The plugin RetireJs was updated.
10 |
11 | [...]
12 |
13 | The plugin can be recompiled from the source :
14 | https://github.com/h3xstream/burp-retire-js
15 |
16 | $ git clone ...
17 | $ mvn clean install -Pburp-only
18 |
19 | That's it let me know if you have question.
20 | ```
21 |
22 | # Maven
23 |
24 | Normal build
25 | ```
26 | mvn clean install
27 | ```
28 |
29 | Release
30 | ```
31 | mvn versions:set -DnewVersion=3.0.1
32 | mvn clean source:jar javadoc:jar package deploy -P!bigjar,signjars,all-modules
33 | ```
34 |
35 | - https://oss.sonatype.org/
36 | - http://central.sonatype.org/pages/ossrh-guide.html
--------------------------------------------------------------------------------
/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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Retire.js (Burp plugin) [](https://travis-ci.org/h3xstream/burp-retire-js)
2 |
3 | [Burp](http://portswigger.net/burp/) / [ZAP](https://www.owasp.org/index.php/OWASP_Zed_Attack_Proxy_Project) extension that integrate [Retire.js](https://github.com/bekk/retire.js) repository to find vulnerable JavaScript libraries. It passively look at JavaScript files loaded and identify those vulnerable based on various signature types (URL, filename, file content or specific hash).
4 |
5 | ## License
6 |
7 | This software is release under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
8 |
9 | ## Downloads
10 |
11 | Last updated : December 10th, 2019
12 |
13 | Burp Suite plugin : [Download](https://raw.githubusercontent.com/h3xstream/burp-retire-js/gh-pages/releases/burp/burp-retire-js-3.0.2.jar) (also available on the [BApp Store](https://pro.portswigger.net/bappstore/ShowBappDetails.aspx?uuid=36238b534a78494db9bf2d03f112265c))
14 |
15 | ZAP plugin : [Download](https://raw.githubusercontent.com/h3xstream/burp-retire-js/gh-pages/releases/zap/retirejs-alpha-3.0.2.zap)
16 |
17 |
18 | --------------------------
19 |
20 | ## Burp plugin
21 |
22 | 
23 |
24 | 
25 |
26 |
27 | ## ZAP plugin
28 |
29 | 
30 |
31 | ## Maven plugin [](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.h3xstream.retirejs%22%20a%3A%22retirejs-maven-plugin%22)
32 |
33 | Run the Maven plugin with the goal `scan`:
34 |
35 | $ cd myproject
36 | $ mvn com.h3xstream.retirejs:retirejs-maven-plugin:scan
37 | [...]
38 | [INFO] --- retirejs-maven-plugin:1.0.0-SNAPSHOT:scan (default-cli) @ myproject ---
39 | [WARNING] jquery.js contains a vulnerable JavaScript library.
40 | [INFO] Path: C:\Code\myproject\src\main\webapp\js\jquery.js
41 | [INFO] jquery version 1.8.1 is vulnerable.
42 | [INFO] + http://bugs.jquery.com/ticket/11290
43 | [INFO] + http://research.insecurelabs.org/jquery/test/
44 | [...]
45 |
46 | The additional parameter `-DretireJsBreakOnFailure` can be use to break the build when at least one vulnerability is found.
47 |
48 | [INFO] ------------------------------------------------------------------------
49 | [INFO] BUILD FAILURE
50 | [INFO] ------------------------------------------------------------------------
51 | [INFO] Total time: 1.450 s
52 | [INFO] Finished at: 2015-02-19T13:37:00-05:00
53 | [INFO] Final Memory: 11M/245M
54 | [INFO] ------------------------------------------------------------------------
55 | [ERROR] Failed to execute goal com.h3xstream.retirejs:retirejs-maven-plugin:1.0.0:scan (default-cli) on project
56 | my-web-app: 6 known vulnerabilities were identified in the JavaScript librairies. -> [Help 1]
57 | [ERROR]
58 |
59 | ### Run the Maven plugin as part of your build
60 |
61 | Use the following configuration to run the Maven plugin as part of your build. Only one `` may be specified at a time.
62 | To scan / iterate earlier in your build cycle, you can bind the plugin to the `validate` phase.
63 |
64 | ```xml
65 |
66 | com.h3xstream.retirejs
67 | retirejs-maven-plugin
68 | 3.0.1
69 |
70 | https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json
71 |
72 |
73 |
74 |
75 | scanProjectJavascript
76 |
77 | scan
78 |
79 | install
80 |
81 |
82 |
83 | ```
84 |
--------------------------------------------------------------------------------
/libs/zap-2.4.3.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/h3xstream/burp-retire-js/bd06f7d9f6802b02c693947f67814eb711ac3378/libs/zap-2.4.3.jar
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 | 4.0.0
6 |
7 | com.h3xstream.retirejs
8 | retirejs-root-pom
9 | 3.0.3
10 |
11 | pom
12 |
13 | Retire.JS proxy scanner (root pom.xml)
14 | The root pom file aggregate the dependencies use by the Retire.js extensions.
15 |
16 |
17 |
18 | all-modules
19 |
20 | true
21 |
22 |
23 | retirejs-core
24 | retirejs-burp-plugin
25 | retirejs-zap-plugin
26 | retirejs-maven-plugin
27 |
28 |
29 |
30 |
31 | burp-only
32 |
33 | false
34 |
35 |
36 | retirejs-core
37 | retirejs-burp-plugin
38 |
39 |
40 |
41 |
42 | signjars
43 |
44 | false
45 |
46 |
47 |
48 |
49 | org.apache.maven.plugins
50 | maven-gpg-plugin
51 | 1.6
52 |
53 |
54 | sign-artifacts
55 | verify
56 |
57 | sign
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | UTF-8
70 | UTF-8
71 | false
72 | none
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | maven-assembly-plugin
81 | 3.1.0
82 |
83 |
84 |
85 | maven-dependency-plugin
86 | 3.1.1
87 |
88 |
89 |
90 | maven-release-plugin
91 | 2.5.3
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | maven-clean-plugin
101 | 3.1.0
102 |
103 |
104 |
105 |
106 | maven-compiler-plugin
107 | 3.7.0
108 |
109 | 1.7
110 | 1.7
111 |
112 |
113 |
114 |
115 | maven-deploy-plugin
116 | 2.8.2
117 |
118 |
119 |
120 | maven-install-plugin
121 | 2.5.2
122 |
123 |
124 |
125 | maven-site-plugin
126 | 3.7.1
127 |
128 |
129 |
130 | org.apache.maven.plugins
131 | maven-javadoc-plugin
132 | 3.0.1
133 |
134 | -Xdoclint:none
135 |
136 |
137 | foo
138 | bar
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | net.portswigger.burp.extender
153 | burp-extender-api
154 | 2.1
155 | provided
156 |
157 |
158 |
159 |
160 |
161 | com.h3xstream.retirejs
162 | retirejs-core
163 | ${project.version}
164 |
165 |
166 |
167 |
168 |
169 | com.esotericsoftware
170 | minlog
171 | 1.3.1
172 |
173 |
174 |
175 |
176 | com.github.spullara.mustache.java
177 | compiler
178 | 0.9.6
179 |
180 |
181 |
182 |
183 |
184 | com.vaadin.external.google
185 | android-json
186 | 0.0.20131108.vaadin1
187 |
188 |
189 |
190 |
191 |
192 | org.zaproxy
193 | zap
194 |
196 | 2.8.0
197 | provided
198 |
199 |
200 |
201 | net.htmlparser.jericho
202 | jericho-html
203 | 3.4
204 | provided
205 |
206 |
207 |
208 | commons-httpclient
209 | commons-httpclient
210 | 3.1
211 | provided
212 |
213 |
214 |
215 | commons-configuration
216 | commons-configuration
217 | 1.10
218 | provided
219 |
220 |
221 |
222 |
223 |
224 | org.apache.logging.log4j
225 | log4j-core
226 | 2.17.1
227 | provided
228 |
229 |
230 |
231 |
232 |
233 | org.testng
234 | testng
235 | 7.5.1
236 | test
237 |
238 |
239 |
240 | commons-io
241 | commons-io
242 | 2.7
243 |
244 |
245 |
246 | org.mockito
247 | mockito-all
248 | 1.10.19
249 | test
250 |
251 |
252 |
253 |
255 |
256 | org.apache.maven
257 | maven-plugin-api
258 | 2.2.1
259 |
260 |
261 |
262 | org.apache.maven
263 | maven-core
264 | 2.2.1
265 |
266 |
267 |
268 |
269 |
270 |
271 | ${basedir}/target/site
272 |
273 |
274 | org.codehaus.mojo
275 | versions-maven-plugin
276 | 2.5
277 |
278 |
279 |
280 | dependency-updates-report
281 | plugin-updates-report
282 | property-updates-report
283 |
284 |
285 |
286 |
287 |
288 |
289 | org.apache.maven.plugins
290 | maven-project-info-reports-plugin
291 | 2.9
292 |
293 |
294 |
295 | org.owasp
296 | dependency-check-maven
297 | 3.3.4
298 |
299 |
300 |
301 | aggregate
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
313 |
314 | https://github.com/h3xstream/burp-retire-js
315 |
316 |
317 |
318 | The Apache Software License, Version 2.0
319 | http://www.apache.org/licenses/LICENSE-2.0.txt
320 | repo
321 |
322 |
323 |
324 |
325 |
326 | scm:git:https://github.com/h3xstream/burp-retire-js.git
327 | scm:git:git@github.com:h3xstream/burp-retire-js.git
328 | git@github.com:h3xstream/burp-retire-js.git
329 |
330 |
331 |
332 |
333 | h3xstream
334 | Philippe Arteau
335 | https://github.com/h3xstream
336 |
337 |
338 |
339 |
340 |
341 | sonatype-nexus-staging
342 | Sonatype staging repository
343 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
344 |
345 |
346 |
347 | sonatype-nexus-snapshots
348 | Sonatype snapshot repository
349 | https://oss.sonatype.org/content/repositories/snapshots/
350 |
351 |
352 |
353 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | com.h3xstream.retirejs
8 | retirejs-root-pom
9 | 3.0.3
10 |
11 |
12 | 4.0.0
13 |
14 | com.h3xstream.retirejs
15 | retirejs-burp-plugin
16 |
17 | RetireJS (Burp plugin)
18 |
19 |
20 |
21 | bigjar
22 |
23 | true
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | maven-assembly-plugin
32 |
33 |
34 | jar-with-dependencies
35 | package
36 |
37 | single
38 |
39 |
40 |
41 | jar-with-dependencies
42 |
43 | burp-retire-js-3
44 | false
45 |
46 | false
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | net.portswigger.burp.extender
61 | burp-extender-api
62 | provided
63 |
64 |
65 |
66 | com.h3xstream.retirejs
67 | retirejs-core
68 |
69 |
70 |
71 |
72 | org.testng
73 | testng
74 | test
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/java/burp/BurpExtender.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import burp.vuln.VulnerableLibraryIssue;
4 | import burp.vuln.VulnerableLibraryIssueBuilder;
5 | import com.esotericsoftware.minlog.Log;
6 | import com.h3xstream.retirejs.repo.JsLibraryResult;
7 | import com.h3xstream.retirejs.repo.ScannerFacade;
8 | import com.h3xstream.retirejs.repo.VulnerabilitiesRepositoryLoader;
9 |
10 | import java.io.IOException;
11 | import java.io.PrintWriter;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 | import org.json.JSONException;
15 |
16 | public class BurpExtender implements IBurpExtender, IScannerCheck {
17 |
18 |
19 | private IBurpExtenderCallbacks callbacks;
20 | private IExtensionHelpers helpers;
21 | private static BurpExtender extender;
22 |
23 |
24 | public static BurpExtender getInstance() {
25 | return extender;
26 | }
27 |
28 | public IExtensionHelpers getHelpers() {
29 | return this.callbacks.getHelpers();
30 | }
31 |
32 | @Override
33 | public void registerExtenderCallbacks(final IBurpExtenderCallbacks callbacks) {
34 |
35 | this.callbacks = callbacks;
36 | BurpExtender.extender = this;
37 | this.helpers = callbacks.getHelpers();
38 | this.callbacks.setExtensionName("Retire.js");
39 |
40 | PrintWriter stdout = new PrintWriter(callbacks.getStdout(), true);
41 | stdout.println("== Retire.js plugin ==");
42 | stdout.println("Passive scan rules to detect vulnerable Javascript libraries");
43 | stdout.println(" - Github : https://github.com/h3xstream/burp-retire-js");
44 | stdout.println("");
45 | stdout.println("== License ==");
46 | stdout.println("Retire.js repository is release under Apache License v2.");
47 | stdout.println("Retire.js Burp plugin is release under LGPL.");
48 | stdout.println("");
49 |
50 | Log.setLogger(new Log.Logger(){
51 | @Override
52 | protected void print (String message) {
53 | try {
54 | if(message.contains("ERROR:")) { //Not the most elegant way, but should be effective.
55 | callbacks.issueAlert(message);
56 | }
57 | callbacks.getStdout().write(message.getBytes());
58 | callbacks.getStdout().write('\n');
59 | } catch (IOException e) {
60 | System.err.println("Error while printing the log : "+e.getMessage()); //Very unlikely
61 | }
62 | }
63 | });
64 | Log.INFO();
65 |
66 | try {
67 | ScannerFacade.loadInstance(new VulnerabilitiesRepositoryLoader().load(VulnerabilitiesRepositoryLoader.REPO_URL,new BurpUpstreamDownloader(this.callbacks)));
68 | } catch (IOException | JSONException e) {
69 | Log.error("ERROR: Problem occurs while preloading the RetireJS vulnerabilities",e);
70 | }
71 |
72 | callbacks.registerScannerCheck(this);
73 |
74 | //Not fully implemented (the passive scan rule is sufficient)
75 | //callbacks.registerMessageEditorTabFactory(this);
76 |
77 | stdout.println("Retire.js plugin loaded");
78 | }
79 |
80 |
81 | @Override
82 | public List doPassiveScan(IHttpRequestResponse requestResponse) {
83 | List issues = new ArrayList();
84 |
85 | byte[] respBytes = requestResponse.getResponse();
86 |
87 | IResponseInfo responseInfo = helpers.analyzeResponse(respBytes);
88 | IRequestInfo requestInfo = helpers.analyzeRequest(requestResponse.getHttpService(), requestResponse.getRequest());
89 |
90 | String path = HttpUtil.getPathRequested(requestInfo);
91 | String contentType = HttpUtil.getContentType(responseInfo);
92 |
93 | try {
94 | //Avoid NPE
95 | boolean jsContentType = contentType != null ? contentType.indexOf("javascript") != -1 : false;
96 |
97 | int bodyOffset = responseInfo.getBodyOffset();
98 | if (jsContentType || path.endsWith(".js")) {
99 |
100 | //The big analysis is spawn here..
101 | Log.debug("Analyzing "+path+" (body="+(respBytes.length-bodyOffset)+" bytes)");
102 | issues = scanJavaScript(respBytes, bodyOffset, path, requestResponse, requestInfo);
103 | }
104 | else if (contentType.indexOf("html") != -1
105 | || path.endsWith(".htm") //Some additional condition just in case the content-type is bogus
106 | || path.endsWith(".html")
107 | || path.endsWith(".aspx")
108 | || path.endsWith(".asp")
109 | || path.endsWith(".php")
110 | || path.endsWith(".jsp")) {
111 |
112 | issues = scanHtmlPage(respBytes, bodyOffset, path, requestResponse, requestInfo);
113 | }
114 | } catch (Exception e) {
115 | Log.error("Exception while scanning the script '"+path+"' (" + e.getClass().getName() + ": "+ e.getMessage()+")");
116 | }
117 | return issues;
118 | }
119 |
120 | @Override
121 | public List doActiveScan(IHttpRequestResponse baseRequestResponse, IScannerInsertionPoint insertionPoint) {
122 | return new ArrayList();
123 | }
124 |
125 | @Override
126 | public int consolidateDuplicateIssues(IScanIssue existingIssue, IScanIssue newIssue) {
127 | boolean bothRetireJsIssue = existingIssue instanceof VulnerableLibraryIssue && newIssue instanceof VulnerableLibraryIssue;
128 |
129 | if(bothRetireJsIssue) {
130 | VulnerableLibraryIssue issue1 = (VulnerableLibraryIssue) existingIssue;
131 | VulnerableLibraryIssue issue2 = (VulnerableLibraryIssue) newIssue;
132 | return issue1.equals(issue2) ? -1: 0;
133 | }
134 |
135 | return -1; //Unknown
136 | }
137 |
138 | private List scanJavaScript(byte[] respBytes, int offset, String scriptName, IHttpRequestResponse resp, IRequestInfo requestInfo) throws IOException, JSONException {
139 |
140 | List res = ScannerFacade.getInstance().scanScript(scriptName, respBytes, offset);
141 |
142 | Log.debug(String.format("%d vulnerability(ies) for the script '%s'.",res.size(),scriptName));
143 |
144 | if(res.size() > 0) { //Transform the list of vulnerability Issue that can be display in Burp Scanner result.
145 | return VulnerableLibraryIssueBuilder.convert(res, resp.getHttpService(), resp, requestInfo);
146 | }
147 |
148 | return new ArrayList(); //Nothing was found
149 | }
150 |
151 | private List scanHtmlPage(byte[] respBytes, int offset, String scriptName, IHttpRequestResponse resp, IRequestInfo requestInfo) throws IOException, JSONException {
152 |
153 | List res = ScannerFacade.getInstance().scanHtml(respBytes,offset);
154 |
155 | Log.debug(String.format("%d vulnerability(ies) for the HTML page '%s'.",res.size(),scriptName));
156 |
157 | if(res.size() > 0) { //Transform the list of vulnerability Issue that can be display in Burp Scanner result.
158 | return VulnerableLibraryIssueBuilder.convert(res, resp.getHttpService(), resp, requestInfo);
159 | }
160 |
161 | return new ArrayList(); //Nothing was found
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/java/burp/BurpUpstreamDownloader.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 | import com.h3xstream.retirejs.repo.dl.Downloader;
4 |
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 | import java.io.PrintWriter;
8 | import java.net.URL;
9 |
10 | public class BurpUpstreamDownloader implements Downloader {
11 |
12 | private final IBurpExtenderCallbacks callbacks;
13 | public BurpUpstreamDownloader(IBurpExtenderCallbacks callbacks) {
14 | this.callbacks = callbacks;
15 | }
16 |
17 | /**
18 | * This implementation use the upstream proxy from Burp with callbacks.makeHttpRequest()
19 | * (Ref: http://blog.portswigger.net/2018/01/your-recipe-for-bapp-store-success.html)
20 | *
21 | * @param url
22 | * @param file
23 | * @throws Exception
24 | */
25 | @Override
26 | public void downloadUrlToFile(String url, File file) throws Exception {
27 | URL urlQuery = new URL(url);
28 |
29 | IExtensionHelpers helpers = callbacks.getHelpers();
30 |
31 | byte[] request = helpers.buildHttpRequest(urlQuery);
32 | int port = urlQuery.getPort() != -1 ? urlQuery.getPort() : (urlQuery.getProtocol().equals("https") ? 443 : 80);
33 | IHttpService service = helpers.buildHttpService(urlQuery.getHost(), port, urlQuery.getProtocol());
34 | IHttpRequestResponse resp = callbacks.makeHttpRequest(service, request);
35 |
36 | IResponseInfo respInfo = helpers.analyzeResponse(resp.getResponse());
37 | String content = new String(resp.getResponse(),respInfo.getBodyOffset(),resp.getResponse().length-respInfo.getBodyOffset());
38 |
39 | try(PrintWriter writer = new PrintWriter(new FileOutputStream(file))) {
40 | writer.print(content);
41 | writer.flush();
42 | }
43 |
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/java/burp/HttpUtil.java:
--------------------------------------------------------------------------------
1 | package burp;
2 |
3 |
4 | public class HttpUtil {
5 |
6 | /**
7 | *
8 | * @param responseInfo
9 | * @return
10 | */
11 | public static String getContentType(IResponseInfo responseInfo) {
12 | for (String header : responseInfo.getHeaders()) {
13 | if (header.toLowerCase().startsWith("content-type: ")) {
14 | return header.substring(14);
15 | }
16 | }
17 | return "";
18 | }
19 |
20 |
21 | /**
22 | * Extract the path from the first header.
23 | *
24 | * Input expected :
25 | * - GET /index.html HTTP/1.1
26 | * - POST /index.html HTTP/1.1
27 | *
28 | * @param request
29 | * @return
30 | */
31 | public static String getPathRequested(IRequestInfo request) {
32 | String h = request.getHeaders().get(0);
33 | return h.substring(h.indexOf(" ") + 1, h.lastIndexOf(" "));
34 | }
35 |
36 | public static String getFileRequested(IRequestInfo request) {
37 | String path = getPathRequested(request);
38 | int lastSlash = path.lastIndexOf('/');
39 | if(lastSlash < 0) lastSlash = 0;
40 | return path.substring(lastSlash+1);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/java/burp/vuln/MockHttpRequestResponse.java:
--------------------------------------------------------------------------------
1 | package burp.vuln;
2 |
3 | import burp.BurpExtender;
4 | import burp.IHttpRequestResponse;
5 | import burp.IHttpRequestResponseWithMarkers;
6 | import burp.IHttpService;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.regex.Matcher;
11 | import java.util.regex.Pattern;
12 |
13 | public class MockHttpRequestResponse implements IHttpRequestResponseWithMarkers {
14 |
15 | IHttpRequestResponse actual;
16 | List requestMarkers = new ArrayList();
17 | List responseMarkers = new ArrayList();
18 |
19 | MockHttpRequestResponse(IHttpRequestResponse actual, String regexRequest, String regexResponse) {
20 |
21 | if(regexRequest != null) {
22 | byte[] requestBytes = actual.getRequest();
23 | addMarkers(requestBytes, requestMarkers, regexRequest);
24 | }
25 |
26 | if(regexResponse != null) {
27 | byte[] responseBytes = actual.getResponse();
28 | addMarkers(responseBytes, responseMarkers, regexResponse);
29 | }
30 |
31 | this.actual = actual;
32 | }
33 |
34 | private void addMarkers(byte[] content, List markers, String... regexValues) {
35 | if(regexValues != null)
36 | for(String value : regexValues) {
37 | if(value == null) continue;
38 | int[] position = indexFromRegex(value, content);
39 | if(position != null) {
40 | markers.add(position);
41 | }
42 | }
43 | }
44 |
45 | @Override
46 | public byte[] getRequest() {
47 | return actual.getRequest();
48 | }
49 |
50 | @Override
51 | public void setRequest(byte[] message) {
52 | actual.setRequest(message);
53 | }
54 |
55 | @Override
56 | public byte[] getResponse() {
57 | return actual.getResponse();
58 | }
59 |
60 | @Override
61 | public void setResponse(byte[] message) {
62 | actual.setResponse(message);
63 | }
64 |
65 | @Override
66 | public String getComment() {
67 | return actual.getComment();
68 | }
69 |
70 | @Override
71 | public void setComment(String comment) {
72 | actual.setComment(comment);
73 | }
74 |
75 | @Override
76 | public String getHighlight() {
77 | return "http";
78 | }
79 |
80 | @Override
81 | public void setHighlight(String color) {
82 | actual.setHighlight(color);
83 | }
84 |
85 | @Override
86 | public IHttpService getHttpService() {
87 | return actual.getHttpService();
88 | }
89 |
90 | @Override
91 | public void setHttpService(IHttpService httpService) {
92 | actual.setHttpService(httpService);
93 | }
94 |
95 | @Override
96 | public List getRequestMarkers() {
97 | return requestMarkers;
98 | }
99 |
100 | @Override
101 | public List getResponseMarkers() {
102 | return responseMarkers;
103 | }
104 |
105 |
106 | public int[] indexFromRegex(String regex, byte[] content) {
107 | //NOTE : Regex are not intend to work on byte array. This will work on most JavaScript files except those with Unicode
108 | Pattern pattern = Pattern.compile(regex);
109 | Matcher m = pattern.matcher(BurpExtender.getInstance().getHelpers().bytesToString(content));
110 |
111 | while (m.find()) {
112 | return new int[] {m.start(), m.end()};
113 | }
114 | return null;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/java/burp/vuln/VulnerableLibraryIssue.java:
--------------------------------------------------------------------------------
1 | package burp.vuln;
2 |
3 | import burp.IHttpRequestResponse;
4 | import burp.IHttpService;
5 | import burp.IScanIssue;
6 | import com.esotericsoftware.minlog.Log;
7 |
8 | import java.net.URL;
9 |
10 | public class VulnerableLibraryIssue implements IScanIssue {
11 |
12 | private IHttpService httpService;
13 | private URL url;
14 | private IHttpRequestResponse httpMessage;
15 | private String name;
16 | private String detail;
17 | private String severity;
18 | private String confidence;
19 |
20 | private String libName;
21 | private String path;
22 |
23 | public VulnerableLibraryIssue(IHttpService httpService, URL url, IHttpRequestResponse httpMessage, String name, //
24 | String detail, String severity,String confidence, String libName, String path) {
25 | this.url = url;
26 | this.name = name;
27 | this.detail = detail;
28 | this.severity = severity;
29 | this.httpService = httpService;
30 | this.httpMessage = httpMessage;
31 | this.confidence = confidence;
32 |
33 | this.libName = libName;
34 | this.path = path;
35 | }
36 |
37 | @Override
38 | public URL getUrl() {
39 | return url;
40 | }
41 |
42 | @Override
43 | public String getIssueName() {
44 | return name;
45 | }
46 |
47 | @Override
48 | public int getIssueType() {
49 | return 0;
50 | }
51 |
52 | @Override
53 | public String getSeverity() {
54 | return severity;
55 | }
56 |
57 | @Override
58 | public String getConfidence() {
59 | return confidence;
60 | }
61 |
62 | @Override
63 | public String getIssueBackground() {
64 | return null;
65 | }
66 |
67 | @Override
68 | public String getRemediationBackground() {
69 | return null;
70 | }
71 |
72 | @Override
73 | public String getIssueDetail() {
74 | return detail;
75 | }
76 |
77 | @Override
78 | public String getRemediationDetail() {
79 | return null;
80 | }
81 |
82 | @Override
83 | public IHttpRequestResponse[] getHttpMessages() {
84 | return new IHttpRequestResponse[] {httpMessage};
85 | }
86 |
87 | @Override
88 | public IHttpService getHttpService() {
89 | return httpService;
90 | }
91 |
92 | @Override
93 | public boolean equals(Object obj) {
94 | if(obj instanceof VulnerableLibraryIssue) {
95 | VulnerableLibraryIssue issue = (VulnerableLibraryIssue)obj;
96 | //Log.debug("libName: "+issue.libName + " == " + this.libName+" ? " + issue.libName.equals(this.libName));
97 | //Log.debug("path : "+issue.path + " == " + this.path+" ? " + issue.path.equals(this.path));
98 | return issue.libName.equals(this.libName) && issue.path.equals(this.path);
99 | }
100 | return false;
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/java/burp/vuln/VulnerableLibraryIssueBuilder.java:
--------------------------------------------------------------------------------
1 | package burp.vuln;
2 |
3 | import burp.*;
4 | import com.esotericsoftware.minlog.Log;
5 | import com.h3xstream.retirejs.repo.JsLibraryResult;
6 | import com.h3xstream.retirejs.vuln.TemplateBuilder;
7 |
8 | import java.io.*;
9 | import java.util.ArrayList;
10 | import java.util.List;
11 |
12 | public class VulnerableLibraryIssueBuilder {
13 |
14 | private static final String TITLE = "Vulnerable version of the library '%s' found";
15 | private static final String TEMPLATE_DESC = "/burp/vuln/description.html";
16 |
17 | public static List convert(List librariesFound, IHttpService httpService, IHttpRequestResponse reqResp, IRequestInfo requestInfo) {
18 | List issues = new ArrayList();
19 | for(JsLibraryResult lib : librariesFound) {
20 |
21 |
22 | //Title summary
23 | String path = HttpUtil.getPathRequested(requestInfo);
24 | String filename = HttpUtil.getFileRequested(requestInfo);
25 |
26 | String libraryName = lib.getLibrary().getName();
27 | String title = String.format(TITLE,libraryName);
28 |
29 | //
30 | String description = TemplateBuilder.buildDescription(TEMPLATE_DESC,libraryName, lib.getDetectedVersion(), //Library detected
31 | lib.getVuln().getInfo(), //List of the URLs
32 | lib.getVuln().getAtOrAbove(), lib.getVuln().getBelow()); //Indicator of the affected versions
33 |
34 | issues.add(new VulnerableLibraryIssue(httpService,
35 | requestInfo.getUrl(), //URL to map the issue to a request (source of the issue)
36 | new MockHttpRequestResponse(reqResp,lib.getRegexRequest(),
37 | lib.getRegexResponse() == null ? lib.getRegexRequest(): lib.getRegexResponse()),
38 | title, //Title of the issue
39 | description, //HTML description
40 | mapToBurpSeverity(lib.getVuln().getSeverity()), //Severity .. Could be high, but the risk can never be confirm automatically..
41 | "Tentative", //The library is old for sure .. if the app is vulnerable, not so sure..
42 |
43 | libraryName, //The two last info are used to differentiate the vuln.
44 | path
45 | ));
46 | }
47 |
48 | Log.debug(issues.size() + " issues raised for the script " + HttpUtil.getPathRequested(requestInfo));
49 | return issues;
50 | }
51 |
52 | private static String mapToBurpSeverity(String severity) {
53 | if(severity.equals("info")) {
54 | return "Information";
55 | }
56 | else if(severity.equals("high") || severity.equals("medium") || severity.equals("low")) {
57 | //First character to upper for "High", "Medium", "Low"
58 | //See : burp.IScanIssue.getSeverity()
59 | return Character.toUpperCase(severity.charAt(0))+severity.substring(1);
60 | }
61 | return "Medium"; //In case the value is invalid, the default will be Medium
62 | }
63 |
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/retirejs-burp-plugin/src/main/resources/burp/vuln/description.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | The library {{detectedLibrary}} version {{detectedVersion}} has known security issues.
4 | For more information, visit those websites:
5 |
15 | The vulnerability is affecting all versions prior {{belowVersion}} (between {{aboveVersion}} and {{belowVersion}})
16 |
17 | Other considerations
18 |
19 | The vulnerability might be affecting a feature of the library that the website is not using. If the vulnerable feature is not used, this alert can be considered false positive.
20 |
21 |
22 | The library name and its version are identified based on a Retire.js signature. If the library identification is not correct, the prior vulnerability does not apply.
23 |