Spring4Shell detection with an Nmap NSE script: a narrow, non-intrusive probe
Spring Framework RCE (CVE-2022-22965) Nmap (NSE) Checker (Non-Intrusive)
At a glance
- What is it?
- alt3kx/CVE-2022-22965 is a Lua Nmap script that checks for the Spring4Shell RCE by sending a crafted query string and looking for a 400 response. It is a quick triage tool, not a full exploit or a comprehensive scanner.
- Who is it for?
- Adopt this script if you need a fast, non-intrusive way to check a Spring MVC or WebFlux endpoint for CVE-2022-22965 during a security assessment. Do not use it as your only verification: a 400 response is a weak signal, and false positives and negatives are likely.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Probably not. The repository last received commits 55 months ago, on April 7, 2022.
- What is it written in?
- Mainly Lua, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.
Star history
- alt3kx/CVE-2022-22965100 ★ · +0
| Repository | 2026-08-18 | 2026-10-04 |
|---|---|---|
| alt3kx/CVE-2022-22965 | 100 | 100 |
0 Stars in the last 47 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
What this NSE script actually detects
The script is written for Nmap's NSE framework, so it integrates with the standard nmap command line. It is a single Lua file that you point at a target with a port and an optional path. The README gives examples like nmap -p443,8080 --script=./CVE-2022-22965.nse <target> -Pn. You can also set the path and HTTP method via script arguments: --script-args 'CVE-2022-22965.path="/path/to/test"' and CVE-2022-22965.method. The default path is / and the default method is GET. That is the entire configuration surface. There is no support for POST bodies, custom headers, or multiple paths in one run. You have to run the script once per path, which is a real limitation for scanning a web app with many endpoints.
The detection mechanism: a 400 status as a signal
The core idea is simple. The script sends a GET request with a parameter like /?class.module.classLoader.definedPackages%5B0%5D=0. If the Spring application is vulnerable, the data binding mechanism tries to resolve that property path. The property 'definedPackages' on the classLoader is read-only or not bindable, so Spring throws a BindException, which results in a 400 Bad Request. A non-vulnerable application might ignore the parameter or return a different status, such as 200 or 404. The README includes three payload variants: the definedPackages one, a URLs%5B0%5D=0 variant, and a DefaultAssertionStatus=nosense variant. All three are GET requests with a query string. The script checks the HTTP response code and flags the target as VULNERABLE if it sees 400. This is a heuristic, not a proof. The author acknowledges this by showing the curl commands for manual inspection, where you grep for 400. The strength is that it is fast and harmless. The weakness is that a 400 can come from many other causes: a malformed request, a WAF, or a reverse proxy. Conversely, a vulnerable endpoint might return 200 if the application catches the exception or if the path does not use data binding. So the script is a probabilistic indicator, and the README does not claim more than that.
Running it: commands and script arguments from the README
To use the script, you need Nmap with NSE support, which is standard in most distributions. The README shows the command as nmap -p <port> --script=./CVE-2022-22965.nse [--script-args 'CVE-2022-22965.path=<PATH>,CVE-2022-22965.method=<HTTP METHOD>'] <target>. The script file is named CVE-2022-22965.nse and you run it from the directory where it lives. The arguments are namespaced with the script name, so you must write CVE-2022-22965.path and CVE-2022-22965.method. The default path is /, which tests the root of the web application. The default method is GET. The examples show scanning ports 443 and 8080 with -Pn to skip host discovery. The output format is an NSE table that lists the vulnerability, the CVE ID, a description, the disclosure date, and a reference to the MITRE CVE page. The README shows the state as 'VULNERABLE (Exploitable)', which is a strong claim given that the script only sees a 400. That wording comes from the NSE template, but it overstates what a status code can prove. A user should read that output as 'potentially vulnerable', not as a confirmed exploit.
Limitations and false positives: when this script misleads
The biggest limitation is the reliance on a 400 status code. Many web servers return 400 for any request with an unexpected query parameter, especially if a WAF or a framework's strict validation is in place. A Spring application that is patched, or that runs on JDK 8, might still return 400 for a different reason. The README does not provide any mechanism to distinguish a Spring-specific 400 from a generic one. Another limitation is that the script only tests one path per run. If the vulnerable endpoint is under /some/app, you must specify that path explicitly. The default / may not hit any data binding endpoint. The script also assumes the application uses GET parameters. If the application only accepts POST, the default method will not work, and you have to change the method argument, but the README does not give an example of POST usage. Finally, the script does not check the response body. A 400 with a Spring error page is different from a 400 with a generic server error, but the script ignores that. This means you could get a false positive on a non-Spring server or a false negative if the Spring app returns a 200 with an error message in the body. The author calls it non-intrusive, which is true, but non-intrusive also means non-confirmatory.
Alternatives: ZAP and manual curl checks
The README credits two inspirations: a Twitter thread from RandoriAttack and a ZAP scan rule from ZAP. The ZAP rule is a more integrated approach because it runs inside a web application scanner, which can crawl the application, discover paths, and send the payload across multiple endpoints. That is a different approach from this NSE script, which requires you to know the path in advance. ZAP can also handle session management and CSRF tokens, which this script cannot. For a manual check, the README provides curl commands that you can run directly. For example: curl -i -s -k -X GET -H 'Host: <target>' -H 'User-Agent: alex666' -H 'Connection: close' 'https://<target>/path/foo/?class.module.classLoader.definedPackages%5B0%5D=0' | grep -i 400. This gives you the full HTTP response, including headers and body, so you can inspect the error page. The NSE script is a convenience wrapper around that curl command, but it loses the body information. If you need a reliable detection, the manual curl is more informative, and ZAP is more thorough for scanning an entire app. The NSE script sits in between: quick, scriptable, and integrated into Nmap output, but shallow.
Maintenance and licence considerations
The repository has no recent releases and the last push is unknown, but the code is a single Lua file that depends on Nmap's NSE libraries. The vulnerability is from 2022, and Spring has since released patches. The script does not need to change unless the detection logic is improved. There is no dependency on external libraries, so maintenance cost is low. The licence is GPL-3.0, which means if you distribute modified versions, you must make the source available under the same licence. For internal use in a security team, that is rarely a problem, but if you embed this script in a commercial product, you need to comply with GPL obligations. The script itself is not actively maintained, so you should treat it as a static tool. It will not gain new features or fixes unless you fork it. The README credits a contributor, milo-minderbinder, for fixes and improvements, which suggests there was some community input, but the repository shows no release history. For a one-off assessment, that is fine. For a long-term scanning infrastructure, you would likely write your own script or use a maintained scanner like nuclei, which has Spring4Shell templates.
Editorial conclusion
Adopt this script if you need a fast, non-intrusive way to check a Spring MVC or WebFlux endpoint for CVE-2022-22965 during a security assessment. Do not use it as your only verification: a 400 response is a weak signal, and false positives and negatives are likely. Before relying on it, verify that the target path actually uses data binding, confirm the JDK version is 9 or later, and test against a known vulnerable instance to see if the payload triggers the expected response. If you need a more thorough check, pair it with a manual request using the curl examples in the README and inspect the full HTTP response, not just the status code.
Frequently asked questions
What does CVE vulnerability mean in the context of CVE-2022-22965?
Here it names one tracked flaw: the Spring Framework remote code execution issue that the Nmap script probes for, disclosed 2022-03-31 and recorded under the identifier CVE-2022-22965. The report prints that identifier on its IDs line and links the MITRE record for it as a reference.
What is the latest version of Spring Security in this project?
The repository says nothing about Spring Security or any version numbering. It targets one CVE in Spring Framework 5.2.x and 5.3.x through a data-binding request, and its last push to the main branch is dated 2022-04-07.
What is the Log4j2 vulnerability, and does this script check it?
The repository does not discuss Log4j2 at all, and the script checks only CVE-2022-22965. Its reference list covers the Spring disclosure: the MITRE record, a lunasec write-up on Spring RCE vulnerabilities, the Spring Framework announcement and a Rapid7 post.
What are the top 5 CVE vulnerabilities covered here?
There is one. The whole repository is a checker for CVE-2022-22965, a Spring Framework RCE reachable through data binding, and the script takes no identifier argument, so it cannot be pointed at a different CVE. Other identifiers would need a different script.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/alt3kx-cve-2022-22965)Star history chart for your README
[](https://hysenlabs.com/projects/alt3kx-cve-2022-22965)